From 92c469cf01991a653d40591d4a80ee176790c172 Mon Sep 17 00:00:00 2001 From: arausly Date: Tue, 17 Jun 2025 15:55:06 +0100 Subject: [PATCH] added fileupload changes --- package.json | 3 + src/extensions/index.ts | 2 + src/extensions/uppy/FileUpload.stories.tsx | 34 ++++ src/extensions/uppy/FileUpload.tsx | 171 +++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 src/extensions/uppy/FileUpload.stories.tsx create mode 100644 src/extensions/uppy/FileUpload.tsx diff --git a/package.json b/package.json index 1a73b18be..d014a2dfc 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,9 @@ "@codemirror/legacy-modes": "^6.5.0", "@mavrin/remark-typograf": "^2.2.0", "@xyflow/react": "^12.6.0", + "@uppy/core": "^2.1.4", + "@uppy/react": "^2.1.2", + "@uppy/xhr-upload": "^2.0.7", "classnames": "^2.5.1", "codemirror": "^6.0.1", "color": "^4.2.3", diff --git a/src/extensions/index.ts b/src/extensions/index.ts index ad93a8294..a3503f469 100644 --- a/src/extensions/index.ts +++ b/src/extensions/index.ts @@ -1,2 +1,4 @@ export * from "./codemirror/CodeMirror"; export * from "./react-flow"; +export * from "./uppy/FileUpload"; +export * from "@uppy/core"; diff --git a/src/extensions/uppy/FileUpload.stories.tsx b/src/extensions/uppy/FileUpload.stories.tsx new file mode 100644 index 000000000..9463908ae --- /dev/null +++ b/src/extensions/uppy/FileUpload.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { Meta, StoryFn } from "@storybook/react"; + +import { FileUpload } from "./FileUpload"; + +export default { + title: "Extensions/FileUpload", + component: FileUpload +} as Meta; + +const TemplateFull: StoryFn = args => + ; + +export const BasicExample = TemplateFull.bind({}); +BasicExample.args = { + id: "fileupload", + onError: null, + onSuccess: null, + handleResponseData: null, + localeOptions: { + strings: { + dropHereOr: "Drop here or %{browse}", + browse: "browse" + } + }, + resetForm: true, + autoProceed: true, + restrictions: { + maxFileSize: 1000000, + minNumberOfFiles: 1, + maxNumberOfFiles: 5, + allowedFileTypes: [".jpg", ".png"] + } +}; diff --git a/src/extensions/uppy/FileUpload.tsx b/src/extensions/uppy/FileUpload.tsx new file mode 100644 index 000000000..5fc0bd590 --- /dev/null +++ b/src/extensions/uppy/FileUpload.tsx @@ -0,0 +1,171 @@ +import React from "react"; +import Uppy, { Locale, Restrictions, UppyEventMap, UppyFile } from "@uppy/core"; +import { DragDrop, ProgressBar, useUppy } from "@uppy/react"; +import XHRUpload, { XHRUploadOptions } from "@uppy/xhr-upload"; + +import { TestableComponent } from "../../components/interfaces"; + +import "@uppy/core/dist/style.css"; +import "@uppy/drag-drop/dist/style.css"; +import "@uppy/progress-bar/dist/style.css"; + +type eventNames = keyof UppyEventMap; + +export interface FileUploadProps extends TestableComponent { + /** + * upload instance id + */ + id?: string; + /** + * upload error callback function + */ + onError?: (responseText: string) => void; + /** + * upload success callback function + */ + onSuccess?: (responseText: string) => void; + /** + * upload response callback function + */ + handleResponseData?: (responseText: string) => void; + /** + * locale options, like button labels etc. + */ + localeOptions?: Locale; + /** + * reset form after upload or in case of error/navigation steps + */ + resetForm?: boolean; + /** + * auto proceed bolean + */ + autoProceed?: boolean; + /** + * upload restrictions + */ + restrictions?: Restrictions; + /** + * additional upload files + */ + additionalFiles?: { + id: string; + name: string; + size: number; + data: Blob; + }[]; + /** + * xhr upload options + */ + xhrUploadOptions: XHRUploadOptions; + /** + * uppy events to register and unregister to + */ + events?: { [key in eventNames]?: any }; + /** + * set uppy instance + */ + setInstance?: (instance: Uppy) => void; +} + +export { ProgressBar as FileUploadProgressBar, DragDrop as FileUploadDragDrop} from "@uppy/react"; +export type {XHRUploadOptions} from "@uppy/xhr-upload" + + +export const FileUpload: React.FC = ({ + id, + onError, + onSuccess, + handleResponseData, + resetForm, + autoProceed = false, + restrictions, + additionalFiles, + xhrUploadOptions, + localeOptions, + events, + setInstance, + children, + ...rest +}) => { + const uppy = useUppy(() => { + return new Uppy({ + id, + autoProceed, + restrictions, + locale: localeOptions, + }) + .on("upload-success", (_, response) => { + onSuccess?.(response.body); + }) + .use(XHRUpload, { + ...xhrUploadOptions, + getResponseData: (responseText) => { + handleResponseData?.(responseText); + return responseText; + }, + getResponseError: (responseText) => { + uppy.reset(); + + onError?.(responseText); + return new Error(responseText); + }, + }); + }); + + //events + React.useEffect(() => { + uppy && setInstance && setInstance(uppy); + const eventEntries = Object.entries(events ?? {}) as [keyof UppyEventMap, (file: UppyFile) => Promise][]; + const unregisterEvents = () => { + eventEntries.length && eventEntries.forEach(([event, handler]) => uppy.off(event, handler)); + }; + + unregisterEvents(); + + eventEntries.length && + eventEntries.forEach(([event, handler]) => { + uppy.on(event, handler); + }); + + return () => unregisterEvents(); + },[events, uppy]); + + React.useEffect(() => { + if (resetForm) { + uppy.reset(); + } + + return () => { + uppy.close(); + }; + }, [resetForm, uppy]); + + React.useEffect(() => { + if (additionalFiles) { + additionalFiles.forEach((file) => { + uppy.addFile({ + name: file.name, + data: file.data, + size: file.size, + id: file.id, + }); + }); + } + }, [additionalFiles, uppy]); + + if (!uppy) return null; + + + return ( +
+ {children ? ( + children + ) : ( + <> + + + + )} +
+ ); +}; \ No newline at end of file