47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import { useState } from "react"
|
|
import { Star } from "lucide-react"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
|
|
interface RatingProps {
|
|
value: number
|
|
onChange?: (value: number) => void
|
|
readonly?: boolean
|
|
className?: string
|
|
size?: "sm" | "md"
|
|
}
|
|
|
|
export function StarRating({ value, onChange, readonly = false, className, size = "md" }: RatingProps) {
|
|
const [hoverValue, setHoverValue] = useState<number | null>(null)
|
|
|
|
const stars = Array.from({ length: 10 }, (_, i) => i + 1)
|
|
const starSize = size === "sm" ? "h-3 w-3" : "h-4 w-4"
|
|
|
|
return (
|
|
<div className={cn("flex gap-0.5", className)}>
|
|
{stars.map((star) => (
|
|
<button
|
|
key={star}
|
|
type="button"
|
|
disabled={readonly}
|
|
className={cn(
|
|
"p-0.5 transition-colors",
|
|
readonly ? "cursor-default" : "cursor-pointer hover:scale-110"
|
|
)}
|
|
onMouseEnter={() => !readonly && setHoverValue(star)}
|
|
onMouseLeave={() => !readonly && setHoverValue(null)}
|
|
onClick={() => !readonly && onChange?.(star)}
|
|
>
|
|
<Star
|
|
className={cn(
|
|
starSize,
|
|
(hoverValue !== null ? star <= hoverValue : star <= value)
|
|
? "fill-primary text-primary"
|
|
: "fill-muted text-muted-foreground"
|
|
)}
|
|
/>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|