y-draft-js

Draft-js editor bindings for Yjs

Usage no npm install needed!

<script type="module">
  import yDraftJs from 'https://cdn.skypack.dev/y-draft-js';
</script>

README

y-draft-js

Draft.js editor bindings for Yjs, a modern, fast, and powerful JavaScript library for collaborative editing.

Awareness has saved SelectionState.

usage

  1. install
npm install y-draft-js
  1. bindYdoc
const ydoc = new Y.Doc();

const provider = new WebsocketProvider(
  'ws://localhost:1234', // your websockt url
  id,
  ydoc,
  {
    connect: false,
  }
);
  1. bind Draft editor & connect to ydoc
import { DraftBinding } from 'y-draft-js';
const draftBind = new DraftBinding({
  ydoc,
  rawPath: 'raw', // your draft-js raw path(or [], exp: ['content', 'raw'])
  editor: editorRef.current, // draft-js editor ref , or plugin editor ref, or null(You can bind the editor asynchronously, exp: draftBind.bindEditor(editorRef.current))
  provider,
});
provider.connect();
  1. destroy the binding on departure
draftBind.destroy();

awareness

All clients are stored in the provider.awareness.

provider.awareness.getStates().forEach(state => {
  console.log(state.selection); // selection state by all clients
});

undoManager

draftBind.undo and draftBind.redo can be used to undo and redo. if you want to jump to the specified step, you can set allowUndo = false in editorState

const newEditorState = EditorState.push(
  editorState,
  newContentState,
  'insert-characters'
);
newEditorState.allowUndo = false; // not allow undo [y-draft-js]
this.setState({ editorState: newEditorState });

exp:


handleKeyCommand = (command, editorState) => {
  if(command === 'y-draft-undo') {
    this.draftBind?.undo();
    return 'handled';
  }
  if(command === 'y-draft-redo') {
    this.draftBind?.redo();
    return 'handled';
  }
  const newState = RichUtils.handleKeyCommand(editorState, command);
  if (newState) {
    this._onChange(newState);
    return true;
  }
  return false;
};

myKeyBindingFn(e) {
  const cmd = getDefaultKeyBinding(e);
  if (cmd === 'undo') return 'y-draft-undo';
  if (cmd === 'redo') return 'y-draft-redo';
  if (cmd) return cmd;
}
<Editor
  editorState={editorState}
  handleKeyCommand={this.handleKeyCommand}
  keyBindingFn={this.myKeyBindingFn}
  onChange={this._onChange}
  ref={ref => (this.editorRef = ref)}
/>

API

Because raw is not suitable for diff calculation save, so it did a layer of RAW conversion, conversion methods will be introduced

  1. getRawBySharedData
const raw = getRawBySharedData(path, ymap); // get raw data by ymap path
  1. toRawSharedData
ymap.set('content', toRawSharedData(raw)); // set raw data to ymap path
  1. getTargetByPath
const target = getTargetByPath(path, ymap); // get target by ymap path
  1. onTargetSync
const cancel = onTargetSync(path, ydoc, callback); // The callback is triggered when the listening target has a value or target is replaced (This is useful when you are not sure if the data under the destination path exists)
// cancel the listener when component unmount
cancel();
  1. toSyncElement
ymap.set('content', toSyncElement(data)); // set data to ydoc (Depth conversion of this data,if the data contains raw, it will be converted to ydoc raw)

develop

  1. start websoket-server
npm run start:server
  1. start demo
npm run dev