{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "upload-progress",
  "type": "registry:component",
  "title": "Upload Progress",
  "description": "Per-file and aggregate progress for in-flight uploads.",
  "dependencies": [
    "files-sdk",
    "lucide-react"
  ],
  "registryDependencies": [
    "progress"
  ],
  "files": [
    {
      "path": "registry/files-sdk/upload-progress/upload-progress.tsx",
      "type": "registry:component",
      "target": "components/files-sdk/upload-progress.tsx",
      "content": "\"use client\";\n\nimport type { FileUploadState, UseFilesResult } from \"files-sdk/react\";\nimport { CheckCircle2Icon, Loader2Icon, XCircleIcon } from \"lucide-react\";\n\nimport { Progress } from \"@/components/ui/progress\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface UploadProgressProps {\n  /** A `useFiles()` instance — reads its ambient `uploads` / `progress`. */\n  files: UseFilesResult;\n  className?: string;\n}\n\nconst StatusIcon = ({ status }: { status: FileUploadState[\"status\"] }) => {\n  if (status === \"success\") {\n    return <CheckCircle2Icon className=\"size-4 text-primary\" />;\n  }\n  if (status === \"error\" || status === \"aborted\") {\n    return <XCircleIcon className=\"size-4 text-destructive\" />;\n  }\n  return <Loader2Icon className=\"size-4 animate-spin text-muted-foreground\" />;\n};\n\nconst UploadRow = ({ upload }: { upload: FileUploadState }) => (\n  <li className=\"flex flex-col gap-1.5\">\n    <div className=\"flex items-center gap-2 text-sm\">\n      <StatusIcon status={upload.status} />\n      <span className=\"min-w-0 flex-1 truncate\">{upload.name}</span>\n      <span className=\"text-muted-foreground text-xs\">\n        {Math.round(upload.progress * 100)}%\n      </span>\n    </div>\n    <Progress\n      className={cn(\n        upload.status === \"error\" && \"bg-destructive/20\",\n        upload.status === \"aborted\" && \"opacity-50\"\n      )}\n      value={upload.progress * 100}\n    />\n  </li>\n);\n\n/**\n * Renders the ambient upload state of a `useFiles()` instance: one row per file\n * plus an aggregate bar when several are in flight. Returns `null` when idle.\n */\nexport const UploadProgress = ({ files, className }: UploadProgressProps) => {\n  const { progress, uploads } = files;\n\n  if (!uploads.length) {\n    return null;\n  }\n\n  return (\n    <div className={cn(\"flex flex-col gap-3\", className)}>\n      {uploads.length > 1 && (\n        <div className=\"flex flex-col gap-1.5\">\n          <div className=\"flex items-center justify-between text-muted-foreground text-xs\">\n            <span>{uploads.length} files</span>\n            <span>{Math.round(progress.fraction * 100)}%</span>\n          </div>\n          <Progress value={progress.fraction * 100} />\n        </div>\n      )}\n      <ul className=\"flex flex-col gap-2\">\n        {uploads.map((upload, index) => (\n          <UploadRow\n            key={upload.key ?? `${upload.name}-${index}`}\n            upload={upload}\n          />\n        ))}\n      </ul>\n    </div>\n  );\n};\n"
    }
  ]
}
