UNPKG

@blocknote/core

Version:

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

87 lines (70 loc) 3.02 kB
import { Plugin, PluginKey } from "prosemirror-state"; import { getBlockInfo } from "../../../api/getBlockInfoFromPos.js"; // ProseMirror Plugin which automatically assigns indices to ordered list items per nesting level. const PLUGIN_KEY = new PluginKey(`numbered-list-indexing`); export const NumberedListIndexingPlugin = () => { return new Plugin({ key: PLUGIN_KEY, appendTransaction: (_transactions, _oldState, newState) => { const tr = newState.tr; tr.setMeta("numberedListIndexing", true); let modified = false; // Traverses each node the doc using DFS, so blocks which are on the same nesting level will be traversed in the // same order they appear. This means the index of each list item block can be calculated by incrementing the // index of the previous list item block. newState.doc.descendants((node, pos) => { if ( node.type.name === "blockContainer" && node.firstChild!.type.name === "numberedListItem" ) { let newIndex = `${node.firstChild!.attrs["start"] || 1}`; const blockInfo = getBlockInfo({ posBeforeNode: pos, node, }); if (!blockInfo.isBlockContainer) { throw new Error("impossible"); } // Checks if this block is the start of a new ordered list, i.e. if it's the first block in the document, the // first block in its nesting level, or the previous block is not an ordered list item. const prevBlock = tr.doc.resolve( blockInfo.bnBlock.beforePos, ).nodeBefore; if (prevBlock) { const prevBlockInfo = getBlockInfo({ posBeforeNode: blockInfo.bnBlock.beforePos - prevBlock.nodeSize, node: prevBlock, }); const isPrevBlockOrderedListItem = prevBlockInfo.blockNoteType === "numberedListItem"; if (isPrevBlockOrderedListItem) { if (!prevBlockInfo.isBlockContainer) { throw new Error("impossible"); } const prevBlockIndex = prevBlockInfo.blockContent.node.attrs["index"]; newIndex = (parseInt(prevBlockIndex) + 1).toString(); } } const contentNode = blockInfo.blockContent.node; const index = contentNode.attrs["index"]; const isFirst = prevBlock?.firstChild?.type.name !== "numberedListItem"; if (index !== newIndex || (contentNode.attrs.start && !isFirst)) { modified = true; const { start, ...attrs } = contentNode.attrs; tr.setNodeMarkup(blockInfo.blockContent.beforePos, undefined, { ...attrs, index: newIndex, ...(typeof start === "number" && isFirst && { start, }), }); } } }); return modified ? tr : null; }, }); };