diff --git a/docs/pages/docs/editor-basics/setup.mdx b/docs/pages/docs/editor-basics/setup.mdx index 84115ddd5f..9fe3894b92 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 5b8d7003d0..07abe865cf 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 e6412633c4..6ac54bd342 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 8ebf62aa63..0edbd9f2d1 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 4e271bae14..6ead645d00 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 fe68128033..fcd63e59ae 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 1ae4c4101a..3255953782 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, }), ]; }),