@blocknote/core 0.19.1 → 0.20.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 (109) hide show
  1. package/dist/blocknote.js +1791 -1483
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +6 -6
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/src/api/blockManipulation/commands/insertBlocks/insertBlocks.js +6 -3
  6. package/dist/src/api/blockManipulation/commands/insertBlocks/insertBlocks.js.map +1 -1
  7. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.js +219 -0
  8. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.js.map +1 -0
  9. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.js +175 -0
  10. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.js.map +1 -0
  11. package/dist/src/api/blockManipulation/commands/splitBlock/splitBlock.test.js +3 -0
  12. package/dist/src/api/blockManipulation/commands/splitBlock/splitBlock.test.js.map +1 -1
  13. package/dist/src/api/blockManipulation/commands/updateBlock/updateBlock.js +6 -3
  14. package/dist/src/api/blockManipulation/commands/updateBlock/updateBlock.js.map +1 -1
  15. package/dist/src/api/blockManipulation/getBlock/getBlock.js +56 -0
  16. package/dist/src/api/blockManipulation/getBlock/getBlock.js.map +1 -0
  17. package/dist/src/api/blockManipulation/selections/selection.js +149 -0
  18. package/dist/src/api/blockManipulation/selections/selection.js.map +1 -0
  19. package/dist/src/api/blockManipulation/selections/selection.test.js +39 -0
  20. package/dist/src/api/blockManipulation/selections/selection.test.js.map +1 -0
  21. package/dist/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.js +3 -0
  22. package/dist/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.js.map +1 -1
  23. package/dist/src/api/clipboard/fromClipboard/handleVSCodePaste.js +3 -3
  24. package/dist/src/api/clipboard/fromClipboard/handleVSCodePaste.js.map +1 -1
  25. package/dist/src/api/nodeUtil.js +1 -1
  26. package/dist/src/api/nodeUtil.js.map +1 -1
  27. package/dist/src/blocks/CodeBlockContent/CodeBlockContent.js +15 -7
  28. package/dist/src/blocks/CodeBlockContent/CodeBlockContent.js.map +1 -1
  29. package/dist/src/blocks/CodeBlockContent/defaultSupportedLanguages.js +38 -18
  30. package/dist/src/blocks/CodeBlockContent/defaultSupportedLanguages.js.map +1 -1
  31. package/dist/src/blocks/TableBlockContent/TableExtension.js +8 -1
  32. package/dist/src/blocks/TableBlockContent/TableExtension.js.map +1 -1
  33. package/dist/src/editor/BlockNoteEditor.js +59 -57
  34. package/dist/src/editor/BlockNoteEditor.js.map +1 -1
  35. package/dist/src/editor/BlockNoteExtensions.js +2 -1
  36. package/dist/src/editor/BlockNoteExtensions.js.map +1 -1
  37. package/dist/src/extensions/FormattingToolbar/FormattingToolbarPlugin.js +4 -2
  38. package/dist/src/extensions/FormattingToolbar/FormattingToolbarPlugin.js.map +1 -1
  39. package/dist/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js +10 -8
  40. package/dist/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js.map +1 -1
  41. package/dist/src/extensions/LinkToolbar/LinkToolbarPlugin.js +7 -3
  42. package/dist/src/extensions/LinkToolbar/LinkToolbarPlugin.js.map +1 -1
  43. package/dist/src/extensions/Placeholder/PlaceholderPlugin.js +13 -7
  44. package/dist/src/extensions/Placeholder/PlaceholderPlugin.js.map +1 -1
  45. package/dist/src/extensions/SideMenu/SideMenuPlugin.js +5 -1
  46. package/dist/src/extensions/SideMenu/SideMenuPlugin.js.map +1 -1
  47. package/dist/src/extensions/SideMenu/dragging.js +5 -1
  48. package/dist/src/extensions/SideMenu/dragging.js.map +1 -1
  49. package/dist/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.js +0 -3
  50. package/dist/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.js.map +1 -1
  51. package/dist/src/extensions/TableHandles/TableHandlesPlugin.js +25 -8
  52. package/dist/src/extensions/TableHandles/TableHandlesPlugin.js.map +1 -1
  53. package/dist/src/i18n/locales/ru.js +1 -1
  54. package/dist/src/index.js +1 -0
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/style.css +1 -1
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/dist/webpack-stats.json +1 -1
  59. package/package.json +3 -3
  60. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +6 -6
  61. package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +9506 -0
  62. package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.ts +295 -0
  63. package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts +338 -0
  64. package/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts +4 -0
  65. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +11 -3
  66. package/src/api/blockManipulation/getBlock/getBlock.ts +141 -0
  67. package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +660 -0
  68. package/src/api/blockManipulation/selections/selection.test.ts +56 -0
  69. package/src/api/blockManipulation/selections/selection.ts +244 -0
  70. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.ts +4 -0
  71. package/src/api/clipboard/fromClipboard/handleVSCodePaste.ts +4 -4
  72. package/src/api/nodeUtil.ts +2 -2
  73. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +18 -8
  74. package/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts +38 -18
  75. package/src/blocks/TableBlockContent/TableExtension.ts +12 -1
  76. package/src/editor/Block.css +3 -0
  77. package/src/editor/BlockNoteEditor.ts +93 -85
  78. package/src/editor/BlockNoteExtensions.ts +3 -1
  79. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +4 -2
  80. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +11 -8
  81. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +11 -4
  82. package/src/extensions/Placeholder/PlaceholderPlugin.ts +23 -15
  83. package/src/extensions/SideMenu/SideMenuPlugin.ts +5 -1
  84. package/src/extensions/SideMenu/dragging.ts +5 -1
  85. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +0 -5
  86. package/src/extensions/TableHandles/TableHandlesPlugin.ts +34 -9
  87. package/src/i18n/locales/ru.ts +1 -1
  88. package/src/index.ts +1 -0
  89. package/types/src/api/blockManipulation/commands/moveBlocks/moveBlocks.d.ts +15 -0
  90. package/types/src/api/blockManipulation/getBlock/getBlock.d.ts +7 -0
  91. package/types/src/api/blockManipulation/selections/selection.d.ts +5 -0
  92. package/types/src/api/blockManipulation/selections/selection.test.d.ts +1 -0
  93. package/types/src/api/nodeUtil.d.ts +1 -1
  94. package/types/src/editor/BlockNoteEditor.d.ts +54 -10
  95. package/types/src/editor/BlockNoteExtensions.d.ts +1 -0
  96. package/types/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.d.ts +1 -0
  97. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +1 -1
  98. package/types/src/index.d.ts +1 -0
  99. package/types/src/pm-nodes/BlockContainer.d.ts +2 -2
  100. package/types/src/pm-nodes/BlockGroup.d.ts +2 -2
  101. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.js +0 -116
  102. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.js.map +0 -1
  103. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.test.js +0 -110
  104. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.test.js.map +0 -1
  105. package/src/api/blockManipulation/commands/moveBlock/__snapshots__/moveBlock.test.ts.snap +0 -3799
  106. package/src/api/blockManipulation/commands/moveBlock/moveBlock.test.ts +0 -196
  107. package/src/api/blockManipulation/commands/moveBlock/moveBlock.ts +0 -176
  108. package/types/src/api/blockManipulation/commands/moveBlock/moveBlock.d.ts +0 -5
  109. /package/types/src/api/blockManipulation/commands/{moveBlock/moveBlock.test.d.ts → moveBlocks/moveBlocks.test.d.ts} +0 -0
@@ -9,11 +9,17 @@ import {
9
9
  import { Node, Schema } from "prosemirror-model";
10
10
  // import "./blocknote.css";
11
11
  import * as Y from "yjs";
12
+ import {
13
+ getBlock,
14
+ getNextBlock,
15
+ getParentBlock,
16
+ getPrevBlock,
17
+ } from "../api/blockManipulation/getBlock/getBlock.js";
12
18
  import { insertBlocks } from "../api/blockManipulation/commands/insertBlocks/insertBlocks.js";
13
19
  import {
14
- moveBlockDown,
15
- moveBlockUp,
16
- } from "../api/blockManipulation/commands/moveBlock/moveBlock.js";
20
+ moveBlocksDown,
21
+ moveBlocksUp,
22
+ } from "../api/blockManipulation/commands/moveBlocks/moveBlocks.js";
17
23
  import {
18
24
  canNestBlock,
19
25
  canUnnestBlock,
@@ -28,6 +34,10 @@ import {
28
34
  getTextCursorPosition,
29
35
  setTextCursorPosition,
30
36
  } from "../api/blockManipulation/selections/textCursorPosition/textCursorPosition.js";
37
+ import {
38
+ getSelection,
39
+ setSelection,
40
+ } from "../api/blockManipulation/selections/selection.js";
31
41
  import { createExternalHTMLExporter } from "../api/exporters/html/externalHTMLExporter.js";
32
42
  import { blocksToMarkdown } from "../api/exporters/markdown/markdownExporter.js";
33
43
  import { HTMLToBlocks } from "../api/parsers/html/parseHTML.js";
@@ -219,6 +229,21 @@ export type BlockNoteEditorOptions<
219
229
  setIdAttribute?: boolean;
220
230
 
221
231
  dropCursor?: (opts: any) => Plugin;
232
+
233
+ /**
234
+ Select desired behavior when pressing `Tab` (or `Shift-Tab`). Specifically,
235
+ what should happen when a user has selected multiple blocks while a toolbar
236
+ is open:
237
+ - `"prefer-navigate-ui"`: Change focus to the toolbar. The user needs to
238
+ first press `Escape` to close the toolbar, and can then indent multiple
239
+ blocks. Better for keyboard accessibility.
240
+ - `"prefer-indent"`: Regardless of whether toolbars are open, indent the
241
+ selection of blocks. In this case, it's not possible to navigate toolbars
242
+ with the keyboard.
243
+
244
+ @default "prefer-navigate-ui"
245
+ */
246
+ tabBehavior: "prefer-navigate-ui" | "prefer-indent";
222
247
  };
223
248
 
224
249
  const blockNoteTipTapOptions = {
@@ -395,6 +420,7 @@ export class BlockNoteEditor<
395
420
  tableHandles: checkDefaultBlockTypeInSchema("table", this),
396
421
  dropCursor: this.options.dropCursor ?? dropCursor,
397
422
  placeholders: newOptions.placeholders,
423
+ tabBehavior: newOptions.tabBehavior,
398
424
  });
399
425
 
400
426
  // add extensions from _tiptapOptions
@@ -474,6 +500,12 @@ export class BlockNoteEditor<
474
500
  return ext;
475
501
  }
476
502
 
503
+ if (!ext.plugin) {
504
+ throw new Error(
505
+ "Extension should either be a TipTap extension or a ProseMirror plugin in a plugin property"
506
+ );
507
+ }
508
+
477
509
  // "blocknote" extensions (prosemirror plugins)
478
510
  return Extension.create({
479
511
  name: key,
@@ -604,39 +636,57 @@ export class BlockNoteEditor<
604
636
 
605
637
  /**
606
638
  * Gets a snapshot of an existing block from the editor.
607
- * @param blockIdentifier The identifier of an existing block that should be retrieved.
608
- * @returns The block that matches the identifier, or `undefined` if no matching block was found.
639
+ * @param blockIdentifier The identifier of an existing block that should be
640
+ * retrieved.
641
+ * @returns The block that matches the identifier, or `undefined` if no
642
+ * matching block was found.
609
643
  */
610
644
  public getBlock(
611
645
  blockIdentifier: BlockIdentifier
612
646
  ): Block<BSchema, ISchema, SSchema> | undefined {
613
- const id =
614
- typeof blockIdentifier === "string"
615
- ? blockIdentifier
616
- : blockIdentifier.id;
617
- let newBlock: Block<BSchema, ISchema, SSchema> | undefined = undefined;
618
-
619
- this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
620
- if (typeof newBlock !== "undefined") {
621
- return false;
622
- }
623
-
624
- if (node.type.name !== "blockContainer" || node.attrs.id !== id) {
625
- return true;
626
- }
647
+ return getBlock(this, blockIdentifier);
648
+ }
627
649
 
628
- newBlock = nodeToBlock(
629
- node,
630
- this.schema.blockSchema,
631
- this.schema.inlineContentSchema,
632
- this.schema.styleSchema,
633
- this.blockCache
634
- );
650
+ /**
651
+ * Gets a snapshot of the previous sibling of an existing block from the
652
+ * editor.
653
+ * @param blockIdentifier The identifier of an existing block for which the
654
+ * previous sibling should be retrieved.
655
+ * @returns The previous sibling of the block that matches the identifier.
656
+ * `undefined` if no matching block was found, or it's the first child/block
657
+ * in the document.
658
+ */
659
+ public getPrevBlock(
660
+ blockIdentifier: BlockIdentifier
661
+ ): Block<BSchema, ISchema, SSchema> | undefined {
662
+ return getPrevBlock(this, blockIdentifier);
663
+ }
635
664
 
636
- return false;
637
- });
665
+ /**
666
+ * Gets a snapshot of the next sibling of an existing block from the editor.
667
+ * @param blockIdentifier The identifier of an existing block for which the
668
+ * next sibling should be retrieved.
669
+ * @returns The next sibling of the block that matches the identifier.
670
+ * `undefined` if no matching block was found, or it's the last child/block in
671
+ * the document.
672
+ */
673
+ public getNextBlock(
674
+ blockIdentifier: BlockIdentifier
675
+ ): Block<BSchema, ISchema, SSchema> | undefined {
676
+ return getNextBlock(this, blockIdentifier);
677
+ }
638
678
 
639
- return newBlock;
679
+ /**
680
+ * Gets a snapshot of the parent of an existing block from the editor.
681
+ * @param blockIdentifier The identifier of an existing block for which the
682
+ * parent should be retrieved.
683
+ * @returns The parent of the block that matches the identifier. `undefined`
684
+ * if no matching block was found, or the block isn't nested.
685
+ */
686
+ public getParentBlock(
687
+ blockIdentifier: BlockIdentifier
688
+ ): Block<BSchema, ISchema, SSchema> | undefined {
689
+ return getParentBlock(this, blockIdentifier);
640
690
  }
641
691
 
642
692
  /**
@@ -722,53 +772,11 @@ export class BlockNoteEditor<
722
772
  * Gets a snapshot of the current selection.
723
773
  */
724
774
  public getSelection(): Selection<BSchema, ISchema, SSchema> | undefined {
725
- // Either the TipTap selection is empty, or it's a node selection. In either
726
- // case, it only spans one block, so we return undefined.
727
- if (
728
- this._tiptapEditor.state.selection.from ===
729
- this._tiptapEditor.state.selection.to ||
730
- "node" in this._tiptapEditor.state.selection
731
- ) {
732
- return undefined;
733
- }
734
-
735
- const blocks: Block<BSchema, ISchema, SSchema>[] = [];
736
-
737
- // TODO: This adds all child blocks to the same array. Needs to find min
738
- // depth and only add blocks at that depth.
739
- this._tiptapEditor.state.doc.descendants((node, pos) => {
740
- if (node.type.spec.group !== "blockContent") {
741
- return true;
742
- }
743
-
744
- // Fixed the block pos and size
745
- // all block is wrapped with a blockContent wrapper
746
- // and blockContent wrapper pos = inner block pos - 1
747
- // blockContent wrapper end = inner block pos + nodeSize + 1
748
- // need to add 1 to start and -1 to end
749
- const end = pos + node.nodeSize - 1;
750
- const start = pos + 1;
751
- if (
752
- end <= this._tiptapEditor.state.selection.from ||
753
- start >= this._tiptapEditor.state.selection.to
754
- ) {
755
- return true;
756
- }
757
-
758
- blocks.push(
759
- nodeToBlock(
760
- this._tiptapEditor.state.doc.resolve(pos).node(),
761
- this.schema.blockSchema,
762
- this.schema.inlineContentSchema,
763
- this.schema.styleSchema,
764
- this.blockCache
765
- )
766
- );
767
-
768
- return false;
769
- });
775
+ return getSelection(this);
776
+ }
770
777
 
771
- return { blocks: blocks };
778
+ public setSelection(startBlock: BlockIdentifier, endBlock: BlockIdentifier) {
779
+ setSelection(this, startBlock, endBlock);
772
780
  }
773
781
 
774
782
  /**
@@ -1026,21 +1034,21 @@ export class BlockNoteEditor<
1026
1034
  }
1027
1035
 
1028
1036
  /**
1029
- * Moves the block containing the text cursor up. If the previous block has
1030
- * children, moves it to the end of its children. If there is no previous
1031
- * block, but the current block is nested, moves it out of & before its parent.
1037
+ * Moves the selected blocks up. If the previous block has children, moves
1038
+ * them to the end of its children. If there is no previous block, but the
1039
+ * current blocks share a common parent, moves them out of & before it.
1032
1040
  */
1033
- public moveBlockUp() {
1034
- moveBlockUp(this);
1041
+ public moveBlocksUp() {
1042
+ moveBlocksUp(this);
1035
1043
  }
1036
1044
 
1037
1045
  /**
1038
- * Moves the block containing the text cursor down. If the next block has
1039
- * children, moves it to the start of its children. If there is no next block,
1040
- * but the current block is nested, moves it out of & after its parent.
1046
+ * Moves the selected blocks down. If the next block has children, moves
1047
+ * them to the start of its children. If there is no next block, but the
1048
+ * current blocks share a common parent, moves them out of & after it.
1041
1049
  */
1042
- public moveBlockDown() {
1043
- moveBlockDown(this);
1050
+ public moveBlocksDown() {
1051
+ moveBlocksDown(this);
1044
1052
  }
1045
1053
 
1046
1054
  /**
@@ -67,6 +67,7 @@ type ExtensionOptions<
67
67
  tableHandles: boolean;
68
68
  dropCursor: (opts: any) => Plugin;
69
69
  placeholders: Record<string | "default", string>;
70
+ tabBehavior?: "prefer-navigate-ui" | "prefer-indent";
70
71
  };
71
72
 
72
73
  /**
@@ -116,7 +117,7 @@ export const getBlockNoteExtensions = <
116
117
  ret["nodeSelectionKeyboard"] = new NodeSelectionKeyboardPlugin();
117
118
 
118
119
  const disableExtensions: string[] = opts.disableExtensions || [];
119
- for (const ext of Object.keys(disableExtensions)) {
120
+ for (const ext of disableExtensions) {
120
121
  delete ret[ext];
121
122
  }
122
123
 
@@ -200,6 +201,7 @@ const getTipTapExtensions = <
200
201
  }),
201
202
  KeyboardShortcutsExtension.configure({
202
203
  editor: opts.editor,
204
+ tabBehavior: opts.tabBehavior,
203
205
  }),
204
206
  BlockGroup.configure({
205
207
  domAttributes: opts.domAttributes,
@@ -139,9 +139,11 @@ export class FormattingToolbarView implements PluginView {
139
139
  // Wrapping in a setTimeout gives enough time to wait for the blur event to
140
140
  // occur before updating the toolbar.
141
141
  const { state, composing } = view;
142
- const { doc, selection } = state;
142
+ const { selection } = state;
143
143
  const isSame =
144
- oldState && oldState.doc.eq(doc) && oldState.selection.eq(selection);
144
+ oldState &&
145
+ oldState.selection.from === state.selection.from &&
146
+ oldState.selection.to === state.selection.to;
145
147
 
146
148
  if (composing || isSame) {
147
149
  return;
@@ -16,6 +16,7 @@ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
16
16
 
17
17
  export const KeyboardShortcutsExtension = Extension.create<{
18
18
  editor: BlockNoteEditor<any, any, any>;
19
+ tabBehavior: "prefer-navigate-ui" | "prefer-indent";
19
20
  }>({
20
21
  priority: 50,
21
22
 
@@ -479,9 +480,10 @@ export const KeyboardShortcutsExtension = Extension.create<{
479
480
  // editor since the browser will try to use tab for keyboard navigation.
480
481
  Tab: () => {
481
482
  if (
482
- this.options.editor.formattingToolbar?.shown ||
483
- this.options.editor.linkToolbar?.shown ||
484
- this.options.editor.filePanel?.shown
483
+ this.options.tabBehavior !== "prefer-indent" &&
484
+ (this.options.editor.formattingToolbar?.shown ||
485
+ this.options.editor.linkToolbar?.shown ||
486
+ this.options.editor.filePanel?.shown)
485
487
  ) {
486
488
  // don't handle tabs if a toolbar is shown, so we can tab into / out of it
487
489
  return false;
@@ -491,9 +493,10 @@ export const KeyboardShortcutsExtension = Extension.create<{
491
493
  },
492
494
  "Shift-Tab": () => {
493
495
  if (
494
- this.options.editor.formattingToolbar?.shown ||
495
- this.options.editor.linkToolbar?.shown ||
496
- this.options.editor.filePanel?.shown
496
+ this.options.tabBehavior !== "prefer-indent" &&
497
+ (this.options.editor.formattingToolbar?.shown ||
498
+ this.options.editor.linkToolbar?.shown ||
499
+ this.options.editor.filePanel?.shown)
497
500
  ) {
498
501
  // don't handle tabs if a toolbar is shown, so we can tab into / out of it
499
502
  return false;
@@ -502,11 +505,11 @@ export const KeyboardShortcutsExtension = Extension.create<{
502
505
  return true;
503
506
  },
504
507
  "Shift-Mod-ArrowUp": () => {
505
- this.options.editor.moveBlockUp();
508
+ this.options.editor.moveBlocksUp();
506
509
  return true;
507
510
  },
508
511
  "Shift-Mod-ArrowDown": () => {
509
- this.options.editor.moveBlockDown();
512
+ this.options.editor.moveBlocksDown();
510
513
  return true;
511
514
  },
512
515
  };
@@ -2,7 +2,7 @@ import { getMarkRange, posToDOMRect, Range } from "@tiptap/core";
2
2
 
3
3
  import { EditorView } from "@tiptap/pm/view";
4
4
  import { Mark } from "prosemirror-model";
5
- import { Plugin, PluginKey, PluginView } from "prosemirror-state";
5
+ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
6
6
 
7
7
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
8
8
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
@@ -52,7 +52,7 @@ class LinkToolbarView implements PluginView {
52
52
 
53
53
  this.startMenuUpdateTimer = () => {
54
54
  this.menuUpdateTimer = setTimeout(() => {
55
- this.update();
55
+ this.update(this.pmView);
56
56
  }, 250);
57
57
  };
58
58
 
@@ -190,8 +190,15 @@ class LinkToolbarView implements PluginView {
190
190
  }
191
191
  }
192
192
 
193
- update() {
194
- if (!this.pmView.hasFocus()) {
193
+ update(view: EditorView, oldState?: EditorState) {
194
+ const { state } = view;
195
+
196
+ const isSame =
197
+ oldState &&
198
+ oldState.selection.from === state.selection.from &&
199
+ oldState.selection.to === state.selection.to;
200
+
201
+ if (isSame || !this.pmView.hasFocus()) {
195
202
  return;
196
203
  }
197
204
 
@@ -48,22 +48,30 @@ export class PlaceholderPlugin {
48
48
  for (const [blockType, placeholder] of Object.entries(placeholders)) {
49
49
  const mustBeFocused = blockType === "default";
50
50
 
51
- styleSheet.insertRule(
52
- `${getSelector(
53
- blockType,
54
- mustBeFocused
55
- )}{ content: ${JSON.stringify(placeholder)}; }`
56
- );
57
-
58
- // For some reason, the placeholders which show when the block is focused
59
- // take priority over ones which show depending on block type, so we need
60
- // to make sure the block specific ones are also used when the block is
61
- // focused.
62
- if (!mustBeFocused) {
51
+ try {
63
52
  styleSheet.insertRule(
64
- `${getSelector(blockType, true)}{ content: ${JSON.stringify(
65
- placeholder
66
- )}; }`
53
+ `${getSelector(
54
+ blockType,
55
+ mustBeFocused
56
+ )} { content: ${JSON.stringify(placeholder)}; }`
57
+ );
58
+
59
+ // For some reason, the placeholders which show when the block is focused
60
+ // take priority over ones which show depending on block type, so we need
61
+ // to make sure the block specific ones are also used when the block is
62
+ // focused.
63
+ if (!mustBeFocused) {
64
+ styleSheet.insertRule(
65
+ `${getSelector(blockType, true)} { content: ${JSON.stringify(
66
+ placeholder
67
+ )}; }`
68
+ );
69
+ }
70
+ } catch (e) {
71
+ // eslint-disable-next-line no-console
72
+ console.warn(
73
+ `Failed to insert placeholder CSS rule - this is likely due to the browser not supporting certain CSS pseudo-element selectors (:has, :only-child:, or :before)`,
74
+ e
67
75
  );
68
76
  }
69
77
  }
@@ -491,7 +491,11 @@ export class SideMenuProsemirrorPlugin<
491
491
  * attached to the same block regardless of which block is hovered by the
492
492
  * mouse cursor.
493
493
  */
494
- freezeMenu = () => (this.view!.menuFrozen = true);
494
+ freezeMenu = () => {
495
+ this.view!.menuFrozen = true;
496
+ this.view!.state!.show = true;
497
+ this.view!.emitUpdate(this.view!.state!);
498
+ };
495
499
  /**
496
500
  * Unfreezes the side menu. When frozen, the side menu will stay
497
501
  * attached to the same block regardless of which block is hovered by the
@@ -149,7 +149,11 @@ export function dragStart<
149
149
 
150
150
  const view = editor.prosemirrorView;
151
151
 
152
- const pos = getNodeById(block.id, view.state.doc).posBeforeNode;
152
+ const posInfo = getNodeById(block.id, view.state.doc);
153
+ if (!posInfo) {
154
+ throw new Error(`Block with ID ${block.id} not found`);
155
+ }
156
+ const pos = posInfo.posBeforeNode;
153
157
 
154
158
  if (pos != null) {
155
159
  const selection = view.state.selection;
@@ -176,14 +176,9 @@ export function getDefaultSlashMenuItems<
176
176
  if (checkDefaultBlockTypeInSchema("codeBlock", editor)) {
177
177
  items.push({
178
178
  onItemClick: () => {
179
- const pos = editor._tiptapEditor.state.selection.from;
180
-
181
179
  insertOrUpdateBlock(editor, {
182
180
  type: "codeBlock",
183
181
  });
184
-
185
- // Move the cursor inside the code block
186
- editor._tiptapEditor.commands.setTextSelection(pos);
187
182
  },
188
183
  badge: formatKeyboardShortcut("Mod-Alt-c"),
189
184
  key: "code_block",
@@ -236,6 +236,9 @@ export class TableHandlesView<
236
236
  blockEl.id,
237
237
  this.editor._tiptapEditor.state.doc
238
238
  );
239
+ if (!pmNodeInfo) {
240
+ throw new Error(`Block with ID ${blockEl.id} not found`);
241
+ }
239
242
 
240
243
  const block = nodeToBlock(
241
244
  pmNodeInfo.node,
@@ -468,17 +471,26 @@ export class TableHandlesView<
468
471
  // the existing selection out of the block.
469
472
  this.editor.setTextCursorPosition(this.state.block.id);
470
473
  };
471
- // Updates drag handle positions on table content updates.
474
+ // Updates drag handles when the table is modified or removed.
472
475
  update() {
473
476
  if (!this.state || !this.state.show) {
474
477
  return;
475
478
  }
476
479
 
477
- const tableBody = this.tableElement!.querySelector("tbody");
478
- if (!tableBody) {
480
+ // Hide handles if the table block has been removed.
481
+ this.state.block = this.editor.getBlock(this.state.block.id)!;
482
+ if (!this.state.block) {
483
+ this.state.show = false;
484
+ this.state.showAddOrRemoveRowsButton = false;
485
+ this.state.showAddOrRemoveColumnsButton = false;
486
+ this.emitUpdate();
487
+
479
488
  return;
480
489
  }
481
490
 
491
+ const rowCount = this.state.block.content.rows.length;
492
+ const colCount = this.state.block.content.rows[0].cells.length;
493
+
482
494
  if (
483
495
  this.state.rowIndex !== undefined &&
484
496
  this.state.colIndex !== undefined
@@ -486,20 +498,33 @@ export class TableHandlesView<
486
498
  // If rows or columns are deleted in the update, the hovered indices for
487
499
  // those may now be out of bounds. If this is the case, they are moved to
488
500
  // the new last row or column.
489
- if (this.state.rowIndex >= tableBody.children.length) {
490
- this.state.rowIndex = tableBody.children.length - 1;
501
+ if (this.state.rowIndex >= rowCount) {
502
+ this.state.rowIndex = rowCount - 1;
491
503
  }
492
- if (this.state.colIndex >= tableBody.children[0].children.length) {
493
- this.state.colIndex = tableBody.children[0].children.length - 1;
504
+ if (this.state.colIndex >= colCount) {
505
+ this.state.colIndex = colCount - 1;
494
506
  }
507
+ }
508
+
509
+ // Update bounding boxes.
510
+ const tableBody = this.tableElement!.querySelector("tbody");
511
+ if (!tableBody) {
512
+ throw new Error(
513
+ "Table block does not contain a 'tbody' HTML element. This should never happen."
514
+ );
515
+ }
495
516
 
517
+ if (
518
+ this.state.rowIndex !== undefined &&
519
+ this.state.colIndex !== undefined
520
+ ) {
496
521
  const row = tableBody.children[this.state.rowIndex];
497
522
  const cell = row.children[this.state.colIndex];
523
+
498
524
  this.state.referencePosCell = cell.getBoundingClientRect();
499
525
  }
500
-
501
- this.state.block = this.editor.getBlock(this.state.block.id)!;
502
526
  this.state.referencePosTable = tableBody.getBoundingClientRect();
527
+
503
528
  this.emitUpdate();
504
529
  }
505
530
 
@@ -271,7 +271,7 @@ export const ru: Dictionary = {
271
271
  image: "Удалить картинку",
272
272
  video: "Удалить видео",
273
273
  audio: "Удалить аудио",
274
- file: "Скачать файл",
274
+ file: "Удалить файл",
275
275
  },
276
276
  },
277
277
  file_preview_toggle: {
package/src/index.ts CHANGED
@@ -46,6 +46,7 @@ export * from "./util/string.js";
46
46
  export * from "./util/typescript.js";
47
47
  export { UnreachableCaseError, assertEmpty } from "./util/typescript.js";
48
48
  export { locales };
49
+ export * from "./api/blockManipulation/commands/updateBlock/updateBlock.js";
49
50
 
50
51
  // for testing from react (TODO: move):
51
52
  export * from "./api/nodeConversions/blockToNode.js";
@@ -0,0 +1,15 @@
1
+ import type { BlockNoteEditor } from "../../../../editor/BlockNoteEditor";
2
+ import { BlockIdentifier } from "../../../../schema/index.js";
3
+ /**
4
+ * Removes the selected blocks from the editor, then inserts them before/after a
5
+ * reference block. Also updates the selection to match the original selection
6
+ * using `getBlockSelectionData` and `updateBlockSelectionFromData`.
7
+ * @param editor The BlockNote editor instance to move the blocks in.
8
+ * @param referenceBlock The reference block to insert the selected blocks
9
+ * before/after.
10
+ * @param placement Whether to insert the selected blocks before or after the
11
+ * reference block.
12
+ */
13
+ export declare function moveSelectedBlocksAndSelection(editor: BlockNoteEditor<any, any, any>, referenceBlock: BlockIdentifier, placement: "before" | "after"): void;
14
+ export declare function moveBlocksUp(editor: BlockNoteEditor<any, any, any>): void;
15
+ export declare function moveBlocksDown(editor: BlockNoteEditor<any, any, any>): void;
@@ -0,0 +1,7 @@
1
+ import { Block } from "../../../blocks/defaultBlocks.js";
2
+ import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js";
3
+ import { BlockIdentifier, BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema/index.js";
4
+ export declare function getBlock<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>, blockIdentifier: BlockIdentifier): Block<BSchema, I, S> | undefined;
5
+ export declare function getPrevBlock<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>, blockIdentifier: BlockIdentifier): Block<BSchema, I, S> | undefined;
6
+ export declare function getNextBlock<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>, blockIdentifier: BlockIdentifier): Block<BSchema, I, S> | undefined;
7
+ export declare function getParentBlock<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>, blockIdentifier: BlockIdentifier): Block<BSchema, I, S> | undefined;
@@ -0,0 +1,5 @@
1
+ import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
2
+ import { Selection } from "../../../editor/selectionTypes.js";
3
+ import { BlockIdentifier, BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema/index.js";
4
+ export declare function getSelection<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>): Selection<BSchema, I, S> | undefined;
5
+ export declare function setSelection<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>, startBlock: BlockIdentifier, endBlock: BlockIdentifier): void;
@@ -5,4 +5,4 @@ import { Node } from "prosemirror-model";
5
5
  export declare function getNodeById(id: string, doc: Node): {
6
6
  node: Node;
7
7
  posBeforeNode: number;
8
- };
8
+ } | undefined;