Notes from discussion 2019 09 27
Benjie Gillam edited this page Sep 27, 2019
·
1 revision
yarn add @remirror/editor-markdown
MarkdownCompatibleEditor
is an editor that's optimised to be exported to (and imported from) markdown. It supports various standard markdown features:
- headings
- bold / italic / underline
- links
- ordered and unordered lists
- code blocks
- hr
- tables
Render the editor like this:
const [prosemirrorState, setProsemirrorState] = useState(
markdown2remirror(`\
# Heading 1
## Heading 2
This is a paragraph with **bold** and _italic_ text.
- bullets
- bullets
1. numbers
1. numbers
`, config
);
return <MarkdownCompatibleEditor state={prosemirrorState} onChange={setProsemirrorState} />;
const [prosemirrorState, setProsemirrorState] = useState(null);
return (
<MarkdownCompatibleEditor
initialMarkdown={markdownText}
onChangeMarkdown={setMarkdownText} // WARNING: this is debounced, it is not realtime
state={prosemirrorState}
onChange={setProsemirrorState}
debounceDuration={500} // optional
/>
);
function MyComponent() {
const getResults = useCallback(() => {
//...
});
const manager = useRemirror({
theme: redAndBlue,
extensions: [
...MarkdownPreset.extensions,
...EmojiPreset.extensions,
BoldExtension,
UnderlineExtension,
[ItalicExtension, { priority: 3, onlyOn: 'Words' }],
[
MentionExtension,
{
getResults,
},
],
],
});
//const prosemirrorState = useRemirrorStateToProsemirror(state); // return state._prosemirror
//const markdown = useDebouncedRemirrorStateToMarkdown(state, 500);
const export = useCallback(() => {
saveToServer(manager.exportToMarkdown());
}, [state]);
const addHeading = useCallback(() => {
manager.dispatch(state.commands.addHeading(/*...*/));
}, [state]);
/*
setState(manager.dispatch(action()))
// manager is NOT immutable.
{
onClick(() => {
manager.dispatch(state => action1(state));
doTheOtherThing();
});
const doTheOtherThing = () => {
manager.dispatch(state => action2(state));
}
manager.dispatch(state => action3(state));
manager.dispatch(state => action4(state));
}
instance2
*/
return (
<div>
<button onClick={export}>Export</button>
<button onClick={addHeading}>Add heading</button>
<RemirrorEditor
manager={manager}
/>
</div>
);
}
function RemirrorEditor({ manager }) {
const pmViewRef = useRef(() => manager.buildView());
const ref = useRef(null);
// Stick the PM View into the DOM
useEffect(() => {
pmViewRef.current.setProps({ dom: ref.current });
}, [ref]);
// Update the PM view based on new state
useEffect(() => {
pmViewRef.current.updateState(manager.getProsemirrorState());
}, [manager]);
return <div ref={ref} />;
}
const nextTick = useRef(null);
const pmsRef = useRef(() => makeInitialProseMirrorState());
const [/*stale*/ prosemirrorState, setProsemirrorState] = useState(pmsRef.current);
function triggerUpdate() {
if (!nextTick.current) {
nextTick.current = setTimeout(() => {
nextTick.current = null;
setProsemirrorState(pmsRef.current);
}, 0);
}
}
let dispatching = false;
const returnValue: RemirrorState = {
_prosemirrorState: prosemirrorState,
dispatch(callback) {
if (dispatching) {
throw new Error('Forbidden dispatch within dispatch');
}
dispatching = true;
const actualProsemirrorState: EditorState = pmsRef.current;
const tr: Transaction = actualProsemirrorState.tr;
// Do the things within the transaction
callback(tr);
const newProsemirrorState: EditorState = actualProsemirrorState.apply(tr);
pmsRef.current = newProsemirrorState;
dispatching = false;
triggerUpdate();
},
};
return returnValue;