@blocknote/core 0.36.1 → 0.38.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 (76) hide show
  1. package/dist/blocknote.cjs +9 -9
  2. package/dist/blocknote.cjs.map +1 -1
  3. package/dist/blocknote.js +1689 -1715
  4. package/dist/blocknote.js.map +1 -1
  5. package/dist/{en-CvDoFvhc.js → en-Bq3Es3Np.js} +6 -12
  6. package/dist/en-Bq3Es3Np.js.map +1 -0
  7. package/dist/en-D3B48eJ7.cjs +2 -0
  8. package/dist/en-D3B48eJ7.cjs.map +1 -0
  9. package/dist/locales.cjs +1 -1
  10. package/dist/locales.cjs.map +1 -1
  11. package/dist/locales.js +109 -229
  12. package/dist/locales.js.map +1 -1
  13. package/dist/style.css +1 -1
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/webpack-stats.json +1 -1
  16. package/package.json +7 -7
  17. package/src/api/clipboard/toClipboard/copyExtension.ts +0 -2
  18. package/src/api/exporters/html/externalHTMLExporter.ts +0 -1
  19. package/src/api/exporters/markdown/markdownExporter.ts +13 -22
  20. package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +2 -12
  21. package/src/api/parsers/html/parseHTML.ts +3 -2
  22. package/src/api/parsers/html/util/nestedLists.test.ts +8 -8
  23. package/src/api/parsers/markdown/parseMarkdown.ts +17 -14
  24. package/src/api/positionMapping.test.ts +155 -34
  25. package/src/api/positionMapping.ts +2 -2
  26. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +0 -1
  27. package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +3 -2
  28. package/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.ts +1 -7
  29. package/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.ts +64 -17
  30. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +0 -1
  31. package/src/blocks/TableBlockContent/TableBlockContent.ts +2 -5
  32. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +0 -1
  33. package/src/editor/BlockNoteEditor.ts +17 -23
  34. package/src/editor/BlockNoteExtensions.ts +3 -0
  35. package/src/editor/editor.css +5 -0
  36. package/src/extensions/Comments/CommentsPlugin.ts +2 -0
  37. package/src/extensions/SideMenu/SideMenuPlugin.ts +0 -2
  38. package/src/i18n/locales/ar.ts +6 -12
  39. package/src/i18n/locales/de.ts +6 -12
  40. package/src/i18n/locales/en.ts +6 -12
  41. package/src/i18n/locales/es.ts +6 -12
  42. package/src/i18n/locales/fr.ts +6 -12
  43. package/src/i18n/locales/he.ts +6 -12
  44. package/src/i18n/locales/hr.ts +6 -12
  45. package/src/i18n/locales/is.ts +6 -12
  46. package/src/i18n/locales/it.ts +6 -12
  47. package/src/i18n/locales/ja.ts +6 -12
  48. package/src/i18n/locales/ko.ts +6 -12
  49. package/src/i18n/locales/nl.ts +6 -12
  50. package/src/i18n/locales/no.ts +6 -12
  51. package/src/i18n/locales/pl.ts +6 -12
  52. package/src/i18n/locales/pt.ts +6 -12
  53. package/src/i18n/locales/ru.ts +6 -12
  54. package/src/i18n/locales/sk.ts +6 -12
  55. package/src/i18n/locales/uk.ts +6 -12
  56. package/src/i18n/locales/vi.ts +6 -12
  57. package/src/i18n/locales/zh-tw.ts +6 -12
  58. package/src/i18n/locales/zh.ts +6 -12
  59. package/src/index.ts +0 -1
  60. package/types/src/api/exporters/markdown/markdownExporter.d.ts +1 -1
  61. package/types/src/api/parsers/html/parseHTML.d.ts +1 -1
  62. package/types/src/api/parsers/markdown/parseMarkdown.d.ts +2 -2
  63. package/types/src/blocks/FileBlockContent/helpers/render/createAddFileButton.d.ts +1 -1
  64. package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +1 -1
  65. package/types/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.d.ts +1 -1
  66. package/types/src/editor/BlockNoteEditor.d.ts +6 -9
  67. package/types/src/editor/BlockNoteExtensions.d.ts +2 -0
  68. package/types/src/extensions/Comments/CommentsPlugin.d.ts +3 -1
  69. package/types/src/i18n/locales/en.d.ts +1 -12
  70. package/types/src/i18n/locales/sk.d.ts +1 -12
  71. package/types/src/index.d.ts +0 -1
  72. package/dist/en-CvDoFvhc.js.map +0 -1
  73. package/dist/en-ub2yVBX0.cjs +0 -2
  74. package/dist/en-ub2yVBX0.cjs.map +0 -1
  75. package/src/util/esmDependencies.ts +0 -51
  76. package/types/src/util/esmDependencies.d.ts +0 -24
@@ -1,23 +1,14 @@
1
- import { beforeEach, afterEach, describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, it, vi } from "vitest";
2
2
  import * as Y from "yjs";
3
3
  import { BlockNoteEditor } from "../editor/BlockNoteEditor.js";
4
4
  import { trackPosition } from "./positionMapping.js";
5
5
 
6
6
  describe("PositionStorage with local editor", () => {
7
- let editor: BlockNoteEditor;
8
-
9
- beforeEach(() => {
10
- editor = BlockNoteEditor.create();
11
- editor.mount(document.createElement("div"));
12
- });
13
-
14
- afterEach(() => {
15
- editor.mount(undefined);
16
- editor._tiptapEditor.destroy();
17
- });
18
-
19
7
  describe("mount and unmount", () => {
20
8
  it("should register transaction handler on creation", () => {
9
+ const editor = BlockNoteEditor.create();
10
+ editor.mount(document.createElement("div"));
11
+
21
12
  editor._tiptapEditor.on = vi.fn();
22
13
  trackPosition(editor, 0);
23
14
 
@@ -25,24 +16,42 @@ describe("PositionStorage with local editor", () => {
25
16
  "transaction",
26
17
  expect.any(Function),
27
18
  );
19
+
20
+ editor.mount(undefined);
21
+ editor._tiptapEditor.destroy();
28
22
  });
29
23
  });
30
24
 
31
25
  describe("set and get positions", () => {
32
26
  it("should store and retrieve positions without Y.js", () => {
27
+ const editor = BlockNoteEditor.create();
28
+ editor.mount(document.createElement("div"));
29
+
33
30
  const getPos = trackPosition(editor, 10);
34
31
 
35
32
  expect(getPos()).toBe(10);
33
+
34
+ editor.mount(undefined);
35
+ editor._tiptapEditor.destroy();
36
36
  });
37
37
 
38
38
  it("should handle right side positions", () => {
39
+ const editor = BlockNoteEditor.create();
40
+ editor.mount(document.createElement("div"));
41
+
39
42
  const getPos = trackPosition(editor, 10, "right");
40
43
 
41
44
  expect(getPos()).toBe(10);
45
+
46
+ editor.mount(undefined);
47
+ editor._tiptapEditor.destroy();
42
48
  });
43
49
  });
44
50
 
45
51
  it("should update mapping for local transactions before the position", () => {
52
+ const editor = BlockNoteEditor.create();
53
+ editor.mount(document.createElement("div"));
54
+
46
55
  // Set initial content
47
56
  editor.insertBlocks(
48
57
  [
@@ -79,9 +88,15 @@ describe("PositionStorage with local editor", () => {
79
88
 
80
89
  // Position should be updated according to mapping
81
90
  expect(getPos()).toBe(14);
91
+
92
+ editor.mount(undefined);
93
+ editor._tiptapEditor.destroy();
82
94
  });
83
95
 
84
96
  it("should not update mapping for local transactions after the position", () => {
97
+ const editor = BlockNoteEditor.create();
98
+ editor.mount(document.createElement("div"));
99
+
85
100
  // Set initial content
86
101
  editor.insertBlocks(
87
102
  [
@@ -117,9 +132,15 @@ describe("PositionStorage with local editor", () => {
117
132
 
118
133
  // Position should not be updated
119
134
  expect(getPos()).toBe(10);
135
+
136
+ editor.mount(undefined);
137
+ editor._tiptapEditor.destroy();
120
138
  });
121
139
 
122
140
  it("should track positions on each side", () => {
141
+ const editor = BlockNoteEditor.create();
142
+ editor.mount(document.createElement("div"));
143
+
123
144
  editor.replaceBlocks(editor.document, [
124
145
  {
125
146
  type: "paragraph",
@@ -142,9 +163,15 @@ describe("PositionStorage with local editor", () => {
142
163
  expect(getStartRightPos()).toBe(8); // 3 + 5 ("Test " length)
143
164
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
144
165
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
166
+
167
+ editor.mount(undefined);
168
+ editor._tiptapEditor.destroy();
145
169
  });
146
170
 
147
171
  it("should handle multiple transactions", () => {
172
+ const editor = BlockNoteEditor.create();
173
+ editor.mount(document.createElement("div"));
174
+
148
175
  editor.replaceBlocks(editor.document, [
149
176
  {
150
177
  type: "paragraph",
@@ -172,6 +199,9 @@ describe("PositionStorage with local editor", () => {
172
199
  expect(getStartRightPos()).toBe(8); // 3 + 5 ("Test " length)
173
200
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
174
201
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
202
+
203
+ editor.mount(undefined);
204
+ editor._tiptapEditor.destroy();
175
205
  });
176
206
  });
177
207
 
@@ -202,16 +232,12 @@ describe("PositionStorage with remote editor", () => {
202
232
  }
203
233
 
204
234
  describe("remote editor", () => {
205
- let localEditor: BlockNoteEditor;
206
- let remoteEditor: BlockNoteEditor;
207
- let ydoc: Y.Doc;
208
- let remoteYdoc: Y.Doc;
209
-
210
- beforeEach(() => {
211
- ydoc = new Y.Doc();
212
- remoteYdoc = new Y.Doc();
235
+ it("should update the local position when collaborating", () => {
236
+ const ydoc = new Y.Doc();
237
+ const remoteYdoc = new Y.Doc();
238
+
213
239
  // Create a mock editor
214
- localEditor = BlockNoteEditor.create({
240
+ const localEditor = BlockNoteEditor.create({
215
241
  collaboration: {
216
242
  fragment: ydoc.getXmlFragment("doc"),
217
243
  user: { color: "#ff0000", name: "Local User" },
@@ -221,7 +247,7 @@ describe("PositionStorage with remote editor", () => {
221
247
  const div = document.createElement("div");
222
248
  localEditor.mount(div);
223
249
 
224
- remoteEditor = BlockNoteEditor.create({
250
+ const remoteEditor = BlockNoteEditor.create({
225
251
  collaboration: {
226
252
  fragment: remoteYdoc.getXmlFragment("doc"),
227
253
  user: { color: "#ff0000", name: "Remote User" },
@@ -232,18 +258,7 @@ describe("PositionStorage with remote editor", () => {
232
258
  const remoteDiv = document.createElement("div");
233
259
  remoteEditor.mount(remoteDiv);
234
260
  setupTwoWaySync(ydoc, remoteYdoc);
235
- });
236
-
237
- afterEach(() => {
238
- ydoc.destroy();
239
- remoteYdoc.destroy();
240
- localEditor.mount(undefined);
241
- localEditor._tiptapEditor.destroy();
242
- remoteEditor.mount(undefined);
243
- remoteEditor._tiptapEditor.destroy();
244
- });
245
261
 
246
- it("should update the local position when collaborating", () => {
247
262
  localEditor.replaceBlocks(localEditor.document, [
248
263
  {
249
264
  type: "paragraph",
@@ -271,9 +286,42 @@ describe("PositionStorage with remote editor", () => {
271
286
  expect(getStartRightPos()).toBe(8); // 3 + 5 ("Test " length)
272
287
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
273
288
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
289
+
290
+ ydoc.destroy();
291
+ remoteYdoc.destroy();
292
+ localEditor.mount(undefined);
293
+ localEditor._tiptapEditor.destroy();
294
+ remoteEditor.mount(undefined);
295
+ remoteEditor._tiptapEditor.destroy();
274
296
  });
275
297
 
276
298
  it("should handle multiple transactions when collaborating", () => {
299
+ const ydoc = new Y.Doc();
300
+ const remoteYdoc = new Y.Doc();
301
+
302
+ // Create a mock editor
303
+ const localEditor = BlockNoteEditor.create({
304
+ collaboration: {
305
+ fragment: ydoc.getXmlFragment("doc"),
306
+ user: { color: "#ff0000", name: "Local User" },
307
+ provider: undefined,
308
+ },
309
+ });
310
+ const div = document.createElement("div");
311
+ localEditor.mount(div);
312
+
313
+ const remoteEditor = BlockNoteEditor.create({
314
+ collaboration: {
315
+ fragment: remoteYdoc.getXmlFragment("doc"),
316
+ user: { color: "#ff0000", name: "Remote User" },
317
+ provider: undefined,
318
+ },
319
+ });
320
+
321
+ const remoteDiv = document.createElement("div");
322
+ remoteEditor.mount(remoteDiv);
323
+ setupTwoWaySync(ydoc, remoteYdoc);
324
+
277
325
  localEditor.replaceBlocks(localEditor.document, [
278
326
  {
279
327
  type: "paragraph",
@@ -305,9 +353,42 @@ describe("PositionStorage with remote editor", () => {
305
353
  expect(getStartRightPos()).toBe(8); // 3 + 5 ("Test " length)
306
354
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
307
355
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
356
+
357
+ ydoc.destroy();
358
+ remoteYdoc.destroy();
359
+ localEditor.mount(undefined);
360
+ localEditor._tiptapEditor.destroy();
361
+ remoteEditor.mount(undefined);
362
+ remoteEditor._tiptapEditor.destroy();
308
363
  });
309
364
 
310
365
  it("should update the local position from a remote transaction", () => {
366
+ const ydoc = new Y.Doc();
367
+ const remoteYdoc = new Y.Doc();
368
+
369
+ // Create a mock editor
370
+ const localEditor = BlockNoteEditor.create({
371
+ collaboration: {
372
+ fragment: ydoc.getXmlFragment("doc"),
373
+ user: { color: "#ff0000", name: "Local User" },
374
+ provider: undefined,
375
+ },
376
+ });
377
+ const div = document.createElement("div");
378
+ localEditor.mount(div);
379
+
380
+ const remoteEditor = BlockNoteEditor.create({
381
+ collaboration: {
382
+ fragment: remoteYdoc.getXmlFragment("doc"),
383
+ user: { color: "#ff0000", name: "Remote User" },
384
+ provider: undefined,
385
+ },
386
+ });
387
+
388
+ const remoteDiv = document.createElement("div");
389
+ remoteEditor.mount(remoteDiv);
390
+ setupTwoWaySync(ydoc, remoteYdoc);
391
+
311
392
  remoteEditor.replaceBlocks(remoteEditor.document, [
312
393
  {
313
394
  type: "paragraph",
@@ -335,9 +416,42 @@ describe("PositionStorage with remote editor", () => {
335
416
  expect(getStartRightPos()).toBe(8); // 3 + 5 ("Test " length)
336
417
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
337
418
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
419
+
420
+ ydoc.destroy();
421
+ remoteYdoc.destroy();
422
+ localEditor.mount(undefined);
423
+ localEditor._tiptapEditor.destroy();
424
+ remoteEditor.mount(undefined);
425
+ remoteEditor._tiptapEditor.destroy();
338
426
  });
339
427
 
340
428
  it("should update the remote position from a remote transaction", () => {
429
+ const ydoc = new Y.Doc();
430
+ const remoteYdoc = new Y.Doc();
431
+
432
+ // Create a mock editor
433
+ const localEditor = BlockNoteEditor.create({
434
+ collaboration: {
435
+ fragment: ydoc.getXmlFragment("doc"),
436
+ user: { color: "#ff0000", name: "Local User" },
437
+ provider: undefined,
438
+ },
439
+ });
440
+ const div = document.createElement("div");
441
+ localEditor.mount(div);
442
+
443
+ const remoteEditor = BlockNoteEditor.create({
444
+ collaboration: {
445
+ fragment: remoteYdoc.getXmlFragment("doc"),
446
+ user: { color: "#ff0000", name: "Remote User" },
447
+ provider: undefined,
448
+ },
449
+ });
450
+
451
+ const remoteDiv = document.createElement("div");
452
+ remoteEditor.mount(remoteDiv);
453
+ setupTwoWaySync(ydoc, remoteYdoc);
454
+
341
455
  remoteEditor.replaceBlocks(remoteEditor.document, [
342
456
  {
343
457
  type: "paragraph",
@@ -365,6 +479,13 @@ describe("PositionStorage with remote editor", () => {
365
479
  expect(getStartRightPos()).toBe(8); // 3 + 5 ("Test " length)
366
480
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
367
481
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
482
+
483
+ ydoc.destroy();
484
+ remoteYdoc.destroy();
485
+ localEditor.mount(undefined);
486
+ localEditor._tiptapEditor.destroy();
487
+ remoteEditor.mount(undefined);
488
+ remoteEditor._tiptapEditor.destroy();
368
489
  });
369
490
  });
370
491
  });
@@ -88,7 +88,7 @@ export function trackPosition(
88
88
 
89
89
  const relativePosition = absolutePositionToRelativePosition(
90
90
  // Track the position after the position if we are on the right side
91
- position + (side === "right" ? 1 : 0),
91
+ position + (side === "right" ? 1 : -1),
92
92
  ySyncPluginState.binding.type,
93
93
  ySyncPluginState.binding.mapping,
94
94
  );
@@ -109,6 +109,6 @@ export function trackPosition(
109
109
  throw new Error("Position not found, cannot track positions");
110
110
  }
111
111
 
112
- return pos + (side === "right" ? -1 : 0);
112
+ return pos + (side === "right" ? -1 : 1);
113
113
  };
114
114
  }
@@ -69,7 +69,6 @@ export const audioRender = (
69
69
  block,
70
70
  editor,
71
71
  { dom: audio },
72
- editor.dictionary.file_blocks.audio.add_button_text,
73
72
  icon.firstElementChild as HTMLElement,
74
73
  );
75
74
  };
@@ -4,7 +4,6 @@ import { BlockFromConfig, FileBlockConfig } from "../../../../schema/index.js";
4
4
  export const createAddFileButton = (
5
5
  block: BlockFromConfig<FileBlockConfig, any, any>,
6
6
  editor: BlockNoteEditor<any, any, any>,
7
- buttonText?: string,
8
7
  buttonIcon?: HTMLElement,
9
8
  ) => {
10
9
  const addFileButton = document.createElement("div");
@@ -23,7 +22,9 @@ export const createAddFileButton = (
23
22
  const addFileButtonText = document.createElement("p");
24
23
  addFileButtonText.className = "bn-add-file-button-text";
25
24
  addFileButtonText.innerHTML =
26
- buttonText || editor.dictionary.file_blocks.file.add_button_text;
25
+ block.type in editor.dictionary.file_blocks.add_button_text
26
+ ? editor.dictionary.file_blocks.add_button_text[block.type]
27
+ : editor.dictionary.file_blocks.add_button_text["file"];
27
28
  addFileButton.appendChild(addFileButtonText);
28
29
 
29
30
  // Prevents focus from moving to the button.
@@ -15,7 +15,6 @@ export const createFileBlockWrapper = (
15
15
  any
16
16
  >,
17
17
  element?: { dom: HTMLElement; destroy?: () => void },
18
- buttonText?: string,
19
18
  buttonIcon?: HTMLElement,
20
19
  ) => {
21
20
  const wrapper = document.createElement("div");
@@ -24,12 +23,7 @@ export const createFileBlockWrapper = (
24
23
  // Show the add file button if the file has not been uploaded yet. Change to
25
24
  // show a loader if a file upload for the block begins.
26
25
  if (block.props.url === "") {
27
- const addFileButton = createAddFileButton(
28
- block,
29
- editor,
30
- buttonText,
31
- buttonIcon,
32
- );
26
+ const addFileButton = createAddFileButton(block, editor, buttonIcon);
33
27
  wrapper.appendChild(addFileButton.dom);
34
28
 
35
29
  const destroyUploadStartHandler = editor.onUploadStart((blockId) => {
@@ -7,17 +7,16 @@ export const createResizableFileBlockWrapper = (
7
7
  editor: BlockNoteEditor<any, any, any>,
8
8
  element: { dom: HTMLElement; destroy?: () => void },
9
9
  resizeHandlesContainerElement: HTMLElement,
10
- buttonText: string,
11
- buttonIcon: HTMLElement,
10
+ buttonIcon?: HTMLElement,
12
11
  ): { dom: HTMLElement; destroy: () => void } => {
13
12
  const { dom, destroy } = createFileBlockWrapper(
14
13
  block,
15
14
  editor,
16
15
  element,
17
- buttonText,
18
16
  buttonIcon,
19
17
  );
20
18
  const wrapper = dom;
19
+ wrapper.style.position = "relative";
21
20
  if (block.props.url && block.props.showPreview) {
22
21
  if (block.props.previewWidth) {
23
22
  wrapper.style.width = `${block.props.previewWidth}px`;
@@ -33,6 +32,15 @@ export const createResizableFileBlockWrapper = (
33
32
  rightResizeHandle.className = "bn-resize-handle";
34
33
  rightResizeHandle.style.right = "4px";
35
34
 
35
+ // This element ensures `mousemove` and `mouseup` events are captured while
36
+ // resizing when the cursor is over the wrapper content. This is because
37
+ // embeds are treated as separate HTML documents, so if the content is an
38
+ // embed, the events will only fire within that document.
39
+ const eventCaptureElement = document.createElement("div");
40
+ eventCaptureElement.style.position = "absolute";
41
+ eventCaptureElement.style.height = "100%";
42
+ eventCaptureElement.style.width = "100%";
43
+
36
44
  // Temporary parameters set when the user begins resizing the element, used to
37
45
  // calculate the new width of the element.
38
46
  let resizeParams:
@@ -46,7 +54,7 @@ export const createResizableFileBlockWrapper = (
46
54
 
47
55
  // Updates the element width with an updated width depending on the cursor X
48
56
  // offset from when the resize began, and which resize handle is being used.
49
- const windowMouseMoveHandler = (event: MouseEvent) => {
57
+ const windowMouseMoveHandler = (event: MouseEvent | TouchEvent) => {
50
58
  if (!resizeParams) {
51
59
  if (
52
60
  !editor.isEditable &&
@@ -62,27 +70,26 @@ export const createResizableFileBlockWrapper = (
62
70
 
63
71
  let newWidth: number;
64
72
 
73
+ const clientX =
74
+ "touches" in event ? event.touches[0].clientX : event.clientX;
75
+
65
76
  if (block.props.textAlignment === "center") {
66
77
  if (resizeParams.handleUsed === "left") {
67
78
  newWidth =
68
79
  resizeParams.initialWidth +
69
- (resizeParams.initialClientX - event.clientX) * 2;
80
+ (resizeParams.initialClientX - clientX) * 2;
70
81
  } else {
71
82
  newWidth =
72
83
  resizeParams.initialWidth +
73
- (event.clientX - resizeParams.initialClientX) * 2;
84
+ (clientX - resizeParams.initialClientX) * 2;
74
85
  }
75
86
  } else {
76
87
  if (resizeParams.handleUsed === "left") {
77
88
  newWidth =
78
- resizeParams.initialWidth +
79
- resizeParams.initialClientX -
80
- event.clientX;
89
+ resizeParams.initialWidth + resizeParams.initialClientX - clientX;
81
90
  } else {
82
91
  newWidth =
83
- resizeParams.initialWidth +
84
- event.clientX -
85
- resizeParams.initialClientX;
92
+ resizeParams.initialWidth + clientX - resizeParams.initialClientX;
86
93
  }
87
94
  }
88
95
 
@@ -99,7 +106,7 @@ export const createResizableFileBlockWrapper = (
99
106
  };
100
107
  // Stops mouse movements from resizing the element and updates the block's
101
108
  // `width` prop to the new value.
102
- const windowMouseUpHandler = (event: MouseEvent) => {
109
+ const windowMouseUpHandler = (event: MouseEvent | TouchEvent) => {
103
110
  // Hides the drag handles if the cursor is no longer over the element.
104
111
  if (
105
112
  (!event.target ||
@@ -118,6 +125,10 @@ export const createResizableFileBlockWrapper = (
118
125
 
119
126
  resizeParams = undefined;
120
127
 
128
+ if (wrapper.contains(eventCaptureElement)) {
129
+ wrapper.removeChild(eventCaptureElement);
130
+ }
131
+
121
132
  editor.updateBlock(block, {
122
133
  props: {
123
134
  previewWidth: width,
@@ -158,54 +169,90 @@ export const createResizableFileBlockWrapper = (
158
169
 
159
170
  // Sets the resize params, allowing the user to begin resizing the element by
160
171
  // moving the cursor left or right.
161
- const leftResizeHandleMouseDownHandler = (event: MouseEvent) => {
172
+ const leftResizeHandleMouseDownHandler = (event: MouseEvent | TouchEvent) => {
162
173
  event.preventDefault();
163
174
 
175
+ if (!wrapper.contains(eventCaptureElement)) {
176
+ wrapper.appendChild(eventCaptureElement);
177
+ }
178
+
179
+ const clientX =
180
+ "touches" in event ? event.touches[0].clientX : event.clientX;
181
+
164
182
  resizeParams = {
165
183
  handleUsed: "left",
166
184
  initialWidth: wrapper.clientWidth,
167
- initialClientX: event.clientX,
185
+ initialClientX: clientX,
168
186
  };
169
187
  };
170
- const rightResizeHandleMouseDownHandler = (event: MouseEvent) => {
188
+ const rightResizeHandleMouseDownHandler = (
189
+ event: MouseEvent | TouchEvent,
190
+ ) => {
171
191
  event.preventDefault();
172
192
 
193
+ if (!wrapper.contains(eventCaptureElement)) {
194
+ wrapper.appendChild(eventCaptureElement);
195
+ }
196
+
197
+ const clientX =
198
+ "touches" in event ? event.touches[0].clientX : event.clientX;
199
+
173
200
  resizeParams = {
174
201
  handleUsed: "right",
175
202
  initialWidth: wrapper.clientWidth,
176
- initialClientX: event.clientX,
203
+ initialClientX: clientX,
177
204
  };
178
205
  };
179
206
 
180
207
  window.addEventListener("mousemove", windowMouseMoveHandler);
208
+ window.addEventListener("touchmove", windowMouseMoveHandler);
181
209
  window.addEventListener("mouseup", windowMouseUpHandler);
210
+ window.addEventListener("touchend", windowMouseUpHandler);
182
211
  wrapper.addEventListener("mouseenter", wrapperMouseEnterHandler);
183
212
  wrapper.addEventListener("mouseleave", wrapperMouseLeaveHandler);
184
213
  leftResizeHandle.addEventListener(
185
214
  "mousedown",
186
215
  leftResizeHandleMouseDownHandler,
187
216
  );
217
+ leftResizeHandle.addEventListener(
218
+ "touchstart",
219
+ leftResizeHandleMouseDownHandler,
220
+ );
188
221
  rightResizeHandle.addEventListener(
189
222
  "mousedown",
190
223
  rightResizeHandleMouseDownHandler,
191
224
  );
225
+ rightResizeHandle.addEventListener(
226
+ "touchstart",
227
+ rightResizeHandleMouseDownHandler,
228
+ );
192
229
 
193
230
  return {
194
231
  dom: wrapper,
195
232
  destroy: () => {
196
233
  destroy?.();
197
234
  window.removeEventListener("mousemove", windowMouseMoveHandler);
235
+ window.removeEventListener("touchmove", windowMouseMoveHandler);
198
236
  window.removeEventListener("mouseup", windowMouseUpHandler);
237
+ window.removeEventListener("touchend", windowMouseUpHandler);
199
238
  wrapper.removeEventListener("mouseenter", wrapperMouseEnterHandler);
200
239
  wrapper.removeEventListener("mouseleave", wrapperMouseLeaveHandler);
201
240
  leftResizeHandle.removeEventListener(
202
241
  "mousedown",
203
242
  leftResizeHandleMouseDownHandler,
204
243
  );
244
+ leftResizeHandle.removeEventListener(
245
+ "touchstart",
246
+ leftResizeHandleMouseDownHandler,
247
+ );
205
248
  rightResizeHandle.removeEventListener(
206
249
  "mousedown",
207
250
  rightResizeHandleMouseDownHandler,
208
251
  );
252
+ rightResizeHandle.removeEventListener(
253
+ "touchstart",
254
+ rightResizeHandleMouseDownHandler,
255
+ );
209
256
  },
210
257
  };
211
258
  };
@@ -80,7 +80,6 @@ export const imageRender = (
80
80
  editor,
81
81
  { dom: imageWrapper },
82
82
  imageWrapper,
83
- editor.dictionary.file_blocks.image.add_button_text,
84
83
  icon.firstElementChild as HTMLElement,
85
84
  );
86
85
  };
@@ -170,11 +170,8 @@ const TableParagraph = createStronglyTypedTiptapNode({
170
170
  ];
171
171
  },
172
172
 
173
- renderHTML({ node, HTMLAttributes }) {
174
- // Insert a line break if there is no content, in order to preserve the
175
- // correct cell height. Otherwise, the cell will have a height of zero +
176
- // padding.
177
- return ["p", HTMLAttributes, node.childCount ? 0 : ["br"]];
173
+ renderHTML({ HTMLAttributes }) {
174
+ return ["p", HTMLAttributes, 0];
178
175
  },
179
176
  });
180
177
 
@@ -80,7 +80,6 @@ export const videoRender = (
80
80
  editor,
81
81
  { dom: videoWrapper },
82
82
  videoWrapper,
83
- editor.dictionary.file_blocks.video.add_button_text,
84
83
  icon.firstElementChild as HTMLElement,
85
84
  );
86
85
  };