From 32cba38899fcfb10b963b73f911433b3b8c832f7 Mon Sep 17 00:00:00 2001 From: 4nalog <88684372+4nalog@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:09:37 +0530 Subject: [PATCH] feat: add draggable file upload component (#690) * feat: add draggable file upload component Signed-off-by: 4nalog <4nalog@protonmail.com> * chore: add support for listening to file changes Signed-off-by: 4nalog <4nalog@protonmail.com> * refactor: extensible file upload input Signed-off-by: 4nalog <4nalog@protonmail.com> * refactor: rename component Signed-off-by: 4nalog <4nalog@protonmail.com> * chore: fix file upload props Signed-off-by: 4nalog <4nalog@protonmail.com> * chore: add comments for file upload component options Signed-off-by: 4nalog <4nalog@protonmail.com> --------- Signed-off-by: 4nalog <4nalog@protonmail.com> --- packages/console/package.json | 4 +- .../components/common/FileUpload/FileItem.tsx | 50 +++++++++++ .../common/FileUpload/FileUpload.tsx | 85 +++++++++++++++++++ yarn.lock | 47 ++++++++++ 4 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 packages/console/src/components/common/FileUpload/FileItem.tsx create mode 100644 packages/console/src/components/common/FileUpload/FileUpload.tsx diff --git a/packages/console/package.json b/packages/console/package.json index 741447e1c..95e572196 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@flyteorg/console", - "version": "0.0.10", + "version": "0.0.11", "description": "Flyteconsole main app module", "main": "./dist/index.js", "module": "./lib/index.js", @@ -93,6 +93,7 @@ "prop-types": "15.6.0", "query-string": "^6.5.0", "react-chartjs-2": "^4.3.1", + "react-dropzone": "^14.2.3", "react-flow-renderer": "10.3.8", "react-ga4": "^1.4.1", "react-intersection-observer": "^8.25.1", @@ -120,6 +121,7 @@ "@types/pure-render-decorator": "^0.2.27", "@types/react": "^16.9.34", "@types/react-dom": "^16.9.7", + "@types/react-dropzone": "^5.1.0", "@types/react-virtualized": "^9.21.4", "@types/serve-static": "^1.7.31", "@types/shallowequal": "^0.2.3" diff --git a/packages/console/src/components/common/FileUpload/FileItem.tsx b/packages/console/src/components/common/FileUpload/FileItem.tsx new file mode 100644 index 000000000..f9b14ce9a --- /dev/null +++ b/packages/console/src/components/common/FileUpload/FileItem.tsx @@ -0,0 +1,50 @@ +import { makeStyles, Theme } from '@material-ui/core'; +import { Clear, Done } from '@material-ui/icons'; +import React from 'react'; + +const useStyles = makeStyles((theme: Theme) => ({ + container: { + background: theme.palette.grey[100], + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + gap: '8px', + alignItems: 'center', + width: '100%', + padding: theme.spacing(1, 0.5), + fontSize: 12, + }, + icon: { + color: theme.palette.success.main, + }, + fileName: { + flex: 1, + textAlign: 'left', + }, +})); + +interface Props { + file: File; + remove: () => void; +} + +function FileItem({ file, remove }: Props) { + const styles = useStyles(); + + return ( +
+ +
{file.name}
+ { + e.preventDefault(); + e.stopPropagation(); + + remove(); + }} + /> +
+ ); +} + +export default FileItem; diff --git a/packages/console/src/components/common/FileUpload/FileUpload.tsx b/packages/console/src/components/common/FileUpload/FileUpload.tsx new file mode 100644 index 000000000..7ab6269e5 --- /dev/null +++ b/packages/console/src/components/common/FileUpload/FileUpload.tsx @@ -0,0 +1,85 @@ +import { makeStyles, Theme } from '@material-ui/core'; +import React from 'react'; +import { DropzoneProps, useDropzone } from 'react-dropzone'; +import FileItem from './FileItem'; + +const useStyles = makeStyles((theme: Theme) => ({ + container: { + margin: 'auto', + maxWidth: '536px', + color: theme.palette.grey[400], + cursor: 'pointer', + }, + uploadContainer: { + width: '100%', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + gap: theme.spacing(0.5), + padding: theme.spacing(4, 3), + border: `0.5px dashed ${theme.palette.divider}`, + borderRadius: '4px', + }, + filesContainer: { + width: '100%', + }, + highlight: { + color: theme.palette.primary.main, + }, +})); + +/** + * For a list of available options, see https://react-dropzone.js.org/ + */ +export interface FileUploadProps extends DropzoneProps { + files: File[]; + setFiles: React.Dispatch>; + helpText?: React.ReactNode; +} + +export function FileUpload({ + files, + setFiles, + helpText, + ...options +}: FileUploadProps) { + const styles = useStyles(); + const { getRootProps, getInputProps } = useDropzone({ + ...options, + onDrop: (acceptedFiles, fileRejections, event) => { + setFiles(acceptedFiles); + options?.onDrop?.(acceptedFiles, fileRejections, event); + }, + }); + + const removeFile = (fileIdx: number) => { + setFiles(files => files.filter((_, idx) => idx !== fileIdx)); + }; + + const ctaText = files.length ? 'Replace file' : 'Upload a file'; + + return ( +
+
+
+ {files.map((file, idx) => ( + removeFile(idx)} + key={file.name} + /> + ))} +
+
+ {ctaText} or drag and drop + here +
+ {helpText} + +
+
+ ); +} + +export default FileUpload; diff --git a/yarn.lock b/yarn.lock index 270d73183..d407696a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2138,6 +2138,7 @@ __metadata: "@types/pure-render-decorator": ^0.2.27 "@types/react": ^16.9.34 "@types/react-dom": ^16.9.7 + "@types/react-dropzone": ^5.1.0 "@types/react-virtualized": ^9.21.4 "@types/serve-static": ^1.7.31 "@types/shallowequal": ^0.2.3 @@ -2168,6 +2169,7 @@ __metadata: prop-types: 15.6.0 query-string: ^6.5.0 react-chartjs-2: ^4.3.1 + react-dropzone: ^14.2.3 react-flow-renderer: 10.3.8 react-ga4: ^1.4.1 react-intersection-observer: ^8.25.1 @@ -5520,6 +5522,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dropzone@npm:^5.1.0": + version: 5.1.0 + resolution: "@types/react-dropzone@npm:5.1.0" + dependencies: + react-dropzone: "*" + checksum: 2b68fcf0153fb00e17088907490b823b6d81a29a02e615f820e347df2546f9318c5f025eeb33d575c648d01a14e55a19e528c32907608b5a7f727ff0e96b3923 + languageName: node + linkType: hard + "@types/react-test-renderer@npm:>=16.9.0": version: 18.0.0 resolution: "@types/react-test-renderer@npm:18.0.0" @@ -7015,6 +7026,13 @@ __metadata: languageName: node linkType: hard +"attr-accept@npm:^2.2.2": + version: 2.2.2 + resolution: "attr-accept@npm:2.2.2" + checksum: 496f7249354ab53e522510c1dc8f67a1887382187adde4dc205507d2f014836a247073b05e9d9ea51e2e9c7f71b0d2aa21730af80efa9af2d68303e5f0565c4d + languageName: node + linkType: hard + "autoprefixer@npm:^9.8.6": version: 9.8.8 resolution: "autoprefixer@npm:9.8.8" @@ -11370,6 +11388,15 @@ __metadata: languageName: node linkType: hard +"file-selector@npm:^0.6.0": + version: 0.6.0 + resolution: "file-selector@npm:0.6.0" + dependencies: + tslib: ^2.4.0 + checksum: 7d051b6e5d793f3c6e2ab287ba5e7c2c6a0971bccc9d56e044c8047ba483e18f60fc0b5771c951dc707c0d15f4f36ccb4f1f1aaf385d21ec8f7700dadf8325ba + languageName: node + linkType: hard + "file-system-cache@npm:^1.0.5": version: 1.1.0 resolution: "file-system-cache@npm:1.1.0" @@ -18516,6 +18543,19 @@ __metadata: languageName: node linkType: hard +"react-dropzone@npm:*, react-dropzone@npm:^14.2.3": + version: 14.2.3 + resolution: "react-dropzone@npm:14.2.3" + dependencies: + attr-accept: ^2.2.2 + file-selector: ^0.6.0 + prop-types: ^15.8.1 + peerDependencies: + react: ">= 16.8 || 18.0.0" + checksum: 174b744d5ca898cf3d84ec1aeb6cef5211c446697e45dc8ece8287a03d291f8d07253206d5a1247ef156fd385d65e7de666d4d5c2986020b8543b8f2434e8b40 + languageName: node + linkType: hard + "react-element-to-jsx-string@npm:^14.3.4": version: 14.3.4 resolution: "react-element-to-jsx-string@npm:14.3.4" @@ -21273,6 +21313,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.4.0": + version: 2.5.0 + resolution: "tslib@npm:2.5.0" + checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0"