@blocknote/core 0.32.0-hackdays.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blocknote.cjs +9 -9
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +2193 -1961
- package/dist/blocknote.js.map +1 -1
- package/dist/en-CsgPjHa4.cjs +2 -0
- package/dist/en-CsgPjHa4.cjs.map +1 -0
- package/dist/{en-qGo6sk9V.js → en-Dx9fwHD4.js} +47 -1
- package/dist/en-Dx9fwHD4.js.map +1 -0
- package/dist/locales.cjs +1 -1
- package/dist/locales.cjs.map +1 -1
- package/dist/locales.js +1453 -30
- package/dist/locales.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/webpack-stats.json +1 -1
- package/package.json +1 -1
- package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +18 -0
- package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +10 -0
- package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +40 -0
- package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +23 -0
- package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +13 -0
- package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +50 -0
- package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +72 -74
- package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +1 -0
- package/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.ts +104 -0
- package/src/blocks/ToggleWrapper/createToggleWrapper.ts +182 -0
- package/src/blocks/defaultBlockTypeGuards.ts +6 -2
- package/src/blocks/defaultBlocks.ts +2 -0
- package/src/editor/Block.css +112 -25
- package/src/editor/BlockNoteEditor.test.ts +1 -0
- package/src/editor/BlockNoteEditor.ts +18 -0
- package/src/editor/BlockNoteExtensions.ts +1 -1
- package/src/editor/BlockNoteTipTapEditor.ts +1 -0
- package/src/editor/editor.css +3 -0
- package/src/extensions/Collaboration/CursorPlugin.ts +33 -2
- package/src/extensions/Collaboration/ForkYDocPlugin.ts +26 -8
- package/src/extensions/Collaboration/UndoPlugin.ts +3 -2
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +4 -1
- package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +16 -4
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +64 -0
- package/src/i18n/locales/ar.ts +46 -0
- package/src/i18n/locales/de.ts +65 -0
- package/src/i18n/locales/en.ts +46 -0
- package/src/i18n/locales/es.ts +46 -0
- package/src/i18n/locales/fr.ts +68 -0
- package/src/i18n/locales/he.ts +402 -0
- package/src/i18n/locales/hr.ts +46 -0
- package/src/i18n/locales/index.ts +1 -0
- package/src/i18n/locales/is.ts +67 -0
- package/src/i18n/locales/it.ts +67 -0
- package/src/i18n/locales/ja.ts +66 -0
- package/src/i18n/locales/ko.ts +46 -0
- package/src/i18n/locales/nl.ts +47 -0
- package/src/i18n/locales/no.ts +64 -0
- package/src/i18n/locales/pl.ts +52 -0
- package/src/i18n/locales/pt.ts +46 -0
- package/src/i18n/locales/ru.ts +62 -0
- package/src/i18n/locales/sk.ts +46 -0
- package/src/i18n/locales/uk.ts +46 -0
- package/src/i18n/locales/vi.ts +46 -0
- package/src/i18n/locales/zh-tw.ts +54 -0
- package/src/i18n/locales/zh.ts +54 -0
- package/src/index.ts +1 -0
- package/src/schema/blocks/createSpec.ts +3 -1
- package/src/util/browser.ts +11 -1
- package/types/src/api/blockManipulation/setupTestEnv.d.ts +42 -2
- package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +12 -3
- package/types/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.d.ts +46 -0
- package/types/src/blocks/ToggleWrapper/createToggleWrapper.d.ts +15 -0
- package/types/src/blocks/defaultBlockTypeGuards.d.ts +2 -2
- package/types/src/blocks/defaultBlocks.d.ts +84 -4
- package/types/src/editor/BlockNoteEditor.d.ts +14 -0
- package/types/src/extensions/Collaboration/CursorPlugin.d.ts +6 -0
- package/types/src/extensions/Collaboration/UndoPlugin.d.ts +4 -1
- package/types/src/i18n/locales/en.d.ts +45 -0
- package/types/src/i18n/locales/he.d.ts +2 -0
- package/types/src/i18n/locales/index.d.ts +1 -0
- package/types/src/i18n/locales/sk.d.ts +45 -0
- package/types/src/index.d.ts +1 -0
- package/types/src/schema/blocks/createSpec.d.ts +2 -1
- package/dist/en-BXVKCwYt.cjs +0 -2
- package/dist/en-BXVKCwYt.cjs.map +0 -1
- package/dist/en-qGo6sk9V.js.map +0 -1
- package/types/src/api/blockManipulation/selections/selection.test.d.ts +0 -1
- package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.d.ts +0 -5
- package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.d.ts +0 -1
- package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +0 -17
|
@@ -5,14 +5,20 @@ import {
|
|
|
5
5
|
PropSchema,
|
|
6
6
|
createBlockSpecFromStronglyTypedTiptapNode,
|
|
7
7
|
createStronglyTypedTiptapNode,
|
|
8
|
+
getBlockFromPos,
|
|
8
9
|
propsToAttributes,
|
|
9
10
|
} from "../../schema/index.js";
|
|
10
11
|
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
|
|
11
12
|
import { defaultProps } from "../defaultProps.js";
|
|
13
|
+
import { createToggleWrapper } from "../ToggleWrapper/createToggleWrapper.js";
|
|
14
|
+
import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
15
|
+
|
|
16
|
+
const HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;
|
|
12
17
|
|
|
13
18
|
export const headingPropSchema = {
|
|
14
19
|
...defaultProps,
|
|
15
|
-
level: { default: 1, values:
|
|
20
|
+
level: { default: 1, values: HEADING_LEVELS },
|
|
21
|
+
isToggleable: { default: false },
|
|
16
22
|
} satisfies PropSchema;
|
|
17
23
|
|
|
18
24
|
const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
@@ -25,8 +31,9 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
25
31
|
},
|
|
26
32
|
|
|
27
33
|
addInputRules() {
|
|
34
|
+
const editor = this.options.editor as BlockNoteEditor<any, any, any>;
|
|
28
35
|
return [
|
|
29
|
-
...
|
|
36
|
+
...editor.settings.heading.levels.map((level) => {
|
|
30
37
|
// Creates a heading of appropriate level when starting with "#", "##", or "###".
|
|
31
38
|
return new InputRule({
|
|
32
39
|
find: new RegExp(`^(#{${level}})\\s$`),
|
|
@@ -58,87 +65,46 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
58
65
|
},
|
|
59
66
|
|
|
60
67
|
addKeyboardShortcuts() {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
) {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return this.editor.commands.command(
|
|
91
|
-
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
92
|
-
type: "heading",
|
|
93
|
-
props: {
|
|
94
|
-
level: 2 as any,
|
|
95
|
-
},
|
|
96
|
-
}),
|
|
97
|
-
);
|
|
98
|
-
},
|
|
99
|
-
"Mod-Alt-3": () => {
|
|
100
|
-
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
101
|
-
if (
|
|
102
|
-
!blockInfo.isBlockContainer ||
|
|
103
|
-
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
104
|
-
) {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return this.editor.commands.command(
|
|
109
|
-
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
110
|
-
type: "heading",
|
|
111
|
-
props: {
|
|
112
|
-
level: 3 as any,
|
|
113
|
-
},
|
|
114
|
-
}),
|
|
115
|
-
);
|
|
116
|
-
},
|
|
117
|
-
};
|
|
68
|
+
const editor = this.options.editor as BlockNoteEditor<any, any, any>;
|
|
69
|
+
|
|
70
|
+
return Object.fromEntries(
|
|
71
|
+
editor.settings.heading.levels.map((level) => [
|
|
72
|
+
`Mod-Alt-${level}`,
|
|
73
|
+
() => {
|
|
74
|
+
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
75
|
+
if (
|
|
76
|
+
!blockInfo.isBlockContainer ||
|
|
77
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
78
|
+
) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return this.editor.commands.command(
|
|
83
|
+
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
84
|
+
type: "heading",
|
|
85
|
+
props: {
|
|
86
|
+
level: level as any,
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
]),
|
|
92
|
+
);
|
|
118
93
|
},
|
|
119
94
|
parseHTML() {
|
|
95
|
+
const editor = this.options.editor as BlockNoteEditor<any, any, any>;
|
|
96
|
+
|
|
120
97
|
return [
|
|
121
98
|
// Parse from internal HTML.
|
|
122
99
|
{
|
|
123
100
|
tag: "div[data-content-type=" + this.name + "]",
|
|
124
101
|
contentElement: ".bn-inline-content",
|
|
125
102
|
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
attrs: { level: 1 },
|
|
103
|
+
...editor.settings.heading.levels.map((level) => ({
|
|
104
|
+
tag: `h${level}`,
|
|
105
|
+
attrs: { level },
|
|
130
106
|
node: "heading",
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
tag: "h2",
|
|
134
|
-
attrs: { level: 2 },
|
|
135
|
-
node: "heading",
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
tag: "h3",
|
|
139
|
-
attrs: { level: 3 },
|
|
140
|
-
node: "heading",
|
|
141
|
-
},
|
|
107
|
+
})),
|
|
142
108
|
];
|
|
143
109
|
},
|
|
144
110
|
|
|
@@ -153,6 +119,38 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
153
119
|
this.options.domAttributes?.inlineContent || {},
|
|
154
120
|
);
|
|
155
121
|
},
|
|
122
|
+
|
|
123
|
+
addNodeView() {
|
|
124
|
+
return ({ node, HTMLAttributes, getPos }) => {
|
|
125
|
+
const { dom, contentDOM } = createDefaultBlockDOMOutputSpec(
|
|
126
|
+
this.name,
|
|
127
|
+
`h${node.attrs.level}`,
|
|
128
|
+
{
|
|
129
|
+
...(this.options.domAttributes?.blockContent || {}),
|
|
130
|
+
...HTMLAttributes,
|
|
131
|
+
},
|
|
132
|
+
this.options.domAttributes?.inlineContent || {},
|
|
133
|
+
);
|
|
134
|
+
dom.removeChild(contentDOM);
|
|
135
|
+
|
|
136
|
+
const editor = this.options.editor;
|
|
137
|
+
const block = getBlockFromPos(getPos, editor, this.editor, this.name);
|
|
138
|
+
|
|
139
|
+
const toggleWrapper = createToggleWrapper(
|
|
140
|
+
block as any,
|
|
141
|
+
editor,
|
|
142
|
+
contentDOM,
|
|
143
|
+
);
|
|
144
|
+
dom.appendChild(toggleWrapper.dom);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
dom,
|
|
148
|
+
contentDOM,
|
|
149
|
+
ignoreMutation: toggleWrapper.ignoreMutation,
|
|
150
|
+
destroy: toggleWrapper.destroy,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
},
|
|
156
154
|
});
|
|
157
155
|
|
|
158
156
|
export const Heading = createBlockSpecFromStronglyTypedTiptapNode(
|
|
@@ -18,6 +18,7 @@ export const handleEnter = (editor: BlockNoteEditor<any, any, any>) => {
|
|
|
18
18
|
|
|
19
19
|
if (
|
|
20
20
|
!(
|
|
21
|
+
blockContent.node.type.name === "toggleListItem" ||
|
|
21
22
|
blockContent.node.type.name === "bulletListItem" ||
|
|
22
23
|
blockContent.node.type.name === "numberedListItem" ||
|
|
23
24
|
blockContent.node.type.name === "checkListItem"
|
package/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { updateBlockCommand } from "../../../api/blockManipulation/commands/updateBlock/updateBlock.js";
|
|
2
|
+
import { getBlockInfoFromSelection } from "../../../api/getBlockInfoFromPos.js";
|
|
3
|
+
import {
|
|
4
|
+
PropSchema,
|
|
5
|
+
createBlockSpecFromStronglyTypedTiptapNode,
|
|
6
|
+
createStronglyTypedTiptapNode,
|
|
7
|
+
getBlockFromPos,
|
|
8
|
+
} from "../../../schema/index.js";
|
|
9
|
+
import { createDefaultBlockDOMOutputSpec } from "../../defaultBlockHelpers.js";
|
|
10
|
+
import { defaultProps } from "../../defaultProps.js";
|
|
11
|
+
import { createToggleWrapper } from "../../ToggleWrapper/createToggleWrapper.js";
|
|
12
|
+
import { handleEnter } from "../ListItemKeyboardShortcuts.js";
|
|
13
|
+
|
|
14
|
+
export const toggleListItemPropSchema = {
|
|
15
|
+
...defaultProps,
|
|
16
|
+
} satisfies PropSchema;
|
|
17
|
+
|
|
18
|
+
const ToggleListItemBlockContent = createStronglyTypedTiptapNode({
|
|
19
|
+
name: "toggleListItem",
|
|
20
|
+
content: "inline*",
|
|
21
|
+
group: "blockContent",
|
|
22
|
+
// This is to make sure that the list item Enter keyboard handler takes
|
|
23
|
+
// priority over the default one.
|
|
24
|
+
priority: 90,
|
|
25
|
+
addKeyboardShortcuts() {
|
|
26
|
+
return {
|
|
27
|
+
Enter: () => handleEnter(this.options.editor),
|
|
28
|
+
"Mod-Shift-6": () => {
|
|
29
|
+
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
30
|
+
if (
|
|
31
|
+
!blockInfo.isBlockContainer ||
|
|
32
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
33
|
+
) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return this.editor.commands.command(
|
|
38
|
+
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
39
|
+
type: "toggleListItem",
|
|
40
|
+
props: {},
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
parseHTML() {
|
|
48
|
+
return [
|
|
49
|
+
// Parse from internal HTML.
|
|
50
|
+
{
|
|
51
|
+
tag: "div[data-content-type=" + this.name + "]",
|
|
52
|
+
contentElement: ".bn-inline-content",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
renderHTML({ HTMLAttributes }) {
|
|
58
|
+
return createDefaultBlockDOMOutputSpec(
|
|
59
|
+
this.name,
|
|
60
|
+
"p",
|
|
61
|
+
{
|
|
62
|
+
...(this.options.domAttributes?.blockContent || {}),
|
|
63
|
+
...HTMLAttributes,
|
|
64
|
+
},
|
|
65
|
+
this.options.domAttributes?.inlineContent || {},
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
addNodeView() {
|
|
70
|
+
return ({ HTMLAttributes, getPos }) => {
|
|
71
|
+
const { dom, contentDOM } = createDefaultBlockDOMOutputSpec(
|
|
72
|
+
this.name,
|
|
73
|
+
"p",
|
|
74
|
+
{
|
|
75
|
+
...(this.options.domAttributes?.blockContent || {}),
|
|
76
|
+
...HTMLAttributes,
|
|
77
|
+
},
|
|
78
|
+
this.options.domAttributes?.inlineContent || {},
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const editor = this.options.editor;
|
|
82
|
+
const block = getBlockFromPos(getPos, editor, this.editor, this.name);
|
|
83
|
+
|
|
84
|
+
const toggleWrapper = createToggleWrapper(
|
|
85
|
+
block as any,
|
|
86
|
+
editor,
|
|
87
|
+
contentDOM,
|
|
88
|
+
);
|
|
89
|
+
dom.appendChild(toggleWrapper.dom);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
dom,
|
|
93
|
+
contentDOM,
|
|
94
|
+
ignoreMutation: toggleWrapper.ignoreMutation,
|
|
95
|
+
destroy: toggleWrapper.destroy,
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const ToggleListItem = createBlockSpecFromStronglyTypedTiptapNode(
|
|
102
|
+
ToggleListItemBlockContent,
|
|
103
|
+
toggleListItemPropSchema,
|
|
104
|
+
);
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { ViewMutationRecord } from "@tiptap/pm/view";
|
|
2
|
+
|
|
3
|
+
import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
4
|
+
import { Block } from "../defaultBlocks.js";
|
|
5
|
+
|
|
6
|
+
type ToggledState = {
|
|
7
|
+
set: (block: Block<any, any, any>, isToggled: boolean) => void;
|
|
8
|
+
get: (block: Block<any, any, any>) => boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const defaultToggledState: ToggledState = {
|
|
12
|
+
set: (block, isToggled: boolean) =>
|
|
13
|
+
window.localStorage.setItem(
|
|
14
|
+
`toggle-${block.id}`,
|
|
15
|
+
isToggled ? "true" : "false",
|
|
16
|
+
),
|
|
17
|
+
get: (block) => window.localStorage.getItem(`toggle-${block.id}`) === "true",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const createToggleWrapper = (
|
|
21
|
+
block: Block<any, any, any>,
|
|
22
|
+
editor: BlockNoteEditor<any, any, any>,
|
|
23
|
+
renderedElement: HTMLElement,
|
|
24
|
+
toggledState: ToggledState = defaultToggledState,
|
|
25
|
+
): {
|
|
26
|
+
dom: HTMLElement;
|
|
27
|
+
contentDOM?: HTMLElement;
|
|
28
|
+
ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
|
|
29
|
+
destroy?: () => void;
|
|
30
|
+
} => {
|
|
31
|
+
if ("isToggleable" in block.props && !block.props.isToggleable) {
|
|
32
|
+
return {
|
|
33
|
+
dom: renderedElement,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const dom = document.createElement("div");
|
|
38
|
+
|
|
39
|
+
const toggleWrapper = document.createElement("div");
|
|
40
|
+
toggleWrapper.className = "bn-toggle-wrapper";
|
|
41
|
+
|
|
42
|
+
const toggleButton = document.createElement("button");
|
|
43
|
+
toggleButton.className = "bn-toggle-button";
|
|
44
|
+
toggleButton.innerHTML =
|
|
45
|
+
// https://fonts.google.com/icons?selected=Material+Symbols+Rounded:chevron_right:FILL@0;wght@700;GRAD@0;opsz@24&icon.query=chevron&icon.style=Rounded&icon.size=24&icon.color=%23e8eaed
|
|
46
|
+
'<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="CURRENTCOLOR"><path d="M320-200v-560l440 280-440 280Z"/></svg>';
|
|
47
|
+
const toggleButtonMouseDown = (event: MouseEvent) => event.preventDefault();
|
|
48
|
+
toggleButton.addEventListener("mousedown", toggleButtonMouseDown);
|
|
49
|
+
const toggleButtonOnClick = () => {
|
|
50
|
+
// Toggles visibility of child blocks. Also adds/removes the "add block"
|
|
51
|
+
// button if there are no child blocks.
|
|
52
|
+
if (toggleWrapper.getAttribute("data-show-children") === "true") {
|
|
53
|
+
toggleWrapper.setAttribute("data-show-children", "false");
|
|
54
|
+
toggledState.set(editor.getBlock(block)!, false);
|
|
55
|
+
|
|
56
|
+
if (dom.contains(toggleAddBlockButton)) {
|
|
57
|
+
dom.removeChild(toggleAddBlockButton);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
toggleWrapper.setAttribute("data-show-children", "true");
|
|
61
|
+
toggledState.set(editor.getBlock(block)!, true);
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
editor.getBlock(block)?.children.length === 0 &&
|
|
65
|
+
!dom.contains(toggleAddBlockButton)
|
|
66
|
+
) {
|
|
67
|
+
dom.appendChild(toggleAddBlockButton);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
toggleButton.addEventListener("click", toggleButtonOnClick);
|
|
72
|
+
|
|
73
|
+
toggleWrapper.appendChild(toggleButton);
|
|
74
|
+
toggleWrapper.appendChild(renderedElement);
|
|
75
|
+
|
|
76
|
+
const toggleAddBlockButton = document.createElement("button");
|
|
77
|
+
toggleAddBlockButton.className = "bn-toggle-add-block-button";
|
|
78
|
+
toggleAddBlockButton.textContent = "Empty toggle. Click to add a block.";
|
|
79
|
+
const toggleAddBlockButtonMouseDown = (event: MouseEvent) =>
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
toggleAddBlockButton.addEventListener(
|
|
82
|
+
"mousedown",
|
|
83
|
+
toggleAddBlockButtonMouseDown,
|
|
84
|
+
);
|
|
85
|
+
const toggleAddBlockButtonOnClick = () => {
|
|
86
|
+
// Adds a single empty child block.
|
|
87
|
+
editor.transact(() => {
|
|
88
|
+
// dom.removeChild(toggleAddBlockButton);
|
|
89
|
+
|
|
90
|
+
const updatedBlock = editor.updateBlock(block, {
|
|
91
|
+
// Single empty block with default type.
|
|
92
|
+
children: [{}],
|
|
93
|
+
});
|
|
94
|
+
editor.setTextCursorPosition(updatedBlock.children[0].id, "end");
|
|
95
|
+
editor.focus();
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
toggleAddBlockButton.addEventListener("click", toggleAddBlockButtonOnClick);
|
|
99
|
+
|
|
100
|
+
dom.appendChild(toggleWrapper);
|
|
101
|
+
|
|
102
|
+
let childCount = block.children.length;
|
|
103
|
+
const onEditorChange = editor.onChange(() => {
|
|
104
|
+
const newChildCount = editor.getBlock(block)?.children.length ?? 0;
|
|
105
|
+
|
|
106
|
+
if (newChildCount > childCount) {
|
|
107
|
+
// If a child block is added while children are hidden, show children.
|
|
108
|
+
if (toggleWrapper.getAttribute("data-show-children") === "false") {
|
|
109
|
+
toggleWrapper.setAttribute("data-show-children", "true");
|
|
110
|
+
toggledState.set(editor.getBlock(block)!, true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Remove the "add block" button as we want to show child blocks and
|
|
114
|
+
// there is at least one child block.
|
|
115
|
+
if (dom.contains(toggleAddBlockButton)) {
|
|
116
|
+
dom.removeChild(toggleAddBlockButton);
|
|
117
|
+
}
|
|
118
|
+
} else if (newChildCount === 0 && newChildCount < childCount) {
|
|
119
|
+
// If the last child block is removed while children are shown, hide
|
|
120
|
+
// children.
|
|
121
|
+
if (toggleWrapper.getAttribute("data-show-children") === "true") {
|
|
122
|
+
toggleWrapper.setAttribute("data-show-children", "false");
|
|
123
|
+
toggledState.set(editor.getBlock(block)!, false);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Remove the "add block" button as we want to hide child blocks,
|
|
127
|
+
// regardless of whether there are child blocks or not.
|
|
128
|
+
if (dom.contains(toggleAddBlockButton)) {
|
|
129
|
+
dom.removeChild(toggleAddBlockButton);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
childCount = newChildCount;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (toggledState.get(block)) {
|
|
137
|
+
toggleWrapper.setAttribute("data-show-children", "true");
|
|
138
|
+
|
|
139
|
+
if (block.children.length === 0) {
|
|
140
|
+
// If the toggle is set to show children, but there are no children,
|
|
141
|
+
// we add the "add block" button.
|
|
142
|
+
dom.appendChild(toggleAddBlockButton);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
toggleWrapper.setAttribute("data-show-children", "false");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
dom,
|
|
150
|
+
// Prevents re-renders when the toggle button is clicked.
|
|
151
|
+
ignoreMutation: (mutation) => {
|
|
152
|
+
if (
|
|
153
|
+
mutation instanceof MutationRecord &&
|
|
154
|
+
// We want to prevent re-renders when the view changes, so we ignore
|
|
155
|
+
// all mutations where the `data-show-children` attribute is changed
|
|
156
|
+
// or the "add block" button is added/removed.
|
|
157
|
+
((mutation.type === "attributes" &&
|
|
158
|
+
mutation.target === toggleWrapper &&
|
|
159
|
+
mutation.attributeName === "data-show-children") ||
|
|
160
|
+
(mutation.type === "childList" &&
|
|
161
|
+
(mutation.addedNodes[0] === toggleAddBlockButton ||
|
|
162
|
+
mutation.removedNodes[0] === toggleAddBlockButton)))
|
|
163
|
+
) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
},
|
|
168
|
+
destroy: () => {
|
|
169
|
+
toggleButton.removeEventListener("mousedown", toggleButtonMouseDown);
|
|
170
|
+
toggleButton.removeEventListener("click", toggleButtonOnClick);
|
|
171
|
+
toggleAddBlockButton.removeEventListener(
|
|
172
|
+
"mousedown",
|
|
173
|
+
toggleAddBlockButtonMouseDown,
|
|
174
|
+
);
|
|
175
|
+
toggleAddBlockButton.removeEventListener(
|
|
176
|
+
"click",
|
|
177
|
+
toggleAddBlockButtonOnClick,
|
|
178
|
+
);
|
|
179
|
+
onEditorChange?.();
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
};
|
|
@@ -24,7 +24,11 @@ export function checkDefaultBlockTypeInSchema<
|
|
|
24
24
|
>(
|
|
25
25
|
blockType: BlockType,
|
|
26
26
|
editor: BlockNoteEditor<any, I, S>,
|
|
27
|
-
): editor is BlockNoteEditor<
|
|
27
|
+
): editor is BlockNoteEditor<
|
|
28
|
+
{ [K in BlockType]: DefaultBlockSchema[BlockType] },
|
|
29
|
+
I,
|
|
30
|
+
S
|
|
31
|
+
> {
|
|
28
32
|
return (
|
|
29
33
|
blockType in editor.schema.blockSchema &&
|
|
30
34
|
editor.schema.blockSchema[blockType] === defaultBlockSchema[blockType]
|
|
@@ -40,7 +44,7 @@ export function checkDefaultInlineContentTypeInSchema<
|
|
|
40
44
|
editor: BlockNoteEditor<B, any, S>,
|
|
41
45
|
): editor is BlockNoteEditor<
|
|
42
46
|
B,
|
|
43
|
-
{
|
|
47
|
+
{ [K in InlineContentType]: DefaultInlineContentSchema[InlineContentType] },
|
|
44
48
|
S
|
|
45
49
|
> {
|
|
46
50
|
return (
|
|
@@ -25,6 +25,7 @@ import { CodeBlock } from "./CodeBlockContent/CodeBlockContent.js";
|
|
|
25
25
|
import { FileBlock } from "./FileBlockContent/FileBlockContent.js";
|
|
26
26
|
import { Heading } from "./HeadingBlockContent/HeadingBlockContent.js";
|
|
27
27
|
import { ImageBlock } from "./ImageBlockContent/ImageBlockContent.js";
|
|
28
|
+
import { ToggleListItem } from "./ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.js";
|
|
28
29
|
import { BulletListItem } from "./ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.js";
|
|
29
30
|
import { CheckListItem } from "./ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.js";
|
|
30
31
|
import { NumberedListItem } from "./ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.js";
|
|
@@ -38,6 +39,7 @@ export const defaultBlockSpecs = {
|
|
|
38
39
|
heading: Heading,
|
|
39
40
|
quote: Quote,
|
|
40
41
|
codeBlock: CodeBlock,
|
|
42
|
+
toggleListItem: ToggleListItem,
|
|
41
43
|
bulletListItem: BulletListItem,
|
|
42
44
|
numberedListItem: NumberedListItem,
|
|
43
45
|
checkListItem: CheckListItem,
|