From b0aab8c20f1e334142539b7bea8de15b44841365 Mon Sep 17 00:00:00 2001 From: Jose Manuel Serrano Amaut <a20122128@pucp.pe> Date: Mon, 27 Feb 2023 16:36:34 -0500 Subject: [PATCH] [FIX]: Refactor some components that shpuld be in the return statement instead of being created every render (Action buttons on dropzone and preparing an uploading status componenets in FileMosaic). Missing refactor code that is too large --- bugs.md | 8 +- future-features.md | 9 +- ideas.md | 2 +- .../dropzone-demo/AdvancedDropzoneDemo.jsx | 6 +- .../DemoFileMosaicImagePreview.tsx | 2 +- .../DemoFileMosaicValidation.jsx | 4 +- .../FileMosaicUploadLayer.tsx | 185 +++++------------- .../components/file-status/AbortedStatus.tsx | 21 ++ .../components/file-status/EmptyStatus.tsx | 20 ++ .../components/file-status/ErrorStatus.tsx | 26 +++ .../components/file-status/FileStatusProps.ts | 6 + .../file-status/PreparingStatus.tsx | 26 +++ .../components/file-status/SuccessStatus.tsx | 24 +++ .../file-status/UploadingStatus.tsx | 41 ++++ src/files-ui/components/file-status/index.ts | 20 ++ 15 files changed, 249 insertions(+), 151 deletions(-) create mode 100644 src/files-ui/components/file-status/AbortedStatus.tsx create mode 100644 src/files-ui/components/file-status/EmptyStatus.tsx create mode 100644 src/files-ui/components/file-status/ErrorStatus.tsx create mode 100644 src/files-ui/components/file-status/FileStatusProps.ts create mode 100644 src/files-ui/components/file-status/PreparingStatus.tsx create mode 100644 src/files-ui/components/file-status/SuccessStatus.tsx create mode 100644 src/files-ui/components/file-status/UploadingStatus.tsx create mode 100644 src/files-ui/components/file-status/index.ts diff --git a/bugs.md b/bugs.md index b16d2d1..86b72e3 100644 --- a/bugs.md +++ b/bugs.md @@ -2,14 +2,16 @@ ## File Item (mosaic) -- on change progress the loader is reredered again and again, I think that the 3 states or layers must be there from the begining +- [SOLVED]on change progress the loader is reredered again and again, I think that the 3 states or layers must be there from the begining "preparing", "uploading", "result" - on cancel, the 2nd layer will dissapear, it will not be any big deal - After uploading, progress must be reiitialized to 0 -- [SOLVED] FileiTEMmAINlAYER WORKS STRANGE AT THE TIME NEW fILEiTEM IS ADDED -- Fileptions (menu collapsed from click in option icon) +- [SOLVED] FileItemMainLayer WORKS STRANGE AT THE TIME NEW fILEiTEM IS ADDED + ## Dropzone +- When no validation, upload removes all files. +- [SOLVED]: action buttons rendered again and again since they were created again every time "value" prop changed - [SOLVED]: Uploading works in 2 times (first time stops after setting progress = UPLOADING.progrss), but fails to recover from the manager. [UPDATED]: Problem is at `useDropzoneFileListUpdater.ts` file. The problem is that hook for updating when user wants to interrupt preparing, is called at the begining of the upload process, with value of undefined in all files. It is probably the last update on localFiles outside Dropzone component. - When file is set from preparing to undefined it can be deleted, however, will appear again if onDelete is called. It would be great to add a reconciliation procedure to support different array sizes in updater hook. Or "canceled" upload status could be added and file Item should not show the "X" button when uploading and canceled. After Upload process, all files with "canceled" upload status should be set to "undefined" again. This can be a workaround. diff --git a/future-features.md b/future-features.md index 7ca731f..4e210cb 100644 --- a/future-features.md +++ b/future-features.md @@ -3,11 +3,11 @@ ## UTILS - custom icons -- menu icon for FileItem -- Outside actions or buttons for Dropzone -- header and footer custom props +- menu icon for FileItem ( FileOptions (menu collapsed from click in option icon) ) +- [DONE]: Outside actions or buttons for Dropzone +- [WIP]:header and footer custom props - FileItem: checkbox support -- FileItem: detect when width of image is greater than height or viceversa in order to decide the orientation +- [DONE] FileItem: ( smart prop ) detect when width of image is greater than height or viceversa in order to decide the orientation ## Upload @@ -25,5 +25,6 @@ - Java spring ## dont know if context would be a good idea + - maybe yes for props like custom buttons for file mosaci - custom file thumbnails diff --git a/ideas.md b/ideas.md index 9fe809d..c001002 100644 --- a/ideas.md +++ b/ideas.md @@ -9,7 +9,7 @@ ## Phrases - Stop pain with developing a complex widget, don't need to create a file upload component from scratch -- If you need to do it from scratch, there is an example [show some basic code] +- If you need to do it from scratch, there is an example [show some basic code] Bellow main page component diff --git a/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx b/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx index f4e7c34..12665f2 100644 --- a/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx +++ b/src/components/demo-components/dropzone-demo/AdvancedDropzoneDemo.jsx @@ -64,10 +64,10 @@ export default function AdvancedDropzoneDemo() { onChange={updateFiles} minHeight="195px" value={extFiles} - maxFiles={3} - maxFileSize={2998000 * 20} + /* maxFiles={3} + maxFileSize={2998000 * 20} */ label="Drag'n drop files here or click to browse" - accept=".png,image/*, video/*" + // accept=".png,image/*, video/*" uploadConfig={{ /* autoUpload: true */ method: "POST", diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx index 5d57524..5b0610b 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicImagePreview.tsx @@ -35,7 +35,7 @@ const DemoFileMosaicImagePreview: React.FC<DemoFileMosaicImagePreviewProps> = ( preview /> ) : ( - <FileInputButton value={value ? [value] : []} onChange={updateFile} /> + <FileInputButton value={value ? [value] : []} onChange={updateFile} accept="image/*"/> )} <FileMosaic {...sampleFileProps} alwaysActive info /> </> diff --git a/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx b/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx index 6470472..93df5d5 100644 --- a/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx +++ b/src/components/demo-components/filemosaic-demo/DemoFileMosaicValidation.jsx @@ -13,14 +13,14 @@ const sampleFilesProps = [ size: 28 * 1024 * 1024, type: "image/png", name: "valid file created from props.png", - valid: true, + valid: false, }, { id: "fileId-3", size: 28 * 1024 * 1024, type: "image/jpeg", name: "non valid file created from props.jpg", - valid: false, + valid: true, }, ]; diff --git a/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.tsx b/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.tsx index 4db3c99..5359ad3 100644 --- a/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.tsx +++ b/src/files-ui/components/file-mosaic/components/FileMosaicUploadLayer/FileMosaicUploadLayer.tsx @@ -7,14 +7,16 @@ import { LocalLabels, UPLOADSTATUS, } from "../../../../core"; -import { - CheckCircle, - Clear, - //CloudDone, - DoDisturb, - //UploadError, -} from "../../../icons"; +import { CheckCircle, Clear, DoDisturb } from "../../../icons"; import { DynamicLoader } from "../../../loader"; +import { + AbortedStatus, + EmptyStatus, + ErrorStatus, + PreparingStatus, + SuccessStatus, + UploadingStatus, +} from "../../../file-status"; const FileMosaicUploadLayer: React.FC<FileMosaicUploadLayerProps> = ( props: FileMosaicUploadLayerProps @@ -48,145 +50,54 @@ const FileMosaicUploadLayer: React.FC<FileMosaicUploadLayerProps> = ( 0 - (statusHistory.length - 1) * 132 + "px"; }; React.useEffect(() => { - if (statusHistory.length > 1) { - elevate(); - } + if (statusHistory.length > 1) elevate(); // eslint-disable-next-line }, [statusHistory.length]); - const PreparingStatus = React.useMemo( - () => () => - ( - <React.Fragment> - <InfiniteLoader onClick={onCancel} size={65} /> - <span>{FileItemStatusLocalizer.preparing as string}</span> - </React.Fragment> - ), - [] - ); - const UploadingStatus = React.useMemo( - () => () => - ( - <React.Fragment> - {progress !== undefined ? ( - <DynamicLoader - size={70} - x={35} - y={35} - radius={32} - percentage={progress} - width={6} - hidePerncentage={progress === undefined || onAbort !== undefined} - onClick={onAbort} - /> - ) : ( - <InfiniteLoader onClick={onAbort} size={70} /> - )} - <span> {FileItemStatusLocalizer.uploading as string}</span> - </React.Fragment> - ), - [progress, onAbort, FileItemStatusLocalizer] - ); - - const SuccessStatus = () => { - return ( - <React.Fragment> - <CheckCircle - color="#4caf50" - size={65} - //style={{ backgroundColor: "rgba(255,255,255,0.8)", borderRadius: "50%", padding: 8 }} - /> - <span> {FileItemStatusLocalizer.success as string}</span> - </React.Fragment> - ); - }; - const ErrorStatus = () => { - return ( - <React.Fragment> - <Clear - color="rgba(255,255,255,0.4)" - style={{ - backgroundColor: "rgba(244, 67, 54, 0.8)", - borderRadius: "50%", - }} - size={65} - /> - <span> {FileItemStatusLocalizer.error as string}</span> - </React.Fragment> - ); - }; - const AbortedStatus = () => { - return ( - <React.Fragment> - <DoDisturb color="#f44336" size={65} /> - <span> {FileItemStatusLocalizer.aborted as string}</span> - </React.Fragment> - ); - }; - const Empty = () => { - return ( - <React.Fragment> - <div style={{ width: "100%", height: "132px" }}> - {/* <span> VACIOOOOO</span> */} - </div> - </React.Fragment> - ); - }; - - const StatusSelector = (status: UPLOADSTATUS | undefined) => { - switch (status) { - /* case "preparing": - return <PreparingStatus />; - case "uploading": - return <UploadingStatus />; */ - case "error": - return <ErrorStatus />; - case "success": - return <SuccessStatus />; - case "aborted": - return <AbortedStatus />; - default: - return <Empty />; - } - }; //default phase return ( <div className={"elevation-layer-container"} ref={elevationContainerRef}> <div className="elevation-list" ref={listContainerStoryRef}> {statusHistory.map((status, index) => { - return ( - <div className="elevation-item" key={index + 1}> - {status === "preparing" ? ( - <React.Fragment> - <InfiniteLoader onClick={onCancel} size={65} /> - <span>{FileItemStatusLocalizer.preparing as string}</span> - </React.Fragment> - ) : status === "uploading" ? ( - <React.Fragment> - {progress !== undefined ? ( - <DynamicLoader - size={70} - x={35} - y={35} - radius={32} - percentage={progress} - width={6} - hidePerncentage={ - progress === undefined || onAbort !== undefined - } - onClick={onAbort} - /> - ) : ( - <InfiniteLoader onClick={onAbort} size={70} /> - )} - <span> {FileItemStatusLocalizer.uploading as string}</span> - </React.Fragment> - ) : ( - StatusSelector(status) - )} - </div> - ); + switch (status) { + case "preparing": + return ( + <div className="elevation-item" key={index + 1}> + <PreparingStatus onCancel={onCancel}/> + </div> + ); + case "uploading": + return ( + <div className="elevation-item" key={index + 1}> + <UploadingStatus onAbort={onAbort} progress={progress}/> + </div> + ); + case "error": + return ( + <div className="elevation-item" key={index + 1}> + <ErrorStatus /> + </div> + ); + case "success": + return ( + <div className="elevation-item" key={index + 1}> + <SuccessStatus /> + </div> + ); + case "aborted": + return ( + <div className="elevation-item" key={index + 1}> + <AbortedStatus /> + </div> + ); + default: + return ( + <div className="elevation-item" key={index + 1}> + <EmptyStatus /> + </div> + ); + } })} </div> </div> diff --git a/src/files-ui/components/file-status/AbortedStatus.tsx b/src/files-ui/components/file-status/AbortedStatus.tsx new file mode 100644 index 0000000..f195e8d --- /dev/null +++ b/src/files-ui/components/file-status/AbortedStatus.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { FileItemLocalizerSelector, LocalLabels } from "../../core"; +import { DoDisturb } from "../icons"; +import { FileStatusProps } from "./FileStatusProps"; +interface AbortedStatusProps extends FileStatusProps {} + +const AbortedStatus: React.FC<AbortedStatusProps> = ( + props: AbortedStatusProps +) => { + const { localization, size } = props; + const FileItemStatusLocalizer: LocalLabels = FileItemLocalizerSelector( + localization + ).status as LocalLabels; + return ( + <React.Fragment> + <DoDisturb color="#f44336" size={size || 65} /> + <span> {FileItemStatusLocalizer.aborted as string}</span> + </React.Fragment> + ); +}; +export default AbortedStatus; diff --git a/src/files-ui/components/file-status/EmptyStatus.tsx b/src/files-ui/components/file-status/EmptyStatus.tsx new file mode 100644 index 0000000..9b872ac --- /dev/null +++ b/src/files-ui/components/file-status/EmptyStatus.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; +interface EmptyStatusProps { + height?: string | number; +} +const EmptyStatus: React.FC<EmptyStatusProps> = (props: EmptyStatusProps) => { + const { height } = props; + const finalHeight: string = !height + ? "132px" + : typeof height === "number" + ? `${height}px` + : height; + return ( + <React.Fragment> + <div style={{ width: "100%", height: finalHeight }}> + {/* <span> EMPTY </span> */} + </div> + </React.Fragment> + ); +}; +export default EmptyStatus; diff --git a/src/files-ui/components/file-status/ErrorStatus.tsx b/src/files-ui/components/file-status/ErrorStatus.tsx new file mode 100644 index 0000000..b17f6bd --- /dev/null +++ b/src/files-ui/components/file-status/ErrorStatus.tsx @@ -0,0 +1,26 @@ +import * as React from "react"; +import { FileItemLocalizerSelector, LocalLabels } from "../../core"; +import { Clear } from "../icons"; +import { FileStatusProps } from "./FileStatusProps"; +interface ErrorStatusProps extends FileStatusProps {} + +const ErrorStatus: React.FC<ErrorStatusProps> = (props: ErrorStatusProps) => { + const { localization, size } = props; + const FileItemStatusLocalizer: LocalLabels = FileItemLocalizerSelector( + localization + ).status as LocalLabels; + return ( + <React.Fragment> + <Clear + color="rgba(255,255,255,0.4)" + style={{ + backgroundColor: "rgba(244, 67, 54, 0.8)", + borderRadius: "50%", + }} + size={size || 65} + /> + <span> {FileItemStatusLocalizer.error as string}</span> + </React.Fragment> + ); +}; +export default ErrorStatus; diff --git a/src/files-ui/components/file-status/FileStatusProps.ts b/src/files-ui/components/file-status/FileStatusProps.ts new file mode 100644 index 0000000..442c799 --- /dev/null +++ b/src/files-ui/components/file-status/FileStatusProps.ts @@ -0,0 +1,6 @@ +import { Localization } from "../../core"; + +export type FileStatusProps = { + localization?: Localization; + size?: number; +} \ No newline at end of file diff --git a/src/files-ui/components/file-status/PreparingStatus.tsx b/src/files-ui/components/file-status/PreparingStatus.tsx new file mode 100644 index 0000000..ba5e813 --- /dev/null +++ b/src/files-ui/components/file-status/PreparingStatus.tsx @@ -0,0 +1,26 @@ +import * as React from "react"; +import { FileItemLocalizerSelector, LocalLabels } from "../../core"; +import InfiniteLoader from "../loader/InfiniteLoader/InfiniteLoader"; +import { FileStatusProps } from "./FileStatusProps"; + +export type PreparingStatusProps = { + [P in keyof FileStatusProps]: FileStatusProps[P]; +} & { + onCancel?: Function; +}; + +const PreparingStatus: React.FC<PreparingStatusProps> = ( + props: PreparingStatusProps +) => { + const { onCancel, localization, size } = props; + const FileItemStatusLocalizer: LocalLabels = FileItemLocalizerSelector( + localization + ).status as LocalLabels; + return ( + <React.Fragment> + <InfiniteLoader onClick={onCancel} size={size || 65} /> + <span>{FileItemStatusLocalizer.preparing as string}</span> + </React.Fragment> + ); +}; +export default PreparingStatus; diff --git a/src/files-ui/components/file-status/SuccessStatus.tsx b/src/files-ui/components/file-status/SuccessStatus.tsx new file mode 100644 index 0000000..76eaf5a --- /dev/null +++ b/src/files-ui/components/file-status/SuccessStatus.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; +import { FileItemLocalizerSelector, LocalLabels } from "../../core"; +import { CheckCircle } from "../icons"; +import { FileStatusProps } from "./FileStatusProps"; +interface SuccessStatusProps extends FileStatusProps {} +const SuccessStatus: React.FC<SuccessStatusProps> = ( + props: SuccessStatusProps +) => { + const { localization, size } = props; + const FileItemStatusLocalizer: LocalLabels = FileItemLocalizerSelector( + localization + ).status as LocalLabels; + return ( + <React.Fragment> + <CheckCircle + color="#4caf50" + size={size || 65} + //style={{ backgroundColor: "rgba(255,255,255,0.8)", borderRadius: "50%", padding: 8 }} + /> + <span> {FileItemStatusLocalizer.success as string}</span> + </React.Fragment> + ); +}; +export default SuccessStatus; diff --git a/src/files-ui/components/file-status/UploadingStatus.tsx b/src/files-ui/components/file-status/UploadingStatus.tsx new file mode 100644 index 0000000..98f20d0 --- /dev/null +++ b/src/files-ui/components/file-status/UploadingStatus.tsx @@ -0,0 +1,41 @@ +import * as React from "react"; +import { FileItemLocalizerSelector, LocalLabels } from "../../core"; +import { DynamicLoader } from "../loader"; +import InfiniteLoader from "../loader/InfiniteLoader/InfiniteLoader"; +import { FileStatusProps } from "./FileStatusProps"; + +export type UploadingStatusProps = { + [P in keyof FileStatusProps]: FileStatusProps[P]; +} & { + onAbort?: Function; + progress?: number; +}; + +const UploadingStatus: React.FC<UploadingStatusProps> = ( + props: UploadingStatusProps +) => { + const { localization, size, onAbort, progress } = props; + const FileItemStatusLocalizer: LocalLabels = FileItemLocalizerSelector( + localization + ).status as LocalLabels; + return ( + <React.Fragment> + {progress !== undefined ? ( + <DynamicLoader + size={70} + x={35} + y={35} + radius={32} + percentage={progress} + width={6} + hidePerncentage={progress === undefined || onAbort !== undefined} + onClick={onAbort} + /> + ) : ( + <InfiniteLoader onClick={onAbort} size={size || 70} /> + )} + <span> {FileItemStatusLocalizer.uploading as string}</span> + </React.Fragment> + ); +}; +export default UploadingStatus; diff --git a/src/files-ui/components/file-status/index.ts b/src/files-ui/components/file-status/index.ts new file mode 100644 index 0000000..8cf5f46 --- /dev/null +++ b/src/files-ui/components/file-status/index.ts @@ -0,0 +1,20 @@ +export { default as AbortedStatus } from "./AbortedStatus"; +export * from "./AbortedStatus"; + +export { default as EmptyStatus } from "./EmptyStatus"; +export * from "./EmptyStatus"; + +export { default as ErrorStatus } from "./ErrorStatus"; +export * from "./ErrorStatus"; + +export { default as PreparingStatus } from "./PreparingStatus"; +export * from "./PreparingStatus"; + +export { default as SuccessStatus } from "./SuccessStatus"; +export * from "./SuccessStatus"; + +export { default as UploadingStatus } from "./UploadingStatus"; +export * from "./UploadingStatus"; + + +export type { FileStatusProps } from "./FileStatusProps"; \ No newline at end of file -- GitLab