返回 AiToEarn
star-rating.tsx
根目录 / project / aitoearn-web / src / components / ui / star-rating.tsx
1 /**
2 * StarRating - 星星评分组件
3 * 功能:支持悬停预览、点击选择评分,可配置尺寸和交互模式
4 */
5
6 'use client'
7
8 import { Star } from 'lucide-react'
9 import { useState } from 'react'
10 import { cn } from '@/utils/className'
11
12 /** 尺寸映射 */
13 const sizeMap = {
14 sm: 'w-5 h-5',
15 md: 'w-6 h-6',
16 lg: 'w-8 h-8',
17 }
18
19 export interface StarRatingProps {
20 /** 当前评分值 */
21 value?: number | null
22 /** 评分变化回调 */
23 onChange?: (value: number) => void
24 /** 星星总数,默认 5 */
25 count?: number
26 /** 星星尺寸,默认 'md' */
27 size?: 'sm' | 'md' | 'lg'
28 /** 是否禁用交互 */
29 disabled?: boolean
30 /** 是否只读(仅展示,无悬停效果) */
31 readOnly?: boolean
32 /** 自定义类名 */
33 className?: string
34 }
35
36 export function StarRating({
37 value = null,
38 onChange,
39 count = 5,
40 size = 'md',
41 disabled = false,
42 readOnly = false,
43 className,
44 }: StarRatingProps) {
45 const [hoverValue, setHoverValue] = useState<number | null>(null)
46
47 // 显示的评分:悬停时用 hoverValue,否则用实际 value
48 const displayValue = hoverValue ?? value
49 // 是否可交互
50 const isInteractive = !disabled && !readOnly && !!onChange
51
52 return (
53 <div
54 className={cn('flex items-center gap-1', className)}
55 onMouseLeave={() => isInteractive && setHoverValue(null)}
56 >
57 {Array.from({ length: count }).map((_, idx) => {
58 const starValue = idx + 1
59 const isActive = displayValue !== null && displayValue >= starValue
60
61 return (
62 <button
63 key={starValue}
64 type="button"
65 disabled={disabled}
66 onClick={() => isInteractive && onChange?.(starValue)}
67 onMouseEnter={() => isInteractive && setHoverValue(starValue)}
68 className={cn(
69 'p-1 rounded transition-colors',
70 isInteractive && 'cursor-pointer hover:bg-muted',
71 isActive ? 'text-amber-400' : 'text-muted-foreground',
72 disabled && 'cursor-not-allowed opacity-50',
73 )}
74 aria-label={`${starValue} star`}
75 >
76 <Star className={sizeMap[size]} fill={isActive ? 'currentColor' : 'none'} />
77 </button>
78 )
79 })}
80 </div>
81 )
82 }
83
84 export default StarRating
85
85 lines Plain Text