Files
astuto/app/javascript/components/common/Dropzone.tsx

82 lines
2.2 KiB
TypeScript
Raw Normal View History

2025-01-22 09:55:36 +01:00
import React, {useEffect, useState} from 'react';
import {useDropzone} from 'react-dropzone';
import I18n from 'i18n-js';
import { SmallMutedText } from './CustomTexts';
interface Props {
files: any[];
setFiles: React.Dispatch<React.SetStateAction<any[]>>;
onDrop?: any;
maxSizeKB?: number;
maxFiles?: number;
2025-01-22 14:04:08 +01:00
accept?: string[];
2025-01-22 09:55:36 +01:00
}
const Dropzone = ({
files,
setFiles,
onDrop,
maxSizeKB = 256,
maxFiles = 1,
2025-01-22 14:04:08 +01:00
accept = ['image/png', 'image/jpeg', 'image/jpg', 'image/x-icon', 'image/icon', 'image/svg+xml', 'image/svg', 'image/webp'],
2025-01-22 09:55:36 +01:00
}: Props) => {
2025-01-22 14:04:08 +01:00
const acceptDict = accept.reduce((acc, type) => {
acc[type] = [];
return acc;
}, {});
2025-01-22 09:55:36 +01:00
const {
getRootProps,
getInputProps,
isDragAccept,
isDragReject
} = useDropzone({
2025-01-22 14:04:08 +01:00
accept: acceptDict,
2025-01-22 09:55:36 +01:00
maxSize: maxSizeKB * 1024,
maxFiles: maxFiles,
onDrop: (acceptedFiles, fileRejections) => {
if (fileRejections.length > 0) {
alert(I18n.t('common.errors.invalid_file', { errors: fileRejections.map(rejection => rejection.errors[0].message).join(', ') }));
}
setFiles(acceptedFiles.map(file => Object.assign(file, {
preview: URL.createObjectURL(file)
})));
if (onDrop) {
onDrop(acceptedFiles);
}
},
});
const thumbnails = files.map(file => (
<div className="thumbnail" key={file.name}>
<div className="thumbnailInner">
<img
src={file.preview}
className="thumbnailImage"
// Revoke data uri after image is loaded
onLoad={() => { URL.revokeObjectURL(file.preview) }}
/>
</div>
</div>
));
useEffect(() => {
// Make sure to revoke the data uris to avoid memory leaks, will run on unmount
return () => files.forEach(file => URL.revokeObjectURL(file.preview));
}, [files]);
return (
<section>
<div {...getRootProps({className: 'dropzone' + (isDragAccept ? ' dropzone-accept' : isDragReject ? ' dropzone-reject' : '')})}>
<input {...getInputProps()} />
<SmallMutedText>{I18n.t('common.drag_and_drop', { maxCount: maxFiles, maxSize: maxSizeKB })}</SmallMutedText>
</div>
<aside className="thumbnailsContainer">
{thumbnails}
</aside>
</section>
);
}
export default Dropzone;