@blocknote/core 0.16.0 → 0.17.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 (248) hide show
  1. package/dist/blocknote.js +3287 -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 +333 -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
@@ -0,0 +1,300 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { setupTestEnv } from "../../setupTestEnv.js";
4
+ import { updateBlock } from "./updateBlock.js";
5
+
6
+ const getEditor = setupTestEnv();
7
+
8
+ describe("Test updateBlock typing", () => {
9
+ it("Type is inferred correctly", () => {
10
+ try {
11
+ getEditor().updateBlock(
12
+ { id: "placeholder-id" },
13
+ {
14
+ // @ts-expect-error invalid type
15
+ type: "non-existing",
16
+ }
17
+ );
18
+ } catch (e) {
19
+ // ID doesn't exist, which is fine - this is a compile-time check
20
+ }
21
+ });
22
+
23
+ it("Props are inferred correctly", () => {
24
+ try {
25
+ getEditor().updateBlock(
26
+ { id: "placeholder-id" },
27
+ {
28
+ type: "paragraph",
29
+ props: {
30
+ // @ts-expect-error invalid type
31
+ level: 1,
32
+ },
33
+ }
34
+ );
35
+ } catch (e) {
36
+ // ID doesn't exist, which is fine - this is a compile-time check
37
+ }
38
+ try {
39
+ getEditor().updateBlock(
40
+ { id: "placeholder-id" },
41
+ {
42
+ type: "heading",
43
+ props: {
44
+ level: 1,
45
+ },
46
+ }
47
+ );
48
+ } catch (e) {
49
+ // ID doesn't exist, which is fine - this is a compile-time check
50
+ }
51
+ });
52
+ });
53
+
54
+ describe("Test updateBlock", () => {
55
+ it.skip("Update ID", () => {
56
+ updateBlock(getEditor(), "heading-with-everything", {
57
+ id: "new-id",
58
+ });
59
+
60
+ expect(getEditor().document).toMatchSnapshot();
61
+ });
62
+
63
+ it("Update type", () => {
64
+ updateBlock(getEditor(), "heading-with-everything", {
65
+ type: "paragraph",
66
+ });
67
+
68
+ expect(getEditor().document).toMatchSnapshot();
69
+ });
70
+
71
+ it("Update single prop", () => {
72
+ updateBlock(getEditor(), "heading-with-everything", {
73
+ props: {
74
+ level: 3,
75
+ },
76
+ });
77
+
78
+ expect(getEditor().document).toMatchSnapshot();
79
+ });
80
+
81
+ it("Update all props", () => {
82
+ updateBlock(getEditor(), "heading-with-everything", {
83
+ props: {
84
+ backgroundColor: "blue",
85
+ level: 3,
86
+ textAlignment: "right",
87
+ textColor: "blue",
88
+ },
89
+ });
90
+
91
+ expect(getEditor().document).toMatchSnapshot();
92
+ });
93
+
94
+ it("Revert single prop", () => {
95
+ updateBlock(getEditor(), "heading-with-everything", {
96
+ props: {
97
+ level: undefined,
98
+ },
99
+ });
100
+
101
+ expect(getEditor().document).toMatchSnapshot();
102
+ });
103
+
104
+ it("Revert all props", () => {
105
+ updateBlock(getEditor(), "heading-with-everything", {
106
+ props: {
107
+ backgroundColor: undefined,
108
+ level: undefined,
109
+ textAlignment: undefined,
110
+ textColor: undefined,
111
+ },
112
+ });
113
+
114
+ expect(getEditor().document).toMatchSnapshot();
115
+ });
116
+
117
+ it("Update with plain content", () => {
118
+ updateBlock(getEditor(), "heading-with-everything", {
119
+ content: "New content",
120
+ });
121
+
122
+ expect(getEditor().document).toMatchSnapshot();
123
+ });
124
+
125
+ it("Update with styled content", () => {
126
+ updateBlock(getEditor(), "heading-with-everything", {
127
+ content: [
128
+ { type: "text", text: "New", styles: { backgroundColor: "blue" } },
129
+ { type: "text", text: " ", styles: {} },
130
+ { type: "text", text: "content", styles: { backgroundColor: "blue" } },
131
+ ],
132
+ });
133
+
134
+ expect(getEditor().document).toMatchSnapshot();
135
+ });
136
+
137
+ it("Update children", () => {
138
+ updateBlock(getEditor(), "heading-with-everything", {
139
+ children: [
140
+ {
141
+ id: "new-nested-paragraph",
142
+ type: "paragraph",
143
+ content: "New nested Paragraph 2",
144
+ children: [
145
+ {
146
+ id: "new-double-nested-paragraph",
147
+ type: "paragraph",
148
+ content: "New double Nested Paragraph 2",
149
+ },
150
+ ],
151
+ },
152
+ ],
153
+ });
154
+
155
+ expect(getEditor().document).toMatchSnapshot();
156
+ });
157
+
158
+ it.skip("Update everything", () => {
159
+ updateBlock(getEditor(), "heading-with-everything", {
160
+ id: "new-id",
161
+ type: "paragraph",
162
+ props: {
163
+ backgroundColor: "blue",
164
+ textAlignment: "right",
165
+ textColor: "blue",
166
+ },
167
+ content: [
168
+ { type: "text", text: "New", styles: { backgroundColor: "blue" } },
169
+ { type: "text", text: " ", styles: {} },
170
+ { type: "text", text: "content", styles: { backgroundColor: "blue" } },
171
+ ],
172
+ children: [
173
+ {
174
+ id: "new-nested-paragraph",
175
+ type: "paragraph",
176
+ content: "New nested Paragraph 2",
177
+ children: [
178
+ {
179
+ id: "new-double-nested-paragraph",
180
+ type: "paragraph",
181
+ content: "New double Nested Paragraph 2",
182
+ },
183
+ ],
184
+ },
185
+ ],
186
+ });
187
+
188
+ expect(getEditor().document).toMatchSnapshot();
189
+ });
190
+
191
+ it("Update inline content to empty table content", () => {
192
+ updateBlock(getEditor(), "paragraph-0", {
193
+ type: "table",
194
+ });
195
+
196
+ expect(getEditor().document).toMatchSnapshot();
197
+ });
198
+
199
+ it("Update table content to empty inline content", () => {
200
+ updateBlock(getEditor(), "table-0", {
201
+ type: "paragraph",
202
+ });
203
+
204
+ expect(getEditor().document).toMatchSnapshot();
205
+ });
206
+
207
+ it("Update inline content to table content", () => {
208
+ updateBlock(getEditor(), "paragraph-0", {
209
+ type: "table",
210
+ content: {
211
+ type: "tableContent",
212
+ rows: [
213
+ {
214
+ cells: ["Cell 1", "Cell 2", "Cell 3"],
215
+ },
216
+ {
217
+ cells: ["Cell 4", "Cell 5", "Cell 6"],
218
+ },
219
+ {
220
+ cells: ["Cell 7", "Cell 8", "Cell 9"],
221
+ },
222
+ ],
223
+ },
224
+ });
225
+
226
+ expect(getEditor().document).toMatchSnapshot();
227
+ });
228
+
229
+ it("Update table content to inline content", () => {
230
+ updateBlock(getEditor(), "table-0", {
231
+ type: "paragraph",
232
+ content: "Paragraph",
233
+ });
234
+
235
+ expect(getEditor().document).toMatchSnapshot();
236
+ });
237
+
238
+ it("Update inline content to no content", () => {
239
+ updateBlock(getEditor(), "paragraph-0", {
240
+ type: "image",
241
+ });
242
+
243
+ expect(getEditor().document).toMatchSnapshot();
244
+ });
245
+
246
+ it("Update no content to empty inline content", () => {
247
+ updateBlock(getEditor(), "image-0", {
248
+ type: "paragraph",
249
+ });
250
+
251
+ expect(getEditor().document).toMatchSnapshot();
252
+ });
253
+
254
+ it("Update no content to inline content", () => {
255
+ updateBlock(getEditor(), "image-0", {
256
+ type: "paragraph",
257
+ content: "Paragraph",
258
+ });
259
+
260
+ expect(getEditor().document).toMatchSnapshot();
261
+ });
262
+
263
+ it("Update no content to empty table content", () => {
264
+ updateBlock(getEditor(), "image-0", {
265
+ type: "table",
266
+ });
267
+
268
+ expect(getEditor().document).toMatchSnapshot();
269
+ });
270
+
271
+ it("Update no content to table content", () => {
272
+ updateBlock(getEditor(), "image-0", {
273
+ type: "table",
274
+ content: {
275
+ type: "tableContent",
276
+ rows: [
277
+ {
278
+ cells: ["Cell 1", "Cell 2", "Cell 3"],
279
+ },
280
+ {
281
+ cells: ["Cell 4", "Cell 5", "Cell 6"],
282
+ },
283
+ {
284
+ cells: ["Cell 7", "Cell 8", "Cell 9"],
285
+ },
286
+ ],
287
+ },
288
+ });
289
+
290
+ expect(getEditor().document).toMatchSnapshot();
291
+ });
292
+
293
+ it("Update table content to no content", () => {
294
+ updateBlock(getEditor(), "table-0", {
295
+ type: "image",
296
+ });
297
+
298
+ expect(getEditor().document).toMatchSnapshot();
299
+ });
300
+ });
@@ -0,0 +1,199 @@
1
+ import { Fragment, Node as PMNode, Slice } from "prosemirror-model";
2
+ import { EditorState } from "prosemirror-state";
3
+
4
+ import { Block, PartialBlock } from "../../../../blocks/defaultBlocks.js";
5
+ import { BlockNoteEditor } from "../../../../editor/BlockNoteEditor.js";
6
+ import {
7
+ BlockIdentifier,
8
+ BlockSchema,
9
+ } from "../../../../schema/blocks/types.js";
10
+ import { InlineContentSchema } from "../../../../schema/inlineContent/types.js";
11
+ import { StyleSchema } from "../../../../schema/styles/types.js";
12
+ import { UnreachableCaseError } from "../../../../util/typescript.js";
13
+ import { getBlockInfoFromResolvedPos } from "../../../getBlockInfoFromPos.js";
14
+ import {
15
+ blockToNode,
16
+ inlineContentToNodes,
17
+ tableContentToNodes,
18
+ } from "../../../nodeConversions/blockToNode.js";
19
+ import { nodeToBlock } from "../../../nodeConversions/nodeToBlock.js";
20
+ import { getNodeById } from "../../../nodeUtil.js";
21
+
22
+ export const updateBlockCommand =
23
+ <
24
+ BSchema extends BlockSchema,
25
+ I extends InlineContentSchema,
26
+ S extends StyleSchema
27
+ >(
28
+ editor: BlockNoteEditor<BSchema, I, S>,
29
+ posBeforeBlock: number,
30
+ block: PartialBlock<BSchema, I, S>
31
+ ) =>
32
+ ({
33
+ state,
34
+ dispatch,
35
+ }: {
36
+ state: EditorState;
37
+ dispatch: ((args?: any) => any) | undefined;
38
+ }) => {
39
+ const { blockContainer, blockContent, blockGroup } =
40
+ getBlockInfoFromResolvedPos(state.doc.resolve(posBeforeBlock));
41
+
42
+ if (dispatch) {
43
+ // Adds blockGroup node with child blocks if necessary.
44
+ if (block.children !== undefined) {
45
+ const childNodes = [];
46
+
47
+ // Creates ProseMirror nodes for each child block, including their descendants.
48
+ for (const child of block.children) {
49
+ childNodes.push(
50
+ blockToNode(child, state.schema, editor.schema.styleSchema)
51
+ );
52
+ }
53
+
54
+ // Checks if a blockGroup node already exists.
55
+ if (blockGroup) {
56
+ // Replaces all child nodes in the existing blockGroup with the ones created earlier.
57
+ state.tr.replace(
58
+ blockGroup.beforePos + 1,
59
+ blockGroup.afterPos - 1,
60
+ new Slice(Fragment.from(childNodes), 0, 0)
61
+ );
62
+ } else {
63
+ // Inserts a new blockGroup containing the child nodes created earlier.
64
+ state.tr.insert(
65
+ blockContent.afterPos,
66
+ state.schema.nodes["blockGroup"].create({}, childNodes)
67
+ );
68
+ }
69
+ }
70
+
71
+ const oldType = blockContent.node.type.name;
72
+ const newType = block.type || oldType;
73
+
74
+ // The code below determines the new content of the block.
75
+ // or "keep" to keep as-is
76
+ let content: PMNode[] | "keep" = "keep";
77
+
78
+ // Has there been any custom content provided?
79
+ if (block.content) {
80
+ if (typeof block.content === "string") {
81
+ // Adds a single text node with no marks to the content.
82
+ content = inlineContentToNodes(
83
+ [block.content],
84
+ state.schema,
85
+ editor.schema.styleSchema
86
+ );
87
+ } else if (Array.isArray(block.content)) {
88
+ // Adds a text node with the provided styles converted into marks to the content,
89
+ // for each InlineContent object.
90
+ content = inlineContentToNodes(
91
+ block.content,
92
+ state.schema,
93
+ editor.schema.styleSchema
94
+ );
95
+ } else if (block.content.type === "tableContent") {
96
+ content = tableContentToNodes(
97
+ block.content,
98
+ state.schema,
99
+ editor.schema.styleSchema
100
+ );
101
+ } else {
102
+ throw new UnreachableCaseError(block.content.type);
103
+ }
104
+ } else {
105
+ // no custom content has been provided, use existing content IF possible
106
+
107
+ // Since some block types contain inline content and others don't,
108
+ // we either need to call setNodeMarkup to just update type &
109
+ // attributes, or replaceWith to replace the whole blockContent.
110
+ const oldContentType = state.schema.nodes[oldType].spec.content;
111
+ const newContentType = state.schema.nodes[newType].spec.content;
112
+
113
+ if (oldContentType === "") {
114
+ // keep old content, because it's empty anyway and should be compatible with
115
+ // any newContentType
116
+ } else if (newContentType !== oldContentType) {
117
+ // the content type changed, replace the previous content
118
+ content = [];
119
+ } else {
120
+ // keep old content, because the content type is the same and should be compatible
121
+ }
122
+ }
123
+
124
+ // Now, changes the blockContent node type and adds the provided props
125
+ // as attributes. Also preserves all existing attributes that are
126
+ // compatible with the new type.
127
+ //
128
+ // Use either setNodeMarkup or replaceWith depending on whether the
129
+ // content is being replaced or not.
130
+ if (content === "keep") {
131
+ // use setNodeMarkup to only update the type and attributes
132
+ state.tr.setNodeMarkup(
133
+ blockContent.beforePos,
134
+ block.type === undefined ? undefined : state.schema.nodes[block.type],
135
+ {
136
+ ...blockContent.node.attrs,
137
+ ...block.props,
138
+ }
139
+ );
140
+ } else {
141
+ // use replaceWith to replace the content and the block itself
142
+ // also reset the selection since replacing the block content
143
+ // sets it to the next block.
144
+ state.tr.replaceWith(
145
+ blockContent.beforePos,
146
+ blockContent.afterPos,
147
+ state.schema.nodes[newType].create(
148
+ {
149
+ ...blockContent.node.attrs,
150
+ ...block.props,
151
+ },
152
+ content
153
+ )
154
+ );
155
+ }
156
+
157
+ // Adds all provided props as attributes to the parent blockContainer node too, and also preserves existing
158
+ // attributes.
159
+ state.tr.setNodeMarkup(blockContainer.beforePos, undefined, {
160
+ ...blockContainer.node.attrs,
161
+ ...block.props,
162
+ });
163
+ }
164
+
165
+ return true;
166
+ };
167
+
168
+ export function updateBlock<
169
+ BSchema extends BlockSchema,
170
+ I extends InlineContentSchema,
171
+ S extends StyleSchema
172
+ >(
173
+ editor: BlockNoteEditor<BSchema, I, S>,
174
+ blockToUpdate: BlockIdentifier,
175
+ update: PartialBlock<BSchema, I, S>
176
+ ): Block<BSchema, I, S> {
177
+ const ttEditor = editor._tiptapEditor;
178
+
179
+ const id =
180
+ typeof blockToUpdate === "string" ? blockToUpdate : blockToUpdate.id;
181
+ const { posBeforeNode } = getNodeById(id, ttEditor.state.doc);
182
+
183
+ ttEditor.commands.command(({ state, dispatch }) => {
184
+ updateBlockCommand(editor, posBeforeNode, update)({ state, dispatch });
185
+ return true;
186
+ });
187
+
188
+ const blockContainerNode = ttEditor.state.doc
189
+ .resolve(posBeforeNode + 1) // TODO: clean?
190
+ .node();
191
+
192
+ return nodeToBlock(
193
+ blockContainerNode,
194
+ editor.schema.blockSchema,
195
+ editor.schema.inlineContentSchema,
196
+ editor.schema.styleSchema,
197
+ editor.blockCache
198
+ );
199
+ }
@@ -0,0 +1,96 @@
1
+ import { selectionToInsertionEnd } from "@tiptap/core";
2
+ import { Node } from "prosemirror-model";
3
+
4
+ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
5
+ import {
6
+ BlockSchema,
7
+ InlineContentSchema,
8
+ StyleSchema,
9
+ } from "../../schema/index.js";
10
+
11
+ // similar to tiptap insertContentAt
12
+ export function insertContentAt<
13
+ BSchema extends BlockSchema,
14
+ I extends InlineContentSchema,
15
+ S extends StyleSchema
16
+ >(
17
+ position: any,
18
+ nodes: Node[],
19
+ editor: BlockNoteEditor<BSchema, I, S>,
20
+ options: {
21
+ updateSelection: boolean;
22
+ } = { updateSelection: true }
23
+ ) {
24
+ const tr = editor._tiptapEditor.state.tr;
25
+
26
+ // don’t dispatch an empty fragment because this can lead to strange errors
27
+ // if (content.toString() === "<>") {
28
+ // return true;
29
+ // }
30
+
31
+ let { from, to } =
32
+ typeof position === "number"
33
+ ? { from: position, to: position }
34
+ : { from: position.from, to: position.to };
35
+
36
+ let isOnlyTextContent = true;
37
+ let isOnlyBlockContent = true;
38
+ // const nodes = isFragment(content) ? content : [content];
39
+
40
+ let text = "";
41
+
42
+ nodes.forEach((node) => {
43
+ // check if added node is valid
44
+ node.check();
45
+
46
+ if (isOnlyTextContent && node.isText && node.marks.length === 0) {
47
+ text += node.text;
48
+ } else {
49
+ isOnlyTextContent = false;
50
+ }
51
+
52
+ isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
53
+ });
54
+
55
+ // check if we can replace the wrapping node by
56
+ // the newly inserted content
57
+ // example:
58
+ // replace an empty paragraph by an inserted image
59
+ // instead of inserting the image below the paragraph
60
+ if (from === to && isOnlyBlockContent) {
61
+ const { parent } = tr.doc.resolve(from);
62
+ const isEmptyTextBlock =
63
+ parent.isTextblock && !parent.type.spec.code && !parent.childCount;
64
+
65
+ if (isEmptyTextBlock) {
66
+ from -= 1;
67
+ to += 1;
68
+ }
69
+ }
70
+
71
+ // if there is only plain text we have to use `insertText`
72
+ // because this will keep the current marks
73
+ if (isOnlyTextContent) {
74
+ // if value is string, we can use it directly
75
+ // otherwise if it is an array, we have to join it
76
+ // if (Array.isArray(value)) {
77
+ // tr.insertText(value.map((v) => v.text || "").join(""), from, to);
78
+ // } else if (typeof value === "object" && !!value && !!value.text) {
79
+ // tr.insertText(value.text, from, to);
80
+ // } else {
81
+ // tr.insertText(value as string, from, to);
82
+ // }
83
+ tr.insertText(text, from, to);
84
+ } else {
85
+ tr.replaceWith(from, to, nodes);
86
+ }
87
+
88
+ // set cursor at end of inserted content
89
+ if (options.updateSelection) {
90
+ selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
91
+ }
92
+
93
+ editor.dispatch(tr);
94
+
95
+ return true;
96
+ }