42 lines
1.4 KiB
TypeScript
42 lines
1.4 KiB
TypeScript
import { Star } from "lucide-react";
|
|
|
|
interface RatingStarsProps {
|
|
value: number;
|
|
max?: number;
|
|
size?: "sm" | "md" | "lg";
|
|
interactive?: boolean;
|
|
onChange?: (value: number) => void;
|
|
}
|
|
|
|
export function RatingStars({ value, max = 5, size = "md", interactive = false, onChange }: RatingStarsProps) {
|
|
const sizeClasses = {
|
|
sm: "w-3 h-3",
|
|
md: "w-5 h-5",
|
|
lg: "w-7 h-7"
|
|
};
|
|
|
|
return (
|
|
<div className="flex items-center gap-0.5" data-testid="rating-stars">
|
|
{Array.from({ length: max }).map((_, i) => {
|
|
const starValue = i + 1;
|
|
const isFilled = value >= starValue;
|
|
|
|
return (
|
|
<button
|
|
key={i}
|
|
type="button"
|
|
disabled={!interactive}
|
|
onClick={() => interactive && onChange?.(starValue)}
|
|
className={`${interactive ? "cursor-pointer hover:scale-110 transition-transform" : "cursor-default"} bg-transparent border-0 p-0`}
|
|
data-testid={`star-${starValue}`}
|
|
>
|
|
<Star
|
|
className={`${sizeClasses[size]} ${isFilled ? "fill-primary text-primary [filter:drop-shadow(0_0_4px_hsl(var(--primary)/0.45))]" : "text-muted-foreground/30"} transition-colors group-hover:brightness-110 ${interactive ? "hover:[filter:drop-shadow(0_0_6px_hsl(var(--glow-1)/0.6))]" : ""}`}
|
|
/>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|