{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "radial-gauge",
  "title": "Radial Gauge",
  "description": "A semicircular or ring gauge, continuous or segmented, with content in the middle.",
  "files": [
    {
      "path": "registry/dashboardcn/ui/radial-gauge.tsx",
      "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface RadialGaugeProps extends React.ComponentProps<\"div\"> {\n  value: number\n  min?: number\n  max?: number\n  /** Diameter in pixels. */\n  size?: number\n  /** Stroke width in pixels. */\n  thickness?: number\n  /** Number of arc segments. 0 draws a continuous arc. */\n  segments?: number\n  /** Gap between segments in degrees. */\n  gap?: number\n  /** Angle where the arc starts, in degrees clockwise from 12 o'clock. */\n  startAngle?: number\n  /** Total sweep of the arc in degrees. 180 is a semicircle, 360 a ring. */\n  sweep?: number\n  /** Any CSS color. Defaults to chart-1. */\n  color?: string\n  trackColor?: string\n  /** Reveal filled segments in sequence when the gauge mounts. */\n  animate?: boolean\n  /** Total reveal time in milliseconds. */\n  animationDuration?: number\n  /** Content rendered in the middle of the gauge. */\n  children?: React.ReactNode\n}\n\n// Round so server and client render identical path strings.\nconst round = (n: number) => Math.round(n * 1000) / 1000\n\nfunction polar(cx: number, cy: number, r: number, angleDeg: number) {\n  const rad = ((angleDeg - 90) * Math.PI) / 180\n  return { x: round(cx + r * Math.cos(rad)), y: round(cy + r * Math.sin(rad)) }\n}\n\nfunction arcPath(cx: number, cy: number, r: number, from: number, to: number) {\n  const start = polar(cx, cy, r, from)\n  const end = polar(cx, cy, r, to)\n  const largeArc = to - from > 180 ? 1 : 0\n  return `M ${start.x} ${start.y} A ${r} ${r} 0 ${largeArc} 1 ${end.x} ${end.y}`\n}\n\nfunction RadialGauge({\n  value,\n  min = 0,\n  max = 100,\n  size = 160,\n  thickness = 10,\n  segments = 0,\n  gap = 2,\n  startAngle,\n  sweep = 180,\n  color = \"var(--chart-1)\",\n  trackColor = \"var(--muted)\",\n  animate = false,\n  animationDuration = 700,\n  className,\n  children,\n  ...props\n}: RadialGaugeProps) {\n  const fraction = Math.min(1, Math.max(0, (value - min) / (max - min || 1)))\n  const start = startAngle ?? (sweep >= 360 ? 0 : -sweep / 2)\n  const r = size / 2 - thickness / 2\n  const cx = size / 2\n  const cy = size / 2\n  // Trim the box to the arc's vertical extent so a semicircle does not\n  // reserve a full circle of space.\n  const angles = Array.from({ length: 64 }, (_, i) => start + (sweep * i) / 63)\n  const ys = angles.map((a) => polar(cx, cy, r, a).y)\n  const top = Math.max(0, Math.min(...ys) - thickness / 2)\n  const bottom = Math.min(size, Math.max(...ys) + thickness / 2)\n  const height = bottom - top\n\n  const arcs: { from: number; to: number; filled: boolean }[] = []\n  if (segments > 0) {\n    const step = sweep / segments\n    for (let i = 0; i < segments; i++) {\n      const from = start + i * step + gap / 2\n      const to = start + (i + 1) * step - gap / 2\n      const mid = (i + 0.5) / segments\n      arcs.push({ from, to, filled: mid <= fraction })\n    }\n  }\n\n  return (\n    <div\n      data-slot=\"radial-gauge\"\n      role=\"meter\"\n      aria-valuemin={min}\n      aria-valuemax={max}\n      aria-valuenow={value}\n      className={cn(\"relative inline-flex shrink-0\", className)}\n      style={{ width: size, height }}\n      {...props}\n    >\n      <svg\n        width={size}\n        height={height}\n        viewBox={`0 ${top} ${size} ${height}`}\n        className=\"absolute inset-0\"\n        aria-hidden=\"true\"\n      >\n        {segments > 0 ? (\n          arcs.map((arc, index) => {\n            const path = arcPath(cx, cy, r, arc.from, arc.to)\n            const filledCount = Math.max(1, Math.round(fraction * segments))\n            const delay = (index / filledCount) * animationDuration * 0.8\n\n            return (\n              <g key={index}>\n                <path\n                  d={path}\n                  fill=\"none\"\n                  stroke={trackColor}\n                  strokeWidth={thickness}\n                  strokeLinecap=\"butt\"\n                />\n                {arc.filled ? (\n                  <path\n                    d={path}\n                    fill=\"none\"\n                    stroke={color}\n                    strokeWidth={thickness}\n                    strokeLinecap=\"butt\"\n                    className={cn(\n                      animate && \"motion-safe:animate-in motion-safe:fade-in\"\n                    )}\n                    style={\n                      animate\n                        ? {\n                            animationDelay: `${delay}ms`,\n                            animationDuration: `${Math.max(120, animationDuration * 0.25)}ms`,\n                            animationFillMode: \"both\",\n                          }\n                        : undefined\n                    }\n                  />\n                ) : null}\n              </g>\n            )\n          })\n        ) : (\n          <>\n            <path\n              d={arcPath(cx, cy, r, start, start + sweep - (sweep >= 360 ? 0.01 : 0))}\n              fill=\"none\"\n              stroke={trackColor}\n              strokeWidth={thickness}\n              strokeLinecap={sweep >= 360 ? \"butt\" : \"round\"}\n            />\n            {fraction > 0 ? (\n              <path\n                d={arcPath(cx, cy, r, start, start + sweep * fraction)}\n                fill=\"none\"\n                stroke={color}\n                strokeWidth={thickness}\n                strokeLinecap=\"round\"\n                className={cn(\n                  \"transition-[d] duration-500\",\n                  animate && \"motion-safe:animate-in motion-safe:fade-in\"\n                )}\n                style={\n                  animate\n                    ? { animationDuration: `${animationDuration}ms` }\n                    : undefined\n                }\n              />\n            ) : null}\n          </>\n        )}\n      </svg>\n      {children ? (\n        <div\n          className={cn(\n            \"absolute inset-x-0 flex flex-col items-center justify-center text-center\",\n            sweep > 200 ? \"inset-y-0\" : \"bottom-0\"\n          )}\n        >\n          {children}\n        </div>\n      ) : null}\n    </div>\n  )\n}\n\nexport { RadialGauge }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}