@blocknote/core 0.15.10 → 0.16.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.js +1019 -957
  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 +2 -2
  7. package/src/api/blockManipulation/blockManipulation.test.ts +7 -32
  8. package/src/api/clipboard/__snapshots__/childToParent.html +1 -0
  9. package/src/api/clipboard/__snapshots__/childrenToNextParent.html +1 -0
  10. package/src/api/clipboard/__snapshots__/childrenToNextParentsChildren.html +1 -0
  11. package/src/api/clipboard/__snapshots__/image.html +1 -0
  12. package/src/api/clipboard/__snapshots__/multipleStyledText.html +1 -0
  13. package/src/api/clipboard/__snapshots__/nestedImage.html +1 -0
  14. package/src/api/clipboard/__snapshots__/partialChildToParent.html +1 -0
  15. package/src/api/clipboard/__snapshots__/styledText.html +1 -0
  16. package/src/api/clipboard/__snapshots__/tableAllCells.html +1 -0
  17. package/src/api/clipboard/__snapshots__/tableCell.html +1 -0
  18. package/src/api/clipboard/__snapshots__/tableCellText.html +1 -0
  19. package/src/api/clipboard/__snapshots__/tableRow.html +1 -0
  20. package/src/api/clipboard/__snapshots__/unstyledText.html +1 -0
  21. package/src/api/clipboard/clipboard.test.ts +284 -0
  22. package/src/api/{parsers → clipboard/fromClipboard}/fileDropExtension.ts +2 -2
  23. package/src/api/{parsers → clipboard/fromClipboard}/handleFileInsertion.ts +4 -4
  24. package/src/api/{parsers → clipboard/fromClipboard}/pasteExtension.ts +14 -7
  25. package/src/api/{exporters → clipboard/toClipboard}/copyExtension.ts +70 -43
  26. package/src/api/exporters/html/externalHTMLExporter.ts +14 -7
  27. package/src/api/exporters/html/htmlConversion.test.ts +4 -147
  28. package/src/api/exporters/html/internalHTMLSerializer.ts +5 -2
  29. package/src/api/parsers/html/parseHTML.test.ts +3 -6
  30. package/src/api/parsers/markdown/__snapshots__/pasted/complex.json +319 -0
  31. package/src/api/parsers/markdown/__snapshots__/pasted/issue-226-1.json +81 -0
  32. package/src/api/parsers/{html/__snapshots__/paste/parse-deep-nested-content.json → markdown/__snapshots__/pasted/issue-226-2.json} +35 -110
  33. package/src/api/parsers/markdown/__snapshots__/pasted/nested.json +81 -0
  34. package/src/api/parsers/markdown/__snapshots__/pasted/non-nested.json +81 -0
  35. package/src/api/parsers/markdown/__snapshots__/pasted/styled.json +61 -0
  36. package/src/api/parsers/markdown/parseMarkdown.test.ts +15 -0
  37. package/src/api/testUtil/paste.ts +46 -0
  38. package/src/blocks/TableBlockContent/TableBlockContent.ts +0 -1
  39. package/src/editor/BlockNoteEditor.ts +2 -2
  40. package/src/editor/BlockNoteExtensions.ts +3 -3
  41. package/src/editor/BlockNoteTipTapEditor.ts +34 -7
  42. package/src/editor/transformPasted.ts +34 -2
  43. package/src/extensions/SideMenu/SideMenuPlugin.ts +6 -7
  44. package/src/extensions/TableHandles/TableHandlesPlugin.ts +26 -0
  45. package/src/schema/blocks/createSpec.ts +20 -15
  46. package/types/src/api/clipboard/clipboard.test.d.ts +1 -0
  47. package/types/src/api/clipboard/fromClipboard/fileDropExtension.d.ts +6 -0
  48. package/types/src/api/{parsers → clipboard/fromClipboard}/handleFileInsertion.d.ts +2 -2
  49. package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +6 -0
  50. package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +12 -0
  51. package/types/src/api/exporters/html/externalHTMLExporter.d.ts +1 -0
  52. package/types/src/api/testUtil/paste.d.ts +2 -0
  53. package/types/src/editor/BlockNoteEditor.d.ts +1 -1
  54. package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -1
  55. package/types/src/editor/transformPasted.d.ts +8 -1
  56. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +3 -0
  57. package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionLeavesBlockChildren.html +0 -1
  58. package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionSpansBlocksChildren.html +0 -1
  59. package/src/api/parsers/html/__snapshots__/paste/parse-google-docs-html.json +0 -476
  60. package/types/src/api/exporters/copyExtension.d.ts +0 -6
  61. package/types/src/api/parsers/fileDropExtension.d.ts +0 -6
  62. package/types/src/api/parsers/pasteExtension.d.ts +0 -6
  63. /package/src/api/{exporters/html/__snapshots_fragment_edge_cases__/selectionWithinBlockChildren.html → clipboard/__snapshots__/multipleChildren.html} +0 -0
  64. /package/src/api/{parsers → clipboard/fromClipboard}/acceptedMIMETypes.ts +0 -0
  65. /package/src/api/parsers/html/__snapshots__/{paste/list-test.json → list-test.json} +0 -0
  66. /package/src/api/parsers/html/__snapshots__/{paste/parse-basic-block-types.json → parse-basic-block-types.json} +0 -0
  67. /package/src/api/parsers/html/__snapshots__/{paste/parse-div-with-inline-content.json → parse-div-with-inline-content.json} +0 -0
  68. /package/src/api/parsers/html/__snapshots__/{paste/parse-divs.json → parse-divs.json} +0 -0
  69. /package/src/api/parsers/html/__snapshots__/{paste/parse-fake-image-caption.json → parse-fake-image-caption.json} +0 -0
  70. /package/src/api/parsers/html/__snapshots__/{paste/parse-image-in-paragraph.json → parse-image-in-paragraph.json} +0 -0
  71. /package/src/api/parsers/html/__snapshots__/{paste/parse-mixed-nested-lists.json → parse-mixed-nested-lists.json} +0 -0
  72. /package/src/api/parsers/html/__snapshots__/{paste/parse-nested-lists-with-paragraphs.json → parse-nested-lists-with-paragraphs.json} +0 -0
  73. /package/src/api/parsers/html/__snapshots__/{paste/parse-nested-lists.json → parse-nested-lists.json} +0 -0
  74. /package/src/api/parsers/html/__snapshots__/{paste/parse-notion-html.json → parse-notion-html.json} +0 -0
  75. /package/src/api/parsers/html/__snapshots__/{paste/parse-two-divs.json → parse-two-divs.json} +0 -0
  76. /package/types/src/api/{parsers → clipboard/fromClipboard}/acceptedMIMETypes.d.ts +0 -0
@@ -0,0 +1,81 @@
1
+ [
2
+ {
3
+ "id": "5",
4
+ "type": "paragraph",
5
+ "props": {
6
+ "textColor": "default",
7
+ "backgroundColor": "default",
8
+ "textAlignment": "left"
9
+ },
10
+ "content": [
11
+ {
12
+ "type": "text",
13
+ "text": "# Heading",
14
+ "styles": {}
15
+ }
16
+ ],
17
+ "children": []
18
+ },
19
+ {
20
+ "id": "6",
21
+ "type": "paragraph",
22
+ "props": {
23
+ "textColor": "default",
24
+ "backgroundColor": "default",
25
+ "textAlignment": "left"
26
+ },
27
+ "content": [
28
+ {
29
+ "type": "text",
30
+ "text": "Paragraph",
31
+ "styles": {}
32
+ }
33
+ ],
34
+ "children": []
35
+ },
36
+ {
37
+ "id": "7",
38
+ "type": "paragraph",
39
+ "props": {
40
+ "textColor": "default",
41
+ "backgroundColor": "default",
42
+ "textAlignment": "left"
43
+ },
44
+ "content": [
45
+ {
46
+ "type": "text",
47
+ "text": "* Bullet List Item",
48
+ "styles": {}
49
+ }
50
+ ],
51
+ "children": []
52
+ },
53
+ {
54
+ "id": "8",
55
+ "type": "paragraph",
56
+ "props": {
57
+ "textColor": "default",
58
+ "backgroundColor": "default",
59
+ "textAlignment": "left"
60
+ },
61
+ "content": [
62
+ {
63
+ "type": "text",
64
+ "text": " 1. Numbered List Item",
65
+ "styles": {}
66
+ }
67
+ ],
68
+ "children": []
69
+ },
70
+ {
71
+ "id": "9",
72
+ "type": "paragraph",
73
+ "props": {
74
+ "textColor": "default",
75
+ "backgroundColor": "default",
76
+ "textAlignment": "left"
77
+ },
78
+ "content": [],
79
+ "children": []
80
+ }
81
+ ]
@@ -0,0 +1,81 @@
1
+ [
2
+ {
3
+ "id": "5",
4
+ "type": "paragraph",
5
+ "props": {
6
+ "textColor": "default",
7
+ "backgroundColor": "default",
8
+ "textAlignment": "left"
9
+ },
10
+ "content": [
11
+ {
12
+ "type": "text",
13
+ "text": "# Heading",
14
+ "styles": {}
15
+ }
16
+ ],
17
+ "children": []
18
+ },
19
+ {
20
+ "id": "6",
21
+ "type": "paragraph",
22
+ "props": {
23
+ "textColor": "default",
24
+ "backgroundColor": "default",
25
+ "textAlignment": "left"
26
+ },
27
+ "content": [
28
+ {
29
+ "type": "text",
30
+ "text": "Paragraph",
31
+ "styles": {}
32
+ }
33
+ ],
34
+ "children": []
35
+ },
36
+ {
37
+ "id": "7",
38
+ "type": "paragraph",
39
+ "props": {
40
+ "textColor": "default",
41
+ "backgroundColor": "default",
42
+ "textAlignment": "left"
43
+ },
44
+ "content": [
45
+ {
46
+ "type": "text",
47
+ "text": "* Bullet List Item",
48
+ "styles": {}
49
+ }
50
+ ],
51
+ "children": []
52
+ },
53
+ {
54
+ "id": "8",
55
+ "type": "paragraph",
56
+ "props": {
57
+ "textColor": "default",
58
+ "backgroundColor": "default",
59
+ "textAlignment": "left"
60
+ },
61
+ "content": [
62
+ {
63
+ "type": "text",
64
+ "text": "1. Numbered List Item",
65
+ "styles": {}
66
+ }
67
+ ],
68
+ "children": []
69
+ },
70
+ {
71
+ "id": "9",
72
+ "type": "paragraph",
73
+ "props": {
74
+ "textColor": "default",
75
+ "backgroundColor": "default",
76
+ "textAlignment": "left"
77
+ },
78
+ "content": [],
79
+ "children": []
80
+ }
81
+ ]
@@ -0,0 +1,61 @@
1
+ [
2
+ {
3
+ "id": "2",
4
+ "type": "paragraph",
5
+ "props": {
6
+ "textColor": "default",
7
+ "backgroundColor": "default",
8
+ "textAlignment": "left"
9
+ },
10
+ "content": [
11
+ {
12
+ "type": "text",
13
+ "text": "Bold",
14
+ "styles": {
15
+ "bold": true
16
+ }
17
+ },
18
+ {
19
+ "type": "text",
20
+ "text": " ",
21
+ "styles": {}
22
+ },
23
+ {
24
+ "type": "text",
25
+ "text": "Italic",
26
+ "styles": {
27
+ "italic": true
28
+ }
29
+ },
30
+ {
31
+ "type": "text",
32
+ "text": " ",
33
+ "styles": {}
34
+ },
35
+ {
36
+ "type": "text",
37
+ "text": "Strikethrough",
38
+ "styles": {
39
+ "strike": true
40
+ }
41
+ },
42
+ {
43
+ "type": "text",
44
+ "text": " ***Multiple***",
45
+ "styles": {}
46
+ }
47
+ ],
48
+ "children": []
49
+ },
50
+ {
51
+ "id": "3",
52
+ "type": "paragraph",
53
+ "props": {
54
+ "textColor": "default",
55
+ "backgroundColor": "default",
56
+ "textAlignment": "left"
57
+ },
58
+ "content": [],
59
+ "children": []
60
+ }
61
+ ]
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { BlockNoteEditor } from "../../..";
3
+ import { doPaste } from "../../testUtil/paste";
3
4
 
4
5
  async function parseMarkdownAndCompareSnapshots(
5
6
  md: string,
@@ -14,6 +15,20 @@ async function parseMarkdownAndCompareSnapshots(
14
15
  expect(JSON.stringify(blocks, undefined, 2)).toMatchFileSnapshot(
15
16
  snapshotPath
16
17
  );
18
+
19
+ doPaste(
20
+ editor._tiptapEditor.view,
21
+ md,
22
+ null,
23
+ true,
24
+ new ClipboardEvent("paste")
25
+ );
26
+
27
+ const pastedSnapshotPath = "./__snapshots__/pasted/" + snapshotName + ".json";
28
+ expect(JSON.stringify(editor.document, undefined, 2)).toMatchFileSnapshot(
29
+ pastedSnapshotPath
30
+ );
31
+
17
32
  editor.mount(undefined);
18
33
  }
19
34
 
@@ -0,0 +1,46 @@
1
+ import { Slice } from "@tiptap/pm/model";
2
+ import { EditorView } from "@tiptap/pm/view";
3
+ import * as pmView from "@tiptap/pm/view";
4
+
5
+ function sliceSingleNode(slice: Slice) {
6
+ return slice.openStart === 0 &&
7
+ slice.openEnd === 0 &&
8
+ slice.content.childCount === 1
9
+ ? slice.content.firstChild
10
+ : null;
11
+ }
12
+
13
+ // This function is a copy of the `doPaste` function from `@tiptap/pm/view`,
14
+ // but made to work in a JSDOM environment. To do this, the `tr.scrollIntoView`
15
+ // call has been removed.
16
+ // https://github.com/ProseMirror/prosemirror-view/blob/17b508f618c944c54776f8ddac45edcb49970796/src/input.ts#L624
17
+ export function doPaste(
18
+ view: EditorView,
19
+ text: string,
20
+ html: string | null,
21
+ preferPlain: boolean,
22
+ event: ClipboardEvent
23
+ ) {
24
+ const slice = (pmView as any).__parseFromClipboard(
25
+ view,
26
+ text,
27
+ html,
28
+ preferPlain,
29
+ view.state.selection.$from
30
+ );
31
+ if (
32
+ view.someProp("handlePaste", (f) => f(view, event, slice || Slice.empty))
33
+ ) {
34
+ return true;
35
+ }
36
+ if (!slice) {
37
+ return false;
38
+ }
39
+
40
+ const singleNode = sliceSingleNode(slice);
41
+ const tr = singleNode
42
+ ? view.state.tr.replaceSelectionWith(singleNode, preferPlain)
43
+ : view.state.tr.replaceSelection(slice);
44
+ view.dispatch(tr.setMeta("paste", true).setMeta("uiEvent", "paste"));
45
+ return true;
46
+ }
@@ -46,7 +46,6 @@ const TableParagraph = Node.create({
46
46
 
47
47
  parseHTML() {
48
48
  return [
49
- { tag: "td" },
50
49
  {
51
50
  tag: "p",
52
51
  getAttrs: (element) => {
@@ -41,9 +41,9 @@ import {
41
41
  InlineContentSchema,
42
42
  InlineContentSpecs,
43
43
  PartialInlineContent,
44
- Styles,
45
44
  StyleSchema,
46
45
  StyleSpecs,
46
+ Styles,
47
47
  } from "../schema";
48
48
  import { mergeCSSClasses } from "../util/browser";
49
49
  import { NoInfer, UnreachableCaseError } from "../util/typescript";
@@ -463,7 +463,7 @@ export class BlockNoteEditor<
463
463
  };
464
464
 
465
465
  if (!this.headless) {
466
- this._tiptapEditor = new BlockNoteTipTapEditor(
466
+ this._tiptapEditor = BlockNoteTipTapEditor.create(
467
467
  tiptapOptions,
468
468
  this.schema.styleSchema
469
469
  ) as BlockNoteTipTapEditor & {
@@ -11,9 +11,9 @@ import { History } from "@tiptap/extension-history";
11
11
  import { Link } from "@tiptap/extension-link";
12
12
  import { Text } from "@tiptap/extension-text";
13
13
  import * as Y from "yjs";
14
- import { createCopyToClipboardExtension } from "../api/exporters/copyExtension";
15
- import { createDropFileExtension } from "../api/parsers/fileDropExtension";
16
- import { createPasteFromClipboardExtension } from "../api/parsers/pasteExtension";
14
+ import { createCopyToClipboardExtension } from "../api/clipboard/toClipboard/copyExtension";
15
+ import { createDropFileExtension } from "../api/clipboard/fromClipboard/fileDropExtension";
16
+ import { createPasteFromClipboardExtension } from "../api/clipboard/fromClipboard/pasteExtension";
17
17
  import { BackgroundColorExtension } from "../extensions/BackgroundColor/BackgroundColorExtension";
18
18
  import { TextAlignmentExtension } from "../extensions/TextAlignment/TextAlignmentExtension";
19
19
  import { TextColorExtension } from "../extensions/TextColor/TextColorExtension";
@@ -23,7 +23,32 @@ export type BlockNoteTipTapEditorOptions = Partial<
23
23
  export class BlockNoteTipTapEditor extends TiptapEditor {
24
24
  private _state: EditorState;
25
25
 
26
- constructor(options: BlockNoteTipTapEditorOptions, styleSchema: StyleSchema) {
26
+ public static create = (
27
+ options: BlockNoteTipTapEditorOptions,
28
+ styleSchema: StyleSchema
29
+ ) => {
30
+ // because we separate the constructor from the creation of the view,
31
+ // we need to patch setTimeout to prevent this code from having any effect:
32
+ // https://github.com/ueberdosis/tiptap/blob/45bac803283446795ad1b03f43d3746fa54a68ff/packages/core/src/Editor.ts#L117
33
+ const oldSetTimeout = globalThis?.window?.setTimeout;
34
+ if (typeof globalThis?.window?.setTimeout !== "undefined") {
35
+ globalThis.window.setTimeout = (() => {
36
+ return 0;
37
+ }) as any;
38
+ }
39
+ try {
40
+ return new BlockNoteTipTapEditor(options, styleSchema);
41
+ } finally {
42
+ if (oldSetTimeout) {
43
+ globalThis.window.setTimeout = oldSetTimeout;
44
+ }
45
+ }
46
+ };
47
+
48
+ protected constructor(
49
+ options: BlockNoteTipTapEditorOptions,
50
+ styleSchema: StyleSchema
51
+ ) {
27
52
  // possible fix for next.js server side rendering
28
53
  // const d = globalThis.document;
29
54
  // const w = globalThis.window;
@@ -32,14 +57,10 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
32
57
  // createElement: () => {},
33
58
  // };
34
59
  // }
35
- // if (!globalThis.window) {
36
- // globalThis.window = {
37
- // setTimeout: () => {},
38
- // };
39
- // }
60
+
40
61
  // options.injectCSS = false
41
- super({ ...options, content: undefined });
42
62
 
63
+ super({ ...options, content: undefined });
43
64
  // try {
44
65
  // globalThis.window = w;
45
66
  // } catch(e) {}
@@ -149,6 +170,12 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
149
170
  this.view.updateState(newState);
150
171
 
151
172
  this.createNodeViews();
173
+
174
+ // emit the created event, call here manually because we blocked the default call in the constructor
175
+ // (https://github.com/ueberdosis/tiptap/blob/45bac803283446795ad1b03f43d3746fa54a68ff/packages/core/src/Editor.ts#L117)
176
+ this.commands.focus(this.options.autofocus);
177
+ this.emit("create", { editor: this });
178
+ this.isInitialized = true;
152
179
  });
153
180
  }
154
181
 
@@ -1,4 +1,4 @@
1
- import { Fragment, Slice } from "@tiptap/pm/model";
1
+ import { Fragment, Schema, Slice } from "@tiptap/pm/model";
2
2
  import { EditorView } from "@tiptap/pm/view";
3
3
 
4
4
  // helper function to remove a child from a fragment
@@ -12,6 +12,37 @@ function removeChild(node: Fragment, n: number) {
12
12
  return Fragment.from(children);
13
13
  }
14
14
 
15
+ /**
16
+ * Wrap adjacent tableRow items in a table.
17
+ *
18
+ * This makes sure the content that we paste is always a table (and not a tableRow)
19
+ * A table works better for the remaing paste handling logic, as it's actually a blockContent node
20
+ */
21
+ export function wrapTableRows(f: Fragment, schema: Schema) {
22
+ const newItems: any[] = [];
23
+ for (let i = 0; i < f.childCount; i++) {
24
+ if (f.child(i).type.name === "tableRow") {
25
+ if (
26
+ newItems.length > 0 &&
27
+ newItems[newItems.length - 1].type.name === "table"
28
+ ) {
29
+ // append to existing table
30
+ const prevTable = newItems[newItems.length - 1];
31
+ const newTable = prevTable.copy(prevTable.content.addToEnd(f.child(i)));
32
+ newItems[newItems.length - 1] = newTable;
33
+ } else {
34
+ // create new table to wrap tableRow with
35
+ const newTable = schema.nodes.table.create(undefined, f.child(i));
36
+ newItems.push(newTable);
37
+ }
38
+ } else {
39
+ newItems.push(f.child(i));
40
+ }
41
+ }
42
+ f = Fragment.from(newItems);
43
+ return f;
44
+ }
45
+
15
46
  /**
16
47
  * fix for https://github.com/ProseMirror/prosemirror/issues/1430#issuecomment-1822570821
17
48
  *
@@ -23,6 +54,8 @@ function removeChild(node: Fragment, n: number) {
23
54
  */
24
55
  export function transformPasted(slice: Slice, view: EditorView) {
25
56
  let f = Fragment.from(slice.content);
57
+ f = wrapTableRows(f, view.state.schema);
58
+
26
59
  for (let i = 0; i < f.childCount; i++) {
27
60
  if (f.child(i).type.spec.group === "blockContent") {
28
61
  const content = [f.child(i)];
@@ -54,6 +87,5 @@ export function transformPasted(slice: Slice, view: EditorView) {
54
87
  f = f.replaceChild(i, container);
55
88
  }
56
89
  }
57
-
58
90
  return new Slice(f, slice.openStart, slice.openEnd);
59
91
  }
@@ -2,9 +2,9 @@ import { PluginView } from "@tiptap/pm/state";
2
2
  import { Node } from "prosemirror-model";
3
3
  import { NodeSelection, Plugin, PluginKey, Selection } from "prosemirror-state";
4
4
  import { EditorView } from "prosemirror-view";
5
+ import * as pmView from "prosemirror-view";
5
6
 
6
7
  import { createExternalHTMLExporter } from "../../api/exporters/html/externalHTMLExporter";
7
- import { createInternalHTMLSerializer } from "../../api/exporters/html/internalHTMLSerializer";
8
8
  import { cleanHTMLToMarkdown } from "../../api/exporters/markdown/markdownExporter";
9
9
  import { getBlockInfoFromPos } from "../../api/getBlockInfoFromPos";
10
10
  import { Block } from "../../blocks/defaultBlocks";
@@ -227,11 +227,10 @@ function dragStart<
227
227
  const selectedSlice = view.state.selection.content();
228
228
  const schema = editor.pmSchema;
229
229
 
230
- const internalHTMLSerializer = createInternalHTMLSerializer(schema, editor);
231
- const internalHTML = internalHTMLSerializer.serializeProseMirrorFragment(
232
- selectedSlice.content,
233
- {}
234
- );
230
+ const clipboardHML = (pmView as any).__serializeForClipboard(
231
+ view,
232
+ selectedSlice
233
+ ).dom.innerHTML;
235
234
 
236
235
  const externalHTMLExporter = createExternalHTMLExporter(schema, editor);
237
236
  const externalHTML = externalHTMLExporter.exportProseMirrorFragment(
@@ -242,7 +241,7 @@ function dragStart<
242
241
  const plainText = cleanHTMLToMarkdown(externalHTML);
243
242
 
244
243
  e.dataTransfer.clearData();
245
- e.dataTransfer.setData("blocknote/html", internalHTML);
244
+ e.dataTransfer.setData("blocknote/html", clipboardHML);
246
245
  e.dataTransfer.setData("text/html", externalHTML);
247
246
  e.dataTransfer.setData("text/plain", plainText);
248
247
  e.dataTransfer.effectAllowed = "move";
@@ -107,6 +107,8 @@ export class TableHandlesView<
107
107
 
108
108
  public menuFrozen = false;
109
109
 
110
+ public mouseState: "up" | "down" | "selecting" = "up";
111
+
110
112
  public prevWasEditable: boolean | null = null;
111
113
 
112
114
  constructor(
@@ -127,6 +129,8 @@ export class TableHandlesView<
127
129
  };
128
130
 
129
131
  pmView.dom.addEventListener("mousemove", this.mouseMoveHandler);
132
+ pmView.dom.addEventListener("mousedown", this.viewMousedownHandler);
133
+ pmView.dom.addEventListener("mouseup", this.viewMouseupHandler);
130
134
 
131
135
  pmView.root.addEventListener(
132
136
  "dragover",
@@ -140,11 +144,33 @@ export class TableHandlesView<
140
144
  pmView.root.addEventListener("scroll", this.scrollHandler, true);
141
145
  }
142
146
 
147
+ viewMousedownHandler = () => {
148
+ this.mouseState = "down";
149
+ };
150
+
151
+ viewMouseupHandler = (event: MouseEvent) => {
152
+ this.mouseState = "up";
153
+ this.mouseMoveHandler(event);
154
+ };
155
+
143
156
  mouseMoveHandler = (event: MouseEvent) => {
144
157
  if (this.menuFrozen) {
145
158
  return;
146
159
  }
147
160
 
161
+ if (this.mouseState === "down") {
162
+ this.mouseState = "selecting";
163
+
164
+ if (this.state?.show) {
165
+ this.state.show = false;
166
+ this.emitUpdate();
167
+ }
168
+ }
169
+
170
+ if (this.mouseState === "selecting") {
171
+ return;
172
+ }
173
+
148
174
  const target = domCellAround(event.target as HTMLElement);
149
175
 
150
176
  if (!target || !this.editor.isEditable) {
@@ -63,14 +63,10 @@ export type CustomBlockImplementation<
63
63
  ) => PartialBlockFromConfig<T, I, S>["props"] | undefined;
64
64
  };
65
65
 
66
- // Function that enables copying of selected content within non-selectable
67
- // blocks.
66
+ // Function that causes events within non-selectable blocks to be handled by the
67
+ // browser instead of the editor.
68
68
  export function applyNonSelectableBlockFix(nodeView: NodeView, editor: Editor) {
69
69
  nodeView.stopEvent = (event) => {
70
- // Ensures copy events are handled by the browser and not by ProseMirror.
71
- if (event.type === "copy" || event.type === "cut") {
72
- return true;
73
- }
74
70
  // Blurs the editor on mouse down as the block is non-selectable. This is
75
71
  // mainly done to prevent UI elements like the formatting toolbar from being
76
72
  // visible while content within a non-selectable block is selected.
@@ -78,9 +74,9 @@ export function applyNonSelectableBlockFix(nodeView: NodeView, editor: Editor) {
78
74
  setTimeout(() => {
79
75
  editor.view.dom.blur();
80
76
  }, 10);
81
- return true;
82
77
  }
83
- return false;
78
+
79
+ return true;
84
80
  };
85
81
  }
86
82
 
@@ -158,15 +154,24 @@ export function createBlockSpec<
158
154
  return getParseRules(blockConfig, blockImplementation.parse);
159
155
  },
160
156
 
161
- renderHTML() {
162
- // renderHTML is not really used, as we always use a nodeView, and we use toExternalHTML / toInternalHTML for serialization
163
- // There's an edge case when this gets called nevertheless; before the nodeviews have been mounted
164
- // this is why we implement it with a temporary placeholder
157
+ renderHTML({ HTMLAttributes }) {
158
+ // renderHTML is used for copy/pasting content from the editor back into
159
+ // the editor, so we need to make sure the `blockContent` element is
160
+ // structured correctly as this is what's used for parsing blocks. We
161
+ // just render a placeholder div inside as the `blockContent` element
162
+ // already has all the information needed for proper parsing.
165
163
  const div = document.createElement("div");
166
164
  div.setAttribute("data-tmp-placeholder", "true");
167
- return {
168
- dom: div,
169
- };
165
+ return wrapInBlockStructure(
166
+ {
167
+ dom: div,
168
+ },
169
+ blockConfig.type,
170
+ {},
171
+ blockConfig.propSchema,
172
+ blockConfig.isFileBlock,
173
+ HTMLAttributes
174
+ );
170
175
  },
171
176
 
172
177
  addNodeView() {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
3
+ import { InlineContentSchema, StyleSchema } from "../../../schema";
4
+ export declare const createDropFileExtension: <BSchema extends Record<string, import("../../../schema").BlockConfig>, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>) => Extension<{
5
+ editor: BlockNoteEditor<BSchema, I, S>;
6
+ }, undefined>;
@@ -1,3 +1,3 @@
1
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
2
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
1
+ import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
2
+ import { BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema";
3
3
  export declare function handleFileInsertion<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(event: DragEvent | ClipboardEvent, editor: BlockNoteEditor<BSchema, I, S>): Promise<void>;
@@ -0,0 +1,6 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
3
+ import { InlineContentSchema, StyleSchema } from "../../../schema";
4
+ export declare const createPasteFromClipboardExtension: <BSchema extends Record<string, import("../../../schema").BlockConfig>, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>) => Extension<{
5
+ editor: BlockNoteEditor<BSchema, I, S>;
6
+ }, undefined>;
@@ -0,0 +1,12 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { EditorView } from "prosemirror-view";
3
+ import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
4
+ import { BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema";
5
+ export declare function selectedFragmentToHTML<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(view: EditorView, editor: BlockNoteEditor<BSchema, I, S>): Promise<{
6
+ clipboardHTML: string;
7
+ externalHTML: string;
8
+ markdown: string;
9
+ }>;
10
+ export declare const createCopyToClipboardExtension: <BSchema extends Record<string, import("../../../schema").BlockConfig>, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>) => Extension<{
11
+ editor: BlockNoteEditor<BSchema, I, S>;
12
+ }, undefined>;
@@ -8,6 +8,7 @@ export interface ExternalHTMLExporter<BSchema extends BlockSchema, I extends Inl
8
8
  }) => string;
9
9
  exportProseMirrorFragment: (fragment: Fragment, options: {
10
10
  document?: Document;
11
+ simplifyBlocks?: boolean;
11
12
  }) => string;
12
13
  }
13
14
  export declare const createExternalHTMLExporter: <BSchema extends Record<string, import("../../../schema").BlockConfig>, I extends InlineContentSchema, S extends StyleSchema>(schema: Schema, editor: BlockNoteEditor<BSchema, I, S>) => ExternalHTMLExporter<BSchema, I, S>;
@@ -0,0 +1,2 @@
1
+ import { EditorView } from "@tiptap/pm/view";
2
+ export declare function doPaste(view: EditorView, text: string, html: string | null, preferPlain: boolean, event: ClipboardEvent): boolean;