From c7131826d2d4d65b916cee7da6f20f1195fed47e Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Mon, 30 Jun 2025 14:40:50 +0200 Subject: [PATCH] Added editor option to scope input rules --- docs/pages/docs/editor-basics/setup.mdx | 5 ++++- .../HeadingBlockContent/HeadingBlockContent.ts | 5 ++++- .../BulletListItemBlockContent.ts | 5 ++++- .../CheckListItemBlockContent.ts | 10 ++++++++-- .../NumberedListItemBlockContent.ts | 4 +++- packages/core/src/editor/BlockNoteEditor.ts | 12 ++++++++++++ packages/core/src/editor/BlockNoteExtensions.ts | 2 ++ 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/docs/pages/docs/editor-basics/setup.mdx b/docs/pages/docs/editor-basics/setup.mdx index 84115ddd5..9fe3894b9 100644 --- a/docs/pages/docs/editor-basics/setup.mdx +++ b/docs/pages/docs/editor-basics/setup.mdx @@ -36,6 +36,7 @@ type BlockNoteEditorOptions = { levels?: number[]; }; initialContent?: PartialBlock[]; + inputRules: "allBlocks" | "paragraphs" | "none"; pasteHandler?: (context: { event: ClipboardEvent; editor: BlockNoteEditor; @@ -43,7 +44,7 @@ type BlockNoteEditorOptions = { pasteBehavior?: "prefer-markdown" | "prefer-html"; }) => boolean | undefined; }) => boolean | undefined; - resolveFileUrl: (url: string) => Promise + resolveFileUrl: (url: string) => Promise; schema?: BlockNoteSchema; setIdAttribute?: boolean; sideMenuDetection?: "viewport" | "editor"; @@ -76,6 +77,8 @@ The hook takes two optional parameters: `initialContent:` The content that should be in the editor when it's created, represented as an array of [Partial Blocks](/docs/manipulating-blocks#partial-blocks). +`inputRules`: Configures when input rules should be active. Input rules change the block type when a string is typed at the start of the block. For example, typing "# " changes the block to a heading level 1 and "- " changes it to a bullet list item. "allBlocks" means the input rules are active regardless of the initial block type, "paragraphs" means the input rules are only active within paragraph blocks, and "none" means the input rules are never active. Defaults to "allBlocks". + `heading`: Configuration for headings. Allows you to configure the number of levels of headings that should be available in the editor. Defaults to `[1, 2, 3]`. Configurable up to 6 levels of headings. `pasteHandler`: A function that can be used to override the default paste behavior. See [Paste Handling](/docs/advanced/paste-handling) for more. diff --git a/packages/core/src/blocks/HeadingBlockContent/HeadingBlockContent.ts b/packages/core/src/blocks/HeadingBlockContent/HeadingBlockContent.ts index 5b8d7003d..07abe865c 100644 --- a/packages/core/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +++ b/packages/core/src/blocks/HeadingBlockContent/HeadingBlockContent.ts @@ -41,7 +41,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({ const blockInfo = getBlockInfoFromSelection(state); if ( !blockInfo.isBlockContainer || - blockInfo.blockContent.node.type.spec.content !== "inline*" + blockInfo.blockContent.node.type.spec.content !== "inline*" || + this.options.inputRules === "none" || + (this.options.inputRules === "paragraphs" && + blockInfo.blockNoteType !== "paragraph") ) { return; } diff --git a/packages/core/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts b/packages/core/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts index e6412633c..6ac54bd34 100644 --- a/packages/core/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +++ b/packages/core/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts @@ -31,7 +31,10 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({ const blockInfo = getBlockInfoFromSelection(state); if ( !blockInfo.isBlockContainer || - blockInfo.blockContent.node.type.spec.content !== "inline*" + blockInfo.blockContent.node.type.spec.content !== "inline*" || + this.options.inputRules === "none" || + (this.options.inputRules === "paragraphs" && + blockInfo.blockNoteType !== "paragraph") ) { return; } diff --git a/packages/core/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts b/packages/core/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts index 8ebf62aa6..0edbd9f2d 100644 --- a/packages/core/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +++ b/packages/core/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts @@ -40,7 +40,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({ const blockInfo = getBlockInfoFromSelection(state); if ( !blockInfo.isBlockContainer || - blockInfo.blockContent.node.type.spec.content !== "inline*" + blockInfo.blockContent.node.type.spec.content !== "inline*" || + this.options.inputRules === "none" || + (this.options.inputRules === "paragraphs" && + blockInfo.blockNoteType !== "paragraph") ) { return; } @@ -65,7 +68,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({ if ( !blockInfo.isBlockContainer || - blockInfo.blockContent.node.type.spec.content !== "inline*" + blockInfo.blockContent.node.type.spec.content !== "inline*" || + this.options.inputRules === "none" || + (this.options.inputRules === "paragraphs" && + blockInfo.blockNoteType !== "paragraph") ) { return; } diff --git a/packages/core/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts b/packages/core/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts index 4e271bae1..6ead645d0 100644 --- a/packages/core/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +++ b/packages/core/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts @@ -50,7 +50,9 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({ if ( !blockInfo.isBlockContainer || blockInfo.blockContent.node.type.spec.content !== "inline*" || - blockInfo.blockNoteType === "numberedListItem" + this.options.inputRules === "none" || + (this.options.inputRules === "paragraphs" && + blockInfo.blockNoteType !== "paragraph") ) { return; } diff --git a/packages/core/src/editor/BlockNoteEditor.ts b/packages/core/src/editor/BlockNoteEditor.ts index fe6812803..fcd63e59a 100644 --- a/packages/core/src/editor/BlockNoteEditor.ts +++ b/packages/core/src/editor/BlockNoteEditor.ts @@ -250,6 +250,17 @@ export type BlockNoteEditorOptions< NoInfer >[]; + /** + * Configures when input rules should be active. Input rules change the block + * type when a string is typed at the start of the block. For example, typing + * "# " changes the block to a heading level 1 and "- " changes it to a + * bullet list item. "allBlocks" means the input rules are active regardless + * of the initial block type, "paragraphs" means the input rules are only + * active within paragraph blocks, and "none" means the input rules are never + * active. Defaults to "allBlocks". + */ + inputRules: "allBlocks" | "paragraphs" | "none"; + /** * @deprecated, provide placeholders via dictionary instead */ @@ -642,6 +653,7 @@ export class BlockNoteEditor< sideMenuDetection: newOptions.sideMenuDetection || "viewport", comments: newOptions.comments, pasteHandler: newOptions.pasteHandler, + inputRules: newOptions.inputRules || "allBlocks", }); // add extensions from _tiptapOptions diff --git a/packages/core/src/editor/BlockNoteExtensions.ts b/packages/core/src/editor/BlockNoteExtensions.ts index 1ae4c4101..325595378 100644 --- a/packages/core/src/editor/BlockNoteExtensions.ts +++ b/packages/core/src/editor/BlockNoteExtensions.ts @@ -95,6 +95,7 @@ type ExtensionOptions< threadStore: ThreadStore; }; pasteHandler: BlockNoteEditorOptions["pasteHandler"]; + inputRules: "allBlocks" | "paragraphs" | "none"; }; /** @@ -286,6 +287,7 @@ const getTipTapExtensions = < blockSpec.implementation.node.configure({ editor: opts.editor, domAttributes: opts.domAttributes, + inputRules: opts.inputRules, }), ]; }),