{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "rating",
  "title": "Rating",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/rating/Rating.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nexport interface RatingProps {\n  /** Currently selected value */\n  modelValue?: number\n  /** Maximum rating value */\n  max?: number\n  /** If true, prevents user interaction */\n  readonly?: boolean\n  /** If true, disables the rating */\n  disabled?: boolean\n  /** Density of the component */\n  density?: 'compact' | 'default' | 'comfortable'\n  /** Color of the selected stars */\n  color?: string\n  /** If true, clicking the same value clears the rating */\n  clearable?: boolean\n  /** If true, stars grow on hover */\n  hover?: boolean\n  /** ARIA label for each rating item */\n  itemAriaLabel?: string\n  /** Icon shown for empty stars */\n  emptyIcon?: string\n  /** Icon shown for full stars */\n  fullIcon?: string\n  /** Icon shown for half stars */\n  halfIcon?: string\n  /** If true, allows fractional values */\n  float?: boolean\n  /** Size of the stars */\n  size?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'\n  /** Custom class for the component */\n  class?: HTMLAttributes['class']\n  /** Show rating count (placeholder for future) */\n  showValue?: boolean\n  /** Card variant styling */\n  variant?: 'outlined' | 'filled' | 'soft'\n  /** If true, creates a half star at 0.5 */\n  halfIncrements?: boolean\n  /** If true, displays tooltips on hover */\n  tooltips?: string[]\n  /** Icon set to use ('mdi' | 'heroicons' | 'lucide') */\n  iconSet?: 'mdi' | 'heroicons' | 'lucide'\n}\n\nconst props = withDefaults(defineProps<RatingProps>(), {\n  modelValue: 0,\n  max: 5,\n  readonly: false,\n  disabled: false,\n  density: 'default',\n  color: 'var(--warning)',\n  clearable: false,\n  hover: false,\n  itemAriaLabel: 'rating',\n  emptyIcon: 'mdi-star-outline',\n  fullIcon: 'mdi-star',\n  halfIcon: 'mdi-star-half-full',\n  float: false,\n  size: 'medium',\n  showValue: false,\n  variant: 'outlined',\n  halfIncrements: false,\n  iconSet: 'mdi',\n})\n\nconst emit = defineEmits<{\n  'update:modelValue': [value: number]\n}>()\n\nconst densityClasses = {\n  compact: 'rating-density-compact',\n  default: 'rating-density-default',\n  comfortable: 'rating-density-comfortable',\n}\n\nconst variantClasses = {\n  outlined: 'rating-variant-outlined',\n  filled: 'rating-variant-filled',\n  soft: 'rating-variant-soft',\n}\n\nconst sizeClasses = {\n  'x-small': 'rating-size-xs',\n  small: 'rating-size-sm',\n  medium: 'rating-size-md',\n  large: 'rating-size-lg',\n  'x-large': 'rating-size-xl',\n}\n\nconst componentClasses = computed(() => [\n  'rating',\n  densityClasses[props.density],\n  variantClasses[props.variant],\n  sizeClasses[props.size],\n  {\n    'rating-readonly': props.readonly,\n    'rating-disabled': props.disabled,\n    'rating-hover': props.hover,\n    'rating-clearable': props.clearable,\n    'rating-show-value': props.showValue,\n  },\n  props.class,\n])\n\nfunction handleClick(value: number) {\n  if (props.disabled || props.readonly) return\n  if (props.clearable && value === props.modelValue) {\n    emit('update:modelValue', 0)\n  } else {\n    emit('update:modelValue', value)\n  }\n}\n\nfunction handleKeydown(event: KeyboardEvent, value: number) {\n  if (event.key === 'Enter' || event.key === ' ') {\n    event.preventDefault()\n    handleClick(value)\n  } else if (event.key === 'ArrowRight' && value < props.max) {\n    emit('update:modelValue', value + 1)\n  } else if (event.key === 'ArrowLeft' && value > 1) {\n    emit('update:modelValue', value - 1)\n  }\n}\n\nfunction getStarClass(index: number): string[] {\n  const value = props.modelValue\n  const classes = ['rating-star']\n\n  if (value >= index + 1) {\n    classes.push('rating-star-full')\n  } else if (value >= index + 0.5 && props.halfIncrements) {\n    classes.push('rating-star-half')\n  } else {\n    classes.push('rating-star-empty')\n  }\n\n  return classes\n}\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"rating\"\n    :class=\"cn(...componentClasses)\"\n    role=\"radiogroup\"\n    :aria-valuenow=\"modelValue\"\n    :aria-valuemin=\"0\"\n    :aria-valuemax=\"max\"\n    :aria-label=\"`Rating: ${modelValue} of ${max}`\"\n  >\n    <button\n      v-for=\"n in max\"\n      :key=\"n\"\n      type=\"button\"\n      class=\"focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none\"\n      :class=\"getStarClass(n - 1)\"\n      :disabled=\"disabled || readonly\"\n      :aria-label=\"`${itemAriaLabel} ${n} of ${max}`\"\n      :tabindex=\"readonly || disabled ? -1 : 0\"\n      @click=\"handleClick(n)\"\n      @keydown=\"handleKeydown($event, n)\"\n    >\n      <!-- Full Star Icon -->\n      <svg\n        v-if=\"modelValue >= n\"\n        class=\"star-icon\"\n        viewBox=\"0 0 24 24\"\n        fill=\"currentColor\"\n        :style=\"{ color: props.color }\"\n      >\n        <path d=\"M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z\" />\n      </svg>\n      <!-- Half Star Icon -->\n      <svg\n        v-else-if=\"modelValue >= n - 0.5 && halfIncrements\"\n        class=\"star-icon star-half\"\n        viewBox=\"0 0 24 24\"\n        :style=\"{ color: props.color }\"\n      >\n        <defs>\n          <linearGradient :id=\"`half-${n}`\">\n            <stop offset=\"50%\" stop-color=\"currentColor\" />\n            <stop offset=\"50%\" stop-color=\"transparent\" />\n          </linearGradient>\n        </defs>\n        <path\n          d=\"M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z\"\n          :fill=\"`url(#half-${n})`\"\n          stroke=\"currentColor\"\n          stroke-width=\"1\"\n        />\n      </svg>\n      <!-- Empty Star Icon -->\n      <svg v-else class=\"star-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n        <path d=\"M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z\" />\n      </svg>\n    </button>\n    <span v-if=\"showValue\" class=\"rating-value\">{{ modelValue }}</span>\n  </div>\n</template>\n\n<style scoped>\n.rating {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.125rem;\n}\n\n.rating-readonly {\n  cursor: default;\n}\n\n.rating-disabled {\n  cursor: not-allowed;\n  opacity: 0.5;\n}\n\n.rating-hover .rating-star:hover {\n  transform: scale(1.15);\n}\n\n.rating-hover .rating-star {\n  transition: transform 0.15s ease-in-out;\n}\n\n.rating-clearable .rating-star {\n  cursor: pointer;\n}\n\n.rating-variant-filled {\n  background: var(--muted);\n  padding: 0.25rem;\n  border-radius: 0.5rem;\n}\n\n.rating-variant-soft {\n  background: var(--accent);\n  padding: 0.25rem;\n  border-radius: 0.5rem;\n}\n\n.rating-star {\n  background: none;\n  border: none;\n  padding: 0.125rem;\n  line-height: 1;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.rating-star:focus-visible {\n  outline: 2px solid v-bind(color);\n  outline-offset: 2px;\n  border-radius: 2px;\n}\n\n/* Density */\n.rating-density-compact .rating-star {\n  padding: 0;\n}\n\n.rating-density-default .rating-star {\n  padding: 0.0625rem;\n}\n\n.rating-density-comfortable .rating-star {\n  padding: 0.125rem;\n}\n\n/* Sizes */\n.rating-size-xs .star-icon {\n  width: 0.875rem;\n  height: 0.875rem;\n}\n\n.rating-size-sm .star-icon {\n  width: 1.125rem;\n  height: 1.125rem;\n}\n\n.rating-size-md .star-icon {\n  width: 1.375rem;\n  height: 1.375rem;\n}\n\n.rating-size-lg .star-icon {\n  width: 1.625rem;\n  height: 1.625rem;\n}\n\n.rating-size-xl .star-icon {\n  width: 2rem;\n  height: 2rem;\n}\n\n.rating-star-empty {\n  color: var(--muted-foreground);\n}\n\n.rating-star-full {\n  color: v-bind(color);\n}\n\n.rating-star-half .star-icon {\n  position: relative;\n}\n\n.rating-value {\n  margin-left: 0.5rem;\n  font-weight: 600;\n  color: var(--foreground);\n}\n\n.rating-show-value {\n  display: flex;\n  align-items: center;\n  gap: 0.25rem;\n}\n</style>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/rating/Rating.vue"
    },
    {
      "path": "packages/registry-vue/components/rating/index.ts",
      "content": "export { default as Rating } from './Rating.vue'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/rating/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Star (or custom icon) rating control — pick a value from 1 to N. Read-only mode for displaying review averages, with half-step support.",
  "categories": [
    "form"
  ]
}