Drag and drop a file or click to select

npm i framer-motion lucid-react
import React, { useState, useRef } from 'react';
import { motion } from 'framer-motion';
import { FileIcon, UploadCloudIcon } from 'lucide-react';
import { cn } from "@/lib/utils"


const FileUploader = ({
  onFileSelect,
  onUploadProgress,
  accept = '*',
  maxSize = Infinity,
  className = '',
  iconSize = 'w-16 h-16',
  iconColor = 'text-teal-500',
  progressColor = 'bg-teal-500',
  progressBgColor = 'bg-teal-200',
}) => {
  const [file, setFile] = useState(null);
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState('');
  const fileInputRef = useRef(null);

  const handleFileChange = (event) => {
    const selectedFile = event.target.files[0];
    if (selectedFile && validateFile(selectedFile)) {
      setFile(selectedFile);
      setError('');
      onFileSelect(selectedFile);
      simulateUpload();
    }
  };

  const validateFile = (file) => {
    if (file.size > maxSize) {
      setError(`File size should not exceed ${formatBytes(maxSize)}`);
      return false;
    }
    return true;
  };

  const simulateUpload = () => {
    let progress = 0;
    const interval = setInterval(() => {
      progress += 10;
      setProgress(progress);
      onUploadProgress(progress);
      if (progress >= 100) {
        clearInterval(interval);
      }
    }, 500);
  };

  const handleDragOver = (event) => {
    event.preventDefault();
  };

  const handleDrop = (event) => {
    event.preventDefault();
    const droppedFile = event.dataTransfer.files[0];
    if (validateFile(droppedFile)) {
      setFile(droppedFile);
      setError('');
      onFileSelect(droppedFile);
      simulateUpload();
    }
  };

  const formatBytes = (bytes, decimals = 2) => {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
  };

  return (
    <div className={cn("w-full max-w-md mx-auto mt-10", className)}>
      <div
        className="border-2 border-dashed border-neutral-600 rounded-lg p-6 cursor-pointer"
        onClick={()=> fileInputRef.current.click()}
        onDragOver={handleDragOver}
        onDrop={handleDrop}
      >
        <input
          type="file"
          ref={fileInputRef}
          onChange={handleFileChange}
          accept={accept}
          className="hidden"
        />
        <div className="flex flex-col items-center">
          {file ? (
            <FileIcon className={`${iconSize} ${iconColor} mb-4`} />
          ) : (
            <UploadCloudIcon className={`${iconSize} text-gray-400 mb-4`} />
          )}
          <p className="text-center text-sm text-neutral-400">
            {file ? file.name : "Drag and drop a file or click to select"}
          </p>
          {error && <p className="text-red-500 mt-2">{error}</p>}
        </div>
      </div>
      {file && (
        <div className="mt-4 w-full">
          <div className="relative pt-1">
            <div className="flex mb-2 items-center justify-between">
              <div>
                <span className={`text-xs font-semibold border border-emerald-600 inline-block py-1 px-2 uppercase rounded-full ${iconColor} ${progressBgColor}`}>
                  Uploading
                </span>
              </div>
              <div className="text-right">
                <span className={`text-xs font-semibold inline-block ${iconColor}`}>
                  {progress}%
                </span>
              </div>
            </div>
            <div className={`overflow-hidden h-2 mb-4 text-xs flex rounded ${progressBgColor}`}>
              <motion.div
                className={`shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center ${progressColor}`}
                initial={{ width: 0 }}
                animate={{ width: `${progress}%` }}
                transition={{ duration: 0.5 }}
              ></motion.div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default FileUploader;