UNPKG

@blocknote/core

Version:

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

97 lines (87 loc) 3.15 kB
import { Fragment, NodeType, Slice } from "prosemirror-model"; import { EditorState, Transaction } from "prosemirror-state"; import { ReplaceAroundStep } from "prosemirror-transform"; import { BlockNoteEditor } from "../../../../editor/BlockNoteEditor.js"; import { getBlockInfoFromTransaction } from "../../../getBlockInfoFromPos.js"; // TODO: Unit tests /** * This is a modified version of https://github.com/ProseMirror/prosemirror-schema-list/blob/569c2770cbb8092d8f11ea53ecf78cb7a4e8f15a/src/schema-list.ts#L232 * * The original function derives too many information from the parentnode and itemtype */ function sinkListItem(itemType: NodeType, groupType: NodeType) { return function (state: EditorState, dispatch?: (tr: Transaction) => void) { const { $from, $to } = state.selection; const range = $from.blockRange( $to, (node) => node.childCount > 0 && (node.type.name === "blockGroup" || node.type.name === "column"), // change necessary to not look at first item child type ); if (!range) { return false; } const startIndex = range.startIndex; if (startIndex === 0) { return false; } const parent = range.parent; const nodeBefore = parent.child(startIndex - 1); if (nodeBefore.type !== itemType) { return false; } if (dispatch) { const nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type === groupType; // change necessary to check groupType instead of parent.type const inner = Fragment.from(nestedBefore ? itemType.create() : null); const slice = new Slice( Fragment.from( itemType.create(null, Fragment.from(groupType.create(null, inner))), // change necessary to create "groupType" instead of parent.type ), nestedBefore ? 3 : 1, 0, ); const before = range.start; const after = range.end; dispatch( state.tr .step( new ReplaceAroundStep( before - (nestedBefore ? 3 : 1), after, before, after, slice, 1, true, ), ) .scrollIntoView(), ); } return true; }; } export function nestBlock(editor: BlockNoteEditor<any, any, any>) { return editor.exec((state, dispatch) => sinkListItem( state.schema.nodes["blockContainer"], state.schema.nodes["blockGroup"], )(state, dispatch), ); } export function unnestBlock(editor: BlockNoteEditor<any, any, any>) { editor._tiptapEditor.commands.liftListItem("blockContainer"); } export function canNestBlock(editor: BlockNoteEditor<any, any, any>) { return editor.transact((tr) => { const { bnBlock: blockContainer } = getBlockInfoFromTransaction(tr); return tr.doc.resolve(blockContainer.beforePos).nodeBefore !== null; }); } export function canUnnestBlock(editor: BlockNoteEditor<any, any, any>) { return editor.transact((tr) => { const { bnBlock: blockContainer } = getBlockInfoFromTransaction(tr); return tr.doc.resolve(blockContainer.beforePos).depth > 1; }); }