{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rating-group-advanced",
  "title": "Rating Group Advanced",
  "description": "Advanced rating group components with half-star, heart, read-only, and custom icon variants.",
  "registryDependencies": [
    "toggle-group"
  ],
  "files": [
    {
      "path": "registry/rating-group/rating-group-advanced.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { StarIcon, HeartIcon } from \"lucide-react\";\nimport { ToggleGroup, ToggleGroupItem } from \"@/components/ui/toggle-group\";\nimport { cn } from \"@/lib/utils\";\n\ninterface RatingGroupAdvancedProps {\n  value?: string;\n  onValueChange?: (value: string) => void;\n  max?: number;\n  className?: string;\n  disabled?: boolean;\n  readOnly?: boolean;\n  size?: \"sm\" | \"default\" | \"lg\";\n  variant?: \"star\" | \"heart\";\n  allowHalf?: boolean;\n  emptyIcon?: React.ComponentType<{ className?: string }>;\n  filledIcon?: React.ComponentType<{ className?: string }>;\n  colors?: {\n    filled?: string;\n    empty?: string;\n    hover?: string;\n  };\n  allowClear?: boolean;\n}\n\nconst defaultColors = {\n  filled: \"fill-yellow-400 text-yellow-400\",\n  empty: \"text-muted-foreground/50\",\n  hover: \"text-yellow-300\",\n};\n\nconst iconVariants = {\n  star: StarIcon,\n  heart: HeartIcon,\n};\n\nfunction RatingGroupAdvanced({\n  value = \"0\",\n  onValueChange,\n  max = 5,\n  className,\n  disabled = false,\n  readOnly = false,\n  size = \"default\",\n  variant = \"star\",\n  allowHalf = false,\n  emptyIcon,\n  filledIcon,\n  colors = defaultColors,\n  allowClear = false,\n  ...props\n}: RatingGroupAdvancedProps) {\n  const [hoveredValue, setHoveredValue] = React.useState<number | null>(null);\n\n  const currentValue = React.useMemo(() => parseFloat(value || \"0\"), [value]);\n  const displayValue = hoveredValue ?? currentValue;\n\n  const EmptyIcon = emptyIcon || iconVariants[variant];\n  const FilledIcon = filledIcon || iconVariants[variant];\n\n  const indices = React.useMemo(\n    () => Array.from({ length: max }, (_, i) => i + 1),\n    [max],\n  );\n\n  const handleMouseEnter = React.useCallback(\n    (starValue: number, isHalf?: boolean) => {\n      if (!disabled && !readOnly) {\n        const finalValue = allowHalf && isHalf ? starValue - 0.5 : starValue;\n        setHoveredValue(finalValue);\n      }\n    },\n    [disabled, readOnly, allowHalf],\n  );\n\n  const handleMouseLeave = React.useCallback(() => {\n    setHoveredValue(null);\n  }, []);\n\n  const handleClick = React.useCallback(\n    (starValue: number, isHalf?: boolean) => {\n      if (!disabled && !readOnly && onValueChange) {\n        const finalValue = allowHalf && isHalf ? starValue - 0.5 : starValue;\n        const newValue =\n          allowClear && currentValue === finalValue\n            ? \"0\"\n            : finalValue.toString();\n        onValueChange(newValue);\n      }\n    },\n    [disabled, readOnly, onValueChange, allowHalf, allowClear, currentValue],\n  );\n\n  const getIconState = (index: number) => {\n    const value = displayValue;\n    if (value >= index) return \"filled\";\n    if (allowHalf && value >= index - 0.5) return \"half\";\n    return \"empty\";\n  };\n\n  const sizeClasses = {\n    sm: \"size-4\",\n    default: \"size-5\",\n    lg: \"size-6\",\n  };\n\n  return (\n    <div className=\"relative\">\n      <ToggleGroup\n        value={[value]}\n        onValueChange={(groupValue) =>\n          onValueChange?.(groupValue[groupValue.length - 1] || \"0\")\n        }\n        size={size}\n        className={cn(\"gap-0\", className)}\n        disabled={disabled}\n        onMouseLeave={handleMouseLeave}\n        {...props}\n      >\n        {indices.map((index) => {\n          const starValue = index.toString();\n          const iconState = getIconState(index);\n\n          return (\n            <div key={starValue} className=\"relative\">\n              <ToggleGroupItem\n                value={starValue}\n                aria-label={`${index} rating`}\n                className={cn(\n                  \"relative border-0 bg-transparent p-0 hover:bg-transparent data-pressed:bg-transparent focus-visible:ring-0\",\n                  \"hover:scale-110 focus-visible:scale-110 transition-transform ease-out\",\n                  (disabled || readOnly) && \"pointer-events-none opacity-50\",\n                )}\n                disabled={disabled || readOnly}\n              >\n                {allowHalf ? (\n                  <div className=\"relative\">\n                    <div\n                      className=\"absolute inset-0 w-1/2 z-10 cursor-pointer\"\n                      onMouseEnter={() => handleMouseEnter(index, true)}\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        handleClick(index, true);\n                      }}\n                    />\n                    <div\n                      className=\"absolute inset-0 left-1/2 w-1/2 z-10 cursor-pointer\"\n                      onMouseEnter={() => handleMouseEnter(index, false)}\n                      onClick={(e) => {\n                        e.stopPropagation();\n                        handleClick(index, false);\n                      }}\n                    />\n                    <div className=\"relative\">\n                      {iconState === \"half\" ? (\n                        <div className=\"relative\">\n                          <EmptyIcon\n                            className={cn(\n                              \"transition-colors ease-out\",\n                              sizeClasses[size],\n                              colors.empty,\n                            )}\n                          />\n                          <div className=\"absolute inset-0 overflow-hidden w-1/2\">\n                            <FilledIcon\n                              className={cn(\n                                \"transition-colors ease-out\",\n                                sizeClasses[size],\n                                colors.filled,\n                              )}\n                            />\n                          </div>\n                        </div>\n                      ) : (\n                        <FilledIcon\n                          className={cn(\n                            \"transition-colors ease-out\",\n                            sizeClasses[size],\n                            iconState === \"filled\"\n                              ? colors.filled\n                              : colors.empty,\n                          )}\n                        />\n                      )}\n                    </div>\n                  </div>\n                ) : (\n                  <div onMouseEnter={() => handleMouseEnter(index)}>\n                    <FilledIcon\n                      className={cn(\n                        \"transition-colors ease-out\",\n                        sizeClasses[size],\n                        iconState === \"filled\" ? colors.filled : colors.empty,\n                      )}\n                    />\n                  </div>\n                )}\n              </ToggleGroupItem>\n            </div>\n          );\n        })}\n      </ToggleGroup>\n    </div>\n  );\n}\n\nexport { RatingGroupAdvanced };\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}