@rikas/mui-activestorage-upload
Version:
React MUI package for direct upload with activestorage
73 lines (72 loc) • 3.95 kB
JavaScript
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 })) })))] }));
};