UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

86 lines (71 loc) 2.83 kB
import { Extension } from "@tiptap/core"; import { Plugin, PluginKey } from "prosemirror-state"; // based on https://github.com/ueberdosis/tiptap/blob/40a9404c94c7fef7900610c195536384781ae101/demos/src/Experiments/TrailingNode/Vue/trailing-node.ts /** * Extension based on: * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts */ export interface TrailingNodeOptions { node: string; } /** * Add a trailing node to the document so the user can always click at the bottom of the document and start typing */ export const TrailingNode = Extension.create<TrailingNodeOptions>({ name: "trailingNode", addProseMirrorPlugins() { const plugin = new PluginKey(this.name); // const disabledNodes = Object.entries(this.editor.schema.nodes) // .map(([, value]) => value) // .filter((node) => this.options.notAfter.includes(node.name)); return [ new Plugin({ key: plugin, appendTransaction: (_, __, state) => { const { doc, tr, schema } = state; const shouldInsertNodeAtEnd = plugin.getState(state); const endPosition = doc.content.size - 2; const type = schema.nodes["blockContainer"]; const contentType = schema.nodes["paragraph"]; if (!shouldInsertNodeAtEnd) { return; } return tr.insert( endPosition, type.create(undefined, contentType.create()), ); }, state: { init: (_, _state) => { // (maybe fix): use same logic as apply() here // so it works when initializing }, apply: (tr, value) => { if (!tr.docChanged) { return value; } let lastNode = tr.doc.lastChild; if (!lastNode || lastNode.type.name !== "blockGroup") { throw new Error("Expected blockGroup"); } lastNode = lastNode.lastChild; if (!lastNode || lastNode.type.name !== "blockContainer") { return true; // not a blockContainer, but for example Columns. Insert trailing node } const lastContentNode = lastNode.firstChild; if (!lastContentNode) { throw new Error("Expected blockContent"); } // If last node is not empty (size > 4) or it doesn't contain // inline content, we need to add a trailing node. return ( lastNode.nodeSize > 4 || lastContentNode.type.spec.content !== "inline*" ); }, }, }), ]; }, });