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