@blocknote/core 0.8.2 → 0.8.3

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 (68) hide show
  1. package/README.md +4 -0
  2. package/dist/blocknote.js +1777 -1849
  3. package/dist/blocknote.js.map +1 -1
  4. package/dist/blocknote.umd.cjs +4 -4
  5. package/dist/blocknote.umd.cjs.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/package.json +2 -2
  8. package/src/BlockNoteEditor.ts +89 -39
  9. package/src/BlockNoteExtensions.ts +1 -58
  10. package/src/api/formatConversions/__snapshots__/formatConversions.test.ts.snap +10 -10
  11. package/src/api/formatConversions/formatConversions.test.ts +587 -605
  12. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +15 -15
  13. package/src/api/nodeConversions/nodeConversions.test.ts +90 -94
  14. package/src/extensions/Blocks/api/blockTypes.ts +3 -2
  15. package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +6 -0
  16. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +101 -114
  17. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +184 -149
  18. package/src/extensions/Placeholder/PlaceholderExtension.ts +2 -2
  19. package/src/extensions/{DraggableBlocks/DraggableBlocksPlugin.ts → SideMenu/SideMenuPlugin.ts} +181 -164
  20. package/src/extensions/SlashMenu/BaseSlashMenuItem.ts +7 -30
  21. package/src/extensions/SlashMenu/SlashMenuPlugin.ts +51 -0
  22. package/src/extensions/SlashMenu/defaultSlashMenuItems.ts +109 -0
  23. package/src/extensions/UniqueID/UniqueID.ts +29 -30
  24. package/src/index.ts +9 -8
  25. package/src/node_modules/.vitest/results.json +1 -0
  26. package/src/shared/BaseUiElementTypes.ts +8 -0
  27. package/src/shared/EditorElement.ts +0 -16
  28. package/src/shared/EventEmitter.ts +58 -0
  29. package/src/shared/plugins/suggestion/SuggestionItem.ts +3 -6
  30. package/src/shared/plugins/suggestion/SuggestionPlugin.ts +341 -403
  31. package/types/src/BlockNoteEditor.d.ts +18 -11
  32. package/types/src/BlockNoteExtensions.d.ts +0 -19
  33. package/types/src/EventEmitter.d.ts +11 -0
  34. package/types/src/extensions/Blocks/api/blockTypes.d.ts +3 -2
  35. package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +0 -17
  36. package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +26 -20
  37. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +18 -24
  38. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +0 -12
  39. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +37 -10
  40. package/types/src/extensions/SideMenu/MultipleNodeSelection.d.ts +24 -0
  41. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +79 -0
  42. package/types/src/extensions/SlashMenu/BaseSlashMenuItem.d.ts +5 -18
  43. package/types/src/extensions/SlashMenu/SlashMenuPlugin.d.ts +13 -0
  44. package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +1 -69
  45. package/types/src/extensions/SlashMenu/index.d.ts +2 -3
  46. package/types/src/index.d.ts +9 -8
  47. package/types/src/shared/BaseUiElementTypes.d.ts +7 -0
  48. package/types/src/shared/EditorElement.d.ts +0 -10
  49. package/types/src/shared/EventEmitter.d.ts +11 -0
  50. package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +2 -7
  51. package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +12 -43
  52. package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +0 -29
  53. package/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +0 -37
  54. package/src/extensions/FormattingToolbar/FormattingToolbarExtension.ts +0 -37
  55. package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +0 -18
  56. package/src/extensions/HyperlinkToolbar/HyperlinkMark.ts +0 -28
  57. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +0 -19
  58. package/src/extensions/SlashMenu/SlashMenuExtension.ts +0 -53
  59. package/src/extensions/SlashMenu/defaultSlashMenuItems.tsx +0 -195
  60. package/src/extensions/SlashMenu/index.ts +0 -5
  61. package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +0 -21
  62. package/types/src/CustomBlock.d.ts +0 -15
  63. package/types/src/extensions/Blocks/nodes/BlockContent/TableContent/TableCol.d.ts +0 -2
  64. package/types/src/extensions/Blocks/nodes/BlockContent/TableContent/TableContent.d.ts +0 -2
  65. package/types/src/extensions/Blocks/nodes/BlockContent/TableContent/TableRow.d.ts +0 -2
  66. package/types/src/extensions/Placeholder/localisation/index.d.ts +0 -2
  67. package/types/src/extensions/Placeholder/localisation/translation.d.ts +0 -51
  68. /package/src/extensions/{DraggableBlocks → SideMenu}/MultipleNodeSelection.ts +0 -0
@@ -1,50 +1,26 @@
1
- import {
2
- Editor,
3
- isNodeSelection,
4
- isTextSelection,
5
- posToDOMRect,
6
- } from "@tiptap/core";
1
+ import { isNodeSelection, isTextSelection, posToDOMRect } from "@tiptap/core";
7
2
  import { EditorState, Plugin, PluginKey } from "prosemirror-state";
8
3
  import { EditorView } from "prosemirror-view";
9
- import { BlockNoteEditor, BlockSchema } from "../..";
10
4
  import {
11
- FormattingToolbar,
12
- FormattingToolbarFactory,
13
- FormattingToolbarStaticParams,
14
- } from "./FormattingToolbarFactoryTypes";
15
-
16
- // Same as TipTap bubblemenu plugin, but with these changes:
17
- // https://github.com/ueberdosis/tiptap/pull/2596/files
18
- export interface FormattingToolbarPluginProps<BSchema extends BlockSchema> {
19
- pluginKey: PluginKey;
20
- tiptapEditor: Editor;
21
- editor: BlockNoteEditor<BSchema>;
22
- formattingToolbarFactory: FormattingToolbarFactory<BSchema>;
23
- }
5
+ BaseUiElementCallbacks,
6
+ BaseUiElementState,
7
+ BlockNoteEditor,
8
+ BlockSchema,
9
+ } from "../..";
10
+ import { EventEmitter } from "../../shared/EventEmitter";
24
11
 
25
- export type FormattingToolbarViewProps<BSchema extends BlockSchema> =
26
- FormattingToolbarPluginProps<BSchema> & {
27
- view: EditorView;
28
- };
12
+ export type FormattingToolbarCallbacks = BaseUiElementCallbacks;
29
13
 
30
- export class FormattingToolbarView<BSchema extends BlockSchema> {
31
- public editor: BlockNoteEditor<BSchema>;
32
- private ttEditor: Editor;
14
+ export type FormattingToolbarState = BaseUiElementState;
33
15
 
34
- public view: EditorView;
35
-
36
- public formattingToolbar: FormattingToolbar;
16
+ export class FormattingToolbarView<BSchema extends BlockSchema> {
17
+ private formattingToolbarState?: FormattingToolbarState;
18
+ public updateFormattingToolbar: () => void;
37
19
 
38
20
  public preventHide = false;
39
-
40
21
  public preventShow = false;
41
-
42
- public toolbarIsOpen = false;
43
-
44
22
  public prevWasEditable: boolean | null = null;
45
23
 
46
- private lastPosition: DOMRect | undefined;
47
-
48
24
  public shouldShow: (props: {
49
25
  view: EditorView;
50
26
  state: EditorState;
@@ -63,24 +39,31 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
63
39
  return !(!view.hasFocus() || empty || isEmptyTextBlock);
64
40
  };
65
41
 
66
- constructor({
67
- editor,
68
- tiptapEditor,
69
- formattingToolbarFactory,
70
- view,
71
- }: FormattingToolbarViewProps<BSchema>) {
72
- this.editor = editor;
73
- this.ttEditor = tiptapEditor;
74
- this.view = view;
42
+ constructor(
43
+ private readonly editor: BlockNoteEditor<BSchema>,
44
+ private readonly pmView: EditorView,
45
+ updateFormattingToolbar: (
46
+ formattingToolbarState: FormattingToolbarState
47
+ ) => void
48
+ ) {
49
+ this.updateFormattingToolbar = () => {
50
+ if (!this.formattingToolbarState) {
51
+ throw new Error(
52
+ "Attempting to update uninitialized formatting toolbar"
53
+ );
54
+ }
55
+
56
+ updateFormattingToolbar(this.formattingToolbarState);
57
+ };
75
58
 
76
- this.formattingToolbar = formattingToolbarFactory(this.getStaticParams());
59
+ pmView.dom.addEventListener("mousedown", this.viewMousedownHandler);
60
+ pmView.dom.addEventListener("mouseup", this.viewMouseupHandler);
61
+ pmView.dom.addEventListener("dragstart", this.dragstartHandler);
77
62
 
78
- this.view.dom.addEventListener("mousedown", this.viewMousedownHandler);
79
- this.view.dom.addEventListener("mouseup", this.viewMouseupHandler);
80
- this.view.dom.addEventListener("dragstart", this.dragstartHandler);
63
+ pmView.dom.addEventListener("focus", this.focusHandler);
64
+ pmView.dom.addEventListener("blur", this.blurHandler);
81
65
 
82
- this.ttEditor.on("focus", this.focusHandler);
83
- this.ttEditor.on("blur", this.blurHandler);
66
+ document.addEventListener("scroll", this.scrollHandler);
84
67
  }
85
68
 
86
69
  viewMousedownHandler = () => {
@@ -89,42 +72,54 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
89
72
 
90
73
  viewMouseupHandler = () => {
91
74
  this.preventShow = false;
92
- setTimeout(() => this.update(this.ttEditor.view));
75
+ setTimeout(() => this.update(this.pmView));
93
76
  };
94
77
 
78
+ // For dragging the whole editor.
95
79
  dragstartHandler = () => {
96
- this.formattingToolbar.hide();
97
- this.toolbarIsOpen = false;
80
+ if (this.formattingToolbarState?.show) {
81
+ this.formattingToolbarState.show = false;
82
+ this.updateFormattingToolbar();
83
+ }
98
84
  };
99
85
 
100
86
  focusHandler = () => {
101
87
  // we use `setTimeout` to make sure `selection` is already updated
102
- setTimeout(() => this.update(this.ttEditor.view));
88
+ setTimeout(() => this.update(this.pmView));
103
89
  };
104
90
 
105
- blurHandler = ({ event }: { event: FocusEvent }) => {
91
+ blurHandler = (event: FocusEvent) => {
106
92
  if (this.preventHide) {
107
93
  this.preventHide = false;
108
94
 
109
95
  return;
110
96
  }
111
97
 
98
+ const editorWrapper = this.pmView.dom.parentElement!;
99
+
112
100
  // Checks if the focus is moving to an element outside the editor. If it is,
113
101
  // the toolbar is hidden.
114
102
  if (
115
103
  // An element is clicked.
116
104
  event &&
117
105
  event.relatedTarget &&
118
- // Element is outside the toolbar.
119
- (this.formattingToolbar.element === (event.relatedTarget as Node) ||
120
- this.formattingToolbar.element?.contains(event.relatedTarget as Node))
106
+ // Element is inside the editor.
107
+ (editorWrapper === (event.relatedTarget as Node) ||
108
+ editorWrapper.contains(event.relatedTarget as Node))
121
109
  ) {
122
110
  return;
123
111
  }
124
112
 
125
- if (this.toolbarIsOpen) {
126
- this.formattingToolbar.hide();
127
- this.toolbarIsOpen = false;
113
+ if (this.formattingToolbarState?.show) {
114
+ this.formattingToolbarState.show = false;
115
+ this.updateFormattingToolbar();
116
+ }
117
+ };
118
+
119
+ scrollHandler = () => {
120
+ if (this.formattingToolbarState?.show) {
121
+ this.formattingToolbarState.referencePos = this.getSelectionBoundingBox();
122
+ this.updateFormattingToolbar();
128
123
  }
129
124
  };
130
125
 
@@ -156,53 +151,48 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
156
151
  to,
157
152
  });
158
153
 
159
- // Checks if menu should be shown.
154
+ // Checks if menu should be shown/updated.
160
155
  if (
161
156
  this.editor.isEditable &&
162
- !this.toolbarIsOpen &&
163
157
  !this.preventShow &&
164
158
  (shouldShow || this.preventHide)
165
159
  ) {
166
- this.formattingToolbar.render({}, true);
167
- this.toolbarIsOpen = true;
160
+ this.formattingToolbarState = {
161
+ show: true,
162
+ referencePos: this.getSelectionBoundingBox(),
163
+ };
168
164
 
169
- return;
170
- }
165
+ this.updateFormattingToolbar();
171
166
 
172
- // Checks if menu should be updated.
173
- if (
174
- this.toolbarIsOpen &&
175
- !this.preventShow &&
176
- (shouldShow || this.preventHide)
177
- ) {
178
- this.formattingToolbar.render({}, false);
179
167
  return;
180
168
  }
181
169
 
182
170
  // Checks if menu should be hidden.
183
171
  if (
184
- this.toolbarIsOpen &&
172
+ this.formattingToolbarState?.show &&
185
173
  !this.preventHide &&
186
174
  (!shouldShow || this.preventShow || !this.editor.isEditable)
187
175
  ) {
188
- this.formattingToolbar.hide();
189
- this.toolbarIsOpen = false;
176
+ this.formattingToolbarState.show = false;
177
+ this.updateFormattingToolbar();
190
178
 
191
179
  return;
192
180
  }
193
181
  }
194
182
 
195
183
  destroy() {
196
- this.view.dom.removeEventListener("mousedown", this.viewMousedownHandler);
197
- this.view.dom.removeEventListener("mouseup", this.viewMouseupHandler);
198
- this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
184
+ this.pmView.dom.removeEventListener("mousedown", this.viewMousedownHandler);
185
+ this.pmView.dom.removeEventListener("mouseup", this.viewMouseupHandler);
186
+ this.pmView.dom.removeEventListener("dragstart", this.dragstartHandler);
199
187
 
200
- this.ttEditor.off("focus", this.focusHandler);
201
- this.ttEditor.off("blur", this.blurHandler);
188
+ this.pmView.dom.removeEventListener("focus", this.focusHandler);
189
+ this.pmView.dom.removeEventListener("blur", this.blurHandler);
190
+
191
+ document.removeEventListener("scroll", this.scrollHandler);
202
192
  }
203
193
 
204
194
  getSelectionBoundingBox() {
205
- const { state } = this.ttEditor.view;
195
+ const { state } = this.pmView;
206
196
  const { selection } = state;
207
197
 
208
198
  // support for CellSelections
@@ -211,44 +201,41 @@ export class FormattingToolbarView<BSchema extends BlockSchema> {
211
201
  const to = Math.max(...ranges.map((range) => range.$to.pos));
212
202
 
213
203
  if (isNodeSelection(selection)) {
214
- const node = this.ttEditor.view.nodeDOM(from) as HTMLElement;
204
+ const node = this.pmView.nodeDOM(from) as HTMLElement;
215
205
 
216
206
  if (node) {
217
207
  return node.getBoundingClientRect();
218
208
  }
219
209
  }
220
210
 
221
- return posToDOMRect(this.ttEditor.view, from, to);
211
+ return posToDOMRect(this.pmView, from, to);
222
212
  }
213
+ }
223
214
 
224
- getStaticParams(): FormattingToolbarStaticParams<BSchema> {
225
- return {
226
- editor: this.editor,
227
- getReferenceRect: () => {
228
- if (!this.toolbarIsOpen) {
229
- if (this.lastPosition === undefined) {
230
- throw new Error(
231
- "Attempted to access selection reference rect before rendering formatting toolbar."
232
- );
233
- }
234
-
235
- return this.lastPosition;
236
- }
237
-
238
- const selectionBoundingBox = this.getSelectionBoundingBox();
239
- this.lastPosition = selectionBoundingBox;
240
-
241
- return selectionBoundingBox;
215
+ export const formattingToolbarPluginKey = new PluginKey(
216
+ "FormattingToolbarPlugin"
217
+ );
218
+
219
+ export class FormattingToolbarProsemirrorPlugin<
220
+ BSchema extends BlockSchema
221
+ > extends EventEmitter<any> {
222
+ private view: FormattingToolbarView<BSchema> | undefined;
223
+ public readonly plugin: Plugin;
224
+
225
+ constructor(editor: BlockNoteEditor<BSchema>) {
226
+ super();
227
+ this.plugin = new Plugin({
228
+ key: formattingToolbarPluginKey,
229
+ view: (editorView) => {
230
+ this.view = new FormattingToolbarView(editor, editorView, (state) => {
231
+ this.emit("update", state);
232
+ });
233
+ return this.view;
242
234
  },
243
- };
235
+ });
244
236
  }
245
- }
246
237
 
247
- export const createFormattingToolbarPlugin = <BSchema extends BlockSchema>(
248
- options: FormattingToolbarPluginProps<BSchema>
249
- ) => {
250
- return new Plugin({
251
- key: new PluginKey("FormattingToolbarPlugin"),
252
- view: (view) => new FormattingToolbarView({ view, ...options }),
253
- });
254
- };
238
+ public onUpdate(callback: (state: FormattingToolbarState) => void) {
239
+ return this.on("update", callback);
240
+ }
241
+ }