{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-upload3",
  "title": "File upload",
  "description": "An upload area that accepts files through drag and drop or the file browser. Every file upload variant works with both Radix UI and Base UI setups.",
  "files": [
    {
      "path": "contents/components/file-upload/3/index.tsx",
      "content": "\"use client\";\n\nimport {\n  FileArchiveIcon,\n  FileIcon,\n  FileSpreadsheetIcon,\n  FileTextIcon,\n  HeadphonesIcon,\n  ImageIcon,\n  Trash2Icon,\n  UploadIcon,\n  VideoIcon,\n  XIcon\n} from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { formatBytes, useFileUpload, type FileMetadata } from \"@/hooks/use-file-upload\";\nimport { cn } from \"@/lib/utils\";\n\nconst initialFiles: FileMetadata[] = [\n  {\n    id: \"file-1\",\n    name: \"brand-assets.zip\",\n    size: 252873,\n    type: \"application/zip\",\n    url: \"#\"\n  },\n  {\n    id: \"file-2\",\n    name: \"cover-photo.jpg\",\n    size: 1530820,\n    type: \"image/jpeg\",\n    url: \"#\"\n  },\n  {\n    id: \"file-3\",\n    name: \"intro-theme.mp3\",\n    size: 1530820,\n    type: \"audio/mpeg\",\n    url: \"#\"\n  }\n];\n\nfunction getFileIcon(type: string, name: string) {\n  if (type.startsWith(\"image/\")) return ImageIcon;\n  if (type.startsWith(\"audio/\")) return HeadphonesIcon;\n  if (type.startsWith(\"video/\")) return VideoIcon;\n  if (type.includes(\"pdf\") || type.startsWith(\"text/\")) return FileTextIcon;\n  if (type.includes(\"sheet\") || type.includes(\"csv\")) return FileSpreadsheetIcon;\n  if (/\\.(zip|rar|7z|tar|gz)$/i.test(name)) return FileArchiveIcon;\n  return FileIcon;\n}\n\nexport default function Component() {\n  const maxFiles = 6;\n  const maxSizeMB = 5;\n  const maxSize = maxSizeMB * 1024 * 1024;\n\n  const [\n    { files, isDragging, errors },\n    {\n      handleDragEnter,\n      handleDragLeave,\n      handleDragOver,\n      handleDrop,\n      openFileDialog,\n      removeFile,\n      clearFiles,\n      getInputProps\n    }\n  ] = useFileUpload({\n    initialFiles,\n    maxFiles,\n    maxSize,\n    multiple: true\n  });\n\n  return (\n    <div className=\"w-full max-w-lg space-y-2\">\n      <div\n        onDragEnter={handleDragEnter}\n        onDragLeave={handleDragLeave}\n        onDragOver={handleDragOver}\n        onDrop={handleDrop}\n        data-dragging={isDragging || undefined}\n        className={cn(\n          \"border-input data-[dragging=true]:bg-accent/40 flex min-h-56 w-full flex-col rounded-xl border border-dashed p-4 transition-colors\",\n          files.length === 0 && \"items-center justify-center p-6\"\n        )}>\n        <input {...getInputProps()} className=\"sr-only\" aria-label=\"Upload files\" />\n\n        {files.length === 0 ? (\n          <div className=\"flex flex-col items-center gap-4 text-center\">\n            <div className=\"border-input text-muted-foreground flex size-14 items-center justify-center rounded-full border\">\n              <ImageIcon className=\"size-5\" />\n            </div>\n            <div className=\"space-y-1\">\n              <p className=\"text-base font-medium\">Drop your files here</p>\n              <p className=\"text-muted-foreground text-sm\">\n                Max {maxFiles} files &middot; Up to {maxSizeMB}MB\n              </p>\n            </div>\n            <Button type=\"button\" variant=\"outline\" onClick={openFileDialog}>\n              <UploadIcon />\n              Select files\n            </Button>\n          </div>\n        ) : (\n          <div className=\"flex w-full flex-col gap-4\">\n            <div className=\"flex items-center justify-between gap-3\">\n              <h3 className=\"text-base font-medium\">Files ({files.length})</h3>\n              <div className=\"flex items-center gap-2\">\n                <Button\n                  type=\"button\"\n                  variant=\"outline\"\n                  onClick={openFileDialog}\n                  disabled={files.length >= maxFiles}>\n                  <UploadIcon />\n                  Add files\n                </Button>\n                <Button type=\"button\" variant=\"outline\" onClick={clearFiles}>\n                  <Trash2Icon />\n                  Remove all\n                </Button>\n              </div>\n            </div>\n\n            <ul className=\"flex flex-col gap-3\">\n              {files.map((file) => {\n                const Icon = getFileIcon(file.file.type, file.file.name);\n\n                return (\n                  <li\n                    key={file.id}\n                    className=\"bg-background flex items-center gap-3 rounded-lg border p-3\">\n                    <div className=\"text-foreground flex size-10 shrink-0 items-center justify-center rounded-md border\">\n                      <Icon className=\"size-5\" />\n                    </div>\n                    <div className=\"min-w-0 flex-1\">\n                      <p className=\"truncate text-sm font-medium\">{file.file.name}</p>\n                      <p className=\"text-muted-foreground text-sm\">\n                        {formatBytes(file.file.size)}\n                      </p>\n                    </div>\n                    <Button\n                      type=\"button\"\n                      size=\"icon\"\n                      variant=\"ghost\"\n                      className=\"text-muted-foreground hover:text-foreground size-8\"\n                      onClick={() => removeFile(file.id)}\n                      aria-label={`Remove ${file.file.name}`}>\n                      <XIcon className=\"size-4\" />\n                    </Button>\n                  </li>\n                );\n              })}\n            </ul>\n          </div>\n        )}\n      </div>\n\n      {errors.length > 0 && (\n        <p className=\"text-destructive text-sm\" role=\"alert\">\n          {errors[0]}\n        </p>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/file-upload3.tsx"
    }
  ],
  "type": "registry:component"
}