{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "uploadthing-progress",
  "title": "UploadThing Progress",
  "description": "A standalone upload progress indicator with bar, ring, and minimal variants.",
  "dependencies": [
    "@uploadthing/react",
    "uploadthing"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/uploadthing/uploadthing-progress/components/elements/uploadthing-progress.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface UploadThingProgressProps {\n  progress: number;\n  variant?: \"bar\" | \"ring\" | \"minimal\";\n  size?: \"sm\" | \"md\" | \"lg\";\n  showLabel?: boolean;\n  className?: string;\n}\n\nconst SIZE_CLASSES = {\n  sm: {\n    bar: \"h-1\",\n    ring: \"w-8 h-8\",\n    text: \"text-xs\",\n  },\n  md: {\n    bar: \"h-2\",\n    ring: \"w-12 h-12\",\n    text: \"text-sm\",\n  },\n  lg: {\n    bar: \"h-3\",\n    ring: \"w-16 h-16\",\n    text: \"text-base\",\n  },\n};\n\nfunction ProgressBar({\n  progress,\n  size,\n  showLabel,\n}: {\n  progress: number;\n  size: \"sm\" | \"md\" | \"lg\";\n  showLabel: boolean;\n}) {\n  return (\n    <div className=\"w-full space-y-1\">\n      {showLabel && (\n        <div className=\"flex justify-between items-center\">\n          <span className={cn(\"text-muted-foreground\", SIZE_CLASSES[size].text)}>\n            Uploading...\n          </span>\n          <span className={cn(\"font-medium tabular-nums\", SIZE_CLASSES[size].text)}>\n            {Math.round(progress)}%\n          </span>\n        </div>\n      )}\n      <div\n        className={cn(\n          \"w-full bg-muted rounded-full overflow-hidden\",\n          SIZE_CLASSES[size].bar\n        )}\n      >\n        <div\n          className=\"h-full bg-primary rounded-full transition-all duration-300 ease-out\"\n          style={{ width: `${progress}%` }}\n        />\n      </div>\n    </div>\n  );\n}\n\nfunction ProgressRing({\n  progress,\n  size,\n  showLabel,\n}: {\n  progress: number;\n  size: \"sm\" | \"md\" | \"lg\";\n  showLabel: boolean;\n}) {\n  const strokeWidth = size === \"sm\" ? 3 : size === \"md\" ? 4 : 5;\n  const radius = size === \"sm\" ? 12 : size === \"md\" ? 20 : 28;\n  const circumference = 2 * Math.PI * radius;\n  const strokeDashoffset = circumference - (progress / 100) * circumference;\n\n  const svgSize = radius * 2 + strokeWidth * 2;\n\n  return (\n    <div className=\"relative inline-flex items-center justify-center\">\n      <svg\n        className={cn(\"-rotate-90\", SIZE_CLASSES[size].ring)}\n        viewBox={`0 0 ${svgSize} ${svgSize}`}\n      >\n        <circle\n          cx={svgSize / 2}\n          cy={svgSize / 2}\n          r={radius}\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth={strokeWidth}\n          className=\"text-muted\"\n        />\n        <circle\n          cx={svgSize / 2}\n          cy={svgSize / 2}\n          r={radius}\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth={strokeWidth}\n          strokeDasharray={circumference}\n          strokeDashoffset={strokeDashoffset}\n          strokeLinecap=\"round\"\n          className=\"text-primary transition-all duration-300 ease-out\"\n        />\n      </svg>\n      {showLabel && (\n        <span\n          className={cn(\n            \"absolute font-medium tabular-nums\",\n            SIZE_CLASSES[size].text\n          )}\n        >\n          {Math.round(progress)}%\n        </span>\n      )}\n    </div>\n  );\n}\n\nfunction ProgressMinimal({\n  progress,\n  size,\n}: {\n  progress: number;\n  size: \"sm\" | \"md\" | \"lg\";\n}) {\n  return (\n    <div className=\"flex items-center gap-2\">\n      <div\n        className={cn(\n          \"flex-1 bg-muted rounded-full overflow-hidden\",\n          SIZE_CLASSES[size].bar\n        )}\n      >\n        <div\n          className=\"h-full bg-primary rounded-full transition-all duration-300 ease-out\"\n          style={{ width: `${progress}%` }}\n        />\n      </div>\n      <span\n        className={cn(\n          \"font-medium tabular-nums text-muted-foreground min-w-[3ch]\",\n          SIZE_CLASSES[size].text\n        )}\n      >\n        {Math.round(progress)}%\n      </span>\n    </div>\n  );\n}\n\nexport function UploadThingProgress({\n  progress,\n  variant = \"bar\",\n  size = \"md\",\n  showLabel = true,\n  className,\n}: UploadThingProgressProps) {\n  const clampedProgress = Math.min(100, Math.max(0, progress));\n\n  return (\n    <div\n      data-slot=\"uploadthing-progress\"\n      className={cn(\n        variant === \"ring\" && \"inline-flex\",\n        variant !== \"ring\" && \"w-full\",\n        className\n      )}\n      role=\"progressbar\"\n      aria-valuenow={clampedProgress}\n      aria-valuemin={0}\n      aria-valuemax={100}\n    >\n      {variant === \"bar\" && (\n        <ProgressBar progress={clampedProgress} size={size} showLabel={showLabel} />\n      )}\n      {variant === \"ring\" && (\n        <ProgressRing progress={clampedProgress} size={size} showLabel={showLabel} />\n      )}\n      {variant === \"minimal\" && (\n        <ProgressMinimal progress={clampedProgress} size={size} />\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Three variants: bar (horizontal), ring (circular), minimal (compact). Three sizes: sm, md, lg. Use with useUploadThing's onUploadProgress.",
  "categories": [
    "uploadthing"
  ],
  "type": "registry:block"
}