diff --git a/packages/core/src/BlockNoteEditor.ts b/packages/core/src/BlockNoteEditor.ts index 84ddb8d74..d059a5cbe 100644 --- a/packages/core/src/BlockNoteEditor.ts +++ b/packages/core/src/BlockNoteEditor.ts @@ -91,6 +91,10 @@ export type BlockNoteEditorOptions = { * Locks the editor from being editable by the user if set to `false`. */ editable: boolean; + /** + * Disables block nesting by the user if set to `false`. + */ + enableNestedBlocks: boolean; /** * The content that should be in the editor when it's created, represented as an array of partial block objects. */ @@ -154,6 +158,7 @@ export class BlockNoteEditor { public blockCache = new WeakMap>(); public readonly schema: BSchema; public ready = false; + public enableNestedBlocks = true; public readonly sideMenu: SideMenuProsemirrorPlugin; public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin; @@ -190,6 +195,10 @@ export class BlockNoteEditor { ); this.hyperlinkToolbar = new HyperlinkToolbarProsemirrorPlugin(this); this.imageToolbar = new ImageToolbarProsemirrorPlugin(this); + this.enableNestedBlocks = + options.enableNestedBlocks !== undefined + ? options.enableNestedBlocks + : true; const extensions = getBlockNoteExtensions({ editor: this, @@ -755,6 +764,10 @@ export class BlockNoteEditor { * Checks if the block containing the text cursor can be nested. */ public canNestBlock() { + if (!this.enableNestedBlocks) { + return false; + } + const { startPos, depth } = getBlockInfoFromPos( this._tiptapEditor.state.doc, this._tiptapEditor.state.selection.from @@ -774,6 +787,10 @@ export class BlockNoteEditor { * Checks if the block containing the text cursor is nested. */ public canUnnestBlock() { + if (!this.enableNestedBlocks) { + return false; + } + const { depth } = getBlockInfoFromPos( this._tiptapEditor.state.doc, this._tiptapEditor.state.selection.from diff --git a/packages/core/src/BlockNoteExtensions.ts b/packages/core/src/BlockNoteExtensions.ts index d328c329b..c2d93f746 100644 --- a/packages/core/src/BlockNoteExtensions.ts +++ b/packages/core/src/BlockNoteExtensions.ts @@ -94,6 +94,7 @@ export const getBlockNoteExtensions = (opts: { Doc, BlockContainer.configure({ domAttributes: opts.domAttributes, + enableNestedBlocks: opts.editor.enableNestedBlocks, }), BlockGroup.configure({ domAttributes: opts.domAttributes, diff --git a/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts b/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts index 2265668ed..080fba6df 100644 --- a/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts +++ b/packages/core/src/extensions/Blocks/nodes/BlockContainer.ts @@ -42,11 +42,18 @@ declare module "@tiptap/core" { */ export const BlockContainer = Node.create<{ domAttributes?: BlockNoteDOMAttributes; + enableNestedBlocks?: boolean; }>({ name: "blockContainer", group: "blockContainer", // A block always contains content, and optionally a blockGroup which contains nested blocks - content: "blockContent blockGroup?", + content() { + if (!this.options.enableNestedBlocks) { + return "blockContent"; + } + + return "blockContent blockGroup?"; + }, // Ensures content-specific keyboard handlers trigger first. priority: 50, defining: true, @@ -621,11 +628,17 @@ export const BlockContainer = Node.create<{ // Always returning true for tab key presses ensures they're not captured by the browser. Otherwise, they blur the // editor since the browser will try to use tab for keyboard navigation. Tab: () => { - this.editor.commands.sinkListItem("blockContainer"); + if (this.options.enableNestedBlocks) { + this.editor.commands.sinkListItem("blockContainer"); + } + return true; }, "Shift-Tab": () => { - this.editor.commands.liftListItem("blockContainer"); + if (this.options.enableNestedBlocks) { + this.editor.commands.liftListItem("blockContainer"); + } + return true; }, "Mod-Alt-0": () =>