Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions examples/02-backend/01-file-uploading/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,9 @@ import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";

// Uploads a file to tmpfiles.org and returns the URL to the uploaded file.
// Creates a local blob URL for the file (for testing without an upload server).
async function uploadFile(file: File) {
const body = new FormData();
body.append("file", file);

const ret = await fetch("https://tmpfiles.org/api/v1/upload", {
method: "POST",
body: body,
});
return (await ret.json()).data.url.replace(
"tmpfiles.org/",
"tmpfiles.org/dl/",
);
return URL.createObjectURL(file);
}

export default function App() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Extension } from "@tiptap/core";
import { Plugin } from "prosemirror-state";

import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js";
import { FilePanelExtension } from "../../../extensions/FilePanel/FilePanel.js";
import {
BlockSchema,
InlineContentSchema,
Expand Down Expand Up @@ -41,6 +42,14 @@ export const createDropFileExtension = <
}

if (format === "Files") {
// If the file panel is open, its React drop handler
// manages the upload — don't insert a duplicate block.
if (
editor.getExtension(FilePanelExtension)?.store.state
) {
return true;
}

handleFileInsertion(event, editor);
return true;
}
Expand Down
58 changes: 49 additions & 9 deletions packages/react/src/components/FilePanel/FilePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {
DefaultInlineContentSchema,
DefaultStyleSchema,
InlineContentSchema,
PartialBlock,
StyleSchema,
} from "@blocknote/core";
import { useState } from "react";
import { DragEvent, useCallback, useState } from "react";

import {
ComponentProps,
Expand Down Expand Up @@ -40,6 +41,43 @@ export const FilePanel = <

const [loading, setLoading] = useState<boolean>(false);

const handleDragOver = useCallback((e: DragEvent) => {
e.preventDefault();
}, []);

const handleDrop = useCallback(
(e: DragEvent) => {
e.preventDefault();
e.stopPropagation();

const file = e.dataTransfer.files?.[0];
if (!file || !editor.uploadFile) {
return;
}

(async () => {
setLoading(true);
try {
let updateData = await editor.uploadFile!(file, props.blockId);
if (typeof updateData === "string") {
updateData = {
props: {
name: file.name,
url: updateData,
},
} as PartialBlock<B, I, S>;
}
editor.updateBlock(props.blockId, updateData);
} catch {
// Leave panel open so the user can retry.
} finally {
setLoading(false);
}
})();
},
[editor, props.blockId],
);

const tabs: PanelProps["tabs"] = props.tabs ?? [
...(editor.uploadFile !== undefined
? [
Expand All @@ -62,13 +100,15 @@ export const FilePanel = <
);

return (
<Components.FilePanel.Root
className={"bn-panel"}
defaultOpenTab={openTab}
openTab={openTab}
setOpenTab={setOpenTab}
tabs={tabs}
loading={loading}
/>
<div onDragOver={handleDragOver} onDrop={handleDrop}>
<Components.FilePanel.Root
className={"bn-panel bn-add-file-panel"}
defaultOpenTab={openTab}
openTab={openTab}
setOpenTab={setOpenTab}
tabs={tabs}
loading={loading}
/>
</div>
);
};