{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "star-rating",
  "title": "Star Rating",
  "description": "A component that displays ratings with stars and an optional label. This component builds trust with visual feedback.",
  "files": [
    {
      "path": "components/shadcncraft/pro-marketing/star-rating.tsx",
      "content": "import * as React from \"react\";\n\nimport { IconPlaceholder } from \"@/registry/icons/icon-placeholder\";\nimport { cn } from \"@/lib/utils\";\n\ntype StarRatingProps = {\n  value: number;\n  max?: number;\n  label?: string;\n  size?: \"sm\" | \"md\" | \"lg\";\n  orientation?: \"vertical\" | \"horizontal\";\n  /**\n   * Precision for partial stars. If set, the component will fill each star\n   * proportionally (e.g., 3.2 -> 60% of the 4th star). Defaults to true.\n   */\n  allowPartial?: boolean;\n  containerClassName?: string;\n};\n\nexport function StarRating({\n  value,\n  max = 5,\n  label,\n  size = \"lg\",\n  orientation = \"vertical\",\n  allowPartial = true,\n  className,\n  containerClassName,\n  ...props\n}: StarRatingProps & Omit<React.ComponentProps<\"div\">, \"children\">) {\n  const safeMax = Math.max(1, Math.floor(max));\n  const clampedValue = Math.max(0, Math.min(value, safeMax));\n\n  return (\n    <div\n      data-size={size}\n      data-orientation={orientation}\n      data-slot=\"star-rating\"\n      className={cn(\n        \"flex flex-col items-center gap-1.5\",\n        \"data-[orientation=horizontal]:flex-row\",\n        \"data-[size=lg]:[--star-size:calc(--spacing(5))] data-[size=md]:[--star-size:calc(--spacing(4))] data-[size=sm]:[--star-size:calc(--spacing(3))]\",\n        containerClassName\n      )}\n      aria-label={`${clampedValue} out of ${safeMax} stars`}\n      role=\"img\"\n      {...props}\n    >\n      <div className={cn(\"flex items-center gap-0.5\", className)}>\n        {Array.from({ length: safeMax }).map((_, index) => {\n          const starIndex = index + 1;\n          const filledRatio = allowPartial\n            ? Math.max(0, Math.min(1, clampedValue - index))\n            : clampedValue >= starIndex\n              ? 1\n              : 0;\n\n          return (\n            <span key={index} className=\"relative inline-flex\" aria-hidden=\"true\">\n              {/* Base (unfilled) star */}\n              <IconPlaceholder\n                lucide=\"Star\"\n                tabler=\"IconStar\"\n                hugeicons=\"StarIcon\"\n                phosphor=\"StarIcon\"\n                remixicon=\"RiStarLine\"\n                className=\"size-(--star-size)\"\n              />\n              {/* Filled overlay */}\n              {filledRatio > 0 ? (\n                <span\n                  className=\"absolute inset-0 overflow-hidden\"\n                  style={{ width: `${filledRatio * 100}%` }}\n                >\n                  <IconPlaceholder\n                    lucide=\"Star\"\n                    tabler=\"IconStar\"\n                    hugeicons=\"StarIcon\"\n                    phosphor=\"StarIcon\"\n                    remixicon=\"RiStarLine\"\n                    fill=\"currentColor\"\n                    className=\"size-(--star-size)\"\n                  />\n                </span>\n              ) : null}\n            </span>\n          );\n        })}\n      </div>\n\n      {label && (\n        <span data-slot=\"star-rating-label\" className=\"text-xs text-muted-foreground\">\n          {label}\n        </span>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/shadcncraft/pro-marketing/star-rating.tsx"
    }
  ],
  "meta": {
    "tier": "free",
    "figmaNodeId": "17897-99964",
    "bundle": "pro-marketing"
  },
  "type": "registry:ui"
}
