|
1 | | -import React from 'react'; |
2 | | -import { FaStar, FaStarHalfAlt, FaRegStar } from 'react-icons/fa'; |
3 | | - |
4 | | -interface RatingProps { value: number; max?: number; size?: 'sm' | 'md' | 'lg'; showValue?: boolean; count?: number; className?: string; onChange?: (value: number) => void; } |
5 | | -const sizes = { sm: 'text-sm', md: 'text-lg', lg: 'text-2xl' }; |
6 | | - |
7 | | -const Rating: React.FC<RatingProps> = ({ value, max = 5, size = 'md', showValue, count, className = '', onChange }) => { |
8 | | - const stars = Array.from({ length: max }, (_, i) => { |
9 | | - if (i + 1 <= Math.floor(value)) return 'full'; |
10 | | - if (i + 0.5 <= value) return 'half'; |
11 | | - return 'empty'; |
12 | | - }); |
13 | | - |
| 1 | +import React, { useState } from 'react'; |
| 2 | +import { FaStar } from 'react-icons/fa'; |
| 3 | +interface Props { value: number; onChange?: (value: number) => void; max?: number; size?: 'sm' | 'md' | 'lg'; readonly?: boolean; } |
| 4 | +const sizes = { sm: 'text-sm', md: 'text-xl', lg: 'text-3xl' }; |
| 5 | +const Rating: React.FC<Props> = ({ value, onChange, max = 5, size = 'md', readonly = false }) => { |
| 6 | + const [hover, setHover] = useState(0); |
14 | 7 | return ( |
15 | | - <div className={`inline-flex items-center gap-1 ${className}`}> |
16 | | - {stars.map((type, i) => { |
17 | | - const Icon = type === 'full' ? FaStar : type === 'half' ? FaStarHalfAlt : FaRegStar; |
18 | | - return <Icon key={i} className={`${sizes[size]} text-yellow-400 ${onChange ? 'cursor-pointer hover:scale-110 transition-transform' : ''}`} |
19 | | - onClick={() => onChange?.(i + 1)} />; |
20 | | - })} |
21 | | - {showValue && <span className="ml-1 font-semibold text-gray-700">{value.toFixed(1)}</span>} |
22 | | - {count !== undefined && <span className="ml-1 text-gray-500 text-sm">({count.toLocaleString()})</span>} |
| 8 | + <div className='flex gap-1'> |
| 9 | + {Array.from({ length: max }, (_, i) => i + 1).map(star => ( |
| 10 | + <button key={star} type='button' disabled={readonly} |
| 11 | + onClick={() => onChange?.(star)} onMouseEnter={() => !readonly && setHover(star)} onMouseLeave={() => setHover(0)} |
| 12 | + className={sizes[size] + ' transition-colors ' + (!readonly ? 'cursor-pointer' : 'cursor-default')}> |
| 13 | + <FaStar className={(hover || value) >= star ? 'text-yellow-400' : 'text-gray-300'} /> |
| 14 | + </button> |
| 15 | + ))} |
23 | 16 | </div> |
24 | 17 | ); |
25 | 18 | }; |
26 | | - |
27 | 19 | export default Rating; |
0 commit comments