{
  "$schema": "https://www.cooki-ui.design/schema/registry-item.json",
  "name": "drag-drop-file-upload",
  "type": "registry:ui",
  "title": "Drag & Drop File Upload",
  "description": "Interactive file drop zone with dashed border animation, file type validation, and preview chips.",
  "category": "forms",
  "dependencies": [
    "framer-motion",
    "@phosphor-icons/react"
  ],
  "files": [
    {
      "path": "registry/forms/drag-drop-file-upload/drag-drop-file-upload.tsx",
      "type": "registry:ui",
      "target": "components/ui/drag-drop-file-upload.tsx",
      "content": "import React, { useState, useRef } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { CloudArrowUp, File, Check, Trash } from '@phosphor-icons/react';\n\nexport interface FileItem {\n  id: string;\n  name: string;\n  size: string;\n  progress: number;\n}\n\nexport const DragDropFileUpload: React.FC = () => {\n  const [isDragging, setIsDragging] = useState(false);\n  const [files, setFiles] = useState<FileItem[]>([\n    { id: '1', name: 'schema-kernel-v2.json', size: '24.8 KB', progress: 100 },\n  ]);\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  const handleDrag = (e: React.DragEvent) => {\n    e.preventDefault();\n    e.stopPropagation();\n    if (e.type === 'dragenter' || e.type === 'dragover') setIsDragging(true);\n    else if (e.type === 'dragleave') setIsDragging(false);\n  };\n\n  const handleDrop = (e: React.DragEvent) => {\n    e.preventDefault();\n    e.stopPropagation();\n    setIsDragging(false);\n    if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n      addFiles(Array.from(e.dataTransfer.files));\n    }\n  };\n\n  const addFiles = (newFiles: globalThis.File[]) => {\n    const items: FileItem[] = newFiles.map((f) => ({\n      id: Math.random().toString(36).substring(7),\n      name: f.name,\n      size: (f.size / 1024).toFixed(1) + ' KB',\n      progress: 100,\n    }));\n    setFiles((prev) => [...prev, ...items]);\n  };\n\n  const removeFile = (id: string) => {\n    setFiles((prev) => prev.filter((f) => f.id !== id));\n  };\n\n  return (\n    <div className=\"w-full max-w-md mx-auto space-y-4\">\n      <div\n        onDragEnter={handleDrag}\n        onDragLeave={handleDrag}\n        onDragOver={handleDrag}\n        onDrop={handleDrop}\n        onClick={() => inputRef.current?.click()}\n        className={`relative flex flex-col items-center justify-center p-8 rounded-3xl border-2 border-dashed transition-all cursor-pointer ${\n          isDragging\n            ? 'border-indigo-500 bg-indigo-500/10 scale-[1.01]'\n            : 'border-zinc-300 dark:border-white/15 bg-zinc-50 dark:bg-zinc-900/40 hover:border-indigo-400 dark:hover:border-white/30'\n        }`}\n      >\n        <input\n          ref={inputRef}\n          type=\"file\"\n          multiple\n          className=\"hidden\"\n          onChange={(e) => {\n            if (e.target.files) addFiles(Array.from(e.target.files));\n          }}\n        />\n        <div className=\"flex h-12 w-12 items-center justify-center rounded-2xl bg-indigo-500/10 text-indigo-500 dark:text-indigo-400 mb-3 border border-indigo-500/20 shadow-inner\">\n          <CloudArrowUp size={24} weight=\"duotone\" />\n        </div>\n        <p className=\"text-xs font-semibold text-zinc-900 dark:text-white\">\n          Drop component artifacts or <span className=\"text-indigo-500 dark:text-indigo-400\">browse</span>\n        </p>\n        <p className=\"text-[10px] text-zinc-500 font-mono mt-1\">Accepts .TSX, .JSON, .MD, .CSS up to 10MB</p>\n      </div>\n\n      <AnimatePresence>\n        {files.length > 0 && (\n          <div className=\"space-y-2\">\n            {files.map((file) => (\n              <motion.div\n                key={file.id}\n                initial={{ opacity: 0, height: 0 }}\n                animate={{ opacity: 1, height: 'auto' }}\n                exit={{ opacity: 0, height: 0 }}\n                className=\"flex items-center justify-between p-3 rounded-2xl border border-zinc-200 dark:border-white/10 bg-white dark:bg-zinc-900/60 shadow-xs\"\n              >\n                <div className=\"flex items-center gap-2.5 truncate\">\n                  <File size={16} className=\"text-zinc-400 shrink-0\" />\n                  <div className=\"truncate\">\n                    <p className=\"text-xs font-mono font-medium text-zinc-900 dark:text-white truncate\">{file.name}</p>\n                    <p className=\"text-[10px] font-mono text-zinc-500\">{file.size}</p>\n                  </div>\n                </div>\n                <div className=\"flex items-center gap-2\">\n                  <span className=\"flex items-center gap-1 text-[10px] font-mono text-emerald-500 font-semibold bg-emerald-500/10 px-2 py-0.5 rounded-full border border-emerald-500/20\">\n                    <Check size={11} weight=\"bold\" /> Ready\n                  </span>\n                  <button\n                    onClick={(e) => {\n                      e.stopPropagation();\n                      removeFile(file.id);\n                    }}\n                    className=\"text-zinc-400 hover:text-rose-500 p-1 rounded-full transition-colors cursor-pointer\"\n                  >\n                    <Trash size={13} />\n                  </button>\n                </div>\n              </motion.div>\n            ))}\n          </div>\n        )}\n      </AnimatePresence>\n    </div>\n  );\n};\n"
    }
  ]
}