import React from 'react'; import { FileRejection } from 'react-dropzone'; import { MultipleFileUpload, MultipleFileUploadMain, MultipleFileUploadStatus, MultipleFileUploadStatusItem, Modal, Checkbox, DropEvent } from '@breakaway/preact-core'; import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon'; interface readFile { fileName: string; data?: string; loadResult?: 'danger' | 'success'; loadError?: DOMException; } export const MultipleFileUploadBasic: React.FunctionComponent = () => { const [isHorizontal, setIsHorizontal] = React.useState(false); const [currentFiles, setCurrentFiles] = React.useState([]); const [readFileData, setReadFileData] = React.useState([]); const [showStatus, setShowStatus] = React.useState(false); const [statusIcon, setStatusIcon] = React.useState('inProgress'); const [modalText, setModalText] = React.useState(''); // only show the status component once a file has been uploaded, but keep the status list component itself even if all files are removed if (!showStatus && currentFiles.length > 0) { setShowStatus(true); } // determine the icon that should be shown for the overall status list React.useEffect(() => { if (readFileData.length < currentFiles.length) { setStatusIcon('inProgress'); } else if (readFileData.every((file) => file.loadResult === 'success')) { setStatusIcon('success'); } else { setStatusIcon('danger'); } }, [readFileData, currentFiles]); // remove files from both state arrays based on their name const removeFiles = (namesOfFilesToRemove: string[]) => { const newCurrentFiles = currentFiles.filter( (currentFile) => !namesOfFilesToRemove.some((fileName) => fileName === currentFile.name) ); setCurrentFiles(newCurrentFiles); const newReadFiles = readFileData.filter( (readFile) => !namesOfFilesToRemove.some((fileName) => fileName === readFile.fileName) ); setReadFileData(newReadFiles); }; // callback that will be called by the react dropzone with the newly dropped file objects const handleFileDrop = (_event: DropEvent, droppedFiles: File[]) => { // identify what, if any, files are re-uploads of already uploaded files const currentFileNames = currentFiles.map((file) => file.name); const reUploads = droppedFiles.filter((droppedFile) => currentFileNames.includes(droppedFile.name)); /** this promise chain is needed because if the file removal is done at the same time as the file adding react * won't realize that the status items for the re-uploaded files needs to be re-rendered */ Promise.resolve() .then(() => removeFiles(reUploads.map((file) => file.name))) .then(() => setCurrentFiles((prevFiles) => [...prevFiles, ...droppedFiles])); }; // callback called by the status item when a file is successfully read with the built-in file reader const handleReadSuccess = (data: string, file: File) => { setReadFileData((prevReadFiles) => [...prevReadFiles, { data, fileName: file.name, loadResult: 'success' }]); }; // callback called by the status item when a file encounters an error while being read with the built-in file reader const handleReadFail = (error: DOMException, file: File) => { setReadFileData((prevReadFiles) => [ ...prevReadFiles, { loadError: error, fileName: file.name, loadResult: 'danger' } ]); }; // dropzone prop that communicates to the user that files they've attempted to upload are not an appropriate type const handleDropRejected = (fileRejections: FileRejection[]) => { if (fileRejections.length === 1) { setModalText(`${fileRejections[0].file.name} is not an accepted file type`); } else { const rejectedMessages = fileRejections.reduce( (acc, fileRejection) => (acc += `${fileRejection.file.name}, `), '' ); setModalText(`${rejectedMessages}are not accepted file types`); } }; const successfullyReadFileCount = readFileData.filter((fileData) => fileData.loadResult === 'success').length; return ( <> } titleText="Drag and drop files here" titleTextSeparator="or" infoText="Accepted file types: JPEG, Doc, PDF, PNG" /> {showStatus && ( {currentFiles.map((file) => ( removeFiles([file.name])} onReadSuccess={handleReadSuccess} onReadFail={handleReadFail} /> ))} )} setModalText('')} > {modalText} setIsHorizontal(!isHorizontal)} /> ); };