Whiteboard doc frames
A doc frame is a shape that shows a rich text document, in an editor of its own. The board stores the frame and the id of its document. Your app keeps the documents, so the board stays free of any storage.
Provide the documents
Tell the board where the documents live with the docs option of WhiteboardKit. Without it, doc frames show no document. This example keeps the documents in memory:
import { Editor } from '@tiptap/core'
import { StarterKit } from '@tiptap/starter-kit'
import { WhiteboardKit } from '@tiptap-pro/extension-whiteboard'
// Document JSON by document id
const documents = new Map()
const editor = new Editor({
element: viewport,
extensions: [
WhiteboardKit.configure({
docs: {
createEditor: ({ element, docId }) =>
new Editor({
element,
extensions: [StarterKit],
content: documents.get(docId),
onUpdate: ({ editor: docEditor }) => documents.set(docId, docEditor.getJSON()),
}),
copy: ({ fromDocId, toDocId }) => {
const document = documents.get(fromDocId)
if (document) {
documents.set(toDocId, structuredClone(document))
}
},
load: ({ docId, content }) => documents.set(docId, content),
},
}),
],
})In React, create the configured kit once, outside the component or with useMemo. See Set up a board in React.
The docs object has these members:
createEditor({ element, docId })creates the editor of a document insideelement, the frame's content element,[data-shape-content]. The board destroys the editor with its frame. The editor can have any extensions, likeStarterKit.copy({ fromDocId, toDocId })copies a document for the copy of its frame, when a user duplicates or pastes the frame. It runs before the copy shows.load({ docId, content }), which is optional, fills the new document of a frame thatimportBoardadds from a board file. It runs before the frame shows. Without it, imported frames start empty.start(docId), which is optional, starts the document of every frame that a change in this editor brings onto the board: a new frame, a copy, and also frames thatsetContentor an undo withUndoRedobring back. It runs before the frame shows. Frames that come through Yjs, from other editors or from its undo, don't start a document here.
So start also runs for frames whose document exists already, like a copy, after copy. Keep a document that the frame already has:
start: (docId) => {
if (!documents.has(docId)) {
documents.set(docId, { type: 'doc', content: [{ type: 'paragraph' }] })
}
},Add a doc frame
The doc tool, key W, adds a frame with a new document and starts editing it. Without docs, the board has no doc tool. In code, add a frame like any other shape:
import { createShapeId } from '@tiptap-pro/extension-whiteboard'
editor.commands.insertShape({ type: 'docFrame', docId: createShapeId(), x: 0, y: 0, width: 420 })The frame shows the document with the id in docId. A frame without a docId uses its own shape id, so it still gets a document of its own. docIdOf returns the id of a frame's document, from its node or the node's JSON.
Edit a document
The board selects, moves and resizes a frame like other shapes. Its height follows its document. The editor of the document is read-only until editing starts: with a click on the selected frame, with Enter, or with editShapeText:
editor.commands.editShapeText({ id: 'frame' })While the document is edited, presses and keys inside the frame belong to its editor. Escape stops editing and selects the frame again. A press on another shape stops editing, too, and so does making the board read-only.
Format the edited document
Your toolbar can work on the edited document and on the text of shapes alike. getEditedDocEditor returns the editor of the document that a frame edits, or null while none is edited:
import { getEditedDocEditor } from '@tiptap-pro/extension-whiteboard'
// A bold button that works in documents too
const textEditor = getEditedDocEditor(editor) ?? editor
textEditor.commands.toggleBold()The board's editor emits docChange on every transaction of a document's editor, also one that only moves the caret, when its editing starts or stops, and when its editor is created or destroyed. Use it to update the toolbar, but not to save documents:
editor.on('docChange', ({ docId }) => {
console.log(`The document ${docId} changed`)
})Read all documents
getDocEditors returns the editors of the documents that the frames on the board show:
import { getDocEditors } from '@tiptap-pro/extension-whiteboard'
const contents = getDocEditors(editor).map((docEditor) => docEditor.getJSON())It returns no editors once the board's editor is destroyed.
Documents that arrive later
A frame grows as tall as its document after each change in this editor. Other users' editors measure the changes that they make. When the document arrives after the frame, like when an AI adds a frame and writes its document later, keep the height that you gave the frame with keepShapeHeights:
import { keepShapeHeights } from '@tiptap-pro/extension-whiteboard'
const { tr } = editor.state
// … add a doc frame whose document arrives later
editor.view.dispatch(keepShapeHeights(tr))Next steps
- Edit a board together with collaboration
- Save a board with its documents to a file with export and import
- See the
BoardDocstype