{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rating",
  "title": "Rating",
  "description": "Stars fill one after another from the left, and the chosen star pops.",
  "dependencies": [
    "@ui-sdk/core@0.1.0-beta.2",
    "@ui-sdk/react@0.1.0-beta.2"
  ],
  "registryDependencies": [
    "@keyframery/motion"
  ],
  "files": [
    {
      "path": "registry/components/keyframery/primitives/rating.tsx",
      "content": "\"use client\"\n\n/* Animated drop-in for `@ui-sdk/react/rating`: same parts, same props. Swap the import and keep your code.\n   This file adds motion only. Colours, radii, shadows and spacing stay yours. */\n\nimport { Rating as RatingRoot, RatingItem as RatingItemPart } from \"@ui-sdk/react/rating\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport * from \"@ui-sdk/react/rating\"\n\n/* UI SDK accepts a class name or a function of state; keep both working. */\nfunction withClass<S>(base: string, className: string | ((state: S) => string | undefined) | undefined) {\n  return typeof className === \"function\" ? (state: S) => cn(base, className(state)) : cn(base, className)\n}\n\n/* Registering --rating-fill makes it animatable, so anything you paint from it (a clip, a\n   gradient, a mask) fills smoothly with no extra work. */\nconst css =\n  `@property --rating-fill{syntax:\"<number>\";inherits:true;initial-value:0}` +\n  `@keyframes kf-rating-pop{0%{scale:.55}100%{scale:1}}`\n\nfunction Rating(props: React.ComponentProps<typeof RatingRoot>) {\n  return (\n    <>\n      <style href=\"kf-rating\" precedence=\"default\">\n        {css}\n      </style>\n      <RatingRoot {...props} />\n    </>\n  )\n}\n\n/* Cascade: stars fill one after another from the left, 45ms apart; emptying is immediate.\n   The star a score lands on pops in from 0.55 with an overshoot. */\nfunction RatingItem({ className, ...props }: React.ComponentProps<typeof RatingItemPart>) {\n  return (\n    <RatingItemPart\n      className={withClass(\n        cn(\n          \"[transition:--rating-fill_calc(300ms*var(--kf-speed,1))_var(--ease-house)]\",\n          \"data-filled:[transition-delay:calc((min(sibling-index(),10)-1)*calc(45ms*var(--kf-speed,1)))]\",\n          \"data-partial:[transition-delay:calc((min(sibling-index(),10)-1)*calc(45ms*var(--kf-speed,1)))]\",\n          \"not-data-readonly:data-checked:animate-[kf-rating-pop_calc(520ms*var(--kf-speed,1))_var(--ease-pop)_both]\",\n          \"motion-reduce:[transition:none]! motion-reduce:animate-none!\",\n        ),\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Rating, RatingItem }\n",
      "type": "registry:component",
      "target": "components/keyframery/primitives/rating.tsx"
    },
    {
      "path": "registry/components/keyframery/rating.tsx",
      "content": "\"use client\"\n\n/* Styled rating: the animated primitive plus the Keyframery look. */\n\nimport * as React from \"react\"\n\nimport { Rating as RatingRoot, RatingItem as RatingItemPart } from \"@/components/keyframery/primitives/rating\"\nimport { cn } from \"@/lib/utils\"\n\nfunction Rating({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof RatingRoot>) {\n  return (\n    <RatingRoot\n      className={cn(\n        \"inline-flex items-center gap-0.5 outline-none\",\n        \"data-disabled:opacity-50\",\n        className as string,\n      )}\n      {...props}\n    >\n      {children ?? <RatingItem />}\n    </RatingRoot>\n  )\n}\n\nconst starPath =\n  \"M8 1.9l1.83 3.72 4.1.6-2.97 2.89.7 4.08L8 11.26l-3.66 1.93.7-4.08L2.07 6.22l4.1-.6L8 1.9z\"\n\nfunction Star({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 16 16\" aria-hidden className={cn(\"absolute inset-0 size-full\", className)}>\n      <path d={starPath} fill=\"currentColor\" stroke=\"currentColor\" strokeWidth=\"1.25\" strokeLinejoin=\"round\" />\n    </svg>\n  )\n}\n\n/* Each star is an empty star with a filled copy on top, clipped to --rating-fill (which the\n   primitive animates, star by star from the left).\n   The fill wipes in from the left; stars a pointer preview adds\n   beyond the current score fill in a lighter tone. */\nfunction RatingItem({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof RatingItemPart>) {\n  return (\n    <RatingItemPart\n      className={cn(\n        \"group/star relative grid size-6 shrink-0 place-items-center rounded-[6px] outline-none\",\n        \"cursor-pointer data-disabled:cursor-not-allowed data-readonly:cursor-default\",\n        \"has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring has-[:focus-visible]:ring-offset-1 has-[:focus-visible]:ring-offset-background\",\n        className as string,\n      )}\n      {...props}\n    >\n      {children ?? (\n        <span aria-hidden className=\"pointer-events-none relative block size-5\">\n          <Star className=\"text-muted-foreground/25 dark:text-muted-foreground/30\" />\n          <Star\n            className={cn(\n              \"text-foreground [clip-path:inset(0_calc((1-var(--rating-fill,0))*100%)_0_0)]\",\n              /* Lighter only for stars the preview adds beyond the current score. */\n              \"[[data-checked]~[data-highlighted]_&]:text-foreground/50\",\n              \"[[data-slot=rating]:not(:has([data-checked]))_[data-highlighted]_&]:text-foreground/50\",\n              \"transition-colors duration-[calc(150ms*var(--kf-speed,1))] motion-reduce:transition-none!\",\n            )}\n          />\n        </span>\n      )}\n    </RatingItemPart>\n  )\n}\n\nexport { Rating, RatingItem }\n",
      "type": "registry:component",
      "target": "components/keyframery/rating.tsx"
    }
  ],
  "type": "registry:component"
}