UNPKG

@rikas/mui-activestorage-upload

Version:

React MUI package for direct upload with activestorage

73 lines (72 loc) 3.95 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useRef } from 'react'; import { v4 as uuid } from 'uuid'; import { Box, Button, Stack, FormHelperText, Typography } from '@mui/material'; import AttachFileIcon from '@mui/icons-material/AttachFile'; import { FileChip } from './FileChip'; export const DirectFileUpload = ({ label, onUploaded, onFileRemove, uploadUrl, error, invalid, mimeTypes = ['*/*'], multiple, }) => { const [uploadedFiles, setUploadedFiles] = useState([]); const inputRef = useRef(null); // Change the state and add the files selected in the input field. If the file is already in the // state, it will not be added again to avoid duplicates. const handleFilesSelect = (event) => { const blobs = event.target.files; if (!blobs) return; const selectedBlobs = Array.from(blobs); const files = selectedBlobs.map((blob) => { return { blob, id: uuid(), name: blob.name, size: blob.size, type: blob.type, signedId: null, percent: 0, done: false, error: null, }; }); // Remove duplicates const newUploads = files.filter((f) => !uploadedFiles.find((file) => file.name === f.name)); setUploadedFiles([...uploadedFiles, ...newUploads]); // By clearing the input field we allow the user to select the same file again. This can be // useful if the user "removes" the uploaded file and then wants to upload it again. if (inputRef.current) { inputRef.current.value = ''; } }; const onFileProgress = (file) => { const { id, percent, signedId } = file; // Simulate a delay to show the progress bar and avoid very quick flickering if the file is very // small (or the upload too fast). setTimeout(() => { const uploadedFile = uploadedFiles.find((file) => file.id === id); if (!uploadedFile) { return; } uploadedFile.signedId = signedId; uploadedFile.percent = percent; uploadedFile.done = true; setUploadedFiles(uploadedFiles.map((file) => (file.id === id ? uploadedFile : file))); // Notify the parent component that all files have been uploaded if (uploadedFiles.every((file) => file.percent === 100)) { onUploaded(uploadedFiles); } }, 1000); }; const onFileError = ({ id, error }) => { const uploadedFile = uploadedFiles.find((file) => file.id === id); if (!uploadedFile) { return; } uploadedFile.error = error; setUploadedFiles(uploadedFiles.map((file) => (file.id === id ? uploadedFile : file))); }; const removeFile = (file) => { const { signedId } = file; setUploadedFiles(uploadedFiles.filter((file) => file.signedId !== signedId)); onFileRemove(file); }; return (_jsxs(Box, { children: [(multiple || uploadedFiles.length === 0) && (_jsxs(Button, Object.assign({ color: invalid ? 'error' : 'primary', variant: "outlined", component: "label", startIcon: _jsx(AttachFileIcon, {}) }, { children: [label, _jsx("input", { hidden: true, multiple: multiple, accept: mimeTypes.join(', '), type: "file", ref: inputRef, onChange: handleFilesSelect })] }))), _jsx(Stack, Object.assign({ direction: "row", spacing: 1 }, { children: uploadedFiles.map((file) => (_jsx(FileChip, { file: file, externalError: error, uploadUrl: uploadUrl, onRemove: removeFile, onProgress: onFileProgress, onFileError: onFileError }, file.id))) })), invalid && (_jsx(FormHelperText, Object.assign({ error: true, sx: { ml: 2 } }, { children: _jsx(Typography, Object.assign({ variant: "caption" }, { children: error })) })))] })); };