/prosemirror-firebase

ProseMirror + Firebase Realtime Database

Primary LanguageJavaScriptMIT LicenseMIT

prosemirror-firebase

Collaborative editing for ProseMirror using Firebase's Realtime Database. Features timestamped and attributed changes, version checkpointing, selection tracking, and more.

usage

Assumes familiarity with ProseMirror and Firebase.

Call new FirebaseEditor with an object containing:

Returns a Promise that resolves when the editor is ready (when existing content has been loaded and an editor view has been created). The resolved value is an object containing the properties and methods of the editor:

  • view (see above)
  • selections (see above)
  • destroy Function that removes all database listeners that were added, removes the client's selection from the database, and calls editorView.destroy

basic

new FirebaseEditor({
    firebaseRef: /*database.Reference*/,
    stateConfig: {
        schema: /*Schema*/,
    },
    view({ stateConfig, updateCollab }) {
        let view = new EditorView(/*dom.Node*/, {
            state: EditorState.create(stateConfig),
            dispatchTransaction(transaction) {
                let newState = view.state.apply(transaction)
                view.updateState(newState)
                updateCollab(transaction, newState)
            },
        })
        return view
    },
})

cursors and selections

function stringToColor(string, alpha = 1) {
    let hue = string.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0) % 360
    return `hsla(${hue}, 100%, 50%, ${alpha})`
}

new FirebaseEditor({
    /*...*/
    view({ stateConfig, updateCollab, selections }) {
        let view = new EditorView(/*dom.Node*/, {
            /*...*/
            decorations({ doc }) {
                return DecorationSet.create(doc, Object.entries(selections).map(
                    function ([ clientID, { from, to } ]) {
                        if (from === to) {
                            let elem = document.createElement('span')
                            elem.style.borderLeft = `1px solid ${stringToColor(clientID)}`
                            return Decoration.widget(from, elem)
                        } else {
                            return Decoration.inline(from, to, {
                                style: `background-color: ${stringToColor(clientID, 0.2)};`,
                            })
                        }
                    }
                ))                  
            },
        })
        return view
    },
})

ready

new FirebaseEditor({
    /*...*/
}).then(({ view, selections, destroy }) => {
    console.log('editor ready')
}).catch(console.error)

license

MIT license.