@blocknote/core 0.1.1 → 0.2.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 (171) hide show
  1. package/README.md +12 -6
  2. package/dist/blocknote.js +1425 -5114
  3. package/dist/blocknote.js.map +1 -1
  4. package/dist/blocknote.umd.cjs +1 -53
  5. package/dist/blocknote.umd.cjs.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/package.json +3 -16
  8. package/src/BlockNoteEditor.ts +54 -0
  9. package/src/BlockNoteExtensions.ts +52 -7
  10. package/src/assets/fonts-inter.css +92 -0
  11. package/src/editor.module.css +37 -0
  12. package/src/extensions/Blocks/BlockAttributes.ts +1 -3
  13. package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +71 -18
  14. package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +66 -0
  15. package/src/extensions/Blocks/index.ts +7 -3
  16. package/src/extensions/Blocks/nodes/Block.module.css +116 -74
  17. package/src/extensions/Blocks/nodes/Block.ts +415 -292
  18. package/src/extensions/Blocks/nodes/BlockGroup.ts +6 -6
  19. package/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.ts +84 -0
  20. package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +177 -0
  21. package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts +77 -0
  22. package/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.ts +34 -0
  23. package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +20 -0
  24. package/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +27 -9
  25. package/src/extensions/DraggableBlocks/{DraggableBlocksPlugin.tsx → DraggableBlocksPlugin.ts} +227 -147
  26. package/src/extensions/FormattingToolbar/FormattingToolbarExtension.ts +29 -0
  27. package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +35 -0
  28. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +308 -0
  29. package/src/extensions/HyperlinkToolbar/HyperlinkMark.ts +28 -0
  30. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +19 -0
  31. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +251 -0
  32. package/src/extensions/Placeholder/PlaceholderExtension.ts +2 -2
  33. package/src/extensions/SlashMenu/SlashMenuExtension.ts +9 -1
  34. package/src/extensions/SlashMenu/SlashMenuItem.ts +1 -3
  35. package/src/extensions/SlashMenu/defaultCommands.tsx +33 -22
  36. package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
  37. package/src/extensions/UniqueID/UniqueID.ts +14 -12
  38. package/src/index.ts +8 -4
  39. package/src/shared/EditorElement.ts +10 -0
  40. package/src/shared/plugins/suggestion/SuggestionItem.ts +1 -8
  41. package/src/shared/plugins/suggestion/SuggestionPlugin.ts +222 -101
  42. package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +21 -0
  43. package/src/{utils.ts → shared/utils.ts} +0 -0
  44. package/types/src/BlockNoteEditor.d.ts +13 -0
  45. package/types/src/BlockNoteExtensions.d.ts +12 -1
  46. package/types/src/EditorElement.d.ts +7 -0
  47. package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +1 -1
  48. package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +19 -0
  49. package/types/src/extensions/Blocks/nodes/Block.d.ts +11 -19
  50. package/types/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.d.ts +8 -0
  51. package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.d.ts +8 -0
  52. package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.d.ts +2 -0
  53. package/types/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.d.ts +6 -0
  54. package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +4 -1
  55. package/types/src/extensions/BubbleMenu/BubbleMenuFactoryTypes.d.ts +27 -0
  56. package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +10 -12
  57. package/types/src/extensions/DraggableBlocks/BlockMenuFactoryTypes.d.ts +12 -0
  58. package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +14 -0
  59. package/types/src/extensions/DraggableBlocks/DragMenuFactoryTypes.d.ts +18 -0
  60. package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +9 -3
  61. package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +23 -1
  62. package/types/src/extensions/FormattingToolbar/FormattingToolbarExtension.d.ts +8 -0
  63. package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +23 -0
  64. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +43 -0
  65. package/types/src/extensions/HyperlinkToolbar/HyperlinkMark.d.ts +8 -0
  66. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +12 -0
  67. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +11 -0
  68. package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +2 -1
  69. package/types/src/extensions/Hyperlinks/HyperlinkMenuFactoryTypes.d.ts +11 -0
  70. package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +10 -1
  71. package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +3 -1
  72. package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +2 -4
  73. package/types/src/index.d.ts +8 -3
  74. package/types/src/shared/EditorElement.d.ts +6 -0
  75. package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +1 -6
  76. package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +15 -10
  77. package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +12 -0
  78. package/types/src/shared/utils.d.ts +2 -0
  79. package/types/src/utils.d.ts +2 -2
  80. package/src/BlockNoteTheme.ts +0 -150
  81. package/src/EditorContent.tsx +0 -2
  82. package/src/extensions/Blocks/OrderedListPlugin.ts +0 -46
  83. package/src/extensions/Blocks/commands/joinBackward.ts +0 -274
  84. package/src/extensions/Blocks/helpers/setBlockHeading.ts +0 -30
  85. package/src/extensions/Blocks/nodes/Content.ts +0 -63
  86. package/src/extensions/Blocks/rule.ts +0 -48
  87. package/src/extensions/BubbleMenu/BubbleMenuExtension.tsx +0 -36
  88. package/src/extensions/BubbleMenu/BubbleMenuPlugin.ts +0 -245
  89. package/src/extensions/BubbleMenu/component/BubbleMenu.tsx +0 -240
  90. package/src/extensions/BubbleMenu/component/LinkToolbarButton.tsx +0 -67
  91. package/src/extensions/DraggableBlocks/components/DragHandle.tsx +0 -102
  92. package/src/extensions/DraggableBlocks/components/DragHandleMenu.tsx +0 -19
  93. package/src/extensions/Hyperlinks/HyperlinkMark.tsx +0 -16
  94. package/src/extensions/Hyperlinks/HyperlinkMenuPlugin.tsx +0 -165
  95. package/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.tsx +0 -44
  96. package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.tsx +0 -34
  97. package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.tsx +0 -31
  98. package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.tsx +0 -40
  99. package/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.tsx +0 -37
  100. package/src/extensions/Hyperlinks/menus/HyperlinkMenu.tsx +0 -63
  101. package/src/fonts-inter.css +0 -94
  102. package/src/globals.css +0 -28
  103. package/src/root.module.css +0 -19
  104. package/src/shared/components/toolbar/Toolbar.tsx +0 -10
  105. package/src/shared/components/toolbar/ToolbarButton.tsx +0 -57
  106. package/src/shared/components/toolbar/ToolbarDropdown.tsx +0 -35
  107. package/src/shared/components/toolbar/ToolbarDropdownItem.tsx +0 -35
  108. package/src/shared/components/toolbar/ToolbarDropdownTarget.tsx +0 -31
  109. package/src/shared/components/tooltip/TooltipContent.module.css +0 -15
  110. package/src/shared/components/tooltip/TooltipContent.tsx +0 -23
  111. package/src/shared/hooks/useEditorForceUpdate.tsx +0 -30
  112. package/src/shared/plugins/suggestion/SuggestionListReactRenderer.tsx +0 -236
  113. package/src/shared/plugins/suggestion/components/SuggestionGroup.tsx +0 -47
  114. package/src/shared/plugins/suggestion/components/SuggestionGroupItem.tsx +0 -82
  115. package/src/shared/plugins/suggestion/components/SuggestionList.tsx +0 -92
  116. package/src/useEditor.ts +0 -51
  117. package/types/src/BlockNoteTheme.d.ts +0 -2
  118. package/types/src/EditorContent.d.ts +0 -1
  119. package/types/src/commands/indentation.d.ts +0 -2
  120. package/types/src/extensions/Blocks/OrderedListPlugin.d.ts +0 -2
  121. package/types/src/extensions/Blocks/commands/joinBackward.d.ts +0 -14
  122. package/types/src/extensions/Blocks/helpers/setBlockHeading.d.ts +0 -5
  123. package/types/src/extensions/Blocks/nodes/Content.d.ts +0 -5
  124. package/types/src/extensions/Blocks/rule.d.ts +0 -16
  125. package/types/src/extensions/BubbleMenu/component/BubbleMenu.d.ts +0 -5
  126. package/types/src/extensions/BubbleMenu/component/DropdownBlockItem.d.ts +0 -10
  127. package/types/src/extensions/BubbleMenu/component/LinkToolbarButton.d.ts +0 -11
  128. package/types/src/extensions/DraggableBlocks/components/DragHandle.d.ts +0 -12
  129. package/types/src/extensions/DraggableBlocks/components/DragHandleMenu.d.ts +0 -6
  130. package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.d.ts +0 -11
  131. package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.d.ts +0 -13
  132. package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.d.ts +0 -8
  133. package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.d.ts +0 -9
  134. package/types/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.d.ts +0 -12
  135. package/types/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.d.ts +0 -12
  136. package/types/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.d.ts +0 -10
  137. package/types/src/extensions/Hyperlinks/menus/HyperlinkMenu.d.ts +0 -21
  138. package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.d.ts +0 -39
  139. package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.d.ts +0 -1
  140. package/types/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.d.ts +0 -11
  141. package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInput.d.ts +0 -39
  142. package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInputStyles.d.ts +0 -3
  143. package/types/src/extensions/Hyperlinks/menus/helpers/ToolbarComponent.d.ts +0 -13
  144. package/types/src/extensions/helpers/formatKeyboardShortcut.d.ts +0 -1
  145. package/types/src/lib/atlaskit/browser.d.ts +0 -12
  146. package/types/src/nodes/ChildgroupNode.d.ts +0 -28
  147. package/types/src/nodes/patchNodes.d.ts +0 -1
  148. package/types/src/plugins/TreeViewPlugin/index.d.ts +0 -2
  149. package/types/src/plugins/animation.d.ts +0 -2
  150. package/types/src/react/BlockNoteComposer.d.ts +0 -17
  151. package/types/src/react/BlockNotePlugin.d.ts +0 -1
  152. package/types/src/react/index.d.ts +0 -3
  153. package/types/src/react/useBlockNoteSetup.d.ts +0 -2
  154. package/types/src/registerBlockNote.d.ts +0 -2
  155. package/types/src/shared/components/toolbar/SimpleToolbarButton.d.ts +0 -15
  156. package/types/src/shared/components/toolbar/SimpleToolbarDropdown.d.ts +0 -11
  157. package/types/src/shared/components/toolbar/SimpleToolbarDropdownItem.d.ts +0 -11
  158. package/types/src/shared/components/toolbar/Toolbar.d.ts +0 -4
  159. package/types/src/shared/components/toolbar/ToolbarButton.d.ts +0 -15
  160. package/types/src/shared/components/toolbar/ToolbarDropdown.d.ts +0 -17
  161. package/types/src/shared/components/toolbar/ToolbarDropdownItem.d.ts +0 -11
  162. package/types/src/shared/components/toolbar/ToolbarDropdownTarget.d.ts +0 -8
  163. package/types/src/shared/components/toolbar/ToolbarSeparator.d.ts +0 -2
  164. package/types/src/shared/components/tooltip/TooltipContent.d.ts +0 -15
  165. package/types/src/shared/hooks/useEditorForceUpdate.d.ts +0 -2
  166. package/types/src/shared/plugins/suggestion/SuggestionListReactRenderer.d.ts +0 -71
  167. package/types/src/shared/plugins/suggestion/components/SuggestionGroup.d.ts +0 -23
  168. package/types/src/shared/plugins/suggestion/components/SuggestionGroupItem.d.ts +0 -9
  169. package/types/src/shared/plugins/suggestion/components/SuggestionList.d.ts +0 -11
  170. package/types/src/themes/BlockNoteEditorTheme.d.ts +0 -11
  171. package/types/src/useEditor.d.ts +0 -11
@@ -1,12 +1,19 @@
1
- import { NodeSelection, Plugin, PluginKey, Selection } from "prosemirror-state";
1
+ import { Editor } from "@tiptap/core";
2
2
  import { Node } from "prosemirror-model";
3
+ import { NodeSelection, Plugin, PluginKey, Selection } from "prosemirror-state";
3
4
  import * as pv from "prosemirror-view";
4
5
  import { EditorView } from "prosemirror-view";
5
- import ReactDOM from "react-dom";
6
- import { DragHandle } from "./components/DragHandle";
7
- import { MantineProvider } from "@mantine/core";
8
- import { BlockNoteTheme } from "../../BlockNoteTheme";
9
6
  import { MultipleNodeSelection } from "../Blocks/MultipleNodeSelection";
7
+ import { DraggableBlocksOptions } from "./DraggableBlocksExtension";
8
+ import {
9
+ BlockSideMenu,
10
+ BlockSideMenuDynamicParams,
11
+ BlockSideMenuFactory,
12
+ BlockSideMenuStaticParams,
13
+ } from "./BlockSideMenuFactoryTypes";
14
+ import { getBlockInfoFromPos } from "../Blocks/helpers/getBlockInfoFromPos";
15
+ import { SlashMenuPluginKey } from "../SlashMenu/SlashMenuExtension";
16
+ import styles from "../../editor.module.css";
10
17
 
11
18
  const serializeForClipboard = (pv as any).__serializeForClipboard;
12
19
  // code based on https://github.com/ueberdosis/tiptap/issues/323#issuecomment-506637799
@@ -91,10 +98,7 @@ function blockPositionFromCoords(
91
98
  return null;
92
99
  }
93
100
 
94
- function blockPositionsFromSelection(
95
- selection: Selection,
96
- doc: Node
97
- ) {
101
+ function blockPositionsFromSelection(selection: Selection, doc: Node) {
98
102
  // Absolute positions just before the first block spanned by the selection, and just after the last block. Having the
99
103
  // selection start and end just before and just after the target blocks ensures no whitespace/line breaks are left
100
104
  // behind after dragging & dropping them.
@@ -107,9 +111,9 @@ function blockPositionsFromSelection(
107
111
  // in. If the anchor should update but the head shouldn't and vice versa, it means the user selection is outside a
108
112
  // block content node, which should never happen.
109
113
  const selectionStartInBlockContent =
110
- doc.resolve(selection.from).node().type.name === "content";
114
+ doc.resolve(selection.from).node().type.spec.group === "blockContent";
111
115
  const selectionEndInBlockContent =
112
- doc.resolve(selection.to).node().type.name === "content";
116
+ doc.resolve(selection.to).node().type.spec.group === "blockContent";
113
117
 
114
118
  // Ensures that entire outermost nodes are selected if the selection spans multiple nesting levels.
115
119
  const minDepth = Math.min(selection.$anchor.depth, selection.$head.depth);
@@ -164,6 +168,7 @@ function setDragImage(view: EditorView, from: number, to = from) {
164
168
 
165
169
  // dataTransfer.setDragImage(element) only works if element is attached to the DOM.
166
170
  dragImageElement = parentClone;
171
+ dragImageElement.className = styles.dragPreview;
167
172
  document.body.appendChild(dragImageElement);
168
173
  }
169
174
 
@@ -220,157 +225,232 @@ function dragStart(e: DragEvent, view: EditorView) {
220
225
  }
221
226
  }
222
227
 
223
- export const createDraggableBlocksPlugin = () => {
224
- let dropElement: HTMLElement | undefined;
228
+ export type BlockMenuViewProps = {
229
+ editor: Editor;
230
+ blockMenuFactory: BlockSideMenuFactory;
231
+ horizontalPosAnchoredAtRoot: boolean;
232
+ };
225
233
 
226
- const WIDTH = 48;
234
+ export class BlockMenuView {
235
+ editor: Editor;
227
236
 
228
237
  // When true, the drag handle with be anchored at the same level as root elements
229
238
  // When false, the drag handle with be just to the left of the element
230
- const horizontalPosAnchoredAtRoot = true;
239
+ horizontalPosAnchoredAtRoot: boolean;
231
240
 
232
- let menuShown = false;
233
- let addClicked = false;
241
+ blockMenu: BlockSideMenu;
234
242
 
235
- const onShow = () => {
236
- menuShown = true;
237
- };
238
- const onHide = () => {
239
- menuShown = false;
240
- };
241
- const onAddClicked = () => {
242
- addClicked = true;
243
- };
243
+ hoveredBlock: HTMLElement | undefined;
244
244
 
245
- return new Plugin({
246
- key: new PluginKey("DraggableBlocksPlugin"),
247
- view(editorView) {
248
- dropElement = document.createElement("div");
249
- dropElement.setAttribute("draggable", "true");
250
- dropElement.style.position = "absolute";
251
- dropElement.style.height = "24px"; // default height
252
- document.body.append(dropElement);
253
-
254
- dropElement.addEventListener("dragstart", (e) =>
255
- dragStart(e, editorView)
256
- );
257
- dropElement.addEventListener("dragend", () => unsetDragImage());
245
+ menuOpen = false;
246
+ menuFrozen = false;
247
+
248
+ constructor({
249
+ editor,
250
+ blockMenuFactory,
251
+ horizontalPosAnchoredAtRoot,
252
+ }: BlockMenuViewProps) {
253
+ this.editor = editor;
254
+ this.horizontalPosAnchoredAtRoot = horizontalPosAnchoredAtRoot;
255
+
256
+ this.blockMenu = blockMenuFactory(this.getStaticParams());
258
257
 
259
- return {
260
- // update(view, prevState) {},
261
- destroy() {
262
- if (!dropElement) {
263
- throw new Error("unexpected");
258
+ // Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
259
+ document.body.addEventListener(
260
+ "mousemove",
261
+ (event) => {
262
+ if (this.menuFrozen) {
263
+ return;
264
+ }
265
+
266
+ // Gets block at mouse cursor's vertical position.
267
+ const coords = {
268
+ left: this.editor.view.dom.clientWidth / 2, // take middle of editor
269
+ top: event.clientY,
270
+ };
271
+ const block = getDraggableBlockFromCoords(coords, this.editor.view);
272
+
273
+ // Closes the menu if the mouse cursor is beyond the editor vertically.
274
+ if (!block) {
275
+ if (this.menuOpen) {
276
+ this.menuOpen = false;
277
+ this.blockMenu.hide();
264
278
  }
265
- dropElement.parentNode!.removeChild(dropElement);
266
- dropElement = undefined;
267
- },
268
- };
269
- },
270
- props: {
271
- // handleDOMEvents: {
272
-
273
- // },
274
- // handleDOMEvents: {
275
- // dragend(view, event) {
276
- // // setTimeout(() => {
277
- // // let node = document.querySelector(".ProseMirror-hideselection");
278
- // // if (node) {
279
- // // node.classList.remove("ProseMirror-hideselection");
280
- // // }
281
- // // }, 50);
282
- // return true;
283
- // },
284
- handleKeyDown(_view, _event) {
285
- if (!dropElement) {
286
- throw new Error("unexpected");
279
+
280
+ return;
281
+ }
282
+
283
+ // Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
284
+ if (
285
+ this.menuOpen &&
286
+ this.hoveredBlock?.hasAttribute("data-id") &&
287
+ this.hoveredBlock?.getAttribute("data-id") === block.id
288
+ ) {
289
+ return;
290
+ }
291
+
292
+ // Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
293
+ const blockContent = block.node.firstChild as HTMLElement;
294
+ this.hoveredBlock = blockContent;
295
+
296
+ if (!blockContent) {
297
+ return;
298
+ }
299
+
300
+ // Shows or updates elements.
301
+ if (!this.menuOpen) {
302
+ this.menuOpen = true;
303
+ this.blockMenu.render(this.getDynamicParams(), true);
304
+ } else {
305
+ this.blockMenu.render(this.getDynamicParams(), false);
287
306
  }
288
- menuShown = false;
289
- addClicked = false;
290
- ReactDOM.render(<></>, dropElement);
291
- return false;
292
307
  },
293
- handleDOMEvents: {
294
- // drag(view, event) {
295
- // // event.dataTransfer!.;
296
- // return false;
297
- // },
298
- mouseleave(_view, _event: any) {
299
- if (!dropElement) {
300
- throw new Error("unexpected");
301
- }
302
- // TODO
303
- // dropElement.style.display = "none";
304
- return true;
305
- },
306
- mousedown(_view, _event: any) {
307
- if (!dropElement) {
308
- throw new Error("unexpected");
309
- }
310
- menuShown = false;
311
- addClicked = false;
312
- ReactDOM.render(<></>, dropElement);
313
- return false;
314
- },
315
- mousemove(view, event: any) {
316
- if (!dropElement) {
317
- throw new Error("unexpected");
318
- }
308
+ true
309
+ );
310
+
311
+ // Hides and unfreezes the menu whenever the user selects the editor with the mouse or presses a key.
312
+ // TODO: Better integration with suggestions menu and only editor scope?
313
+ document.body.addEventListener(
314
+ "mousedown",
315
+ (event) => {
316
+ if (this.blockMenu.element?.contains(event.target as HTMLElement)) {
317
+ return;
318
+ }
319
319
 
320
- if (menuShown || addClicked) {
321
- // The submenu is open, don't move draghandle
322
- // Or if the user clicked the add button
323
- return true;
324
- }
325
- const coords = {
326
- left: view.dom.clientWidth / 2, // take middle of editor
327
- top: event.clientY,
328
- };
329
- const block = getDraggableBlockFromCoords(coords, view);
330
-
331
- if (!block) {
332
- console.warn("Perhaps we should hide element?");
333
- return true;
334
- }
320
+ if (this.menuOpen) {
321
+ this.menuOpen = false;
322
+ this.blockMenu.hide();
323
+ }
335
324
 
336
- // I want the dim of the blocks content node
337
- // because if the block contains other blocks
338
- // Its dims change, moving the position of the drag handle
339
- const blockContent = block.node.firstChild as HTMLElement;
325
+ this.menuFrozen = false;
326
+ },
327
+ true
328
+ );
329
+ document.body.addEventListener(
330
+ "keydown",
331
+ () => {
332
+ if (this.menuOpen) {
333
+ this.menuOpen = false;
334
+ this.blockMenu.hide();
335
+ }
340
336
 
341
- if (!blockContent) {
342
- return true;
343
- }
337
+ this.menuFrozen = false;
338
+ },
339
+ true
340
+ );
341
+ }
342
+
343
+ destroy() {
344
+ if (this.menuOpen) {
345
+ this.menuOpen = false;
346
+ this.blockMenu.hide();
347
+ }
348
+ }
349
+
350
+ addBlock() {
351
+ this.menuOpen = false;
352
+ this.menuFrozen = true;
353
+ this.blockMenu.hide();
354
+
355
+ const blockBoundingBox = this.hoveredBlock!.getBoundingClientRect();
356
+
357
+ const pos = this.editor.view.posAtCoords({
358
+ left: blockBoundingBox.left,
359
+ top: blockBoundingBox.top,
360
+ });
361
+ if (!pos) {
362
+ return;
363
+ }
364
+
365
+ const blockInfo = getBlockInfoFromPos(this.editor.state.doc, pos.pos);
366
+ if (blockInfo === undefined) {
367
+ return;
368
+ }
369
+
370
+ const { contentNode, endPos } = blockInfo;
344
371
 
345
- const rect = absoluteRect(blockContent);
346
- const win = block.node.ownerDocument.defaultView!;
347
- const dropElementRect = dropElement.getBoundingClientRect();
348
- const left =
349
- (horizontalPosAnchoredAtRoot ? getHorizontalAnchor() : rect.left) -
350
- WIDTH +
351
- win.pageXOffset;
352
- rect.top +=
353
- rect.height / 2 - dropElementRect.height / 2 + win.pageYOffset;
354
-
355
- dropElement.style.left = left + "px";
356
- dropElement.style.top = rect.top + "px";
357
-
358
- ReactDOM.render(
359
- <MantineProvider theme={BlockNoteTheme}>
360
- <DragHandle
361
- onShow={onShow}
362
- onHide={onHide}
363
- onAddClicked={onAddClicked}
364
- key={block.id + ""}
365
- view={view}
366
- coords={coords}
367
- />
368
- </MantineProvider>,
369
- dropElement
370
- );
371
- return true;
372
- },
372
+ // Creates a new block if current one is not empty for the suggestion menu to open in.
373
+ if (contentNode.textContent.length !== 0) {
374
+ const newBlockInsertionPos = endPos + 1;
375
+ const newBlockContentPos = newBlockInsertionPos + 2;
376
+
377
+ this.editor
378
+ .chain()
379
+ .BNCreateBlock(newBlockInsertionPos)
380
+ .BNSetContentType(newBlockContentPos, { name: "textContent" })
381
+ .setTextSelection(newBlockContentPos)
382
+ .run();
383
+ }
384
+
385
+ // Focuses and activates the suggestion menu.
386
+ this.editor.view.focus();
387
+ this.editor.view.dispatch(
388
+ this.editor.view.state.tr.scrollIntoView().setMeta(SlashMenuPluginKey, {
389
+ // TODO import suggestion plugin key
390
+ activate: true,
391
+ type: "drag",
392
+ })
393
+ );
394
+ }
395
+
396
+ deleteBlock() {
397
+ this.menuOpen = false;
398
+ this.blockMenu.hide();
399
+
400
+ const blockBoundingBox = this.hoveredBlock!.getBoundingClientRect();
401
+
402
+ const pos = this.editor.view.posAtCoords({
403
+ left: blockBoundingBox.left,
404
+ top: blockBoundingBox.top,
405
+ });
406
+ if (!pos) {
407
+ return;
408
+ }
409
+
410
+ this.editor.commands.BNDeleteBlock(pos.pos);
411
+ }
412
+
413
+ getStaticParams(): BlockSideMenuStaticParams {
414
+ return {
415
+ addBlock: () => this.addBlock(),
416
+ deleteBlock: () => this.deleteBlock(),
417
+ blockDragStart: (event: DragEvent) => dragStart(event, this.editor.view),
418
+ blockDragEnd: () => unsetDragImage(),
419
+ freezeMenu: () => {
420
+ this.menuFrozen = true;
421
+ },
422
+ unfreezeMenu: () => {
423
+ this.menuFrozen = false;
373
424
  },
374
- },
425
+ };
426
+ }
427
+
428
+ getDynamicParams(): BlockSideMenuDynamicParams {
429
+ const blockBoundingBox = this.hoveredBlock!.getBoundingClientRect();
430
+
431
+ return {
432
+ blockBoundingBox: new DOMRect(
433
+ this.horizontalPosAnchoredAtRoot
434
+ ? getHorizontalAnchor()
435
+ : blockBoundingBox.x,
436
+ blockBoundingBox.y,
437
+ blockBoundingBox.width,
438
+ blockBoundingBox.height
439
+ ),
440
+ };
441
+ }
442
+ }
443
+
444
+ export const createDraggableBlocksPlugin = (
445
+ options: DraggableBlocksOptions
446
+ ) => {
447
+ return new Plugin({
448
+ key: new PluginKey("DraggableBlocksPlugin"),
449
+ view: () =>
450
+ new BlockMenuView({
451
+ editor: options.editor,
452
+ blockMenuFactory: options.blockSideMenuFactory,
453
+ horizontalPosAnchoredAtRoot: true,
454
+ }),
375
455
  });
376
456
  };
@@ -0,0 +1,29 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { PluginKey } from "prosemirror-state";
3
+ import { FormattingToolbarFactory } from "./FormattingToolbarFactoryTypes";
4
+ import { createFormattingToolbarPlugin } from "./FormattingToolbarPlugin";
5
+
6
+ /**
7
+ * The menu that is displayed when selecting a piece of text.
8
+ */
9
+ export const FormattingToolbarExtension = Extension.create<{
10
+ formattingToolbarFactory: FormattingToolbarFactory;
11
+ }>({
12
+ name: "FormattingToolbarExtension",
13
+
14
+ addProseMirrorPlugins() {
15
+ if (!this.options.formattingToolbarFactory) {
16
+ throw new Error(
17
+ "UI Element factory not defined for FormattingToolbarExtension"
18
+ );
19
+ }
20
+
21
+ return [
22
+ createFormattingToolbarPlugin({
23
+ editor: this.editor,
24
+ formattingToolbarFactory: this.options.formattingToolbarFactory,
25
+ pluginKey: new PluginKey("FormattingToolbarPlugin"),
26
+ }),
27
+ ];
28
+ },
29
+ });
@@ -0,0 +1,35 @@
1
+ import { EditorElement, ElementFactory } from "../../shared/EditorElement";
2
+ import { BlockContentType } from "../Blocks/nodes/Block";
3
+
4
+ export type FormattingToolbarStaticParams = {
5
+ toggleBold: () => void;
6
+ toggleItalic: () => void;
7
+ toggleUnderline: () => void;
8
+ toggleStrike: () => void;
9
+ setHyperlink: (url: string, text?: string) => void;
10
+
11
+ setBlockType: (type: BlockContentType) => void;
12
+ };
13
+
14
+ export type FormattingToolbarDynamicParams = {
15
+ boldIsActive: boolean;
16
+ italicIsActive: boolean;
17
+ underlineIsActive: boolean;
18
+ strikeIsActive: boolean;
19
+ hyperlinkIsActive: boolean;
20
+ activeHyperlinkUrl: string;
21
+ activeHyperlinkText: string;
22
+
23
+ // BlockContentType is mostly used to set a block's type, so the attr field is optional as block content types have
24
+ // default values for attributes. However, it means that a block type's attributes field will never be undefined due to
25
+ // these default values, which the Required type enforces.
26
+ activeBlockType: Required<BlockContentType>;
27
+
28
+ selectionBoundingBox: DOMRect;
29
+ };
30
+
31
+ export type FormattingToolbar = EditorElement<FormattingToolbarDynamicParams>;
32
+ export type FormattingToolbarFactory = ElementFactory<
33
+ FormattingToolbarStaticParams,
34
+ FormattingToolbarDynamicParams
35
+ >;