File Upload Medium
A drag-and-drop file upload area with PNG, JPEG, and PDF type validation and a live list of uploaded files.
Drag and drop files here or click to select
FileUploadMedium.jsx
'use client' import { useRef, useState } from 'react' import { CloudUpload, File, X } from 'lucide-react' export default function FileUploadMedium() { const fileInputRef = useRef(null) const [dragOver, setDragOver] = useState(false) const [uploadedFiles, setUploadedFiles] = useState([]) const [error, setError] = useState('') const allowedFileTypes = ['image/png', 'image/jpeg', 'application/pdf'] const handleClick = (event) => { event.stopPropagation() if (fileInputRef.current) { fileInputRef.current.click() } } const validateFileType = (files) => { return files.every((file) => allowedFileTypes.includes(file.type)) } const handleFileChange = (event) => { const files = Array.from(event.target.files) if (files.length > 0) { if (validateFileType(files)) { setUploadedFiles((prevFiles) => [...prevFiles, ...files]) } else { showError('Invalid file type. Please upload only PNG, JPEG, or PDF files.') } } } const handleDrop = (event) => { event.preventDefault() setDragOver(false) const files = Array.from(event.dataTransfer.files) if (files.length > 0) { if (validateFileType(files)) { setUploadedFiles((prevFiles) => [...prevFiles, ...files]) } else { showError('Invalid file type. Please upload only PNG, JPEG, or PDF files.') } } } const handleDragOver = (event) => { event.preventDefault() setDragOver(true) } const handleDragLeave = () => { setDragOver(false) } const showError = (message) => { setError(message) setTimeout(() => { setError('') }, 3000) } const removeFile = (index) => { setUploadedFiles((prevFiles) => prevFiles.filter((_, i) => i !== index)) } const formatFileSize = (size) => { if (size < 1024) return `${size} bytes` else if (size < 1048576) return `${(size / 1024).toFixed(2)} KB` else return `${(size / 1048576).toFixed(2)} MB` } return ( <div className="p-4 pb-8"> <div className="flex flex-col items-start relative"> <div onClick={handleClick} onDrop={handleDrop} onDragOver={handleDragOver} onDragLeave={handleDragLeave} className={`flex flex-col items-center border-2 border-dashed dark:border-[#4c4c4c] border-gray-300 hover:border-gray-300 dark:hover:border-[#4c4c4c] p-6 rounded-lg max-w-[350px] cursor-pointer hover:bg-gray-50 dark:hover:bg-[#121212] transition duration-150 ease-in-out relative text-sm ${ dragOver ? 'bg-gray-100 dark:bg-[#121212] border-gray-300' : '' }`}> <CloudUpload className="text-gray-400 mb-2 text-xs dark:text-[#f1f1f1]" style={{ fontSize: 40 }} /> <span className="text-[#121212] dark:text-[#f1f1f1]">Drag and drop files here or click to select</span> <input ref={fileInputRef} type="file" accept=".png, .jpeg, .jpg, .pdf" multiple className="absolute inset-0 opacity-0 cursor-pointer" onChange={handleFileChange} onClick={(e) => e.stopPropagation()} /> </div> <div className="mt-4 max-w-[350px]"> {uploadedFiles.map((file, index) => ( <div key={index} className="flex items-center justify-between text-[#121212] dark:text-[#f1f1f1] mb-2 max-w-[350px] border p-2 rounded-xl border-[#e8e8e8] dark:border-[#4c4c4c]"> <div className="flex flex-row items-center"> <File className="text-gray-400 dark:text-text-primary mr-2" /> <div className="flex flex-col"> <span className="flex-1 text-xs">{file.name}</span> <span className="text-[10px]">({formatFileSize(file.size)})</span> </div> </div> <button onClick={() => removeFile(index)} className="text-gray-400 hover:text-gray-700 transition duration-150 ease-in-out cursor-pointer"> <X /> </button> </div> ))} </div> {error && <div className="absolute bottom-0 right-0 bg-red-500 text-white p-3 rounded-md shadow-md">{error}</div>} </div> </div> ) }