{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "circular-progress",
  "title": "Circular Progress",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/circular-progress/CircularProgress.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { circularProgressVariants } from './circular-progress.variants'\n\n// Inlined union: SFC compiler can't extract runtime props from\n// `CircularProgressVariants['size']`.\nconst props = withDefaults(\n  defineProps<{\n    class?: HTMLAttributes['class']\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  {\n    value: 0,\n    size: 'default',\n    thickness: 8,\n    indeterminate: false,\n    showValue: false,\n    suffix: '%',\n    ariaLabel: 'Progress',\n  },\n)\n\nconst sizePx = computed(() => {\n  if (typeof props.size === 'number') return props.size\n  switch (props.size) {\n    case 'sm':\n      return 40\n    case 'lg':\n      return 80\n    default:\n      return 56\n  }\n})\n\nconst normalizedValue = computed(() => Math.min(100, Math.max(0, props.value)))\n\nconst radius = computed(() => (sizePx.value - props.thickness) / 2)\nconst circumference = computed(() => 2 * Math.PI * radius.value)\nconst strokeDashoffset = computed(() => {\n  if (props.indeterminate) return circumference.value * 0.25\n  return circumference.value * (1 - normalizedValue.value / 100)\n})\n\nconst resolvedColor = computed(() => props.color || 'var(--primary)')\nconst resolvedTrackColor = computed(() => props.trackColor || 'var(--muted)')\n\nconst viewBox = computed(() => `0 0 ${sizePx.value} ${sizePx.value}`)\nconst center = computed(() => sizePx.value / 2)\n\nconst fontSize = computed(() => {\n  const s = sizePx.value\n  if (s <= 40) return 'text-xs'\n  if (s <= 56) return 'text-sm'\n  return 'text-base'\n})\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"circular-progress\"\n    :data-size=\"typeof size === 'string' ? size : 'custom'\"\n    :data-indeterminate=\"indeterminate ? 'true' : 'false'\"\n    :class=\"cn(circularProgressVariants(), props.class)\"\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  >\n    <svg :width=\"sizePx\" :height=\"sizePx\" :viewBox=\"viewBox\" class=\"block\">\n      <!-- Track -->\n      <circle\n        :cx=\"center\"\n        :cy=\"center\"\n        :r=\"radius\"\n        fill=\"none\"\n        :stroke=\"resolvedTrackColor\"\n        :stroke-width=\"thickness\"\n      />\n      <!-- Progress arc -->\n      <g\n        :transform=\"indeterminate ? undefined : `rotate(-90 ${center} ${center})`\"\n        :class=\"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          :stroke-width=\"thickness\"\n          stroke-linecap=\"round\"\n          :stroke-dasharray=\"circumference\"\n          :stroke-dashoffset=\"strokeDashoffset\"\n          :class=\"!indeterminate ? 'transition-[stroke-dashoffset] duration-500 ease-out' : ''\"\n        />\n      </g>\n    </svg>\n\n    <div v-if=\"showValue || $slots.default\" class=\"absolute inset-0 flex items-center justify-center\">\n      <slot :value=\"normalizedValue\">\n        <span v-if=\"showValue\" :class=\"cn('text-foreground font-medium tabular-nums', fontSize)\">\n          {{ Math.round(normalizedValue) }}{{ suffix }}\n        </span>\n      </slot>\n    </div>\n  </div>\n</template>\n\n<style scoped>\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",
      "type": "registry:ui",
      "target": "~/app/components/ui/circular-progress/CircularProgress.vue"
    },
    {
      "path": "packages/registry-vue/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.vue` 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": "~/app/components/ui/circular-progress/circular-progress.variants.ts"
    },
    {
      "path": "packages/registry-vue/components/circular-progress/index.ts",
      "content": "export { default as CircularProgress } from './CircularProgress.vue'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// CircularProgress.vue <-> index.ts circular import that broke dev SSR for Card).\nexport { circularProgressVariants, type CircularProgressVariants } from './circular-progress.variants'\n",
      "type": "registry:ui",
      "target": "~/app/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"
  ]
}