{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "circular-progress",
  "title": "Circular Progress",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/circular-progress/CircularProgress.tsx",
      "content": "import * as React from 'react'\nimport { cn } from '@/lib/utils'\nimport { circularProgressVariants } from './circular-progress.variants'\n\nexport interface CircularProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Progress value 0-100. Ignored when indeterminate is true. */\n  value?: number\n  /** Diameter in pixels. */\n  size?: 'sm' | 'default' | 'lg' | number\n  /** Stroke thickness in pixels. */\n  thickness?: number\n  /** Progress arc color. Defaults to primary. */\n  color?: string\n  /** Track (background ring) color. */\n  trackColor?: string\n  /** Indeterminate spinning mode. */\n  indeterminate?: boolean\n  /** Show the numeric value in the center. */\n  showValue?: boolean\n  /** Suffix appended to the value (e.g. '%'). */\n  suffix?: string\n  /** Accessible label. */\n  ariaLabel?: string\n}\n\nconst sizePxMap = {\n  sm: 40,\n  default: 56,\n  lg: 80,\n} as const\n\nconst CircularProgress = React.forwardRef<HTMLDivElement, CircularProgressProps>(\n  (\n    {\n      className,\n      value = 0,\n      size = 'default',\n      thickness = 8,\n      color,\n      trackColor,\n      indeterminate = false,\n      showValue = false,\n      suffix = '%',\n      ariaLabel = 'Progress',\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const sizePx = React.useMemo(() => {\n      if (typeof size === 'number') return size\n      return sizePxMap[size] ?? 56\n    }, [size])\n\n    const normalizedValue = React.useMemo(() => Math.min(100, Math.max(0, value)), [value])\n\n    const radius = React.useMemo(() => (sizePx - thickness) / 2, [sizePx, thickness])\n    const circumference = React.useMemo(() => 2 * Math.PI * radius, [radius])\n    const strokeDashoffset = React.useMemo(() => {\n      if (indeterminate) return circumference * 0.25\n      return circumference * (1 - normalizedValue / 100)\n    }, [indeterminate, circumference, normalizedValue])\n\n    const resolvedColor = React.useMemo(() => color || 'var(--primary)', [color])\n    const resolvedTrackColor = React.useMemo(() => trackColor || 'var(--muted)', [trackColor])\n\n    const viewBox = React.useMemo(() => `0 0 ${sizePx} ${sizePx}`, [sizePx])\n    const center = React.useMemo(() => sizePx / 2, [sizePx])\n\n    const fontSize = React.useMemo(() => {\n      const s = sizePx\n      if (s <= 40) return 'text-xs'\n      if (s <= 56) return 'text-sm'\n      return 'text-base'\n    }, [sizePx])\n\n    return (\n      <div\n        ref={ref}\n        data-uipkge=\"\"\n        data-slot=\"circular-progress\"\n        data-size={typeof size === 'string' ? size : 'custom'}\n        data-indeterminate={indeterminate ? 'true' : 'false'}\n        className={cn(circularProgressVariants(), className)}\n        style={{ width: `${sizePx}px`, height: `${sizePx}px` }}\n        role=\"progressbar\"\n        aria-valuemin={0}\n        aria-valuemax={100}\n        aria-valuenow={indeterminate ? undefined : normalizedValue}\n        aria-label={ariaLabel}\n        {...props}\n      >\n        <style>{`\n@media (prefers-reduced-motion: no-preference) {\n  .animate-spin-circular {\n    animation: spin-circular 1.4s linear infinite;\n  }\n}\n\n@keyframes spin-circular {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n`}</style>\n        <svg width={sizePx} height={sizePx} viewBox={viewBox} className=\"block\">\n          {/* Track */}\n          <circle\n            cx={center}\n            cy={center}\n            r={radius}\n            fill=\"none\"\n            stroke={resolvedTrackColor}\n            strokeWidth={thickness}\n          />\n          {/* Progress arc */}\n          <g\n            transform={indeterminate ? undefined : `rotate(-90 ${center} ${center})`}\n            className={indeterminate ? 'animate-spin-circular' : ''}\n            style={indeterminate ? { transformBox: 'fill-box', transformOrigin: 'center' } : undefined}\n          >\n            <circle\n              cx={center}\n              cy={center}\n              r={radius}\n              fill=\"none\"\n              stroke={resolvedColor}\n              strokeWidth={thickness}\n              strokeLinecap=\"round\"\n              strokeDasharray={circumference}\n              strokeDashoffset={strokeDashoffset}\n              className={!indeterminate ? 'transition-[stroke-dashoffset] duration-500 ease-out' : ''}\n            />\n          </g>\n        </svg>\n\n        {showValue || children ? (\n          <div className=\"absolute inset-0 flex items-center justify-center\">\n            {children ? (\n              children\n            ) : (\n              <span className={cn('text-foreground font-medium tabular-nums', fontSize)}>\n                {Math.round(normalizedValue)}\n                {suffix}\n              </span>\n            )}\n          </div>\n        ) : null}\n      </div>\n    )\n  },\n)\nCircularProgress.displayName = 'CircularProgress'\n\nexport { CircularProgress }\n",
      "type": "registry:ui",
      "target": "~/components/ui/circular-progress/CircularProgress.tsx"
    },
    {
      "path": "packages/registry-react/components/circular-progress/circular-progress.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so `CircularProgress.tsx` can `import { circularProgressVariants } from\n * './circular-progress.variants'` without creating a circular dependency through\n * the index. See card.variants.ts for the same pattern + the SSR symptom that\n * motivated the split.\n */\nexport const circularProgressVariants = cva('relative inline-flex items-center justify-center')\n\nexport type CircularProgressVariants = VariantProps<typeof circularProgressVariants>\n",
      "type": "registry:ui",
      "target": "~/components/ui/circular-progress/circular-progress.variants.ts"
    },
    {
      "path": "packages/registry-react/components/circular-progress/index.ts",
      "content": "export { CircularProgress, type CircularProgressProps } from './CircularProgress'\nexport { circularProgressVariants, type CircularProgressVariants } from './circular-progress.variants'\n",
      "type": "registry:ui",
      "target": "~/components/ui/circular-progress/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Radial/circular progress indicator. Supports value (0-100), size presets or custom pixel size, stroke thickness, custom arc and track colors, indeterminate spinning mode, a label slot for center content, and a show-value prop that renders the percentage in the center.",
  "categories": [
    "feedback",
    "display"
  ]
}