import React, { useCallback, useState } from 'react'; import { Upload, Image, X } from 'lucide-react'; import { cn } from '@/lib/utils'; import { useToast } from '@/hooks/use-toast'; interface DragDropZoneProps { onFilesAdded: (files: File[]) => void; maxFiles?: number; maxSizeMB?: number; allowedFileTypes?: string[]; disabled?: boolean; className?: string; children?: React.ReactNode; } export function DragDropZone({ onFilesAdded, maxFiles = 10, maxSizeMB = 25, allowedFileTypes = ['image/*'], disabled = false, className = '', children, }: DragDropZoneProps) { const [isDragOver, setIsDragOver] = useState(false); const { toast } = useToast(); const validateFiles = useCallback((files: FileList) => { const validFiles: File[] = []; const errors: string[] = []; Array.from(files).forEach((file) => { // Check file type const isValidType = allowedFileTypes.some(type => { if (type === 'image/*') { return file.type.startsWith('image/'); } return file.type === type; }); if (!isValidType) { errors.push(`${file.name}: Invalid file type`); return; } // Check file size if (file.size > maxSizeMB * 1024 * 1024) { errors.push(`${file.name}: File too large (max ${maxSizeMB}MB)`); return; } validFiles.push(file); }); // Check total file count if (validFiles.length > maxFiles) { errors.push(`Too many files. Maximum ${maxFiles} files allowed.`); return { validFiles: validFiles.slice(0, maxFiles), errors }; } return { validFiles, errors }; }, [allowedFileTypes, maxSizeMB, maxFiles]); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (!disabled) { setIsDragOver(true); } }, [disabled]); const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragOver(false); }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragOver(false); if (disabled) return; const files = e.dataTransfer.files; if (files.length === 0) return; const { validFiles, errors } = validateFiles(files); if (errors.length > 0) { toast({ variant: 'destructive', title: 'File Validation Error', description: errors.join(', '), }); } if (validFiles.length > 0) { onFilesAdded(validFiles); } }, [disabled, validateFiles, onFilesAdded, toast]); const handleFileInput = useCallback((e: React.ChangeEvent) => { if (disabled) return; const files = e.target.files; if (!files || files.length === 0) return; const { validFiles, errors } = validateFiles(files); if (errors.length > 0) { toast({ variant: 'destructive', title: 'File Validation Error', description: errors.join(', '), }); } if (validFiles.length > 0) { onFilesAdded(validFiles); } // Reset input e.target.value = ''; }, [disabled, validateFiles, onFilesAdded, toast]); if (children) { return (
{children}
); } return (
{isDragOver ? ( ) : ( )}

{isDragOver ? 'Drop files here' : 'Drag & drop photos here'}

or click to browse files

Max {maxFiles} files, {maxSizeMB}MB each

); }