Skip to content

Commit 1e9d6fc

Browse files
authored
refactor: move BlockNoteEditor implementations to separate managers (#2030)
1 parent 884347d commit 1e9d6fc

File tree

13 files changed

+1613
-472
lines changed

13 files changed

+1613
-472
lines changed

packages/core/src/editor/BlockNoteEditor.ts

Lines changed: 211 additions & 466 deletions
Large diffs are not rendered by default.

packages/core/src/editor/BlockNoteExtensions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as Y from "yjs";
99
import { createDropFileExtension } from "../api/clipboard/fromClipboard/fileDropExtension.js";
1010
import { createPasteFromClipboardExtension } from "../api/clipboard/fromClipboard/pasteExtension.js";
1111
import { createCopyToClipboardExtension } from "../api/clipboard/toClipboard/copyExtension.js";
12-
import type { ThreadStore } from "../comments/index.js";
12+
import type { ThreadStore, User } from "../comments/index.js";
1313
import { BackgroundColorExtension } from "../extensions/BackgroundColor/BackgroundColorExtension.js";
1414
import { BlockChangePlugin } from "../extensions/BlockChange/BlockChangePlugin.js";
1515
import { CursorPlugin } from "../extensions/Collaboration/CursorPlugin.js";
@@ -96,6 +96,7 @@ type ExtensionOptions<
9696
comments?: {
9797
schema?: BlockNoteSchema<any, any, any>;
9898
threadStore: ThreadStore;
99+
resolveUsers?: (userIds: string[]) => Promise<User[]>;
99100
};
100101
pasteHandler: BlockNoteEditorOptions<any, any, any>["pasteHandler"];
101102
};
@@ -162,6 +163,7 @@ export const getBlockNoteExtensions = <
162163
opts.editor,
163164
opts.comments.threadStore,
164165
CommentMark.name,
166+
opts.comments.resolveUsers,
165167
opts.comments.schema,
166168
);
167169
}
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import { insertBlocks } from "../../api/blockManipulation/commands/insertBlocks/insertBlocks.js";
2+
import {
3+
moveBlocksDown,
4+
moveBlocksUp,
5+
} from "../../api/blockManipulation/commands/moveBlocks/moveBlocks.js";
6+
import {
7+
canNestBlock,
8+
canUnnestBlock,
9+
nestBlock,
10+
unnestBlock,
11+
} from "../../api/blockManipulation/commands/nestBlock/nestBlock.js";
12+
import { removeAndInsertBlocks } from "../../api/blockManipulation/commands/replaceBlocks/replaceBlocks.js";
13+
import { updateBlock } from "../../api/blockManipulation/commands/updateBlock/updateBlock.js";
14+
import {
15+
getBlock,
16+
getNextBlock,
17+
getParentBlock,
18+
getPrevBlock,
19+
} from "../../api/blockManipulation/getBlock/getBlock.js";
20+
import { docToBlocks } from "../../api/nodeConversions/nodeToBlock.js";
21+
import {
22+
Block,
23+
DefaultBlockSchema,
24+
DefaultInlineContentSchema,
25+
DefaultStyleSchema,
26+
PartialBlock,
27+
} from "../../blocks/defaultBlocks.js";
28+
import {
29+
BlockIdentifier,
30+
BlockSchema,
31+
InlineContentSchema,
32+
StyleSchema,
33+
} from "../../schema/index.js";
34+
import { BlockNoteEditor } from "../BlockNoteEditor.js";
35+
36+
export class BlockManager<
37+
BSchema extends BlockSchema = DefaultBlockSchema,
38+
ISchema extends InlineContentSchema = DefaultInlineContentSchema,
39+
SSchema extends StyleSchema = DefaultStyleSchema,
40+
> {
41+
constructor(private editor: BlockNoteEditor<BSchema, ISchema, SSchema>) {}
42+
43+
/**
44+
* Gets a snapshot of all top-level (non-nested) blocks in the editor.
45+
* @returns A snapshot of all top-level (non-nested) blocks in the editor.
46+
*/
47+
public get document(): Block<BSchema, ISchema, SSchema>[] {
48+
return this.editor.transact((tr) => {
49+
return docToBlocks(tr.doc, this.editor.pmSchema);
50+
});
51+
}
52+
53+
/**
54+
* Gets a snapshot of an existing block from the editor.
55+
* @param blockIdentifier The identifier of an existing block that should be
56+
* retrieved.
57+
* @returns The block that matches the identifier, or `undefined` if no
58+
* matching block was found.
59+
*/
60+
public getBlock(
61+
blockIdentifier: BlockIdentifier,
62+
): Block<BSchema, ISchema, SSchema> | undefined {
63+
return this.editor.transact((tr) => getBlock(tr.doc, blockIdentifier));
64+
}
65+
66+
/**
67+
* Gets a snapshot of the previous sibling of an existing block from the
68+
* editor.
69+
* @param blockIdentifier The identifier of an existing block for which the
70+
* previous sibling should be retrieved.
71+
* @returns The previous sibling of the block that matches the identifier.
72+
* `undefined` if no matching block was found, or it's the first child/block
73+
* in the document.
74+
*/
75+
public getPrevBlock(
76+
blockIdentifier: BlockIdentifier,
77+
): Block<BSchema, ISchema, SSchema> | undefined {
78+
return this.editor.transact((tr) => getPrevBlock(tr.doc, blockIdentifier));
79+
}
80+
81+
/**
82+
* Gets a snapshot of the next sibling of an existing block from the editor.
83+
* @param blockIdentifier The identifier of an existing block for which the
84+
* next sibling should be retrieved.
85+
* @returns The next sibling of the block that matches the identifier.
86+
* `undefined` if no matching block was found, or it's the last child/block in
87+
* the document.
88+
*/
89+
public getNextBlock(
90+
blockIdentifier: BlockIdentifier,
91+
): Block<BSchema, ISchema, SSchema> | undefined {
92+
return this.editor.transact((tr) => getNextBlock(tr.doc, blockIdentifier));
93+
}
94+
95+
/**
96+
* Gets a snapshot of the parent of an existing block from the editor.
97+
* @param blockIdentifier The identifier of an existing block for which the
98+
* parent should be retrieved.
99+
* @returns The parent of the block that matches the identifier. `undefined`
100+
* if no matching block was found, or the block isn't nested.
101+
*/
102+
public getParentBlock(
103+
blockIdentifier: BlockIdentifier,
104+
): Block<BSchema, ISchema, SSchema> | undefined {
105+
return this.editor.transact((tr) =>
106+
getParentBlock(tr.doc, blockIdentifier),
107+
);
108+
}
109+
110+
/**
111+
* Traverses all blocks in the editor depth-first, and executes a callback for each.
112+
* @param callback The callback to execute for each block. Returning `false` stops the traversal.
113+
* @param reverse Whether the blocks should be traversed in reverse order.
114+
*/
115+
public forEachBlock(
116+
callback: (block: Block<BSchema, ISchema, SSchema>) => boolean,
117+
reverse = false,
118+
): void {
119+
const blocks = this.document.slice();
120+
121+
if (reverse) {
122+
blocks.reverse();
123+
}
124+
125+
function traverseBlockArray(
126+
blockArray: Block<BSchema, ISchema, SSchema>[],
127+
): boolean {
128+
for (const block of blockArray) {
129+
if (callback(block) === false) {
130+
return false;
131+
}
132+
133+
const children = reverse
134+
? block.children.slice().reverse()
135+
: block.children;
136+
137+
if (!traverseBlockArray(children)) {
138+
return false;
139+
}
140+
}
141+
142+
return true;
143+
}
144+
145+
traverseBlockArray(blocks);
146+
}
147+
148+
/**
149+
* Inserts new blocks into the editor. If a block's `id` is undefined, BlockNote generates one automatically. Throws an
150+
* error if the reference block could not be found.
151+
* @param blocksToInsert An array of partial blocks that should be inserted.
152+
* @param referenceBlock An identifier for an existing block, at which the new blocks should be inserted.
153+
* @param placement Whether the blocks should be inserted just before, just after, or nested inside the
154+
* `referenceBlock`.
155+
*/
156+
public insertBlocks(
157+
blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
158+
referenceBlock: BlockIdentifier,
159+
placement: "before" | "after" = "before",
160+
) {
161+
return this.editor.transact((tr) =>
162+
insertBlocks(tr, blocksToInsert, referenceBlock, placement),
163+
);
164+
}
165+
166+
/**
167+
* Updates an existing block in the editor. Since updatedBlock is a PartialBlock object, some fields might not be
168+
* defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could
169+
* not be found.
170+
* @param blockToUpdate The block that should be updated.
171+
* @param update A partial block which defines how the existing block should be changed.
172+
*/
173+
public updateBlock(
174+
blockToUpdate: BlockIdentifier,
175+
update: PartialBlock<BSchema, ISchema, SSchema>,
176+
) {
177+
return this.editor.transact((tr) => updateBlock(tr, blockToUpdate, update));
178+
}
179+
180+
/**
181+
* Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.
182+
* @param blocksToRemove An array of identifiers for existing blocks that should be removed.
183+
*/
184+
public removeBlocks(blocksToRemove: BlockIdentifier[]) {
185+
return this.editor.transact(
186+
(tr) => removeAndInsertBlocks(tr, blocksToRemove, []).removedBlocks,
187+
);
188+
}
189+
190+
/**
191+
* Replaces existing blocks in the editor with new blocks. If the blocks that should be removed are not adjacent or
192+
* are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in
193+
* `blocksToRemove`. Throws an error if any of the blocks to remove could not be found.
194+
* @param blocksToRemove An array of blocks that should be replaced.
195+
* @param blocksToInsert An array of partial blocks to replace the old ones with.
196+
*/
197+
public replaceBlocks(
198+
blocksToRemove: BlockIdentifier[],
199+
blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
200+
) {
201+
return this.editor.transact((tr) =>
202+
removeAndInsertBlocks(tr, blocksToRemove, blocksToInsert),
203+
);
204+
}
205+
206+
/**
207+
* Checks if the block containing the text cursor can be nested.
208+
*/
209+
public canNestBlock() {
210+
return canNestBlock(this.editor);
211+
}
212+
213+
/**
214+
* Nests the block containing the text cursor into the block above it.
215+
*/
216+
public nestBlock() {
217+
nestBlock(this.editor);
218+
}
219+
220+
/**
221+
* Checks if the block containing the text cursor is nested.
222+
*/
223+
public canUnnestBlock() {
224+
return canUnnestBlock(this.editor);
225+
}
226+
227+
/**
228+
* Lifts the block containing the text cursor out of its parent.
229+
*/
230+
public unnestBlock() {
231+
unnestBlock(this.editor);
232+
}
233+
234+
/**
235+
* Moves the selected blocks up. If the previous block has children, moves
236+
* them to the end of its children. If there is no previous block, but the
237+
* current blocks share a common parent, moves them out of & before it.
238+
*/
239+
public moveBlocksUp() {
240+
return moveBlocksUp(this.editor);
241+
}
242+
243+
/**
244+
* Moves the selected blocks down. If the next block has children, moves
245+
* them to the start of its children. If there is no next block, but the
246+
* current blocks share a common parent, moves them out of & after it.
247+
*/
248+
public moveBlocksDown() {
249+
return moveBlocksDown(this.editor);
250+
}
251+
}

0 commit comments

Comments
 (0)