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 (
{feedback}
}