@blocknote/core 0.13.2 → 0.13.3
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 +5282 -2785
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +7 -7
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +2 -2
- package/src/api/exporters/html/__snapshots__/file/basic/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/basic/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/button/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/button/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/nested/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/nested/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noCaption/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noCaption/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noName/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/file/noName/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/basic/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/button/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/button/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/nested/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/image/noCaption/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noCaption/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noName/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noName/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noPreview/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/image/noPreview/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/basic/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/basic/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/button/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/button/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/nested/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleFile/nested/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/basic/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/basic/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/button/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/button/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/nested/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/nested/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noName/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noName/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/internal.html +1 -0
- package/src/api/exporters/markdown/__snapshots__/file/basic/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/file/button/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/file/nested/markdown.md +7 -0
- package/src/api/exporters/markdown/__snapshots__/file/noCaption/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/file/noName/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/image/basic/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/image/button/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/image/nested/markdown.md +2 -2
- package/src/api/exporters/markdown/__snapshots__/image/noCaption/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/image/noName/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/image/noPreview/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/simpleFile/basic/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/simpleFile/button/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/simpleFile/nested/markdown.md +7 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/basic/markdown.md +3 -1
- package/src/api/exporters/markdown/__snapshots__/simpleImage/button/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/nested/markdown.md +6 -2
- package/src/api/exporters/markdown/__snapshots__/simpleImage/noCaption/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/noName/markdown.md +3 -0
- package/src/api/exporters/markdown/__snapshots__/simpleImage/noPreview/markdown.md +3 -0
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +212 -4
- package/src/api/parsers/html/__snapshots__/paste/parse-basic-block-types.json +3 -1
- package/src/api/parsers/html/__snapshots__/paste/parse-fake-image-caption.json +3 -1
- package/src/api/testUtil/cases/customBlocks.ts +79 -33
- package/src/api/testUtil/cases/customInlineContent.ts +1 -1
- package/src/api/testUtil/cases/customStyles.ts +1 -1
- package/src/api/testUtil/cases/defaultSchema.ts +114 -4
- package/src/blocks/AudioBlockContent/AudioBlockContent.ts +162 -0
- package/src/blocks/AudioBlockContent/audioBlockHelpers.ts +5 -0
- package/src/blocks/FileBlockContent/FileBlockContent.ts +121 -0
- package/src/blocks/FileBlockContent/fileBlockHelpers.ts +377 -0
- package/src/blocks/ImageBlockContent/ImageBlockContent.ts +135 -356
- package/src/blocks/ImageBlockContent/imageBlockHelpers.ts +6 -0
- package/src/blocks/VideoBlockContent/VideoBlockContent.ts +182 -0
- package/src/blocks/VideoBlockContent/videoBlockHelpers.ts +6 -0
- package/src/blocks/defaultBlockTypeGuards.ts +53 -1
- package/src/blocks/defaultBlocks.ts +8 -2
- package/src/editor/Block.css +67 -27
- package/src/editor/BlockNoteEditor.ts +14 -10
- package/src/editor/BlockNoteSchema.ts +12 -3
- package/src/extensions/{ImagePanel/ImageToolbarPlugin.ts → FilePanel/FilePanelPlugin.ts} +22 -25
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +14 -2
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +59 -2
- package/src/i18n/locales/en.ts +102 -11
- package/src/i18n/locales/fr.ts +104 -11
- package/src/i18n/locales/index.ts +8 -2
- package/src/i18n/locales/is.ts +288 -0
- package/src/i18n/locales/ja.ts +300 -0
- package/src/i18n/locales/ko.ts +292 -0
- package/src/i18n/locales/nl.ts +101 -8
- package/src/i18n/locales/pl.ts +280 -0
- package/src/i18n/locales/pt.ts +281 -0
- package/src/i18n/locales/vi.ts +281 -0
- package/src/i18n/locales/zh.ts +107 -8
- package/src/index.ts +9 -2
- package/src/pm-nodes/BlockContainer.ts +2 -2
- package/src/schema/blocks/createSpec.ts +1 -0
- package/src/schema/blocks/internal.ts +10 -0
- package/src/schema/blocks/types.ts +41 -5
- package/src/util/string.ts +12 -0
- package/types/src/api/testUtil/cases/customBlocks.d.ts +228 -42
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +178 -4
- package/types/src/api/testUtil/cases/customStyles.d.ts +178 -4
- package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +104 -0
- package/types/src/blocks/AudioBlockContent/audioBlockHelpers.d.ts +3 -0
- package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +96 -0
- package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +30 -0
- package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +53 -14
- package/types/src/blocks/ImageBlockContent/imageBlockHelpers.d.ts +4 -0
- package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +132 -0
- package/types/src/blocks/VideoBlockContent/videoBlockHelpers.d.ts +4 -0
- package/types/src/blocks/defaultBlockTypeGuards.d.ts +6 -1
- package/types/src/blocks/defaultBlocks.d.ts +356 -8
- package/types/src/editor/BlockNoteEditor.d.ts +5 -5
- package/types/src/extensions/{ImagePanel/ImageToolbarPlugin.d.ts → FilePanel/FilePanelPlugin.d.ts} +9 -12
- package/types/src/i18n/locales/en.d.ts +49 -7
- package/types/src/i18n/locales/fr.d.ts +2 -184
- package/types/src/i18n/locales/index.d.ts +7 -1
- package/types/src/i18n/locales/is.d.ts +2 -0
- package/types/src/i18n/locales/ja.d.ts +2 -0
- package/types/src/i18n/locales/ko.d.ts +2 -0
- package/types/src/i18n/locales/pl.d.ts +2 -0
- package/types/src/i18n/locales/pt.d.ts +2 -0
- package/types/src/i18n/locales/vi.d.ts +2 -0
- package/types/src/index.d.ts +8 -2
- package/types/src/schema/blocks/internal.d.ts +1 -1
- package/types/src/schema/blocks/types.d.ts +26 -1
- package/types/src/util/string.d.ts +1 -0
- /package/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
- /package/types/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
2
|
+
import {
|
|
3
|
+
BlockFromConfig,
|
|
4
|
+
createBlockSpec,
|
|
5
|
+
FileBlockConfig,
|
|
6
|
+
Props,
|
|
7
|
+
PropSchema,
|
|
8
|
+
} from "../../schema";
|
|
9
|
+
import { defaultProps } from "../defaultProps";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
createAddFileButton,
|
|
13
|
+
createDefaultFilePreview,
|
|
14
|
+
createFigureWithCaption,
|
|
15
|
+
createFileAndCaptionWrapper,
|
|
16
|
+
createLinkWithCaption,
|
|
17
|
+
createResizeHandlesWrapper,
|
|
18
|
+
parseFigureElement,
|
|
19
|
+
} from "../FileBlockContent/fileBlockHelpers";
|
|
20
|
+
import { parseVideoElement } from "./videoBlockHelpers";
|
|
21
|
+
|
|
22
|
+
export const videoPropSchema = {
|
|
23
|
+
textAlignment: defaultProps.textAlignment,
|
|
24
|
+
backgroundColor: defaultProps.backgroundColor,
|
|
25
|
+
// File name.
|
|
26
|
+
name: {
|
|
27
|
+
default: "" as const,
|
|
28
|
+
},
|
|
29
|
+
// File url.
|
|
30
|
+
url: {
|
|
31
|
+
default: "" as const,
|
|
32
|
+
},
|
|
33
|
+
// File caption.
|
|
34
|
+
caption: {
|
|
35
|
+
default: "" as const,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
showPreview: {
|
|
39
|
+
default: true,
|
|
40
|
+
},
|
|
41
|
+
// File preview width in px.
|
|
42
|
+
previewWidth: {
|
|
43
|
+
default: 512,
|
|
44
|
+
},
|
|
45
|
+
} satisfies PropSchema;
|
|
46
|
+
|
|
47
|
+
export const videoBlockConfig = {
|
|
48
|
+
type: "video" as const,
|
|
49
|
+
propSchema: videoPropSchema,
|
|
50
|
+
content: "none",
|
|
51
|
+
isFileBlock: true,
|
|
52
|
+
isFileBlockPlaceholder: (block: any) => !block.props.url,
|
|
53
|
+
fileBlockAcceptMimeTypes: ["video/*"],
|
|
54
|
+
} satisfies FileBlockConfig;
|
|
55
|
+
|
|
56
|
+
export const videoRender = (
|
|
57
|
+
block: BlockFromConfig<typeof videoBlockConfig, any, any>,
|
|
58
|
+
editor: BlockNoteEditor<any, any, any>
|
|
59
|
+
) => {
|
|
60
|
+
const wrapper = document.createElement("div");
|
|
61
|
+
wrapper.className = "bn-file-block-content-wrapper";
|
|
62
|
+
|
|
63
|
+
if (block.props.url === "") {
|
|
64
|
+
const fileBlockVideoIcon = document.createElement("div");
|
|
65
|
+
fileBlockVideoIcon.innerHTML =
|
|
66
|
+
'<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>';
|
|
67
|
+
const addVideoButton = createAddFileButton(
|
|
68
|
+
block,
|
|
69
|
+
editor,
|
|
70
|
+
editor.dictionary.file_blocks.video.add_button_text,
|
|
71
|
+
fileBlockVideoIcon.firstElementChild as HTMLElement
|
|
72
|
+
);
|
|
73
|
+
wrapper.appendChild(addVideoButton.dom);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
dom: wrapper,
|
|
77
|
+
destroy: () => {
|
|
78
|
+
addVideoButton?.destroy?.();
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
} else if (!block.props.showPreview) {
|
|
82
|
+
const file = createDefaultFilePreview(block).dom;
|
|
83
|
+
const element = createFileAndCaptionWrapper(block, file);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
dom: element.dom,
|
|
87
|
+
};
|
|
88
|
+
} else {
|
|
89
|
+
const video = document.createElement("video");
|
|
90
|
+
video.className = "bn-visual-media";
|
|
91
|
+
video.src = block.props.url;
|
|
92
|
+
video.controls = true;
|
|
93
|
+
video.contentEditable = "false";
|
|
94
|
+
video.draggable = false;
|
|
95
|
+
video.width = Math.min(
|
|
96
|
+
block.props.previewWidth,
|
|
97
|
+
editor.domElement.firstElementChild!.clientWidth
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const file = createResizeHandlesWrapper(
|
|
101
|
+
block,
|
|
102
|
+
editor,
|
|
103
|
+
video,
|
|
104
|
+
() => video.width,
|
|
105
|
+
(width) => (video.width = width)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const element = createFileAndCaptionWrapper(block, file.dom);
|
|
109
|
+
wrapper.appendChild(element.dom);
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
dom: wrapper,
|
|
113
|
+
destroy: file.destroy,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const videoParse = (
|
|
119
|
+
element: HTMLElement
|
|
120
|
+
): Partial<Props<typeof videoBlockConfig.propSchema>> | undefined => {
|
|
121
|
+
if (element.tagName === "VIDEO") {
|
|
122
|
+
return parseVideoElement(element as HTMLVideoElement);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (element.tagName === "FIGURE") {
|
|
126
|
+
const parsedFigure = parseFigureElement(element, "video");
|
|
127
|
+
if (!parsedFigure) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const { targetElement, caption } = parsedFigure;
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
...parseVideoElement(targetElement as HTMLVideoElement),
|
|
135
|
+
caption,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return undefined;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const videoToExternalHTML = (
|
|
143
|
+
block: BlockFromConfig<typeof videoBlockConfig, any, any>
|
|
144
|
+
) => {
|
|
145
|
+
if (!block.props.url) {
|
|
146
|
+
const div = document.createElement("p");
|
|
147
|
+
div.textContent = "Add video";
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
dom: div,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let video;
|
|
155
|
+
if (block.props.showPreview) {
|
|
156
|
+
video = document.createElement("video");
|
|
157
|
+
video.src = block.props.url;
|
|
158
|
+
video.width = block.props.previewWidth;
|
|
159
|
+
} else {
|
|
160
|
+
video = document.createElement("a");
|
|
161
|
+
video.href = block.props.url;
|
|
162
|
+
video.textContent = block.props.name || block.props.url;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (block.props.caption) {
|
|
166
|
+
if (block.props.showPreview) {
|
|
167
|
+
return createFigureWithCaption(video, block.props.caption);
|
|
168
|
+
} else {
|
|
169
|
+
return createLinkWithCaption(video, block.props.caption);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
dom: video,
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const VideoBlock = createBlockSpec(videoBlockConfig, {
|
|
179
|
+
render: videoRender,
|
|
180
|
+
parse: videoParse,
|
|
181
|
+
toExternalHTML: videoToExternalHTML,
|
|
182
|
+
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { BlockNoteEditor } from "../editor/BlockNoteEditor";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BlockFromConfig,
|
|
4
|
+
BlockSchema,
|
|
5
|
+
FileBlockConfig,
|
|
6
|
+
InlineContentSchema,
|
|
7
|
+
StyleSchema,
|
|
8
|
+
} from "../schema";
|
|
3
9
|
import { Block, DefaultBlockSchema, defaultBlockSchema } from "./defaultBlocks";
|
|
4
10
|
import { defaultProps } from "./defaultProps";
|
|
5
11
|
|
|
@@ -33,6 +39,52 @@ export function checkBlockIsDefaultType<
|
|
|
33
39
|
);
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
export function checkBlockIsFileBlock<
|
|
43
|
+
B extends BlockSchema,
|
|
44
|
+
I extends InlineContentSchema,
|
|
45
|
+
S extends StyleSchema
|
|
46
|
+
>(
|
|
47
|
+
block: Block<any, I, S>,
|
|
48
|
+
editor: BlockNoteEditor<B, I, S>
|
|
49
|
+
): block is BlockFromConfig<FileBlockConfig, I, S> {
|
|
50
|
+
return (
|
|
51
|
+
(block.type in editor.schema.blockSchema &&
|
|
52
|
+
editor.schema.blockSchema[block.type].isFileBlock) ||
|
|
53
|
+
false
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function checkBlockIsFileBlockWithPreview<
|
|
58
|
+
B extends BlockSchema,
|
|
59
|
+
I extends InlineContentSchema,
|
|
60
|
+
S extends StyleSchema
|
|
61
|
+
>(
|
|
62
|
+
block: Block<any, I, S>,
|
|
63
|
+
editor: BlockNoteEditor<B, I, S>
|
|
64
|
+
): block is BlockFromConfig<
|
|
65
|
+
FileBlockConfig & {
|
|
66
|
+
propSchema: Required<FileBlockConfig["propSchema"]>;
|
|
67
|
+
},
|
|
68
|
+
I,
|
|
69
|
+
S
|
|
70
|
+
> {
|
|
71
|
+
return (
|
|
72
|
+
(block.type in editor.schema.blockSchema &&
|
|
73
|
+
editor.schema.blockSchema[block.type].isFileBlock &&
|
|
74
|
+
"showPreview" in editor.schema.blockSchema[block.type].propSchema) ||
|
|
75
|
+
false
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function checkBlockIsFileBlockWithPlaceholder<
|
|
80
|
+
B extends BlockSchema,
|
|
81
|
+
I extends InlineContentSchema,
|
|
82
|
+
S extends StyleSchema
|
|
83
|
+
>(block: Block<B, I, S>, editor: BlockNoteEditor<B, I, S>) {
|
|
84
|
+
const config = editor.schema.blockSchema[block.type];
|
|
85
|
+
return config.isFileBlock && config.isFileBlockPlaceholder(block);
|
|
86
|
+
}
|
|
87
|
+
|
|
36
88
|
export function checkBlockTypeHasDefaultProp<
|
|
37
89
|
Prop extends keyof typeof defaultProps,
|
|
38
90
|
I extends InlineContentSchema,
|
|
@@ -19,19 +19,25 @@ import {
|
|
|
19
19
|
getInlineContentSchemaFromSpecs,
|
|
20
20
|
getStyleSchemaFromSpecs,
|
|
21
21
|
} from "../schema";
|
|
22
|
+
import { FileBlock } from "./FileBlockContent/FileBlockContent";
|
|
23
|
+
import { ImageBlock } from "./ImageBlockContent/ImageBlockContent";
|
|
22
24
|
import { Heading } from "./HeadingBlockContent/HeadingBlockContent";
|
|
23
|
-
import { Image } from "./ImageBlockContent/ImageBlockContent";
|
|
24
25
|
import { BulletListItem } from "./ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent";
|
|
25
26
|
import { NumberedListItem } from "./ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent";
|
|
26
27
|
import { Paragraph } from "./ParagraphBlockContent/ParagraphBlockContent";
|
|
27
28
|
import { Table } from "./TableBlockContent/TableBlockContent";
|
|
29
|
+
import { VideoBlock } from "./VideoBlockContent/VideoBlockContent";
|
|
30
|
+
import { AudioBlock } from "./AudioBlockContent/AudioBlockContent";
|
|
28
31
|
|
|
29
32
|
export const defaultBlockSpecs = {
|
|
30
33
|
paragraph: Paragraph,
|
|
31
34
|
heading: Heading,
|
|
32
35
|
bulletListItem: BulletListItem,
|
|
33
36
|
numberedListItem: NumberedListItem,
|
|
34
|
-
|
|
37
|
+
file: FileBlock,
|
|
38
|
+
image: ImageBlock,
|
|
39
|
+
video: VideoBlock,
|
|
40
|
+
audio: AudioBlock,
|
|
35
41
|
table: Table,
|
|
36
42
|
} satisfies BlockSpecs;
|
|
37
43
|
|
package/src/editor/Block.css
CHANGED
|
@@ -13,15 +13,9 @@ BASIC STYLES
|
|
|
13
13
|
flex-direction: column;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/*Ensures block content inside React node views spans editor width*/
|
|
17
|
-
.bn-react-node-view-renderer {
|
|
18
|
-
display: flex;
|
|
19
|
-
flex-grow: 1;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
.bn-block-content {
|
|
17
|
+
display: flex;
|
|
23
18
|
padding: 3px 0;
|
|
24
|
-
flex-grow: 1;
|
|
25
19
|
transition: font-size 0.2s;
|
|
26
20
|
width: 100%;
|
|
27
21
|
/*
|
|
@@ -36,6 +30,13 @@ BASIC STYLES
|
|
|
36
30
|
/*margin: 0px;*/
|
|
37
31
|
}
|
|
38
32
|
|
|
33
|
+
.bn-block-content.ProseMirror-selectednode > *,
|
|
34
|
+
/* Case for node view renderers */
|
|
35
|
+
.ProseMirror-selectednode > .bn-block-content > * {
|
|
36
|
+
border-radius: 4px;
|
|
37
|
+
outline: 4px solid rgb(100, 160, 255);
|
|
38
|
+
}
|
|
39
|
+
|
|
39
40
|
/*
|
|
40
41
|
NESTED BLOCKS
|
|
41
42
|
*/
|
|
@@ -235,49 +236,75 @@ NESTED BLOCKS
|
|
|
235
236
|
content: "▪";
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
/*
|
|
239
|
+
/* FILES */
|
|
240
|
+
|
|
241
|
+
/* Add block button & default preview */
|
|
242
|
+
[data-file-block] .bn-file-block-content-wrapper:has(.bn-add-file-button),
|
|
243
|
+
[data-file-block] .bn-file-block-content-wrapper:has(.bn-file-default-preview) {
|
|
244
|
+
width: 100%;
|
|
245
|
+
}
|
|
239
246
|
|
|
240
|
-
[data-
|
|
247
|
+
[data-file-block] .bn-file-block-content-wrapper {
|
|
248
|
+
cursor: pointer;
|
|
241
249
|
display: flex;
|
|
242
250
|
flex-direction: column;
|
|
243
|
-
justify-content:
|
|
251
|
+
justify-content: stretch;
|
|
244
252
|
user-select: none;
|
|
245
|
-
width: 100%;
|
|
246
253
|
}
|
|
247
254
|
|
|
248
|
-
[data-
|
|
249
|
-
display: flex;
|
|
250
|
-
flex-direction: row;
|
|
255
|
+
[data-file-block] .bn-add-file-button {
|
|
251
256
|
align-items: center;
|
|
252
|
-
|
|
253
|
-
background-color: whitesmoke;
|
|
257
|
+
background-color: rgb(242, 241, 238);
|
|
254
258
|
border-radius: 4px;
|
|
259
|
+
color: rgb(125, 121, 122);
|
|
255
260
|
cursor: pointer;
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: row;
|
|
263
|
+
gap: 10px;
|
|
256
264
|
padding: 12px;
|
|
257
265
|
width: 100%;
|
|
258
266
|
}
|
|
259
267
|
|
|
260
|
-
[data-
|
|
261
|
-
background-color:
|
|
268
|
+
[data-file-block] .bn-add-file-button:hover {
|
|
269
|
+
background-color: rgb(225, 225, 225);
|
|
262
270
|
}
|
|
263
271
|
|
|
264
|
-
[data-
|
|
265
|
-
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 5H4V19L13.2923 9.70649C13.6828 9.31595 14.3159 9.31591 14.7065 9.70641L20 15.0104V5ZM2 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 11C6.89543 11 6 10.1046 6 9C6 7.89543 6.89543 7 8 7C9.10457 7 10 7.89543 10 9C10 10.1046 9.10457 11 8 11Z'%3E%3C/path%3E%3C/svg%3E");
|
|
272
|
+
[data-file-block] .bn-add-file-button-icon {
|
|
266
273
|
width: 24px;
|
|
267
274
|
height: 24px;
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
[data-
|
|
271
|
-
|
|
277
|
+
[data-file-block] .bn-add-file-button .bn-add-file-button-text {
|
|
278
|
+
font-size: 0.9rem;
|
|
272
279
|
}
|
|
273
280
|
|
|
274
|
-
[data-
|
|
281
|
+
[data-file-block] .bn-file-and-caption-wrapper {
|
|
275
282
|
display: flex;
|
|
276
283
|
flex-direction: column;
|
|
277
284
|
border-radius: 4px;
|
|
278
285
|
}
|
|
279
286
|
|
|
280
|
-
[data-
|
|
287
|
+
[data-file-block] .bn-file-default-preview {
|
|
288
|
+
align-items: center;
|
|
289
|
+
border-radius: 4px;
|
|
290
|
+
display: flex;
|
|
291
|
+
flex-direction: row;
|
|
292
|
+
gap: 4px;
|
|
293
|
+
padding: 4px;
|
|
294
|
+
width: 100%;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
[data-file-block] .bn-file-default-preview:hover,
|
|
298
|
+
.ProseMirror-selectednode .bn-file-default-preview {
|
|
299
|
+
background-color: rgb(225, 225, 225);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
[data-file-block] .bn-file-default-preview-icon {
|
|
303
|
+
width: 24px;
|
|
304
|
+
height: 24px;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
[data-file-block] .bn-visual-media-wrapper {
|
|
281
308
|
display: flex;
|
|
282
309
|
flex-direction: row;
|
|
283
310
|
align-items: center;
|
|
@@ -285,12 +312,12 @@ NESTED BLOCKS
|
|
|
285
312
|
width: fit-content;
|
|
286
313
|
}
|
|
287
314
|
|
|
288
|
-
[data-
|
|
315
|
+
[data-file-block] .bn-visual-media {
|
|
289
316
|
border-radius: 4px;
|
|
290
317
|
max-width: 100%;
|
|
291
318
|
}
|
|
292
319
|
|
|
293
|
-
[data-
|
|
320
|
+
[data-file-block] .bn-visual-media-resize-handle {
|
|
294
321
|
position: absolute;
|
|
295
322
|
width: 8px;
|
|
296
323
|
height: 30px;
|
|
@@ -300,8 +327,17 @@ NESTED BLOCKS
|
|
|
300
327
|
cursor: ew-resize;
|
|
301
328
|
}
|
|
302
329
|
|
|
303
|
-
[data-content-type="
|
|
330
|
+
[data-content-type="audio"] > .bn-file-block-content-wrapper, .bn-audio {
|
|
331
|
+
width: 100%;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
[data-file-block] .bn-file-caption {
|
|
304
335
|
font-size: 0.8em;
|
|
336
|
+
padding-block: 4px;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
[data-file-block] .bn-file-caption:empty {
|
|
340
|
+
padding-block: 0;
|
|
305
341
|
}
|
|
306
342
|
|
|
307
343
|
/* PLACEHOLDERS*/
|
|
@@ -392,17 +428,21 @@ NESTED BLOCKS
|
|
|
392
428
|
|
|
393
429
|
/* TEXT ALIGNMENT */
|
|
394
430
|
[data-text-alignment="left"] {
|
|
431
|
+
justify-content: flex-start;
|
|
395
432
|
text-align: left;
|
|
396
433
|
}
|
|
397
434
|
|
|
398
435
|
[data-text-alignment="center"] {
|
|
436
|
+
justify-content: center;
|
|
399
437
|
text-align: center;
|
|
400
438
|
}
|
|
401
439
|
|
|
402
440
|
[data-text-alignment="right"] {
|
|
441
|
+
justify-content: flex-end;
|
|
403
442
|
text-align: right;
|
|
404
443
|
}
|
|
405
444
|
|
|
406
445
|
[data-text-alignment="justify"] {
|
|
446
|
+
justify-content: flex-start;
|
|
407
447
|
text-align: justify;
|
|
408
448
|
}
|
|
@@ -26,8 +26,8 @@ import {
|
|
|
26
26
|
DefaultStyleSchema,
|
|
27
27
|
PartialBlock,
|
|
28
28
|
} from "../blocks/defaultBlocks";
|
|
29
|
+
import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlugin";
|
|
29
30
|
import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin";
|
|
30
|
-
import { ImagePanelProsemirrorPlugin } from "../extensions/ImagePanel/ImageToolbarPlugin";
|
|
31
31
|
import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin";
|
|
32
32
|
import { SideMenuProsemirrorPlugin } from "../extensions/SideMenu/SideMenuPlugin";
|
|
33
33
|
import { SuggestionMenuProseMirrorPlugin } from "../extensions/SuggestionMenu/SuggestionPlugin";
|
|
@@ -113,9 +113,9 @@ export type BlockNoteEditorOptions<
|
|
|
113
113
|
/**
|
|
114
114
|
* A custom function to handle file uploads.
|
|
115
115
|
* @param file The file that should be uploaded.
|
|
116
|
-
* @returns The URL of the uploaded file
|
|
116
|
+
* @returns The URL of the uploaded file OR an object containing props that should be set on the file block (such as an id)
|
|
117
117
|
*/
|
|
118
|
-
uploadFile: (file: File) => Promise<string
|
|
118
|
+
uploadFile: (file: File) => Promise<string | Record<string, any>>;
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* When enabled, allows for collaboration between multiple users.
|
|
@@ -187,13 +187,19 @@ export class BlockNoteEditor<
|
|
|
187
187
|
ISchema,
|
|
188
188
|
SSchema
|
|
189
189
|
>;
|
|
190
|
-
public readonly
|
|
190
|
+
public readonly filePanel?: FilePanelProsemirrorPlugin<
|
|
191
|
+
BSchema,
|
|
192
|
+
ISchema,
|
|
193
|
+
SSchema
|
|
194
|
+
>;
|
|
191
195
|
public readonly tableHandles?: TableHandlesProsemirrorPlugin<
|
|
192
196
|
ISchema,
|
|
193
197
|
SSchema
|
|
194
198
|
>;
|
|
195
199
|
|
|
196
|
-
public readonly uploadFile:
|
|
200
|
+
public readonly uploadFile:
|
|
201
|
+
| ((file: File) => Promise<string | Record<string, any>>)
|
|
202
|
+
| undefined;
|
|
197
203
|
|
|
198
204
|
public static create<
|
|
199
205
|
BSchema extends BlockSchema = DefaultBlockSchema,
|
|
@@ -254,10 +260,8 @@ export class BlockNoteEditor<
|
|
|
254
260
|
this.linkToolbar = new LinkToolbarProsemirrorPlugin(this);
|
|
255
261
|
this.sideMenu = new SideMenuProsemirrorPlugin(this);
|
|
256
262
|
this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
this.imagePanel = new ImagePanelProsemirrorPlugin(this as any);
|
|
260
|
-
}
|
|
263
|
+
this.filePanel = new FilePanelProsemirrorPlugin(this as any);
|
|
264
|
+
|
|
261
265
|
if (checkDefaultBlockTypeInSchema("table", this)) {
|
|
262
266
|
this.tableHandles = new TableHandlesProsemirrorPlugin(this as any);
|
|
263
267
|
}
|
|
@@ -282,7 +286,7 @@ export class BlockNoteEditor<
|
|
|
282
286
|
this.linkToolbar.plugin,
|
|
283
287
|
this.sideMenu.plugin,
|
|
284
288
|
this.suggestionMenus.plugin,
|
|
285
|
-
...(this.
|
|
289
|
+
...(this.filePanel ? [this.filePanel.plugin] : []),
|
|
286
290
|
...(this.tableHandles ? [this.tableHandles.plugin] : []),
|
|
287
291
|
PlaceholderPlugin(this, newOptions.placeholders),
|
|
288
292
|
];
|
|
@@ -23,6 +23,15 @@ import type {
|
|
|
23
23
|
} from "../schema/blocks/types";
|
|
24
24
|
import type { BlockNoteEditor } from "./BlockNoteEditor";
|
|
25
25
|
|
|
26
|
+
function removeUndefined<T extends Record<string, any> | undefined>(obj: T): T {
|
|
27
|
+
if (!obj) {
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
return Object.fromEntries(
|
|
31
|
+
Object.entries(obj).filter(([, value]) => value !== undefined)
|
|
32
|
+
) as T;
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
export class BlockNoteSchema<
|
|
27
36
|
BSchema extends BlockSchema,
|
|
28
37
|
ISchema extends InlineContentSchema,
|
|
@@ -84,10 +93,10 @@ export class BlockNoteSchema<
|
|
|
84
93
|
inlineContentSpecs?: InlineContentSpecs;
|
|
85
94
|
styleSpecs?: StyleSpecs;
|
|
86
95
|
}) {
|
|
87
|
-
this.blockSpecs = opts?.blockSpecs || defaultBlockSpecs;
|
|
96
|
+
this.blockSpecs = removeUndefined(opts?.blockSpecs) || defaultBlockSpecs;
|
|
88
97
|
this.inlineContentSpecs =
|
|
89
|
-
opts?.inlineContentSpecs || defaultInlineContentSpecs;
|
|
90
|
-
this.styleSpecs = opts?.styleSpecs || defaultStyleSpecs;
|
|
98
|
+
removeUndefined(opts?.inlineContentSpecs) || defaultInlineContentSpecs;
|
|
99
|
+
this.styleSpecs = removeUndefined(opts?.styleSpecs) || defaultStyleSpecs;
|
|
91
100
|
|
|
92
101
|
this.blockSchema = getBlockSchemaFromSpecs(this.blockSpecs) as any;
|
|
93
102
|
this.inlineContentSchema = getInlineContentSchemaFromSpecs(
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
2
2
|
import { EditorView } from "prosemirror-view";
|
|
3
3
|
|
|
4
|
-
import { DefaultBlockSchema } from "../../blocks/defaultBlocks";
|
|
5
4
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
6
5
|
import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
|
|
7
6
|
import type {
|
|
8
7
|
BlockFromConfig,
|
|
8
|
+
BlockSchema,
|
|
9
|
+
FileBlockConfig,
|
|
9
10
|
InlineContentSchema,
|
|
10
11
|
StyleSchema,
|
|
11
12
|
} from "../../schema";
|
|
12
13
|
import { EventEmitter } from "../../util/EventEmitter";
|
|
13
14
|
|
|
14
|
-
export type
|
|
15
|
+
export type FilePanelState<
|
|
15
16
|
I extends InlineContentSchema,
|
|
16
17
|
S extends StyleSchema
|
|
17
18
|
> = UiElementPosition & {
|
|
18
19
|
// TODO: This typing is not quite right (children should be from BSchema)
|
|
19
|
-
block: BlockFromConfig<
|
|
20
|
+
block: BlockFromConfig<FileBlockConfig, I, S>;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
export class
|
|
23
|
-
|
|
24
|
-
S extends StyleSchema
|
|
25
|
-
> implements PluginView
|
|
23
|
+
export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
|
|
24
|
+
implements PluginView
|
|
26
25
|
{
|
|
27
|
-
public state?:
|
|
26
|
+
public state?: FilePanelState<I, S>;
|
|
28
27
|
public emitUpdate: () => void;
|
|
29
28
|
|
|
30
29
|
public prevWasEditable: boolean | null = null;
|
|
@@ -32,11 +31,11 @@ export class ImagePanelView<
|
|
|
32
31
|
constructor(
|
|
33
32
|
private readonly pluginKey: PluginKey,
|
|
34
33
|
private readonly pmView: EditorView,
|
|
35
|
-
emitUpdate: (state:
|
|
34
|
+
emitUpdate: (state: FilePanelState<I, S>) => void
|
|
36
35
|
) {
|
|
37
36
|
this.emitUpdate = () => {
|
|
38
37
|
if (!this.state) {
|
|
39
|
-
throw new Error("Attempting to update uninitialized
|
|
38
|
+
throw new Error("Attempting to update uninitialized file panel");
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
emitUpdate(this.state);
|
|
@@ -77,7 +76,7 @@ export class ImagePanelView<
|
|
|
77
76
|
|
|
78
77
|
update(view: EditorView, prevState: EditorState) {
|
|
79
78
|
const pluginState: {
|
|
80
|
-
block: BlockFromConfig<
|
|
79
|
+
block: BlockFromConfig<FileBlockConfig, I, S>;
|
|
81
80
|
} = this.pluginKey.getState(view.state);
|
|
82
81
|
|
|
83
82
|
if (!this.state?.show && pluginState.block) {
|
|
@@ -124,27 +123,26 @@ export class ImagePanelView<
|
|
|
124
123
|
}
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
const
|
|
126
|
+
const filePanelPluginKey = new PluginKey("FilePanelPlugin");
|
|
128
127
|
|
|
129
|
-
export class
|
|
128
|
+
export class FilePanelProsemirrorPlugin<
|
|
129
|
+
B extends BlockSchema,
|
|
130
130
|
I extends InlineContentSchema,
|
|
131
131
|
S extends StyleSchema
|
|
132
132
|
> extends EventEmitter<any> {
|
|
133
|
-
private view:
|
|
133
|
+
private view: FilePanelView<I, S> | undefined;
|
|
134
134
|
public readonly plugin: Plugin;
|
|
135
135
|
|
|
136
|
-
constructor(
|
|
137
|
-
_editor: BlockNoteEditor<{ image: DefaultBlockSchema["image"] }, I, S>
|
|
138
|
-
) {
|
|
136
|
+
constructor(_editor: BlockNoteEditor<B, I, S>) {
|
|
139
137
|
super();
|
|
140
138
|
this.plugin = new Plugin<{
|
|
141
|
-
block: BlockFromConfig<
|
|
139
|
+
block: BlockFromConfig<FileBlockConfig, I, S> | undefined;
|
|
142
140
|
}>({
|
|
143
|
-
key:
|
|
141
|
+
key: filePanelPluginKey,
|
|
144
142
|
view: (editorView) => {
|
|
145
|
-
this.view = new
|
|
143
|
+
this.view = new FilePanelView(
|
|
146
144
|
// editor,
|
|
147
|
-
|
|
145
|
+
filePanelPluginKey,
|
|
148
146
|
editorView,
|
|
149
147
|
(state) => {
|
|
150
148
|
this.emit("update", state);
|
|
@@ -168,9 +166,8 @@ export class ImagePanelProsemirrorPlugin<
|
|
|
168
166
|
};
|
|
169
167
|
},
|
|
170
168
|
apply: (transaction) => {
|
|
171
|
-
const block:
|
|
172
|
-
|
|
173
|
-
| undefined = transaction.getMeta(imagePanelPluginKey)?.block;
|
|
169
|
+
const block: BlockFromConfig<FileBlockConfig, I, S> | undefined =
|
|
170
|
+
transaction.getMeta(filePanelPluginKey)?.block;
|
|
174
171
|
|
|
175
172
|
return {
|
|
176
173
|
block,
|
|
@@ -184,7 +181,7 @@ export class ImagePanelProsemirrorPlugin<
|
|
|
184
181
|
return this.view?.state?.show || false;
|
|
185
182
|
}
|
|
186
183
|
|
|
187
|
-
public onUpdate(callback: (state:
|
|
184
|
+
public onUpdate(callback: (state: FilePanelState<I, S>) => void) {
|
|
188
185
|
return this.on("update", callback);
|
|
189
186
|
}
|
|
190
187
|
|