1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
6 import classnames from 'classnames';
7 import { CustomStyleRulesCallback } from 'common/custom-theme';
17 } from '@mui/material';
18 import { WithStyles } from '@mui/styles';
19 import withStyles from '@mui/styles/withStyles';
20 import Dropzone from 'react-dropzone';
21 import { CloudUploadIcon, RemoveIcon } from "../icon/icon";
22 import { formatFileSize, formatProgress, formatUploadSpeed } from "common/formatters";
23 import { UploadFile } from 'store/file-uploader/file-uploader-actions';
25 type CssRules = "root" | "dropzone" | "dropzoneWrapper" | "container" | "uploadIcon"
26 | "dropzoneBorder" | "dropzoneBorderLeft" | "dropzoneBorderRight" | "dropzoneBorderTop" | "dropzoneBorderBottom"
27 | "dropzoneBorderHorzActive" | "dropzoneBorderVertActive" | "deleteButton" | "deleteButtonDisabled" | "deleteIcon";
29 const styles: CustomStyleRulesCallback<CssRules> = theme => ({
41 border: "1px solid rgba(0, 0, 0, 0.42)",
42 boxSizing: 'border-box',
47 transition: "transform 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms",
48 pointerEvents: "none",
49 backgroundColor: "#6a1b9a"
56 transform: "scaleY(0)",
58 dropzoneBorderRight: {
63 transform: "scaleY(0)",
70 transform: "scaleX(0)",
72 dropzoneBorderBottom: {
77 transform: "scaleX(0)",
79 dropzoneBorderHorzActive: {
80 transform: "scaleY(1)"
82 dropzoneBorderVertActive: {
83 transform: "scaleX(1)"
89 verticalAlign: "middle"
94 deleteButtonDisabled: {
102 interface FileUploadPropsData {
105 onDrop: (files: File[]) => void;
106 onDelete: (file: UploadFile) => void;
109 interface FileUploadState {
113 export type FileUploadProps = FileUploadPropsData & WithStyles<CssRules>;
115 export const FileUpload = withStyles(styles)(
116 class extends React.Component<FileUploadProps, FileUploadState> {
117 constructor(props: FileUploadProps) {
123 onDelete = (event: React.MouseEvent<HTMLButtonElement>, file: UploadFile): void => {
124 const { onDelete, disabled } = this.props;
126 event.stopPropagation();
132 let interval = setInterval(() => {
133 const key = Object.keys((window as any).cancelTokens).find(key => key.indexOf(file.file.name) > -1);
136 clearInterval(interval);
137 (window as any).cancelTokens[key]();
138 delete (window as any).cancelTokens[key];
144 const { classes, onDrop, disabled, files } = this.props;
146 <div className={"file-upload-dropzone " + classes.dropzoneWrapper}>
147 <div className={classnames(classes.dropzoneBorder, classes.dropzoneBorderLeft, { [classes.dropzoneBorderHorzActive]: this.state.focused })} />
148 <div className={classnames(classes.dropzoneBorder, classes.dropzoneBorderRight, { [classes.dropzoneBorderHorzActive]: this.state.focused })} />
149 <div className={classnames(classes.dropzoneBorder, classes.dropzoneBorderTop, { [classes.dropzoneBorderVertActive]: this.state.focused })} />
150 <div className={classnames(classes.dropzoneBorder, classes.dropzoneBorderBottom, { [classes.dropzoneBorderVertActive]: this.state.focused })} />
151 <Dropzone className={classes.dropzone}
152 onDrop={files => onDrop(files)}
154 const el = document.getElementsByClassName("file-upload-dropzone")[0];
155 const inputs = el.getElementsByTagName("input");
156 if (inputs.length > 0) {
160 data-cy="drag-and-drop"
174 {files.length === 0 &&
175 <Grid container justifyContent="center" alignItems="center" className={classes.container}>
176 <Grid item component={"span"}>
177 <Typography variant='subtitle1'>
178 <CloudUploadIcon className={classes.uploadIcon} /> Drag and drop data or click to browse
183 <Table style={{ width: "100%" }}>
186 <TableCell>File name</TableCell>
187 <TableCell>File size</TableCell>
188 <TableCell>Upload speed</TableCell>
189 <TableCell>Upload progress</TableCell>
190 <TableCell>Delete</TableCell>
195 <TableRow key={f.id}>
196 <TableCell>{f.file.name}</TableCell>
197 <TableCell>{formatFileSize(f.file.size)}</TableCell>
198 <TableCell>{formatUploadSpeed(f.prevLoaded, f.loaded, f.prevTime, f.currentTime)}</TableCell>
199 <TableCell>{formatProgress(f.loaded, f.total)}</TableCell>
203 onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => this.onDelete(event, f)}
204 className={disabled ? classnames(classes.deleteButtonDisabled, classes.deleteIcon) : classnames(classes.deleteButton, classes.deleteIcon)}