@blocknote/core 0.15.6 → 0.15.9
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 +1486 -1350
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +5 -5
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +23 -23
- package/src/api/exporters/copyExtension.ts +48 -33
- package/src/api/exporters/html/__snapshots__/complex/misc/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/customParagraph/styled/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/file/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/file/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/file/noCaption/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/file/noName/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/fontSize/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/between-links/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/end/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/link/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/multiple/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/only/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/start/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/hardbreak/styles/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/noCaption/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/noName/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/noPreview/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/link/adjacent/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/link/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/link/styled/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/mention/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/paragraph/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/paragraph/empty/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/paragraph/lineBreaks/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/paragraph/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/paragraph/styled/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleCustomParagraph/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleCustomParagraph/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleCustomParagraph/styled/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/button/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/noName/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/small/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/tag/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionLeavesBlockChildren.html +1 -1
- package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionSpansBlocksChildren.html +1 -1
- package/src/api/exporters/html/__snapshots_fragment_edge_cases__/selectionWithinBlockChildren.html +1 -1
- package/src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts +51 -2
- package/src/api/parsers/handleFileInsertion.ts +81 -17
- package/src/blocks/AudioBlockContent/AudioBlockContent.ts +24 -48
- package/src/blocks/FileBlockContent/FileBlockContent.ts +4 -22
- package/src/blocks/FileBlockContent/fileBlockHelpers.ts +72 -1
- package/src/blocks/ImageBlockContent/ImageBlockContent.ts +36 -62
- package/src/blocks/VideoBlockContent/VideoBlockContent.ts +34 -59
- package/src/editor/BlockNoteEditor.test.ts +13 -0
- package/src/editor/BlockNoteEditor.ts +89 -13
- package/src/editor/BlockNoteExtensions.ts +4 -2
- package/src/editor/BlockNoteTipTapEditor.ts +4 -1
- package/src/extensions/FilePanel/FilePanelPlugin.ts +10 -6
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +0 -1
- package/src/extensions/SideMenu/SideMenuPlugin.ts +23 -12
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +22 -10
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +6 -1
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +5 -1
- package/src/extensions/UniqueID/UniqueID.ts +15 -4
- package/src/pm-nodes/BlockContainer.ts +1 -2
- package/src/schema/blocks/types.ts +1 -1
- package/src/schema/inlineContent/createSpec.ts +54 -5
- package/types/src/api/testUtil/cases/customBlocks.d.ts +6 -6
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +6 -6
- package/types/src/api/testUtil/cases/customStyles.d.ts +6 -6
- package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +5 -5
- package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +2 -2
- package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +11 -1
- package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +5 -5
- package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +5 -5
- package/types/src/blocks/defaultBlocks.d.ts +12 -12
- package/types/src/editor/BlockNoteEditor.d.ts +27 -5
- package/types/src/editor/BlockNoteExtensions.d.ts +1 -0
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +1 -1
- package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +1 -0
- package/types/src/schema/blocks/types.d.ts +1 -1
- package/types/src/schema/inlineContent/createSpec.d.ts +3 -3
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
2
2
|
import { PartialBlock } from "../../blocks/defaultBlocks";
|
|
3
|
-
import { insertOrUpdateBlock } from "../../extensions/SuggestionMenu/getDefaultSlashMenuItems";
|
|
4
3
|
import {
|
|
5
4
|
BlockSchema,
|
|
6
5
|
FileBlockConfig,
|
|
7
6
|
InlineContentSchema,
|
|
8
7
|
StyleSchema,
|
|
9
8
|
} from "../../schema";
|
|
9
|
+
import { getBlockInfoFromPos } from "../getBlockInfoFromPos";
|
|
10
10
|
import { acceptedMIMETypes } from "./acceptedMIMETypes";
|
|
11
11
|
|
|
12
|
+
function checkFileExtensionsMatch(
|
|
13
|
+
fileExtension1: string,
|
|
14
|
+
fileExtension2: string
|
|
15
|
+
) {
|
|
16
|
+
if (!fileExtension1.startsWith(".") || !fileExtension2.startsWith(".")) {
|
|
17
|
+
throw new Error(`The strings provided are not valid file extensions.`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return fileExtension1 === fileExtension2;
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
function checkMIMETypesMatch(mimeType1: string, mimeType2: string) {
|
|
13
24
|
const types1 = mimeType1.split("/");
|
|
14
25
|
const types2 = mimeType2.split("/");
|
|
@@ -71,29 +82,82 @@ export async function handleFileInsertion<
|
|
|
71
82
|
// Gets file block corresponding to MIME type.
|
|
72
83
|
let fileBlockType = "file";
|
|
73
84
|
for (const fileBlockConfig of fileBlockConfigs) {
|
|
74
|
-
for (const mimeType of fileBlockConfig.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
for (const mimeType of fileBlockConfig.fileBlockAccept || []) {
|
|
86
|
+
const isFileExtension = mimeType.startsWith(".");
|
|
87
|
+
const file = items[i].getAsFile();
|
|
88
|
+
|
|
89
|
+
if (file) {
|
|
90
|
+
if (
|
|
91
|
+
(!isFileExtension &&
|
|
92
|
+
file.type &&
|
|
93
|
+
checkMIMETypesMatch(items[i].type, mimeType)) ||
|
|
94
|
+
(isFileExtension &&
|
|
95
|
+
checkFileExtensionsMatch(
|
|
96
|
+
"." + file.name.split(".").pop(),
|
|
97
|
+
mimeType
|
|
98
|
+
))
|
|
99
|
+
) {
|
|
100
|
+
fileBlockType = fileBlockConfig.type;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
78
103
|
}
|
|
79
104
|
}
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
const file = items[i].getAsFile();
|
|
83
108
|
if (file) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
109
|
+
const fileBlock = {
|
|
110
|
+
type: fileBlockType,
|
|
111
|
+
props: {
|
|
112
|
+
name: file.name,
|
|
113
|
+
},
|
|
114
|
+
} as PartialBlock<BSchema, I, S>;
|
|
115
|
+
|
|
116
|
+
let insertedBlockId: string | undefined = undefined;
|
|
117
|
+
|
|
118
|
+
if (event.type === "paste") {
|
|
119
|
+
insertedBlockId = editor.insertBlocks(
|
|
120
|
+
[fileBlock],
|
|
121
|
+
editor.getTextCursorPosition().block,
|
|
122
|
+
"after"
|
|
123
|
+
)[0].id;
|
|
124
|
+
} else if (event.type === "drop") {
|
|
125
|
+
const coords = {
|
|
126
|
+
left: (event as DragEvent).clientX,
|
|
127
|
+
top: (event as DragEvent).clientY,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const pos = editor._tiptapEditor.view.posAtCoords(coords);
|
|
131
|
+
if (!pos) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const blockInfo = getBlockInfoFromPos(
|
|
136
|
+
editor._tiptapEditor.state.doc,
|
|
137
|
+
pos.pos
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
insertedBlockId = editor.insertBlocks(
|
|
141
|
+
[fileBlock],
|
|
142
|
+
blockInfo.id,
|
|
143
|
+
"after"
|
|
144
|
+
)[0].id;
|
|
145
|
+
} else {
|
|
146
|
+
return;
|
|
96
147
|
}
|
|
148
|
+
|
|
149
|
+
const updateData = await editor.uploadFile(file, insertedBlockId);
|
|
150
|
+
|
|
151
|
+
const updatedFileBlock =
|
|
152
|
+
typeof updateData === "string"
|
|
153
|
+
? ({
|
|
154
|
+
props: {
|
|
155
|
+
url: updateData,
|
|
156
|
+
},
|
|
157
|
+
} as PartialBlock<BSchema, I, S>)
|
|
158
|
+
: { ...updateData };
|
|
159
|
+
|
|
160
|
+
editor.updateBlock(insertedBlockId, updatedFileBlock);
|
|
97
161
|
}
|
|
98
162
|
}
|
|
99
163
|
}
|
|
@@ -9,10 +9,9 @@ import {
|
|
|
9
9
|
import { defaultProps } from "../defaultProps";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
-
createAddFileButton,
|
|
13
|
-
createDefaultFilePreview,
|
|
14
12
|
createFigureWithCaption,
|
|
15
13
|
createFileAndCaptionWrapper,
|
|
14
|
+
createFileBlockWrapper,
|
|
16
15
|
createLinkWithCaption,
|
|
17
16
|
parseFigureElement,
|
|
18
17
|
} from "../FileBlockContent/fileBlockHelpers";
|
|
@@ -43,58 +42,35 @@ export const audioBlockConfig = {
|
|
|
43
42
|
propSchema: audioPropSchema,
|
|
44
43
|
content: "none",
|
|
45
44
|
isFileBlock: true,
|
|
46
|
-
|
|
45
|
+
fileBlockAccept: ["audio/*"],
|
|
47
46
|
} satisfies FileBlockConfig;
|
|
48
47
|
|
|
49
48
|
export const audioRender = (
|
|
50
49
|
block: BlockFromConfig<typeof audioBlockConfig, any, any>,
|
|
51
50
|
editor: BlockNoteEditor<any, any, any>
|
|
52
51
|
) => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const file = createDefaultFilePreview(block).dom;
|
|
76
|
-
const element = createFileAndCaptionWrapper(block, file);
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
dom: element.dom,
|
|
80
|
-
};
|
|
81
|
-
} else {
|
|
82
|
-
const audio = document.createElement("audio");
|
|
83
|
-
audio.className = "bn-audio";
|
|
84
|
-
editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
|
|
85
|
-
audio.src = downloadUrl;
|
|
86
|
-
});
|
|
87
|
-
audio.controls = true;
|
|
88
|
-
audio.contentEditable = "false";
|
|
89
|
-
audio.draggable = false;
|
|
90
|
-
|
|
91
|
-
const element = createFileAndCaptionWrapper(block, audio);
|
|
92
|
-
wrapper.appendChild(element.dom);
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
dom: wrapper,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
52
|
+
const icon = document.createElement("div");
|
|
53
|
+
icon.innerHTML =
|
|
54
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 16.0001H5.88889L11.1834 20.3319C11.2727 20.405 11.3846 20.4449 11.5 20.4449C11.7761 20.4449 12 20.2211 12 19.9449V4.05519C12 3.93977 11.9601 3.8279 11.887 3.73857C11.7121 3.52485 11.3971 3.49335 11.1834 3.66821L5.88889 8.00007H2C1.44772 8.00007 1 8.44778 1 9.00007V15.0001C1 15.5524 1.44772 16.0001 2 16.0001ZM23 12C23 15.292 21.5539 18.2463 19.2622 20.2622L17.8445 18.8444C19.7758 17.1937 21 14.7398 21 12C21 9.26016 19.7758 6.80629 17.8445 5.15557L19.2622 3.73779C21.5539 5.75368 23 8.70795 23 12ZM18 12C18 10.0883 17.106 8.38548 15.7133 7.28673L14.2842 8.71584C15.3213 9.43855 16 10.64 16 12C16 13.36 15.3213 14.5614 14.2842 15.2841L15.7133 16.7132C17.106 15.6145 18 13.9116 18 12Z"></path></svg>';
|
|
55
|
+
|
|
56
|
+
const audio = document.createElement("audio");
|
|
57
|
+
audio.className = "bn-audio";
|
|
58
|
+
editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
|
|
59
|
+
audio.src = downloadUrl;
|
|
60
|
+
});
|
|
61
|
+
audio.controls = true;
|
|
62
|
+
audio.contentEditable = "false";
|
|
63
|
+
audio.draggable = false;
|
|
64
|
+
|
|
65
|
+
const element = createFileAndCaptionWrapper(block, audio);
|
|
66
|
+
|
|
67
|
+
return createFileBlockWrapper(
|
|
68
|
+
block,
|
|
69
|
+
editor,
|
|
70
|
+
element,
|
|
71
|
+
editor.dictionary.file_blocks.audio.add_button_text,
|
|
72
|
+
icon.firstElementChild as HTMLElement
|
|
73
|
+
);
|
|
98
74
|
};
|
|
99
75
|
|
|
100
76
|
export const audioParse = (
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
} from "../../schema";
|
|
8
8
|
import { defaultProps } from "../defaultProps";
|
|
9
9
|
import {
|
|
10
|
-
createAddFileButton,
|
|
11
10
|
createDefaultFilePreview,
|
|
12
11
|
createFileAndCaptionWrapper,
|
|
12
|
+
createFileBlockWrapper,
|
|
13
13
|
createLinkWithCaption,
|
|
14
14
|
parseEmbedElement,
|
|
15
15
|
parseFigureElement,
|
|
@@ -42,28 +42,10 @@ export const fileRender = (
|
|
|
42
42
|
block: BlockFromConfig<typeof fileBlockConfig, any, any>,
|
|
43
43
|
editor: BlockNoteEditor<any, any, any>
|
|
44
44
|
) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const wrapper = document.createElement("div");
|
|
48
|
-
wrapper.className = "bn-file-block-content-wrapper";
|
|
45
|
+
const file = createDefaultFilePreview(block).dom;
|
|
46
|
+
const element = createFileAndCaptionWrapper(block, file);
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
const addFileButton = createAddFileButton(block, editor);
|
|
52
|
-
wrapper.appendChild(addFileButton.dom);
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
dom: wrapper,
|
|
56
|
-
destroy: addFileButton.destroy,
|
|
57
|
-
};
|
|
58
|
-
} else {
|
|
59
|
-
const file = createDefaultFilePreview(block).dom;
|
|
60
|
-
const element = createFileAndCaptionWrapper(block, file);
|
|
61
|
-
wrapper.appendChild(element.dom);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
dom: wrapper,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
48
|
+
return createFileBlockWrapper(block, editor, element);
|
|
67
49
|
};
|
|
68
50
|
|
|
69
51
|
export const fileParse = (element: HTMLElement) => {
|
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BlockFromConfig,
|
|
4
|
+
BlockSchemaWithBlock,
|
|
5
|
+
FileBlockConfig,
|
|
6
|
+
} from "../../schema";
|
|
7
|
+
|
|
8
|
+
export const createFileBlockWrapper = (
|
|
9
|
+
block: BlockFromConfig<FileBlockConfig, any, any>,
|
|
10
|
+
editor: BlockNoteEditor<
|
|
11
|
+
BlockSchemaWithBlock<FileBlockConfig["type"], FileBlockConfig>,
|
|
12
|
+
any,
|
|
13
|
+
any
|
|
14
|
+
>,
|
|
15
|
+
// TODO: Maybe make optional for default preview
|
|
16
|
+
element: { dom: HTMLElement; destroy?: () => void },
|
|
17
|
+
buttonText?: string,
|
|
18
|
+
buttonIcon?: HTMLElement
|
|
19
|
+
) => {
|
|
20
|
+
const wrapper = document.createElement("div");
|
|
21
|
+
wrapper.className = "bn-file-block-content-wrapper";
|
|
22
|
+
|
|
23
|
+
if (block.props.url === "") {
|
|
24
|
+
const addFileButton = createAddFileButton(
|
|
25
|
+
block,
|
|
26
|
+
editor,
|
|
27
|
+
buttonText,
|
|
28
|
+
buttonIcon
|
|
29
|
+
);
|
|
30
|
+
wrapper.appendChild(addFileButton.dom);
|
|
31
|
+
|
|
32
|
+
const loading = document.createElement("div");
|
|
33
|
+
loading.className = "bn-file-loading-preview";
|
|
34
|
+
loading.textContent = "Loading...";
|
|
35
|
+
|
|
36
|
+
const destroyUploadStartHandler = editor.onUploadStart((blockId) => {
|
|
37
|
+
if (blockId === block.id) {
|
|
38
|
+
wrapper.removeChild(addFileButton.dom);
|
|
39
|
+
wrapper.appendChild(loading);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const destroyUploadEndHandler = editor.onUploadEnd((blockId) => {
|
|
43
|
+
if (blockId === block.id) {
|
|
44
|
+
wrapper.removeChild(loading);
|
|
45
|
+
wrapper.appendChild(addFileButton.dom);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
dom: wrapper,
|
|
51
|
+
destroy: () => {
|
|
52
|
+
addFileButton.destroy?.();
|
|
53
|
+
destroyUploadStartHandler();
|
|
54
|
+
destroyUploadEndHandler();
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
} else if (block.props.showPreview === false) {
|
|
58
|
+
// TODO: Not using the wrapper element here?
|
|
59
|
+
const file = createDefaultFilePreview(block).dom;
|
|
60
|
+
const element = createFileAndCaptionWrapper(block, file);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
dom: element.dom,
|
|
64
|
+
};
|
|
65
|
+
} else {
|
|
66
|
+
wrapper.appendChild(element.dom);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
dom: wrapper,
|
|
70
|
+
destroy: element.destroy,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
3
74
|
|
|
4
75
|
// Default file preview, displaying a file icon and file name.
|
|
5
76
|
export const createDefaultFilePreview = (
|
|
@@ -7,12 +7,10 @@ import {
|
|
|
7
7
|
PropSchema,
|
|
8
8
|
} from "../../schema";
|
|
9
9
|
import { defaultProps } from "../defaultProps";
|
|
10
|
-
|
|
11
10
|
import {
|
|
12
|
-
createAddFileButton,
|
|
13
|
-
createDefaultFilePreview,
|
|
14
11
|
createFigureWithCaption,
|
|
15
12
|
createFileAndCaptionWrapper,
|
|
13
|
+
createFileBlockWrapper,
|
|
16
14
|
createLinkWithCaption,
|
|
17
15
|
createResizeHandlesWrapper,
|
|
18
16
|
parseFigureElement,
|
|
@@ -49,71 +47,47 @@ export const imageBlockConfig = {
|
|
|
49
47
|
propSchema: imagePropSchema,
|
|
50
48
|
content: "none",
|
|
51
49
|
isFileBlock: true,
|
|
52
|
-
|
|
50
|
+
fileBlockAccept: ["image/*"],
|
|
53
51
|
} satisfies FileBlockConfig;
|
|
54
52
|
|
|
55
53
|
export const imageRender = (
|
|
56
54
|
block: BlockFromConfig<typeof imageBlockConfig, any, any>,
|
|
57
55
|
editor: BlockNoteEditor<any, any, any>
|
|
58
56
|
) => {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
editor.
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
);
|
|
100
|
-
|
|
101
|
-
const file = createResizeHandlesWrapper(
|
|
102
|
-
block,
|
|
103
|
-
editor,
|
|
104
|
-
image,
|
|
105
|
-
() => image.width,
|
|
106
|
-
(width) => (image.width = width)
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const element = createFileAndCaptionWrapper(block, file.dom);
|
|
110
|
-
wrapper.appendChild(element.dom);
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
dom: wrapper,
|
|
114
|
-
destroy: file.destroy,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
57
|
+
const icon = document.createElement("div");
|
|
58
|
+
icon.innerHTML =
|
|
59
|
+
'<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>';
|
|
60
|
+
|
|
61
|
+
const image = document.createElement("img");
|
|
62
|
+
image.className = "bn-visual-media";
|
|
63
|
+
editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
|
|
64
|
+
image.src = downloadUrl;
|
|
65
|
+
});
|
|
66
|
+
image.alt = block.props.name || block.props.caption || "BlockNote image";
|
|
67
|
+
image.contentEditable = "false";
|
|
68
|
+
image.draggable = false;
|
|
69
|
+
image.width = Math.min(
|
|
70
|
+
block.props.previewWidth,
|
|
71
|
+
editor.domElement.firstElementChild!.clientWidth
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const file = createResizeHandlesWrapper(
|
|
75
|
+
block,
|
|
76
|
+
editor,
|
|
77
|
+
image,
|
|
78
|
+
() => image.width,
|
|
79
|
+
(width) => (image.width = width)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const element = createFileAndCaptionWrapper(block, file.dom);
|
|
83
|
+
|
|
84
|
+
return createFileBlockWrapper(
|
|
85
|
+
block,
|
|
86
|
+
editor,
|
|
87
|
+
element,
|
|
88
|
+
editor.dictionary.file_blocks.image.add_button_text,
|
|
89
|
+
icon.firstElementChild as HTMLElement
|
|
90
|
+
);
|
|
117
91
|
};
|
|
118
92
|
|
|
119
93
|
export const imageParse = (
|
|
@@ -9,10 +9,9 @@ import {
|
|
|
9
9
|
import { defaultProps } from "../defaultProps";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
-
createAddFileButton,
|
|
13
|
-
createDefaultFilePreview,
|
|
14
12
|
createFigureWithCaption,
|
|
15
13
|
createFileAndCaptionWrapper,
|
|
14
|
+
createFileBlockWrapper,
|
|
16
15
|
createLinkWithCaption,
|
|
17
16
|
createResizeHandlesWrapper,
|
|
18
17
|
parseFigureElement,
|
|
@@ -49,69 +48,45 @@ export const videoBlockConfig = {
|
|
|
49
48
|
propSchema: videoPropSchema,
|
|
50
49
|
content: "none",
|
|
51
50
|
isFileBlock: true,
|
|
52
|
-
|
|
51
|
+
fileBlockAccept: ["video/*"],
|
|
53
52
|
} satisfies FileBlockConfig;
|
|
54
53
|
|
|
55
54
|
export const videoRender = (
|
|
56
55
|
block: BlockFromConfig<typeof videoBlockConfig, any, any>,
|
|
57
56
|
editor: BlockNoteEditor<any, any, any>
|
|
58
57
|
) => {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
video.controls = true;
|
|
92
|
-
video.contentEditable = "false";
|
|
93
|
-
video.draggable = false;
|
|
94
|
-
video.width = Math.min(
|
|
95
|
-
block.props.previewWidth,
|
|
96
|
-
editor.domElement.firstElementChild!.clientWidth
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const file = createResizeHandlesWrapper(
|
|
100
|
-
block,
|
|
101
|
-
editor,
|
|
102
|
-
video,
|
|
103
|
-
() => video.width,
|
|
104
|
-
(width) => (video.width = width)
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const element = createFileAndCaptionWrapper(block, file.dom);
|
|
108
|
-
wrapper.appendChild(element.dom);
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
dom: wrapper,
|
|
112
|
-
destroy: file.destroy,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
58
|
+
const icon = document.createElement("div");
|
|
59
|
+
icon.innerHTML =
|
|
60
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 5V19H16V5H8ZM4 5V7H6V5H4ZM18 5V7H20V5H18ZM4 9V11H6V9H4ZM18 9V11H20V9H18ZM4 13V15H6V13H4ZM18 13V15H20V13H18ZM4 17V19H6V17H4ZM18 17V19H20V17H18Z"></path></svg>';
|
|
61
|
+
|
|
62
|
+
const video = document.createElement("video");
|
|
63
|
+
video.className = "bn-visual-media";
|
|
64
|
+
video.src = block.props.url;
|
|
65
|
+
video.controls = true;
|
|
66
|
+
video.contentEditable = "false";
|
|
67
|
+
video.draggable = false;
|
|
68
|
+
video.width = Math.min(
|
|
69
|
+
block.props.previewWidth,
|
|
70
|
+
editor.domElement.firstElementChild!.clientWidth
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const file = createResizeHandlesWrapper(
|
|
74
|
+
block,
|
|
75
|
+
editor,
|
|
76
|
+
video,
|
|
77
|
+
() => video.width,
|
|
78
|
+
(width) => (video.width = width)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const element = createFileAndCaptionWrapper(block, file.dom);
|
|
82
|
+
|
|
83
|
+
return createFileBlockWrapper(
|
|
84
|
+
block,
|
|
85
|
+
editor,
|
|
86
|
+
element,
|
|
87
|
+
editor.dictionary.file_blocks.video.add_button_text,
|
|
88
|
+
icon.firstElementChild as HTMLElement
|
|
89
|
+
);
|
|
115
90
|
};
|
|
116
91
|
|
|
117
92
|
export const videoParse = (
|
|
@@ -57,3 +57,16 @@ it("immediately replaces doc", async () => {
|
|
|
57
57
|
]
|
|
58
58
|
`);
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
it("adds id attribute when requested", async () => {
|
|
62
|
+
const editor = BlockNoteEditor.create({
|
|
63
|
+
setIdAttribute: true,
|
|
64
|
+
});
|
|
65
|
+
const blocks = await editor.tryParseMarkdownToBlocks(
|
|
66
|
+
"This is a normal text\n\n# And this is a large heading"
|
|
67
|
+
);
|
|
68
|
+
editor.replaceBlocks(editor.document, blocks);
|
|
69
|
+
expect(
|
|
70
|
+
await editor.blocksToFullHTML(editor.document)
|
|
71
|
+
).toMatchInlineSnapshot(`"<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1" id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1" id="1"><div class="bn-block-content" data-content-type="paragraph"><p class="bn-inline-content">This is a normal text</p></div></div></div><div class="bn-block-outer" data-node-type="blockOuter" data-id="2" id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2" id="2"><div class="bn-block-content" data-content-type="heading" data-level="1"><h1 class="bn-inline-content">And this is a large heading</h1></div></div></div></div>"`);
|
|
72
|
+
});
|