@blocknote/core 0.16.0 → 0.17.1

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 (248) hide show
  1. package/dist/blocknote.js +3292 -2755
  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/webpack-stats.json +1 -1
  6. package/package.json +5 -2
  7. package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +3087 -0
  8. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.test.ts +132 -0
  9. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +71 -0
  10. package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +2276 -0
  11. package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts +131 -0
  12. package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts +103 -0
  13. package/src/api/blockManipulation/commands/moveBlock/__snapshots__/moveBlock.test.ts.snap +3767 -0
  14. package/src/api/blockManipulation/commands/moveBlock/moveBlock.test.ts +192 -0
  15. package/src/api/blockManipulation/commands/moveBlock/moveBlock.ts +178 -0
  16. package/src/api/blockManipulation/commands/removeBlocks/__snapshots__/removeBlocks.test.ts.snap +1136 -0
  17. package/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.ts +34 -0
  18. package/src/api/blockManipulation/commands/removeBlocks/removeBlocks.ts +100 -0
  19. package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +4931 -0
  20. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.ts +222 -0
  21. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +70 -0
  22. package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +2924 -0
  23. package/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts +136 -0
  24. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +48 -0
  25. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +8376 -0
  26. package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +300 -0
  27. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +199 -0
  28. package/src/api/blockManipulation/insertContentAt.ts +96 -0
  29. package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +316 -0
  30. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +53 -0
  31. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.ts +130 -0
  32. package/src/api/blockManipulation/setupTestEnv.ts +179 -0
  33. package/src/api/clipboard/__snapshots__/tableAllCells.html +1 -1
  34. package/src/api/clipboard/clipboard.test.ts +5 -6
  35. package/src/api/clipboard/fromClipboard/fileDropExtension.ts +8 -4
  36. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +11 -6
  37. package/src/api/clipboard/fromClipboard/pasteExtension.ts +8 -4
  38. package/src/api/clipboard/toClipboard/copyExtension.ts +113 -61
  39. package/src/api/exporters/html/__snapshots__/complex/misc/external.html +1 -1
  40. package/src/api/exporters/html/__snapshots__/lists/basic/external.html +1 -1
  41. package/src/api/exporters/html/__snapshots__/lists/nested/external.html +1 -1
  42. package/src/api/exporters/html/externalHTMLExporter.ts +42 -94
  43. package/src/api/exporters/html/htmlConversion.test.ts +19 -13
  44. package/src/api/exporters/html/internalHTMLSerializer.ts +21 -72
  45. package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +263 -0
  46. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +158 -0
  47. package/src/api/exporters/markdown/markdownExporter.test.ts +10 -10
  48. package/src/api/exporters/markdown/markdownExporter.ts +11 -7
  49. package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +2 -2
  50. package/src/api/getBlockInfoFromPos.ts +172 -90
  51. package/src/api/nodeConversions/blockToNode.ts +257 -0
  52. package/src/api/nodeConversions/fragmentToBlocks.ts +60 -0
  53. package/src/api/nodeConversions/nodeConversions.test.ts +9 -8
  54. package/src/api/nodeConversions/{nodeConversions.ts → nodeToBlock.ts} +20 -262
  55. package/src/api/parsers/html/parseHTML.test.ts +2 -2
  56. package/src/api/parsers/html/parseHTML.ts +8 -4
  57. package/src/api/parsers/html/util/nestedLists.test.ts +2 -2
  58. package/src/api/parsers/markdown/parseMarkdown.test.ts +2 -2
  59. package/src/api/parsers/markdown/parseMarkdown.ts +8 -4
  60. package/src/api/testUtil/cases/customBlocks.ts +11 -11
  61. package/src/api/testUtil/cases/customInlineContent.ts +6 -6
  62. package/src/api/testUtil/cases/customStyles.ts +6 -6
  63. package/src/api/testUtil/cases/defaultSchema.ts +4 -4
  64. package/src/api/testUtil/index.ts +6 -6
  65. package/src/api/testUtil/partialBlockTestUtil.ts +5 -5
  66. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +5 -5
  67. package/src/blocks/FileBlockContent/FileBlockContent.ts +4 -4
  68. package/src/blocks/FileBlockContent/fileBlockHelpers.ts +2 -2
  69. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +61 -39
  70. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +5 -5
  71. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +30 -18
  72. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +67 -33
  73. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +23 -19
  74. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +22 -24
  75. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +31 -19
  76. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +16 -11
  77. package/src/blocks/TableBlockContent/TableBlockContent.ts +4 -4
  78. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +5 -5
  79. package/src/blocks/defaultBlockHelpers.ts +4 -4
  80. package/src/blocks/defaultBlockTypeGuards.ts +5 -5
  81. package/src/blocks/defaultBlocks.ts +13 -13
  82. package/src/blocks/defaultProps.ts +1 -1
  83. package/src/editor/BlockNoteEditor.test.ts +14 -7
  84. package/src/editor/BlockNoteEditor.ts +82 -149
  85. package/src/editor/BlockNoteExtensions.ts +15 -11
  86. package/src/editor/BlockNoteSchema.ts +7 -7
  87. package/src/editor/BlockNoteTipTapEditor.ts +5 -3
  88. package/src/editor/cursorPositionTypes.ts +7 -2
  89. package/src/editor/selectionTypes.ts +6 -2
  90. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +1 -1
  91. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +1 -1
  92. package/src/extensions/FilePanel/FilePanelPlugin.ts +4 -4
  93. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +8 -4
  94. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +334 -0
  95. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +9 -4
  96. package/src/extensions/{NonEditableBlocks/NonEditableBlockPlugin.ts → NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts} +2 -2
  97. package/src/extensions/Placeholder/PlaceholderPlugin.ts +1 -1
  98. package/src/extensions/SideMenu/SideMenuPlugin.ts +72 -401
  99. package/src/extensions/SideMenu/dragging.ts +251 -0
  100. package/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts +1 -1
  101. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +8 -4
  102. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +8 -4
  103. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +19 -15
  104. package/src/extensions/TableHandles/TableHandlesPlugin.ts +11 -7
  105. package/src/extensions/TextColor/TextColorExtension.ts +1 -1
  106. package/src/extensions/TextColor/TextColorMark.ts +1 -1
  107. package/src/i18n/dictionary.ts +1 -1
  108. package/src/i18n/locales/ar.ts +1 -1
  109. package/src/i18n/locales/fr.ts +1 -1
  110. package/src/i18n/locales/hr.ts +308 -0
  111. package/src/i18n/locales/index.ts +15 -14
  112. package/src/i18n/locales/is.ts +1 -1
  113. package/src/i18n/locales/ja.ts +1 -1
  114. package/src/i18n/locales/ko.ts +1 -1
  115. package/src/i18n/locales/nl.ts +1 -1
  116. package/src/i18n/locales/pl.ts +1 -1
  117. package/src/i18n/locales/pt.ts +1 -1
  118. package/src/i18n/locales/ru.ts +1 -1
  119. package/src/i18n/locales/vi.ts +1 -1
  120. package/src/i18n/locales/zh.ts +1 -1
  121. package/src/index.ts +45 -44
  122. package/src/pm-nodes/BlockContainer.ts +3 -647
  123. package/src/pm-nodes/BlockGroup.ts +2 -2
  124. package/src/pm-nodes/index.ts +3 -3
  125. package/src/schema/blocks/createSpec.ts +8 -7
  126. package/src/schema/blocks/internal.ts +9 -9
  127. package/src/schema/blocks/types.ts +4 -4
  128. package/src/schema/index.ts +10 -10
  129. package/src/schema/inlineContent/createSpec.ts +9 -10
  130. package/src/schema/inlineContent/internal.ts +3 -3
  131. package/src/schema/inlineContent/types.ts +2 -2
  132. package/src/schema/styles/createSpec.ts +4 -3
  133. package/src/schema/styles/internal.ts +1 -1
  134. package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +4 -0
  135. package/types/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.d.ts +7 -0
  136. package/types/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.d.ts +1 -0
  137. package/types/src/api/blockManipulation/commands/moveBlock/moveBlock.d.ts +5 -0
  138. package/types/src/api/blockManipulation/commands/moveBlock/moveBlock.test.d.ts +1 -0
  139. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.d.ts +7 -0
  140. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.d.ts +1 -0
  141. package/types/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.d.ts +7 -0
  142. package/types/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.d.ts +1 -0
  143. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.d.ts +5 -0
  144. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.test.d.ts +1 -0
  145. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +11 -0
  146. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.test.d.ts +1 -0
  147. package/types/src/api/blockManipulation/insertContentAt.d.ts +6 -0
  148. package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.d.ts +5 -0
  149. package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.d.ts +1 -0
  150. package/types/src/api/blockManipulation/setupTestEnv.d.ts +492 -0
  151. package/types/src/api/clipboard/fromClipboard/fileDropExtension.d.ts +3 -3
  152. package/types/src/api/clipboard/fromClipboard/handleFileInsertion.d.ts +1 -1
  153. package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +2 -2
  154. package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +5 -5
  155. package/types/src/api/exporters/html/externalHTMLExporter.d.ts +7 -9
  156. package/types/src/api/exporters/html/internalHTMLSerializer.d.ts +6 -10
  157. package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +10 -0
  158. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +11 -0
  159. package/types/src/api/exporters/markdown/markdownExporter.d.ts +3 -3
  160. package/types/src/api/getBlockInfoFromPos.d.ts +63 -20
  161. package/types/src/api/nodeConversions/blockToNode.d.ts +15 -0
  162. package/types/src/api/nodeConversions/fragmentToBlocks.d.ts +7 -0
  163. package/types/src/api/nodeConversions/nodeToBlock.d.ts +16 -0
  164. package/types/src/api/parsers/html/parseHTML.d.ts +2 -2
  165. package/types/src/api/parsers/markdown/parseMarkdown.d.ts +2 -2
  166. package/types/src/api/testUtil/cases/customBlocks.d.ts +39 -39
  167. package/types/src/api/testUtil/cases/customInlineContent.d.ts +35 -35
  168. package/types/src/api/testUtil/cases/customStyles.d.ts +35 -35
  169. package/types/src/api/testUtil/cases/defaultSchema.d.ts +2 -2
  170. package/types/src/api/testUtil/index.d.ts +6 -6
  171. package/types/src/api/testUtil/partialBlockTestUtil.d.ts +4 -4
  172. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +4 -4
  173. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +4 -4
  174. package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +2 -2
  175. package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +2 -2
  176. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +4 -4
  177. package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -2
  178. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +2 -2
  179. package/types/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.d.ts +2 -2
  180. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -2
  181. package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -2
  182. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +2 -2
  183. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +4 -4
  184. package/types/src/blocks/defaultBlockHelpers.d.ts +3 -3
  185. package/types/src/blocks/defaultBlockTypeGuards.d.ts +4 -4
  186. package/types/src/blocks/defaultBlocks.d.ts +38 -38
  187. package/types/src/blocks/defaultProps.d.ts +1 -1
  188. package/types/src/editor/BlockNoteEditor.d.ts +28 -16
  189. package/types/src/editor/BlockNoteExtensions.d.ts +3 -3
  190. package/types/src/editor/BlockNoteSchema.d.ts +4 -4
  191. package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
  192. package/types/src/editor/cursorPositionTypes.d.ts +3 -2
  193. package/types/src/editor/selectionTypes.d.ts +2 -2
  194. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +1 -1
  195. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  196. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +4 -4
  197. package/types/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.d.ts +5 -0
  198. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +4 -4
  199. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +2 -0
  200. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +1 -1
  201. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +12 -28
  202. package/types/src/extensions/SideMenu/dragging.d.ts +17 -0
  203. package/types/src/extensions/SuggestionMenu/DefaultSuggestionItem.d.ts +1 -1
  204. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +4 -4
  205. package/types/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.d.ts +3 -3
  206. package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +4 -4
  207. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +4 -4
  208. package/types/src/extensions/TextColor/TextColorMark.d.ts +1 -1
  209. package/types/src/i18n/dictionary.d.ts +1 -1
  210. package/types/src/i18n/locales/ar.d.ts +1 -1
  211. package/types/src/i18n/locales/fr.d.ts +1 -1
  212. package/types/src/i18n/locales/hr.d.ts +239 -0
  213. package/types/src/i18n/locales/index.d.ts +15 -14
  214. package/types/src/i18n/locales/is.d.ts +1 -1
  215. package/types/src/i18n/locales/ja.d.ts +1 -1
  216. package/types/src/i18n/locales/ko.d.ts +1 -1
  217. package/types/src/i18n/locales/nl.d.ts +1 -1
  218. package/types/src/i18n/locales/pl.d.ts +1 -1
  219. package/types/src/i18n/locales/pt.d.ts +1 -1
  220. package/types/src/i18n/locales/ru.d.ts +1 -1
  221. package/types/src/i18n/locales/vi.d.ts +1 -1
  222. package/types/src/i18n/locales/zh.d.ts +1 -1
  223. package/types/src/index.d.ts +45 -44
  224. package/types/src/pm-nodes/BlockContainer.d.ts +2 -16
  225. package/types/src/pm-nodes/BlockGroup.d.ts +1 -1
  226. package/types/src/pm-nodes/index.d.ts +3 -3
  227. package/types/src/schema/blocks/createSpec.d.ts +5 -5
  228. package/types/src/schema/blocks/internal.d.ts +5 -5
  229. package/types/src/schema/blocks/types.d.ts +4 -4
  230. package/types/src/schema/index.d.ts +10 -10
  231. package/types/src/schema/inlineContent/createSpec.d.ts +3 -3
  232. package/types/src/schema/inlineContent/internal.d.ts +2 -2
  233. package/types/src/schema/inlineContent/types.d.ts +2 -2
  234. package/types/src/schema/styles/createSpec.d.ts +1 -1
  235. package/types/src/schema/styles/internal.d.ts +1 -1
  236. package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +0 -714
  237. package/src/api/blockManipulation/blockManipulation.test.ts +0 -292
  238. package/src/api/blockManipulation/blockManipulation.ts +0 -350
  239. package/src/api/exporters/html/util/sharedHTMLConversion.ts +0 -130
  240. package/src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts +0 -218
  241. package/src/api/getCurrentBlockContentType.ts +0 -14
  242. package/types/src/api/blockManipulation/blockManipulation.d.ts +0 -14
  243. package/types/src/api/exporters/html/util/sharedHTMLConversion.d.ts +0 -9
  244. package/types/src/api/exporters/html/util/simplifyBlocksRehypePlugin.d.ts +0 -16
  245. package/types/src/api/getCurrentBlockContentType.d.ts +0 -2
  246. package/types/src/api/nodeConversions/nodeConversions.d.ts +0 -24
  247. package/types/src/extensions/NonEditableBlocks/NonEditableBlockPlugin.d.ts +0 -2
  248. /package/types/src/api/blockManipulation/{blockManipulation.test.d.ts → commands/insertBlocks/insertBlocks.test.d.ts} +0 -0
@@ -1,21 +1,22 @@
1
1
  import { PluginView } from "@tiptap/pm/state";
2
- import { Node } from "prosemirror-model";
3
- import { NodeSelection, Plugin, PluginKey, Selection } from "prosemirror-state";
2
+ import { EditorState, Plugin, PluginKey } from "prosemirror-state";
4
3
  import { EditorView } from "prosemirror-view";
5
- import * as pmView from "prosemirror-view";
6
4
 
7
- import { createExternalHTMLExporter } from "../../api/exporters/html/externalHTMLExporter";
8
- import { cleanHTMLToMarkdown } from "../../api/exporters/markdown/markdownExporter";
9
- import { getBlockInfoFromPos } from "../../api/getBlockInfoFromPos";
10
- import { Block } from "../../blocks/defaultBlocks";
11
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
12
- import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
13
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
14
- import { EventEmitter } from "../../util/EventEmitter";
15
- import { initializeESMDependencies } from "../../util/esmDependencies";
16
- import { MultipleNodeSelection } from "./MultipleNodeSelection";
17
-
18
- let dragImageElement: Element | undefined;
5
+ import { Block } from "../../blocks/defaultBlocks.js";
6
+ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
7
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
8
+ import {
9
+ BlockSchema,
10
+ InlineContentSchema,
11
+ StyleSchema,
12
+ } from "../../schema/index.js";
13
+ import { EventEmitter } from "../../util/EventEmitter.js";
14
+ import { initializeESMDependencies } from "../../util/esmDependencies.js";
15
+ import {
16
+ dragStart,
17
+ getDraggableBlockFromElement,
18
+ unsetDragImage,
19
+ } from "./dragging.js";
19
20
 
20
21
  export type SideMenuState<
21
22
  BSchema extends BlockSchema,
@@ -26,230 +27,49 @@ export type SideMenuState<
26
27
  block: Block<BSchema, I, S>;
27
28
  };
28
29
 
29
- export function getDraggableBlockFromElement(
30
- element: Element,
30
+ const getBlockFromMousePos = (
31
+ mousePos: {
32
+ x: number;
33
+ y: number;
34
+ },
31
35
  view: EditorView
32
- ) {
33
- while (
34
- element &&
35
- element.parentElement &&
36
- element.parentElement !== view.dom &&
37
- element.getAttribute?.("data-node-type") !== "blockContainer"
38
- ) {
39
- element = element.parentElement;
40
- }
41
- if (element.getAttribute?.("data-node-type") !== "blockContainer") {
42
- return undefined;
43
- }
44
- return { node: element as HTMLElement, id: element.getAttribute("data-id")! };
45
- }
46
-
47
- function blockPositionFromElement(element: Element, view: EditorView) {
48
- const block = getDraggableBlockFromElement(element, view);
49
-
50
- if (block && block.node.nodeType === 1) {
51
- // TODO: this uses undocumented PM APIs? do we need this / let's add docs?
52
- const docView = (view as any).docView;
53
- const desc = docView.nearestDesc(block.node, true);
54
- if (!desc || desc === docView) {
55
- return null;
56
- }
57
- return desc.posBefore;
58
- }
59
- return null;
60
- }
61
-
62
- function blockPositionsFromSelection(selection: Selection, doc: Node) {
63
- // Absolute positions just before the first block spanned by the selection, and just after the last block. Having the
64
- // selection start and end just before and just after the target blocks ensures no whitespace/line breaks are left
65
- // behind after dragging & dropping them.
66
- let beforeFirstBlockPos: number;
67
- let afterLastBlockPos: number;
68
-
69
- // Even the user starts dragging blocks but drops them in the same place, the selection will still be moved just
70
- // before & just after the blocks spanned by the selection, and therefore doesn't need to change if they try to drag
71
- // the same blocks again. If this happens, the anchor & head move out of the block content node they were originally
72
- // in. If the anchor should update but the head shouldn't and vice versa, it means the user selection is outside a
73
- // block content node, which should never happen.
74
- const selectionStartInBlockContent =
75
- doc.resolve(selection.from).node().type.spec.group === "blockContent";
76
- const selectionEndInBlockContent =
77
- doc.resolve(selection.to).node().type.spec.group === "blockContent";
78
-
79
- // Ensures that entire outermost nodes are selected if the selection spans multiple nesting levels.
80
- const minDepth = Math.min(selection.$anchor.depth, selection.$head.depth);
81
-
82
- if (selectionStartInBlockContent && selectionEndInBlockContent) {
83
- // Absolute positions at the start of the first block in the selection and at the end of the last block. User
84
- // selections will always start and end in block content nodes, but we want the start and end positions of their
85
- // parent block nodes, which is why minDepth - 1 is used.
86
- const startFirstBlockPos = selection.$from.start(minDepth - 1);
87
- const endLastBlockPos = selection.$to.end(minDepth - 1);
88
-
89
- // Shifting start and end positions by one moves them just outside the first and last selected blocks.
90
- beforeFirstBlockPos = doc.resolve(startFirstBlockPos - 1).pos;
91
- afterLastBlockPos = doc.resolve(endLastBlockPos + 1).pos;
92
- } else {
93
- beforeFirstBlockPos = selection.from;
94
- afterLastBlockPos = selection.to;
95
- }
96
-
97
- return { from: beforeFirstBlockPos, to: afterLastBlockPos };
98
- }
99
-
100
- function setDragImage(view: EditorView, from: number, to = from) {
101
- if (from === to) {
102
- // Moves to position to be just after the first (and only) selected block.
103
- to += view.state.doc.resolve(from + 1).node().nodeSize;
104
- }
105
-
106
- // Parent element is cloned to remove all unselected children without affecting the editor content.
107
- const parentClone = view.domAtPos(from).node.cloneNode(true) as Element;
108
- const parent = view.domAtPos(from).node as Element;
109
-
110
- const getElementIndex = (parentElement: Element, targetElement: Element) =>
111
- Array.prototype.indexOf.call(parentElement.children, targetElement);
112
-
113
- const firstSelectedBlockIndex = getElementIndex(
114
- parent,
115
- // Expects from position to be just before the first selected block.
116
- view.domAtPos(from + 1).node.parentElement!
117
- );
118
- const lastSelectedBlockIndex = getElementIndex(
119
- parent,
120
- // Expects to position to be just after the last selected block.
121
- view.domAtPos(to - 1).node.parentElement!
122
- );
123
-
124
- for (let i = parent.childElementCount - 1; i >= 0; i--) {
125
- if (i > lastSelectedBlockIndex || i < firstSelectedBlockIndex) {
126
- parentClone.removeChild(parentClone.children[i]);
127
- }
128
- }
129
-
130
- // dataTransfer.setDragImage(element) only works if element is attached to the DOM.
131
- unsetDragImage(view.root);
132
- dragImageElement = parentClone;
133
-
134
- // TODO: This is hacky, need a better way of assigning classes to the editor so that they can also be applied to the
135
- // drag preview.
136
- const classes = view.dom.className.split(" ");
137
- const inheritedClasses = classes
138
- .filter(
139
- (className) =>
140
- className !== "ProseMirror" &&
141
- className !== "bn-root" &&
142
- className !== "bn-editor"
143
- )
144
- .join(" ");
145
-
146
- dragImageElement.className =
147
- dragImageElement.className + " bn-drag-preview " + inheritedClasses;
148
-
149
- if (view.root instanceof ShadowRoot) {
150
- view.root.appendChild(dragImageElement);
151
- } else {
152
- view.root.body.appendChild(dragImageElement);
153
- }
154
- }
155
-
156
- function unsetDragImage(rootEl: Document | ShadowRoot) {
157
- if (dragImageElement !== undefined) {
158
- if (rootEl instanceof ShadowRoot) {
159
- rootEl.removeChild(dragImageElement);
160
- } else {
161
- rootEl.body.removeChild(dragImageElement);
162
- }
163
-
164
- dragImageElement = undefined;
165
- }
166
- }
167
-
168
- function dragStart<
169
- BSchema extends BlockSchema,
170
- I extends InlineContentSchema,
171
- S extends StyleSchema
172
- >(
173
- e: { dataTransfer: DataTransfer | null; clientY: number },
174
- editor: BlockNoteEditor<BSchema, I, S>
175
- ) {
176
- if (!e.dataTransfer) {
36
+ ): { node: HTMLElement; id: string } | undefined => {
37
+ // Editor itself may have padding or other styling which affects
38
+ // size/position, so we get the boundingRect of the first child (i.e. the
39
+ // blockGroup that wraps all blocks in the editor) for more accurate side
40
+ // menu placement.
41
+ if (!view.dom.firstChild) {
177
42
  return;
178
43
  }
179
44
 
180
- const view = editor.prosemirrorView;
45
+ const editorBoundingBox = (
46
+ view.dom.firstChild as HTMLElement
47
+ ).getBoundingClientRect();
181
48
 
182
- const editorBoundingBox = view.dom.getBoundingClientRect();
49
+ // this.horizontalPosAnchor = editorBoundingBox.x;
183
50
 
51
+ // Gets block at mouse cursor's vertical position.
184
52
  const coords = {
185
53
  left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
186
- top: e.clientY,
54
+ top: mousePos.y,
187
55
  };
188
56
 
189
57
  const elements = view.root.elementsFromPoint(coords.left, coords.top);
190
- let blockEl = undefined;
58
+ let block = undefined;
191
59
 
192
60
  for (const element of elements) {
193
61
  if (view.dom.contains(element)) {
194
- blockEl = getDraggableBlockFromElement(element, view);
62
+ block = getDraggableBlockFromElement(element, view);
195
63
  break;
196
64
  }
197
65
  }
198
66
 
199
- if (!blockEl) {
200
- return;
201
- }
202
-
203
- const pos = blockPositionFromElement(blockEl.node, view);
204
- if (pos != null) {
205
- const selection = view.state.selection;
206
- const doc = view.state.doc;
207
-
208
- const { from, to } = blockPositionsFromSelection(selection, doc);
209
-
210
- const draggedBlockInSelection = from <= pos && pos < to;
211
- const multipleBlocksSelected =
212
- selection.$anchor.node() !== selection.$head.node() ||
213
- selection instanceof MultipleNodeSelection;
214
-
215
- if (draggedBlockInSelection && multipleBlocksSelected) {
216
- view.dispatch(
217
- view.state.tr.setSelection(MultipleNodeSelection.create(doc, from, to))
218
- );
219
- setDragImage(view, from, to);
220
- } else {
221
- view.dispatch(
222
- view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos))
223
- );
224
- setDragImage(view, pos);
225
- }
226
-
227
- const selectedSlice = view.state.selection.content();
228
- const schema = editor.pmSchema;
229
-
230
- const clipboardHML = (pmView as any).__serializeForClipboard(
231
- view,
232
- selectedSlice
233
- ).dom.innerHTML;
234
-
235
- const externalHTMLExporter = createExternalHTMLExporter(schema, editor);
236
- const externalHTML = externalHTMLExporter.exportProseMirrorFragment(
237
- selectedSlice.content,
238
- {}
239
- );
240
-
241
- const plainText = cleanHTMLToMarkdown(externalHTML);
242
-
243
- e.dataTransfer.clearData();
244
- e.dataTransfer.setData("blocknote/html", clipboardHML);
245
- e.dataTransfer.setData("text/html", externalHTML);
246
- e.dataTransfer.setData("text/plain", plainText);
247
- e.dataTransfer.effectAllowed = "move";
248
- e.dataTransfer.setDragImage(dragImageElement!, 0, 0);
249
- view.dragging = { slice: selectedSlice, move: true };
250
- }
251
- }
67
+ return block;
68
+ };
252
69
 
70
+ /**
71
+ * With the sidemenu plugin we can position a menu next to a hovered block.
72
+ */
253
73
  export class SideMenuView<
254
74
  BSchema extends BlockSchema,
255
75
  I extends InlineContentSchema,
@@ -259,20 +79,10 @@ export class SideMenuView<
259
79
  public state?: SideMenuState<BSchema, I, S>;
260
80
  public readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;
261
81
 
262
- private needUpdate = false;
263
82
  private mousePos: { x: number; y: number } | undefined;
264
83
 
265
- // When true, the drag handle with be anchored at the same level as root elements
266
- // When false, the drag handle with be just to the left of the element
267
- // TODO: Is there any case where we want this to be false?
268
- private horizontalPosAnchoredAtRoot: boolean;
269
- private horizontalPosAnchor: number | undefined;
270
-
271
84
  private hoveredBlock: HTMLElement | undefined;
272
85
 
273
- // Used to check if currently dragged content comes from this editor instance.
274
- public isDragging = false;
275
-
276
86
  public menuFrozen = false;
277
87
 
278
88
  constructor(
@@ -288,14 +98,6 @@ export class SideMenuView<
288
98
  emitUpdate(this.state);
289
99
  };
290
100
 
291
- this.horizontalPosAnchoredAtRoot = true;
292
-
293
- if (this.pmView.dom.firstChild) {
294
- this.horizontalPosAnchor = (
295
- this.pmView.dom.firstChild as HTMLElement
296
- ).getBoundingClientRect().x;
297
- }
298
-
299
101
  this.pmView.root.addEventListener(
300
102
  "drop",
301
103
  this.onDrop as EventListener,
@@ -306,7 +108,6 @@ export class SideMenuView<
306
108
  this.onDragOver as EventListener
307
109
  );
308
110
  initializeESMDependencies();
309
- this.pmView.dom.addEventListener("dragstart", this.onDragStart);
310
111
 
311
112
  // Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
312
113
  this.pmView.root.addEventListener(
@@ -315,64 +116,31 @@ export class SideMenuView<
315
116
  true
316
117
  );
317
118
 
318
- // Unfreezes the menu whenever the user clicks.
319
- this.pmView.dom.addEventListener("mousedown", this.onMouseDown);
320
119
  // Hides and unfreezes the menu whenever the user presses a key.
321
120
  this.pmView.root.addEventListener(
322
121
  "keydown",
323
122
  this.onKeyDown as EventListener,
324
123
  true
325
124
  );
326
-
327
- // Setting capture=true ensures that any parent container of the editor that
328
- // gets scrolled will trigger the scroll event. Scroll events do not bubble
329
- // and so won't propagate to the document by default.
330
- this.pmView.root.addEventListener("scroll", this.onScroll, true);
331
125
  }
332
126
 
333
- updateState = () => {
334
- if (this.menuFrozen || !this.mousePos) {
335
- return;
336
- }
127
+ updateState = (state: SideMenuState<BSchema, I, S>) => {
128
+ this.state = state;
129
+ this.emitUpdate(this.state);
130
+ };
337
131
 
338
- // Editor itself may have padding or other styling which affects
339
- // size/position, so we get the boundingRect of the first child (i.e. the
340
- // blockGroup that wraps all blocks in the editor) for more accurate side
341
- // menu placement.
342
- if (!this.pmView.dom.firstChild) {
132
+ updateStateFromMousePos = () => {
133
+ if (this.menuFrozen || !this.mousePos) {
343
134
  return;
344
135
  }
345
136
 
346
- const editorBoundingBox = (
347
- this.pmView.dom.firstChild as HTMLElement
348
- ).getBoundingClientRect();
349
-
350
- this.horizontalPosAnchor = editorBoundingBox.x;
351
-
352
- // Gets block at mouse cursor's vertical position.
353
- const coords = {
354
- left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
355
- top: this.mousePos.y,
356
- };
357
-
358
- const elements = this.pmView.root.elementsFromPoint(
359
- coords.left,
360
- coords.top
361
- );
362
- let block = undefined;
363
-
364
- for (const element of elements) {
365
- if (this.pmView.dom.contains(element)) {
366
- block = getDraggableBlockFromElement(element, this.pmView);
367
- break;
368
- }
369
- }
137
+ const block = getBlockFromMousePos(this.mousePos, this.pmView);
370
138
 
371
139
  // Closes the menu if the mouse cursor is beyond the editor vertically.
372
140
  if (!block || !this.editor.isEditable) {
373
141
  if (this.state?.show) {
374
142
  this.state.show = false;
375
- this.needUpdate = true;
143
+ this.updateState(this.state);
376
144
  }
377
145
 
378
146
  return;
@@ -396,16 +164,19 @@ export class SideMenuView<
396
164
  return;
397
165
  }
398
166
 
167
+ // TODO: needed?
168
+
399
169
  // Shows or updates elements.
400
170
  if (this.editor.isEditable) {
171
+ const editorBoundingBox = (
172
+ this.pmView.dom.firstChild as HTMLElement
173
+ ).getBoundingClientRect();
401
174
  const blockContentBoundingBox = blockContent.getBoundingClientRect();
402
175
 
403
- this.state = {
176
+ this.updateState({
404
177
  show: true,
405
178
  referencePos: new DOMRect(
406
- this.horizontalPosAnchoredAtRoot
407
- ? this.horizontalPosAnchor
408
- : blockContentBoundingBox.x,
179
+ editorBoundingBox.x,
409
180
  blockContentBoundingBox.y,
410
181
  blockContentBoundingBox.width,
411
182
  blockContentBoundingBox.height
@@ -413,18 +184,10 @@ export class SideMenuView<
413
184
  block: this.editor.getBlock(
414
185
  this.hoveredBlock!.getAttribute("data-id")!
415
186
  )!,
416
- };
417
- this.needUpdate = true;
187
+ });
418
188
  }
419
189
  };
420
190
 
421
- /**
422
- * Sets isDragging when dragging text.
423
- */
424
- onDragStart = () => {
425
- this.isDragging = true;
426
- };
427
-
428
191
  /**
429
192
  * If the event is outside the editor contents,
430
193
  * we dispatch a fake event, so that we can still drop the content
@@ -433,7 +196,10 @@ export class SideMenuView<
433
196
  onDrop = (event: DragEvent) => {
434
197
  this.editor._tiptapEditor.commands.blur();
435
198
 
436
- if ((event as any).synthetic || !this.isDragging) {
199
+ if (
200
+ (event as any).synthetic ||
201
+ !event.dataTransfer?.types.includes("blocknote/html")
202
+ ) {
437
203
  return;
438
204
  }
439
205
 
@@ -442,8 +208,6 @@ export class SideMenuView<
442
208
  top: event.clientY,
443
209
  });
444
210
 
445
- this.isDragging = false;
446
-
447
211
  if (!pos || pos.inside === -1) {
448
212
  const evt = new Event("drop", event) as any;
449
213
  const editorBoundingBox = (
@@ -472,7 +236,10 @@ export class SideMenuView<
472
236
  * when dragging / dropping to the side of the editor
473
237
  */
474
238
  onDragOver = (event: DragEvent) => {
475
- if ((event as any).synthetic || !this.isDragging) {
239
+ if (
240
+ (event as any).synthetic ||
241
+ !event.dataTransfer?.types.includes("blocknote/html")
242
+ ) {
476
243
  return;
477
244
  }
478
245
  const pos = this.pmView.posAtCoords({
@@ -500,15 +267,6 @@ export class SideMenuView<
500
267
  // Typing in editor should hide side menu
501
268
  this.state.show = false;
502
269
  this.emitUpdate(this.state);
503
- this.menuFrozen = false;
504
- }
505
- };
506
-
507
- onMouseDown = () => {
508
- if (this.state && this.state.show && this.menuFrozen) {
509
- this.menuFrozen = false;
510
- this.state.show = false;
511
- this.emitUpdate(this.state);
512
270
  }
513
271
  };
514
272
 
@@ -528,7 +286,8 @@ export class SideMenuView<
528
286
  this.mousePos.y > editorOuterBoundingBox.top &&
529
287
  this.mousePos.y < editorOuterBoundingBox.bottom;
530
288
 
531
- const editorWrapper = this.pmView.dom.parentElement!;
289
+ // TODO: remove parentElement, but then we need to remove padding from boundingbox or find a different solution
290
+ const editorWrapper = this.pmView.dom!.parentElement!;
532
291
 
533
292
  // Doesn't update if the mouse hovers an element that's over the editor but
534
293
  // isn't a part of it or the side menu.
@@ -552,29 +311,7 @@ export class SideMenuView<
552
311
  return;
553
312
  }
554
313
 
555
- this.updateState();
556
-
557
- if (this.needUpdate) {
558
- this.emitUpdate(this.state!);
559
- this.needUpdate = false;
560
- }
561
- };
562
-
563
- onScroll = () => {
564
- if (this.state?.show && this.hoveredBlock?.firstChild) {
565
- const blockContent = this.hoveredBlock.firstChild as HTMLElement;
566
- const blockContentBoundingBox = blockContent.getBoundingClientRect();
567
-
568
- this.state.referencePos = new DOMRect(
569
- this.horizontalPosAnchoredAtRoot
570
- ? this.horizontalPosAnchor
571
- : blockContentBoundingBox.x,
572
- blockContentBoundingBox.y,
573
- blockContentBoundingBox.width,
574
- blockContentBoundingBox.height
575
- );
576
- this.emitUpdate(this.state);
577
- }
314
+ this.updateStateFromMousePos();
578
315
  };
579
316
 
580
317
  // Needed in cases where the editor state updates without the mouse cursor
@@ -584,14 +321,10 @@ export class SideMenuView<
584
321
  // allowing the user to click the button again without moving the cursor. This
585
322
  // would otherwise not update the side menu, and so clicking the button again
586
323
  // would attempt to remove the same block again, causing an error.
587
- update() {
588
- const prevBlockId = this.state?.block.id;
589
-
590
- this.updateState();
591
-
592
- if (this.needUpdate && this.state && prevBlockId !== this.state.block.id) {
593
- this.emitUpdate(this.state);
594
- this.needUpdate = false;
324
+ update(_view: EditorView, prevState: EditorState) {
325
+ const docChanged = !prevState.doc.eq(this.pmView.state.doc);
326
+ if (docChanged && this.state?.show) {
327
+ this.updateStateFromMousePos();
595
328
  }
596
329
  }
597
330
 
@@ -609,73 +342,18 @@ export class SideMenuView<
609
342
  "dragover",
610
343
  this.onDragOver as EventListener
611
344
  );
612
- this.pmView.dom.removeEventListener("dragstart", this.onDragStart);
345
+
613
346
  this.pmView.root.removeEventListener(
614
347
  "drop",
615
348
  this.onDrop as EventListener,
616
349
  true
617
350
  );
618
- this.pmView.root.removeEventListener("scroll", this.onScroll, true);
619
- this.pmView.dom.removeEventListener("mousedown", this.onMouseDown);
620
351
  this.pmView.root.removeEventListener(
621
352
  "keydown",
622
353
  this.onKeyDown as EventListener,
623
354
  true
624
355
  );
625
356
  }
626
-
627
- addBlock() {
628
- if (this.state?.show) {
629
- this.state.show = false;
630
- this.emitUpdate(this.state);
631
- }
632
-
633
- if (!this.hoveredBlock?.firstChild) {
634
- return;
635
- }
636
-
637
- const blockContent = this.hoveredBlock.firstChild as HTMLElement;
638
- const blockContentBoundingBox = blockContent.getBoundingClientRect();
639
-
640
- const pos = this.pmView.posAtCoords({
641
- left: blockContentBoundingBox.left + blockContentBoundingBox.width / 2,
642
- top: blockContentBoundingBox.top + blockContentBoundingBox.height / 2,
643
- });
644
- if (!pos) {
645
- return;
646
- }
647
-
648
- const blockInfo = getBlockInfoFromPos(
649
- this.editor._tiptapEditor.state.doc,
650
- pos.pos
651
- );
652
- if (blockInfo === undefined) {
653
- return;
654
- }
655
-
656
- const { contentNode, startPos, endPos } = blockInfo;
657
-
658
- // Creates a new block if current one is not empty for the suggestion menu to open in.
659
- if (
660
- contentNode.type.spec.content !== "inline*" ||
661
- contentNode.textContent.length !== 0
662
- ) {
663
- const newBlockInsertionPos = endPos + 1;
664
- const newBlockContentPos = newBlockInsertionPos + 2;
665
-
666
- this.editor._tiptapEditor
667
- .chain()
668
- .BNCreateBlock(newBlockInsertionPos)
669
- // .BNUpdateBlock(newBlockContentPos, { type: "paragraph", props: {} })
670
- .setTextSelection(newBlockContentPos)
671
- .run();
672
- } else {
673
- this.editor._tiptapEditor.commands.setTextSelection(startPos + 1);
674
- }
675
-
676
- // Focuses and activates the slash menu.
677
- this.editor.openSuggestionMenu("/");
678
- }
679
357
  }
680
358
 
681
359
  export const sideMenuPluginKey = new PluginKey("SideMenuPlugin");
@@ -705,12 +383,6 @@ export class SideMenuProsemirrorPlugin<
705
383
  return this.on("update", callback);
706
384
  }
707
385
 
708
- /**
709
- * If the block is empty, opens the slash menu. If the block has content,
710
- * creates a new block below and opens the slash menu in it.
711
- */
712
- addBlock = () => this.view!.addBlock();
713
-
714
386
  /**
715
387
  * Handles drag & drop events for blocks.
716
388
  */
@@ -718,7 +390,6 @@ export class SideMenuProsemirrorPlugin<
718
390
  dataTransfer: DataTransfer | null;
719
391
  clientY: number;
720
392
  }) => {
721
- this.view!.isDragging = true;
722
393
  dragStart(event, this.editor);
723
394
  };
724
395