{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rating",
  "title": "Rating",
  "description": "stars · display (half stars, `showValue`, `count`) or input with `onChange`",
  "dependencies": [
    "cn@^0.4.0",
    "lucide-react@^1.52.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/kit/rating.tsx",
      "content": "'use client';\nimport { cn } from 'cn';\nimport { StarIcon } from 'lucide-react';\n\ntype Props = {\n  /** 0 to `max`. Shown to the nearest half star. */\n  value: number;\n  /** Makes it an input: each star is a radio button. Omit to display. */\n  onChange?: (value: number) => void;\n  max?: number;\n  /** Display only: the number beside the stars, and how many reviews (`4.6 · 120 reviews`). */\n  showValue?: boolean;\n  count?: number;\n  size?: 'sm' | 'md' | 'lg';\n  /** What is being rated, for screen readers (\"Rate your order\"). Default \"Rating\". */\n  'aria-label'?: string;\n  className?: string;\n};\n\nconst PX = { sm: 'size-3.5', md: 'size-[18px]', lg: 'size-7' } as const;\n\nfunction Star({ size, fraction }: { size: string; fraction: number }) {\n  return (\n    <span className={cn('relative inline-block', size)} aria-hidden>\n      <StarIcon className={cn('text-muted-foreground absolute inset-0', size)} strokeWidth={1.75} />\n      {fraction > 0 ? (\n        <span className=\"absolute inset-y-0 left-0 overflow-hidden\" style={{ width: `${fraction * 100}%` }}>\n          <StarIcon className={cn('text-warning fill-warning', size)} strokeWidth={1.75} />\n        </span>\n      ) : null}\n    </span>\n  );\n}\n\n/**\n * Stars. Without `onChange` it shows a score (half stars, optional number and review count); with `onChange` it is\n * an input whose stars are radio buttons.\n */\nexport function Rating({ value, onChange, max = 5, showValue, count, size, className, ...props }: Props) {\n  const stars = Array.from({ length: max }, (_, i) => i + 1);\n  if (onChange) {\n    const px = PX[size ?? 'lg'];\n    return (\n      <div role=\"radiogroup\" aria-label={props['aria-label'] ?? 'Rating'} className={cn('flex', className)}>\n        {stars.map((n) => (\n          <button\n            key={n}\n            type=\"button\"\n            role=\"radio\"\n            aria-checked={value === n}\n            aria-label={`${n} ${n === 1 ? 'star' : 'stars'}`}\n            onClick={() => onChange(n)}\n            className=\"focus-visible:ring-ring/50 flex size-11 items-center justify-center rounded-md outline-none hover:opacity-80 focus-visible:ring-[3px]\">\n            <Star size={px} fraction={n <= value ? 1 : 0} />\n          </button>\n        ))}\n      </div>\n    );\n  }\n  const px = PX[size ?? 'sm'];\n  const half = Math.round(value * 2) / 2;\n  return (\n    <div role=\"img\" aria-label={`Rated ${value} out of ${max}${count ? `, ${count} reviews` : ''}`} className={cn('flex items-center gap-1.5', className)}>\n      <span className=\"flex gap-0.5\">\n        {stars.map((n) => (\n          <Star key={n} size={px} fraction={Math.max(0, Math.min(1, half - (n - 1)))} />\n        ))}\n      </span>\n      {showValue || count ? (\n        <span className={cn('text-muted-foreground', size === 'md' || size === 'lg' ? 'text-sm' : 'text-xs')}>\n          {[showValue ? value.toFixed(1) : null, count ? `${count} reviews` : null].filter(Boolean).join(' · ')}\n        </span>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/kit/rating.tsx"
    }
  ],
  "meta": {
    "exports": [
      "Rating"
    ],
    "api": "<Rating value onChange? showValue? count? size? aria-label? />"
  },
  "docs": "Needs the kit theme (`@kit/theme`) and a next-themes ThemeProvider (attribute=\"class\") at the root.",
  "categories": [
    "inputs"
  ],
  "type": "registry:component"
}