83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
import React, { useRef } from 'react';
|
|
|
|
interface FileUploadProps {
|
|
onFiles: (files: File[]) => void;
|
|
accept?: string;
|
|
multiple?: boolean;
|
|
maxSizeMB?: number;
|
|
}
|
|
|
|
export default function FileUpload({
|
|
onFiles,
|
|
accept,
|
|
multiple = false,
|
|
maxSizeMB
|
|
}: FileUploadProps) {
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const handleFileSelection = (files: FileList | null) => {
|
|
if (!files) return;
|
|
|
|
const fileArray = Array.from(files);
|
|
const filteredFiles = fileArray.filter(file => {
|
|
if (maxSizeMB && file.size > maxSizeMB * 1024 * 1024) {
|
|
console.warn(`File ${file.name} exceeds size limit of ${maxSizeMB}MB`);
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
onFiles(filteredFiles);
|
|
};
|
|
|
|
const handleDrop = (e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
handleFileSelection(e.dataTransfer.files);
|
|
};
|
|
|
|
const handleDragOver = (e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleClick = () => {
|
|
inputRef.current?.click();
|
|
};
|
|
|
|
return (
|
|
<div
|
|
onClick={handleClick}
|
|
onDragOver={handleDragOver}
|
|
onDrop={handleDrop}
|
|
className="relative group cursor-pointer border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-lg p-8 text-center transition-colors hover:border-blue-500 dark:hover:border-blue-400 hover:bg-slate-50 dark:hover:bg-slate-800/50"
|
|
>
|
|
<input
|
|
type="file"
|
|
ref={inputRef}
|
|
onChange={(e) => handleFileSelection(e.target.files)}
|
|
accept={accept}
|
|
multiple={multiple}
|
|
className="hidden"
|
|
/>
|
|
|
|
<div className="flex flex-col items-center justify-center gap-2">
|
|
<svg
|
|
className="w-10 h-10 text-slate-400 group-hover:text-blue-500 transition-colors"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13L15.01 13m-1.01 0L14 13m1.01 0L15 13" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
<p className="text-sm text-slate-600 dark:text-slate-400">
|
|
<span className="font-semibold text-blue-600 dark:text-blue-400">Drop files here</span> or click to upload
|
|
</p>
|
|
{maxSizeMB && (
|
|
<p className="text-xs text-slate-400">Max size: {maxSizeMB}MB</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |