@blocknote/core 0.29.1 → 0.30.1
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/README.md +125 -0
- package/dist/blocknote.cjs +9 -9
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +1501 -1359
- package/dist/blocknote.js.map +1 -1
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js.map +1 -1
- package/dist/locales.cjs +1 -1
- package/dist/locales.cjs.map +1 -1
- package/dist/locales.js +751 -9
- package/dist/locales.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +7 -8
- package/src/api/README.md +1 -1
- package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +0 -7
- package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.test.ts +19 -14
- package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +5 -5
- package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +0 -5
- package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts +3 -3
- package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts +12 -12
- package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +0 -20
- package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.ts +14 -14
- package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts +16 -16
- package/src/api/blockManipulation/commands/nestBlock/nestBlock.ts +8 -8
- package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +0 -12
- package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.ts +12 -12
- package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +7 -7
- package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +0 -6
- package/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts +10 -10
- package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +2 -2
- package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +0 -17
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +42 -42
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +18 -18
- package/src/api/blockManipulation/getBlock/getBlock.ts +9 -9
- package/src/api/blockManipulation/insertContentAt.ts +1 -1
- package/src/api/blockManipulation/selections/selection.ts +11 -11
- package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +7 -7
- package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.ts +6 -6
- package/src/api/blockManipulation/tables/tables.test.ts +106 -106
- package/src/api/blockManipulation/tables/tables.ts +35 -35
- package/src/api/clipboard/fromClipboard/fileDropExtension.ts +2 -2
- package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +9 -9
- package/src/api/clipboard/fromClipboard/handleVSCodePaste.ts +3 -3
- package/src/api/clipboard/fromClipboard/pasteExtension.ts +21 -3
- package/src/api/clipboard/toClipboard/copyExtension.ts +22 -22
- package/src/api/exporters/html/externalHTMLExporter.ts +6 -6
- package/src/api/exporters/html/internalHTMLSerializer.ts +3 -3
- package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +16 -16
- package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +14 -14
- package/src/api/exporters/markdown/markdownExporter.ts +3 -3
- package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +3 -3
- package/src/api/getBlockInfoFromPos.ts +6 -6
- package/src/api/nodeConversions/blockToNode.ts +26 -26
- package/src/api/nodeConversions/fragmentToBlocks.ts +1 -1
- package/src/api/nodeConversions/nodeToBlock.ts +37 -33
- package/src/api/nodeUtil.test.ts +16 -16
- package/src/api/nodeUtil.ts +10 -10
- package/src/api/parsers/html/parseHTML.ts +1 -1
- package/src/api/parsers/html/util/nestedLists.ts +2 -2
- package/src/api/parsers/markdown/parseMarkdown.ts +1 -1
- package/src/api/pmUtil.ts +4 -4
- package/src/api/positionMapping.test.ts +3 -3
- package/src/api/positionMapping.ts +5 -5
- package/src/blocks/AudioBlockContent/AudioBlockContent.ts +9 -4
- package/src/blocks/CodeBlockContent/CodeBlockContent.ts +40 -26
- package/src/blocks/FileBlockContent/FileBlockContent.ts +7 -2
- package/src/blocks/FileBlockContent/helpers/parse/parseFigureElement.ts +2 -2
- package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +5 -5
- package/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.ts +2 -2
- package/src/blocks/FileBlockContent/helpers/render/createFileNameWithIcon.ts +1 -1
- package/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.ts +15 -8
- package/src/blocks/FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.ts +1 -1
- package/src/blocks/FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.ts +1 -1
- package/src/blocks/FileBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts +2 -2
- package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +9 -6
- package/src/blocks/ImageBlockContent/ImageBlockContent.ts +14 -6
- package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +13 -29
- package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +24 -13
- package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +1 -1
- package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +1 -1
- package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +13 -30
- package/src/blocks/ListItemBlockContent/getListItemContent.ts +115 -0
- package/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts +1 -1
- package/src/blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.ts +3 -3
- package/src/blocks/PageBreakBlockContent/schema.ts +2 -2
- package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +9 -5
- package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +10 -5
- package/src/blocks/README.md +1 -1
- package/src/blocks/TableBlockContent/TableBlockContent.ts +76 -19
- package/src/blocks/TableBlockContent/TableExtension.ts +3 -3
- package/src/blocks/VideoBlockContent/VideoBlockContent.ts +14 -6
- package/src/blocks/defaultBlockHelpers.ts +24 -8
- package/src/blocks/defaultBlockTypeGuards.ts +16 -16
- package/src/blocks/defaultBlocks.ts +3 -3
- package/src/comments/threadstore/DefaultThreadStoreAuth.ts +3 -3
- package/src/comments/threadstore/ThreadStore.ts +1 -1
- package/src/comments/threadstore/TipTapThreadStore.ts +10 -10
- package/src/comments/threadstore/yjs/RESTYjsThreadStore.ts +4 -4
- package/src/comments/threadstore/yjs/YjsThreadStore.test.ts +2 -2
- package/src/comments/threadstore/yjs/YjsThreadStore.ts +14 -14
- package/src/comments/threadstore/yjs/YjsThreadStoreBase.ts +1 -1
- package/src/comments/threadstore/yjs/yjsHelpers.ts +6 -6
- package/src/editor/Block.css +10 -1
- package/src/editor/BlockNoteEditor.test.ts +3 -3
- package/src/editor/BlockNoteEditor.ts +110 -61
- package/src/editor/BlockNoteExtensions.ts +24 -15
- package/src/editor/BlockNoteSchema.ts +4 -4
- package/src/editor/BlockNoteTipTapEditor.ts +10 -10
- package/src/editor/README.md +1 -1
- package/src/editor/cursorPositionTypes.ts +1 -1
- package/src/editor/editor.css +15 -3
- package/src/editor/selectionTypes.ts +1 -1
- package/src/editor/transformPasted.ts +2 -2
- package/src/exporter/Exporter.ts +5 -5
- package/src/exporter/mapping.ts +7 -7
- package/src/extensions/BackgroundColor/BackgroundColorMark.ts +1 -1
- package/src/extensions/Collaboration/CursorPlugin.ts +152 -0
- package/src/extensions/Collaboration/SyncPlugin.ts +15 -0
- package/src/extensions/Collaboration/UndoPlugin.ts +14 -0
- package/src/extensions/Comments/CommentsPlugin.ts +9 -9
- package/src/extensions/Comments/userstore/UserStore.ts +2 -2
- package/src/extensions/FilePanel/FilePanelPlugin.ts +37 -28
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +6 -8
- package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +29 -26
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +11 -11
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +4 -4
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +10 -10
- package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +2 -2
- package/src/extensions/README.md +1 -1
- package/src/extensions/SideMenu/MultipleNodeSelection.ts +1 -1
- package/src/extensions/SideMenu/SideMenuPlugin.ts +31 -31
- package/src/extensions/SideMenu/dragging.ts +8 -8
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +17 -17
- package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +2 -2
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +12 -12
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +54 -53
- package/src/extensions/TrailingNode/TrailingNodeExtension.ts +1 -1
- package/src/extensions/UniqueID/UniqueID.ts +6 -6
- package/src/extensions/getDraggableBlockFromElement.ts +1 -1
- package/src/fonts/inter.css +18 -9
- package/src/i18n/locales/index.ts +2 -0
- package/src/i18n/locales/ru.ts +2 -2
- package/src/i18n/locales/sk.ts +355 -0
- package/src/i18n/locales/zh-tw.ts +390 -0
- package/src/locales.ts +1 -1
- package/src/pm-nodes/BlockContainer.ts +7 -6
- package/src/pm-nodes/BlockGroup.ts +1 -1
- package/src/pm-nodes/Doc.ts +4 -4
- package/src/schema/README.md +1 -1
- package/src/schema/blocks/createSpec.ts +15 -15
- package/src/schema/blocks/internal.ts +17 -18
- package/src/schema/blocks/types.ts +27 -26
- package/src/schema/inlineContent/createSpec.ts +16 -20
- package/src/schema/inlineContent/internal.ts +9 -9
- package/src/schema/inlineContent/types.ts +26 -26
- package/src/schema/propTypes.ts +8 -8
- package/src/schema/styles/createSpec.ts +2 -2
- package/src/schema/styles/internal.ts +7 -7
- package/src/schema/styles/types.ts +2 -2
- package/src/util/EventEmitter.ts +4 -4
- package/src/util/README.md +1 -1
- package/src/util/combineByGroup.ts +1 -1
- package/src/util/table.ts +33 -30
- package/types/src/api/blockManipulation/setupTestEnv.d.ts +8 -4
- package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +8 -4
- package/types/src/blocks/ListItemBlockContent/getListItemContent.d.ts +28 -0
- package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +8 -4
- package/types/src/blocks/defaultBlockHelpers.d.ts +1 -0
- package/types/src/blocks/defaultBlocks.d.ts +16 -8
- package/types/src/editor/BlockNoteEditor.d.ts +21 -2
- package/types/src/extensions/Collaboration/CursorPlugin.d.ts +31 -0
- package/types/src/extensions/Collaboration/SyncPlugin.d.ts +7 -0
- package/types/src/extensions/Collaboration/UndoPlugin.d.ts +6 -0
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +1 -1
- package/types/src/i18n/locales/index.d.ts +2 -0
- package/types/src/i18n/locales/sk.d.ts +313 -0
- package/types/src/i18n/locales/zh-tw.d.ts +2 -0
- package/types/src/schema/blocks/types.d.ts +2 -1
- package/src/extensions/Collaboration/createCollaborationExtensions.ts +0 -147
- package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +0 -17
package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "../../../schema/index.js";
|
|
13
13
|
import { createDefaultBlockDOMOutputSpec } from "../../defaultBlockHelpers.js";
|
|
14
14
|
import { defaultProps } from "../../defaultProps.js";
|
|
15
|
+
import { getListItemContent } from "../getListItemContent.js";
|
|
15
16
|
import { handleEnter } from "../ListItemKeyboardShortcuts.js";
|
|
16
17
|
|
|
17
18
|
export const checkListItemPropSchema = {
|
|
@@ -51,7 +52,7 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
51
52
|
props: {
|
|
52
53
|
checked: false as any,
|
|
53
54
|
},
|
|
54
|
-
})
|
|
55
|
+
}),
|
|
55
56
|
)
|
|
56
57
|
// Removes the characters used to set the list.
|
|
57
58
|
.deleteRange({ from: range.from, to: range.to });
|
|
@@ -76,7 +77,7 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
76
77
|
props: {
|
|
77
78
|
checked: true as any,
|
|
78
79
|
},
|
|
79
|
-
})
|
|
80
|
+
}),
|
|
80
81
|
)
|
|
81
82
|
// Removes the characters used to set the list.
|
|
82
83
|
.deleteRange({ from: range.from, to: range.to });
|
|
@@ -101,7 +102,7 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
101
102
|
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
102
103
|
type: "checkListItem",
|
|
103
104
|
props: {},
|
|
104
|
-
})
|
|
105
|
+
}),
|
|
105
106
|
);
|
|
106
107
|
},
|
|
107
108
|
};
|
|
@@ -109,10 +110,12 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
109
110
|
|
|
110
111
|
parseHTML() {
|
|
111
112
|
return [
|
|
113
|
+
// Parse from internal HTML.
|
|
112
114
|
{
|
|
113
115
|
tag: "div[data-content-type=" + this.name + "]",
|
|
116
|
+
contentElement: ".bn-inline-content",
|
|
114
117
|
},
|
|
115
|
-
//
|
|
118
|
+
// Parse from external HTML.
|
|
116
119
|
{
|
|
117
120
|
tag: "input",
|
|
118
121
|
getAttrs: (element) => {
|
|
@@ -120,6 +123,11 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
120
123
|
return false;
|
|
121
124
|
}
|
|
122
125
|
|
|
126
|
+
// Ignore if we already parsed an ancestor list item to avoid double-parsing.
|
|
127
|
+
if (element.closest("[data-content-type]") || element.closest("li")) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
123
131
|
if ((element as HTMLInputElement).type === "checkbox") {
|
|
124
132
|
return { checked: (element as HTMLInputElement).checked };
|
|
125
133
|
}
|
|
@@ -128,7 +136,6 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
128
136
|
},
|
|
129
137
|
node: "checkListItem",
|
|
130
138
|
},
|
|
131
|
-
// Container element for checkbox + label.
|
|
132
139
|
{
|
|
133
140
|
tag: "li",
|
|
134
141
|
getAttrs: (element) => {
|
|
@@ -144,11 +151,11 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
144
151
|
|
|
145
152
|
if (
|
|
146
153
|
parent.tagName === "UL" ||
|
|
147
|
-
(parent.tagName === "DIV" && parent.parentElement
|
|
154
|
+
(parent.tagName === "DIV" && parent.parentElement?.tagName === "UL")
|
|
148
155
|
) {
|
|
149
156
|
const checkbox =
|
|
150
157
|
(element.querySelector(
|
|
151
|
-
"input[type=checkbox]"
|
|
158
|
+
"input[type=checkbox]",
|
|
152
159
|
) as HTMLInputElement) || null;
|
|
153
160
|
|
|
154
161
|
if (checkbox === null) {
|
|
@@ -160,6 +167,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
160
167
|
|
|
161
168
|
return false;
|
|
162
169
|
},
|
|
170
|
+
// As `li` elements can contain multiple paragraphs, we need to merge their contents
|
|
171
|
+
// into a single one so that ProseMirror can parse everything correctly.
|
|
172
|
+
getContent: (node, schema) =>
|
|
173
|
+
getListItemContent(node, schema, this.name),
|
|
163
174
|
node: "checkListItem",
|
|
164
175
|
},
|
|
165
176
|
];
|
|
@@ -185,7 +196,7 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
185
196
|
...(this.options.domAttributes?.blockContent || {}),
|
|
186
197
|
...HTMLAttributes,
|
|
187
198
|
},
|
|
188
|
-
this.options.domAttributes?.inlineContent || {}
|
|
199
|
+
this.options.domAttributes?.inlineContent || {},
|
|
189
200
|
);
|
|
190
201
|
|
|
191
202
|
dom.insertBefore(checkbox, contentDOM);
|
|
@@ -223,12 +234,12 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
223
234
|
if (typeof getPos !== "boolean") {
|
|
224
235
|
const beforeBlockContainerPos = getNearestBlockPos(
|
|
225
236
|
editor.state.doc,
|
|
226
|
-
getPos()
|
|
237
|
+
getPos(),
|
|
227
238
|
);
|
|
228
239
|
|
|
229
240
|
if (beforeBlockContainerPos.node.type.name !== "blockContainer") {
|
|
230
241
|
throw new Error(
|
|
231
|
-
`Expected blockContainer node, got ${beforeBlockContainerPos.node.type.name}
|
|
242
|
+
`Expected blockContainer node, got ${beforeBlockContainerPos.node.type.name}`,
|
|
232
243
|
);
|
|
233
244
|
}
|
|
234
245
|
|
|
@@ -238,7 +249,7 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
238
249
|
props: {
|
|
239
250
|
checked: checkbox.checked as any,
|
|
240
251
|
},
|
|
241
|
-
})
|
|
252
|
+
}),
|
|
242
253
|
);
|
|
243
254
|
}
|
|
244
255
|
};
|
|
@@ -251,7 +262,7 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
251
262
|
...(this.options.domAttributes?.blockContent || {}),
|
|
252
263
|
...HTMLAttributes,
|
|
253
264
|
},
|
|
254
|
-
this.options.domAttributes?.inlineContent || {}
|
|
265
|
+
this.options.domAttributes?.inlineContent || {},
|
|
255
266
|
);
|
|
256
267
|
|
|
257
268
|
if (typeof getPos !== "boolean") {
|
|
@@ -284,5 +295,5 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
284
295
|
|
|
285
296
|
export const CheckListItem = createBlockSpecFromStronglyTypedTiptapNode(
|
|
286
297
|
checkListItemBlockContent,
|
|
287
|
-
checkListItemPropSchema
|
|
298
|
+
checkListItemPropSchema,
|
|
288
299
|
);
|
package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts
CHANGED
|
@@ -35,7 +35,7 @@ export const NumberedListIndexingPlugin = () => {
|
|
|
35
35
|
// first block in its nesting level, or the previous block is not an ordered list item.
|
|
36
36
|
|
|
37
37
|
const prevBlock = tr.doc.resolve(
|
|
38
|
-
blockInfo.bnBlock.beforePos
|
|
38
|
+
blockInfo.bnBlock.beforePos,
|
|
39
39
|
).nodeBefore;
|
|
40
40
|
|
|
41
41
|
if (prevBlock) {
|
package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "../../../schema/index.js";
|
|
10
10
|
import { createDefaultBlockDOMOutputSpec } from "../../defaultBlockHelpers.js";
|
|
11
11
|
import { defaultProps } from "../../defaultProps.js";
|
|
12
|
+
import { getListItemContent } from "../getListItemContent.js";
|
|
12
13
|
import { handleEnter } from "../ListItemKeyboardShortcuts.js";
|
|
13
14
|
import { NumberedListIndexingPlugin } from "./NumberedListIndexingPlugin.js";
|
|
14
15
|
|
|
@@ -64,7 +65,7 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
64
65
|
({
|
|
65
66
|
start: startIndex,
|
|
66
67
|
} as any),
|
|
67
|
-
})
|
|
68
|
+
}),
|
|
68
69
|
)
|
|
69
70
|
// Removes the "1." characters used to set the list.
|
|
70
71
|
.deleteRange({ from: range.from, to: range.to });
|
|
@@ -89,7 +90,7 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
89
90
|
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
90
91
|
type: "numberedListItem",
|
|
91
92
|
props: {},
|
|
92
|
-
})
|
|
93
|
+
}),
|
|
93
94
|
);
|
|
94
95
|
},
|
|
95
96
|
};
|
|
@@ -101,11 +102,12 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
101
102
|
|
|
102
103
|
parseHTML() {
|
|
103
104
|
return [
|
|
105
|
+
// Parse from internal HTML.
|
|
104
106
|
{
|
|
105
107
|
tag: "div[data-content-type=" + this.name + "]",
|
|
108
|
+
contentElement: ".bn-inline-content",
|
|
106
109
|
},
|
|
107
|
-
//
|
|
108
|
-
// (e.g.: when pasting from other apps)
|
|
110
|
+
// Parse from external HTML.
|
|
109
111
|
{
|
|
110
112
|
tag: "li",
|
|
111
113
|
getAttrs: (element) => {
|
|
@@ -121,7 +123,7 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
121
123
|
|
|
122
124
|
if (
|
|
123
125
|
parent.tagName === "OL" ||
|
|
124
|
-
(parent.tagName === "DIV" && parent.parentElement
|
|
126
|
+
(parent.tagName === "DIV" && parent.parentElement?.tagName === "OL")
|
|
125
127
|
) {
|
|
126
128
|
const startIndex =
|
|
127
129
|
parseInt(parent.getAttribute("start") || "1") || 1;
|
|
@@ -137,29 +139,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
137
139
|
|
|
138
140
|
return false;
|
|
139
141
|
},
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
tag: "p",
|
|
146
|
-
getAttrs: (element) => {
|
|
147
|
-
if (typeof element === "string") {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const parent = element.parentElement;
|
|
152
|
-
|
|
153
|
-
if (parent === null) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (parent.getAttribute("data-content-type") === "numberedListItem") {
|
|
158
|
-
return {};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return false;
|
|
162
|
-
},
|
|
142
|
+
// As `li` elements can contain multiple paragraphs, we need to merge their contents
|
|
143
|
+
// into a single one so that ProseMirror can parse everything correctly.
|
|
144
|
+
getContent: (node, schema) =>
|
|
145
|
+
getListItemContent(node, schema, this.name),
|
|
163
146
|
priority: 300,
|
|
164
147
|
node: "numberedListItem",
|
|
165
148
|
},
|
|
@@ -177,12 +160,12 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
177
160
|
...(this.options.domAttributes?.blockContent || {}),
|
|
178
161
|
...HTMLAttributes,
|
|
179
162
|
},
|
|
180
|
-
this.options.domAttributes?.inlineContent || {}
|
|
163
|
+
this.options.domAttributes?.inlineContent || {},
|
|
181
164
|
);
|
|
182
165
|
},
|
|
183
166
|
});
|
|
184
167
|
|
|
185
168
|
export const NumberedListItem = createBlockSpecFromStronglyTypedTiptapNode(
|
|
186
169
|
NumberedListItemBlockContent,
|
|
187
|
-
numberedListItemPropSchema
|
|
170
|
+
numberedListItemPropSchema,
|
|
188
171
|
);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { DOMParser, Fragment, Schema } from "prosemirror-model";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This function is used to parse the content of a list item external HTML node.
|
|
5
|
+
*
|
|
6
|
+
* Due to a change in how prosemirror-model handles parsing elements, we have additional flexibility in how we can "fit" content into a list item.
|
|
7
|
+
*
|
|
8
|
+
* We've decided to take an approach that is similar to Notion. The core rules of the algorithm are:
|
|
9
|
+
*
|
|
10
|
+
* - If the first child of an `li` has ONLY text content, take the text content, and flatten it into the list item. Subsequent siblings are carried over as is, as children of the list item.
|
|
11
|
+
* - e.g. `<li><h1>Hello</h1><p>World</p></li> -> <li>Hello<blockGroup><blockContainer><p>World</p></blockContainer></blockGroup></li>`
|
|
12
|
+
* - Else, take the content and insert it as children instead.
|
|
13
|
+
* - e.g. `<li><img src="url" /></li> -> <li><p></p><blockGroup><blockContainer><img src="url" /></blockContainer></blockGroup></li>`
|
|
14
|
+
*
|
|
15
|
+
* This ensures that a list item's content is always valid ProseMirror content. Smoothing over differences between how external HTML may be rendered, and how ProseMirror expects content to be structured.
|
|
16
|
+
*/
|
|
17
|
+
export function getListItemContent(
|
|
18
|
+
/**
|
|
19
|
+
* The `li` element to parse.
|
|
20
|
+
*/
|
|
21
|
+
_node: Node,
|
|
22
|
+
/**
|
|
23
|
+
* The schema to use for parsing.
|
|
24
|
+
*/
|
|
25
|
+
schema: Schema,
|
|
26
|
+
/**
|
|
27
|
+
* The name of the list item node.
|
|
28
|
+
*/
|
|
29
|
+
name: string,
|
|
30
|
+
): Fragment {
|
|
31
|
+
/**
|
|
32
|
+
* To actually implement this algorithm, we need to leverage ProseMirror's "fitting" algorithm.
|
|
33
|
+
* Where, if content is parsed which doesn't fit into the current node, it will be moved into the parent node.
|
|
34
|
+
*
|
|
35
|
+
* This allows us to parse multiple pieces of content from within the list item (even though it normally would not match the list item's schema) and "throw" the excess content into the list item's children.
|
|
36
|
+
*
|
|
37
|
+
* The expected return value is a `Fragment` which contains the list item's content as the first element, and the children wrapped in a blockGroup node. Like so:
|
|
38
|
+
* ```
|
|
39
|
+
* Fragment<[Node<Text>, Node<BlockGroup<Node<BlockContainer<any>>>>]>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
const parser = DOMParser.fromSchema(schema);
|
|
43
|
+
|
|
44
|
+
// TODO: This will be unnecessary in the future: https://github.com/ProseMirror/prosemirror-model/commit/166188d4f9db96eb86fb7de62e72049c86c9dd79
|
|
45
|
+
const node = _node as HTMLElement;
|
|
46
|
+
|
|
47
|
+
// Move the `li` element's content into a new `div` element
|
|
48
|
+
// This is a hacky workaround to not re-trigger list item parsing,
|
|
49
|
+
// when we are looking to understand what the list item's content actually is, in terms of the schema.
|
|
50
|
+
const clonedNodeDiv = document.createElement("div");
|
|
51
|
+
// Mark the `div` element as a `blockGroup` to make the parsing easier.
|
|
52
|
+
clonedNodeDiv.setAttribute("data-node-type", "blockGroup");
|
|
53
|
+
// Clone all children of the `li` element into the new `div` element
|
|
54
|
+
for (const child of Array.from(node.childNodes)) {
|
|
55
|
+
clonedNodeDiv.appendChild(child.cloneNode(true));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Parses children of the `li` element into a `blockGroup` with `blockContainer` node children
|
|
59
|
+
// This is the structure of list item children, so parsing into this structure allows for
|
|
60
|
+
// easy separation of list item content from child list item content.
|
|
61
|
+
let blockGroupNode = parser.parse(clonedNodeDiv, {
|
|
62
|
+
topNode: schema.nodes.blockGroup.create(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// There is an edge case where a list item's content may contain a `<input>` element.
|
|
66
|
+
// Causing it to be recognized as a `checkListItem`.
|
|
67
|
+
// We want to skip this, and just parse the list item's content as is.
|
|
68
|
+
if (blockGroupNode.firstChild?.firstChild?.type.name === "checkListItem") {
|
|
69
|
+
// We skip the first child, by cutting it out of the `blockGroup` node.
|
|
70
|
+
// and continuing with the rest of the algorithm.
|
|
71
|
+
blockGroupNode = blockGroupNode.copy(
|
|
72
|
+
blockGroupNode.content.cut(
|
|
73
|
+
blockGroupNode.firstChild.firstChild.nodeSize + 2,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Structure above is `blockGroup<blockContainer<any>[]>`
|
|
79
|
+
// We want to extract the first `blockContainer` node's content, and see if it is a text block.
|
|
80
|
+
const listItemsFirstChild = blockGroupNode.firstChild?.firstChild;
|
|
81
|
+
|
|
82
|
+
// If the first node is not a text block, then it's first child is not compatible with the list item node.
|
|
83
|
+
if (!listItemsFirstChild?.isTextblock) {
|
|
84
|
+
// So, we do not try inserting anything into the list item, and instead return anything we found as children for the list item.
|
|
85
|
+
return Fragment.from(blockGroupNode);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// If it is a text block, then we know it only contains text content.
|
|
89
|
+
// So, we extract it, and insert its content into the `listItemNode`.
|
|
90
|
+
// The remaining nodes in the `blockGroup` stay in-place.
|
|
91
|
+
const listItemNode = schema.nodes[name].create(
|
|
92
|
+
{},
|
|
93
|
+
listItemsFirstChild.content,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// We have `blockGroup<listItemsFirstChild, ...blockContainer<any>[]>`
|
|
97
|
+
// We want to extract out the rest of the nodes as `<...blockContainer<any>[]>`
|
|
98
|
+
const remainingListItemChildren = blockGroupNode.content.cut(
|
|
99
|
+
// +2 for the `blockGroup` node's start and end markers
|
|
100
|
+
listItemsFirstChild.nodeSize + 2,
|
|
101
|
+
);
|
|
102
|
+
const hasRemainingListItemChildren = remainingListItemChildren.size > 0;
|
|
103
|
+
|
|
104
|
+
if (hasRemainingListItemChildren) {
|
|
105
|
+
// Copy the remaining list item children back into the `blockGroup` node.
|
|
106
|
+
// This will make it back into: `blockGroup<...blockContainer<any>[]>`
|
|
107
|
+
const listItemsChildren = blockGroupNode.copy(remainingListItemChildren);
|
|
108
|
+
|
|
109
|
+
// Return the `listItem` node's content, then add the parsed children after to be lifted out by ProseMirror "fitting" algorithm.
|
|
110
|
+
return listItemNode.content.addToEnd(listItemsChildren);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Otherwise, just return the `listItem` node's content.
|
|
114
|
+
return listItemNode.content;
|
|
115
|
+
}
|
|
@@ -22,7 +22,7 @@ export const pageBreakRender = () => {
|
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
export const pageBreakParse = (
|
|
25
|
-
element: HTMLElement
|
|
25
|
+
element: HTMLElement,
|
|
26
26
|
): Partial<Props<typeof pageBreakConfig.propSchema>> | undefined => {
|
|
27
27
|
if (element.tagName === "DIV" && element.hasAttribute("data-page-break")) {
|
|
28
28
|
return {
|
|
@@ -10,9 +10,9 @@ import { pageBreakSchema } from "./schema.js";
|
|
|
10
10
|
|
|
11
11
|
export function checkPageBreakBlocksInSchema<
|
|
12
12
|
I extends InlineContentSchema,
|
|
13
|
-
S extends StyleSchema
|
|
13
|
+
S extends StyleSchema,
|
|
14
14
|
>(
|
|
15
|
-
editor: BlockNoteEditor<any, I, S
|
|
15
|
+
editor: BlockNoteEditor<any, I, S>,
|
|
16
16
|
): editor is BlockNoteEditor<typeof pageBreakSchema.blockSchema, I, S> {
|
|
17
17
|
return (
|
|
18
18
|
"pageBreak" in editor.schema.blockSchema &&
|
|
@@ -24,7 +24,7 @@ export function checkPageBreakBlocksInSchema<
|
|
|
24
24
|
export function getPageBreakSlashMenuItems<
|
|
25
25
|
BSchema extends BlockSchema,
|
|
26
26
|
I extends InlineContentSchema,
|
|
27
|
-
S extends StyleSchema
|
|
27
|
+
S extends StyleSchema,
|
|
28
28
|
>(editor: BlockNoteEditor<BSchema, I, S>) {
|
|
29
29
|
const items: (Omit<DefaultSuggestionItem, "key"> & { key: "page_break" })[] =
|
|
30
30
|
[];
|
|
@@ -18,9 +18,9 @@ export const pageBreakSchema = BlockNoteSchema.create({
|
|
|
18
18
|
export const withPageBreak = <
|
|
19
19
|
B extends BlockSchema,
|
|
20
20
|
I extends InlineContentSchema,
|
|
21
|
-
S extends StyleSchema
|
|
21
|
+
S extends StyleSchema,
|
|
22
22
|
>(
|
|
23
|
-
schema: BlockNoteSchema<B, I, S
|
|
23
|
+
schema: BlockNoteSchema<B, I, S>,
|
|
24
24
|
) => {
|
|
25
25
|
return BlockNoteSchema.create({
|
|
26
26
|
blockSpecs: {
|
|
@@ -31,7 +31,7 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
|
|
|
31
31
|
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
32
32
|
type: "paragraph",
|
|
33
33
|
props: {},
|
|
34
|
-
})
|
|
34
|
+
}),
|
|
35
35
|
);
|
|
36
36
|
},
|
|
37
37
|
};
|
|
@@ -39,10 +39,14 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
|
|
|
39
39
|
|
|
40
40
|
parseHTML() {
|
|
41
41
|
return [
|
|
42
|
-
|
|
42
|
+
// Parse from internal HTML.
|
|
43
|
+
{
|
|
44
|
+
tag: "div[data-content-type=" + this.name + "]",
|
|
45
|
+
contentElement: ".bn-inline-content",
|
|
46
|
+
},
|
|
47
|
+
// Parse from external HTML.
|
|
43
48
|
{
|
|
44
49
|
tag: "p",
|
|
45
|
-
priority: 200,
|
|
46
50
|
getAttrs: (element) => {
|
|
47
51
|
if (typeof element === "string" || !element.textContent?.trim()) {
|
|
48
52
|
return false;
|
|
@@ -63,12 +67,12 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
|
|
|
63
67
|
...(this.options.domAttributes?.blockContent || {}),
|
|
64
68
|
...HTMLAttributes,
|
|
65
69
|
},
|
|
66
|
-
this.options.domAttributes?.inlineContent || {}
|
|
70
|
+
this.options.domAttributes?.inlineContent || {},
|
|
67
71
|
);
|
|
68
72
|
},
|
|
69
73
|
});
|
|
70
74
|
|
|
71
75
|
export const Paragraph = createBlockSpecFromStronglyTypedTiptapNode(
|
|
72
76
|
ParagraphBlockContent,
|
|
73
|
-
paragraphPropSchema
|
|
77
|
+
paragraphPropSchema,
|
|
74
78
|
);
|
|
@@ -36,7 +36,7 @@ export const QuoteBlockContent = createStronglyTypedTiptapNode({
|
|
|
36
36
|
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
37
37
|
type: "quote",
|
|
38
38
|
props: {},
|
|
39
|
-
})
|
|
39
|
+
}),
|
|
40
40
|
)
|
|
41
41
|
// Removes the ">" character used to set the list.
|
|
42
42
|
.deleteRange({ from: range.from, to: range.to });
|
|
@@ -59,7 +59,7 @@ export const QuoteBlockContent = createStronglyTypedTiptapNode({
|
|
|
59
59
|
return this.editor.commands.command(
|
|
60
60
|
updateBlockCommand(blockInfo.bnBlock.beforePos, {
|
|
61
61
|
type: "quote",
|
|
62
|
-
})
|
|
62
|
+
}),
|
|
63
63
|
);
|
|
64
64
|
},
|
|
65
65
|
};
|
|
@@ -67,7 +67,12 @@ export const QuoteBlockContent = createStronglyTypedTiptapNode({
|
|
|
67
67
|
|
|
68
68
|
parseHTML() {
|
|
69
69
|
return [
|
|
70
|
-
|
|
70
|
+
// Parse from internal HTML.
|
|
71
|
+
{
|
|
72
|
+
tag: "div[data-content-type=" + this.name + "]",
|
|
73
|
+
contentElement: ".bn-inline-content",
|
|
74
|
+
},
|
|
75
|
+
// Parse from external HTML.
|
|
71
76
|
{
|
|
72
77
|
tag: "blockquote",
|
|
73
78
|
node: "quote",
|
|
@@ -83,12 +88,12 @@ export const QuoteBlockContent = createStronglyTypedTiptapNode({
|
|
|
83
88
|
...(this.options.domAttributes?.blockContent || {}),
|
|
84
89
|
...HTMLAttributes,
|
|
85
90
|
},
|
|
86
|
-
this.options.domAttributes?.inlineContent || {}
|
|
91
|
+
this.options.domAttributes?.inlineContent || {},
|
|
87
92
|
);
|
|
88
93
|
},
|
|
89
94
|
});
|
|
90
95
|
|
|
91
96
|
export const Quote = createBlockSpecFromStronglyTypedTiptapNode(
|
|
92
97
|
QuoteBlockContent,
|
|
93
|
-
quotePropSchema
|
|
98
|
+
quotePropSchema,
|
|
94
99
|
);
|
package/src/blocks/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TableCell } from "@tiptap/extension-table-cell";
|
|
2
2
|
import { TableHeader } from "@tiptap/extension-table-header";
|
|
3
3
|
import { TableRow } from "@tiptap/extension-table-row";
|
|
4
|
-
import { Node as PMNode } from "prosemirror-model";
|
|
4
|
+
import { DOMParser, Fragment, Node as PMNode, Schema } from "prosemirror-model";
|
|
5
5
|
import { TableView } from "prosemirror-tables";
|
|
6
6
|
|
|
7
7
|
import { NodeView } from "prosemirror-view";
|
|
@@ -27,7 +27,11 @@ export const TableBlockContent = createStronglyTypedTiptapNode({
|
|
|
27
27
|
isolating: true,
|
|
28
28
|
|
|
29
29
|
parseHTML() {
|
|
30
|
-
return [
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
tag: "table",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
31
35
|
},
|
|
32
36
|
|
|
33
37
|
renderHTML({ HTMLAttributes }) {
|
|
@@ -38,7 +42,7 @@ export const TableBlockContent = createStronglyTypedTiptapNode({
|
|
|
38
42
|
...(this.options.domAttributes?.blockContent || {}),
|
|
39
43
|
...HTMLAttributes,
|
|
40
44
|
},
|
|
41
|
-
this.options.domAttributes?.inlineContent || {}
|
|
45
|
+
this.options.domAttributes?.inlineContent || {},
|
|
42
46
|
);
|
|
43
47
|
},
|
|
44
48
|
|
|
@@ -55,18 +59,18 @@ export const TableBlockContent = createStronglyTypedTiptapNode({
|
|
|
55
59
|
constructor(
|
|
56
60
|
public node: PMNode,
|
|
57
61
|
public cellMinWidth: number,
|
|
58
|
-
public blockContentHTMLAttributes: Record<string, string
|
|
62
|
+
public blockContentHTMLAttributes: Record<string, string>,
|
|
59
63
|
) {
|
|
60
64
|
super(node, cellMinWidth);
|
|
61
65
|
|
|
62
66
|
const blockContent = document.createElement("div");
|
|
63
67
|
blockContent.className = mergeCSSClasses(
|
|
64
68
|
"bn-block-content",
|
|
65
|
-
blockContentHTMLAttributes.class
|
|
69
|
+
blockContentHTMLAttributes.class,
|
|
66
70
|
);
|
|
67
71
|
blockContent.setAttribute("data-content-type", "table");
|
|
68
72
|
for (const [attribute, value] of Object.entries(
|
|
69
|
-
blockContentHTMLAttributes
|
|
73
|
+
blockContentHTMLAttributes,
|
|
70
74
|
)) {
|
|
71
75
|
if (attribute !== "class") {
|
|
72
76
|
blockContent.setAttribute(attribute, value);
|
|
@@ -113,17 +117,6 @@ const TableParagraph = createStronglyTypedTiptapNode({
|
|
|
113
117
|
|
|
114
118
|
parseHTML() {
|
|
115
119
|
return [
|
|
116
|
-
{
|
|
117
|
-
preserveWhitespace: "full",
|
|
118
|
-
// set this rule as high priority so it takes precedence over the default paragraph rule,
|
|
119
|
-
// but only if we're in the tableContent context
|
|
120
|
-
priority: 210,
|
|
121
|
-
context: "tableContent",
|
|
122
|
-
tag: "p",
|
|
123
|
-
getAttrs: (_element) => {
|
|
124
|
-
return {};
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
120
|
{
|
|
128
121
|
tag: "p",
|
|
129
122
|
getAttrs: (element) => {
|
|
@@ -131,18 +124,24 @@ const TableParagraph = createStronglyTypedTiptapNode({
|
|
|
131
124
|
return false;
|
|
132
125
|
}
|
|
133
126
|
|
|
127
|
+
// Only parse in internal HTML.
|
|
128
|
+
if (!element.closest("[data-content-type]")) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
134
132
|
const parent = element.parentElement;
|
|
135
133
|
|
|
136
134
|
if (parent === null) {
|
|
137
135
|
return false;
|
|
138
136
|
}
|
|
139
137
|
|
|
140
|
-
if (parent.tagName === "TD") {
|
|
138
|
+
if (parent.tagName === "TD" || parent.tagName === "TH") {
|
|
141
139
|
return {};
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
return false;
|
|
145
143
|
},
|
|
144
|
+
node: "tableParagraph",
|
|
146
145
|
},
|
|
147
146
|
];
|
|
148
147
|
},
|
|
@@ -152,6 +151,42 @@ const TableParagraph = createStronglyTypedTiptapNode({
|
|
|
152
151
|
},
|
|
153
152
|
});
|
|
154
153
|
|
|
154
|
+
/**
|
|
155
|
+
* This will flatten a node's content to fit into a table cell's paragraph.
|
|
156
|
+
*/
|
|
157
|
+
function parseTableContent(node: HTMLElement, schema: Schema) {
|
|
158
|
+
const parser = DOMParser.fromSchema(schema);
|
|
159
|
+
|
|
160
|
+
// This will parse the content of the table paragraph as though it were a blockGroup.
|
|
161
|
+
// Resulting in a structure like:
|
|
162
|
+
// <blockGroup>
|
|
163
|
+
// <blockContainer>
|
|
164
|
+
// <p>Hello</p>
|
|
165
|
+
// </blockContainer>
|
|
166
|
+
// <blockContainer>
|
|
167
|
+
// <p>Hello</p>
|
|
168
|
+
// </blockContainer>
|
|
169
|
+
// </blockGroup>
|
|
170
|
+
const parsedContent = parser.parse(node, {
|
|
171
|
+
topNode: schema.nodes.blockGroup.create(),
|
|
172
|
+
});
|
|
173
|
+
const extractedContent: PMNode[] = [];
|
|
174
|
+
|
|
175
|
+
// Try to extract any content within the blockContainer.
|
|
176
|
+
parsedContent.content.descendants((child) => {
|
|
177
|
+
// As long as the child is an inline node, we can append it to the fragment.
|
|
178
|
+
if (child.isInline) {
|
|
179
|
+
// And append it to the fragment
|
|
180
|
+
extractedContent.push(child);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return undefined;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return Fragment.fromArray(extractedContent);
|
|
188
|
+
}
|
|
189
|
+
|
|
155
190
|
export const Table = createBlockSpecFromStronglyTypedTiptapNode(
|
|
156
191
|
TableBlockContent,
|
|
157
192
|
tablePropSchema,
|
|
@@ -167,10 +202,32 @@ export const Table = createBlockSpecFromStronglyTypedTiptapNode(
|
|
|
167
202
|
* So, we manually fix this up when reading back in the `nodeToBlock` and only ever place a single tableContent back into the cell.
|
|
168
203
|
*/
|
|
169
204
|
content: "tableContent+",
|
|
205
|
+
parseHTML() {
|
|
206
|
+
return [
|
|
207
|
+
{
|
|
208
|
+
tag: "th",
|
|
209
|
+
// As `th` elements can contain multiple paragraphs, we need to merge their contents
|
|
210
|
+
// into a single one so that ProseMirror can parse everything correctly.
|
|
211
|
+
getContent: (node, schema) =>
|
|
212
|
+
parseTableContent(node as HTMLElement, schema),
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
},
|
|
170
216
|
}),
|
|
171
217
|
TableCell.extend({
|
|
172
218
|
content: "tableContent+",
|
|
219
|
+
parseHTML() {
|
|
220
|
+
return [
|
|
221
|
+
{
|
|
222
|
+
tag: "td",
|
|
223
|
+
// As `td` elements can contain multiple paragraphs, we need to merge their contents
|
|
224
|
+
// into a single one so that ProseMirror can parse everything correctly.
|
|
225
|
+
getContent: (node, schema) =>
|
|
226
|
+
parseTableContent(node as HTMLElement, schema),
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
},
|
|
173
230
|
}),
|
|
174
231
|
TableRow,
|
|
175
|
-
]
|
|
232
|
+
],
|
|
176
233
|
);
|