@blocknote/core 0.13.4 → 0.14.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.
Files changed (51) hide show
  1. package/dist/blocknote.js +526 -477
  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/dist/webpack-stats.json +1 -1
  7. package/package.json +29 -29
  8. package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +98 -0
  9. package/src/api/blockManipulation/blockManipulation.test.ts +86 -7
  10. package/src/api/exporters/html/__snapshots__/customBlock/basic/external.html +1 -0
  11. package/src/api/exporters/html/__snapshots__/customBlock/basic/internal.html +1 -0
  12. package/src/api/exporters/html/__snapshots__/customParagraph/lineBreaks/external.html +1 -0
  13. package/src/api/exporters/html/__snapshots__/customParagraph/lineBreaks/internal.html +1 -0
  14. package/src/api/exporters/html/__snapshots__/paragraph/lineBreaks/external.html +1 -0
  15. package/src/api/exporters/html/__snapshots__/paragraph/lineBreaks/internal.html +1 -0
  16. package/src/api/exporters/markdown/__snapshots__/customBlock/basic/markdown.md +5 -0
  17. package/src/api/exporters/markdown/__snapshots__/customParagraph/lineBreaks/markdown.md +1 -0
  18. package/src/api/exporters/markdown/__snapshots__/paragraph/lineBreaks/markdown.md +2 -0
  19. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +566 -0
  20. package/src/api/nodeConversions/nodeConversions.test.ts +2 -0
  21. package/src/api/nodeConversions/nodeConversions.ts +2 -4
  22. package/src/api/parsers/html/__snapshots__/paste/parse-image-in-paragraph.json +16 -0
  23. package/src/api/parsers/html/parseHTML.test.ts +8 -0
  24. package/src/api/parsers/pasteExtension.ts +5 -0
  25. package/src/api/testUtil/cases/customBlocks.ts +9 -0
  26. package/src/api/testUtil/cases/defaultSchema.ts +9 -0
  27. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +6 -6
  28. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +7 -2
  29. package/src/blocks/TableBlockContent/TableBlockContent.ts +23 -1
  30. package/src/editor/Block.css +2 -3
  31. package/src/editor/BlockNoteEditor.ts +7 -6
  32. package/src/editor/BlockNoteExtensions.ts +10 -1
  33. package/src/editor/BlockNoteTipTapEditor.ts +1 -0
  34. package/src/extensions/FilePanel/FilePanelPlugin.ts +16 -12
  35. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +12 -15
  36. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +6 -2
  37. package/src/extensions/Placeholder/PlaceholderPlugin.ts +5 -1
  38. package/src/extensions/SideMenu/SideMenuPlugin.ts +157 -118
  39. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +5 -2
  40. package/src/extensions/TableHandles/TableHandlesPlugin.ts +7 -4
  41. package/src/i18n/locales/pt.ts +1 -1
  42. package/src/i18n/locales/zh.ts +1 -1
  43. package/src/pm-nodes/BlockContainer.ts +11 -7
  44. package/src/schema/blocks/createSpec.ts +2 -2
  45. package/src/schema/inlineContent/createSpec.ts +2 -2
  46. package/types/src/editor/BlockNoteEditor.d.ts +1 -1
  47. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +5 -5
  48. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +0 -1
  49. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +9 -8
  50. package/types/src/schema/blocks/createSpec.d.ts +2 -2
  51. package/types/src/schema/inlineContent/createSpec.d.ts +2 -2
@@ -26,46 +26,26 @@ export type SideMenuState<
26
26
  block: Block<BSchema, I, S>;
27
27
  };
28
28
 
29
- export function getDraggableBlockFromCoords(
30
- coords: { left: number; top: number },
29
+ export function getDraggableBlockFromElement(
30
+ element: Element,
31
31
  view: EditorView
32
32
  ) {
33
- if (!view.dom.isConnected) {
34
- // view is not connected to the DOM, this can cause posAtCoords to fail
35
- // (Cannot read properties of null (reading 'nearestDesc'), https://github.com/TypeCellOS/BlockNote/issues/123)
36
- return undefined;
37
- }
38
-
39
- const pos = view.posAtCoords(coords);
40
- if (!pos) {
41
- return undefined;
42
- }
43
- let node = view.domAtPos(pos.pos).node as HTMLElement;
44
-
45
- if (node === view.dom) {
46
- // mouse over root
47
- return undefined;
48
- }
49
-
50
33
  while (
51
- node &&
52
- node.parentNode &&
53
- node.parentNode !== view.dom &&
54
- !node.hasAttribute?.("data-id")
34
+ element &&
35
+ element.parentElement &&
36
+ element.parentElement !== view.dom &&
37
+ !element.hasAttribute?.("data-id")
55
38
  ) {
56
- node = node.parentNode as HTMLElement;
39
+ element = element.parentElement;
57
40
  }
58
- if (!node) {
41
+ if (!element.hasAttribute("data-id")) {
59
42
  return undefined;
60
43
  }
61
- return { node, id: node.getAttribute("data-id")! };
44
+ return { node: element as HTMLElement, id: element.getAttribute("data-id")! };
62
45
  }
63
46
 
64
- function blockPositionFromCoords(
65
- coords: { left: number; top: number },
66
- view: EditorView
67
- ) {
68
- const block = getDraggableBlockFromCoords(coords, view);
47
+ function blockPositionFromElement(element: Element, view: EditorView) {
48
+ const block = getDraggableBlockFromElement(element, view);
69
49
 
70
50
  if (block && block.node.nodeType === 1) {
71
51
  // TODO: this uses undocumented PM APIs? do we need this / let's add docs?
@@ -197,7 +177,21 @@ function dragStart<
197
177
  top: e.clientY,
198
178
  };
199
179
 
200
- const pos = blockPositionFromCoords(coords, view);
180
+ const elements = document.elementsFromPoint(coords.left, coords.top);
181
+ let blockEl = undefined;
182
+
183
+ for (const element of elements) {
184
+ if (view.dom.contains(element)) {
185
+ blockEl = getDraggableBlockFromElement(element, view);
186
+ break;
187
+ }
188
+ }
189
+
190
+ if (!blockEl) {
191
+ return;
192
+ }
193
+
194
+ const pos = blockPositionFromElement(blockEl.node, view);
201
195
  if (pos != null) {
202
196
  const selection = view.state.selection;
203
197
  const doc = view.state.doc;
@@ -252,8 +246,11 @@ export class SideMenuView<
252
246
  S extends StyleSchema
253
247
  > implements PluginView
254
248
  {
255
- private state?: SideMenuState<BSchema, I, S>;
256
- private readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;
249
+ public state?: SideMenuState<BSchema, I, S>;
250
+ public readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;
251
+
252
+ private needUpdate = false;
253
+ private mousePos: { x: number; y: number } | undefined;
257
254
 
258
255
  // When true, the drag handle with be anchored at the same level as root elements
259
256
  // When false, the drag handle with be just to the left of the element
@@ -293,15 +290,98 @@ export class SideMenuView<
293
290
  // Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
294
291
  document.body.addEventListener("mousemove", this.onMouseMove, true);
295
292
 
296
- // Makes menu scroll with the page.
297
- document.addEventListener("scroll", this.onScroll);
298
-
299
- // Unfreezes the menu whenever the user clicks anywhere.
300
- document.body.addEventListener("mousedown", this.onMouseDown, true);
293
+ // Unfreezes the menu whenever the user clicks.
294
+ this.pmView.dom.addEventListener("mousedown", this.onMouseDown);
301
295
  // Hides and unfreezes the menu whenever the user presses a key.
302
296
  document.body.addEventListener("keydown", this.onKeyDown, true);
297
+
298
+ // Setting capture=true ensures that any parent container of the editor that
299
+ // gets scrolled will trigger the scroll event. Scroll events do not bubble
300
+ // and so won't propagate to the document by default.
301
+ document.addEventListener("scroll", this.onScroll, true);
303
302
  }
304
303
 
304
+ updateState = () => {
305
+ if (this.menuFrozen || !this.mousePos) {
306
+ return;
307
+ }
308
+
309
+ // Editor itself may have padding or other styling which affects
310
+ // size/position, so we get the boundingRect of the first child (i.e. the
311
+ // blockGroup that wraps all blocks in the editor) for more accurate side
312
+ // menu placement.
313
+ const editorBoundingBox = (
314
+ this.pmView.dom.firstChild! as HTMLElement
315
+ ).getBoundingClientRect();
316
+
317
+ this.horizontalPosAnchor = editorBoundingBox.x;
318
+
319
+ // Gets block at mouse cursor's vertical position.
320
+ const coords = {
321
+ left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
322
+ top: this.mousePos.y,
323
+ };
324
+
325
+ const elements = document.elementsFromPoint(coords.left, coords.top);
326
+ let block = undefined;
327
+
328
+ for (const element of elements) {
329
+ if (this.pmView.dom.contains(element)) {
330
+ block = getDraggableBlockFromElement(element, this.pmView);
331
+ break;
332
+ }
333
+ }
334
+
335
+ // Closes the menu if the mouse cursor is beyond the editor vertically.
336
+ if (!block || !this.editor.isEditable) {
337
+ if (this.state?.show) {
338
+ this.state.show = false;
339
+ this.needUpdate = true;
340
+ }
341
+
342
+ return;
343
+ }
344
+
345
+ // Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
346
+ if (
347
+ this.state?.show &&
348
+ this.hoveredBlock?.hasAttribute("data-id") &&
349
+ this.hoveredBlock?.getAttribute("data-id") === block.id
350
+ ) {
351
+ return;
352
+ }
353
+
354
+ this.hoveredBlock = block.node;
355
+
356
+ // Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
357
+ const blockContent = block.node.firstChild as HTMLElement;
358
+
359
+ if (!blockContent) {
360
+ return;
361
+ }
362
+
363
+ // Shows or updates elements.
364
+ if (this.editor.isEditable) {
365
+ const blockContentBoundingBox = blockContent.getBoundingClientRect();
366
+
367
+ this.state = {
368
+ show: true,
369
+ referencePos: new DOMRect(
370
+ this.horizontalPosAnchoredAtRoot
371
+ ? this.horizontalPosAnchor
372
+ : blockContentBoundingBox.x,
373
+ blockContentBoundingBox.y,
374
+ blockContentBoundingBox.width,
375
+ blockContentBoundingBox.height
376
+ ),
377
+ block: this.editor.getBlock(
378
+ this.hoveredBlock!.getAttribute("data-id")!
379
+ )!,
380
+ };
381
+ this.needUpdate = true;
382
+ }
383
+ };
384
+
305
385
  /**
306
386
  * Sets isDragging when dragging text.
307
387
  */
@@ -381,34 +461,25 @@ export class SideMenuView<
381
461
  }
382
462
  };
383
463
 
384
- onMouseDown = (_event: MouseEvent) => {
385
- if (this.state && !this.state.show) {
386
- this.state.show = true;
464
+ onMouseDown = () => {
465
+ if (this.state && this.state.show && this.menuFrozen) {
466
+ this.menuFrozen = false;
467
+ this.state.show = false;
387
468
  this.emitUpdate(this.state);
388
469
  }
389
- this.menuFrozen = false;
390
470
  };
391
471
 
392
472
  onMouseMove = (event: MouseEvent) => {
393
- if (this.menuFrozen) {
394
- return;
395
- }
473
+ this.mousePos = { x: event.clientX, y: event.clientY };
396
474
 
397
- // Editor itself may have padding or other styling which affects
398
- // size/position, so we get the boundingRect of the first child (i.e. the
399
- // blockGroup that wraps all blocks in the editor) for more accurate side
400
- // menu placement.
401
- const editorBoundingBox = (
402
- this.pmView.dom.firstChild! as HTMLElement
403
- ).getBoundingClientRect();
404
475
  // We want the full area of the editor to check if the cursor is hovering
405
476
  // above it though.
406
477
  const editorOuterBoundingBox = this.pmView.dom.getBoundingClientRect();
407
478
  const cursorWithinEditor =
408
- event.clientX >= editorOuterBoundingBox.left &&
409
- event.clientX <= editorOuterBoundingBox.right &&
410
- event.clientY >= editorOuterBoundingBox.top &&
411
- event.clientY <= editorOuterBoundingBox.bottom;
479
+ this.mousePos.x > editorOuterBoundingBox.left &&
480
+ this.mousePos.x < editorOuterBoundingBox.right &&
481
+ this.mousePos.y > editorOuterBoundingBox.top &&
482
+ this.mousePos.y < editorOuterBoundingBox.bottom;
412
483
 
413
484
  const editorWrapper = this.pmView.dom.parentElement!;
414
485
 
@@ -434,63 +505,11 @@ export class SideMenuView<
434
505
  return;
435
506
  }
436
507
 
437
- this.horizontalPosAnchor = editorBoundingBox.x;
438
-
439
- // Gets block at mouse cursor's vertical position.
440
- const coords = {
441
- left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
442
- top: event.clientY,
443
- };
444
- const block = getDraggableBlockFromCoords(coords, this.pmView);
445
-
446
- // Closes the menu if the mouse cursor is beyond the editor vertically.
447
- if (!block || !this.editor.isEditable) {
448
- if (this.state?.show) {
449
- this.state.show = false;
450
- this.emitUpdate(this.state);
451
- }
452
-
453
- return;
454
- }
455
-
456
- // Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
457
- if (
458
- this.state?.show &&
459
- this.hoveredBlock?.hasAttribute("data-id") &&
460
- this.hoveredBlock?.getAttribute("data-id") === block.id
461
- ) {
462
- return;
463
- }
464
-
465
- this.hoveredBlock = block.node;
466
-
467
- // Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
468
- const blockContent = block.node.firstChild as HTMLElement;
469
-
470
- if (!blockContent) {
471
- return;
472
- }
473
-
474
- // Shows or updates elements.
475
- if (this.editor.isEditable) {
476
- const blockContentBoundingBox = blockContent.getBoundingClientRect();
508
+ this.updateState();
477
509
 
478
- this.state = {
479
- show: true,
480
- referencePos: new DOMRect(
481
- this.horizontalPosAnchoredAtRoot
482
- ? this.horizontalPosAnchor
483
- : blockContentBoundingBox.x,
484
- blockContentBoundingBox.y,
485
- blockContentBoundingBox.width,
486
- blockContentBoundingBox.height
487
- ),
488
- block: this.editor.getBlock(
489
- this.hoveredBlock!.getAttribute("data-id")!
490
- )!,
491
- };
492
-
493
- this.emitUpdate(this.state);
510
+ if (this.needUpdate) {
511
+ this.emitUpdate(this.state!);
512
+ this.needUpdate = false;
494
513
  }
495
514
  };
496
515
 
@@ -511,6 +530,24 @@ export class SideMenuView<
511
530
  }
512
531
  };
513
532
 
533
+ // Needed in cases where the editor state updates without the mouse cursor
534
+ // moving, as some state updates can require a side menu update. For example,
535
+ // adding a button to the side menu which removes the block can cause the
536
+ // block below to jump up into the place of the removed block when clicked,
537
+ // allowing the user to click the button again without moving the cursor. This
538
+ // would otherwise not update the side menu, and so clicking the button again
539
+ // would attempt to remove the same block again, causing an error.
540
+ update() {
541
+ const prevBlockId = this.state?.block.id;
542
+
543
+ this.updateState();
544
+
545
+ if (this.needUpdate && this.state && prevBlockId !== this.state.block.id) {
546
+ this.emitUpdate(this.state);
547
+ this.needUpdate = false;
548
+ }
549
+ }
550
+
514
551
  destroy() {
515
552
  if (this.state?.show) {
516
553
  this.state.show = false;
@@ -520,8 +557,8 @@ export class SideMenuView<
520
557
  document.body.removeEventListener("dragover", this.onDragOver);
521
558
  this.pmView.dom.removeEventListener("dragstart", this.onDragStart);
522
559
  document.body.removeEventListener("drop", this.onDrop, true);
523
- document.removeEventListener("scroll", this.onScroll);
524
- document.body.removeEventListener("mousedown", this.onMouseDown, true);
560
+ document.removeEventListener("scroll", this.onScroll, true);
561
+ this.pmView.dom.removeEventListener("mousedown", this.onMouseDown);
525
562
  document.body.removeEventListener("keydown", this.onKeyDown, true);
526
563
  }
527
564
 
@@ -531,8 +568,6 @@ export class SideMenuView<
531
568
  this.emitUpdate(this.state);
532
569
  }
533
570
 
534
- this.menuFrozen = true;
535
-
536
571
  const blockContent = this.hoveredBlock!.firstChild! as HTMLElement;
537
572
  const blockContentBoundingBox = blockContent.getBoundingClientRect();
538
573
 
@@ -642,5 +677,9 @@ export class SideMenuProsemirrorPlugin<
642
677
  * attached to the same block regardless of which block is hovered by the
643
678
  * mouse cursor.
644
679
  */
645
- unfreezeMenu = () => (this.view!.menuFrozen = false);
680
+ unfreezeMenu = () => {
681
+ this.view!.menuFrozen = false;
682
+ this.view!.state!.show = false;
683
+ this.view!.emitUpdate(this.view!.state!);
684
+ };
646
685
  }
@@ -37,7 +37,10 @@ class SuggestionMenuView<
37
37
  emitUpdate(menuName, this.state);
38
38
  };
39
39
 
40
- document.addEventListener("scroll", this.handleScroll);
40
+ // Setting capture=true ensures that any parent container of the editor that
41
+ // gets scrolled will trigger the scroll event. Scroll events do not bubble
42
+ // and so won't propagate to the document by default.
43
+ document.addEventListener("scroll", this.handleScroll, true);
41
44
  }
42
45
 
43
46
  handleScroll = () => {
@@ -92,7 +95,7 @@ class SuggestionMenuView<
92
95
  }
93
96
 
94
97
  destroy() {
95
- document.removeEventListener("scroll", this.handleScroll);
98
+ document.removeEventListener("scroll", this.handleScroll, true);
96
99
  }
97
100
 
98
101
  closeMenu = () => {
@@ -11,7 +11,7 @@ import {
11
11
  } from "../../schema";
12
12
  import { checkBlockIsDefaultType } from "../../blocks/defaultBlockTypeGuards";
13
13
  import { EventEmitter } from "../../util/EventEmitter";
14
- import { getDraggableBlockFromCoords } from "../SideMenu/SideMenuPlugin";
14
+ import { getDraggableBlockFromElement } from "../SideMenu/SideMenuPlugin";
15
15
 
16
16
  let dragImageElement: HTMLElement | undefined;
17
17
 
@@ -119,7 +119,10 @@ export class TableHandlesView<
119
119
  document.addEventListener("dragover", this.dragOverHandler);
120
120
  document.addEventListener("drop", this.dropHandler);
121
121
 
122
- document.addEventListener("scroll", this.scrollHandler);
122
+ // Setting capture=true ensures that any parent container of the editor that
123
+ // gets scrolled will trigger the scroll event. Scroll events do not bubble
124
+ // and so won't propagate to the document by default.
125
+ document.addEventListener("scroll", this.scrollHandler, true);
123
126
  }
124
127
 
125
128
  mouseMoveHandler = (event: MouseEvent) => {
@@ -143,7 +146,7 @@ export class TableHandlesView<
143
146
  const tableRect =
144
147
  target.parentElement!.parentElement!.getBoundingClientRect();
145
148
 
146
- const blockEl = getDraggableBlockFromCoords(cellRect, this.pmView);
149
+ const blockEl = getDraggableBlockFromElement(target, this.pmView);
147
150
  if (!blockEl) {
148
151
  return;
149
152
  }
@@ -361,7 +364,7 @@ export class TableHandlesView<
361
364
  document.removeEventListener("dragover", this.dragOverHandler);
362
365
  document.removeEventListener("drop", this.dropHandler);
363
366
 
364
- document.removeEventListener("scroll", this.scrollHandler);
367
+ document.removeEventListener("scroll", this.scrollHandler, true);
365
368
  }
366
369
  }
367
370
 
@@ -43,7 +43,7 @@ export const pt: Dictionary = {
43
43
  "lista marcada",
44
44
  "caixa de seleção",
45
45
  ],
46
- group: "Blocos básicos",
46
+ group: "Blocos Básicos",
47
47
  },
48
48
  paragraph: {
49
49
  title: "Parágrafo",
@@ -61,7 +61,7 @@ export const zh: Dictionary = {
61
61
  "勾选列表",
62
62
  "复选框",
63
63
  ],
64
- group: "基本块",
64
+ group: "基础",
65
65
  },
66
66
  paragraph: {
67
67
  title: "段落",
@@ -220,7 +220,11 @@ export const BlockContainer = Node.create<{
220
220
  if (block.content) {
221
221
  if (typeof block.content === "string") {
222
222
  // Adds a single text node with no marks to the content.
223
- content = [state.schema.text(block.content)];
223
+ content = inlineContentToNodes(
224
+ [block.content],
225
+ state.schema,
226
+ this.options.editor.schema.styleSchema
227
+ );
224
228
  } else if (Array.isArray(block.content)) {
225
229
  // Adds a text node with the provided styles converted into marks to the content,
226
230
  // for each InlineContent object.
@@ -611,7 +615,7 @@ export const BlockContainer = Node.create<{
611
615
  // of the block.
612
616
  () =>
613
617
  commands.command(({ state }) => {
614
- const { node, depth } = getBlockInfoFromPos(
618
+ const { contentNode, depth } = getBlockInfoFromPos(
615
619
  state.doc,
616
620
  state.selection.from
617
621
  )!;
@@ -620,7 +624,7 @@ export const BlockContainer = Node.create<{
620
624
  state.selection.$anchor.parentOffset === 0;
621
625
  const selectionEmpty =
622
626
  state.selection.anchor === state.selection.head;
623
- const blockEmpty = node.textContent.length === 0;
627
+ const blockEmpty = contentNode.childCount === 0;
624
628
  const blockIndented = depth > 2;
625
629
 
626
630
  if (
@@ -638,7 +642,7 @@ export const BlockContainer = Node.create<{
638
642
  // empty & at the start of the block.
639
643
  () =>
640
644
  commands.command(({ state, chain }) => {
641
- const { node, endPos } = getBlockInfoFromPos(
645
+ const { contentNode, endPos } = getBlockInfoFromPos(
642
646
  state.doc,
643
647
  state.selection.from
644
648
  )!;
@@ -647,7 +651,7 @@ export const BlockContainer = Node.create<{
647
651
  state.selection.$anchor.parentOffset === 0;
648
652
  const selectionEmpty =
649
653
  state.selection.anchor === state.selection.head;
650
- const blockEmpty = node.textContent.length === 0;
654
+ const blockEmpty = contentNode.childCount === 0;
651
655
 
652
656
  if (selectionAtBlockStart && selectionEmpty && blockEmpty) {
653
657
  const newBlockInsertionPos = endPos + 1;
@@ -667,14 +671,14 @@ export const BlockContainer = Node.create<{
667
671
  // deletes the selection beforehand, if it's not empty.
668
672
  () =>
669
673
  commands.command(({ state, chain }) => {
670
- const { node } = getBlockInfoFromPos(
674
+ const { contentNode } = getBlockInfoFromPos(
671
675
  state.doc,
672
676
  state.selection.from
673
677
  )!;
674
678
 
675
679
  const selectionAtBlockStart =
676
680
  state.selection.$anchor.parentOffset === 0;
677
- const blockEmpty = node.textContent.length === 0;
681
+ const blockEmpty = contentNode.childCount === 0;
678
682
 
679
683
  if (!blockEmpty) {
680
684
  chain()
@@ -1,4 +1,4 @@
1
- import { ParseRule } from "@tiptap/pm/model";
1
+ import { TagParseRule } from "@tiptap/pm/model";
2
2
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
3
3
  import { InlineContentSchema } from "../inlineContent/types";
4
4
  import { StyleSchema } from "../styles/types";
@@ -68,7 +68,7 @@ export function getParseRules(
68
68
  config: BlockConfig,
69
69
  customParseFunction: CustomBlockImplementation<any, any, any>["parse"]
70
70
  ) {
71
- const rules: ParseRule[] = [
71
+ const rules: TagParseRule[] = [
72
72
  {
73
73
  tag: "[data-content-type=" + config.type + "]",
74
74
  contentElement: "[data-editable]",
@@ -1,5 +1,5 @@
1
1
  import { Node } from "@tiptap/core";
2
- import { ParseRule } from "@tiptap/pm/model";
2
+ import { TagParseRule } from "@tiptap/pm/model";
3
3
  import { nodeToCustomInlineContent } from "../../api/nodeConversions/nodeConversions";
4
4
  import { propsToAttributes } from "../blocks/internal";
5
5
  import { Props } from "../propTypes";
@@ -46,7 +46,7 @@ export type CustomInlineContentImplementation<
46
46
 
47
47
  export function getInlineContentParseRules(
48
48
  config: CustomInlineContentConfig
49
- ): ParseRule[] {
49
+ ): TagParseRule[] {
50
50
  return [
51
51
  {
52
52
  tag: `[data-inline-content-type="${config.type}"]`,
@@ -98,7 +98,7 @@ export declare class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockS
98
98
  readonly linkToolbar: LinkToolbarProsemirrorPlugin<BSchema, ISchema, SSchema>;
99
99
  readonly sideMenu: SideMenuProsemirrorPlugin<BSchema, ISchema, SSchema>;
100
100
  readonly suggestionMenus: SuggestionMenuProseMirrorPlugin<BSchema, ISchema, SSchema>;
101
- readonly filePanel?: FilePanelProsemirrorPlugin<BSchema, ISchema, SSchema>;
101
+ readonly filePanel?: FilePanelProsemirrorPlugin<ISchema, SSchema>;
102
102
  readonly tableHandles?: TableHandlesProsemirrorPlugin<ISchema, SSchema>;
103
103
  readonly uploadFile: ((file: File) => Promise<string | Record<string, any>>) | undefined;
104
104
  readonly resolveFileUrl: (url: string) => Promise<string>;
@@ -2,18 +2,18 @@ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
3
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
4
4
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
5
- import type { BlockFromConfig, BlockSchema, FileBlockConfig, InlineContentSchema, StyleSchema } from "../../schema";
5
+ import type { BlockFromConfig, FileBlockConfig, InlineContentSchema, StyleSchema } from "../../schema";
6
6
  import { EventEmitter } from "../../util/EventEmitter";
7
7
  export type FilePanelState<I extends InlineContentSchema, S extends StyleSchema> = UiElementPosition & {
8
8
  block: BlockFromConfig<FileBlockConfig, I, S>;
9
9
  };
10
10
  export declare class FilePanelView<I extends InlineContentSchema, S extends StyleSchema> implements PluginView {
11
+ private readonly editor;
11
12
  private readonly pluginKey;
12
13
  private readonly pmView;
13
14
  state?: FilePanelState<I, S>;
14
15
  emitUpdate: () => void;
15
- prevWasEditable: boolean | null;
16
- constructor(pluginKey: PluginKey, pmView: EditorView, emitUpdate: (state: FilePanelState<I, S>) => void);
16
+ constructor(editor: BlockNoteEditor<Record<string, FileBlockConfig>, I, S>, pluginKey: PluginKey, pmView: EditorView, emitUpdate: (state: FilePanelState<I, S>) => void);
17
17
  mouseDownHandler: () => void;
18
18
  dragstartHandler: () => void;
19
19
  scrollHandler: () => void;
@@ -21,10 +21,10 @@ export declare class FilePanelView<I extends InlineContentSchema, S extends Styl
21
21
  closeMenu: () => void;
22
22
  destroy(): void;
23
23
  }
24
- export declare class FilePanelProsemirrorPlugin<B extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema> extends EventEmitter<any> {
24
+ export declare class FilePanelProsemirrorPlugin<I extends InlineContentSchema, S extends StyleSchema> extends EventEmitter<any> {
25
25
  private view;
26
26
  readonly plugin: Plugin;
27
- constructor(_editor: BlockNoteEditor<B, I, S>);
27
+ constructor(editor: BlockNoteEditor<Record<string, FileBlockConfig>, I, S>);
28
28
  get shown(): boolean;
29
29
  onUpdate(callback: (state: FilePanelState<I, S>) => void): () => void;
30
30
  closeMenu: () => void;
@@ -12,7 +12,6 @@ export declare class FormattingToolbarView implements PluginView {
12
12
  emitUpdate: () => void;
13
13
  preventHide: boolean;
14
14
  preventShow: boolean;
15
- prevWasEditable: boolean | null;
16
15
  shouldShow: (props: {
17
16
  view: EditorView;
18
17
  state: EditorState;
@@ -9,24 +9,24 @@ import { EventEmitter } from "../../util/EventEmitter";
9
9
  export type SideMenuState<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema> = UiElementPosition & {
10
10
  block: Block<BSchema, I, S>;
11
11
  };
12
- export declare function getDraggableBlockFromCoords(coords: {
13
- left: number;
14
- top: number;
15
- }, view: EditorView): {
12
+ export declare function getDraggableBlockFromElement(element: Element, view: EditorView): {
16
13
  node: HTMLElement;
17
14
  id: string;
18
15
  } | undefined;
19
16
  export declare class SideMenuView<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema> implements PluginView {
20
17
  private readonly editor;
21
18
  private readonly pmView;
22
- private state?;
23
- private readonly emitUpdate;
19
+ state?: SideMenuState<BSchema, I, S>;
20
+ readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;
21
+ private needUpdate;
22
+ private mousePos;
24
23
  private horizontalPosAnchoredAtRoot;
25
24
  private horizontalPosAnchor;
26
25
  private hoveredBlock;
27
26
  isDragging: boolean;
28
27
  menuFrozen: boolean;
29
28
  constructor(editor: BlockNoteEditor<BSchema, I, S>, pmView: EditorView, emitUpdate: (state: SideMenuState<BSchema, I, S>) => void);
29
+ updateState: () => void;
30
30
  /**
31
31
  * Sets isDragging when dragging text.
32
32
  */
@@ -44,9 +44,10 @@ export declare class SideMenuView<BSchema extends BlockSchema, I extends InlineC
44
44
  */
45
45
  onDragOver: (event: DragEvent) => void;
46
46
  onKeyDown: (_event: KeyboardEvent) => void;
47
- onMouseDown: (_event: MouseEvent) => void;
47
+ onMouseDown: () => void;
48
48
  onMouseMove: (event: MouseEvent) => void;
49
49
  onScroll: () => void;
50
+ update(): void;
50
51
  destroy(): void;
51
52
  addBlock(): void;
52
53
  }
@@ -84,5 +85,5 @@ export declare class SideMenuProsemirrorPlugin<BSchema extends BlockSchema, I ex
84
85
  * attached to the same block regardless of which block is hovered by the
85
86
  * mouse cursor.
86
87
  */
87
- unfreezeMenu: () => boolean;
88
+ unfreezeMenu: () => void;
88
89
  }
@@ -1,4 +1,4 @@
1
- import { ParseRule } from "@tiptap/pm/model";
1
+ import { TagParseRule } from "@tiptap/pm/model";
2
2
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
3
3
  import { InlineContentSchema } from "../inlineContent/types";
4
4
  import { StyleSchema } from "../styles/types";
@@ -28,7 +28,7 @@ export type CustomBlockImplementation<T extends CustomBlockConfig, I extends Inl
28
28
  };
29
29
  parse?: (el: HTMLElement) => PartialBlockFromConfig<T, I, S>["props"] | undefined;
30
30
  };
31
- export declare function getParseRules(config: BlockConfig, customParseFunction: CustomBlockImplementation<any, any, any>["parse"]): ParseRule[];
31
+ export declare function getParseRules(config: BlockConfig, customParseFunction: CustomBlockImplementation<any, any, any>["parse"]): TagParseRule[];
32
32
  export declare function createBlockSpec<T extends CustomBlockConfig, I extends InlineContentSchema, S extends StyleSchema>(blockConfig: T, blockImplementation: CustomBlockImplementation<T, I, S>): {
33
33
  config: T;
34
34
  implementation: import("./types").TiptapBlockImplementation<T, any, InlineContentSchema, StyleSchema>;