@blocknote/core 0.7.0 → 0.8.0
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 +1428 -1252
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +2 -2
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +4 -3
- package/src/BlockNoteEditor.ts +100 -53
- package/src/BlockNoteExtensions.ts +24 -14
- package/src/api/blockManipulation/blockManipulation.test.ts +6 -3
- package/src/api/blockManipulation/blockManipulation.ts +7 -6
- package/src/api/formatConversions/formatConversions.test.ts +13 -8
- package/src/api/formatConversions/formatConversions.ts +15 -12
- package/src/api/nodeConversions/nodeConversions.test.ts +29 -10
- package/src/api/nodeConversions/nodeConversions.ts +33 -12
- package/src/api/nodeConversions/testUtil.ts +8 -4
- package/src/editor.module.css +0 -1
- package/src/extensions/Blocks/api/block.ts +229 -0
- package/src/extensions/Blocks/api/blockTypes.ts +158 -71
- package/src/extensions/Blocks/api/cursorPositionTypes.ts +5 -5
- package/src/extensions/Blocks/api/defaultBlocks.ts +44 -0
- package/src/extensions/Blocks/api/selectionTypes.ts +3 -3
- package/src/extensions/Blocks/api/serialization.ts +29 -0
- package/src/extensions/Blocks/index.ts +0 -8
- package/src/extensions/Blocks/nodes/Block.module.css +24 -12
- package/src/extensions/Blocks/nodes/BlockContainer.ts +8 -4
- package/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.ts +4 -4
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +5 -5
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +100 -97
- package/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts +4 -4
- package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +11 -9
- package/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +6 -5
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +12 -11
- package/src/extensions/FormattingToolbar/FormattingToolbarExtension.ts +21 -16
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +9 -5
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +30 -42
- package/src/extensions/Placeholder/PlaceholderExtension.ts +1 -0
- package/src/extensions/SlashMenu/BaseSlashMenuItem.ts +5 -2
- package/src/extensions/SlashMenu/SlashMenuExtension.ts +37 -33
- package/src/extensions/SlashMenu/defaultSlashMenuItems.tsx +14 -10
- package/src/extensions/SlashMenu/index.ts +2 -2
- package/src/index.ts +4 -0
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +29 -13
- package/types/src/BlockNoteEditor.d.ts +37 -23
- package/types/src/BlockNoteExtensions.d.ts +15 -8
- package/types/src/api/blockManipulation/blockManipulation.d.ts +4 -4
- package/types/src/api/formatConversions/formatConversions.d.ts +5 -5
- package/types/src/api/nodeConversions/nodeConversions.d.ts +3 -3
- package/types/src/api/nodeConversions/testUtil.d.ts +2 -2
- package/types/src/extensions/Blocks/api/block.d.ts +6 -5
- package/types/src/extensions/Blocks/api/blockTypes.d.ts +77 -33
- package/types/src/extensions/Blocks/api/cursorPositionTypes.d.ts +5 -5
- package/types/src/extensions/Blocks/api/selectionTypes.d.ts +3 -3
- package/types/src/extensions/Blocks/api/serialization.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +3 -3
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +1 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +1 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +1 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +1 -2
- package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +7 -7
- package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +5 -4
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +11 -10
- package/types/src/extensions/FormattingToolbar/FormattingToolbarExtension.d.ts +6 -5
- package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +4 -3
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +16 -19
- package/types/src/extensions/SlashMenu/BaseSlashMenuItem.d.ts +4 -3
- package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +5 -4
- package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +66 -1
- package/types/src/extensions/SlashMenu/index.d.ts +2 -2
- package/types/src/index.d.ts +4 -0
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +5 -4
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://github.com/TypeCellOS/BlockNote",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.8.0",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"types",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"module": "./dist/blocknote.js",
|
|
31
31
|
"exports": {
|
|
32
32
|
".": {
|
|
33
|
+
"types": "./types/src/index.d.ts",
|
|
33
34
|
"import": "./dist/blocknote.js",
|
|
34
35
|
"require": "./dist/blocknote.umd.cjs"
|
|
35
36
|
},
|
|
@@ -91,7 +92,7 @@
|
|
|
91
92
|
"eslint-config-react-app": "^7.0.0",
|
|
92
93
|
"jsdom": "^21.1.0",
|
|
93
94
|
"prettier": "^2.7.1",
|
|
94
|
-
"typescript": "^
|
|
95
|
+
"typescript": "^5.0.4",
|
|
95
96
|
"vite": "^4.1.2",
|
|
96
97
|
"vite-plugin-eslint": "^1.8.1",
|
|
97
98
|
"vitest": "^0.28.5"
|
|
@@ -109,5 +110,5 @@
|
|
|
109
110
|
"access": "public",
|
|
110
111
|
"registry": "https://registry.npmjs.org/"
|
|
111
112
|
},
|
|
112
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "cec8be26bbdf008846b907b52966ae3ef126cd64"
|
|
113
114
|
}
|
package/src/BlockNoteEditor.ts
CHANGED
|
@@ -22,9 +22,14 @@ import styles from "./editor.module.css";
|
|
|
22
22
|
import {
|
|
23
23
|
Block,
|
|
24
24
|
BlockIdentifier,
|
|
25
|
+
BlockSchema,
|
|
25
26
|
PartialBlock,
|
|
26
27
|
} from "./extensions/Blocks/api/blockTypes";
|
|
27
28
|
import { TextCursorPosition } from "./extensions/Blocks/api/cursorPositionTypes";
|
|
29
|
+
import {
|
|
30
|
+
DefaultBlockSchema,
|
|
31
|
+
defaultBlockSchema,
|
|
32
|
+
} from "./extensions/Blocks/api/defaultBlocks";
|
|
28
33
|
import {
|
|
29
34
|
ColorStyle,
|
|
30
35
|
Styles,
|
|
@@ -37,21 +42,23 @@ import {
|
|
|
37
42
|
defaultSlashMenuItems,
|
|
38
43
|
} from "./extensions/SlashMenu";
|
|
39
44
|
|
|
40
|
-
export type BlockNoteEditorOptions = {
|
|
41
|
-
// TODO: Figure out if enableBlockNoteExtensions
|
|
45
|
+
export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
|
|
46
|
+
// TODO: Figure out if enableBlockNoteExtensions/disableHistoryExtension are needed and document them.
|
|
42
47
|
enableBlockNoteExtensions: boolean;
|
|
43
48
|
|
|
44
49
|
/**
|
|
45
50
|
* UI element factories for creating a custom UI, including custom positioning
|
|
46
51
|
* & rendering.
|
|
47
52
|
*/
|
|
48
|
-
uiFactories: UiFactories
|
|
53
|
+
uiFactories: UiFactories<BSchema>;
|
|
49
54
|
/**
|
|
50
55
|
* TODO: why is this called slashCommands and not slashMenuItems?
|
|
51
56
|
*
|
|
57
|
+
* (couldn't fix any type, see https://github.com/TypeCellOS/BlockNote/pull/191#discussion_r1210708771)
|
|
58
|
+
*
|
|
52
59
|
* @default defaultSlashMenuItems from `./extensions/SlashMenu`
|
|
53
60
|
*/
|
|
54
|
-
slashCommands: BaseSlashMenuItem[];
|
|
61
|
+
slashCommands: BaseSlashMenuItem<any>[];
|
|
55
62
|
|
|
56
63
|
/**
|
|
57
64
|
* The HTML element that should be used as the parent element for the editor.
|
|
@@ -68,15 +75,15 @@ export type BlockNoteEditorOptions = {
|
|
|
68
75
|
/**
|
|
69
76
|
* A callback function that runs when the editor is ready to be used.
|
|
70
77
|
*/
|
|
71
|
-
onEditorReady: (editor: BlockNoteEditor) => void;
|
|
78
|
+
onEditorReady: (editor: BlockNoteEditor<BSchema>) => void;
|
|
72
79
|
/**
|
|
73
80
|
* A callback function that runs whenever the editor's contents change.
|
|
74
81
|
*/
|
|
75
|
-
onEditorContentChange: (editor: BlockNoteEditor) => void;
|
|
82
|
+
onEditorContentChange: (editor: BlockNoteEditor<BSchema>) => void;
|
|
76
83
|
/**
|
|
77
84
|
* A callback function that runs whenever the text cursor position changes.
|
|
78
85
|
*/
|
|
79
|
-
onTextCursorPositionChange: (editor: BlockNoteEditor) => void;
|
|
86
|
+
onTextCursorPositionChange: (editor: BlockNoteEditor<BSchema>) => void;
|
|
80
87
|
/**
|
|
81
88
|
* Locks the editor from being editable by the user if set to `false`.
|
|
82
89
|
*/
|
|
@@ -84,7 +91,7 @@ export type BlockNoteEditorOptions = {
|
|
|
84
91
|
/**
|
|
85
92
|
* The content that should be in the editor when it's created, represented as an array of partial block objects.
|
|
86
93
|
*/
|
|
87
|
-
initialContent: PartialBlock[];
|
|
94
|
+
initialContent: PartialBlock<BSchema>[];
|
|
88
95
|
/**
|
|
89
96
|
* Use default BlockNote font and reset the styles of <p> <li> <h1> elements etc., that are used in BlockNote.
|
|
90
97
|
*
|
|
@@ -98,6 +105,11 @@ export type BlockNoteEditorOptions = {
|
|
|
98
105
|
*/
|
|
99
106
|
theme: "light" | "dark";
|
|
100
107
|
|
|
108
|
+
/**
|
|
109
|
+
* A list of block types that should be available in the editor.
|
|
110
|
+
*/
|
|
111
|
+
blockSchema: BSchema;
|
|
112
|
+
|
|
101
113
|
/**
|
|
102
114
|
* When enabled, allows for collaboration between multiple users.
|
|
103
115
|
*/
|
|
@@ -133,9 +145,10 @@ const blockNoteTipTapOptions = {
|
|
|
133
145
|
enableCoreExtensions: false,
|
|
134
146
|
};
|
|
135
147
|
|
|
136
|
-
export class BlockNoteEditor {
|
|
148
|
+
export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
|
|
137
149
|
public readonly _tiptapEditor: TiptapEditor & { contentComponent: any };
|
|
138
|
-
|
|
150
|
+
public blockCache = new WeakMap<Node, Block<BSchema>>();
|
|
151
|
+
public readonly schema: BSchema;
|
|
139
152
|
|
|
140
153
|
public get domElement() {
|
|
141
154
|
return this._tiptapEditor.view.dom as HTMLDivElement;
|
|
@@ -145,20 +158,34 @@ export class BlockNoteEditor {
|
|
|
145
158
|
this._tiptapEditor.view.focus();
|
|
146
159
|
}
|
|
147
160
|
|
|
148
|
-
constructor(
|
|
161
|
+
constructor(
|
|
162
|
+
private readonly options: Partial<BlockNoteEditorOptions<BSchema>> = {}
|
|
163
|
+
) {
|
|
149
164
|
// apply defaults
|
|
150
|
-
options
|
|
165
|
+
const newOptions: Omit<typeof options, "defaultStyles" | "blockSchema"> & {
|
|
166
|
+
defaultStyles: boolean;
|
|
167
|
+
blockSchema: BSchema;
|
|
168
|
+
} = {
|
|
151
169
|
defaultStyles: true,
|
|
170
|
+
// TODO: There's a lot of annoying typing stuff to deal with here. If
|
|
171
|
+
// BSchema is specified, then options.blockSchema should also be required.
|
|
172
|
+
// If BSchema is not specified, then options.blockSchema should also not
|
|
173
|
+
// be defined. Unfortunately, trying to implement these constraints seems
|
|
174
|
+
// to be a huge pain, hence the `as any` casts.
|
|
175
|
+
blockSchema: options.blockSchema || (defaultBlockSchema as any),
|
|
152
176
|
...options,
|
|
153
177
|
};
|
|
154
178
|
|
|
155
|
-
const extensions = getBlockNoteExtensions({
|
|
179
|
+
const extensions = getBlockNoteExtensions<BSchema>({
|
|
156
180
|
editor: this,
|
|
157
|
-
uiFactories:
|
|
158
|
-
slashCommands:
|
|
159
|
-
|
|
181
|
+
uiFactories: newOptions.uiFactories || {},
|
|
182
|
+
slashCommands: newOptions.slashCommands || defaultSlashMenuItems,
|
|
183
|
+
blockSchema: newOptions.blockSchema,
|
|
184
|
+
collaboration: newOptions.collaboration,
|
|
160
185
|
});
|
|
161
186
|
|
|
187
|
+
this.schema = newOptions.blockSchema;
|
|
188
|
+
|
|
162
189
|
const tiptapOptions: EditorOptions = {
|
|
163
190
|
// TODO: This approach to setting initial content is "cleaner" but requires the PM editor schema, which is only
|
|
164
191
|
// created after initializing the TipTap editor. Not sure it's feasible.
|
|
@@ -168,39 +195,39 @@ export class BlockNoteEditor {
|
|
|
168
195
|
// blockToNode(block, this._tiptapEditor.schema).toJSON()
|
|
169
196
|
// ),
|
|
170
197
|
...blockNoteTipTapOptions,
|
|
171
|
-
...
|
|
198
|
+
...newOptions._tiptapOptions,
|
|
172
199
|
onCreate: () => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.replaceBlocks(this.topLevelBlocks,
|
|
200
|
+
newOptions.onEditorReady?.(this);
|
|
201
|
+
newOptions.initialContent &&
|
|
202
|
+
this.replaceBlocks(this.topLevelBlocks, newOptions.initialContent);
|
|
176
203
|
},
|
|
177
204
|
onUpdate: () => {
|
|
178
|
-
|
|
205
|
+
newOptions.onEditorContentChange?.(this);
|
|
179
206
|
},
|
|
180
207
|
onSelectionUpdate: () => {
|
|
181
|
-
|
|
208
|
+
newOptions.onTextCursorPositionChange?.(this);
|
|
182
209
|
},
|
|
183
210
|
editable: options.editable === undefined ? true : options.editable,
|
|
184
211
|
extensions:
|
|
185
|
-
|
|
186
|
-
?
|
|
187
|
-
: [...(
|
|
212
|
+
newOptions.enableBlockNoteExtensions === false
|
|
213
|
+
? newOptions._tiptapOptions?.extensions
|
|
214
|
+
: [...(newOptions._tiptapOptions?.extensions || []), ...extensions],
|
|
188
215
|
editorProps: {
|
|
189
216
|
attributes: {
|
|
190
217
|
"data-theme": options.theme || "light",
|
|
191
|
-
...(
|
|
218
|
+
...(newOptions.editorDOMAttributes || {}),
|
|
192
219
|
class: [
|
|
193
220
|
styles.bnEditor,
|
|
194
221
|
styles.bnRoot,
|
|
195
|
-
|
|
196
|
-
|
|
222
|
+
newOptions.defaultStyles ? styles.defaultStyles : "",
|
|
223
|
+
newOptions.editorDOMAttributes?.class || "",
|
|
197
224
|
].join(" "),
|
|
198
225
|
},
|
|
199
226
|
},
|
|
200
227
|
};
|
|
201
228
|
|
|
202
|
-
if (
|
|
203
|
-
tiptapOptions.element =
|
|
229
|
+
if (newOptions.parentElement) {
|
|
230
|
+
tiptapOptions.element = newOptions.parentElement;
|
|
204
231
|
}
|
|
205
232
|
|
|
206
233
|
this._tiptapEditor = new Editor(tiptapOptions) as Editor & {
|
|
@@ -212,11 +239,11 @@ export class BlockNoteEditor {
|
|
|
212
239
|
* Gets a snapshot of all top-level (non-nested) blocks in the editor.
|
|
213
240
|
* @returns A snapshot of all top-level (non-nested) blocks in the editor.
|
|
214
241
|
*/
|
|
215
|
-
public get topLevelBlocks(): Block[] {
|
|
216
|
-
const blocks: Block[] = [];
|
|
242
|
+
public get topLevelBlocks(): Block<BSchema>[] {
|
|
243
|
+
const blocks: Block<BSchema>[] = [];
|
|
217
244
|
|
|
218
245
|
this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
|
|
219
|
-
blocks.push(nodeToBlock(node, this.blockCache));
|
|
246
|
+
blocks.push(nodeToBlock(node, this.schema, this.blockCache));
|
|
220
247
|
|
|
221
248
|
return false;
|
|
222
249
|
});
|
|
@@ -229,12 +256,14 @@ export class BlockNoteEditor {
|
|
|
229
256
|
* @param blockIdentifier The identifier of an existing block that should be retrieved.
|
|
230
257
|
* @returns The block that matches the identifier, or `undefined` if no matching block was found.
|
|
231
258
|
*/
|
|
232
|
-
public getBlock(
|
|
259
|
+
public getBlock(
|
|
260
|
+
blockIdentifier: BlockIdentifier
|
|
261
|
+
): Block<BSchema> | undefined {
|
|
233
262
|
const id =
|
|
234
263
|
typeof blockIdentifier === "string"
|
|
235
264
|
? blockIdentifier
|
|
236
265
|
: blockIdentifier.id;
|
|
237
|
-
let newBlock: Block | undefined = undefined;
|
|
266
|
+
let newBlock: Block<BSchema> | undefined = undefined;
|
|
238
267
|
|
|
239
268
|
this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
|
|
240
269
|
if (typeof newBlock !== "undefined") {
|
|
@@ -245,7 +274,7 @@ export class BlockNoteEditor {
|
|
|
245
274
|
return true;
|
|
246
275
|
}
|
|
247
276
|
|
|
248
|
-
newBlock = nodeToBlock(node, this.blockCache);
|
|
277
|
+
newBlock = nodeToBlock(node, this.schema, this.blockCache);
|
|
249
278
|
|
|
250
279
|
return false;
|
|
251
280
|
});
|
|
@@ -259,7 +288,7 @@ export class BlockNoteEditor {
|
|
|
259
288
|
* @param reverse Whether the blocks should be traversed in reverse order.
|
|
260
289
|
*/
|
|
261
290
|
public forEachBlock(
|
|
262
|
-
callback: (block: Block) => boolean,
|
|
291
|
+
callback: (block: Block<BSchema>) => boolean,
|
|
263
292
|
reverse: boolean = false
|
|
264
293
|
): void {
|
|
265
294
|
const blocks = this.topLevelBlocks.slice();
|
|
@@ -268,7 +297,7 @@ export class BlockNoteEditor {
|
|
|
268
297
|
blocks.reverse();
|
|
269
298
|
}
|
|
270
299
|
|
|
271
|
-
function traverseBlockArray(blockArray: Block[]): boolean {
|
|
300
|
+
function traverseBlockArray(blockArray: Block<BSchema>[]): boolean {
|
|
272
301
|
for (const block of blockArray) {
|
|
273
302
|
if (!callback(block)) {
|
|
274
303
|
return false;
|
|
@@ -289,11 +318,19 @@ export class BlockNoteEditor {
|
|
|
289
318
|
traverseBlockArray(blocks);
|
|
290
319
|
}
|
|
291
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Executes a callback whenever the editor's contents change.
|
|
323
|
+
* @param callback The callback to execute.
|
|
324
|
+
*/
|
|
325
|
+
public onEditorContentChange(callback: () => void) {
|
|
326
|
+
this._tiptapEditor.on("update", callback);
|
|
327
|
+
}
|
|
328
|
+
|
|
292
329
|
/**
|
|
293
330
|
* Gets a snapshot of the current text cursor position.
|
|
294
331
|
* @returns A snapshot of the current text cursor position.
|
|
295
332
|
*/
|
|
296
|
-
public getTextCursorPosition(): TextCursorPosition {
|
|
333
|
+
public getTextCursorPosition(): TextCursorPosition<BSchema> {
|
|
297
334
|
const { node, depth, startPos, endPos } = getBlockInfoFromPos(
|
|
298
335
|
this._tiptapEditor.state.doc,
|
|
299
336
|
this._tiptapEditor.state.selection.from
|
|
@@ -321,15 +358,15 @@ export class BlockNoteEditor {
|
|
|
321
358
|
}
|
|
322
359
|
|
|
323
360
|
return {
|
|
324
|
-
block: nodeToBlock(node, this.blockCache),
|
|
361
|
+
block: nodeToBlock(node, this.schema, this.blockCache),
|
|
325
362
|
prevBlock:
|
|
326
363
|
prevNode === undefined
|
|
327
364
|
? undefined
|
|
328
|
-
: nodeToBlock(prevNode, this.blockCache),
|
|
365
|
+
: nodeToBlock(prevNode, this.schema, this.blockCache),
|
|
329
366
|
nextBlock:
|
|
330
367
|
nextNode === undefined
|
|
331
368
|
? undefined
|
|
332
|
-
: nodeToBlock(nextNode, this.blockCache),
|
|
369
|
+
: nodeToBlock(nextNode, this.schema, this.blockCache),
|
|
333
370
|
};
|
|
334
371
|
}
|
|
335
372
|
|
|
@@ -363,7 +400,7 @@ export class BlockNoteEditor {
|
|
|
363
400
|
/**
|
|
364
401
|
* Gets a snapshot of the current selection.
|
|
365
402
|
*/
|
|
366
|
-
public getSelection(): Selection | undefined {
|
|
403
|
+
public getSelection(): Selection<BSchema> | undefined {
|
|
367
404
|
if (
|
|
368
405
|
this._tiptapEditor.state.selection.from ===
|
|
369
406
|
this._tiptapEditor.state.selection.to
|
|
@@ -371,7 +408,7 @@ export class BlockNoteEditor {
|
|
|
371
408
|
return undefined;
|
|
372
409
|
}
|
|
373
410
|
|
|
374
|
-
const blocks: Block[] = [];
|
|
411
|
+
const blocks: Block<BSchema>[] = [];
|
|
375
412
|
|
|
376
413
|
this._tiptapEditor.state.doc.descendants((node, pos) => {
|
|
377
414
|
if (node.type.spec.group !== "blockContent") {
|
|
@@ -388,6 +425,7 @@ export class BlockNoteEditor {
|
|
|
388
425
|
blocks.push(
|
|
389
426
|
nodeToBlock(
|
|
390
427
|
this._tiptapEditor.state.doc.resolve(pos).node(),
|
|
428
|
+
this.schema,
|
|
391
429
|
this.blockCache
|
|
392
430
|
)
|
|
393
431
|
);
|
|
@@ -423,7 +461,7 @@ export class BlockNoteEditor {
|
|
|
423
461
|
* `referenceBlock`. Inserts the blocks at the start of the existing block's children if "nested" is used.
|
|
424
462
|
*/
|
|
425
463
|
public insertBlocks(
|
|
426
|
-
blocksToInsert: PartialBlock[],
|
|
464
|
+
blocksToInsert: PartialBlock<BSchema>[],
|
|
427
465
|
referenceBlock: BlockIdentifier,
|
|
428
466
|
placement: "before" | "after" | "nested" = "before"
|
|
429
467
|
): void {
|
|
@@ -437,7 +475,10 @@ export class BlockNoteEditor {
|
|
|
437
475
|
* @param blockToUpdate The block that should be updated.
|
|
438
476
|
* @param update A partial block which defines how the existing block should be changed.
|
|
439
477
|
*/
|
|
440
|
-
public updateBlock(
|
|
478
|
+
public updateBlock(
|
|
479
|
+
blockToUpdate: BlockIdentifier,
|
|
480
|
+
update: PartialBlock<BSchema>
|
|
481
|
+
) {
|
|
441
482
|
updateBlock(blockToUpdate, update, this._tiptapEditor);
|
|
442
483
|
}
|
|
443
484
|
|
|
@@ -458,7 +499,7 @@ export class BlockNoteEditor {
|
|
|
458
499
|
*/
|
|
459
500
|
public replaceBlocks(
|
|
460
501
|
blocksToRemove: BlockIdentifier[],
|
|
461
|
-
blocksToInsert: PartialBlock[]
|
|
502
|
+
blocksToInsert: PartialBlock<BSchema>[]
|
|
462
503
|
) {
|
|
463
504
|
replaceBlocks(blocksToRemove, blocksToInsert, this._tiptapEditor);
|
|
464
505
|
}
|
|
@@ -504,6 +545,8 @@ export class BlockNoteEditor {
|
|
|
504
545
|
]);
|
|
505
546
|
const colorStyles = new Set<ColorStyle>(["textColor", "backgroundColor"]);
|
|
506
547
|
|
|
548
|
+
this._tiptapEditor.view.focus();
|
|
549
|
+
|
|
507
550
|
for (const [style, value] of Object.entries(styles)) {
|
|
508
551
|
if (toggleStyles.has(style as ToggledStyle)) {
|
|
509
552
|
this._tiptapEditor.commands.setMark(style);
|
|
@@ -518,6 +561,8 @@ export class BlockNoteEditor {
|
|
|
518
561
|
* @param styles The styles to remove.
|
|
519
562
|
*/
|
|
520
563
|
public removeStyles(styles: Styles) {
|
|
564
|
+
this._tiptapEditor.view.focus();
|
|
565
|
+
|
|
521
566
|
for (const style of Object.keys(styles)) {
|
|
522
567
|
this._tiptapEditor.commands.unsetMark(style);
|
|
523
568
|
}
|
|
@@ -537,6 +582,8 @@ export class BlockNoteEditor {
|
|
|
537
582
|
]);
|
|
538
583
|
const colorStyles = new Set<ColorStyle>(["textColor", "backgroundColor"]);
|
|
539
584
|
|
|
585
|
+
this._tiptapEditor.view.focus();
|
|
586
|
+
|
|
540
587
|
for (const [style, value] of Object.entries(styles)) {
|
|
541
588
|
if (toggleStyles.has(style as ToggledStyle)) {
|
|
542
589
|
this._tiptapEditor.commands.toggleMark(style);
|
|
@@ -632,7 +679,7 @@ export class BlockNoteEditor {
|
|
|
632
679
|
* @param blocks An array of blocks that should be serialized into HTML.
|
|
633
680
|
* @returns The blocks, serialized as an HTML string.
|
|
634
681
|
*/
|
|
635
|
-
public async blocksToHTML(blocks: Block[]): Promise<string> {
|
|
682
|
+
public async blocksToHTML(blocks: Block<BSchema>[]): Promise<string> {
|
|
636
683
|
return blocksToHTML(blocks, this._tiptapEditor.schema);
|
|
637
684
|
}
|
|
638
685
|
|
|
@@ -643,8 +690,8 @@ export class BlockNoteEditor {
|
|
|
643
690
|
* @param html The HTML string to parse blocks from.
|
|
644
691
|
* @returns The blocks parsed from the HTML string.
|
|
645
692
|
*/
|
|
646
|
-
public async HTMLToBlocks(html: string): Promise<Block[]> {
|
|
647
|
-
return HTMLToBlocks(html, this._tiptapEditor.schema);
|
|
693
|
+
public async HTMLToBlocks(html: string): Promise<Block<BSchema>[]> {
|
|
694
|
+
return HTMLToBlocks(html, this.schema, this._tiptapEditor.schema);
|
|
648
695
|
}
|
|
649
696
|
|
|
650
697
|
/**
|
|
@@ -653,7 +700,7 @@ export class BlockNoteEditor {
|
|
|
653
700
|
* @param blocks An array of blocks that should be serialized into Markdown.
|
|
654
701
|
* @returns The blocks, serialized as a Markdown string.
|
|
655
702
|
*/
|
|
656
|
-
public async blocksToMarkdown(blocks: Block[]): Promise<string> {
|
|
703
|
+
public async blocksToMarkdown(blocks: Block<BSchema>[]): Promise<string> {
|
|
657
704
|
return blocksToMarkdown(blocks, this._tiptapEditor.schema);
|
|
658
705
|
}
|
|
659
706
|
|
|
@@ -664,8 +711,8 @@ export class BlockNoteEditor {
|
|
|
664
711
|
* @param markdown The Markdown string to parse blocks from.
|
|
665
712
|
* @returns The blocks parsed from the Markdown string.
|
|
666
713
|
*/
|
|
667
|
-
public async markdownToBlocks(markdown: string): Promise<Block[]> {
|
|
668
|
-
return markdownToBlocks(markdown, this._tiptapEditor.schema);
|
|
714
|
+
public async markdownToBlocks(markdown: string): Promise<Block<BSchema>[]> {
|
|
715
|
+
return markdownToBlocks(markdown, this.schema, this._tiptapEditor.schema);
|
|
669
716
|
}
|
|
670
717
|
|
|
671
718
|
/**
|
|
@@ -20,15 +20,20 @@ import styles from "./editor.module.css";
|
|
|
20
20
|
import { BackgroundColorExtension } from "./extensions/BackgroundColor/BackgroundColorExtension";
|
|
21
21
|
import { BackgroundColorMark } from "./extensions/BackgroundColor/BackgroundColorMark";
|
|
22
22
|
import { blocks } from "./extensions/Blocks";
|
|
23
|
+
import { BlockSchema } from "./extensions/Blocks/api/blockTypes";
|
|
24
|
+
import { CustomBlockSerializerExtension } from "./extensions/Blocks/api/serialization";
|
|
23
25
|
import blockStyles from "./extensions/Blocks/nodes/Block.module.css";
|
|
24
26
|
import { BlockSideMenuFactory } from "./extensions/DraggableBlocks/BlockSideMenuFactoryTypes";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
+
import { createDraggableBlocksExtension } from "./extensions/DraggableBlocks/DraggableBlocksExtension";
|
|
28
|
+
import { createFormattingToolbarExtension } from "./extensions/FormattingToolbar/FormattingToolbarExtension";
|
|
27
29
|
import { FormattingToolbarFactory } from "./extensions/FormattingToolbar/FormattingToolbarFactoryTypes";
|
|
28
30
|
import HyperlinkMark from "./extensions/HyperlinkToolbar/HyperlinkMark";
|
|
29
31
|
import { HyperlinkToolbarFactory } from "./extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes";
|
|
30
32
|
import { Placeholder } from "./extensions/Placeholder/PlaceholderExtension";
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
BaseSlashMenuItem,
|
|
35
|
+
createSlashMenuExtension,
|
|
36
|
+
} from "./extensions/SlashMenu";
|
|
32
37
|
import { TextAlignmentExtension } from "./extensions/TextAlignment/TextAlignmentExtension";
|
|
33
38
|
import { TextColorExtension } from "./extensions/TextColor/TextColorExtension";
|
|
34
39
|
import { TextColorMark } from "./extensions/TextColor/TextColorMark";
|
|
@@ -36,20 +41,21 @@ import { TrailingNode } from "./extensions/TrailingNode/TrailingNodeExtension";
|
|
|
36
41
|
import UniqueID from "./extensions/UniqueID/UniqueID";
|
|
37
42
|
import { SuggestionsMenuFactory } from "./shared/plugins/suggestion/SuggestionsMenuFactoryTypes";
|
|
38
43
|
|
|
39
|
-
export type UiFactories = Partial<{
|
|
40
|
-
formattingToolbarFactory: FormattingToolbarFactory
|
|
44
|
+
export type UiFactories<BSchema extends BlockSchema> = Partial<{
|
|
45
|
+
formattingToolbarFactory: FormattingToolbarFactory<BSchema>;
|
|
41
46
|
hyperlinkToolbarFactory: HyperlinkToolbarFactory;
|
|
42
|
-
slashMenuFactory: SuggestionsMenuFactory<BaseSlashMenuItem
|
|
43
|
-
blockSideMenuFactory: BlockSideMenuFactory
|
|
47
|
+
slashMenuFactory: SuggestionsMenuFactory<BaseSlashMenuItem<BSchema>>;
|
|
48
|
+
blockSideMenuFactory: BlockSideMenuFactory<BSchema>;
|
|
44
49
|
}>;
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
52
|
* Get all the Tiptap extensions BlockNote is configured with by default
|
|
48
53
|
*/
|
|
49
|
-
export const getBlockNoteExtensions = (opts: {
|
|
50
|
-
editor: BlockNoteEditor
|
|
51
|
-
uiFactories: UiFactories
|
|
52
|
-
slashCommands: BaseSlashMenuItem[];
|
|
54
|
+
export const getBlockNoteExtensions = <BSchema extends BlockSchema>(opts: {
|
|
55
|
+
editor: BlockNoteEditor<BSchema>;
|
|
56
|
+
uiFactories: UiFactories<BSchema>;
|
|
57
|
+
slashCommands: BaseSlashMenuItem<any>[]; // couldn't fix type, see https://github.com/TypeCellOS/BlockNote/pull/191#discussion_r1210708771
|
|
58
|
+
blockSchema: BSchema;
|
|
53
59
|
collaboration?: {
|
|
54
60
|
fragment: Y.XmlFragment;
|
|
55
61
|
user: {
|
|
@@ -101,6 +107,10 @@ export const getBlockNoteExtensions = (opts: {
|
|
|
101
107
|
|
|
102
108
|
// custom blocks:
|
|
103
109
|
...blocks,
|
|
110
|
+
...Object.values(opts.blockSchema).map((blockSpec) =>
|
|
111
|
+
blockSpec.node.configure({ editor: opts.editor })
|
|
112
|
+
),
|
|
113
|
+
CustomBlockSerializerExtension,
|
|
104
114
|
|
|
105
115
|
Dropcursor.configure({ width: 5, color: "#ddeeff" }),
|
|
106
116
|
// This needs to be at the bottom of this list, because Key events (such as enter, when selecting a /command),
|
|
@@ -147,7 +157,7 @@ export const getBlockNoteExtensions = (opts: {
|
|
|
147
157
|
|
|
148
158
|
if (opts.uiFactories.blockSideMenuFactory) {
|
|
149
159
|
ret.push(
|
|
150
|
-
|
|
160
|
+
createDraggableBlocksExtension<BSchema>().configure({
|
|
151
161
|
editor: opts.editor,
|
|
152
162
|
blockSideMenuFactory: opts.uiFactories.blockSideMenuFactory,
|
|
153
163
|
})
|
|
@@ -156,7 +166,7 @@ export const getBlockNoteExtensions = (opts: {
|
|
|
156
166
|
|
|
157
167
|
if (opts.uiFactories.formattingToolbarFactory) {
|
|
158
168
|
ret.push(
|
|
159
|
-
|
|
169
|
+
createFormattingToolbarExtension<BSchema>().configure({
|
|
160
170
|
editor: opts.editor,
|
|
161
171
|
formattingToolbarFactory: opts.uiFactories.formattingToolbarFactory,
|
|
162
172
|
})
|
|
@@ -175,7 +185,7 @@ export const getBlockNoteExtensions = (opts: {
|
|
|
175
185
|
|
|
176
186
|
if (opts.uiFactories.slashMenuFactory) {
|
|
177
187
|
ret.push(
|
|
178
|
-
|
|
188
|
+
createSlashMenuExtension<BSchema>().configure({
|
|
179
189
|
editor: opts.editor,
|
|
180
190
|
commands: opts.slashCommands,
|
|
181
191
|
slashMenuFactory: opts.uiFactories.slashMenuFactory,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
2
|
import { Block, BlockNoteEditor, PartialBlock } from "../..";
|
|
3
|
+
import { DefaultBlockSchema } from "../../extensions/Blocks/api/defaultBlocks";
|
|
3
4
|
|
|
4
5
|
let editor: BlockNoteEditor;
|
|
5
6
|
|
|
@@ -14,11 +15,13 @@ function waitForEditor() {
|
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
let singleBlock: PartialBlock
|
|
18
|
+
let singleBlock: PartialBlock<DefaultBlockSchema>;
|
|
18
19
|
|
|
19
|
-
let multipleBlocks: PartialBlock[];
|
|
20
|
+
let multipleBlocks: PartialBlock<DefaultBlockSchema>[];
|
|
20
21
|
|
|
21
|
-
let insert: (
|
|
22
|
+
let insert: (
|
|
23
|
+
placement: "before" | "nested" | "after"
|
|
24
|
+
) => Block<DefaultBlockSchema>[];
|
|
22
25
|
|
|
23
26
|
beforeEach(() => {
|
|
24
27
|
(window as Window & { __TEST_OPTIONS?: {} }).__TEST_OPTIONS = {};
|
|
@@ -2,13 +2,14 @@ import { Editor } from "@tiptap/core";
|
|
|
2
2
|
import { Node } from "prosemirror-model";
|
|
3
3
|
import {
|
|
4
4
|
BlockIdentifier,
|
|
5
|
+
BlockSchema,
|
|
5
6
|
PartialBlock,
|
|
6
7
|
} from "../../extensions/Blocks/api/blockTypes";
|
|
7
8
|
import { blockToNode } from "../nodeConversions/nodeConversions";
|
|
8
9
|
import { getNodeById } from "../util/nodeUtil";
|
|
9
10
|
|
|
10
|
-
export function insertBlocks(
|
|
11
|
-
blocksToInsert: PartialBlock[],
|
|
11
|
+
export function insertBlocks<BSchema extends BlockSchema>(
|
|
12
|
+
blocksToInsert: PartialBlock<BSchema>[],
|
|
12
13
|
referenceBlock: BlockIdentifier,
|
|
13
14
|
placement: "before" | "after" | "nested" = "before",
|
|
14
15
|
editor: Editor
|
|
@@ -56,9 +57,9 @@ export function insertBlocks(
|
|
|
56
57
|
editor.view.dispatch(editor.state.tr.insert(insertionPos, nodesToInsert));
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
export function updateBlock(
|
|
60
|
+
export function updateBlock<BSchema extends BlockSchema>(
|
|
60
61
|
blockToUpdate: BlockIdentifier,
|
|
61
|
-
update: PartialBlock
|
|
62
|
+
update: PartialBlock<BSchema>,
|
|
62
63
|
editor: Editor
|
|
63
64
|
) {
|
|
64
65
|
const id =
|
|
@@ -115,9 +116,9 @@ export function removeBlocks(
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
export function replaceBlocks(
|
|
119
|
+
export function replaceBlocks<BSchema extends BlockSchema>(
|
|
119
120
|
blocksToRemove: BlockIdentifier[],
|
|
120
|
-
blocksToInsert: PartialBlock[],
|
|
121
|
+
blocksToInsert: PartialBlock<BSchema>[],
|
|
121
122
|
editor: Editor
|
|
122
123
|
) {
|
|
123
124
|
insertBlocks(blocksToInsert, blocksToRemove[0], "before", editor);
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
2
|
import { Block, BlockNoteEditor } from "../..";
|
|
3
3
|
import UniqueID from "../../extensions/UniqueID/UniqueID";
|
|
4
|
+
import { DefaultBlockSchema } from "../../extensions/Blocks/api/defaultBlocks";
|
|
4
5
|
|
|
5
6
|
let editor: BlockNoteEditor;
|
|
6
7
|
|
|
7
|
-
let nonNestedBlocks: Block[];
|
|
8
|
+
let nonNestedBlocks: Block<DefaultBlockSchema>[];
|
|
8
9
|
let nonNestedHTML: string;
|
|
9
10
|
let nonNestedMarkdown: string;
|
|
10
11
|
|
|
11
|
-
let nestedBlocks: Block[];
|
|
12
|
+
let nestedBlocks: Block<DefaultBlockSchema>[];
|
|
12
13
|
// let nestedHTML: string;
|
|
13
14
|
// let nestedMarkdown: string;
|
|
14
15
|
|
|
15
|
-
let styledBlocks: Block[];
|
|
16
|
+
let styledBlocks: Block<DefaultBlockSchema>[];
|
|
16
17
|
let styledHTML: string;
|
|
17
18
|
let styledMarkdown: string;
|
|
18
19
|
|
|
19
|
-
let complexBlocks: Block[];
|
|
20
|
+
let complexBlocks: Block<DefaultBlockSchema>[];
|
|
20
21
|
// let complexHTML: string;
|
|
21
22
|
// let complexMarkdown: string;
|
|
22
23
|
|
|
24
|
+
function removeInlineContentClass(html: string) {
|
|
25
|
+
return html.replace(/ class="_inlineContent_1c48ad"/g, "");
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
beforeEach(() => {
|
|
24
29
|
(window as Window & { __TEST_OPTIONS?: {} }).__TEST_OPTIONS = {};
|
|
25
30
|
|
|
@@ -665,7 +670,7 @@ describe("Non-Nested Block/HTML/Markdown Conversions", () => {
|
|
|
665
670
|
it("Convert non-nested blocks to HTML", async () => {
|
|
666
671
|
const output = await editor.blocksToHTML(nonNestedBlocks);
|
|
667
672
|
|
|
668
|
-
expect(output).toMatchSnapshot();
|
|
673
|
+
expect(removeInlineContentClass(output)).toMatchSnapshot();
|
|
669
674
|
});
|
|
670
675
|
|
|
671
676
|
it("Convert non-nested blocks to Markdown", async () => {
|
|
@@ -691,7 +696,7 @@ describe("Nested Block/HTML/Markdown Conversions", () => {
|
|
|
691
696
|
it("Convert nested blocks to HTML", async () => {
|
|
692
697
|
const output = await editor.blocksToHTML(nestedBlocks);
|
|
693
698
|
|
|
694
|
-
expect(output).toMatchSnapshot();
|
|
699
|
+
expect(removeInlineContentClass(output)).toMatchSnapshot();
|
|
695
700
|
});
|
|
696
701
|
|
|
697
702
|
it("Convert nested blocks to Markdown", async () => {
|
|
@@ -717,7 +722,7 @@ describe("Styled Block/HTML/Markdown Conversions", () => {
|
|
|
717
722
|
it("Convert styled blocks to HTML", async () => {
|
|
718
723
|
const output = await editor.blocksToHTML(styledBlocks);
|
|
719
724
|
|
|
720
|
-
expect(output).toMatchSnapshot();
|
|
725
|
+
expect(removeInlineContentClass(output)).toMatchSnapshot();
|
|
721
726
|
});
|
|
722
727
|
|
|
723
728
|
it("Convert styled blocks to Markdown", async () => {
|
|
@@ -743,7 +748,7 @@ describe("Complex Block/HTML/Markdown Conversions", () => {
|
|
|
743
748
|
it("Convert complex blocks to HTML", async () => {
|
|
744
749
|
const output = await editor.blocksToHTML(complexBlocks);
|
|
745
750
|
|
|
746
|
-
expect(output).toMatchSnapshot();
|
|
751
|
+
expect(removeInlineContentClass(output)).toMatchSnapshot();
|
|
747
752
|
});
|
|
748
753
|
|
|
749
754
|
it("Convert complex blocks to Markdown", async () => {
|