Files
ToBePlayed-Frontend/src/components/RatingStars.jsx
T
lobo 45f5087227 feat(library): set status and rating in one step (#56)
Library rows: the status badge is a menu that saves at once.
Game page: status and rating in the top card, saved on click.
Status changes and hiding a game show an Undo toast.
Other game actions moved into a "..." menu; split and unlink ask first.
New sorts: name, last played, time to beat; "last synced" is gone.
Page numbers and 44 px touch targets on phones.
2026-10-21 11:09:44 +02:00

43 lines
1.8 KiB
React

import {useTranslation} from 'react-i18next';
import {Star} from 'lucide-react';
import {cn} from '@/lib/utils';
/**
* Five star buttons. Clicking the chosen star again sends 0, so the parent decides what that means.
*
* @param value 0 for no rating
* @param labelledBy id of the element that names the group
* @param readOnly shows the stars as one image, without buttons
*/
export default function RatingStars({value, onChange, labelledBy, disabled, readOnly}) {
const {t} = useTranslation();
if (readOnly) {
return (
<span role="img" aria-label={t('BACKLOG.RATING_STARS', {count: value})} className="inline-flex gap-0.5">
{[1, 2, 3, 4, 5].map((n) => (
<Star key={n} className={cn('size-3.5', n <= value ? 'fill-amber-400 text-amber-400' : 'text-muted-foreground/40')}/>
))}
</span>
);
}
return (
<div role="group" aria-labelledby={labelledBy} className="flex h-9 items-center gap-1">
{[1, 2, 3, 4, 5].map((n) => (
<button
key={n}
type="button"
disabled={disabled}
aria-label={t('BACKLOG.RATING_STARS', {count: n})}
aria-pressed={value === n}
onClick={() => onChange(value === n ? 0 : n)}
className="rounded-sm p-0.5 pointer-coarse:size-11 pointer-coarse:p-2.5 outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
<Star className={cn('size-6 transition-colors',
n <= value ? 'fill-amber-400 text-amber-400' : 'text-muted-foreground/40 hover:text-amber-400')}/>
</button>
))}
</div>
);
}