@blocknote/core 0.13.2 → 0.13.4
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.js +5730 -2891
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +7 -7
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +2 -2
- package/src/api/exporters/html/__snapshots__/file/basic/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/basic/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/button/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/button/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/nested/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/nested/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noCaption/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noCaption/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noName/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noName/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/basic/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/button/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/button/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/nested/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/noCaption/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noCaption/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noName/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noName/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noPreview/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noPreview/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/lists/basic/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/lists/basic/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/lists/nested/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/lists/nested/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/basic/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/basic/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/button/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/button/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/nested/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/nested/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/basic/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/button/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/button/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/nested/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noName/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noName/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/internal.html +1 -0
- package/src/api/exporters/html/externalHTMLExporter.ts +4 -3
- package/src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts +1 -1
- package/src/api/exporters/markdown/__snapshots__/file/basic/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/file/button/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/file/nested/markdown.md +7 -0
- package/src/api/exporters/markdown/__snapshots__/file/noCaption/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/file/noName/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/image/basic/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/image/button/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/image/nested/markdown.md +2 -2
- package/src/api/exporters/markdown/__snapshots__/image/noCaption/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/image/noName/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/image/noPreview/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/lists/basic/markdown.md +8 -0
- package/src/api/exporters/markdown/__snapshots__/lists/nested/markdown.md +10 -0
- package/src/api/exporters/markdown/__snapshots__/simpleFile/basic/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/simpleFile/button/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/simpleFile/nested/markdown.md +7 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/basic/markdown.md +3 -1
- package/src/api/exporters/markdown/__snapshots__/simpleImage/button/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/nested/markdown.md +6 -2
- package/src/api/exporters/markdown/__snapshots__/simpleImage/noCaption/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/noName/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/noPreview/markdown.md +3 -0
- package/src/api/exporters/markdown/markdownExporter.ts +2 -0
- package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +42 -0
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +262 -4
- package/src/api/parsers/html/__snapshots__/paste/list-test.json +74 -2
- package/src/api/parsers/html/__snapshots__/paste/parse-basic-block-types.json +3 -1
- package/src/api/parsers/html/__snapshots__/paste/parse-fake-image-caption.json +3 -1
- package/src/api/parsers/html/__snapshots__/paste/parse-mixed-nested-lists.json +135 -10
- package/src/api/parsers/html/__snapshots__/paste/parse-nested-lists-with-paragraphs.json +132 -7
- package/src/api/parsers/html/__snapshots__/paste/parse-nested-lists.json +111 -3
- package/src/api/parsers/html/parseHTML.test.ts +166 -95
- package/src/api/testUtil/cases/customBlocks.ts +82 -33
- package/src/api/testUtil/cases/customInlineContent.ts +1 -1
- package/src/api/testUtil/cases/customStyles.ts +1 -1
- package/src/api/testUtil/cases/defaultSchema.ts +185 -4
- package/src/blocks/AudioBlockContent/AudioBlockContent.ts +163 -0
- package/src/blocks/AudioBlockContent/audioBlockHelpers.ts +5 -0
- package/src/blocks/FileBlockContent/FileBlockContent.ts +120 -0
- package/src/blocks/FileBlockContent/fileBlockHelpers.ts +377 -0
- package/src/blocks/ImageBlockContent/ImageBlockContent.ts +134 -354
- package/src/blocks/ImageBlockContent/imageBlockHelpers.ts +6 -0
- package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +3 -0
- package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +266 -0
- package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +2 -1
- package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +1 -0
- package/src/blocks/VideoBlockContent/VideoBlockContent.ts +181 -0
- package/src/blocks/VideoBlockContent/videoBlockHelpers.ts +6 -0
- package/src/blocks/defaultBlockTypeGuards.ts +53 -1
- package/src/blocks/defaultBlocks.ts +11 -2
- package/src/editor/Block.css +89 -27
- package/src/editor/BlockNoteEditor.ts +24 -10
- package/src/editor/BlockNoteSchema.ts +12 -3
- package/src/editor/transformPasted.ts +2 -1
- package/src/extensions/{ImagePanel/ImageToolbarPlugin.ts → FilePanel/FilePanelPlugin.ts} +22 -25
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +14 -2
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +72 -2
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +27 -27
- package/src/extensions/TextAlignment/TextAlignmentExtension.ts +7 -1
- package/src/i18n/locales/en.ts +117 -11
- package/src/i18n/locales/fr.ts +118 -11
- package/src/i18n/locales/index.ts +8 -2
- package/src/i18n/locales/is.ts +295 -0
- package/src/i18n/locales/ja.ts +323 -0
- package/src/i18n/locales/ko.ts +307 -0
- package/src/i18n/locales/nl.ts +108 -8
- package/src/i18n/locales/pl.ts +287 -0
- package/src/i18n/locales/pt.ts +295 -0
- package/src/i18n/locales/vi.ts +295 -0
- package/src/i18n/locales/zh.ts +123 -8
- package/src/index.ts +9 -2
- package/src/pm-nodes/BlockContainer.ts +18 -6
- package/src/schema/blocks/createSpec.ts +1 -0
- package/src/schema/blocks/internal.ts +10 -0
- package/src/schema/blocks/types.ts +40 -5
- package/src/util/string.ts +12 -0
- package/types/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.d.ts +7 -0
- package/types/src/api/testUtil/cases/customBlocks.d.ts +272 -54
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +222 -16
- package/types/src/api/testUtil/cases/customStyles.d.ts +222 -16
- package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +101 -0
- package/types/src/blocks/AudioBlockContent/audioBlockHelpers.d.ts +3 -0
- package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +93 -0
- package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +30 -0
- package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +50 -14
- package/types/src/blocks/ImageBlockContent/imageBlockHelpers.d.ts +4 -0
- package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +55 -0
- package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +129 -0
- package/types/src/blocks/VideoBlockContent/videoBlockHelpers.d.ts +4 -0
- package/types/src/blocks/defaultBlockTypeGuards.d.ts +6 -1
- package/types/src/blocks/defaultBlocks.d.ts +444 -32
- package/types/src/editor/BlockNoteEditor.d.ts +12 -5
- package/types/src/extensions/{ImagePanel/ImageToolbarPlugin.d.ts → FilePanel/FilePanelPlugin.d.ts} +9 -12
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +1 -1
- package/types/src/i18n/locales/en.d.ts +56 -7
- package/types/src/i18n/locales/fr.d.ts +2 -184
- package/types/src/i18n/locales/index.d.ts +7 -1
- package/types/src/i18n/locales/is.d.ts +2 -0
- package/types/src/i18n/locales/ja.d.ts +2 -0
- package/types/src/i18n/locales/ko.d.ts +2 -0
- package/types/src/i18n/locales/pl.d.ts +2 -0
- package/types/src/i18n/locales/pt.d.ts +2 -0
- package/types/src/i18n/locales/vi.d.ts +2 -0
- package/types/src/index.d.ts +8 -2
- package/types/src/pm-nodes/BlockContainer.d.ts +1 -1
- package/types/src/schema/blocks/internal.d.ts +1 -1
- package/types/src/schema/blocks/types.d.ts +25 -1
- package/types/src/util/string.d.ts +1 -0
- /package/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
- /package/types/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { InputRule } from "@tiptap/core";
|
|
2
|
+
import {
|
|
3
|
+
PropSchema,
|
|
4
|
+
createBlockSpecFromStronglyTypedTiptapNode,
|
|
5
|
+
createStronglyTypedTiptapNode,
|
|
6
|
+
} from "../../../schema";
|
|
7
|
+
import { createDefaultBlockDOMOutputSpec } from "../../defaultBlockHelpers";
|
|
8
|
+
import { defaultProps } from "../../defaultProps";
|
|
9
|
+
import { handleEnter } from "../ListItemKeyboardShortcuts";
|
|
10
|
+
import { getCurrentBlockContentType } from "../../../api/getCurrentBlockContentType";
|
|
11
|
+
|
|
12
|
+
export const checkListItemPropSchema = {
|
|
13
|
+
...defaultProps,
|
|
14
|
+
checked: {
|
|
15
|
+
default: false,
|
|
16
|
+
},
|
|
17
|
+
} satisfies PropSchema;
|
|
18
|
+
|
|
19
|
+
const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
20
|
+
name: "checkListItem",
|
|
21
|
+
content: "inline*",
|
|
22
|
+
group: "blockContent",
|
|
23
|
+
addAttributes() {
|
|
24
|
+
return {
|
|
25
|
+
checked: {
|
|
26
|
+
default: false,
|
|
27
|
+
// instead of "checked" attributes, use "data-checked"
|
|
28
|
+
parseHTML: (element) =>
|
|
29
|
+
element.getAttribute("data-checked") === "true" || undefined,
|
|
30
|
+
renderHTML: (attributes) => {
|
|
31
|
+
return attributes.checked
|
|
32
|
+
? {
|
|
33
|
+
"data-checked": (attributes.checked as boolean).toString(),
|
|
34
|
+
}
|
|
35
|
+
: {};
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
addInputRules() {
|
|
42
|
+
return [
|
|
43
|
+
// Creates a checklist when starting with "[]" or "[X]".
|
|
44
|
+
new InputRule({
|
|
45
|
+
find: new RegExp(`\\[\\s*\\]\\s$`),
|
|
46
|
+
handler: ({ state, chain, range }) => {
|
|
47
|
+
if (getCurrentBlockContentType(this.editor) !== "inline*") {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
chain()
|
|
52
|
+
.BNUpdateBlock(state.selection.from, {
|
|
53
|
+
type: "checkListItem",
|
|
54
|
+
props: {
|
|
55
|
+
checked: false as any,
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
// Removes the characters used to set the list.
|
|
59
|
+
.deleteRange({ from: range.from, to: range.to });
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
new InputRule({
|
|
63
|
+
find: new RegExp(`\\[[Xx]\\]\\s$`),
|
|
64
|
+
handler: ({ state, chain, range }) => {
|
|
65
|
+
if (getCurrentBlockContentType(this.editor) !== "inline*") {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
chain()
|
|
70
|
+
.BNUpdateBlock(state.selection.from, {
|
|
71
|
+
type: "checkListItem",
|
|
72
|
+
props: {
|
|
73
|
+
checked: true as any,
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
// Removes the characters used to set the list.
|
|
77
|
+
.deleteRange({ from: range.from, to: range.to });
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
];
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
addKeyboardShortcuts() {
|
|
84
|
+
return {
|
|
85
|
+
Enter: () => handleEnter(this.editor),
|
|
86
|
+
"Mod-Shift-9": () => {
|
|
87
|
+
if (getCurrentBlockContentType(this.editor) !== "inline*") {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this.editor.commands.BNUpdateBlock(
|
|
92
|
+
this.editor.state.selection.anchor,
|
|
93
|
+
{
|
|
94
|
+
type: "checkListItem",
|
|
95
|
+
props: {},
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
parseHTML() {
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
tag: "div[data-content-type=" + this.name + "]", // TODO: remove if we can't come up with test case that needs this
|
|
106
|
+
},
|
|
107
|
+
// Checkbox only.
|
|
108
|
+
{
|
|
109
|
+
tag: "input",
|
|
110
|
+
getAttrs: (element) => {
|
|
111
|
+
if (typeof element === "string") {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if ((element as HTMLInputElement).type === "checkbox") {
|
|
116
|
+
return { checked: (element as HTMLInputElement).checked };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
},
|
|
121
|
+
node: "checkListItem",
|
|
122
|
+
},
|
|
123
|
+
// Container element for checkbox + label.
|
|
124
|
+
{
|
|
125
|
+
tag: "li",
|
|
126
|
+
getAttrs: (element) => {
|
|
127
|
+
if (typeof element === "string") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const parent = element.parentElement;
|
|
132
|
+
|
|
133
|
+
if (parent === null) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
parent.tagName === "UL" ||
|
|
139
|
+
(parent.tagName === "DIV" && parent.parentElement!.tagName === "UL")
|
|
140
|
+
) {
|
|
141
|
+
const checkbox =
|
|
142
|
+
(element.querySelector(
|
|
143
|
+
"input[type=checkbox]"
|
|
144
|
+
) as HTMLInputElement) || null;
|
|
145
|
+
|
|
146
|
+
if (checkbox === null) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { checked: checkbox.checked };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return false;
|
|
154
|
+
},
|
|
155
|
+
node: "checkListItem",
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// Since there is no HTML checklist element, there isn't really any
|
|
161
|
+
// standardization for what checklists should look like in the DOM. GDocs'
|
|
162
|
+
// and Notion's aren't cross compatible, for example. This implementation
|
|
163
|
+
// has a semantically correct DOM structure (though missing a label for the
|
|
164
|
+
// checkbox) which is also converted correctly to Markdown by remark.
|
|
165
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
166
|
+
const checkbox = document.createElement("input");
|
|
167
|
+
checkbox.type = "checkbox";
|
|
168
|
+
checkbox.checked = node.attrs.checked;
|
|
169
|
+
if (node.attrs.checked) {
|
|
170
|
+
checkbox.setAttribute("checked", "");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const { dom, contentDOM } = createDefaultBlockDOMOutputSpec(
|
|
174
|
+
this.name,
|
|
175
|
+
"p",
|
|
176
|
+
{
|
|
177
|
+
...(this.options.domAttributes?.blockContent || {}),
|
|
178
|
+
...HTMLAttributes,
|
|
179
|
+
},
|
|
180
|
+
this.options.domAttributes?.inlineContent || {}
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
dom.insertBefore(checkbox, contentDOM);
|
|
184
|
+
|
|
185
|
+
return { dom, contentDOM };
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
// Need to render node view since the checkbox needs to be able to update the
|
|
189
|
+
// node. This is only possible with a node view as it exposes `getPos`.
|
|
190
|
+
addNodeView() {
|
|
191
|
+
return ({ node, getPos, editor, HTMLAttributes }) => {
|
|
192
|
+
// Need to wrap certain elements in a div or keyboard navigation gets
|
|
193
|
+
// confused.
|
|
194
|
+
const wrapper = document.createElement("div");
|
|
195
|
+
const checkboxWrapper = document.createElement("div");
|
|
196
|
+
checkboxWrapper.contentEditable = "false";
|
|
197
|
+
|
|
198
|
+
const checkbox = document.createElement("input");
|
|
199
|
+
checkbox.type = "checkbox";
|
|
200
|
+
checkbox.checked = node.attrs.checked;
|
|
201
|
+
if (node.attrs.checked) {
|
|
202
|
+
checkbox.setAttribute("checked", "");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const changeHandler = () => {
|
|
206
|
+
if (!editor.isEditable) {
|
|
207
|
+
// This seems like the most effective way of blocking the checkbox
|
|
208
|
+
// from being toggled, as event.preventDefault() does not stop it for
|
|
209
|
+
// "click" or "change" events.
|
|
210
|
+
checkbox.checked = !checkbox.checked;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (typeof getPos !== "boolean") {
|
|
215
|
+
this.editor.commands.BNUpdateBlock(getPos(), {
|
|
216
|
+
type: "checkListItem",
|
|
217
|
+
props: {
|
|
218
|
+
checked: checkbox.checked as any,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
checkbox.addEventListener("change", changeHandler);
|
|
224
|
+
|
|
225
|
+
const { dom, contentDOM } = createDefaultBlockDOMOutputSpec(
|
|
226
|
+
this.name,
|
|
227
|
+
"p",
|
|
228
|
+
{
|
|
229
|
+
...(this.options.domAttributes?.blockContent || {}),
|
|
230
|
+
...HTMLAttributes,
|
|
231
|
+
},
|
|
232
|
+
this.options.domAttributes?.inlineContent || {}
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (typeof getPos !== "boolean") {
|
|
236
|
+
// Since `node` is a blockContent node, we have to get the block ID from
|
|
237
|
+
// the parent blockContainer node. This means we can't add the label in
|
|
238
|
+
// `renderHTML` as we can't use `getPos` and therefore can't get the
|
|
239
|
+
// parent blockContainer node.
|
|
240
|
+
const blockID = this.editor.state.doc.resolve(getPos()).node().attrs.id;
|
|
241
|
+
const label = "label-" + blockID;
|
|
242
|
+
checkbox.setAttribute("aria-labelledby", label);
|
|
243
|
+
contentDOM.id = label;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
dom.removeChild(contentDOM);
|
|
247
|
+
dom.appendChild(wrapper);
|
|
248
|
+
wrapper.appendChild(checkboxWrapper);
|
|
249
|
+
wrapper.appendChild(contentDOM);
|
|
250
|
+
checkboxWrapper.appendChild(checkbox);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
dom,
|
|
254
|
+
contentDOM,
|
|
255
|
+
destroy: () => {
|
|
256
|
+
checkbox.removeEventListener("change", changeHandler);
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
export const CheckListItem = createBlockSpecFromStronglyTypedTiptapNode(
|
|
264
|
+
checkListItemBlockContent,
|
|
265
|
+
checkListItemPropSchema
|
|
266
|
+
);
|
|
@@ -13,7 +13,8 @@ export const handleEnter = (editor: Editor) => {
|
|
|
13
13
|
if (
|
|
14
14
|
!(
|
|
15
15
|
contentType.name === "bulletListItem" ||
|
|
16
|
-
contentType.name === "numberedListItem"
|
|
16
|
+
contentType.name === "numberedListItem" ||
|
|
17
|
+
contentType.name === "checkListItem"
|
|
17
18
|
) ||
|
|
18
19
|
!selectionEmpty
|
|
19
20
|
) {
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
2
|
+
import {
|
|
3
|
+
BlockFromConfig,
|
|
4
|
+
createBlockSpec,
|
|
5
|
+
FileBlockConfig,
|
|
6
|
+
Props,
|
|
7
|
+
PropSchema,
|
|
8
|
+
} from "../../schema";
|
|
9
|
+
import { defaultProps } from "../defaultProps";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
createAddFileButton,
|
|
13
|
+
createDefaultFilePreview,
|
|
14
|
+
createFigureWithCaption,
|
|
15
|
+
createFileAndCaptionWrapper,
|
|
16
|
+
createLinkWithCaption,
|
|
17
|
+
createResizeHandlesWrapper,
|
|
18
|
+
parseFigureElement,
|
|
19
|
+
} from "../FileBlockContent/fileBlockHelpers";
|
|
20
|
+
import { parseVideoElement } from "./videoBlockHelpers";
|
|
21
|
+
|
|
22
|
+
export const videoPropSchema = {
|
|
23
|
+
textAlignment: defaultProps.textAlignment,
|
|
24
|
+
backgroundColor: defaultProps.backgroundColor,
|
|
25
|
+
// File name.
|
|
26
|
+
name: {
|
|
27
|
+
default: "" as const,
|
|
28
|
+
},
|
|
29
|
+
// File url.
|
|
30
|
+
url: {
|
|
31
|
+
default: "" as const,
|
|
32
|
+
},
|
|
33
|
+
// File caption.
|
|
34
|
+
caption: {
|
|
35
|
+
default: "" as const,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
showPreview: {
|
|
39
|
+
default: true,
|
|
40
|
+
},
|
|
41
|
+
// File preview width in px.
|
|
42
|
+
previewWidth: {
|
|
43
|
+
default: 512,
|
|
44
|
+
},
|
|
45
|
+
} satisfies PropSchema;
|
|
46
|
+
|
|
47
|
+
export const videoBlockConfig = {
|
|
48
|
+
type: "video" as const,
|
|
49
|
+
propSchema: videoPropSchema,
|
|
50
|
+
content: "none",
|
|
51
|
+
isFileBlock: true,
|
|
52
|
+
fileBlockAcceptMimeTypes: ["video/*"],
|
|
53
|
+
} satisfies FileBlockConfig;
|
|
54
|
+
|
|
55
|
+
export const videoRender = (
|
|
56
|
+
block: BlockFromConfig<typeof videoBlockConfig, any, any>,
|
|
57
|
+
editor: BlockNoteEditor<any, any, any>
|
|
58
|
+
) => {
|
|
59
|
+
const wrapper = document.createElement("div");
|
|
60
|
+
wrapper.className = "bn-file-block-content-wrapper";
|
|
61
|
+
|
|
62
|
+
if (block.props.url === "") {
|
|
63
|
+
const fileBlockVideoIcon = document.createElement("div");
|
|
64
|
+
fileBlockVideoIcon.innerHTML =
|
|
65
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 5V19H16V5H8ZM4 5V7H6V5H4ZM18 5V7H20V5H18ZM4 9V11H6V9H4ZM18 9V11H20V9H18ZM4 13V15H6V13H4ZM18 13V15H20V13H18ZM4 17V19H6V17H4ZM18 17V19H20V17H18Z"></path></svg>';
|
|
66
|
+
const addVideoButton = createAddFileButton(
|
|
67
|
+
block,
|
|
68
|
+
editor,
|
|
69
|
+
editor.dictionary.file_blocks.video.add_button_text,
|
|
70
|
+
fileBlockVideoIcon.firstElementChild as HTMLElement
|
|
71
|
+
);
|
|
72
|
+
wrapper.appendChild(addVideoButton.dom);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
dom: wrapper,
|
|
76
|
+
destroy: () => {
|
|
77
|
+
addVideoButton?.destroy?.();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
} else if (!block.props.showPreview) {
|
|
81
|
+
const file = createDefaultFilePreview(block).dom;
|
|
82
|
+
const element = createFileAndCaptionWrapper(block, file);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
dom: element.dom,
|
|
86
|
+
};
|
|
87
|
+
} else {
|
|
88
|
+
const video = document.createElement("video");
|
|
89
|
+
video.className = "bn-visual-media";
|
|
90
|
+
video.src = block.props.url;
|
|
91
|
+
video.controls = true;
|
|
92
|
+
video.contentEditable = "false";
|
|
93
|
+
video.draggable = false;
|
|
94
|
+
video.width = Math.min(
|
|
95
|
+
block.props.previewWidth,
|
|
96
|
+
editor.domElement.firstElementChild!.clientWidth
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const file = createResizeHandlesWrapper(
|
|
100
|
+
block,
|
|
101
|
+
editor,
|
|
102
|
+
video,
|
|
103
|
+
() => video.width,
|
|
104
|
+
(width) => (video.width = width)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const element = createFileAndCaptionWrapper(block, file.dom);
|
|
108
|
+
wrapper.appendChild(element.dom);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
dom: wrapper,
|
|
112
|
+
destroy: file.destroy,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const videoParse = (
|
|
118
|
+
element: HTMLElement
|
|
119
|
+
): Partial<Props<typeof videoBlockConfig.propSchema>> | undefined => {
|
|
120
|
+
if (element.tagName === "VIDEO") {
|
|
121
|
+
return parseVideoElement(element as HTMLVideoElement);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (element.tagName === "FIGURE") {
|
|
125
|
+
const parsedFigure = parseFigureElement(element, "video");
|
|
126
|
+
if (!parsedFigure) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { targetElement, caption } = parsedFigure;
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
...parseVideoElement(targetElement as HTMLVideoElement),
|
|
134
|
+
caption,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return undefined;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const videoToExternalHTML = (
|
|
142
|
+
block: BlockFromConfig<typeof videoBlockConfig, any, any>
|
|
143
|
+
) => {
|
|
144
|
+
if (!block.props.url) {
|
|
145
|
+
const div = document.createElement("p");
|
|
146
|
+
div.textContent = "Add video";
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
dom: div,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let video;
|
|
154
|
+
if (block.props.showPreview) {
|
|
155
|
+
video = document.createElement("video");
|
|
156
|
+
video.src = block.props.url;
|
|
157
|
+
video.width = block.props.previewWidth;
|
|
158
|
+
} else {
|
|
159
|
+
video = document.createElement("a");
|
|
160
|
+
video.href = block.props.url;
|
|
161
|
+
video.textContent = block.props.name || block.props.url;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (block.props.caption) {
|
|
165
|
+
if (block.props.showPreview) {
|
|
166
|
+
return createFigureWithCaption(video, block.props.caption);
|
|
167
|
+
} else {
|
|
168
|
+
return createLinkWithCaption(video, block.props.caption);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
dom: video,
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const VideoBlock = createBlockSpec(videoBlockConfig, {
|
|
178
|
+
render: videoRender,
|
|
179
|
+
parse: videoParse,
|
|
180
|
+
toExternalHTML: videoToExternalHTML,
|
|
181
|
+
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { BlockNoteEditor } from "../editor/BlockNoteEditor";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BlockFromConfig,
|
|
4
|
+
BlockSchema,
|
|
5
|
+
FileBlockConfig,
|
|
6
|
+
InlineContentSchema,
|
|
7
|
+
StyleSchema,
|
|
8
|
+
} from "../schema";
|
|
3
9
|
import { Block, DefaultBlockSchema, defaultBlockSchema } from "./defaultBlocks";
|
|
4
10
|
import { defaultProps } from "./defaultProps";
|
|
5
11
|
|
|
@@ -33,6 +39,52 @@ export function checkBlockIsDefaultType<
|
|
|
33
39
|
);
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
export function checkBlockIsFileBlock<
|
|
43
|
+
B extends BlockSchema,
|
|
44
|
+
I extends InlineContentSchema,
|
|
45
|
+
S extends StyleSchema
|
|
46
|
+
>(
|
|
47
|
+
block: Block<any, I, S>,
|
|
48
|
+
editor: BlockNoteEditor<B, I, S>
|
|
49
|
+
): block is BlockFromConfig<FileBlockConfig, I, S> {
|
|
50
|
+
return (
|
|
51
|
+
(block.type in editor.schema.blockSchema &&
|
|
52
|
+
editor.schema.blockSchema[block.type].isFileBlock) ||
|
|
53
|
+
false
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function checkBlockIsFileBlockWithPreview<
|
|
58
|
+
B extends BlockSchema,
|
|
59
|
+
I extends InlineContentSchema,
|
|
60
|
+
S extends StyleSchema
|
|
61
|
+
>(
|
|
62
|
+
block: Block<any, I, S>,
|
|
63
|
+
editor: BlockNoteEditor<B, I, S>
|
|
64
|
+
): block is BlockFromConfig<
|
|
65
|
+
FileBlockConfig & {
|
|
66
|
+
propSchema: Required<FileBlockConfig["propSchema"]>;
|
|
67
|
+
},
|
|
68
|
+
I,
|
|
69
|
+
S
|
|
70
|
+
> {
|
|
71
|
+
return (
|
|
72
|
+
(block.type in editor.schema.blockSchema &&
|
|
73
|
+
editor.schema.blockSchema[block.type].isFileBlock &&
|
|
74
|
+
"showPreview" in editor.schema.blockSchema[block.type].propSchema) ||
|
|
75
|
+
false
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function checkBlockIsFileBlockWithPlaceholder<
|
|
80
|
+
B extends BlockSchema,
|
|
81
|
+
I extends InlineContentSchema,
|
|
82
|
+
S extends StyleSchema
|
|
83
|
+
>(block: Block<B, I, S>, editor: BlockNoteEditor<B, I, S>) {
|
|
84
|
+
const config = editor.schema.blockSchema[block.type];
|
|
85
|
+
return config.isFileBlock && !block.props.url;
|
|
86
|
+
}
|
|
87
|
+
|
|
36
88
|
export function checkBlockTypeHasDefaultProp<
|
|
37
89
|
Prop extends keyof typeof defaultProps,
|
|
38
90
|
I extends InlineContentSchema,
|
|
@@ -19,20 +19,29 @@ import {
|
|
|
19
19
|
getInlineContentSchemaFromSpecs,
|
|
20
20
|
getStyleSchemaFromSpecs,
|
|
21
21
|
} from "../schema";
|
|
22
|
+
|
|
22
23
|
import { Heading } from "./HeadingBlockContent/HeadingBlockContent";
|
|
23
|
-
import { Image } from "./ImageBlockContent/ImageBlockContent";
|
|
24
24
|
import { BulletListItem } from "./ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent";
|
|
25
25
|
import { NumberedListItem } from "./ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent";
|
|
26
|
+
import { CheckListItem } from "./ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent";
|
|
26
27
|
import { Paragraph } from "./ParagraphBlockContent/ParagraphBlockContent";
|
|
27
28
|
import { Table } from "./TableBlockContent/TableBlockContent";
|
|
29
|
+
import { FileBlock } from "./FileBlockContent/FileBlockContent";
|
|
30
|
+
import { ImageBlock } from "./ImageBlockContent/ImageBlockContent";
|
|
31
|
+
import { VideoBlock } from "./VideoBlockContent/VideoBlockContent";
|
|
32
|
+
import { AudioBlock } from "./AudioBlockContent/AudioBlockContent";
|
|
28
33
|
|
|
29
34
|
export const defaultBlockSpecs = {
|
|
30
35
|
paragraph: Paragraph,
|
|
31
36
|
heading: Heading,
|
|
32
37
|
bulletListItem: BulletListItem,
|
|
33
38
|
numberedListItem: NumberedListItem,
|
|
34
|
-
|
|
39
|
+
checkListItem: CheckListItem,
|
|
35
40
|
table: Table,
|
|
41
|
+
file: FileBlock,
|
|
42
|
+
image: ImageBlock,
|
|
43
|
+
video: VideoBlock,
|
|
44
|
+
audio: AudioBlock,
|
|
36
45
|
} satisfies BlockSpecs;
|
|
37
46
|
|
|
38
47
|
export const defaultBlockSchema = getBlockSchemaFromSpecs(defaultBlockSpecs);
|