@blocknote/core 0.7.1-alpha.0 → 0.8.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/dist/blocknote.js +1711 -1469
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +6 -2
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/BlockNoteEditor.ts +104 -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/__snapshots__/nodeConversions.test.ts.snap +292 -0
- package/src/api/nodeConversions/nodeConversions.test.ts +265 -10
- package/src/api/nodeConversions/nodeConversions.ts +199 -47
- package/src/api/nodeConversions/testUtil.ts +8 -4
- package/src/editor.module.css +5 -6
- 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 +28 -16
- 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 +57 -14
- package/src/extensions/FormattingToolbar/FormattingToolbarExtension.ts +21 -16
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +9 -5
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +38 -58
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +19 -0
- 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 +38 -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 +2 -4
- 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/defaultBlocks.d.ts +4 -4
- 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 +12 -11
- 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/Placeholder/localisation/index.d.ts +2 -0
- package/types/src/extensions/Placeholder/localisation/translation.d.ts +51 -0
- 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/types/src/extensions/Blocks/api/alertBlock.d.ts +0 -13
- package/types/src/extensions/Blocks/api/alertBlock2.d.ts +0 -13
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.1",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"types",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"eslint-config-react-app": "^7.0.0",
|
|
93
93
|
"jsdom": "^21.1.0",
|
|
94
94
|
"prettier": "^2.7.1",
|
|
95
|
-
"typescript": "^
|
|
95
|
+
"typescript": "^5.0.4",
|
|
96
96
|
"vite": "^4.1.2",
|
|
97
97
|
"vite-plugin-eslint": "^1.8.1",
|
|
98
98
|
"vitest": "^0.28.5"
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"access": "public",
|
|
111
111
|
"registry": "https://registry.npmjs.org/"
|
|
112
112
|
},
|
|
113
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "2265d9068126df0881dd813ba6a265b82f48b466"
|
|
114
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,32 +145,51 @@ 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;
|
|
142
155
|
}
|
|
143
156
|
|
|
157
|
+
public isFocused() {
|
|
158
|
+
return this._tiptapEditor.view.hasFocus();
|
|
159
|
+
}
|
|
160
|
+
|
|
144
161
|
public focus() {
|
|
145
162
|
this._tiptapEditor.view.focus();
|
|
146
163
|
}
|
|
147
164
|
|
|
148
|
-
constructor(
|
|
165
|
+
constructor(
|
|
166
|
+
private readonly options: Partial<BlockNoteEditorOptions<BSchema>> = {}
|
|
167
|
+
) {
|
|
149
168
|
// apply defaults
|
|
150
|
-
options
|
|
169
|
+
const newOptions: Omit<typeof options, "defaultStyles" | "blockSchema"> & {
|
|
170
|
+
defaultStyles: boolean;
|
|
171
|
+
blockSchema: BSchema;
|
|
172
|
+
} = {
|
|
151
173
|
defaultStyles: true,
|
|
174
|
+
// TODO: There's a lot of annoying typing stuff to deal with here. If
|
|
175
|
+
// BSchema is specified, then options.blockSchema should also be required.
|
|
176
|
+
// If BSchema is not specified, then options.blockSchema should also not
|
|
177
|
+
// be defined. Unfortunately, trying to implement these constraints seems
|
|
178
|
+
// to be a huge pain, hence the `as any` casts.
|
|
179
|
+
blockSchema: options.blockSchema || (defaultBlockSchema as any),
|
|
152
180
|
...options,
|
|
153
181
|
};
|
|
154
182
|
|
|
155
|
-
const extensions = getBlockNoteExtensions({
|
|
183
|
+
const extensions = getBlockNoteExtensions<BSchema>({
|
|
156
184
|
editor: this,
|
|
157
|
-
uiFactories:
|
|
158
|
-
slashCommands:
|
|
159
|
-
|
|
185
|
+
uiFactories: newOptions.uiFactories || {},
|
|
186
|
+
slashCommands: newOptions.slashCommands || defaultSlashMenuItems,
|
|
187
|
+
blockSchema: newOptions.blockSchema,
|
|
188
|
+
collaboration: newOptions.collaboration,
|
|
160
189
|
});
|
|
161
190
|
|
|
191
|
+
this.schema = newOptions.blockSchema;
|
|
192
|
+
|
|
162
193
|
const tiptapOptions: EditorOptions = {
|
|
163
194
|
// TODO: This approach to setting initial content is "cleaner" but requires the PM editor schema, which is only
|
|
164
195
|
// created after initializing the TipTap editor. Not sure it's feasible.
|
|
@@ -168,39 +199,39 @@ export class BlockNoteEditor {
|
|
|
168
199
|
// blockToNode(block, this._tiptapEditor.schema).toJSON()
|
|
169
200
|
// ),
|
|
170
201
|
...blockNoteTipTapOptions,
|
|
171
|
-
...
|
|
202
|
+
...newOptions._tiptapOptions,
|
|
172
203
|
onCreate: () => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.replaceBlocks(this.topLevelBlocks,
|
|
204
|
+
newOptions.onEditorReady?.(this);
|
|
205
|
+
newOptions.initialContent &&
|
|
206
|
+
this.replaceBlocks(this.topLevelBlocks, newOptions.initialContent);
|
|
176
207
|
},
|
|
177
208
|
onUpdate: () => {
|
|
178
|
-
|
|
209
|
+
newOptions.onEditorContentChange?.(this);
|
|
179
210
|
},
|
|
180
211
|
onSelectionUpdate: () => {
|
|
181
|
-
|
|
212
|
+
newOptions.onTextCursorPositionChange?.(this);
|
|
182
213
|
},
|
|
183
214
|
editable: options.editable === undefined ? true : options.editable,
|
|
184
215
|
extensions:
|
|
185
|
-
|
|
186
|
-
?
|
|
187
|
-
: [...(
|
|
216
|
+
newOptions.enableBlockNoteExtensions === false
|
|
217
|
+
? newOptions._tiptapOptions?.extensions
|
|
218
|
+
: [...(newOptions._tiptapOptions?.extensions || []), ...extensions],
|
|
188
219
|
editorProps: {
|
|
189
220
|
attributes: {
|
|
190
221
|
"data-theme": options.theme || "light",
|
|
191
|
-
...(
|
|
222
|
+
...(newOptions.editorDOMAttributes || {}),
|
|
192
223
|
class: [
|
|
193
224
|
styles.bnEditor,
|
|
194
225
|
styles.bnRoot,
|
|
195
|
-
|
|
196
|
-
|
|
226
|
+
newOptions.defaultStyles ? styles.defaultStyles : "",
|
|
227
|
+
newOptions.editorDOMAttributes?.class || "",
|
|
197
228
|
].join(" "),
|
|
198
229
|
},
|
|
199
230
|
},
|
|
200
231
|
};
|
|
201
232
|
|
|
202
|
-
if (
|
|
203
|
-
tiptapOptions.element =
|
|
233
|
+
if (newOptions.parentElement) {
|
|
234
|
+
tiptapOptions.element = newOptions.parentElement;
|
|
204
235
|
}
|
|
205
236
|
|
|
206
237
|
this._tiptapEditor = new Editor(tiptapOptions) as Editor & {
|
|
@@ -212,11 +243,11 @@ export class BlockNoteEditor {
|
|
|
212
243
|
* Gets a snapshot of all top-level (non-nested) blocks in the editor.
|
|
213
244
|
* @returns A snapshot of all top-level (non-nested) blocks in the editor.
|
|
214
245
|
*/
|
|
215
|
-
public get topLevelBlocks(): Block[] {
|
|
216
|
-
const blocks: Block[] = [];
|
|
246
|
+
public get topLevelBlocks(): Block<BSchema>[] {
|
|
247
|
+
const blocks: Block<BSchema>[] = [];
|
|
217
248
|
|
|
218
249
|
this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
|
|
219
|
-
blocks.push(nodeToBlock(node, this.blockCache));
|
|
250
|
+
blocks.push(nodeToBlock(node, this.schema, this.blockCache));
|
|
220
251
|
|
|
221
252
|
return false;
|
|
222
253
|
});
|
|
@@ -229,12 +260,14 @@ export class BlockNoteEditor {
|
|
|
229
260
|
* @param blockIdentifier The identifier of an existing block that should be retrieved.
|
|
230
261
|
* @returns The block that matches the identifier, or `undefined` if no matching block was found.
|
|
231
262
|
*/
|
|
232
|
-
public getBlock(
|
|
263
|
+
public getBlock(
|
|
264
|
+
blockIdentifier: BlockIdentifier
|
|
265
|
+
): Block<BSchema> | undefined {
|
|
233
266
|
const id =
|
|
234
267
|
typeof blockIdentifier === "string"
|
|
235
268
|
? blockIdentifier
|
|
236
269
|
: blockIdentifier.id;
|
|
237
|
-
let newBlock: Block | undefined = undefined;
|
|
270
|
+
let newBlock: Block<BSchema> | undefined = undefined;
|
|
238
271
|
|
|
239
272
|
this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
|
|
240
273
|
if (typeof newBlock !== "undefined") {
|
|
@@ -245,7 +278,7 @@ export class BlockNoteEditor {
|
|
|
245
278
|
return true;
|
|
246
279
|
}
|
|
247
280
|
|
|
248
|
-
newBlock = nodeToBlock(node, this.blockCache);
|
|
281
|
+
newBlock = nodeToBlock(node, this.schema, this.blockCache);
|
|
249
282
|
|
|
250
283
|
return false;
|
|
251
284
|
});
|
|
@@ -259,7 +292,7 @@ export class BlockNoteEditor {
|
|
|
259
292
|
* @param reverse Whether the blocks should be traversed in reverse order.
|
|
260
293
|
*/
|
|
261
294
|
public forEachBlock(
|
|
262
|
-
callback: (block: Block) => boolean,
|
|
295
|
+
callback: (block: Block<BSchema>) => boolean,
|
|
263
296
|
reverse: boolean = false
|
|
264
297
|
): void {
|
|
265
298
|
const blocks = this.topLevelBlocks.slice();
|
|
@@ -268,7 +301,7 @@ export class BlockNoteEditor {
|
|
|
268
301
|
blocks.reverse();
|
|
269
302
|
}
|
|
270
303
|
|
|
271
|
-
function traverseBlockArray(blockArray: Block[]): boolean {
|
|
304
|
+
function traverseBlockArray(blockArray: Block<BSchema>[]): boolean {
|
|
272
305
|
for (const block of blockArray) {
|
|
273
306
|
if (!callback(block)) {
|
|
274
307
|
return false;
|
|
@@ -289,11 +322,19 @@ export class BlockNoteEditor {
|
|
|
289
322
|
traverseBlockArray(blocks);
|
|
290
323
|
}
|
|
291
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Executes a callback whenever the editor's contents change.
|
|
327
|
+
* @param callback The callback to execute.
|
|
328
|
+
*/
|
|
329
|
+
public onEditorContentChange(callback: () => void) {
|
|
330
|
+
this._tiptapEditor.on("update", callback);
|
|
331
|
+
}
|
|
332
|
+
|
|
292
333
|
/**
|
|
293
334
|
* Gets a snapshot of the current text cursor position.
|
|
294
335
|
* @returns A snapshot of the current text cursor position.
|
|
295
336
|
*/
|
|
296
|
-
public getTextCursorPosition(): TextCursorPosition {
|
|
337
|
+
public getTextCursorPosition(): TextCursorPosition<BSchema> {
|
|
297
338
|
const { node, depth, startPos, endPos } = getBlockInfoFromPos(
|
|
298
339
|
this._tiptapEditor.state.doc,
|
|
299
340
|
this._tiptapEditor.state.selection.from
|
|
@@ -321,15 +362,15 @@ export class BlockNoteEditor {
|
|
|
321
362
|
}
|
|
322
363
|
|
|
323
364
|
return {
|
|
324
|
-
block: nodeToBlock(node, this.blockCache),
|
|
365
|
+
block: nodeToBlock(node, this.schema, this.blockCache),
|
|
325
366
|
prevBlock:
|
|
326
367
|
prevNode === undefined
|
|
327
368
|
? undefined
|
|
328
|
-
: nodeToBlock(prevNode, this.blockCache),
|
|
369
|
+
: nodeToBlock(prevNode, this.schema, this.blockCache),
|
|
329
370
|
nextBlock:
|
|
330
371
|
nextNode === undefined
|
|
331
372
|
? undefined
|
|
332
|
-
: nodeToBlock(nextNode, this.blockCache),
|
|
373
|
+
: nodeToBlock(nextNode, this.schema, this.blockCache),
|
|
333
374
|
};
|
|
334
375
|
}
|
|
335
376
|
|
|
@@ -363,7 +404,7 @@ export class BlockNoteEditor {
|
|
|
363
404
|
/**
|
|
364
405
|
* Gets a snapshot of the current selection.
|
|
365
406
|
*/
|
|
366
|
-
public getSelection(): Selection | undefined {
|
|
407
|
+
public getSelection(): Selection<BSchema> | undefined {
|
|
367
408
|
if (
|
|
368
409
|
this._tiptapEditor.state.selection.from ===
|
|
369
410
|
this._tiptapEditor.state.selection.to
|
|
@@ -371,7 +412,7 @@ export class BlockNoteEditor {
|
|
|
371
412
|
return undefined;
|
|
372
413
|
}
|
|
373
414
|
|
|
374
|
-
const blocks: Block[] = [];
|
|
415
|
+
const blocks: Block<BSchema>[] = [];
|
|
375
416
|
|
|
376
417
|
this._tiptapEditor.state.doc.descendants((node, pos) => {
|
|
377
418
|
if (node.type.spec.group !== "blockContent") {
|
|
@@ -388,6 +429,7 @@ export class BlockNoteEditor {
|
|
|
388
429
|
blocks.push(
|
|
389
430
|
nodeToBlock(
|
|
390
431
|
this._tiptapEditor.state.doc.resolve(pos).node(),
|
|
432
|
+
this.schema,
|
|
391
433
|
this.blockCache
|
|
392
434
|
)
|
|
393
435
|
);
|
|
@@ -423,7 +465,7 @@ export class BlockNoteEditor {
|
|
|
423
465
|
* `referenceBlock`. Inserts the blocks at the start of the existing block's children if "nested" is used.
|
|
424
466
|
*/
|
|
425
467
|
public insertBlocks(
|
|
426
|
-
blocksToInsert: PartialBlock[],
|
|
468
|
+
blocksToInsert: PartialBlock<BSchema>[],
|
|
427
469
|
referenceBlock: BlockIdentifier,
|
|
428
470
|
placement: "before" | "after" | "nested" = "before"
|
|
429
471
|
): void {
|
|
@@ -437,7 +479,10 @@ export class BlockNoteEditor {
|
|
|
437
479
|
* @param blockToUpdate The block that should be updated.
|
|
438
480
|
* @param update A partial block which defines how the existing block should be changed.
|
|
439
481
|
*/
|
|
440
|
-
public updateBlock(
|
|
482
|
+
public updateBlock(
|
|
483
|
+
blockToUpdate: BlockIdentifier,
|
|
484
|
+
update: PartialBlock<BSchema>
|
|
485
|
+
) {
|
|
441
486
|
updateBlock(blockToUpdate, update, this._tiptapEditor);
|
|
442
487
|
}
|
|
443
488
|
|
|
@@ -458,7 +503,7 @@ export class BlockNoteEditor {
|
|
|
458
503
|
*/
|
|
459
504
|
public replaceBlocks(
|
|
460
505
|
blocksToRemove: BlockIdentifier[],
|
|
461
|
-
blocksToInsert: PartialBlock[]
|
|
506
|
+
blocksToInsert: PartialBlock<BSchema>[]
|
|
462
507
|
) {
|
|
463
508
|
replaceBlocks(blocksToRemove, blocksToInsert, this._tiptapEditor);
|
|
464
509
|
}
|
|
@@ -504,6 +549,8 @@ export class BlockNoteEditor {
|
|
|
504
549
|
]);
|
|
505
550
|
const colorStyles = new Set<ColorStyle>(["textColor", "backgroundColor"]);
|
|
506
551
|
|
|
552
|
+
this._tiptapEditor.view.focus();
|
|
553
|
+
|
|
507
554
|
for (const [style, value] of Object.entries(styles)) {
|
|
508
555
|
if (toggleStyles.has(style as ToggledStyle)) {
|
|
509
556
|
this._tiptapEditor.commands.setMark(style);
|
|
@@ -518,6 +565,8 @@ export class BlockNoteEditor {
|
|
|
518
565
|
* @param styles The styles to remove.
|
|
519
566
|
*/
|
|
520
567
|
public removeStyles(styles: Styles) {
|
|
568
|
+
this._tiptapEditor.view.focus();
|
|
569
|
+
|
|
521
570
|
for (const style of Object.keys(styles)) {
|
|
522
571
|
this._tiptapEditor.commands.unsetMark(style);
|
|
523
572
|
}
|
|
@@ -537,6 +586,8 @@ export class BlockNoteEditor {
|
|
|
537
586
|
]);
|
|
538
587
|
const colorStyles = new Set<ColorStyle>(["textColor", "backgroundColor"]);
|
|
539
588
|
|
|
589
|
+
this._tiptapEditor.view.focus();
|
|
590
|
+
|
|
540
591
|
for (const [style, value] of Object.entries(styles)) {
|
|
541
592
|
if (toggleStyles.has(style as ToggledStyle)) {
|
|
542
593
|
this._tiptapEditor.commands.toggleMark(style);
|
|
@@ -632,7 +683,7 @@ export class BlockNoteEditor {
|
|
|
632
683
|
* @param blocks An array of blocks that should be serialized into HTML.
|
|
633
684
|
* @returns The blocks, serialized as an HTML string.
|
|
634
685
|
*/
|
|
635
|
-
public async blocksToHTML(blocks: Block[]): Promise<string> {
|
|
686
|
+
public async blocksToHTML(blocks: Block<BSchema>[]): Promise<string> {
|
|
636
687
|
return blocksToHTML(blocks, this._tiptapEditor.schema);
|
|
637
688
|
}
|
|
638
689
|
|
|
@@ -643,8 +694,8 @@ export class BlockNoteEditor {
|
|
|
643
694
|
* @param html The HTML string to parse blocks from.
|
|
644
695
|
* @returns The blocks parsed from the HTML string.
|
|
645
696
|
*/
|
|
646
|
-
public async HTMLToBlocks(html: string): Promise<Block[]> {
|
|
647
|
-
return HTMLToBlocks(html, this._tiptapEditor.schema);
|
|
697
|
+
public async HTMLToBlocks(html: string): Promise<Block<BSchema>[]> {
|
|
698
|
+
return HTMLToBlocks(html, this.schema, this._tiptapEditor.schema);
|
|
648
699
|
}
|
|
649
700
|
|
|
650
701
|
/**
|
|
@@ -653,7 +704,7 @@ export class BlockNoteEditor {
|
|
|
653
704
|
* @param blocks An array of blocks that should be serialized into Markdown.
|
|
654
705
|
* @returns The blocks, serialized as a Markdown string.
|
|
655
706
|
*/
|
|
656
|
-
public async blocksToMarkdown(blocks: Block[]): Promise<string> {
|
|
707
|
+
public async blocksToMarkdown(blocks: Block<BSchema>[]): Promise<string> {
|
|
657
708
|
return blocksToMarkdown(blocks, this._tiptapEditor.schema);
|
|
658
709
|
}
|
|
659
710
|
|
|
@@ -664,8 +715,8 @@ export class BlockNoteEditor {
|
|
|
664
715
|
* @param markdown The Markdown string to parse blocks from.
|
|
665
716
|
* @returns The blocks parsed from the Markdown string.
|
|
666
717
|
*/
|
|
667
|
-
public async markdownToBlocks(markdown: string): Promise<Block[]> {
|
|
668
|
-
return markdownToBlocks(markdown, this._tiptapEditor.schema);
|
|
718
|
+
public async markdownToBlocks(markdown: string): Promise<Block<BSchema>[]> {
|
|
719
|
+
return markdownToBlocks(markdown, this.schema, this._tiptapEditor.schema);
|
|
669
720
|
}
|
|
670
721
|
|
|
671
722
|
/**
|
|
@@ -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 () => {
|