Skip to content

Commit 3747dac

Browse files
feat: Return values for block manipulation methods (#458)
* Made block manipulation methods return the block(s) they insert/update/remove * Cleaned up code * Minor changes
1 parent 295c7ba commit 3747dac

File tree

2 files changed

+117
-50
lines changed

2 files changed

+117
-50
lines changed
Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import { Editor } from "@tiptap/core";
21
import { Node } from "prosemirror-model";
32

43
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
54
import {
5+
Block,
66
BlockIdentifier,
77
BlockSchema,
88
InlineContentSchema,
99
PartialBlock,
1010
StyleSchema,
1111
} from "../../schema";
12-
import { blockToNode } from "../nodeConversions/nodeConversions";
12+
import { blockToNode, nodeToBlock } from "../nodeConversions/nodeConversions";
1313
import { getNodeById } from "../nodeUtil";
1414
import { Transaction } from "prosemirror-state";
1515

@@ -22,7 +22,7 @@ export function insertBlocks<
2222
referenceBlock: BlockIdentifier,
2323
placement: "before" | "after" | "nested" = "before",
2424
editor: BlockNoteEditor<BSchema, I, S>
25-
): void {
25+
): Block<BSchema, I, S>[] {
2626
const ttEditor = editor._tiptapEditor;
2727

2828
const id =
@@ -35,39 +35,53 @@ export function insertBlocks<
3535
);
3636
}
3737

38-
let insertionPos = -1;
39-
4038
const { node, posBeforeNode } = getNodeById(id, ttEditor.state.doc);
4139

4240
if (placement === "before") {
43-
insertionPos = posBeforeNode;
41+
ttEditor.view.dispatch(
42+
ttEditor.state.tr.insert(posBeforeNode, nodesToInsert)
43+
);
4444
}
4545

4646
if (placement === "after") {
47-
insertionPos = posBeforeNode + node.nodeSize;
47+
ttEditor.view.dispatch(
48+
ttEditor.state.tr.insert(posBeforeNode + node.nodeSize, nodesToInsert)
49+
);
4850
}
4951

5052
if (placement === "nested") {
5153
// Case if block doesn't already have children.
5254
if (node.childCount < 2) {
53-
insertionPos = posBeforeNode + node.firstChild!.nodeSize + 1;
54-
5555
const blockGroupNode = ttEditor.state.schema.nodes["blockGroup"].create(
5656
{},
5757
nodesToInsert
5858
);
5959

6060
ttEditor.view.dispatch(
61-
ttEditor.state.tr.insert(insertionPos, blockGroupNode)
61+
ttEditor.state.tr.insert(
62+
posBeforeNode + node.firstChild!.nodeSize + 1,
63+
blockGroupNode
64+
)
6265
);
63-
64-
return;
6566
}
67+
}
6668

67-
insertionPos = posBeforeNode + node.firstChild!.nodeSize + 2;
69+
// Now that the `PartialBlock`s have been converted to nodes, we can
70+
// re-convert them into full `Block`s.
71+
const insertedBlocks: Block<BSchema, I, S>[] = [];
72+
for (const node of nodesToInsert) {
73+
insertedBlocks.push(
74+
nodeToBlock(
75+
node,
76+
editor.blockSchema,
77+
editor.inlineContentSchema,
78+
editor.styleSchema,
79+
editor.blockCache
80+
)
81+
);
6882
}
6983

70-
ttEditor.view.dispatch(ttEditor.state.tr.insert(insertionPos, nodesToInsert));
84+
return insertedBlocks;
7185
}
7286

7387
export function updateBlock<
@@ -77,37 +91,56 @@ export function updateBlock<
7791
>(
7892
blockToUpdate: BlockIdentifier,
7993
update: PartialBlock<BSchema, I, S>,
80-
editor: Editor
81-
) {
94+
editor: BlockNoteEditor<BSchema, I, S>
95+
): Block<BSchema, I, S> {
96+
const ttEditor = editor._tiptapEditor;
97+
8298
const id =
8399
typeof blockToUpdate === "string" ? blockToUpdate : blockToUpdate.id;
84-
const { posBeforeNode } = getNodeById(id, editor.state.doc);
100+
const { posBeforeNode } = getNodeById(id, ttEditor.state.doc);
85101

86-
editor.commands.BNUpdateBlock(posBeforeNode + 1, update);
102+
ttEditor.commands.BNUpdateBlock(posBeforeNode + 1, update);
103+
104+
const blockContainerNode = ttEditor.state.doc
105+
.resolve(posBeforeNode + 1)
106+
.node();
107+
108+
return nodeToBlock(
109+
blockContainerNode,
110+
editor.blockSchema,
111+
editor.inlineContentSchema,
112+
editor.styleSchema,
113+
editor.blockCache
114+
);
87115
}
88116

89-
function removeBlocksWithCallback(
117+
function removeBlocksWithCallback<
118+
BSchema extends BlockSchema,
119+
I extends InlineContentSchema,
120+
S extends StyleSchema
121+
>(
90122
blocksToRemove: BlockIdentifier[],
91-
editor: Editor,
123+
editor: BlockNoteEditor<BSchema, I, S>,
92124
// Should return new removedSize.
93125
callback?: (
94126
node: Node,
95127
pos: number,
96128
tr: Transaction,
97129
removedSize: number
98130
) => number
99-
) {
100-
const tr = editor.state.tr;
131+
): Block<BSchema, I, S>[] {
132+
const ttEditor = editor._tiptapEditor;
133+
const tr = ttEditor.state.tr;
101134

102135
const idsOfBlocksToRemove = new Set<string>(
103136
blocksToRemove.map((block) =>
104137
typeof block === "string" ? block : block.id
105138
)
106139
);
107-
140+
const removedBlocks: Block<BSchema, I, S>[] = [];
108141
let removedSize = 0;
109142

110-
editor.state.doc.descendants((node, pos) => {
143+
ttEditor.state.doc.descendants((node, pos) => {
111144
// Skips traversing nodes after all target blocks have been removed.
112145
if (idsOfBlocksToRemove.size === 0) {
113146
return false;
@@ -121,10 +154,20 @@ function removeBlocksWithCallback(
121154
return true;
122155
}
123156

124-
removedSize = callback?.(node, pos, tr, removedSize) || removedSize;
125-
157+
// Saves the block that is being deleted.
158+
removedBlocks.push(
159+
nodeToBlock(
160+
node,
161+
editor.blockSchema,
162+
editor.inlineContentSchema,
163+
editor.styleSchema,
164+
editor.blockCache
165+
)
166+
);
126167
idsOfBlocksToRemove.delete(node.attrs.id);
127168

169+
// Removes the block and calculates the change in document size.
170+
removedSize = callback?.(node, pos, tr, removedSize) || removedSize;
128171
const oldDocSize = tr.doc.nodeSize;
129172
tr.delete(pos - removedSize - 1, pos - removedSize + node.nodeSize + 1);
130173
const newDocSize = tr.doc.nodeSize;
@@ -133,6 +176,7 @@ function removeBlocksWithCallback(
133176
return false;
134177
});
135178

179+
// Throws an error if now all blocks could be found.
136180
if (idsOfBlocksToRemove.size > 0) {
137181
const notFoundIds = [...idsOfBlocksToRemove].join("\n");
138182

@@ -142,14 +186,20 @@ function removeBlocksWithCallback(
142186
);
143187
}
144188

145-
editor.view.dispatch(tr);
189+
ttEditor.view.dispatch(tr);
190+
191+
return removedBlocks;
146192
}
147193

148-
export function removeBlocks(
194+
export function removeBlocks<
195+
BSchema extends BlockSchema,
196+
I extends InlineContentSchema,
197+
S extends StyleSchema
198+
>(
149199
blocksToRemove: BlockIdentifier[],
150-
editor: Editor
151-
) {
152-
removeBlocksWithCallback(blocksToRemove, editor);
200+
editor: BlockNoteEditor<BSchema, I, S>
201+
): Block<BSchema, I, S>[] {
202+
return removeBlocksWithCallback(blocksToRemove, editor);
153203
}
154204

155205
export function replaceBlocks<
@@ -160,24 +210,24 @@ export function replaceBlocks<
160210
blocksToRemove: BlockIdentifier[],
161211
blocksToInsert: PartialBlock<BSchema, I, S>[],
162212
editor: BlockNoteEditor<BSchema, I, S>
163-
) {
213+
): {
214+
insertedBlocks: Block<BSchema, I, S>[];
215+
removedBlocks: Block<BSchema, I, S>[];
216+
} {
164217
const ttEditor = editor._tiptapEditor;
165218

166219
const nodesToInsert: Node[] = [];
167-
for (const blockSpec of blocksToInsert) {
168-
nodesToInsert.push(
169-
blockToNode(blockSpec, ttEditor.schema, editor.styleSchema)
170-
);
220+
for (const block of blocksToInsert) {
221+
nodesToInsert.push(blockToNode(block, ttEditor.schema, editor.styleSchema));
171222
}
172223

173224
const idOfFirstBlock =
174225
typeof blocksToRemove[0] === "string"
175226
? blocksToRemove[0]
176227
: blocksToRemove[0].id;
177-
178-
removeBlocksWithCallback(
228+
const removedBlocks = removeBlocksWithCallback(
179229
blocksToRemove,
180-
ttEditor,
230+
editor,
181231
(node, pos, tr, removedSize) => {
182232
if (node.attrs.id === idOfFirstBlock) {
183233
const oldDocSize = tr.doc.nodeSize;
@@ -190,4 +240,21 @@ export function replaceBlocks<
190240
return removedSize;
191241
}
192242
);
243+
244+
// Now that the `PartialBlock`s have been converted to nodes, we can
245+
// re-convert them into full `Block`s.
246+
const insertedBlocks: Block<BSchema, I, S>[] = [];
247+
for (const node of nodesToInsert) {
248+
insertedBlocks.push(
249+
nodeToBlock(
250+
node,
251+
editor.blockSchema,
252+
editor.inlineContentSchema,
253+
editor.styleSchema,
254+
editor.blockCache
255+
)
256+
);
257+
}
258+
259+
return { insertedBlocks, removedBlocks };
193260
}

packages/core/src/editor/BlockNoteEditor.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import { HTMLToBlocks } from "../api/parsers/html/parseHTML";
2121
import { markdownToBlocks } from "../api/parsers/markdown/parseMarkdown";
2222
import {
2323
DefaultBlockSchema,
24-
DefaultInlineContentSchema,
25-
DefaultStyleSchema,
2624
defaultBlockSchema,
2725
defaultBlockSpecs,
26+
DefaultInlineContentSchema,
2827
defaultInlineContentSpecs,
28+
DefaultStyleSchema,
2929
defaultStyleSpecs,
3030
} from "../blocks/defaultBlocks";
3131
import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin";
@@ -45,17 +45,17 @@ import {
4545
BlockSchemaFromSpecs,
4646
BlockSchemaWithBlock,
4747
BlockSpecs,
48+
getBlockSchemaFromSpecs,
49+
getInlineContentSchemaFromSpecs,
50+
getStyleSchemaFromSpecs,
4851
InlineContentSchema,
4952
InlineContentSchemaFromSpecs,
5053
InlineContentSpecs,
5154
PartialBlock,
55+
Styles,
5256
StyleSchema,
5357
StyleSchemaFromSpecs,
5458
StyleSpecs,
55-
Styles,
56-
getBlockSchemaFromSpecs,
57-
getInlineContentSchemaFromSpecs,
58-
getStyleSchemaFromSpecs,
5959
} from "../schema";
6060
import { mergeCSSClasses } from "../util/browser";
6161
import { UnreachableCaseError } from "../util/typescript";
@@ -775,8 +775,8 @@ export class BlockNoteEditor<
775775
blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
776776
referenceBlock: BlockIdentifier,
777777
placement: "before" | "after" | "nested" = "before"
778-
): void {
779-
insertBlocks(blocksToInsert, referenceBlock, placement, this);
778+
) {
779+
return insertBlocks(blocksToInsert, referenceBlock, placement, this);
780780
}
781781

782782
/**
@@ -790,15 +790,15 @@ export class BlockNoteEditor<
790790
blockToUpdate: BlockIdentifier,
791791
update: PartialBlock<BSchema, ISchema, SSchema>
792792
) {
793-
updateBlock(blockToUpdate, update, this._tiptapEditor);
793+
return updateBlock(blockToUpdate, update, this);
794794
}
795795

796796
/**
797797
* Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.
798798
* @param blocksToRemove An array of identifiers for existing blocks that should be removed.
799799
*/
800800
public removeBlocks(blocksToRemove: BlockIdentifier[]) {
801-
removeBlocks(blocksToRemove, this._tiptapEditor);
801+
return removeBlocks(blocksToRemove, this);
802802
}
803803

804804
/**
@@ -812,7 +812,7 @@ export class BlockNoteEditor<
812812
blocksToRemove: BlockIdentifier[],
813813
blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[]
814814
) {
815-
replaceBlocks(blocksToRemove, blocksToInsert, this);
815+
return replaceBlocks(blocksToRemove, blocksToInsert, this);
816816
}
817817

818818
/**

0 commit comments

Comments
 (0)