@blocknote/core 0.4.3 → 0.4.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 +522 -521
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +10 -10
- package/dist/blocknote.umd.cjs.map +1 -1
- package/package.json +2 -2
- package/src/BlockNoteEditor.ts +102 -63
- package/src/api/formatConversions/formatConversions.ts +4 -4
- package/src/extensions/Blocks/nodes/BlockContainer.ts +84 -111
- package/types/src/BlockNoteEditor.d.ts +62 -44
- package/types/src/api/formatConversions/formatConversions.d.ts +2 -2
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://github.com/yousefed/blocknote",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "0.4.
|
|
6
|
+
"version": "0.4.4",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"types",
|
|
@@ -106,5 +106,5 @@
|
|
|
106
106
|
"access": "public",
|
|
107
107
|
"registry": "https://registry.npmjs.org/"
|
|
108
108
|
},
|
|
109
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "9d8f356669e7f644ee437aad0df07b4cc37a0083"
|
|
110
110
|
}
|
package/src/BlockNoteEditor.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Editor, EditorOptions } from "@tiptap/core";
|
|
2
2
|
import { Node } from "prosemirror-model";
|
|
3
3
|
// import "./blocknote.css";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Block,
|
|
6
|
+
BlockIdentifier,
|
|
7
|
+
PartialBlock,
|
|
8
|
+
} from "./extensions/Blocks/api/blockTypes";
|
|
5
9
|
import { getBlockNoteExtensions, UiFactories } from "./BlockNoteExtensions";
|
|
6
10
|
import styles from "./editor.module.css";
|
|
7
11
|
import {
|
|
@@ -34,8 +38,9 @@ export type BlockNoteEditorOptions = {
|
|
|
34
38
|
slashCommands: BaseSlashMenuItem[];
|
|
35
39
|
parentElement: HTMLElement;
|
|
36
40
|
editorDOMAttributes: Record<string, string>;
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
onEditorCreate: (editor: BlockNoteEditor) => void;
|
|
42
|
+
onEditorContentChange: (editor: BlockNoteEditor) => void;
|
|
43
|
+
onTextCursorPositionChange: (editor: BlockNoteEditor) => void;
|
|
39
44
|
|
|
40
45
|
// tiptap options, undocumented
|
|
41
46
|
_tiptapOptions: any;
|
|
@@ -69,11 +74,14 @@ export class BlockNoteEditor {
|
|
|
69
74
|
const tiptapOptions: EditorOptions = {
|
|
70
75
|
...blockNoteTipTapOptions,
|
|
71
76
|
...options._tiptapOptions,
|
|
77
|
+
onCreate: () => {
|
|
78
|
+
options.onEditorCreate?.(this);
|
|
79
|
+
},
|
|
72
80
|
onUpdate: () => {
|
|
73
|
-
options.
|
|
81
|
+
options.onEditorContentChange?.(this);
|
|
74
82
|
},
|
|
75
|
-
|
|
76
|
-
options.
|
|
83
|
+
onSelectionUpdate: () => {
|
|
84
|
+
options.onTextCursorPositionChange?.(this);
|
|
77
85
|
},
|
|
78
86
|
extensions:
|
|
79
87
|
options.enableBlockNoteExtensions === false
|
|
@@ -97,7 +105,8 @@ export class BlockNoteEditor {
|
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
/**
|
|
100
|
-
* Gets a
|
|
108
|
+
* Gets a snapshot of all top-level (non-nested) blocks in the editor.
|
|
109
|
+
* @returns A snapshot of all top-level (non-nested) blocks in the editor.
|
|
101
110
|
*/
|
|
102
111
|
public get topLevelBlocks(): Block[] {
|
|
103
112
|
const blocks: Block[] = [];
|
|
@@ -112,12 +121,40 @@ export class BlockNoteEditor {
|
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @
|
|
124
|
+
* Gets a snapshot of an existing block from the editor.
|
|
125
|
+
* @param blockIdentifier The identifier of an existing block that should be retrieved.
|
|
126
|
+
* @returns The block that matches the identifier, or `undefined` if no matching block was found.
|
|
127
|
+
*/
|
|
128
|
+
public getBlock(blockIdentifier: BlockIdentifier): Block | undefined {
|
|
129
|
+
const id =
|
|
130
|
+
typeof blockIdentifier === "string"
|
|
131
|
+
? blockIdentifier
|
|
132
|
+
: blockIdentifier.id;
|
|
133
|
+
let newBlock: Block | undefined = undefined;
|
|
134
|
+
|
|
135
|
+
this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
|
|
136
|
+
if (typeof newBlock !== "undefined") {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (node.type.name !== "blockContainer" || node.attrs.id !== id) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
newBlock = nodeToBlock(node, this.blockCache);
|
|
145
|
+
|
|
146
|
+
return false;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return newBlock;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Traverses all blocks in the editor depth-first, and executes a callback for each.
|
|
154
|
+
* @param callback The callback to execute for each block. Returning `false` stops the traversal.
|
|
118
155
|
* @param reverse Whether the blocks should be traversed in reverse order.
|
|
119
156
|
*/
|
|
120
|
-
public
|
|
157
|
+
public forEachBlock(
|
|
121
158
|
callback: (block: Block) => void,
|
|
122
159
|
reverse: boolean = false
|
|
123
160
|
): void {
|
|
@@ -139,7 +176,8 @@ export class BlockNoteEditor {
|
|
|
139
176
|
}
|
|
140
177
|
|
|
141
178
|
/**
|
|
142
|
-
* Gets
|
|
179
|
+
* Gets a snapshot of the current text cursor position.
|
|
180
|
+
* @returns A snapshot of the current text cursor position.
|
|
143
181
|
*/
|
|
144
182
|
public getTextCursorPosition(): TextCursorPosition {
|
|
145
183
|
const { node, depth, startPos, endPos } = getBlockInfoFromPos(
|
|
@@ -181,14 +219,19 @@ export class BlockNoteEditor {
|
|
|
181
219
|
};
|
|
182
220
|
}
|
|
183
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Sets the text cursor position to the start or end of an existing block. Throws an error if the target block could
|
|
224
|
+
* not be found.
|
|
225
|
+
* @param targetBlock The identifier of an existing block that the text cursor should be moved to.
|
|
226
|
+
* @param placement Whether the text cursor should be placed at the start or end of the block.
|
|
227
|
+
*/
|
|
184
228
|
public setTextCursorPosition(
|
|
185
|
-
|
|
229
|
+
targetBlock: BlockIdentifier,
|
|
186
230
|
placement: "start" | "end" = "start"
|
|
187
231
|
) {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
232
|
+
const id = typeof targetBlock === "string" ? targetBlock : targetBlock.id;
|
|
233
|
+
|
|
234
|
+
const { posBeforeNode } = getNodeById(id, this._tiptapEditor.state.doc);
|
|
192
235
|
const { startPos, contentNode } = getBlockInfoFromPos(
|
|
193
236
|
this._tiptapEditor.state.doc,
|
|
194
237
|
posBeforeNode + 2
|
|
@@ -204,48 +247,46 @@ export class BlockNoteEditor {
|
|
|
204
247
|
}
|
|
205
248
|
|
|
206
249
|
/**
|
|
207
|
-
* Inserts
|
|
208
|
-
*
|
|
209
|
-
* @param
|
|
210
|
-
* @param
|
|
211
|
-
*
|
|
250
|
+
* Inserts new blocks into the editor. If a block's `id` is undefined, BlockNote generates one automatically. Throws an
|
|
251
|
+
* error if the reference block could not be found.
|
|
252
|
+
* @param blocksToInsert An array of partial blocks that should be inserted.
|
|
253
|
+
* @param referenceBlock An identifier for an existing block, at which the new blocks should be inserted.
|
|
254
|
+
* @param placement Whether the blocks should be inserted just before, just after, or nested inside the
|
|
255
|
+
* `referenceBlock`. Inserts the blocks at the start of the existing block's children if "nested" is used.
|
|
212
256
|
*/
|
|
213
257
|
public insertBlocks(
|
|
214
258
|
blocksToInsert: PartialBlock[],
|
|
215
|
-
|
|
259
|
+
referenceBlock: BlockIdentifier,
|
|
216
260
|
placement: "before" | "after" | "nested" = "before"
|
|
217
261
|
): void {
|
|
218
|
-
insertBlocks(
|
|
219
|
-
blocksToInsert,
|
|
220
|
-
blockToInsertAt,
|
|
221
|
-
placement,
|
|
222
|
-
this._tiptapEditor
|
|
223
|
-
);
|
|
262
|
+
insertBlocks(blocksToInsert, referenceBlock, placement, this._tiptapEditor);
|
|
224
263
|
}
|
|
225
264
|
|
|
226
265
|
/**
|
|
227
|
-
* Updates
|
|
266
|
+
* Updates an existing block in the editor. Since updatedBlock is a PartialBlock object, some fields might not be
|
|
267
|
+
* defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could
|
|
268
|
+
* not be found.
|
|
228
269
|
* @param blockToUpdate The block that should be updated.
|
|
229
|
-
* @param
|
|
270
|
+
* @param update A partial block which defines how the existing block should be changed.
|
|
230
271
|
*/
|
|
231
|
-
public updateBlock(blockToUpdate: Block,
|
|
232
|
-
updateBlock(blockToUpdate,
|
|
272
|
+
public updateBlock(blockToUpdate: Block, update: PartialBlock) {
|
|
273
|
+
updateBlock(blockToUpdate, update, this._tiptapEditor);
|
|
233
274
|
}
|
|
234
275
|
|
|
235
276
|
/**
|
|
236
|
-
* Removes
|
|
237
|
-
* @param blocksToRemove An array of blocks that should be removed.
|
|
277
|
+
* Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.
|
|
278
|
+
* @param blocksToRemove An array of identifiers for existing blocks that should be removed.
|
|
238
279
|
*/
|
|
239
280
|
public removeBlocks(blocksToRemove: Block[]) {
|
|
240
281
|
removeBlocks(blocksToRemove, this._tiptapEditor);
|
|
241
282
|
}
|
|
242
283
|
|
|
243
284
|
/**
|
|
244
|
-
* Replaces
|
|
245
|
-
*
|
|
246
|
-
* of the blocks could not be found.
|
|
285
|
+
* Replaces existing blocks in the editor with new blocks. If the blocks that should be removed are not adjacent or
|
|
286
|
+
* are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in
|
|
287
|
+
* `blocksToRemove`. Throws an error if any of the blocks to remove could not be found.
|
|
247
288
|
* @param blocksToRemove An array of blocks that should be replaced.
|
|
248
|
-
* @param blocksToInsert An array of blocks to replace the old ones with.
|
|
289
|
+
* @param blocksToInsert An array of partial blocks to replace the old ones with.
|
|
249
290
|
*/
|
|
250
291
|
public replaceBlocks(
|
|
251
292
|
blocksToRemove: Block[],
|
|
@@ -255,46 +296,44 @@ export class BlockNoteEditor {
|
|
|
255
296
|
}
|
|
256
297
|
|
|
257
298
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this._tiptapEditor.on("update", callback);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Serializes a list of blocks into an HTML string. The output is not the same as what's rendered by the editor, and
|
|
267
|
-
* is simplified in order to better conform to HTML standards. Block structuring elements are removed, children of
|
|
268
|
-
* blocks which aren't list items are lifted out of them, and list items blocks are wrapped in `ul`/`ol` tags.
|
|
269
|
-
* @param blocks The list of blocks to serialize into HTML.
|
|
299
|
+
* Serializes blocks into an HTML string. To better conform to HTML standards, children of blocks which aren't list
|
|
300
|
+
* items are un-nested in the output HTML.
|
|
301
|
+
* @param blocks An array of blocks that should be serialized into HTML.
|
|
302
|
+
* @returns The blocks, serialized as an HTML string.
|
|
270
303
|
*/
|
|
271
304
|
public async blocksToHTML(blocks: Block[]): Promise<string> {
|
|
272
305
|
return blocksToHTML(blocks, this._tiptapEditor.schema);
|
|
273
306
|
}
|
|
274
307
|
|
|
275
308
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
309
|
+
* Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and
|
|
310
|
+
* `InlineNode` objects from any HTML inline elements, though not all element types are recognized. If BlockNote
|
|
311
|
+
* doesn't recognize an HTML element's tag, it will parse it as a paragraph or plain text.
|
|
312
|
+
* @param html The HTML string to parse blocks from.
|
|
313
|
+
* @returns The blocks parsed from the HTML string.
|
|
278
314
|
*/
|
|
279
|
-
public async HTMLToBlocks(
|
|
280
|
-
return HTMLToBlocks(
|
|
315
|
+
public async HTMLToBlocks(html: string): Promise<Block[]> {
|
|
316
|
+
return HTMLToBlocks(html, this._tiptapEditor.schema);
|
|
281
317
|
}
|
|
282
318
|
|
|
283
319
|
/**
|
|
284
|
-
* Serializes
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* @
|
|
320
|
+
* Serializes blocks into a Markdown string. The output is simplified as Markdown does not support all features of
|
|
321
|
+
* BlockNote - children of blocks which aren't list items are un-nested and certain styles are removed.
|
|
322
|
+
* @param blocks An array of blocks that should be serialized into Markdown.
|
|
323
|
+
* @returns The blocks, serialized as a Markdown string.
|
|
288
324
|
*/
|
|
289
325
|
public async blocksToMarkdown(blocks: Block[]): Promise<string> {
|
|
290
326
|
return blocksToMarkdown(blocks, this._tiptapEditor.schema);
|
|
291
327
|
}
|
|
292
328
|
|
|
293
329
|
/**
|
|
294
|
-
* Creates a list of blocks from a Markdown string.
|
|
295
|
-
*
|
|
330
|
+
* Creates a list of blocks from a Markdown string. Tries to create `Block` and `InlineNode` objects based on
|
|
331
|
+
* Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it
|
|
332
|
+
* as text.
|
|
333
|
+
* @param markdown The Markdown string to parse blocks from.
|
|
334
|
+
* @returns The blocks parsed from the Markdown string.
|
|
296
335
|
*/
|
|
297
|
-
public async markdownToBlocks(
|
|
298
|
-
return markdownToBlocks(
|
|
336
|
+
public async markdownToBlocks(markdown: string): Promise<Block[]> {
|
|
337
|
+
return markdownToBlocks(markdown, this._tiptapEditor.schema);
|
|
299
338
|
}
|
|
300
339
|
}
|
|
@@ -38,11 +38,11 @@ export async function blocksToHTML(
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export async function HTMLToBlocks(
|
|
41
|
-
|
|
41
|
+
html: string,
|
|
42
42
|
schema: Schema
|
|
43
43
|
): Promise<Block[]> {
|
|
44
44
|
const htmlNode = document.createElement("div");
|
|
45
|
-
htmlNode.innerHTML =
|
|
45
|
+
htmlNode.innerHTML = html.trim();
|
|
46
46
|
|
|
47
47
|
const parser = DOMParser.fromSchema(schema);
|
|
48
48
|
const parentNode = parser.parse(htmlNode);
|
|
@@ -72,7 +72,7 @@ export async function blocksToMarkdown(
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export async function markdownToBlocks(
|
|
75
|
-
|
|
75
|
+
markdown: string,
|
|
76
76
|
schema: Schema
|
|
77
77
|
): Promise<Block[]> {
|
|
78
78
|
const htmlString = await unified()
|
|
@@ -80,7 +80,7 @@ export async function markdownToBlocks(
|
|
|
80
80
|
.use(remarkGfm)
|
|
81
81
|
.use(remarkRehype)
|
|
82
82
|
.use(rehypeStringify)
|
|
83
|
-
.process(
|
|
83
|
+
.process(markdown);
|
|
84
84
|
|
|
85
85
|
return HTMLToBlocks(htmlString.value as string, schema);
|
|
86
86
|
}
|
|
@@ -110,10 +110,10 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
110
110
|
|
|
111
111
|
return true;
|
|
112
112
|
},
|
|
113
|
-
// Deletes a block at a given position
|
|
113
|
+
// Deletes a block at a given position.
|
|
114
114
|
BNDeleteBlock:
|
|
115
115
|
(posInBlock) =>
|
|
116
|
-
({ state,
|
|
116
|
+
({ state, dispatch }) => {
|
|
117
117
|
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
118
118
|
if (blockInfo === undefined) {
|
|
119
119
|
return false;
|
|
@@ -123,10 +123,89 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
123
123
|
|
|
124
124
|
if (dispatch) {
|
|
125
125
|
state.tr.deleteRange(startPos, endPos);
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return true;
|
|
129
|
+
},
|
|
130
|
+
// Updates a block at a given position.
|
|
131
|
+
BNUpdateBlock:
|
|
132
|
+
(posInBlock, block) =>
|
|
133
|
+
({ state, dispatch }) => {
|
|
134
|
+
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
135
|
+
if (blockInfo === undefined) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { startPos, endPos, node, contentNode } = blockInfo;
|
|
140
|
+
|
|
141
|
+
if (dispatch) {
|
|
142
|
+
// Adds blockGroup node with child blocks if necessary.
|
|
143
|
+
if (block.children !== undefined) {
|
|
144
|
+
const childNodes = [];
|
|
145
|
+
|
|
146
|
+
// Creates ProseMirror nodes for each child block, including their descendants.
|
|
147
|
+
for (const child of block.children) {
|
|
148
|
+
childNodes.push(blockToNode(child, state.schema));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Checks if a blockGroup node already exists.
|
|
152
|
+
if (node.childCount === 2) {
|
|
153
|
+
// Replaces all child nodes in the existing blockGroup with the ones created earlier.
|
|
154
|
+
state.tr.replace(
|
|
155
|
+
startPos + contentNode.nodeSize + 1,
|
|
156
|
+
endPos - 1,
|
|
157
|
+
new Slice(Fragment.from(childNodes), 0, 0)
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
// Inserts a new blockGroup containing the child nodes created earlier.
|
|
161
|
+
state.tr.insert(
|
|
162
|
+
startPos + contentNode.nodeSize,
|
|
163
|
+
state.schema.nodes["blockGroup"].create({}, childNodes)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Replaces the blockContent node's content if necessary.
|
|
169
|
+
if (block.content !== undefined) {
|
|
170
|
+
let content: PMNode[] = [];
|
|
171
|
+
|
|
172
|
+
// Checks if the provided content is a string or InlineContent[] type.
|
|
173
|
+
if (typeof block.content === "string") {
|
|
174
|
+
// Adds a single text node with no marks to the content.
|
|
175
|
+
content.push(state.schema.text(block.content));
|
|
176
|
+
} else {
|
|
177
|
+
// Adds a text node with the provided styles converted into marks to the content, for each InlineContent
|
|
178
|
+
// object.
|
|
179
|
+
content = inlineContentToNodes(block.content, state.schema);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Replaces the contents of the blockContent node with the previously created text node(s).
|
|
183
|
+
state.tr.replace(
|
|
184
|
+
startPos + 1,
|
|
185
|
+
startPos + contentNode.nodeSize - 1,
|
|
186
|
+
new Slice(Fragment.from(content), 0, 0)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Changes the blockContent node type and adds the provided props as attributes. Also preserves all existing
|
|
191
|
+
// attributes that are compatible with the new type.
|
|
192
|
+
state.tr.setNodeMarkup(
|
|
193
|
+
startPos,
|
|
194
|
+
block.type === undefined
|
|
195
|
+
? undefined
|
|
196
|
+
: state.schema.nodes[block.type],
|
|
197
|
+
{
|
|
198
|
+
...contentNode.attrs,
|
|
199
|
+
...block.props,
|
|
200
|
+
}
|
|
128
201
|
);
|
|
129
|
-
|
|
202
|
+
|
|
203
|
+
// Adds all provided props as attributes to the parent blockContainer node too, and also preserves existing
|
|
204
|
+
// attributes.
|
|
205
|
+
state.tr.setNodeMarkup(startPos - 1, undefined, {
|
|
206
|
+
...node.attrs,
|
|
207
|
+
...block.props,
|
|
208
|
+
});
|
|
130
209
|
}
|
|
131
210
|
|
|
132
211
|
return true;
|
|
@@ -283,112 +362,6 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
283
362
|
|
|
284
363
|
return true;
|
|
285
364
|
},
|
|
286
|
-
// Updates a block to the given specification.
|
|
287
|
-
BNUpdateBlock:
|
|
288
|
-
(posInBlock, block) =>
|
|
289
|
-
({ state, dispatch }) => {
|
|
290
|
-
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
291
|
-
if (blockInfo === undefined) {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const { startPos, endPos, node, contentNode } = blockInfo;
|
|
296
|
-
|
|
297
|
-
if (dispatch) {
|
|
298
|
-
// Adds blockGroup node with child blocks if necessary.
|
|
299
|
-
if (block.children !== undefined) {
|
|
300
|
-
const childNodes = [];
|
|
301
|
-
|
|
302
|
-
// Creates ProseMirror nodes for each child block, including their descendants.
|
|
303
|
-
for (const child of block.children) {
|
|
304
|
-
childNodes.push(blockToNode(child, state.schema));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Checks if a blockGroup node already exists.
|
|
308
|
-
if (node.childCount === 2) {
|
|
309
|
-
// Replaces all child nodes in the existing blockGroup with the ones created earlier.
|
|
310
|
-
state.tr.replace(
|
|
311
|
-
startPos + contentNode.nodeSize + 1,
|
|
312
|
-
endPos - 1,
|
|
313
|
-
new Slice(Fragment.from(childNodes), 0, 0)
|
|
314
|
-
);
|
|
315
|
-
} else {
|
|
316
|
-
// Inserts a new blockGroup containing the child nodes created earlier.
|
|
317
|
-
state.tr.insert(
|
|
318
|
-
startPos + contentNode.nodeSize,
|
|
319
|
-
state.schema.nodes["blockGroup"].create({}, childNodes)
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Replaces the blockContent node's content if necessary.
|
|
325
|
-
if (block.content !== undefined) {
|
|
326
|
-
let content: PMNode[] = [];
|
|
327
|
-
|
|
328
|
-
// Checks if the provided content is a string or InlineContent[] type.
|
|
329
|
-
if (typeof block.content === "string") {
|
|
330
|
-
// Adds a single text node with no marks to the content.
|
|
331
|
-
content.push(state.schema.text(block.content));
|
|
332
|
-
} else {
|
|
333
|
-
// Adds a text node with the provided styles converted into marks to the content, for each InlineContent
|
|
334
|
-
// object.
|
|
335
|
-
content = inlineContentToNodes(block.content, state.schema);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Replaces the contents of the blockContent node with the previously created text node(s).
|
|
339
|
-
state.tr.replace(
|
|
340
|
-
startPos + 1,
|
|
341
|
-
startPos + contentNode.nodeSize - 1,
|
|
342
|
-
new Slice(Fragment.from(content), 0, 0)
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Changes the block type and adds the provided props as node attributes. Also preserves all existing node
|
|
347
|
-
// attributes that are compatible with the new type.
|
|
348
|
-
state.tr.setNodeMarkup(
|
|
349
|
-
startPos,
|
|
350
|
-
block.type === undefined
|
|
351
|
-
? undefined
|
|
352
|
-
: state.schema.nodes[block.type],
|
|
353
|
-
{
|
|
354
|
-
...contentNode.attrs,
|
|
355
|
-
...block.props,
|
|
356
|
-
}
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return true;
|
|
361
|
-
},
|
|
362
|
-
// Updates a block to the given specification if it's empty, otherwise creates a new block from that specification
|
|
363
|
-
// below it.
|
|
364
|
-
BNCreateOrUpdateBlock:
|
|
365
|
-
(posInBlock, block) =>
|
|
366
|
-
({ state, chain }) => {
|
|
367
|
-
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
368
|
-
if (blockInfo === undefined) {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const { node, startPos, endPos } = blockInfo;
|
|
373
|
-
|
|
374
|
-
if (node.textContent.length === 0) {
|
|
375
|
-
const oldBlockContentPos = startPos + 1;
|
|
376
|
-
|
|
377
|
-
return chain()
|
|
378
|
-
.BNUpdateBlock(posInBlock, block)
|
|
379
|
-
.setTextSelection(oldBlockContentPos)
|
|
380
|
-
.run();
|
|
381
|
-
} else {
|
|
382
|
-
const newBlockInsertionPos = endPos + 1;
|
|
383
|
-
const newBlockContentPos = newBlockInsertionPos + 1;
|
|
384
|
-
|
|
385
|
-
return chain()
|
|
386
|
-
.BNCreateBlock(newBlockInsertionPos)
|
|
387
|
-
.BNUpdateBlock(newBlockContentPos, block)
|
|
388
|
-
.setTextSelection(newBlockContentPos)
|
|
389
|
-
.run();
|
|
390
|
-
}
|
|
391
|
-
},
|
|
392
365
|
};
|
|
393
366
|
},
|
|
394
367
|
|