UNPKG

mui2-file-dropzone

Version:

A Material-UI file-upload dropzone. Written in TypeScript.

297 lines 14.8 kB
import { __awaiter } from "tslib"; import AttachFileIcon from "@mui/icons-material/AttachFile"; import CloudUploadIcon from "@mui/icons-material/CloudUpload"; import { Box } from "@mui/material"; import Snackbar from "@mui/material/Snackbar"; import Typography from "@mui/material/Typography"; import clsx from "clsx"; import PropTypes from "prop-types"; import React, { Fragment, PureComponent } from "react"; import Dropzone, { ErrorCode } from "react-dropzone"; import { convertBytesToMbsOrKbs, isImage, readFile } from "../helpers"; import { withTheme } from "../withTheme"; import PreviewList from "./PreviewList"; import SnackbarContentWrapper from "./SnackbarContentWrapper"; const defaultSnackbarAnchorOrigin = { horizontal: "left", vertical: "bottom", }; const defaultGetPreviewIcon = (fileObject, classes) => { const { data, file } = fileObject || {}; if (isImage(file)) { const src = typeof data === "string" ? data : undefined; return React.createElement("img", { className: classes === null || classes === void 0 ? void 0 : classes.image, role: "presentation", src: src }); } return (React.createElement(AttachFileIcon, { sx: { height: 100, width: "initial", maxWidth: "100%", color: "text.primary", transition: "all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms", boxSizing: "border-box", boxShadow: "rgba(0, 0, 0, 0.12) 0 1px 6px, rgba(0, 0, 0, 0.12) 0 1px 4px", borderRadius: 1, zIndex: 5, opacity: 1, }, className: classes === null || classes === void 0 ? void 0 : classes.image })); }; export const FileObjectShape = PropTypes.shape({ file: PropTypes.object, data: PropTypes.any, }); /** * This components creates a Material-UI Dropzone, with previews and snackbar notifications. */ class DropzoneAreaBase extends PureComponent { constructor() { super(...arguments); this.state = { openSnackBar: false, snackbarMessage: "", snackbarVariant: "success", }; this.handleDropAccepted = (acceptedFiles, evt) => __awaiter(this, void 0, void 0, function* () { const { fileObjects, filesLimit, getFileAddedMessage = DropzoneAreaBase.defaultProps.getFileAddedMessage, getFileLimitExceedMessage = DropzoneAreaBase.defaultProps .getFileLimitExceedMessage, onAdd, onDrop, } = this.props; if (filesLimit && filesLimit > 1 && fileObjects.length + acceptedFiles.length > filesLimit) { this.setState({ openSnackBar: true, snackbarMessage: getFileLimitExceedMessage(filesLimit), snackbarVariant: "error", }, this.notifyAlert); return; } // Notify Drop event if (onDrop) { onDrop(acceptedFiles, evt); } // Retrieve fileObjects data const fileObjs = yield Promise.all(acceptedFiles.map((file) => __awaiter(this, void 0, void 0, function* () { const data = yield readFile(file); return { file, data, }; }))); // Notify added files if (onAdd) { onAdd(fileObjs); } // Display message const message = fileObjs.reduce((msg, fileObj) => msg + getFileAddedMessage(fileObj.file.name), ""); this.setState({ openSnackBar: true, snackbarMessage: message, snackbarVariant: "success", }, this.notifyAlert); }); this.handleDropRejected = (rejectedFiles, evt) => { const { acceptedFiles, filesLimit, fileObjects, getDropRejectMessage = DropzoneAreaBase.defaultProps.getDropRejectMessage, getFileLimitExceedMessage = DropzoneAreaBase.defaultProps .getFileLimitExceedMessage, maxFileSize, onDropRejected, } = this.props; let message = ""; if (filesLimit && fileObjects.length + rejectedFiles.length > filesLimit) { message = getFileLimitExceedMessage(filesLimit); } else { rejectedFiles.forEach((rejectedFile) => { message = getDropRejectMessage(rejectedFile, acceptedFiles !== null && acceptedFiles !== void 0 ? acceptedFiles : {}, maxFileSize); }); } if (onDropRejected) { onDropRejected(rejectedFiles, evt); } this.setState({ openSnackBar: true, snackbarMessage: message, snackbarVariant: "error", }, this.notifyAlert); }; this.handleRemove = (fileIndex) => (event) => { event.stopPropagation(); const { fileObjects, getFileRemovedMessage = DropzoneAreaBase.defaultProps .getFileRemovedMessage, onDelete, } = this.props; // Find removed fileObject const removedFileObj = fileObjects[fileIndex]; // Notify removed file if (onDelete) { onDelete(removedFileObj, fileIndex); } this.setState({ openSnackBar: true, snackbarMessage: getFileRemovedMessage(removedFileObj.file.name), snackbarVariant: "info", }, this.notifyAlert); }; this.handleCloseSnackbar = () => { this.setState({ openSnackBar: false, }); }; this.defaultSx = { root: { "@keyframes progress": { "0%": { backgroundPosition: "0 0", }, "100%": { backgroundPosition: "-70px 0", }, }, position: "relative", width: "100%", minHeight: "250px", backgroundColor: "background.paper", border: "dashed", borderColor: "divider", borderRadius: 1, boxSizing: "border-box", cursor: "pointer", overflow: "hidden", }, active: { animation: "$progress 2s linear infinite !important", backgroundImage: `repeating-linear-gradient(-45deg, ${this.props.theme.palette.background.paper}, ${this.props.theme.palette.background.paper} 25px, ${this.props.theme.palette.divider} 25px, ${this.props.theme.palette.divider} 50px)`, backgroundSize: "150% 100%", border: "solid", borderColor: "primary.light", }, invalid: { backgroundImage: `repeating-linear-gradient(-45deg, ${this.props.theme.palette.error.light}, ${this.props.theme.palette.error.light} 25px, ${this.props.theme.palette.error.dark} 25px, ${this.props.theme.palette.error.dark} 50px)`, borderColor: "error.main", }, textContainer: { textAlign: "center", }, text: { marginBottom: 3, marginTop: 3, }, icon: { width: 51, height: 51, color: "text.primary", }, }; } notifyAlert() { const { onAlert } = this.props; const { openSnackBar, snackbarMessage, snackbarVariant } = this.state; if (openSnackBar && onAlert) { onAlert(snackbarMessage, snackbarVariant); } } render() { const { acceptedFiles, alertSnackbarProps, classes = {}, disableRejectionFeedback, dropzoneClass, dropzoneParagraphClass, dropzoneProps, dropzoneText, fileObjects, filesLimit, getPreviewIcon = DropzoneAreaBase.defaultProps.getPreviewIcon, Icon, inputProps, maxFileSize, previewChipProps, previewGridClasses, previewGridProps, previewText, showAlerts, showFileNames, showFileNamesInPreview, showPreviews, showPreviewsInDropzone, useChipsForPreview, } = this.props; const { openSnackBar, snackbarMessage, snackbarVariant } = this.state; const isMultiple = (filesLimit && filesLimit > 1) || !filesLimit; const previewsVisible = showPreviews && fileObjects.length > 0; const previewsInDropzoneVisible = showPreviewsInDropzone && fileObjects.length > 0; return (React.createElement(Fragment, null, React.createElement(Dropzone, Object.assign({}, dropzoneProps, { accept: acceptedFiles, onDropAccepted: this.handleDropAccepted, onDropRejected: this.handleDropRejected, maxSize: maxFileSize, multiple: isMultiple }), ({ getRootProps, getInputProps, isDragActive, isDragReject }) => { const isActive = isDragActive; const isInvalid = !disableRejectionFeedback && isDragReject; return (React.createElement(Box, Object.assign({ sx: Object.assign(Object.assign(Object.assign({}, this.defaultSx.root), (isActive ? this.defaultSx.active : {})), (isInvalid ? this.defaultSx.invalid : {})) }, getRootProps({ className: clsx(classes.root, dropzoneClass, isActive && classes.active, isInvalid && classes.invalid), })), React.createElement("input", Object.assign({}, getInputProps(inputProps))), React.createElement(Box, { sx: this.defaultSx.textContainer, className: classes.textContainer }, React.createElement(Typography, { variant: "h5", component: "p", sx: this.defaultSx.text, className: clsx(classes.text, dropzoneParagraphClass) }, dropzoneText), Icon ? (React.createElement(Icon, { sx: this.defaultSx.icon, className: classes.icon })) : (React.createElement(CloudUploadIcon, { sx: this.defaultSx.icon, className: classes.icon }))), previewsInDropzoneVisible ? (React.createElement(PreviewList, { fileObjects: fileObjects, handleRemove: this.handleRemove, getPreviewIcon: getPreviewIcon, showFileNames: showFileNames, useChipsForPreview: useChipsForPreview, previewChipProps: previewChipProps, previewGridClasses: previewGridClasses, previewGridProps: previewGridProps })) : null)); }), previewsVisible ? (React.createElement(Fragment, null, React.createElement(Typography, { variant: "subtitle1", component: "span" }, previewText), React.createElement(PreviewList, { fileObjects: fileObjects, handleRemove: this.handleRemove, getPreviewIcon: getPreviewIcon, showFileNames: showFileNamesInPreview, useChipsForPreview: useChipsForPreview, previewChipProps: previewChipProps, previewGridClasses: previewGridClasses, previewGridProps: previewGridProps }))) : null, (typeof showAlerts === "boolean" && showAlerts) || (Array.isArray(showAlerts) && showAlerts.includes(snackbarVariant)) ? (React.createElement(Snackbar, Object.assign({ anchorOrigin: defaultSnackbarAnchorOrigin, autoHideDuration: 6000 }, alertSnackbarProps, { open: openSnackBar, onClose: this.handleCloseSnackbar }), React.createElement(SnackbarContentWrapper, { onClose: this.handleCloseSnackbar, variant: snackbarVariant, message: snackbarMessage }))) : null)); } } DropzoneAreaBase.propTypes = { classes: PropTypes.object, acceptedFiles: PropTypes.object, filesLimit: PropTypes.number, Icon: PropTypes.elementType, fileObjects: PropTypes.arrayOf(FileObjectShape), maxFileSize: PropTypes.number, dropzoneText: PropTypes.string, dropzoneClass: PropTypes.string, dropzoneParagraphClass: PropTypes.string, disableRejectionFeedback: PropTypes.bool, showPreviews: PropTypes.bool, showPreviewsInDropzone: PropTypes.bool, showFileNames: PropTypes.bool, showFileNamesInPreview: PropTypes.bool, useChipsForPreview: PropTypes.bool, previewChipProps: PropTypes.object, previewGridClasses: PropTypes.object, previewGridProps: PropTypes.object, previewText: PropTypes.string, showAlerts: PropTypes.oneOfType([ PropTypes.bool, PropTypes.arrayOf(PropTypes.oneOf(["error", "success", "info", "warning"])), ]), alertSnackbarProps: PropTypes.object, dropzoneProps: PropTypes.object, inputProps: PropTypes.object, getFileLimitExceedMessage: PropTypes.func, getFileAddedMessage: PropTypes.func, getFileRemovedMessage: PropTypes.func, getDropRejectMessage: PropTypes.func, getPreviewIcon: PropTypes.func, onAdd: PropTypes.func, onDelete: PropTypes.func, onDrop: PropTypes.func, onDropRejected: PropTypes.func, onAlert: PropTypes.func, }; DropzoneAreaBase.defaultProps = { acceptedFiles: [], fileObjects: [], dropzoneText: "Drag and drop a file here or click", previewText: "Preview:", disableRejectionFeedback: false, showPreviews: false, // By default previews show up under in the dialog and inside in the standalone showPreviewsInDropzone: true, showFileNames: false, showFileNamesInPreview: false, useChipsForPreview: false, previewChipProps: {}, previewGridClasses: {}, previewGridProps: {}, showAlerts: true, alertSnackbarProps: { anchorOrigin: { horizontal: "left", vertical: "bottom", }, autoHideDuration: 6000, }, getFileLimitExceedMessage: ((filesLimit) => `Maximum allowed number of files exceeded. Only ${filesLimit} allowed`), getFileAddedMessage: ((fileName) => `File ${fileName} successfully added.`), getPreviewIcon: defaultGetPreviewIcon, getFileRemovedMessage: ((fileName) => `File ${fileName} removed.`), getDropRejectMessage: ((rejectedFile, acceptedFiles, maxFileSize) => { let message = `File ${rejectedFile.file.name} was rejected. `; if (rejectedFile.errors.some((error) => error.code === ErrorCode.FileInvalidType)) { message += "File type not supported. Only support: " + JSON.stringify(acceptedFiles) + ". "; } if (rejectedFile.errors.some((error) => error.code === ErrorCode.FileTooLarge) && maxFileSize) { message += "File is too big. Size limit is " + convertBytesToMbsOrKbs(maxFileSize) + ". "; } return message; }), }; // @ts-expect-error const ThemedDropzoneAreaBase = withTheme(DropzoneAreaBase); export default ThemedDropzoneAreaBase; //# sourceMappingURL=DropzoneAreaBase.js.map