Skip to content

Notes from discussion 2019 09 27

Benjie Gillam edited this page Sep 27, 2019 · 1 revision

Installation

yarn add @remirror/editor-markdown

Usage

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;
Clone this wiki locally