@blocknote/core 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/blocknote.js +1623 -1318
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +5 -5
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +2 -2
  7. package/src/BlockNoteEditor.ts +44 -12
  8. package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +21 -21
  9. package/src/api/blockManipulation/blockManipulation.test.ts +8 -11
  10. package/src/api/formatConversions/__snapshots__/formatConversions.test.ts.snap +3 -3
  11. package/src/api/formatConversions/formatConversions.test.ts +5 -5
  12. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +3 -3
  13. package/src/api/nodeConversions/nodeConversions.test.ts +10 -4
  14. package/src/api/nodeConversions/nodeConversions.ts +9 -7
  15. package/src/api/nodeConversions/testUtil.ts +3 -3
  16. package/src/editor.module.css +1 -1
  17. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +5 -3
  18. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +2 -1
  19. package/src/extensions/Blocks/NonEditableBlockPlugin.ts +17 -0
  20. package/src/extensions/Blocks/api/block.ts +29 -16
  21. package/src/extensions/Blocks/api/blockTypes.ts +79 -27
  22. package/src/extensions/Blocks/api/defaultBlocks.ts +13 -41
  23. package/src/extensions/Blocks/api/defaultProps.ts +16 -0
  24. package/src/extensions/Blocks/nodes/Block.module.css +78 -24
  25. package/src/extensions/Blocks/nodes/BlockContainer.ts +17 -41
  26. package/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.ts +59 -13
  27. package/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/ImageBlockContent.ts +305 -0
  28. package/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts +13 -0
  29. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +24 -2
  30. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +146 -120
  31. package/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts +12 -2
  32. package/src/extensions/ImageToolbar/ImageToolbarPlugin.ts +239 -0
  33. package/src/extensions/SlashMenu/defaultSlashMenuItems.ts +47 -6
  34. package/src/extensions/TextColor/TextColorExtension.ts +4 -3
  35. package/src/extensions/TextColor/TextColorMark.ts +2 -1
  36. package/src/index.ts +4 -0
  37. package/types/src/BlockNoteEditor.d.ts +9 -0
  38. package/types/src/BlockNoteExtensions.d.ts +1 -1
  39. package/types/src/extensions/Blocks/api/block.d.ts +7 -8
  40. package/types/src/extensions/Blocks/api/blockTypes.d.ts +29 -20
  41. package/types/src/extensions/Blocks/api/defaultBlocks.d.ts +55 -51
  42. package/types/src/extensions/Blocks/api/defaultProps.d.ts +2 -2
  43. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +43 -9
  44. package/types/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/ImageBlockContent.d.ts +2 -2
  45. package/types/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +1 -0
  46. package/types/src/extensions/Blocks/nodes/BlockContent/ImageBlockContent/uploadToTmpFilesOrg_DEV_ONLY.d.ts +1 -0
  47. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +35 -9
  48. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +35 -9
  49. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +36 -1
  50. package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +1 -1
  51. package/types/src/index.d.ts +4 -0
  52. package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +1 -1
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.9.3",
6
+ "version": "0.9.4",
7
7
  "files": [
8
8
  "dist",
9
9
  "types",
@@ -109,5 +109,5 @@
109
109
  "access": "public",
110
110
  "registry": "https://registry.npmjs.org/"
111
111
  },
112
- "gitHead": "3a3ca4dfc448556f9796ce884b6ba61f036dfe1a"
112
+ "gitHead": "31e3c53bdcc082e3793bbb5517462cc18abd9e22"
113
113
  }
@@ -11,9 +11,9 @@ import {
11
11
  updateBlock,
12
12
  } from "./api/blockManipulation/blockManipulation";
13
13
  import {
14
+ HTMLToBlocks,
14
15
  blocksToHTML,
15
16
  blocksToMarkdown,
16
- HTMLToBlocks,
17
17
  markdownToBlocks,
18
18
  } from "./api/formatConversions/formatConversions";
19
19
  import {
@@ -44,6 +44,7 @@ import { getBlockInfoFromPos } from "./extensions/Blocks/helpers/getBlockInfoFro
44
44
 
45
45
  import { FormattingToolbarProsemirrorPlugin } from "./extensions/FormattingToolbar/FormattingToolbarPlugin";
46
46
  import { HyperlinkToolbarProsemirrorPlugin } from "./extensions/HyperlinkToolbar/HyperlinkToolbarPlugin";
47
+ import { ImageToolbarProsemirrorPlugin } from "./extensions/ImageToolbar/ImageToolbarPlugin";
47
48
  import { SideMenuProsemirrorPlugin } from "./extensions/SideMenu/SideMenuPlugin";
48
49
  import { BaseSlashMenuItem } from "./extensions/SlashMenu/BaseSlashMenuItem";
49
50
  import { SlashMenuProsemirrorPlugin } from "./extensions/SlashMenu/SlashMenuPlugin";
@@ -106,6 +107,13 @@ export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
106
107
  */
107
108
  blockSchema: BSchema;
108
109
 
110
+ /**
111
+ * A custom function to handle file uploads.
112
+ * @param file The file that should be uploaded.
113
+ * @returns The URL of the uploaded file.
114
+ */
115
+ uploadFile: (file: File) => Promise<string>;
116
+
109
117
  /**
110
118
  * When enabled, allows for collaboration between multiple users.
111
119
  */
@@ -151,6 +159,9 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
151
159
  public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin<BSchema>;
152
160
  public readonly slashMenu: SlashMenuProsemirrorPlugin<BSchema, any>;
153
161
  public readonly hyperlinkToolbar: HyperlinkToolbarProsemirrorPlugin<BSchema>;
162
+ public readonly imageToolbar: ImageToolbarProsemirrorPlugin<BSchema>;
163
+
164
+ public readonly uploadFile: ((file: File) => Promise<string>) | undefined;
154
165
 
155
166
  constructor(
156
167
  private readonly options: Partial<BlockNoteEditorOptions<BSchema>> = {}
@@ -178,6 +189,7 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
178
189
  getDefaultSlashMenuItems(newOptions.blockSchema)
179
190
  );
180
191
  this.hyperlinkToolbar = new HyperlinkToolbarProsemirrorPlugin(this);
192
+ this.imageToolbar = new ImageToolbarProsemirrorPlugin(this);
181
193
 
182
194
  const extensions = getBlockNoteExtensions<BSchema>({
183
195
  editor: this,
@@ -195,6 +207,7 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
195
207
  this.formattingToolbar.plugin,
196
208
  this.slashMenu.plugin,
197
209
  this.hyperlinkToolbar.plugin,
210
+ this.imageToolbar.plugin,
198
211
  ];
199
212
  },
200
213
  });
@@ -202,6 +215,8 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
202
215
 
203
216
  this.schema = newOptions.blockSchema;
204
217
 
218
+ this.uploadFile = newOptions.uploadFile;
219
+
205
220
  const initialContent =
206
221
  newOptions.initialContent ||
207
222
  (options.collaboration
@@ -216,28 +231,36 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
216
231
  const tiptapOptions: EditorOptions = {
217
232
  ...blockNoteTipTapOptions,
218
233
  ...newOptions._tiptapOptions,
219
- onCreate: () => {
220
- newOptions.onEditorReady?.(this);
221
- this.ready = true;
222
- },
223
234
  onBeforeCreate(editor) {
224
235
  if (!initialContent) {
225
236
  // when using collaboration
226
237
  return;
227
238
  }
228
- // we have to set the initial content here, because now we can use the editor schema
229
- // which has been created at this point
230
- const schema = editor.editor.schema;
231
- const ic = initialContent.map((block) => blockToNode(block, schema));
232
239
 
240
+ // We always set the initial content to a single paragraph block. This
241
+ // allows us to easily replace it with the actual initial content once
242
+ // the TipTap editor is initialized.
243
+ const schema = editor.editor.schema;
233
244
  const root = schema.node(
234
245
  "doc",
235
246
  undefined,
236
- schema.node("blockGroup", undefined, ic)
247
+ schema.node("blockGroup", undefined, [
248
+ blockToNode({ id: "initialBlock", type: "paragraph" }, schema),
249
+ ])
237
250
  );
238
- // override the initialcontent
239
251
  editor.editor.options.content = root.toJSON();
240
252
  },
253
+ onCreate: () => {
254
+ // We need to wait for the TipTap editor to init before we can set the
255
+ // initial content, as the schema may contain custom blocks which need
256
+ // it to render.
257
+ if (initialContent !== undefined) {
258
+ this.replaceBlocks(this.topLevelBlocks, initialContent);
259
+ }
260
+
261
+ newOptions.onEditorReady?.(this);
262
+ this.ready = true;
263
+ },
241
264
  onUpdate: () => {
242
265
  // This seems to be necessary due to a bug in TipTap:
243
266
  // https://github.com/ueberdosis/tiptap/issues/2583
@@ -460,6 +483,12 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
460
483
  posBeforeNode + 2
461
484
  )!;
462
485
 
486
+ // For blocks without inline content
487
+ if (contentNode.type.spec.content === "") {
488
+ this._tiptapEditor.commands.setNodeSelection(startPos);
489
+ return;
490
+ }
491
+
463
492
  if (placement === "start") {
464
493
  this._tiptapEditor.commands.setTextSelection(startPos + 1);
465
494
  } else {
@@ -473,9 +502,12 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
473
502
  * Gets a snapshot of the current selection.
474
503
  */
475
504
  public getSelection(): Selection<BSchema> | undefined {
505
+ // Either the TipTap selection is empty, or it's a node selection. In either
506
+ // case, it only spans one block, so we return undefined.
476
507
  if (
477
508
  this._tiptapEditor.state.selection.from ===
478
- this._tiptapEditor.state.selection.to
509
+ this._tiptapEditor.state.selection.to ||
510
+ "node" in this._tiptapEditor.state.selection
479
511
  ) {
480
512
  return undefined;
481
513
  }
@@ -1,4 +1,4 @@
1
- // Vitest Snapshot v1
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blocks 1`] = `
4
4
  [
@@ -16,7 +16,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
16
16
  "id": "2",
17
17
  "props": {
18
18
  "backgroundColor": "default",
19
- "level": "1",
19
+ "level": 1,
20
20
  "textAlignment": "left",
21
21
  "textColor": "default",
22
22
  },
@@ -33,7 +33,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
33
33
  "id": "1",
34
34
  "props": {
35
35
  "backgroundColor": "default",
36
- "level": "1",
36
+ "level": 1,
37
37
  "textAlignment": "left",
38
38
  "textColor": "default",
39
39
  },
@@ -53,7 +53,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
53
53
  "id": "4",
54
54
  "props": {
55
55
  "backgroundColor": "default",
56
- "level": "2",
56
+ "level": 2,
57
57
  "textAlignment": "left",
58
58
  "textColor": "default",
59
59
  },
@@ -70,7 +70,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
70
70
  "id": "3",
71
71
  "props": {
72
72
  "backgroundColor": "default",
73
- "level": "2",
73
+ "level": 2,
74
74
  "textAlignment": "left",
75
75
  "textColor": "default",
76
76
  },
@@ -106,7 +106,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
106
106
  "id": "2",
107
107
  "props": {
108
108
  "backgroundColor": "default",
109
- "level": "1",
109
+ "level": 1,
110
110
  "textAlignment": "left",
111
111
  "textColor": "default",
112
112
  },
@@ -142,7 +142,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
142
142
  "id": "4",
143
143
  "props": {
144
144
  "backgroundColor": "default",
145
- "level": "2",
145
+ "level": 2,
146
146
  "textAlignment": "left",
147
147
  "textColor": "default",
148
148
  },
@@ -159,7 +159,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete multiple blo
159
159
  "id": "3",
160
160
  "props": {
161
161
  "backgroundColor": "default",
162
- "level": "2",
162
+ "level": 2,
163
163
  "textAlignment": "left",
164
164
  "textColor": "default",
165
165
  },
@@ -286,7 +286,7 @@ exports[`Insert, Update, & Delete Blocks > Insert, update, & delete single block
286
286
  "id": "1",
287
287
  "props": {
288
288
  "backgroundColor": "default",
289
- "level": "3",
289
+ "level": 3,
290
290
  "textAlignment": "right",
291
291
  "textColor": "default",
292
292
  },
@@ -349,7 +349,7 @@ exports[`Inserting Blocks with Different Placements > Insert after existing bloc
349
349
  "id": "2",
350
350
  "props": {
351
351
  "backgroundColor": "default",
352
- "level": "1",
352
+ "level": 1,
353
353
  "textAlignment": "left",
354
354
  "textColor": "default",
355
355
  },
@@ -366,7 +366,7 @@ exports[`Inserting Blocks with Different Placements > Insert after existing bloc
366
366
  "id": "1",
367
367
  "props": {
368
368
  "backgroundColor": "default",
369
- "level": "1",
369
+ "level": 1,
370
370
  "textAlignment": "left",
371
371
  "textColor": "default",
372
372
  },
@@ -386,7 +386,7 @@ exports[`Inserting Blocks with Different Placements > Insert after existing bloc
386
386
  "id": "4",
387
387
  "props": {
388
388
  "backgroundColor": "default",
389
- "level": "2",
389
+ "level": 2,
390
390
  "textAlignment": "left",
391
391
  "textColor": "default",
392
392
  },
@@ -403,7 +403,7 @@ exports[`Inserting Blocks with Different Placements > Insert after existing bloc
403
403
  "id": "3",
404
404
  "props": {
405
405
  "backgroundColor": "default",
406
- "level": "2",
406
+ "level": 2,
407
407
  "textAlignment": "left",
408
408
  "textColor": "default",
409
409
  },
@@ -439,7 +439,7 @@ exports[`Inserting Blocks with Different Placements > Insert before existing blo
439
439
  "id": "2",
440
440
  "props": {
441
441
  "backgroundColor": "default",
442
- "level": "1",
442
+ "level": 1,
443
443
  "textAlignment": "left",
444
444
  "textColor": "default",
445
445
  },
@@ -456,7 +456,7 @@ exports[`Inserting Blocks with Different Placements > Insert before existing blo
456
456
  "id": "1",
457
457
  "props": {
458
458
  "backgroundColor": "default",
459
- "level": "1",
459
+ "level": 1,
460
460
  "textAlignment": "left",
461
461
  "textColor": "default",
462
462
  },
@@ -476,7 +476,7 @@ exports[`Inserting Blocks with Different Placements > Insert before existing blo
476
476
  "id": "4",
477
477
  "props": {
478
478
  "backgroundColor": "default",
479
- "level": "2",
479
+ "level": 2,
480
480
  "textAlignment": "left",
481
481
  "textColor": "default",
482
482
  },
@@ -493,7 +493,7 @@ exports[`Inserting Blocks with Different Placements > Insert before existing blo
493
493
  "id": "3",
494
494
  "props": {
495
495
  "backgroundColor": "default",
496
- "level": "2",
496
+ "level": 2,
497
497
  "textAlignment": "left",
498
498
  "textColor": "default",
499
499
  },
@@ -531,7 +531,7 @@ exports[`Inserting Blocks with Different Placements > Insert nested inside exist
531
531
  "id": "2",
532
532
  "props": {
533
533
  "backgroundColor": "default",
534
- "level": "1",
534
+ "level": 1,
535
535
  "textAlignment": "left",
536
536
  "textColor": "default",
537
537
  },
@@ -548,7 +548,7 @@ exports[`Inserting Blocks with Different Placements > Insert nested inside exist
548
548
  "id": "1",
549
549
  "props": {
550
550
  "backgroundColor": "default",
551
- "level": "1",
551
+ "level": 1,
552
552
  "textAlignment": "left",
553
553
  "textColor": "default",
554
554
  },
@@ -568,7 +568,7 @@ exports[`Inserting Blocks with Different Placements > Insert nested inside exist
568
568
  "id": "4",
569
569
  "props": {
570
570
  "backgroundColor": "default",
571
- "level": "2",
571
+ "level": 2,
572
572
  "textAlignment": "left",
573
573
  "textColor": "default",
574
574
  },
@@ -585,7 +585,7 @@ exports[`Inserting Blocks with Different Placements > Insert nested inside exist
585
585
  "id": "3",
586
586
  "props": {
587
587
  "backgroundColor": "default",
588
- "level": "2",
588
+ "level": 2,
589
589
  "textAlignment": "left",
590
590
  "textColor": "default",
591
591
  },
@@ -1,6 +1,5 @@
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";
4
3
 
5
4
  let editor: BlockNoteEditor;
6
5
 
@@ -15,13 +14,11 @@ function waitForEditor() {
15
14
  });
16
15
  }
17
16
 
18
- let singleBlock: PartialBlock<DefaultBlockSchema>;
17
+ let singleBlock: PartialBlock;
19
18
 
20
- let multipleBlocks: PartialBlock<DefaultBlockSchema>[];
19
+ let multipleBlocks: PartialBlock[];
21
20
 
22
- let insert: (
23
- placement: "before" | "nested" | "after"
24
- ) => Block<DefaultBlockSchema>[];
21
+ let insert: (placement: "before" | "nested" | "after") => Block[];
25
22
 
26
23
  beforeEach(() => {
27
24
  (window as Window & { __TEST_OPTIONS?: any }).__TEST_OPTIONS = {};
@@ -37,14 +34,14 @@ beforeEach(() => {
37
34
  {
38
35
  type: "heading",
39
36
  props: {
40
- level: "1",
37
+ level: 1,
41
38
  },
42
39
  content: "Heading 1",
43
40
  children: [
44
41
  {
45
42
  type: "heading",
46
43
  props: {
47
- level: "1",
44
+ level: 1,
48
45
  },
49
46
  content: "Nested Heading 1",
50
47
  },
@@ -53,14 +50,14 @@ beforeEach(() => {
53
50
  {
54
51
  type: "heading",
55
52
  props: {
56
- level: "2",
53
+ level: 2,
57
54
  },
58
55
  content: "Heading 2",
59
56
  children: [
60
57
  {
61
58
  type: "heading",
62
59
  props: {
63
- level: "2",
60
+ level: 2,
64
61
  },
65
62
  content: "Nested Heading 2",
66
63
  },
@@ -123,7 +120,7 @@ describe("Insert, Update, & Delete Blocks", () => {
123
120
  type: "heading",
124
121
  props: {
125
122
  textAlignment: "right",
126
- level: "3",
123
+ level: 3,
127
124
  },
128
125
  content: [
129
126
  {
@@ -1,4 +1,4 @@
1
- // Vitest Snapshot v1
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Complex Block/HTML/Markdown Conversions > Convert complex blocks to HTML 1`] = `"<h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3><p><span data-text-color=\\"purple\\"><span data-background-color=\\"green\\">Paragraph</span></span></p><p>P<strong>ara</strong><em>grap</em>h</p><p>P<u>ara</u><s>grap</s>h</p><ul><li><p>Bullet List Item</p></li><li><p>Bullet List Item</p><ul><li><p>Bullet List Item</p><ul><li><p>Bullet List Item</p></li></ul><p>Paragraph</p><ol><li><p>Numbered List Item</p></li><li><p>Numbered List Item</p></li><li><p>Numbered List Item</p><ol><li><p>Numbered List Item</p></li></ol></li></ol><ul><li><p>Bullet List Item</p></li></ul></li><li><p>Bullet List Item</p></li></ul></li><li><p>Bullet List Item</p></li></ul>"`;
4
4
 
@@ -68,7 +68,7 @@ exports[`Non-Nested Block/HTML/Markdown Conversions > Convert non-nested HTML to
68
68
  "id": "1",
69
69
  "props": {
70
70
  "backgroundColor": "default",
71
- "level": "1",
71
+ "level": 1,
72
72
  "textAlignment": "left",
73
73
  "textColor": "default",
74
74
  },
@@ -142,7 +142,7 @@ exports[`Non-Nested Block/HTML/Markdown Conversions > Convert non-nested Markdow
142
142
  "id": "1",
143
143
  "props": {
144
144
  "backgroundColor": "default",
145
- "level": "1",
145
+ "level": 1,
146
146
  "textAlignment": "left",
147
147
  "textColor": "default",
148
148
  },
@@ -12,7 +12,7 @@ const getNonNestedBlocks = (): Block[] => [
12
12
  backgroundColor: "default",
13
13
  textColor: "default",
14
14
  textAlignment: "left",
15
- level: "1",
15
+ level: 1,
16
16
  },
17
17
  content: [
18
18
  {
@@ -84,7 +84,7 @@ const getNestedBlocks = (): Block[] => [
84
84
  backgroundColor: "default",
85
85
  textColor: "default",
86
86
  textAlignment: "left",
87
- level: "1",
87
+ level: 1,
88
88
  },
89
89
  content: [
90
90
  {
@@ -224,7 +224,7 @@ const getComplexBlocks = (): Block[] => [
224
224
  backgroundColor: "red",
225
225
  textColor: "yellow",
226
226
  textAlignment: "right",
227
- level: "1",
227
+ level: 1,
228
228
  },
229
229
  content: [
230
230
  {
@@ -241,7 +241,7 @@ const getComplexBlocks = (): Block[] => [
241
241
  backgroundColor: "orange",
242
242
  textColor: "orange",
243
243
  textAlignment: "center",
244
- level: "2",
244
+ level: 2,
245
245
  },
246
246
  content: [
247
247
  {
@@ -258,7 +258,7 @@ const getComplexBlocks = (): Block[] => [
258
258
  backgroundColor: "yellow",
259
259
  textColor: "red",
260
260
  textAlignment: "left",
261
- level: "3",
261
+ level: 3,
262
262
  },
263
263
  content: [
264
264
  {
@@ -1,4 +1,4 @@
1
- // Vitest Snapshot v1
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Complex ProseMirror Node Conversions > Convert complex block to node 1`] = `
4
4
  {
@@ -10,7 +10,7 @@ exports[`Complex ProseMirror Node Conversions > Convert complex block to node 1`
10
10
  "content": [
11
11
  {
12
12
  "attrs": {
13
- "level": "2",
13
+ "level": 2,
14
14
  "textAlignment": "right",
15
15
  },
16
16
  "content": [
@@ -142,7 +142,7 @@ exports[`Complex ProseMirror Node Conversions > Convert complex node to block 1`
142
142
  "id": "1",
143
143
  "props": {
144
144
  "backgroundColor": "blue",
145
- "level": "2",
145
+ "level": 2,
146
146
  "textAlignment": "right",
147
147
  "textColor": "yellow",
148
148
  },
@@ -49,7 +49,10 @@ describe("Simple ProseMirror Node Conversions", () => {
49
49
 
50
50
  expect(firstBlockConversion).toMatchSnapshot();
51
51
 
52
- const firstNodeConversion = blockToNode(firstBlockConversion, tt.schema);
52
+ const firstNodeConversion = blockToNode<DefaultBlockSchema>(
53
+ firstBlockConversion,
54
+ tt.schema
55
+ );
53
56
 
54
57
  expect(firstNodeConversion).toStrictEqual(node);
55
58
  });
@@ -63,7 +66,7 @@ describe("Complex ProseMirror Node Conversions", () => {
63
66
  backgroundColor: "blue",
64
67
  textColor: "yellow",
65
68
  textAlignment: "right",
66
- level: "2",
69
+ level: 2,
67
70
  },
68
71
  content: [
69
72
  {
@@ -111,7 +114,7 @@ describe("Complex ProseMirror Node Conversions", () => {
111
114
  },
112
115
  [
113
116
  tt.schema.nodes["heading"].create(
114
- { textAlignment: "right", level: "2" },
117
+ { textAlignment: "right", level: 2 },
115
118
  [
116
119
  tt.schema.text("Heading ", [
117
120
  tt.schema.mark("bold"),
@@ -147,7 +150,10 @@ describe("Complex ProseMirror Node Conversions", () => {
147
150
 
148
151
  expect(firstBlockConversion).toMatchSnapshot();
149
152
 
150
- const firstNodeConversion = blockToNode(firstBlockConversion, tt.schema);
153
+ const firstNodeConversion = blockToNode<DefaultBlockSchema>(
154
+ firstBlockConversion,
155
+ tt.schema
156
+ );
151
157
 
152
158
  expect(firstNodeConversion).toStrictEqual(node);
153
159
  });
@@ -5,8 +5,7 @@ import {
5
5
  BlockSchema,
6
6
  PartialBlock,
7
7
  } from "../../extensions/Blocks/api/blockTypes";
8
-
9
- import { defaultProps } from "../../extensions/Blocks/api/defaultBlocks";
8
+ import { defaultProps } from "../../extensions/Blocks/api/defaultProps";
10
9
  import {
11
10
  ColorStyle,
12
11
  InlineContent,
@@ -16,9 +15,9 @@ import {
16
15
  Styles,
17
16
  ToggledStyle,
18
17
  } from "../../extensions/Blocks/api/inlineContentTypes";
18
+ import { getBlockInfo } from "../../extensions/Blocks/helpers/getBlockInfoFromPos";
19
19
  import UniqueID from "../../extensions/UniqueID/UniqueID";
20
20
  import { UnreachableCaseError } from "../../shared/utils";
21
- import { getBlockInfo } from "../../extensions/Blocks/helpers/getBlockInfoFromPos";
22
21
 
23
22
  const toggleStyles = new Set<ToggledStyle>([
24
23
  "bold",
@@ -409,7 +408,7 @@ export function nodeToBlock<BSchema extends BlockSchema>(
409
408
  }
410
409
  }
411
410
 
412
- const content = contentNodeToInlineContent(blockInfo.contentNode);
411
+ const blockSpec = blockSchema[blockInfo.contentType.name];
413
412
 
414
413
  const children: Block<BSchema>[] = [];
415
414
  for (let i = 0; i < blockInfo.numChildBlocks; i++) {
@@ -420,11 +419,14 @@ export function nodeToBlock<BSchema extends BlockSchema>(
420
419
 
421
420
  const block: Block<BSchema> = {
422
421
  id,
423
- type: blockInfo.contentType.name,
422
+ type: blockSpec.node.name,
424
423
  props,
425
- content,
424
+ content:
425
+ blockSpec.node.config.content === "inline*"
426
+ ? contentNodeToInlineContent(blockInfo.contentNode)
427
+ : undefined,
426
428
  children,
427
- };
429
+ } as Block<BSchema>;
428
430
 
429
431
  blockCache?.set(node, block);
430
432
 
@@ -48,14 +48,14 @@ export function partialBlockToBlockForTesting<BSchema extends BlockSchema>(
48
48
  ): Block<BSchema> {
49
49
  const withDefaults = {
50
50
  id: "",
51
- type: "paragraph" as any,
51
+ type: "paragraph",
52
52
  // because at this point we don't have an easy way to access default props at runtime,
53
53
  // partialBlockToBlockForTesting will not set them.
54
54
  props: {} as any,
55
- content: [],
55
+ content: [] as any,
56
56
  children: [],
57
57
  ...partialBlock,
58
- };
58
+ } satisfies PartialBlock<BSchema>;
59
59
 
60
60
  return {
61
61
  ...withDefaults,
@@ -36,7 +36,7 @@ Tippy popups that are appended to document.body directly
36
36
  .defaultStyles h2,
37
37
  .defaultStyles h3,
38
38
  .defaultStyles li {
39
- all: unset !important;
39
+ all: unset;
40
40
  margin: 0;
41
41
  padding: 0;
42
42
  font-size: inherit;
@@ -1,5 +1,6 @@
1
1
  import { Extension } from "@tiptap/core";
2
2
  import { getBlockInfoFromPos } from "../Blocks/helpers/getBlockInfoFromPos";
3
+ import { defaultProps } from "../Blocks/api/defaultProps";
3
4
 
4
5
  declare module "@tiptap/core" {
5
6
  interface Commands<ReturnType> {
@@ -21,13 +22,14 @@ export const BackgroundColorExtension = Extension.create({
21
22
  types: ["blockContainer"],
22
23
  attributes: {
23
24
  backgroundColor: {
24
- default: "default",
25
+ default: defaultProps.backgroundColor.default,
25
26
  parseHTML: (element) =>
26
27
  element.hasAttribute("data-background-color")
27
28
  ? element.getAttribute("data-background-color")
28
- : "default",
29
+ : defaultProps.backgroundColor.default,
29
30
  renderHTML: (attributes) =>
30
- attributes.backgroundColor !== "default" && {
31
+ attributes.backgroundColor !==
32
+ defaultProps.backgroundColor.default && {
31
33
  "data-background-color": attributes.backgroundColor,
32
34
  },
33
35
  },
@@ -1,4 +1,5 @@
1
1
  import { Mark } from "@tiptap/core";
2
+ import { defaultProps } from "../Blocks/api/defaultProps";
2
3
 
3
4
  declare module "@tiptap/core" {
4
5
  interface Commands<ReturnType> {
@@ -51,7 +52,7 @@ export const BackgroundColorMark = Mark.create({
51
52
  setBackgroundColor:
52
53
  (color) =>
53
54
  ({ commands }) => {
54
- if (color !== "default") {
55
+ if (color !== defaultProps.backgroundColor.default) {
55
56
  return commands.setMark(this.name, { color: color });
56
57
  }
57
58
 
@@ -0,0 +1,17 @@
1
+ import { Plugin, PluginKey } from "prosemirror-state";
2
+
3
+ const PLUGIN_KEY = new PluginKey("non-editable-block");
4
+ // Prevent typing for blocks without inline content, as this would otherwise
5
+ // convert them into paragraph blocks.
6
+ export const NonEditableBlockPlugin = () => {
7
+ return new Plugin({
8
+ key: PLUGIN_KEY,
9
+ props: {
10
+ handleKeyDown: (view, event) => {
11
+ if ("node" in view.state.selection) {
12
+ event.preventDefault();
13
+ }
14
+ },
15
+ },
16
+ });
17
+ };