From 3b69286a8e1d71d2e3aeefd51c8a09f988da4808 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Tue, 4 Nov 2025 15:47:01 +0700 Subject: [PATCH] chore(FE-188): add DropFileInput component --- src/components/input/DropFileInput.tsx | 193 +++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/components/input/DropFileInput.tsx diff --git a/src/components/input/DropFileInput.tsx b/src/components/input/DropFileInput.tsx new file mode 100644 index 00000000..4e3f11bd --- /dev/null +++ b/src/components/input/DropFileInput.tsx @@ -0,0 +1,193 @@ +import { useEffect } from 'react'; +import { useDropzone, type Accept } from 'react-dropzone'; + +import { Icon } from '@iconify/react'; +import Button from '@/components/Button'; + +import { cn } from '@/lib/helper'; + +interface DropFileInputProps { + name: string; + label?: string; + bottomLabel?: string; + caption?: string; + values?: File[]; + accept?: Accept; + required?: boolean; + maxFiles?: number; // defaults to 1 + maxSize?: number; // defaults to 2097152 (2 MB) + isError?: boolean; + errorMessage?: string; + disabled?: boolean; + onChange?: (files: File[]) => void; + onDelete?: (index: number) => void; + className?: { + wrapper?: string; + inputContainer?: string; + label?: string; + inputWrapper?: string; + caption?: string; + bottomLabel?: string; + errorMessage?: string; + fileItemContainer?: string; + }; +} + +const DropFileInput: React.FC = ({ + name, + label, + bottomLabel, + caption = 'Seret atau Pilih Dokumen', + values, + accept, + required, + maxFiles = Infinity, + maxSize, + isError, + errorMessage, + disabled, + onChange, + onDelete, + className, +}) => { + const isDisabled = + Boolean(values && maxFiles && values.length >= maxFiles) || disabled; + + const { + acceptedFiles, + getRootProps, + getInputProps, + isFocused, + isDragAccept, + isDragReject, + } = useDropzone({ + maxSize, + maxFiles, + accept: accept, + disabled: isDisabled, + }); + + useEffect(() => { + if (values && maxFiles && values.length <= maxFiles) { + onChange?.([...values, ...acceptedFiles]); + } + }, [acceptedFiles]); + + return ( +
+
+ {label && ( + + )} + +
+ + {caption && ( +

+ {caption} +

+ )} +
+ + {!isError && bottomLabel && ( +

+ {bottomLabel} +

+ )} + {isError && ( +

+ {errorMessage} +

+ )} +
+ + {values && values.length > 0 && ( +
+ {values.map((file, idx) => ( +
+
+ +
+ +
+

{file.name}

+
+ + +
+ ))} +
+ )} +
+ ); +}; + +export default DropFileInput;