@blocknote/core 0.13.2 → 0.13.4

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 (163) hide show
  1. package/dist/blocknote.js +5730 -2891
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +7 -7
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/dist/webpack-stats.json +1 -1
  7. package/package.json +2 -2
  8. package/src/api/exporters/html/__snapshots__/file/basic/external.html +1 -0
  9. package/src/api/exporters/html/__snapshots__/file/basic/internal.html +1 -0
  10. package/src/api/exporters/html/__snapshots__/file/button/external.html +1 -0
  11. package/src/api/exporters/html/__snapshots__/file/button/internal.html +1 -0
  12. package/src/api/exporters/html/__snapshots__/file/nested/external.html +1 -0
  13. package/src/api/exporters/html/__snapshots__/file/nested/internal.html +1 -0
  14. package/src/api/exporters/html/__snapshots__/file/noCaption/external.html +1 -0
  15. package/src/api/exporters/html/__snapshots__/file/noCaption/internal.html +1 -0
  16. package/src/api/exporters/html/__snapshots__/file/noName/external.html +1 -0
  17. package/src/api/exporters/html/__snapshots__/file/noName/internal.html +1 -0
  18. package/src/api/exporters/html/__snapshots__/image/basic/external.html +1 -1
  19. package/src/api/exporters/html/__snapshots__/image/basic/internal.html +1 -1
  20. package/src/api/exporters/html/__snapshots__/image/button/external.html +1 -1
  21. package/src/api/exporters/html/__snapshots__/image/button/internal.html +1 -1
  22. package/src/api/exporters/html/__snapshots__/image/nested/external.html +1 -1
  23. package/src/api/exporters/html/__snapshots__/image/nested/internal.html +1 -1
  24. package/src/api/exporters/html/__snapshots__/image/noCaption/external.html +1 -0
  25. package/src/api/exporters/html/__snapshots__/image/noCaption/internal.html +1 -0
  26. package/src/api/exporters/html/__snapshots__/image/noName/external.html +1 -0
  27. package/src/api/exporters/html/__snapshots__/image/noName/internal.html +1 -0
  28. package/src/api/exporters/html/__snapshots__/image/noPreview/external.html +1 -0
  29. package/src/api/exporters/html/__snapshots__/image/noPreview/internal.html +1 -0
  30. package/src/api/exporters/html/__snapshots__/lists/basic/external.html +1 -0
  31. package/src/api/exporters/html/__snapshots__/lists/basic/internal.html +1 -0
  32. package/src/api/exporters/html/__snapshots__/lists/nested/external.html +1 -0
  33. package/src/api/exporters/html/__snapshots__/lists/nested/internal.html +1 -0
  34. package/src/api/exporters/html/__snapshots__/simpleFile/basic/external.html +1 -0
  35. package/src/api/exporters/html/__snapshots__/simpleFile/basic/internal.html +1 -0
  36. package/src/api/exporters/html/__snapshots__/simpleFile/button/external.html +1 -0
  37. package/src/api/exporters/html/__snapshots__/simpleFile/button/internal.html +1 -0
  38. package/src/api/exporters/html/__snapshots__/simpleFile/nested/external.html +1 -0
  39. package/src/api/exporters/html/__snapshots__/simpleFile/nested/internal.html +1 -0
  40. package/src/api/exporters/html/__snapshots__/simpleImage/basic/external.html +1 -1
  41. package/src/api/exporters/html/__snapshots__/simpleImage/basic/internal.html +1 -1
  42. package/src/api/exporters/html/__snapshots__/simpleImage/button/external.html +1 -1
  43. package/src/api/exporters/html/__snapshots__/simpleImage/button/internal.html +1 -1
  44. package/src/api/exporters/html/__snapshots__/simpleImage/nested/external.html +1 -1
  45. package/src/api/exporters/html/__snapshots__/simpleImage/nested/internal.html +1 -1
  46. package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/external.html +1 -0
  47. package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/internal.html +1 -0
  48. package/src/api/exporters/html/__snapshots__/simpleImage/noName/external.html +1 -0
  49. package/src/api/exporters/html/__snapshots__/simpleImage/noName/internal.html +1 -0
  50. package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/external.html +1 -0
  51. package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/internal.html +1 -0
  52. package/src/api/exporters/html/externalHTMLExporter.ts +4 -3
  53. package/src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts +1 -1
  54. package/src/api/exporters/markdown/__snapshots__/file/basic/markdown.md +3 -0
  55. package/src/api/exporters/markdown/__snapshots__/file/button/markdown.md +1 -0
  56. package/src/api/exporters/markdown/__snapshots__/file/nested/markdown.md +7 -0
  57. package/src/api/exporters/markdown/__snapshots__/file/noCaption/markdown.md +1 -0
  58. package/src/api/exporters/markdown/__snapshots__/file/noName/markdown.md +3 -0
  59. package/src/api/exporters/markdown/__snapshots__/image/basic/markdown.md +1 -1
  60. package/src/api/exporters/markdown/__snapshots__/image/button/markdown.md +1 -1
  61. package/src/api/exporters/markdown/__snapshots__/image/nested/markdown.md +2 -2
  62. package/src/api/exporters/markdown/__snapshots__/image/noCaption/markdown.md +1 -0
  63. package/src/api/exporters/markdown/__snapshots__/image/noName/markdown.md +3 -0
  64. package/src/api/exporters/markdown/__snapshots__/image/noPreview/markdown.md +3 -0
  65. package/src/api/exporters/markdown/__snapshots__/lists/basic/markdown.md +8 -0
  66. package/src/api/exporters/markdown/__snapshots__/lists/nested/markdown.md +10 -0
  67. package/src/api/exporters/markdown/__snapshots__/simpleFile/basic/markdown.md +3 -0
  68. package/src/api/exporters/markdown/__snapshots__/simpleFile/button/markdown.md +1 -0
  69. package/src/api/exporters/markdown/__snapshots__/simpleFile/nested/markdown.md +7 -0
  70. package/src/api/exporters/markdown/__snapshots__/simpleImage/basic/markdown.md +3 -1
  71. package/src/api/exporters/markdown/__snapshots__/simpleImage/button/markdown.md +1 -0
  72. package/src/api/exporters/markdown/__snapshots__/simpleImage/nested/markdown.md +6 -2
  73. package/src/api/exporters/markdown/__snapshots__/simpleImage/noCaption/markdown.md +1 -0
  74. package/src/api/exporters/markdown/__snapshots__/simpleImage/noName/markdown.md +3 -0
  75. package/src/api/exporters/markdown/__snapshots__/simpleImage/noPreview/markdown.md +3 -0
  76. package/src/api/exporters/markdown/markdownExporter.ts +2 -0
  77. package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +42 -0
  78. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +262 -4
  79. package/src/api/parsers/html/__snapshots__/paste/list-test.json +74 -2
  80. package/src/api/parsers/html/__snapshots__/paste/parse-basic-block-types.json +3 -1
  81. package/src/api/parsers/html/__snapshots__/paste/parse-fake-image-caption.json +3 -1
  82. package/src/api/parsers/html/__snapshots__/paste/parse-mixed-nested-lists.json +135 -10
  83. package/src/api/parsers/html/__snapshots__/paste/parse-nested-lists-with-paragraphs.json +132 -7
  84. package/src/api/parsers/html/__snapshots__/paste/parse-nested-lists.json +111 -3
  85. package/src/api/parsers/html/parseHTML.test.ts +166 -95
  86. package/src/api/testUtil/cases/customBlocks.ts +82 -33
  87. package/src/api/testUtil/cases/customInlineContent.ts +1 -1
  88. package/src/api/testUtil/cases/customStyles.ts +1 -1
  89. package/src/api/testUtil/cases/defaultSchema.ts +185 -4
  90. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +163 -0
  91. package/src/blocks/AudioBlockContent/audioBlockHelpers.ts +5 -0
  92. package/src/blocks/FileBlockContent/FileBlockContent.ts +120 -0
  93. package/src/blocks/FileBlockContent/fileBlockHelpers.ts +377 -0
  94. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +134 -354
  95. package/src/blocks/ImageBlockContent/imageBlockHelpers.ts +6 -0
  96. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +3 -0
  97. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +266 -0
  98. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +2 -1
  99. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +1 -0
  100. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +181 -0
  101. package/src/blocks/VideoBlockContent/videoBlockHelpers.ts +6 -0
  102. package/src/blocks/defaultBlockTypeGuards.ts +53 -1
  103. package/src/blocks/defaultBlocks.ts +11 -2
  104. package/src/editor/Block.css +89 -27
  105. package/src/editor/BlockNoteEditor.ts +24 -10
  106. package/src/editor/BlockNoteSchema.ts +12 -3
  107. package/src/editor/transformPasted.ts +2 -1
  108. package/src/extensions/{ImagePanel/ImageToolbarPlugin.ts → FilePanel/FilePanelPlugin.ts} +22 -25
  109. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +14 -2
  110. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +72 -2
  111. package/src/extensions/TableHandles/TableHandlesPlugin.ts +27 -27
  112. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +7 -1
  113. package/src/i18n/locales/en.ts +117 -11
  114. package/src/i18n/locales/fr.ts +118 -11
  115. package/src/i18n/locales/index.ts +8 -2
  116. package/src/i18n/locales/is.ts +295 -0
  117. package/src/i18n/locales/ja.ts +323 -0
  118. package/src/i18n/locales/ko.ts +307 -0
  119. package/src/i18n/locales/nl.ts +108 -8
  120. package/src/i18n/locales/pl.ts +287 -0
  121. package/src/i18n/locales/pt.ts +295 -0
  122. package/src/i18n/locales/vi.ts +295 -0
  123. package/src/i18n/locales/zh.ts +123 -8
  124. package/src/index.ts +9 -2
  125. package/src/pm-nodes/BlockContainer.ts +18 -6
  126. package/src/schema/blocks/createSpec.ts +1 -0
  127. package/src/schema/blocks/internal.ts +10 -0
  128. package/src/schema/blocks/types.ts +40 -5
  129. package/src/util/string.ts +12 -0
  130. package/types/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.d.ts +7 -0
  131. package/types/src/api/testUtil/cases/customBlocks.d.ts +272 -54
  132. package/types/src/api/testUtil/cases/customInlineContent.d.ts +222 -16
  133. package/types/src/api/testUtil/cases/customStyles.d.ts +222 -16
  134. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +101 -0
  135. package/types/src/blocks/AudioBlockContent/audioBlockHelpers.d.ts +3 -0
  136. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +93 -0
  137. package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +30 -0
  138. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +50 -14
  139. package/types/src/blocks/ImageBlockContent/imageBlockHelpers.d.ts +4 -0
  140. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +55 -0
  141. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +129 -0
  142. package/types/src/blocks/VideoBlockContent/videoBlockHelpers.d.ts +4 -0
  143. package/types/src/blocks/defaultBlockTypeGuards.d.ts +6 -1
  144. package/types/src/blocks/defaultBlocks.d.ts +444 -32
  145. package/types/src/editor/BlockNoteEditor.d.ts +12 -5
  146. package/types/src/extensions/{ImagePanel/ImageToolbarPlugin.d.ts → FilePanel/FilePanelPlugin.d.ts} +9 -12
  147. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +1 -1
  148. package/types/src/i18n/locales/en.d.ts +56 -7
  149. package/types/src/i18n/locales/fr.d.ts +2 -184
  150. package/types/src/i18n/locales/index.d.ts +7 -1
  151. package/types/src/i18n/locales/is.d.ts +2 -0
  152. package/types/src/i18n/locales/ja.d.ts +2 -0
  153. package/types/src/i18n/locales/ko.d.ts +2 -0
  154. package/types/src/i18n/locales/pl.d.ts +2 -0
  155. package/types/src/i18n/locales/pt.d.ts +2 -0
  156. package/types/src/i18n/locales/vi.d.ts +2 -0
  157. package/types/src/index.d.ts +8 -2
  158. package/types/src/pm-nodes/BlockContainer.d.ts +1 -1
  159. package/types/src/schema/blocks/internal.d.ts +1 -1
  160. package/types/src/schema/blocks/types.d.ts +25 -1
  161. package/types/src/util/string.d.ts +1 -0
  162. /package/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
  163. /package/types/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
@@ -1,404 +1,184 @@
1
1
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
2
-
3
2
  import {
4
3
  BlockFromConfig,
5
- BlockSchemaWithBlock,
6
- CustomBlockConfig,
7
- InlineContentSchema,
8
- PropSchema,
9
- StyleSchema,
10
4
  createBlockSpec,
5
+ FileBlockConfig,
6
+ Props,
7
+ PropSchema,
11
8
  } from "../../schema";
12
9
  import { defaultProps } from "../defaultProps";
13
10
 
11
+ import {
12
+ createAddFileButton,
13
+ createDefaultFilePreview,
14
+ createFigureWithCaption,
15
+ createFileAndCaptionWrapper,
16
+ createLinkWithCaption,
17
+ createResizeHandlesWrapper,
18
+ parseFigureElement,
19
+ } from "../FileBlockContent/fileBlockHelpers";
20
+ import { parseImageElement } from "./imageBlockHelpers";
21
+
14
22
  export const imagePropSchema = {
15
23
  textAlignment: defaultProps.textAlignment,
16
24
  backgroundColor: defaultProps.backgroundColor,
17
- // Image url.
25
+ // File name.
26
+ name: {
27
+ default: "" as const,
28
+ },
29
+ // File url.
18
30
  url: {
19
31
  default: "" as const,
20
32
  },
21
- // Image caption.
33
+ // File caption.
22
34
  caption: {
23
35
  default: "" as const,
24
36
  },
25
- // Image width in px.
26
- width: {
27
- default: 512 as const,
37
+
38
+ showPreview: {
39
+ default: true,
40
+ },
41
+ // File preview width in px.
42
+ previewWidth: {
43
+ default: 512,
28
44
  },
29
45
  } satisfies PropSchema;
30
46
 
31
- // Converts text alignment prop values to the flexbox `align-items` values.
32
- const textAlignmentToAlignItems = (
33
- textAlignment: "left" | "center" | "right" | "justify"
34
- ): "flex-start" | "center" | "flex-end" => {
35
- switch (textAlignment) {
36
- case "left":
37
- return "flex-start";
38
- case "center":
39
- return "center";
40
- case "right":
41
- return "flex-end";
42
- default:
43
- return "flex-start";
44
- }
45
- };
46
-
47
- // Min image width in px.
48
- const minWidth = 64;
49
-
50
- const blockConfig = {
47
+ export const imageBlockConfig = {
51
48
  type: "image" as const,
52
49
  propSchema: imagePropSchema,
53
50
  content: "none",
54
- } satisfies CustomBlockConfig;
51
+ isFileBlock: true,
52
+ fileBlockAcceptMimeTypes: ["image/*"],
53
+ } satisfies FileBlockConfig;
55
54
 
56
- export const renderImage = (
57
- block: BlockFromConfig<typeof blockConfig, InlineContentSchema, StyleSchema>,
58
- editor: BlockNoteEditor<BlockSchemaWithBlock<"image", typeof blockConfig>>
55
+ export const imageRender = (
56
+ block: BlockFromConfig<typeof imageBlockConfig, any, any>,
57
+ editor: BlockNoteEditor<any, any, any>
59
58
  ) => {
60
- // Wrapper element to set the image alignment, contains both image/image
61
- // upload dashboard and caption.
62
59
  const wrapper = document.createElement("div");
63
- wrapper.className = "bn-image-block-content-wrapper";
64
- wrapper.style.alignItems = textAlignmentToAlignItems(
65
- block.props.textAlignment
66
- );
67
-
68
- // Button element that acts as a placeholder for images with no src.
69
- const addImageButton = document.createElement("div");
70
- addImageButton.className = "bn-add-image-button";
71
-
72
- // Icon for the add image button.
73
- const addImageButtonIcon = document.createElement("div");
74
- addImageButtonIcon.className = "bn-add-image-button-icon";
75
-
76
- // Text for the add image button.
77
- const addImageButtonText = document.createElement("p");
78
- addImageButtonText.className = "bn-add-image-button-text";
79
- addImageButtonText.innerText = editor.dictionary.image.add_button;
80
-
81
- // Wrapper element for the image, resize handles and caption.
82
- const imageAndCaptionWrapper = document.createElement("div");
83
- imageAndCaptionWrapper.className = "bn-image-and-caption-wrapper";
84
-
85
- // Wrapper element for the image and resize handles.
86
- const imageWrapper = document.createElement("div");
87
- imageWrapper.className = "bn-image-wrapper";
88
-
89
- // Image element.
90
- const image = document.createElement("img");
91
- image.className = "bn-image";
92
- image.src = block.props.url;
93
- image.alt = "placeholder";
94
- image.contentEditable = "false";
95
- image.draggable = false;
96
- image.style.width = `${Math.min(
97
- block.props.width,
98
- editor.domElement.firstElementChild!.clientWidth
99
- )}px`;
100
-
101
- // Resize handle elements.
102
- const leftResizeHandle = document.createElement("div");
103
- leftResizeHandle.className = "bn-image-resize-handle";
104
- leftResizeHandle.style.left = "4px";
105
- const rightResizeHandle = document.createElement("div");
106
- rightResizeHandle.className = "bn-image-resize-handle";
107
- rightResizeHandle.style.right = "4px";
108
-
109
- // Caption element.
110
- const caption = document.createElement("p");
111
- caption.className = "bn-image-caption";
112
- caption.innerText = block.props.caption;
113
- caption.style.padding = block.props.caption ? "4px" : "";
114
-
115
- // Adds a light blue outline to selected image blocks.
116
- const handleEditorUpdate = () => {
117
- const selection = editor.getSelection()?.blocks || [];
118
- const currentBlock = editor.getTextCursorPosition().block;
60
+ wrapper.className = "bn-file-block-content-wrapper";
119
61
 
120
- const isSelected =
121
- [currentBlock, ...selection].find(
122
- (selectedBlock) => selectedBlock.id === block.id
123
- ) !== undefined;
124
-
125
- if (isSelected) {
126
- addImageButton.style.outline = "4px solid rgb(100, 160, 255)";
127
- imageAndCaptionWrapper.style.outline = "4px solid rgb(100, 160, 255)";
128
- } else {
129
- addImageButton.style.outline = "";
130
- imageAndCaptionWrapper.style.outline = "";
131
- }
132
- };
133
- editor.onEditorContentChange(handleEditorUpdate);
134
- editor.onEditorSelectionChange(handleEditorUpdate);
135
-
136
- // Temporary parameters set when the user begins resizing the image, used to
137
- // calculate the new width of the image.
138
- let resizeParams:
139
- | {
140
- handleUsed: "left" | "right";
141
- initialWidth: number;
142
- initialClientX: number;
143
- }
144
- | undefined;
145
-
146
- // Updates the image width with an updated width depending on the cursor X
147
- // offset from when the resize began, and which resize handle is being used.
148
- const windowMouseMoveHandler = (event: MouseEvent) => {
149
- if (!resizeParams) {
150
- if (
151
- !editor.isEditable &&
152
- imageWrapper.contains(leftResizeHandle) &&
153
- imageWrapper.contains(rightResizeHandle)
154
- ) {
155
- imageWrapper.removeChild(leftResizeHandle);
156
- imageWrapper.removeChild(rightResizeHandle);
157
- }
158
-
159
- return;
160
- }
161
-
162
- let newWidth: number;
163
-
164
- if (textAlignmentToAlignItems(block.props.textAlignment) === "center") {
165
- if (resizeParams.handleUsed === "left") {
166
- newWidth =
167
- resizeParams.initialWidth +
168
- (resizeParams.initialClientX - event.clientX) * 2;
169
- } else {
170
- newWidth =
171
- resizeParams.initialWidth +
172
- (event.clientX - resizeParams.initialClientX) * 2;
173
- }
174
- } else {
175
- if (resizeParams.handleUsed === "left") {
176
- newWidth =
177
- resizeParams.initialWidth +
178
- resizeParams.initialClientX -
179
- event.clientX;
180
- } else {
181
- newWidth =
182
- resizeParams.initialWidth +
183
- event.clientX -
184
- resizeParams.initialClientX;
185
- }
186
- }
187
-
188
- // Ensures the image is not wider than the editor and not smaller than a
189
- // predetermined minimum width.
190
- if (newWidth < minWidth) {
191
- image.style.width = `${minWidth}px`;
192
- } else if (newWidth > editor.domElement.firstElementChild!.clientWidth) {
193
- image.style.width = `${
194
- editor.domElement.firstElementChild!.clientWidth
195
- }px`;
196
- } else {
197
- image.style.width = `${newWidth}px`;
198
- }
199
- };
200
- // Stops mouse movements from resizing the image and updates the block's
201
- // `width` prop to the new value.
202
- const windowMouseUpHandler = (event: MouseEvent) => {
203
- // Hides the drag handles if the cursor is no longer over the image.
204
- if (
205
- (!event.target ||
206
- !imageWrapper.contains(event.target as Node) ||
207
- !editor.isEditable) &&
208
- imageWrapper.contains(leftResizeHandle) &&
209
- imageWrapper.contains(rightResizeHandle)
210
- ) {
211
- imageWrapper.removeChild(leftResizeHandle);
212
- imageWrapper.removeChild(rightResizeHandle);
213
- }
214
-
215
- if (!resizeParams) {
216
- return;
217
- }
218
-
219
- resizeParams = undefined;
62
+ if (block.props.url === "") {
63
+ const fileBlockImageIcon = document.createElement("div");
64
+ fileBlockImageIcon.innerHTML =
65
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 11.1005L7 9.1005L12.5 14.6005L16 11.1005L19 14.1005V5H5V11.1005ZM4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3ZM15.5 10C14.6716 10 14 9.32843 14 8.5C14 7.67157 14.6716 7 15.5 7C16.3284 7 17 7.67157 17 8.5C17 9.32843 16.3284 10 15.5 10Z"></path></svg>';
66
+ const addImageButton = createAddFileButton(
67
+ block,
68
+ editor,
69
+ editor.dictionary.file_blocks.image.add_button_text,
70
+ fileBlockImageIcon.firstElementChild as HTMLElement
71
+ );
72
+ wrapper.appendChild(addImageButton.dom);
220
73
 
221
- editor.updateBlock(block, {
222
- type: "image",
223
- props: {
224
- // Removes "px" from the end of the width string and converts to float.
225
- width: parseFloat(image.style.width.slice(0, -2)) as any,
74
+ return {
75
+ dom: wrapper,
76
+ destroy: () => {
77
+ addImageButton?.destroy?.();
226
78
  },
79
+ };
80
+ } else if (!block.props.showPreview) {
81
+ const file = createDefaultFilePreview(block).dom;
82
+ const element = createFileAndCaptionWrapper(block, file);
83
+
84
+ return {
85
+ dom: element.dom,
86
+ };
87
+ } else {
88
+ const image = document.createElement("img");
89
+ image.className = "bn-visual-media";
90
+ editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
91
+ image.src = downloadUrl;
227
92
  });
228
- };
93
+ image.alt = block.props.name || block.props.caption || "BlockNote image";
94
+ image.contentEditable = "false";
95
+ image.draggable = false;
96
+ image.width = Math.min(
97
+ block.props.previewWidth,
98
+ editor.domElement.firstElementChild!.clientWidth
99
+ );
229
100
 
230
- // Prevents focus from moving to the button.
231
- const addImageButtonMouseDownHandler = (event: MouseEvent) => {
232
- event.preventDefault();
233
- };
234
- // Opens the image toolbar.
235
- const addImageButtonClickHandler = () => {
236
- editor._tiptapEditor.view.dispatch(
237
- editor._tiptapEditor.state.tr.setMeta(editor.imagePanel!.plugin, {
238
- block: block,
239
- })
101
+ const file = createResizeHandlesWrapper(
102
+ block,
103
+ editor,
104
+ image,
105
+ () => image.width,
106
+ (width) => (image.width = width)
240
107
  );
241
- };
242
108
 
243
- // Shows the resize handles when hovering over the image with the cursor.
244
- const imageMouseEnterHandler = () => {
245
- if (editor.isEditable) {
246
- imageWrapper.appendChild(leftResizeHandle);
247
- imageWrapper.appendChild(rightResizeHandle);
248
- }
249
- };
250
- // Hides the resize handles when the cursor leaves the image, unless the
251
- // cursor moves to one of the resize handles.
252
- const imageMouseLeaveHandler = (event: MouseEvent) => {
253
- if (
254
- event.relatedTarget === leftResizeHandle ||
255
- event.relatedTarget === rightResizeHandle
256
- ) {
257
- return;
258
- }
109
+ const element = createFileAndCaptionWrapper(block, file.dom);
110
+ wrapper.appendChild(element.dom);
259
111
 
260
- if (resizeParams) {
261
- return;
262
- }
112
+ return {
113
+ dom: wrapper,
114
+ destroy: file.destroy,
115
+ };
116
+ }
117
+ };
263
118
 
264
- if (
265
- editor.isEditable &&
266
- imageWrapper.contains(leftResizeHandle) &&
267
- imageWrapper.contains(rightResizeHandle)
268
- ) {
269
- imageWrapper.removeChild(leftResizeHandle);
270
- imageWrapper.removeChild(rightResizeHandle);
271
- }
272
- };
119
+ export const imageParse = (
120
+ element: HTMLElement
121
+ ): Partial<Props<typeof imageBlockConfig.propSchema>> | undefined => {
122
+ if (element.tagName === "IMG") {
123
+ return parseImageElement(element as HTMLImageElement);
124
+ }
273
125
 
274
- // Sets the resize params, allowing the user to begin resizing the image by
275
- // moving the cursor left or right.
276
- const leftResizeHandleMouseDownHandler = (event: MouseEvent) => {
277
- event.preventDefault();
126
+ if (element.tagName === "FIGURE") {
127
+ const parsedFigure = parseFigureElement(element, "img");
128
+ if (!parsedFigure) {
129
+ return undefined;
130
+ }
278
131
 
279
- imageWrapper.appendChild(leftResizeHandle);
280
- imageWrapper.appendChild(rightResizeHandle);
132
+ const { targetElement, caption } = parsedFigure;
281
133
 
282
- resizeParams = {
283
- handleUsed: "left",
284
- initialWidth: block.props.width,
285
- initialClientX: event.clientX,
134
+ return {
135
+ ...parseImageElement(targetElement as HTMLImageElement),
136
+ caption,
286
137
  };
287
- };
288
- const rightResizeHandleMouseDownHandler = (event: MouseEvent) => {
289
- event.preventDefault();
290
-
291
- imageWrapper.appendChild(leftResizeHandle);
292
- imageWrapper.appendChild(rightResizeHandle);
138
+ }
293
139
 
294
- resizeParams = {
295
- handleUsed: "right",
296
- initialWidth: block.props.width,
297
- initialClientX: event.clientX,
298
- };
299
- };
140
+ return undefined;
141
+ };
300
142
 
301
- addImageButton.appendChild(addImageButtonIcon);
302
- addImageButton.appendChild(addImageButtonText);
143
+ export const imageToExternalHTML = (
144
+ block: BlockFromConfig<typeof imageBlockConfig, any, any>
145
+ ) => {
146
+ if (!block.props.url) {
147
+ const div = document.createElement("p");
148
+ div.textContent = "Add image";
303
149
 
304
- imageAndCaptionWrapper.appendChild(imageWrapper);
305
- imageWrapper.appendChild(image);
306
- imageAndCaptionWrapper.appendChild(caption);
150
+ return {
151
+ dom: div,
152
+ };
153
+ }
307
154
 
308
- if (block.props.url === "") {
309
- wrapper.appendChild(addImageButton);
155
+ let image;
156
+ if (block.props.showPreview) {
157
+ image = document.createElement("img");
158
+ image.src = block.props.url;
159
+ image.alt = block.props.name || block.props.caption || "BlockNote image";
160
+ image.width = block.props.previewWidth;
310
161
  } else {
311
- wrapper.appendChild(imageAndCaptionWrapper);
162
+ image = document.createElement("a");
163
+ image.href = block.props.url;
164
+ image.textContent = block.props.name || block.props.url;
312
165
  }
313
166
 
314
- window.addEventListener("mousemove", windowMouseMoveHandler);
315
- window.addEventListener("mouseup", windowMouseUpHandler);
316
- addImageButton.addEventListener("mousedown", addImageButtonMouseDownHandler);
317
- addImageButton.addEventListener("click", addImageButtonClickHandler);
318
- image.addEventListener("mouseenter", imageMouseEnterHandler);
319
- image.addEventListener("mouseleave", imageMouseLeaveHandler);
320
- leftResizeHandle.addEventListener(
321
- "mousedown",
322
- leftResizeHandleMouseDownHandler
323
- );
324
- rightResizeHandle.addEventListener(
325
- "mousedown",
326
- rightResizeHandleMouseDownHandler
327
- );
167
+ if (block.props.caption) {
168
+ if (block.props.showPreview) {
169
+ return createFigureWithCaption(image, block.props.caption);
170
+ } else {
171
+ return createLinkWithCaption(image, block.props.caption);
172
+ }
173
+ }
328
174
 
329
175
  return {
330
- dom: wrapper,
331
- destroy: () => {
332
- window.removeEventListener("mousemove", windowMouseMoveHandler);
333
- window.removeEventListener("mouseup", windowMouseUpHandler);
334
- addImageButton.removeEventListener(
335
- "mousedown",
336
- addImageButtonMouseDownHandler
337
- );
338
- addImageButton.removeEventListener("click", addImageButtonClickHandler);
339
- leftResizeHandle.removeEventListener(
340
- "mousedown",
341
- leftResizeHandleMouseDownHandler
342
- );
343
- rightResizeHandle.removeEventListener(
344
- "mousedown",
345
- rightResizeHandleMouseDownHandler
346
- );
347
- },
176
+ dom: image,
348
177
  };
349
178
  };
350
179
 
351
- export const Image = createBlockSpec(
352
- {
353
- type: "image" as const,
354
- propSchema: imagePropSchema,
355
- content: "none",
356
- },
357
- {
358
- render: renderImage,
359
- toExternalHTML: (block) => {
360
- if (block.props.url === "") {
361
- const div = document.createElement("p");
362
- div.innerHTML = "Add Image";
363
-
364
- return {
365
- dom: div,
366
- };
367
- }
368
-
369
- const figure = document.createElement("figure");
370
-
371
- const img = document.createElement("img");
372
- img.src = block.props.url;
373
- figure.appendChild(img);
374
-
375
- if (block.props.caption !== "") {
376
- const figcaption = document.createElement("figcaption");
377
- figcaption.innerHTML = block.props.caption;
378
- figure.appendChild(figcaption);
379
- }
380
-
381
- return {
382
- dom: figure,
383
- };
384
- },
385
- parse: (element: HTMLElement) => {
386
- if (element.tagName === "FIGURE") {
387
- const img = element.querySelector("img");
388
- const caption = element.querySelector("figcaption");
389
- return {
390
- url: img?.getAttribute("src") || "",
391
- caption:
392
- caption?.textContent || img?.getAttribute("alt") || undefined,
393
- };
394
- } else if (element.tagName === "IMG") {
395
- return {
396
- url: element.getAttribute("src") || "",
397
- caption: element.getAttribute("alt") || undefined,
398
- };
399
- }
400
-
401
- return undefined;
402
- },
403
- }
404
- );
180
+ export const ImageBlock = createBlockSpec(imageBlockConfig, {
181
+ render: imageRender,
182
+ parse: imageParse,
183
+ toExternalHTML: imageToExternalHTML,
184
+ });
@@ -0,0 +1,6 @@
1
+ export const parseImageElement = (imageElement: HTMLImageElement) => {
2
+ const url = imageElement.src || undefined;
3
+ const previewWidth = imageElement.width || undefined;
4
+
5
+ return { url, previewWidth };
6
+ };
@@ -17,6 +17,9 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
17
17
  name: "bulletListItem",
18
18
  content: "inline*",
19
19
  group: "blockContent",
20
+ // This is to make sure that check list parse rules run before, since they
21
+ // both parse `li` elements but check lists are more specific.
22
+ priority: 90,
20
23
  addInputRules() {
21
24
  return [
22
25
  // Creates an unordered list when starting with "-", "+", or "*".