import React from 'react'; import { MultipleFileUpload, MultipleFileUploadMain, MultipleFileUploadStatus, MultipleFileUploadStatusItem, Checkbox, HelperText, HelperTextItem, 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 [fileUploadShouldFail, setFileUploadShouldFail] = 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'); // 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); }; /** Forces uploaded files to become corrupted if "Demonstrate error reporting by forcing uploads to fail" is selected in the example, * only used in this example for demonstration purposes */ const updateCurrentFiles = (files: File[]) => { if (fileUploadShouldFail) { const corruptedFiles = files.map((file) => ({ ...file, lastModified: 'foo' as unknown as number })); setCurrentFiles((prevFiles) => [...prevFiles, ...corruptedFiles]); } else { setCurrentFiles((prevFiles) => [...prevFiles, ...files]); } }; // 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(() => updateCurrentFiles(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' } ]); }; // add helper text to a status item showing any error encountered during the file reading process const createHelperText = (file: File) => { const fileResult = readFileData.find((readFile) => readFile.fileName === file.name); if (fileResult?.loadError) { return ( {fileResult.loadError.toString()} ); } }; 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} progressHelperText={createHelperText(file)} /> ))} )} setIsHorizontal(!isHorizontal)} /> setFileUploadShouldFail(!fileUploadShouldFail)} /> ); };