{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "progress-circle",
  "title": "Progress Circle",
  "description": "Progress Circle from the Get IT Done design system.",
  "dependencies": [
    "class-variance-authority@^0.7.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/progress-circle.tsx",
      "content": "'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { usePreset } from '@/components/ui/labels-context';\n\n// Figma \"Progress circle\" Type=Progress: Size xxs|xs|sm|md|lg, Shape Circle|Half circle, Label False|True.\n// Diameters land on the spacing scale (64, 160, 192, 240, 288); the stroke scales with the size.\nconst progressCircleVariants = cva('relative inline-flex shrink-0 items-center justify-center', {\n  variants: {\n    size: {\n      xxs: 'h-16 w-16',\n      xs: 'h-40 w-40',\n      sm: 'h-48 w-48',\n      md: 'h-60 w-60',\n      lg: 'h-72 w-72',\n    },\n    shape: {\n      circle: '',\n      half: '',\n    },\n  },\n  compoundVariants: [\n    { shape: 'half', size: 'xxs', className: 'h-8' },\n    { shape: 'half', size: 'xs', className: 'h-20' },\n    { shape: 'half', size: 'sm', className: 'h-24' },\n    { shape: 'half', size: 'md', className: 'h-32' },\n    { shape: 'half', size: 'lg', className: 'h-36' },\n  ],\n  defaultVariants: { size: 'md', shape: 'circle' },\n});\n\nconst strokeBySize = { xxs: 12, xs: 10, sm: 8, md: 8, lg: 8 } as const;\n\nconst labelBySize = {\n  xxs: 'typography-ui',\n  xs: 'typography-ui-lg-semibold',\n  sm: 'typography-ui-lg-semibold',\n  md: 'typography-subheading',\n  lg: 'typography-subheading',\n} as const;\n\nexport interface ProgressArcProps extends React.SVGAttributes<SVGSVGElement> {\n  // Percentage 0..100 of the arc that is filled.\n  percent: number;\n  shape?: 'circle' | 'half';\n  strokeWidth?: number;\n  trackClassName?: string;\n  indicatorClassName?: string;\n  // Arc ends. `round` by default, `butt` (flat) in the zero preset. Set it to override.\n  linecap?: 'round' | 'butt';\n}\n\n// Decorative arc shared by ProgressCircle and ScoreRing. pathLength=100 lets the dash array use percent directly.\nexport const ProgressArc = ({\n  percent,\n  shape = 'circle',\n  strokeWidth = 10,\n  trackClassName,\n  indicatorClassName,\n  linecap,\n  className,\n  ...props\n}: ProgressArcProps) => {\n  const zero = usePreset() === 'zero';\n  const strokeLinecap = linecap ?? (zero ? 'butt' : 'round');\n  const clamped = Math.min(Math.max(percent, 0), 100);\n  const half = shape === 'half';\n  const radius = 50 - strokeWidth / 2;\n  const path = half\n    ? `M ${strokeWidth / 2} 50 A ${radius} ${radius} 0 0 1 ${100 - strokeWidth / 2} 50`\n    : `M 50 ${strokeWidth / 2} A ${radius} ${radius} 0 1 1 49.99 ${strokeWidth / 2}`;\n  return (\n    <svg\n      viewBox={half ? '0 0 100 50' : '0 0 100 100'}\n      aria-hidden=\"true\"\n      focusable=\"false\"\n      className={cn('h-full w-full', className)}\n      {...props}\n    >\n      <path d={path} pathLength={100} fill=\"none\" stroke=\"currentColor\" strokeWidth={strokeWidth} strokeLinecap={strokeLinecap} className={cn('text-background-neutral-default', trackClassName)} />\n      {clamped > 0 && (\n        <path\n          d={path}\n          pathLength={100}\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth={strokeWidth}\n          strokeLinecap={strokeLinecap}\n          strokeDasharray={`${clamped} 100`}\n          data-progress-indicator=\"\"\n          className={cn('text-primary transition-[stroke-dasharray] duration-150 motion-reduce:transition-none', indicatorClassName)}\n        />\n      )}\n    </svg>\n  );\n};\n\nexport interface ProgressCircleProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n    VariantProps<typeof progressCircleVariants> {\n  // Current value, clamped to [0, max]. null/undefined renders without aria-valuenow.\n  value?: number | null;\n  max?: number;\n  variant?: 'default' | 'destructive';\n  // Shows the percentage in the middle (circle) or below the arc (half circle).\n  showLabel?: boolean;\n  // Replaces the default \"{percent}%\" text when showLabel is set.\n  label?: React.ReactNode;\n}\n\nconst ProgressCircle = React.forwardRef<HTMLDivElement, ProgressCircleProps>(\n  ({ className, size, shape, variant = 'default', value, max = 100, showLabel = false, label, ...props }, ref) => {\n    const resolvedSize = size ?? 'md';\n    const determinate = value != null;\n    const clamped = determinate ? Math.min(Math.max(value, 0), max) : 0;\n    const percent = determinate && max > 0 ? (clamped / max) * 100 : 0;\n    const half = shape === 'half';\n    return (\n      <div\n        ref={ref}\n        role=\"progressbar\"\n        aria-valuemin={0}\n        aria-valuemax={max}\n        aria-valuenow={determinate ? clamped : undefined}\n        data-state={determinate ? (clamped >= max ? 'complete' : 'loading') : 'indeterminate'}\n        className={cn(progressCircleVariants({ size, shape }), className)}\n        {...props}\n      >\n        <ProgressArc\n          percent={percent}\n          shape={half ? 'half' : 'circle'}\n          strokeWidth={strokeBySize[resolvedSize]}\n          indicatorClassName={variant === 'destructive' ? 'text-text-destructive' : undefined}\n        />\n        {showLabel && (\n          <span\n            aria-hidden=\"true\"\n            className={cn(\n              'absolute flex items-center justify-center text-foreground tabular-nums',\n              half ? 'inset-x-0 bottom-0' : 'inset-0',\n              labelBySize[resolvedSize],\n            )}\n          >\n            {label ?? `${Math.round(percent)}%`}\n          </span>\n        )}\n      </div>\n    );\n  },\n);\nProgressCircle.displayName = 'ProgressCircle';\n\nexport { ProgressCircle, progressCircleVariants };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}