- Notifications
You must be signed in to change notification settings - Fork17
Quill Editor binding for Yjs
License
yjs/y-quill
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Quill Editor binding forYjs -Demo
This binding maps a Y.Text to a Quill instance. It optionally supports shared cursors viathequill-cursors module.
import{QuillBinding}from'y-quill'importQuillfrom'quill'importQuillCursorsfrom'quill-cursors'..Quill.register('modules/cursors',QuillCursors)consttype=ydoc.getText('quill')vareditor=newQuill('#editor-container',{modules:{cursors:true,toolbar:[[{header:[1,2,false]}],['bold','italic','underline'],['image','code-block']]},placeholder:'Start collaborating...',theme:'snow'// or 'bubble'})// Optionally specify an Awareness instance, if supported by the Providerconstbinding=newQuillBinding(type,editor,provider.awareness)/*// Define user name and user name// Check the quill-cursors package on how to change the way cursors are renderedprovider.awareness.setLocalStateField('user', { name: 'Typing Jimmy', color: 'blue'})*/
Also lookhere for a working example.
The Delta format supports "custom embeds", a feature for embedding customdata using custom data models. This feature is currently not well documented, butit enables us to create good data models for tables and other complex widgets(like drawing widgets) that can't be well represented using text+formatting.
y-quill supports custom embeds. However, you need teach y-quill how to translatethe custom deltas to Yjs transformations and how to translate Yjstransformations to custom deltas.
This package already ships with a couple of custom deltas that might be useful.
Notes:
- You need to register a "Blot" to render those embeds! This TableEmbed Blot might beavailable in a third-party package or you can build it yourself.
- The Quill project currently doesn't talk a lot about this feature. This mightmean that it is unstable, or subject to future changes. y-quill will try tokeep track of the changes.
import{tableEmbed}from'y-quill/embeds/table-embed'importTableEmbedfrom'quill/modules/tableEmbed.js'TableEmbed.register()constembeds={'table-embed':tableEmbed}constbinding=newQuillBinding(type,editor,provider.awareness,{ embeds})
The QuillBinding accepts aembeds
option that is used as a lookup table fortransforming custom embed operations to Yjs operations and back.
Here is an example for building adelta
embed that simply nests another deltain a custom embed:
importDeltafrom'quill-delta'// Register a custom `delta` embed in `quill-embed` module.Delta.registerEmbed('delta',{compose:(a,b)=>newDelta(a).compose(newDelta(b)).ops,transform:(a,b,priority)=>newDelta(a).transform(newDelta(b),priority).ops,invert:(a,b)=>newDelta(a).invert(newDelta(b)).ops})/** * This object is used to translate between quill-deltas and Yjs * transformations. */constembeds={/** * The `delta` embed might be useful to render another editor (e.g. quill, or * a code editor like codemirror) inside of a quill instance. */delta:{/** * A custom embed is always represented as a Y.XmlElement, because it is a * very versatile shared type and the only one that can be "named" ( * `yxml.nodeName` will be the name of the custom embed). You must represent * your data using a Y.XmlElement. However, you may embed other Yjs types inside the Y.XmlElement. * * The `update` function is called whenever the quill editor changes the * custom embed (i.e. `[ retain: { delta: op} ]`, where `op` is the second * parameter of `update`). * * The `update` function is also called when the embed is created so we may * initialize some content here if needed. * *@param {Y.XmlElement<{ ytext: Y.Text }>} yxml *@param {DeltaOp} op */update:(yxml,op)=>{if(!yxml.hasAttribute('ytext')){// The "delta" will be represented as a Y.Text, which we will maintain// on the "ytext" propertyyxml.setAttribute('ytext',newY.Text())}constytext=yxml.getAttribute('ytext')ytext?.applyDelta(op)},/** * Translate Yjs events to a delta embed event. * In this case, Y.Text (the child of yxml) already emits a quill-compatible * delta event that we can simply return. * *@param {Y.XmlElement} yxml *@param {Array<Y.YEvent<any>>} events *@return {DeltaOps} */eventsToDelta:(yxml,events)=>{constytext=yxml.getAttribute('ytext')constytextevent=events.find(event=>event.target===ytext)if(ytextevent){return/**@type {any} */(ytextevent.delta)}return[]},/** * Translate the Y.XmlElement to a custom embed delta. * In this case, we can simply return the delta representation of the * Y.Text. */typeToDelta:(yxml)=>{returnyxml.getAttribute('ytext').toDelta()}}}constbinding=newQuillBinding(type,editor,provider.awareness,{ embeds})
The MIT License © Kevin Jahns