{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "progress-circular",
  "title": "Circular progress",
  "description": "Styled progress: the Circular set. 5 decorative progress variations (RingProgress, ConicProgress, DualRingProgress, DashRingProgress, GlowRingProgress) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/progress-circular.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Circular progress family: SVG rings inside a square box. The arc = value. Colour comes ONLY from tokens (stroke=var(--color-info) and so on). framer-motion drives strokeDashoffset and dasharray; under reduced-motion the arc snaps straight to its filled position. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst box: Record<StyledSize, string> = {\n  sm: \"size-10\",\n  md: \"size-12\",\n  lg: \"size-14\",\n  xl: \"size-16\",\n}\n\nconst R = 42\nconst C = 2 * Math.PI * R\n\ntype Props = React.ComponentProps<\"div\"> & { size?: StyledSize; value?: number; label?: string }\n\nfunction clamp(v: number) {\n  return Math.max(0, Math.min(100, v))\n}\n\n/* Ring: tek SVG yay (stroke-dasharray), ortada % metni. */\nexport function RingProgress({ className, size = \"md\", value = 60, label = \"Progress\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  const v = clamp(value)\n  const offset = C * (1 - v / 100)\n  return (\n    <div\n      data-slot=\"styled-progress\"\n      role=\"progressbar\"\n      aria-valuenow={v}\n      aria-valuemin={0}\n      aria-valuemax={100}\n      aria-label={label}\n      className={cn(\"relative grid place-items-center\", box[size], className)}\n      {...props}\n    >\n      <svg viewBox=\"0 0 100 100\" className=\"size-full -rotate-90\">\n        <circle cx=\"50\" cy=\"50\" r={R} fill=\"none\" strokeWidth=\"9\" className=\"stroke-secondary\" />\n        <motion.circle\n          cx=\"50\"\n          cy=\"50\"\n          r={R}\n          fill=\"none\"\n          strokeWidth=\"9\"\n          strokeLinecap=\"round\"\n          stroke=\"var(--color-info)\"\n          strokeDasharray={C}\n          initial={reduce ? false : { strokeDashoffset: C }}\n          animate={{ strokeDashoffset: offset }}\n          transition={reduce ? undefined : { duration: 0.9, ease: \"easeOut\" }}\n        />\n      </svg>\n      <span className=\"absolute text-xs font-medium tabular-nums text-foreground\">{Math.round(v)}%</span>\n    </div>\n  )\n}\n\n/* Conic: a token conic-gradient ring; the arc matches the value. It is carved into\n   the ring with a mask. */\nexport function ConicProgress({ className, size = \"md\", value = 60, label = \"Progress\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  const v = clamp(value)\n  return (\n    <div\n      data-slot=\"styled-progress\"\n      role=\"progressbar\"\n      aria-valuenow={v}\n      aria-valuemin={0}\n      aria-valuemax={100}\n      aria-label={label}\n      className={cn(\"relative grid place-items-center\", box[size], className)}\n      {...props}\n    >\n      <motion.div\n        aria-hidden=\"true\"\n        className=\"size-full rounded-full [-webkit-mask:radial-gradient(farthest-side,transparent_calc(100%-6px),var(--color-foreground)_0)] [background:conic-gradient(var(--color-info)_calc(var(--p)*3.6deg),var(--color-secondary)_0)] [mask:radial-gradient(farthest-side,transparent_calc(100%-6px),var(--color-foreground)_0)]\"\n        initial={reduce ? false : ({ \"--p\": 0 } as Record<string, number>)}\n        animate={{ \"--p\": v } as Record<string, number>}\n        transition={reduce ? undefined : { duration: 0.9, ease: \"easeOut\" }}\n      />\n      <span className=\"absolute text-xs font-medium tabular-nums text-foreground\">{Math.round(v)}%</span>\n    </div>\n  )\n}\n\n/* DualRing: ic ice iki yay (dis info, ic success); ikisi de value kadar dolar. */\nexport function DualRingProgress({ className, size = \"md\", value = 60, label = \"Progress\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  const v = clamp(value)\n  const rOuter = 42\n  const rInner = 30\n  const cOuter = 2 * Math.PI * rOuter\n  const cInner = 2 * Math.PI * rInner\n  return (\n    <div\n      data-slot=\"styled-progress\"\n      role=\"progressbar\"\n      aria-valuenow={v}\n      aria-valuemin={0}\n      aria-valuemax={100}\n      aria-label={label}\n      className={cn(\"relative grid place-items-center\", box[size], className)}\n      {...props}\n    >\n      <svg viewBox=\"0 0 100 100\" className=\"size-full -rotate-90\">\n        <circle cx=\"50\" cy=\"50\" r={rOuter} fill=\"none\" strokeWidth=\"7\" className=\"stroke-secondary\" />\n        <circle cx=\"50\" cy=\"50\" r={rInner} fill=\"none\" strokeWidth=\"7\" className=\"stroke-secondary\" />\n        <motion.circle\n          cx=\"50\"\n          cy=\"50\"\n          r={rOuter}\n          fill=\"none\"\n          strokeWidth=\"7\"\n          strokeLinecap=\"round\"\n          stroke=\"var(--color-info)\"\n          strokeDasharray={cOuter}\n          initial={reduce ? false : { strokeDashoffset: cOuter }}\n          animate={{ strokeDashoffset: cOuter * (1 - v / 100) }}\n          transition={reduce ? undefined : { duration: 0.9, ease: \"easeOut\" }}\n        />\n        <motion.circle\n          cx=\"50\"\n          cy=\"50\"\n          r={rInner}\n          fill=\"none\"\n          strokeWidth=\"7\"\n          strokeLinecap=\"round\"\n          stroke=\"var(--color-success)\"\n          strokeDasharray={cInner}\n          initial={reduce ? false : { strokeDashoffset: cInner }}\n          animate={{ strokeDashoffset: cInner * (1 - v / 100) }}\n          transition={reduce ? undefined : { duration: 0.9, ease: \"easeOut\", delay: 0.1 }}\n        />\n      </svg>\n    </div>\n  )\n}\n\n/* DashRing: cizgili (dashed) halka; info yay dolarken ustteki token dash gaplari oyar. */\nexport function DashRingProgress({ className, size = \"md\", value = 60, label = \"Progress\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  const v = clamp(value)\n  return (\n    <div\n      data-slot=\"styled-progress\"\n      role=\"progressbar\"\n      aria-valuenow={v}\n      aria-valuemin={0}\n      aria-valuemax={100}\n      aria-label={label}\n      className={cn(\"relative grid place-items-center\", box[size], className)}\n      {...props}\n    >\n      <svg viewBox=\"0 0 100 100\" className=\"size-full -rotate-90\">\n        <motion.circle\n          cx=\"50\"\n          cy=\"50\"\n          r={R}\n          fill=\"none\"\n          strokeWidth=\"8\"\n          strokeLinecap=\"butt\"\n          stroke=\"var(--color-info)\"\n          pathLength={100}\n          initial={reduce ? false : { strokeDasharray: \"0 100\" }}\n          animate={{ strokeDasharray: `${v} 100` }}\n          transition={reduce ? undefined : { duration: 0.9, ease: \"easeOut\" }}\n        />\n        <circle\n          cx=\"50\"\n          cy=\"50\"\n          r={R}\n          fill=\"none\"\n          strokeWidth=\"9\"\n          strokeDasharray=\"3 4\"\n          className=\"stroke-secondary\"\n        />\n      </svg>\n    </div>\n  )\n}\n\n/* GlowRing: info yay + token drop-shadow parlama; ortada % metni. */\nexport function GlowRingProgress({ className, size = \"md\", value = 60, label = \"Progress\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  const v = clamp(value)\n  const offset = C * (1 - v / 100)\n  return (\n    <div\n      data-slot=\"styled-progress\"\n      role=\"progressbar\"\n      aria-valuenow={v}\n      aria-valuemin={0}\n      aria-valuemax={100}\n      aria-label={label}\n      className={cn(\"relative grid place-items-center\", box[size], className)}\n      {...props}\n    >\n      <svg viewBox=\"0 0 100 100\" className=\"size-full -rotate-90\">\n        <circle cx=\"50\" cy=\"50\" r={R} fill=\"none\" strokeWidth=\"8\" className=\"stroke-secondary\" />\n        <motion.circle\n          cx=\"50\"\n          cy=\"50\"\n          r={R}\n          fill=\"none\"\n          strokeWidth=\"8\"\n          strokeLinecap=\"round\"\n          stroke=\"var(--color-info)\"\n          className=\"[filter:drop-shadow(0_0_4px_color-mix(in_oklab,var(--color-info)_70%,transparent))]\"\n          strokeDasharray={C}\n          initial={reduce ? false : { strokeDashoffset: C }}\n          animate={{ strokeDashoffset: offset }}\n          transition={reduce ? undefined : { duration: 0.9, ease: \"easeOut\" }}\n        />\n      </svg>\n      <span className=\"absolute text-xs font-medium tabular-nums text-foreground\">{Math.round(v)}%</span>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/progress-circular.tsx"
    }
  ],
  "categories": [
    "styled",
    "progress"
  ],
  "type": "registry:component"
}