import React, { useEffect, useMemo } from 'react'; import { useDropzone } from 'react-dropzone'; import { resolveWidgetFieldValue } from '@/_helpers/utils'; import toast from 'react-hot-toast'; export const FilePicker = ({ width, height, component, currentState, onComponentOptionChanged, onEvent, darkMode }) => { //* properties definitions const enableDropzone = component.definition.properties.enableDropzone?.value ?? true; const enablePicker = component.definition.properties?.enablePicker?.value ?? true; const maxFileCount = component.definition.properties.maxFileCount?.value ?? 2; const enableMultiple = component.definition.properties.enableMultiple?.value ?? false; const fileType = component.definition.properties.fileType?.value ?? 'image/*'; const maxSize = component.definition.properties.maxSize?.value ?? 1048576; const minSize = component.definition.properties.minSize?.value ?? 0; const parsedEnableDropzone = typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone, currentState) : true; const parsedEnablePicker = typeof enablePicker !== 'boolean' ? resolveWidgetFieldValue(enablePicker, currentState) : true; const parsedMaxFileCount = typeof maxFileCount !== 'number' ? resolveWidgetFieldValue(maxFileCount, currentState) : maxFileCount; const parsedEnableMultiple = typeof enableMultiple !== 'boolean' ? resolveWidgetFieldValue(enableMultiple, currentState) : enableMultiple; const parsedFileType = resolveWidgetFieldValue(fileType, currentState); const parsedMinSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(minSize, currentState) : minSize; const parsedMaxSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(maxSize, currentState) : maxSize; //* styles definitions const widgetVisibility = component.definition.styles?.visibility?.value ?? true; const disabledState = component.definition.styles?.disabledState?.value ?? false; const parsedDisabledState = typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState; const parsedWidgetVisibility = typeof widgetVisibility !== 'boolean' ? resolveWidgetFieldValue(widgetVisibility, currentState) : widgetVisibility; const bgThemeColor = darkMode ? '#232E3C' : '#fff'; const baseStyle = { flex: 1, flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '20px', borderWidth: 1.5, borderRadius: 2, borderColor: '#42536A', borderStyle: 'dashed', color: '#bdbdbd', outline: 'none', transition: 'border .24s ease-in-out', display: parsedWidgetVisibility ? 'flex' : 'none', height, backgroundColor: !parsedDisabledState && bgThemeColor, }; const activeStyle = { borderColor: '#2196f3', }; const acceptStyle = { borderColor: '#00e676', }; const rejectStyle = { borderColor: '#ff1744', }; const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles, fileRejections } = useDropzone({ accept: parsedFileType, noClick: !parsedEnablePicker, noDrag: !parsedEnableDropzone, noKeyboard: true, maxFiles: parsedMaxFileCount, minSize: parsedMinSize, maxSize: parsedMaxSize, multiple: parsedEnableMultiple, disabled: parsedDisabledState, }); const style = useMemo( () => ({ ...baseStyle, ...(isDragActive && parsedEnableDropzone ? activeStyle : {}), ...(isDragAccept && parsedEnableDropzone ? acceptStyle : {}), ...(isDragReject && parsedEnableDropzone ? rejectStyle : {}), }), // eslint-disable-next-line react-hooks/exhaustive-deps [baseStyle, isDragActive, isDragAccept, acceptStyle, isDragReject] ); const [accepted, setAccepted] = React.useState(false); const [showSelectdFiles, setShowSelectedFiles] = React.useState(false); const [selectedFiles, setSelectedFiles] = React.useState([]); /** * *getFileData() * @param {*} file * @param {*} method: readAsDataURL, readAsText */ const getFileData = (file = {}, method = 'readAsText') => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (result) => { resolve([result, reader]); }; reader[method](file); reader.onerror = (error) => { reject(error); if (error.name == 'NotReadableError') { toast.error(error.message); } }; }).then((result) => { if (method === 'readAsDataURL') { return result[0].srcElement.result.split(',')[1]; } return result[0].srcElement.result; }); }; const fileReader = async (file) => { // * readAsText const readFileAsText = await getFileData(file); // * readAsDataURL const readFileAsDataURL = await getFileData(file, 'readAsDataURL'); return { name: file.name, type: file.type, content: readFileAsText, dataURL: readFileAsDataURL, }; }; useEffect(() => { if (acceptedFiles.length === 0) { onComponentOptionChanged(component, 'file', []); } if (acceptedFiles.length !== 0) { const fileData = parsedEnableMultiple ? [...selectedFiles] : []; acceptedFiles.map((acceptedFile) => { const acceptedFileData = fileReader(acceptedFile); acceptedFileData.then((data) => { fileData.push(data); }); }); setSelectedFiles(fileData); onComponentOptionChanged(component, 'file', fileData).then(() => onEvent('onFileSelected', { component }).then(() => { setAccepted(true); return new Promise(function (resolve, reject) { setTimeout(() => { setShowSelectedFiles(true); setAccepted(false); resolve(); }, 600); }); }) ); } if (fileRejections.length > 0) { fileRejections.map((rejectedFile) => toast.error(rejectedFile.errors[0].message)); } return () => { setAccepted(false); setShowSelectedFiles(false); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [acceptedFiles.length, fileRejections.length]); const clearSelectedFiles = (index) => { setSelectedFiles((prevState) => { const copy = JSON.parse(JSON.stringify(prevState)); copy.splice(index, 1); return copy; }); }; useEffect(() => { if (selectedFiles.length === 0) { setShowSelectedFiles(false); } onComponentOptionChanged(component, 'file', selectedFiles); // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedFiles]); return (
{showSelectdFiles ? ( {selectedFiles.map((acceptedFile, index) => ( <>
0} feedback={acceptedFile.name} cls="text-secondary d-flex justify-content-start file-list" />
))}
) : ( //* Dropzone
)}
); }; FilePicker.Signifiers = ({ signifier, feedback, cls }) => { if (signifier) { return
{feedback === null ?
:

{feedback}

}
; } return null; }; FilePicker.AcceptedFiles = ({ children, width, height, showFilezone, bgThemeColor }) => { const styles = { borderWidth: 1.5, borderRadius: 2, borderColor: '#42536A', borderStyle: 'dashed', color: '#bdbdbd', outline: 'none', padding: '5px', overflowX: 'hidden', overflowY: 'auto', scrollbarWidth: 'none', width, height, backgroundColor: bgThemeColor, }; return ( ); };