Skip to content

Commit 9431c32

Browse files
committed
feat: make headings configurable up to 6 levels
1 parent 13a4202 commit 9431c32

File tree

24 files changed

+202
-186
lines changed

24 files changed

+202
-186
lines changed

docs/pages/docs/editor-basics/setup.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ type BlockNoteEditorOptions = {
3232
width?: number;
3333
class?: string;
3434
}) => Plugin;
35+
heading?: {
36+
levels?: number[];
37+
};
3538
initialContent?: PartialBlock[];
3639
pasteHandler?: (context: {
3740
event: ClipboardEvent;
@@ -73,6 +76,8 @@ The hook takes two optional parameters:
7376

7477
`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).
7578

79+
`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.
80+
7681
`pasteHandler`: A function that can be used to override the default paste behavior. See [Paste Handling](/docs/advanced/paste-handling) for more.
7782

7883
`resolveFileUrl:` Function to resolve file URLs for display/download. Useful for creating authenticated URLs or implementing custom protocols.

packages/core/src/blocks/HeadingBlockContent/HeadingBlockContent.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
1212
import { defaultProps } from "../defaultProps.js";
1313
import { createToggleWrapper } from "../ToggleWrapper/createToggleWrapper.js";
14+
import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
1415

1516
const HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;
1617

@@ -30,8 +31,9 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
3031
},
3132

3233
addInputRules() {
34+
const editor = this.options.editor as BlockNoteEditor<any, any, any>;
3335
return [
34-
...HEADING_LEVELS.map((level) => {
36+
...editor.settings.heading.levels.map((level) => {
3537
// Creates a heading of appropriate level when starting with "#", "##", or "###".
3638
return new InputRule({
3739
find: new RegExp(`^(#{${level}})\\s$`),
@@ -63,8 +65,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
6365
},
6466

6567
addKeyboardShortcuts() {
68+
const editor = this.options.editor as BlockNoteEditor<any, any, any>;
69+
6670
return Object.fromEntries(
67-
HEADING_LEVELS.map((level) => [
71+
editor.settings.heading.levels.map((level) => [
6872
`Mod-Alt-${level}`,
6973
() => {
7074
const blockInfo = getBlockInfoFromSelection(this.editor.state);
@@ -88,13 +92,15 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
8892
);
8993
},
9094
parseHTML() {
95+
const editor = this.options.editor as BlockNoteEditor<any, any, any>;
96+
9197
return [
9298
// Parse from internal HTML.
9399
{
94100
tag: "div[data-content-type=" + this.name + "]",
95101
contentElement: ".bn-inline-content",
96102
},
97-
...HEADING_LEVELS.map((level) => ({
103+
...editor.settings.heading.levels.map((level) => ({
98104
tag: `h${level}`,
99105
attrs: { level },
100106
node: "heading",

packages/core/src/editor/BlockNoteEditor.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,18 @@ export type BlockNoteEditorOptions<
229229
class?: string;
230230
}) => Plugin;
231231

232+
/**
233+
* Configuration for headings
234+
*/
235+
heading?: {
236+
/**
237+
* The levels of headings that should be available in the editor.
238+
* @note Configurable up to 6 levels of headings.
239+
* @default [1, 2, 3]
240+
*/
241+
levels?: (1 | 2 | 3 | 4 | 5 | 6)[];
242+
};
243+
232244
/**
233245
* The content that should be in the editor when it's created, represented as an array of partial block objects.
234246
*/
@@ -527,6 +539,9 @@ export class BlockNoteEditor<
527539
headers: boolean;
528540
};
529541
codeBlock: CodeBlockOptions;
542+
heading: {
543+
levels: (1 | 2 | 3 | 4 | 5 | 6)[];
544+
};
530545
};
531546

532547
public static create<
@@ -580,6 +595,9 @@ export class BlockNoteEditor<
580595
supportedLanguages: options?.codeBlock?.supportedLanguages ?? {},
581596
createHighlighter: options?.codeBlock?.createHighlighter ?? undefined,
582597
},
598+
heading: {
599+
levels: options?.heading?.levels ?? [1, 2, 3],
600+
},
583601
};
584602

585603
// apply defaults

packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -122,70 +122,6 @@ export function getDefaultSlashMenuItems<
122122
key: "heading_3",
123123
...editor.dictionary.slash_menu.heading_3,
124124
},
125-
{
126-
onItemClick: () => {
127-
insertOrUpdateBlock(editor, {
128-
type: "heading",
129-
props: { level: 4 },
130-
});
131-
},
132-
badge: formatKeyboardShortcut("Mod-Alt-4"),
133-
key: "heading_4",
134-
...editor.dictionary.slash_menu.heading_4,
135-
},
136-
{
137-
onItemClick: () => {
138-
insertOrUpdateBlock(editor, {
139-
type: "heading",
140-
props: { level: 5 },
141-
});
142-
},
143-
badge: formatKeyboardShortcut("Mod-Alt-5"),
144-
key: "heading_5",
145-
...editor.dictionary.slash_menu.heading_5,
146-
},
147-
{
148-
onItemClick: () => {
149-
insertOrUpdateBlock(editor, {
150-
type: "heading",
151-
props: { level: 6 },
152-
});
153-
},
154-
badge: formatKeyboardShortcut("Mod-Alt-6"),
155-
key: "heading_6",
156-
...editor.dictionary.slash_menu.heading_6,
157-
},
158-
{
159-
onItemClick: () => {
160-
insertOrUpdateBlock(editor, {
161-
type: "heading",
162-
props: { level: 1, isToggleable: true },
163-
});
164-
},
165-
key: "toggle_heading",
166-
...editor.dictionary.slash_menu.toggle_heading,
167-
},
168-
{
169-
onItemClick: () => {
170-
insertOrUpdateBlock(editor, {
171-
type: "heading",
172-
props: { level: 2, isToggleable: true },
173-
});
174-
},
175-
176-
key: "toggle_heading_2",
177-
...editor.dictionary.slash_menu.toggle_heading_2,
178-
},
179-
{
180-
onItemClick: () => {
181-
insertOrUpdateBlock(editor, {
182-
type: "heading",
183-
props: { level: 3, isToggleable: true },
184-
});
185-
},
186-
key: "toggle_heading_3",
187-
...editor.dictionary.slash_menu.toggle_heading_3,
188-
},
189125
);
190126
}
191127

@@ -379,6 +315,57 @@ export function getDefaultSlashMenuItems<
379315
});
380316
}
381317

318+
if (checkDefaultBlockTypeInSchema("heading", editor)) {
319+
items.push(
320+
{
321+
onItemClick: () => {
322+
insertOrUpdateBlock(editor, {
323+
type: "heading",
324+
props: { level: 1, isToggleable: true },
325+
});
326+
},
327+
key: "toggle_heading",
328+
...editor.dictionary.slash_menu.toggle_heading,
329+
},
330+
{
331+
onItemClick: () => {
332+
insertOrUpdateBlock(editor, {
333+
type: "heading",
334+
props: { level: 2, isToggleable: true },
335+
});
336+
},
337+
338+
key: "toggle_heading_2",
339+
...editor.dictionary.slash_menu.toggle_heading_2,
340+
},
341+
{
342+
onItemClick: () => {
343+
insertOrUpdateBlock(editor, {
344+
type: "heading",
345+
props: { level: 3, isToggleable: true },
346+
});
347+
},
348+
key: "toggle_heading_3",
349+
...editor.dictionary.slash_menu.toggle_heading_3,
350+
},
351+
);
352+
353+
editor.settings.heading.levels
354+
.filter((level): level is 4 | 5 | 6 => level > 3)
355+
.forEach((level) => {
356+
items.push({
357+
onItemClick: () => {
358+
insertOrUpdateBlock(editor, {
359+
type: "heading",
360+
props: { level: level },
361+
});
362+
},
363+
key: `heading_${level}`,
364+
...editor.dictionary.slash_menu[`heading_${level}`],
365+
});
366+
});
367+
}
368+
382369
items.push({
383370
onItemClick: () => {
384371
editor.openSuggestionMenu(":", {

packages/core/src/i18n/locales/ar.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,37 @@ export const ar: Dictionary = {
2424
title: "عنوان 4",
2525
subtext: "عنوان فرعي ثانوي صغير",
2626
aliases: ["ع4", "عنوان4", "عنوان فرعي صغير"],
27-
group: "العناوين",
27+
group: "العناوين الفرعية",
2828
},
2929
heading_5: {
3030
title: "عنوان 5",
3131
subtext: "عنوان فرعي صغير",
3232
aliases: ["ع5", "عنوان5", "عنوان فرعي صغير"],
33-
group: "العناوين",
33+
group: "العناوين الفرعية",
3434
},
3535
heading_6: {
3636
title: "عنوان 6",
3737
subtext: "أدنى مستوى للعناوين",
3838
aliases: ["ع6", "عنوان6", "العنوان الفرعي الأدنى"],
39-
group: "العناوين",
39+
group: "العناوين الفرعية",
4040
},
4141
toggle_heading: {
4242
title: "عنوان قابل للطي 1",
4343
subtext: "عنوان قابل للطي لإظهار وإخفاء المحتوى",
4444
aliases: ["ع", "عنوان1", "ع1", "قابل للطي", "طي"],
45-
group: "العناوين",
45+
group: "العناوين الفرعية",
4646
},
4747
toggle_heading_2: {
4848
title: "عنوان قابل للطي 2",
4949
subtext: "عنوان فرعي قابل للطي لإظهار وإخفاء المحتوى",
5050
aliases: ["ع2", "عنوان2", "عنوان فرعي", "قابل للطي", "طي"],
51-
group: "العناوين",
51+
group: "العناوين الفرعية",
5252
},
5353
toggle_heading_3: {
5454
title: "عنوان قابل للطي 3",
5555
subtext: "عنوان فرعي ثانوي قابل للطي لإظهار وإخفاء المحتوى",
5656
aliases: ["ع3", "عنوان3", "عنوان فرعي", "قابل للطي", "طي"],
57-
group: "العناوين",
57+
group: "العناوين الفرعية",
5858
},
5959
quote: {
6060
title: "اقتباس",

packages/core/src/i18n/locales/de.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,25 @@ export const de: Dictionary = {
2424
title: "Überschrift 4",
2525
subtext: "Überschrift für kleinere Unterabschnitte",
2626
aliases: ["h4", "überschrift4", "unterüberschrift4"],
27-
group: "Überschriften",
27+
group: "Unterüberschriften",
2828
},
2929
heading_5: {
3030
title: "Überschrift 5",
3131
subtext: "Überschrift für tiefere Unterabschnitte",
3232
aliases: ["h5", "überschrift5", "unterüberschrift5"],
33-
group: "Überschriften",
33+
group: "Unterüberschriften",
3434
},
3535
heading_6: {
3636
title: "Überschrift 6",
3737
subtext: "Überschrift auf der untersten Ebene",
3838
aliases: ["h6", "überschrift6", "unterüberschrift6"],
39-
group: "Überschriften",
39+
group: "Unterüberschriften",
4040
},
4141
toggle_heading: {
4242
title: "Aufklappbare Überschrift 1",
4343
subtext: "Aufklappbare Hauptebene Überschrift",
4444
aliases: ["h", "überschrift1", "h1", "aufklappbar", "einklappbar"],
45-
group: "Überschriften",
45+
group: "Unterüberschrift",
4646
},
4747
toggle_heading_2: {
4848
title: "Aufklappbare Überschrift 2",
@@ -54,7 +54,7 @@ export const de: Dictionary = {
5454
"aufklappbar",
5555
"einklappbar",
5656
],
57-
group: "Überschriften",
57+
group: "Unterüberschriften",
5858
},
5959
toggle_heading_3: {
6060
title: "Aufklappbare Überschrift 3",
@@ -66,7 +66,7 @@ export const de: Dictionary = {
6666
"aufklappbar",
6767
"einklappbar",
6868
],
69-
group: "Überschriften",
69+
group: "Unterüberschriften",
7070
},
7171
quote: {
7272
title: "Zitat",

packages/core/src/i18n/locales/en.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,37 @@ export const en = {
2222
title: "Heading 4",
2323
subtext: "Minor subsection heading",
2424
aliases: ["h4", "heading4", "subheading4"],
25-
group: "Headings",
25+
group: "Subheadings",
2626
},
2727
heading_5: {
2828
title: "Heading 5",
2929
subtext: "Small subsection heading",
3030
aliases: ["h5", "heading5", "subheading5"],
31-
group: "Headings",
31+
group: "Subheadings",
3232
},
3333
heading_6: {
3434
title: "Heading 6",
3535
subtext: "Lowest-level heading",
3636
aliases: ["h6", "heading6", "subheading6"],
37-
group: "Headings",
37+
group: "Subheadings",
3838
},
3939
toggle_heading: {
4040
title: "Toggle Heading 1",
4141
subtext: "Toggleable top-level heading",
4242
aliases: ["h", "heading1", "h1", "collapsable"],
43-
group: "Headings",
43+
group: "Subheadings",
4444
},
4545
toggle_heading_2: {
4646
title: "Toggle Heading 2",
4747
subtext: "Toggleable key section heading",
4848
aliases: ["h2", "heading2", "subheading", "collapsable"],
49-
group: "Headings",
49+
group: "Subheadings",
5050
},
5151
toggle_heading_3: {
5252
title: "Toggle Heading 3",
5353
subtext: "Toggleable subsection and group heading",
5454
aliases: ["h3", "heading3", "subheading", "collapsable"],
55-
group: "Headings",
55+
group: "Subheadings",
5656
},
5757
quote: {
5858
title: "Quote",

0 commit comments

Comments
 (0)