Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added support for uppy #842

Merged
merged 24 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a357971
feat: add support for uppy
ezhil56x Jun 5, 2024
ddc9b86
Merge branch 'TypeCellOS:main' into uppy
ezhil56x Jun 7, 2024
6079f06
feat: add support for uppy
ezhil56x Jun 13, 2024
f844baa
Merge branch 'main' of github.com:ezhil56x/BlockNote into uppy
ezhil56x Jun 13, 2024
53bd537
feat: add support for uppy
ezhil56x Jun 17, 2024
dcdc194
feat: add support for uppy
ezhil56x Jun 17, 2024
fca6777
feat: add support for uppy
ezhil56x Jun 17, 2024
02997f4
feat: add support for uppy
ezhil56x Jun 17, 2024
ceeeecd
feat: add support for uppy
ezhil56x Jun 17, 2024
3d225ed
feat: add support for uppy
ezhil56x Jun 17, 2024
56efb1f
feat: add support for uppy
ezhil56x Jun 17, 2024
5962e1a
Merge branch 'TypeCellOS:main' into uppy
ezhil56x Jun 18, 2024
640ead1
feat: add support for uppy
ezhil56x Jun 19, 2024
06053ac
Merge branch 'TypeCellOS:main' into uppy
ezhil56x Jun 25, 2024
a3ed8fc
feat: add support for uppy
ezhil56x Jun 25, 2024
40d20eb
feat: add support for uppy
ezhil56x Jun 26, 2024
1dbc075
Merge branch 'TypeCellOS:main' into uppy
ezhil56x Jul 3, 2024
fb26ec0
Merge remote-tracking branch 'origin/main' into pr/ezhil56x/842
YousefED Jul 9, 2024
4b82a24
update uppy integration
YousefED Jul 9, 2024
23f6b35
fix build
YousefED Jul 9, 2024
3434081
Made Uppy panel also appear for file replace button
matthewlipski Jul 11, 2024
de583ba
Merge branch 'refs/heads/main' into uppy
matthewlipski Jul 11, 2024
6b3ce17
Updated `examples.gen.tsx`
matthewlipski Jul 11, 2024
a70c0ca
Updated `examples.gen.tsx`
matthewlipski Jul 11, 2024
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
20 changes: 20 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/.bnexample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"playground": true,
"docs": true,
"author": "ezhil56x",
"tags": ["Intermediate", "Files"],
"dependencies": {
"@uppy/core": "^3.13.1",
"@uppy/dashboard": "^3.9.1",
"@uppy/drag-drop": "^3.1.1",
"@uppy/file-input": "^3.1.2",
"@uppy/image-editor": "^2.4.6",
"@uppy/progress-bar": "^3.1.1",
"@uppy/react": "^3.4.0",
"@uppy/screen-capture": "^3.2.0",
"@uppy/status-bar": "^3.1.1",
"@uppy/webcam": "^3.4.2",
"@uppy/xhr-upload": "^3.4.0",
"react-icons": "^5.2.1"
}
}
53 changes: 53 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import {
FilePanelController,
FormattingToolbar,
FormattingToolbarController,
getFormattingToolbarItems,
useCreateBlockNote,
} from "@blocknote/react";

import { uploadFile, UppyFilePanel } from "./UppyFilePanel";
import { FileReplaceButton } from "./FileReplaceButton";

export default function App() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
initialContent: [
{
type: "paragraph",
content: "Welcome to this demo!",
},
{
type: "paragraph",
content: "Upload an image using the button below",
},
{
type: "image",
},
{
type: "paragraph",
},
],
uploadFile,
});

// Renders the editor instance using a React component.
return (
<BlockNoteView editor={editor} formattingToolbar={false} filePanel={false}>
<FormattingToolbarController
formattingToolbar={(props) => {
// Replaces default file replace button with one that opens Uppy.
const items = getFormattingToolbarItems();
items.splice(2, 1, <FileReplaceButton key={"fileReplaceButton"} />);

return <FormattingToolbar {...props}>{items}</FormattingToolbar>;
}}
/>
{/* Replaces default file panel with Uppy one. */}
<FilePanelController filePanel={UppyFilePanel} />
</BlockNoteView>
);
}
74 changes: 74 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/FileReplaceButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
BlockSchema,
checkBlockIsFileBlock,
InlineContentSchema,
StyleSchema,
} from "@blocknote/core";
import {
useBlockNoteEditor,
useComponentsContext,
useDictionary,
useSelectedBlocks,
} from "@blocknote/react";
import { useEffect, useState } from "react";
import { RiImageEditFill } from "react-icons/ri";
import { UppyFilePanel } from "./UppyFilePanel";

// Copied with minor changes from:
// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/FileReplaceButton.tsx
// Opens Uppy file panel instead of the default one.
export const FileReplaceButton = () => {
const dict = useDictionary();
const Components = useComponentsContext()!;

const editor = useBlockNoteEditor<
BlockSchema,
InlineContentSchema,
StyleSchema
>();

const selectedBlocks = useSelectedBlocks(editor);

const [isOpen, setIsOpen] = useState<boolean>(false);

useEffect(() => {
setIsOpen(false);
}, [selectedBlocks]);

const block = selectedBlocks.length === 1 ? selectedBlocks[0] : undefined;

if (
block === undefined ||
!checkBlockIsFileBlock(block, editor) ||
!editor.isEditable
) {
return null;
}

return (
<Components.Generic.Popover.Root opened={isOpen} position={"bottom"}>
<Components.Generic.Popover.Trigger>
<Components.FormattingToolbar.Button
className={"bn-button"}
onClick={() => setIsOpen(!isOpen)}
isSelected={isOpen}
mainTooltip={
dict.formatting_toolbar.file_replace.tooltip[block.type] ||
dict.formatting_toolbar.file_replace.tooltip["file"]
}
label={
dict.formatting_toolbar.file_replace.tooltip[block.type] ||
dict.formatting_toolbar.file_replace.tooltip["file"]
}
icon={<RiImageEditFill />}
/>
</Components.Generic.Popover.Trigger>
<Components.Generic.Popover.Content
className={"bn-popover-content bn-panel-popover"}
variant={"panel-popover"}>
{/* Replaces default file panel with our Uppy one. */}
<UppyFilePanel block={block as any} />
</Components.Generic.Popover.Content>
</Components.Generic.Popover.Root>
);
};
19 changes: 19 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Uploading Files with Uppy

This example allows users to upload files using [Uppy](https://uppy.io/) and use them in the editor.

Uppy is highly extensible and has an extensive ecosystem of plugins. For example, you can:

- record audio, screen or webcam
- import files from Box / Dropbox / Facebook / Google Drive / Google Photos / Instagram / OneDrive / Zoom
- select files from Unsplash
- show an image editor (crop, rotate, etc)

(in this example, we've enabled the Webcam, ScreenCapture and Image Editor plugin)

**Try it out:** Click the "Add Image" button and you can either drop files or click "browse files" to upload them.

**Relevant Docs:**

- [Editor Setup](/docs/editor-basics/setup)
- [Image](/docs/editor-basics/default-schema#image)
104 changes: 104 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/UppyFilePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { FilePanelProps, useBlockNoteEditor } from "@blocknote/react";
import Uppy, { UploadSuccessCallback } from "@uppy/core";
import "@uppy/core/dist/style.min.css";
import "@uppy/dashboard/dist/style.min.css";
import { Dashboard } from "@uppy/react";
import XHR from "@uppy/xhr-upload";
import { useEffect } from "react";

// Image editor plugin
import ImageEditor from "@uppy/image-editor";
import "@uppy/image-editor/dist/style.min.css";

// Screen capture plugin
import ScreenCapture from "@uppy/screen-capture";
import "@uppy/screen-capture/dist/style.min.css";

// Webcam plugin
import Webcam from "@uppy/webcam";
import "@uppy/webcam/dist/style.min.css";

// Configure your Uppy instance here.
const uppy = new Uppy()
// Enabled plugins - you probably want to customize this
// See https://uppy.io/examples/ for all the integrations like Google Drive,
// Instagram Dropbox etc.
.use(Webcam)
.use(ScreenCapture)
.use(ImageEditor)

// Uses an XHR upload plugin to upload files to tmpfiles.org.
// You want to replace this with your own upload endpoint or Uppy Companion
// server.
.use(XHR, {
endpoint: "https://tmpfiles.org/api/v1/upload",
getResponseData(text, resp) {
return {
url: JSON.parse(text).data.url.replace(
"tmpfiles.org/",
"tmpfiles.org/dl/"
),
};
},
});

export function UppyFilePanel(props: FilePanelProps) {
const { block } = props;
const editor = useBlockNoteEditor();

useEffect(() => {
// Listen for successful tippy uploads, and then update the Block with the
// uploaded URL.
const handler: UploadSuccessCallback<Record<string, unknown>> = (
file,
response
) => {
if (!file) {
return;
}

if (file.source === "uploadFile") {
// Didn't originate from Dashboard, should be handled by `uploadFile`
return;
}
if (response.status === 200) {
const updateData = {
props: {
name: file?.name,
url: response.uploadURL,
},
};
editor.updateBlock(block, updateData);

// File should be removed from the Uppy instance after upload.
uppy.removeFile(file.id);
}
};
uppy.on("upload-success", handler);
return () => {
uppy.off("upload-success", handler);
};
}, [block, editor]);

// set up dashboard as in https://uppy.io/examples/
return <Dashboard uppy={uppy} width={400} height={500} />;
}

// Implementation for the BlockNote `uploadFile` function.
// This function is used when for example, files are dropped into the editor.
export async function uploadFile(file: File) {
const id = uppy.addFile({
id: file.name,
name: file.name,
type: file.type,
data: file,
source: "uploadFile",
});

try {
const result = await uppy.upload();
return result.successful[0].response!.uploadURL!;
} finally {
uppy.removeFile(id);
}
}
14 changes: 14 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html lang="en">
<head>
<script>
<!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY -->
</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Uploading Files with Uppy</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
11 changes: 11 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const root = createRoot(document.getElementById("root")!);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
49 changes: 49 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@blocknote/example-file-uploading-uppy",
"description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
"private": true,
"version": "0.12.4",
"scripts": {
"start": "vite",
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --max-warnings 0"
},
"dependencies": {
"@blocknote/core": "latest",
"@blocknote/react": "latest",
"@blocknote/ariakit": "latest",
"@blocknote/mantine": "latest",
"@blocknote/shadcn": "latest",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"@uppy/core": "^3.13.1",
"@uppy/dashboard": "^3.9.1",
"@uppy/drag-drop": "^3.1.1",
"@uppy/file-input": "^3.1.2",
"@uppy/image-editor": "^2.4.6",
"@uppy/progress-bar": "^3.1.1",
"@uppy/react": "^3.4.0",
"@uppy/screen-capture": "^3.2.0",
"@uppy/status-bar": "^3.1.1",
"@uppy/webcam": "^3.4.2",
"@uppy/xhr-upload": "^3.4.0",
"react-icons": "^5.2.1"
},
"devDependencies": {
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^4.0.4",
"eslint": "^8.10.0",
"vite": "^4.4.8"
},
"eslintConfig": {
"extends": [
"../../../.eslintrc.js"
]
},
"eslintIgnore": [
"dist"
]
}
36 changes: 36 additions & 0 deletions examples/01-basic/12-file-uploading-uppy/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"composite": true
},
"include": [
"."
],
"__ADD_FOR_LOCAL_DEV_references": [
{
"path": "../../../packages/core/"
},
{
"path": "../../../packages/react/"
}
]
}
Loading
Loading