KitlenoKitlenov2.0.0

Forms & Input / File Upload

File Upload

File upload components are used to allow users to select and upload files from their device. They are commonly used in forms, dashboards, and content management systems.

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>
  )
}