{
  "name": "file-upload",
  "type": "registry:ui",
  "dependencies": [
    "@tabler/icons-react",
    "react-dropzone",
    "motion"
  ],
  "files": [
    {
      "path": "components/ui/file-upload.tsx",
      "content": "\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport React, { useRef, useState } from \"react\";\nimport { motion } from \"motion/react\";\nimport { IconUpload } from \"@tabler/icons-react\";\nimport { useDropzone } from \"react-dropzone\";\n\nconst mainVariant = {\n  initial: {\n    x: 0,\n    y: 0,\n  },\n  animate: {\n    x: 20,\n    y: -20,\n    opacity: 0.9,\n  },\n};\n\nconst secondaryVariant = {\n  initial: {\n    opacity: 0,\n  },\n  animate: {\n    opacity: 1,\n  },\n};\n\nexport const FileUpload = ({\n  onChange,\n}: {\n  onChange?: (files: File[]) => void;\n}) => {\n  const [files, setFiles] = useState<File[]>([]);\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  const handleFileChange = (newFiles: File[]) => {\n    setFiles((prevFiles) => [...prevFiles, ...newFiles]);\n    onChange && onChange(newFiles);\n  };\n\n  const handleClick = () => {\n    fileInputRef.current?.click();\n  };\n\n  const { getRootProps, isDragActive } = useDropzone({\n    multiple: false,\n    noClick: true,\n    onDrop: handleFileChange,\n    onDropRejected: (error) => {\n      console.log(error);\n    },\n  });\n\n  return (\n    <div className=\"w-full\" {...getRootProps()}>\n      <motion.div\n        onClick={handleClick}\n        whileHover=\"animate\"\n        className=\"group/file relative block w-full cursor-pointer overflow-hidden rounded-lg p-10\"\n      >\n        <input\n          ref={fileInputRef}\n          id=\"file-upload-handle\"\n          type=\"file\"\n          onChange={(e) => handleFileChange(Array.from(e.target.files || []))}\n          className=\"hidden\"\n        />\n        <div className=\"absolute inset-0 [mask-image:radial-gradient(ellipse_at_center,white,transparent)]\">\n          <GridPattern />\n        </div>\n        <div className=\"flex flex-col items-center justify-center\">\n          <p className=\"relative z-20 font-sans text-base font-bold text-neutral-700 dark:text-neutral-300\">\n            Upload file\n          </p>\n          <p className=\"relative z-20 mt-2 font-sans text-base font-normal text-neutral-400 dark:text-neutral-400\">\n            Drag or drop your files here or click to upload\n          </p>\n          <div className=\"relative mx-auto mt-10 w-full max-w-xl\">\n            {files.length > 0 &&\n              files.map((file, idx) => (\n                <motion.div\n                  key={\"file\" + idx}\n                  layoutId={idx === 0 ? \"file-upload\" : \"file-upload-\" + idx}\n                  className={cn(\n                    \"relative z-40 mx-auto mt-4 flex w-full flex-col items-start justify-start overflow-hidden rounded-md bg-white p-4 md:h-24 dark:bg-neutral-900\",\n                    \"shadow-sm\",\n                  )}\n                >\n                  <div className=\"flex w-full items-center justify-between gap-4\">\n                    <motion.p\n                      initial={{ opacity: 0 }}\n                      animate={{ opacity: 1 }}\n                      layout\n                      className=\"max-w-xs truncate text-base text-neutral-700 dark:text-neutral-300\"\n                    >\n                      {file.name}\n                    </motion.p>\n                    <motion.p\n                      initial={{ opacity: 0 }}\n                      animate={{ opacity: 1 }}\n                      layout\n                      className=\"shadow-input w-fit shrink-0 rounded-lg px-2 py-1 text-sm text-neutral-600 dark:bg-neutral-800 dark:text-white\"\n                    >\n                      {(file.size / (1024 * 1024)).toFixed(2)} MB\n                    </motion.p>\n                  </div>\n\n                  <div className=\"mt-2 flex w-full flex-col items-start justify-between text-sm text-neutral-600 md:flex-row md:items-center dark:text-neutral-400\">\n                    <motion.p\n                      initial={{ opacity: 0 }}\n                      animate={{ opacity: 1 }}\n                      layout\n                      className=\"rounded-md bg-gray-100 px-1 py-0.5 dark:bg-neutral-800\"\n                    >\n                      {file.type}\n                    </motion.p>\n\n                    <motion.p\n                      initial={{ opacity: 0 }}\n                      animate={{ opacity: 1 }}\n                      layout\n                    >\n                      modified{\" \"}\n                      {new Date(file.lastModified).toLocaleDateString()}\n                    </motion.p>\n                  </div>\n                </motion.div>\n              ))}\n            {!files.length && (\n              <motion.div\n                layoutId=\"file-upload\"\n                variants={mainVariant}\n                transition={{\n                  type: \"spring\",\n                  stiffness: 300,\n                  damping: 20,\n                }}\n                className={cn(\n                  \"relative z-40 mx-auto mt-4 flex h-32 w-full max-w-[8rem] items-center justify-center rounded-md bg-white group-hover/file:shadow-2xl dark:bg-neutral-900\",\n                  \"shadow-[0px_10px_50px_rgba(0,0,0,0.1)]\",\n                )}\n              >\n                {isDragActive ? (\n                  <motion.p\n                    initial={{ opacity: 0 }}\n                    animate={{ opacity: 1 }}\n                    className=\"flex flex-col items-center text-neutral-600\"\n                  >\n                    Drop it\n                    <IconUpload className=\"h-4 w-4 text-neutral-600 dark:text-neutral-400\" />\n                  </motion.p>\n                ) : (\n                  <IconUpload className=\"h-4 w-4 text-neutral-600 dark:text-neutral-300\" />\n                )}\n              </motion.div>\n            )}\n\n            {!files.length && (\n              <motion.div\n                variants={secondaryVariant}\n                className=\"absolute inset-0 z-30 mx-auto mt-4 flex h-32 w-full max-w-[8rem] items-center justify-center rounded-md border border-dashed border-sky-400 bg-transparent opacity-0\"\n              ></motion.div>\n            )}\n          </div>\n        </div>\n      </motion.div>\n    </div>\n  );\n};\n\nexport function GridPattern() {\n  const columns = 41;\n  const rows = 11;\n  return (\n    <div className=\"flex shrink-0 scale-105 flex-wrap items-center justify-center gap-x-px gap-y-px bg-gray-100 dark:bg-neutral-900\">\n      {Array.from({ length: rows }).map((_, row) =>\n        Array.from({ length: columns }).map((_, col) => {\n          const index = row * columns + col;\n          return (\n            <div\n              key={`${col}-${row}`}\n              className={`flex h-10 w-10 shrink-0 rounded-[2px] ${\n                index % 2 === 0\n                  ? \"bg-gray-50 dark:bg-neutral-950\"\n                  : \"bg-gray-50 shadow-[0px_0px_1px_3px_rgba(255,255,255,1)_inset] dark:bg-neutral-950 dark:shadow-[0px_0px_1px_3px_rgba(0,0,0,1)_inset]\"\n              }`}\n            />\n          );\n        }),\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/file-upload.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "File Upload"
}