Skip to content

Commit 0dc7720

Browse files
committed
feat(ui): add Rating stars component
1 parent 18583e1 commit 0dc7720

1 file changed

Lines changed: 14 additions & 22 deletions

File tree

src/components/ui/Rating.tsx

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
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);
147
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+
))}
2316
</div>
2417
);
2518
};
26-
2719
export default Rating;

0 commit comments

Comments
 (0)