@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.
- package/dist/blocknote.js +1019 -957
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +6 -6
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +2 -2
- package/src/api/blockManipulation/blockManipulation.test.ts +7 -32
- package/src/api/clipboard/__snapshots__/childToParent.html +1 -0
- package/src/api/clipboard/__snapshots__/childrenToNextParent.html +1 -0
- package/src/api/clipboard/__snapshots__/childrenToNextParentsChildren.html +1 -0
- package/src/api/clipboard/__snapshots__/image.html +1 -0
- package/src/api/clipboard/__snapshots__/multipleStyledText.html +1 -0
- package/src/api/clipboard/__snapshots__/nestedImage.html +1 -0
- package/src/api/clipboard/__snapshots__/partialChildToParent.html +1 -0
- package/src/api/clipboard/__snapshots__/styledText.html +1 -0
- package/src/api/clipboard/__snapshots__/tableAllCells.html +1 -0
- package/src/api/clipboard/__snapshots__/tableCell.html +1 -0
- package/src/api/clipboard/__snapshots__/tableCellText.html +1 -0
- package/src/api/clipboard/__snapshots__/tableRow.html +1 -0
- package/src/api/clipboard/__snapshots__/unstyledText.html +1 -0
- package/src/api/clipboard/clipboard.test.ts +284 -0
- package/src/api/{parsers → clipboard/fromClipboard}/fileDropExtension.ts +2 -2
- package/src/api/{parsers → clipboard/fromClipboard}/handleFileInsertion.ts +4 -4
- package/src/api/{parsers → clipboard/fromClipboard}/pasteExtension.ts +14 -7
- package/src/api/{exporters → clipboard/toClipboard}/copyExtension.ts +70 -43
- package/src/api/exporters/html/externalHTMLExporter.ts +14 -7
- package/src/api/exporters/html/htmlConversion.test.ts +4 -147
- package/src/api/exporters/html/internalHTMLSerializer.ts +5 -2
- package/src/api/parsers/html/parseHTML.test.ts +3 -6
- package/src/api/parsers/markdown/__snapshots__/pasted/complex.json +319 -0
- package/src/api/parsers/markdown/__snapshots__/pasted/issue-226-1.json +81 -0
- package/src/api/parsers/{html/__snapshots__/paste/parse-deep-nested-content.json → markdown/__snapshots__/pasted/issue-226-2.json} +35 -110
- package/src/api/parsers/markdown/__snapshots__/pasted/nested.json +81 -0
- package/src/api/parsers/markdown/__snapshots__/pasted/non-nested.json +81 -0
- package/src/api/parsers/markdown/__snapshots__/pasted/styled.json +61 -0
- package/src/api/parsers/markdown/parseMarkdown.test.ts +15 -0
- package/src/api/testUtil/paste.ts +46 -0
- package/src/blocks/TableBlockContent/TableBlockContent.ts +0 -1
- package/src/editor/BlockNoteEditor.ts +2 -2
- package/src/editor/BlockNoteExtensions.ts +3 -3
- package/src/editor/BlockNoteTipTapEditor.ts +34 -7
- package/src/editor/transformPasted.ts +34 -2
- package/src/extensions/SideMenu/SideMenuPlugin.ts +6 -7
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +26 -0
- package/src/schema/blocks/createSpec.ts +20 -15
- package/types/src/api/clipboard/clipboard.test.d.ts +1 -0
- package/types/src/api/clipboard/fromClipboard/fileDropExtension.d.ts +6 -0
- package/types/src/api/{parsers → clipboard/fromClipboard}/handleFileInsertion.d.ts +2 -2
- package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +6 -0
- package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +12 -0
- package/types/src/api/exporters/html/externalHTMLExporter.d.ts +1 -0
- package/types/src/api/testUtil/paste.d.ts +2 -0
- package/types/src/editor/BlockNoteEditor.d.ts +1 -1
- package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -1
- package/types/src/editor/transformPasted.d.ts +8 -1
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +3 -0
- package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionLeavesBlockChildren.html +0 -1
- package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionSpansBlocksChildren.html +0 -1
- package/src/api/parsers/html/__snapshots__/paste/parse-google-docs-html.json +0 -476
- package/types/src/api/exporters/copyExtension.d.ts +0 -6
- package/types/src/api/parsers/fileDropExtension.d.ts +0 -6
- package/types/src/api/parsers/pasteExtension.d.ts +0 -6
- /package/src/api/{exporters/html/__snapshots_fragment_edge_cases__/selectionWithinBlockChildren.html → clipboard/__snapshots__/multipleChildren.html} +0 -0
- /package/src/api/{parsers → clipboard/fromClipboard}/acceptedMIMETypes.ts +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/list-test.json → list-test.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-basic-block-types.json → parse-basic-block-types.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-div-with-inline-content.json → parse-div-with-inline-content.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-divs.json → parse-divs.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-fake-image-caption.json → parse-fake-image-caption.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-image-in-paragraph.json → parse-image-in-paragraph.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-mixed-nested-lists.json → parse-mixed-nested-lists.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-nested-lists-with-paragraphs.json → parse-nested-lists-with-paragraphs.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-nested-lists.json → parse-nested-lists.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-notion-html.json → parse-notion-html.json} +0 -0
- /package/src/api/parsers/html/__snapshots__/{paste/parse-two-divs.json → parse-two-divs.json} +0 -0
- /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
|
+
}
|
|
@@ -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 =
|
|
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/
|
|
15
|
-
import { createDropFileExtension } from "../api/
|
|
16
|
-
import { createPasteFromClipboardExtension } from "../api/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
231
|
-
|
|
232
|
-
selectedSlice
|
|
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",
|
|
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
|
|
67
|
-
//
|
|
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
|
-
|
|
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
|
|
163
|
-
//
|
|
164
|
-
// this is
|
|
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
|
-
|
|
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 "
|
|
2
|
-
import { BlockSchema, InlineContentSchema, StyleSchema } from "
|
|
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>;
|