|
| 1 | +import React, { useRef, useState } from 'react'; |
| 2 | +import { FaCloudUploadAlt, FaTimes, FaFile } from 'react-icons/fa'; |
| 3 | +interface Props { onUpload: (files: File[]) => void; accept?: string; multiple?: boolean; maxSize?: number; } |
| 4 | +const FileUpload: React.FC<Props> = ({ onUpload, accept, multiple = false, maxSize = 10 * 1024 * 1024 }) => { |
| 5 | + const inputRef = useRef<HTMLInputElement>(null); |
| 6 | + const [dragActive, setDragActive] = useState(false); |
| 7 | + const [files, setFiles] = useState<File[]>([]); |
| 8 | + const handleFiles = (fileList: FileList) => { const valid = Array.from(fileList).filter(f => f.size <= maxSize); setFiles(prev => [...prev, ...valid]); onUpload(valid); }; |
| 9 | + const removeFile = (index: number) => setFiles(f => f.filter((_, i) => i !== index)); |
| 10 | + return ( |
| 11 | + <div> |
| 12 | + <div onClick={() => inputRef.current?.click()} onDragOver={e => { e.preventDefault(); setDragActive(true); }} onDragLeave={() => setDragActive(false)} |
| 13 | + onDrop={e => { e.preventDefault(); setDragActive(false); handleFiles(e.dataTransfer.files); }} |
| 14 | + className={'border-2 border-dashed rounded-2xl p-8 text-center cursor-pointer transition-colors ' + (dragActive ? 'border-blue-500 bg-blue-50' : 'border-gray-300 dark:border-gray-600 hover:border-blue-400')}> |
| 15 | + <FaCloudUploadAlt className='text-4xl text-gray-400 mx-auto mb-3' /> |
| 16 | + <p className='text-sm text-gray-600 dark:text-gray-400'>Drag & drop or click to upload</p> |
| 17 | + <p className='text-xs text-gray-400 mt-1'>Max size: {Math.round(maxSize / 1024 / 1024)}MB</p> |
| 18 | + <input ref={inputRef} type='file' accept={accept} multiple={multiple} onChange={e => e.target.files && handleFiles(e.target.files)} className='hidden' /> |
| 19 | + </div> |
| 20 | + {files.length > 0 && <div className='mt-3 space-y-2'>{files.map((f, i) => ( |
| 21 | + <div key={i} className='flex items-center gap-2 p-2 bg-gray-50 dark:bg-gray-800 rounded-lg text-sm'> |
| 22 | + <FaFile className='text-gray-400' /><span className='flex-1 truncate'>{f.name}</span> |
| 23 | + <span className='text-gray-400 text-xs'>{(f.size / 1024).toFixed(1)}KB</span> |
| 24 | + <button onClick={() => removeFile(i)}><FaTimes className='text-red-400' /></button> |
| 25 | + </div> |
| 26 | + ))}</div>} |
| 27 | + </div> |
| 28 | + ); |
| 29 | +}; |
| 30 | +export default FileUpload; |
0 commit comments