90 lines
2.4 KiB
JavaScript
90 lines
2.4 KiB
JavaScript
import { useState } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { Star } from 'lucide-react';
|
|
|
|
const StarRating = ({
|
|
rating,
|
|
onRatingChange,
|
|
maxStars = 5,
|
|
size = 24,
|
|
color = '#ffc107',
|
|
readOnly = false,
|
|
className = ''
|
|
}) => {
|
|
const [hoverRating, setHoverRating] = useState(null);
|
|
|
|
const handleClick = (value) => {
|
|
if (!readOnly && onRatingChange) {
|
|
onRatingChange(value);
|
|
}
|
|
};
|
|
|
|
const handleMouseEnter = (value) => {
|
|
if (!readOnly) {
|
|
setHoverRating(value);
|
|
}
|
|
};
|
|
|
|
const handleMouseLeave = () => {
|
|
if (!readOnly) {
|
|
setHoverRating(null);
|
|
}
|
|
};
|
|
|
|
const getStarIcon = (index) => {
|
|
const currentRating = hoverRating !== null ? hoverRating : rating;
|
|
|
|
if (currentRating > index) {
|
|
const hasHalfStar = currentRating % 1 > 0.5 && index + 0.5 <= currentRating;
|
|
if (hasHalfStar) {
|
|
// For half star, we'll use a combination approach or just show full star
|
|
// Since we don't have StarOutline, we'll approximate with full stars
|
|
return <Star className={`w-${size} h-${size} text-${color}`} />;
|
|
}
|
|
return <Star className={`w-${size} h-${size} text-${color}`} />;
|
|
}
|
|
return <Star className={`w-${size} h-${size} text-gray-400`} />;
|
|
};
|
|
|
|
return (
|
|
<div className={`flex gap-1 ${className}`} onMouseLeave={handleMouseLeave}>
|
|
{[...Array(maxStars)].map((_, index) => (
|
|
<motion.div
|
|
key={index}
|
|
whileHover={{ scale: readOnly ? 1 : 1.1 }}
|
|
onClick={() => handleClick(index + 1)}
|
|
onMouseEnter={() => handleMouseEnter(index + 1)}
|
|
>
|
|
{getStarIcon(index)}
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default StarRating;
|
|
|
|
// Helper functions
|
|
export function getStarCount(rating, maxStars = 5) {
|
|
return Math.round(rating * maxStars) / maxStars;
|
|
}
|
|
|
|
export function formatRating(rating) {
|
|
if (rating === 0) return 'لا يوجد تقييم';
|
|
return `${rating.toFixed(1)}`; // Show 1 decimal place
|
|
}
|
|
|
|
export function getRatingColor(rating) {
|
|
if (rating >= 4.5) return 'text-green-600';
|
|
if (rating >= 3.5) return 'text-yellow-600';
|
|
if (rating >= 2.5) return 'text-orange-600';
|
|
return 'text-red-600';
|
|
}
|
|
|
|
export function getRatingText(rating) {
|
|
if (rating >= 4.5) return 'ممتاز';
|
|
if (rating >= 3.5) return 'جيد جداً';
|
|
if (rating >= 2.5) return 'جيد';
|
|
if (rating >= 1.5) return 'مقبول';
|
|
return 'ضعيف';
|
|
} |