Skip to content

Commit c689e4b

Browse files
fix: File HTML export and resizing UX (#1305)
* Fixed issues with exporting file blocks to external HTML * Changed editor view typing to possibly be undefined * Updated resizing logic * Improved file resize UX * Cleaned up React DOM structure * Cleaned up vanilla DOM structure * React code cleanup * Styles cleanup * Small fixes * - Refactored `ResizableFileBlockWrapper` to get rid of context - Renamed `DefaultFilePreview` to `FileNameWithIcon` - Made file `src` get set straight away only if `resolveFileUrl` is undefined * Cleaned up references to view * Updated unit tests * Implemented PR feedback * Fixes unit tests * Implemented PR feedback * Updated unit test snapshots * Added `ServerBlockNoteEditor` tests * Fixed e2e test
1 parent dc2300e commit c689e4b

File tree

87 files changed

+1202
-1110
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1202
-1110
lines changed

examples/06-custom-schema/04-pdf-file-block/PDF.tsx

+7-28
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { FileBlockConfig } from "@blocknote/core";
22
import {
3-
AddFileButton,
43
createReactBlockSpec,
5-
DefaultFilePreview,
6-
FileAndCaptionWrapper,
74
ReactCustomBlockRenderProps,
5+
ResizableFileBlockWrapper,
86
} from "@blocknote/react";
97

108
import { RiFilePdfFill } from "react-icons/ri";
@@ -53,31 +51,12 @@ export const PDF = createReactBlockSpec(
5351
},
5452
{
5553
render: (props) => (
56-
<div className={"bn-file-block-content-wrapper"}>
57-
{props.block.props.url === "" ? (
58-
<AddFileButton
59-
{...props}
60-
editor={props.editor as any}
61-
buttonText={"Add PDF"}
62-
buttonIcon={<RiFilePdfFill size={24} />}
63-
/>
64-
) : !props.block.props.showPreview ? (
65-
<FileAndCaptionWrapper
66-
block={props.block}
67-
editor={props.editor as any}>
68-
<DefaultFilePreview
69-
block={props.block}
70-
editor={props.editor as any}
71-
/>
72-
</FileAndCaptionWrapper>
73-
) : (
74-
<FileAndCaptionWrapper
75-
block={props.block}
76-
editor={props.editor as any}>
77-
<PDFPreview block={props.block} editor={props.editor as any} />
78-
</FileAndCaptionWrapper>
79-
)}
80-
</div>
54+
<ResizableFileBlockWrapper
55+
{...(props as any)}
56+
bbuttonText={"Add PDF"}
57+
buttonIcon={<RiFilePdfFill size={24} />}>
58+
<PDFPreview {...(props as any)} />
59+
</ResizableFileBlockWrapper>
8160
),
8261
}
8362
);

packages/core/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function makeSelectionSpanContent(selectionType: "text" | "node" | "cell") {
2222
const { blockContent } = blockInfo;
2323

2424
if (selectionType === "cell") {
25-
getEditor()._tiptapEditor.view.dispatch(
25+
getEditor().dispatch(
2626
getEditor()._tiptapEditor.state.tr.setSelection(
2727
CellSelection.create(
2828
getEditor()._tiptapEditor.state.doc,
@@ -36,7 +36,7 @@ function makeSelectionSpanContent(selectionType: "text" | "node" | "cell") {
3636
)
3737
);
3838
} else if (selectionType === "node") {
39-
getEditor()._tiptapEditor.view.dispatch(
39+
getEditor().dispatch(
4040
getEditor()._tiptapEditor.state.tr.setSelection(
4141
NodeSelection.create(
4242
getEditor()._tiptapEditor.state.doc,
@@ -45,7 +45,7 @@ function makeSelectionSpanContent(selectionType: "text" | "node" | "cell") {
4545
)
4646
);
4747
} else {
48-
getEditor()._tiptapEditor.view.dispatch(
48+
getEditor().dispatch(
4949
getEditor()._tiptapEditor.state.tr.setSelection(
5050
TextSelection.create(
5151
getEditor()._tiptapEditor.state.doc,

packages/core/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ function updateBlockSelectionFromData(
125125
);
126126
}
127127

128-
editor._tiptapEditor.view.dispatch(
129-
editor._tiptapEditor.state.tr.setSelection(selection)
130-
);
128+
editor.dispatch(editor._tiptapEditor.state.tr.setSelection(selection));
131129
}
132130

133131
/**

packages/core/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function setSelectionWithOffset(
3838
throw new Error("Target block is not a block container");
3939
}
4040

41-
getEditor()._tiptapEditor.view.dispatch(
41+
getEditor().dispatch(
4242
getEditor()._tiptapEditor.state.tr.setSelection(
4343
TextSelection.create(doc, info.blockContent.beforePos + offset + 1)
4444
)

packages/core/src/api/clipboard/clipboard.test.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,18 @@ describe("Test ProseMirror selection clipboard HTML", () => {
167167
// Sets the editor selection to the given start and end positions, then
168168
// exports the selected content to HTML and compares it to a snapshot.
169169
async function testSelection(testCase: SelectionTestCase) {
170+
if (!editor.prosemirrorView) {
171+
throw new Error("Editor view not initialized.");
172+
}
173+
170174
editor.dispatch(
171175
editor._tiptapEditor.state.tr.setSelection(
172-
testCase.createSelection(editor._tiptapEditor.view.state.doc)
176+
testCase.createSelection(editor.prosemirrorView.state.doc)
173177
)
174178
);
175179

176180
const { clipboardHTML, externalHTML } = selectedFragmentToHTML(
177-
editor._tiptapEditor.view,
181+
editor.prosemirrorView,
178182
editor
179183
);
180184

@@ -184,7 +188,7 @@ describe("Test ProseMirror selection clipboard HTML", () => {
184188

185189
const originalDocument = editor.document;
186190
doPaste(
187-
editor._tiptapEditor.view,
191+
editor.prosemirrorView,
188192
"text",
189193
clipboardHTML,
190194
false,

packages/core/src/api/clipboard/fromClipboard/handleFileInsertion.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export async function handleFileInsertion<
131131
top: (event as DragEvent).clientY,
132132
};
133133

134-
const pos = editor._tiptapEditor.view.posAtCoords(coords);
134+
const pos = editor.prosemirrorView?.posAtCoords(coords);
135135
if (!pos) {
136136
return;
137137
}

packages/core/src/api/clipboard/fromClipboard/handleVSCodePaste.ts

+7-14
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
import { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js";
2-
import {
3-
BlockSchema,
4-
InlineContentSchema,
5-
StyleSchema,
6-
} from "../../../schema/index.js";
7-
8-
export async function handleVSCodePaste<
9-
BSchema extends BlockSchema,
10-
I extends InlineContentSchema,
11-
S extends StyleSchema
12-
>(event: ClipboardEvent, editor: BlockNoteEditor<BSchema, I, S>) {
13-
const view = editor.prosemirrorView;
1+
import { EditorView } from "prosemirror-view";
2+
3+
export async function handleVSCodePaste(
4+
event: ClipboardEvent,
5+
view: EditorView
6+
) {
147
const { schema } = view.state;
158

169
if (!event.clipboardData) {
@@ -38,7 +31,7 @@ export async function handleVSCodePaste<
3831

3932
// strip carriage return chars from text pasted as code
4033
// see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd
41-
editor._tiptapEditor.view.pasteHTML(
34+
view.pasteHTML(
4235
`<pre><code class="language-${language}">${text.replace(
4336
/\r\n?/g,
4437
"\n"

packages/core/src/api/clipboard/fromClipboard/pasteExtension.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ export const createPasteFromClipboardExtension = <
1919
>(
2020
editor: BlockNoteEditor<BSchema, I, S>
2121
) =>
22-
Extension.create<{ editor: BlockNoteEditor<BSchema, I, S> }, undefined>({
22+
Extension.create({
2323
name: "pasteFromClipboard",
2424
addProseMirrorPlugins() {
2525
return [
2626
new Plugin({
2727
props: {
2828
handleDOMEvents: {
29-
paste(_view, event) {
29+
paste(view, event) {
3030
event.preventDefault();
3131

3232
if (!editor.isEditable) {
@@ -45,7 +45,7 @@ export const createPasteFromClipboardExtension = <
4545
}
4646

4747
if (format === "vscode-editor-data") {
48-
handleVSCodePaste(event, editor);
48+
handleVSCodePaste(event, view);
4949
return true;
5050
}
5151

@@ -57,18 +57,18 @@ export const createPasteFromClipboardExtension = <
5757
let data = event.clipboardData!.getData(format);
5858

5959
if (format === "blocknote/html") {
60-
editor._tiptapEditor.view.pasteHTML(data);
60+
view.pasteHTML(data);
6161
return true;
6262
}
6363

6464
if (format === "text/html") {
6565
const htmlNode = nestedListsToBlockNoteStructure(data.trim());
6666
data = htmlNode.innerHTML;
67-
editor._tiptapEditor.view.pasteHTML(data);
67+
view.pasteHTML(data);
6868
return true;
6969
}
7070

71-
editor._tiptapEditor.view.pasteText(data);
71+
view.pasteText(data);
7272

7373
return true;
7474
},
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-file-default-preview"><div class="bn-file-default-preview-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-default-preview-name">example</p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div></div>
1+
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name">example</p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-file-default-preview"><div class="bn-file-default-preview-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-default-preview-name">example</p></div><p class="bn-file-caption">Caption</p></div></div></div><div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-file-default-preview"><div class="bn-file-default-preview-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-default-preview-name">example</p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div></div></div></div></div>
1+
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name">example</p></div><p class="bn-file-caption">Caption</p></div></div><div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name">example</p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div></div></div></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-file-default-preview"><div class="bn-file-default-preview-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-default-preview-name">example</p></div><p class="bn-file-caption"></p></div></div></div></div></div></div>
1+
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-name="example" data-url="exampleURL" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name">example</p></div></div></div></div></div></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-file-default-preview"><div class="bn-file-default-preview-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-default-preview-name"></p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div></div>
1+
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="file" data-url="exampleURL" data-caption="Caption" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-name-with-icon"><div class="bn-file-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg></div><p class="bn-file-name"></p></div><p class="bn-file-caption">Caption</p></div></div></div></div></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="image" data-name="example" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" alt="example" draggable="false" width="0"></div><p class="bn-file-caption" style="width: 256px;">Caption</p></div></div></div></div></div></div>
1+
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="image" data-name="example" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" src="exampleURL" alt="example" draggable="false"></div><p class="bn-file-caption">Caption</p></div></div></div></div></div>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="image" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" alt="Caption" draggable="false" width="0"></div><p class="bn-file-caption" style="width: 256px;">Caption</p></div></div></div><div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2"><div class="bn-block-content" data-content-type="image" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-file-and-caption-wrapper"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" alt="Caption" draggable="false" width="0"></div><p class="bn-file-caption" style="width: 256px;">Caption</p></div></div></div></div></div></div></div></div></div>
1+
<div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="1"><div class="bn-block" data-node-type="blockContainer" data-id="1"><div class="bn-block-content" data-content-type="image" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" src="exampleURL" alt="Caption" draggable="false"></div><p class="bn-file-caption">Caption</p></div></div><div class="bn-block-group" data-node-type="blockGroup"><div class="bn-block-outer" data-node-type="blockOuter" data-id="2"><div class="bn-block" data-node-type="blockContainer" data-id="2"><div class="bn-block-content" data-content-type="image" data-url="exampleURL" data-caption="Caption" data-preview-width="256" data-file-block=""><div class="bn-file-block-content-wrapper"><div class="bn-visual-media-wrapper"><img class="bn-visual-media" src="exampleURL" alt="Caption" draggable="false"></div><p class="bn-file-caption">Caption</p></div></div></div></div></div></div></div></div>

0 commit comments

Comments
 (0)