Sizing konstanty (seat/standings/guess/totals) presunute do boardTheme.ts a farebne odlisenie tipu v PlayerCircle (nad tip = terakota). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
2.9 KiB
TypeScript
74 lines
2.9 KiB
TypeScript
interface Props {
|
|
name: string;
|
|
/** Tricks won this round. */
|
|
won: number;
|
|
/** Bid for this round (null until the player has guessed). */
|
|
guess: number | null;
|
|
/** Whether it is this player's turn — the only state that highlights a circle. */
|
|
active: boolean;
|
|
size?: number;
|
|
}
|
|
|
|
export default function PlayerCircle({ name, won, guess, active, size = 52 }: Props) {
|
|
const nameFont = Math.max(9, Math.round(size * 0.17));
|
|
const valueFont = Math.round(size * 0.32);
|
|
// Bid status at a glance: terracotta once the player has taken more tricks
|
|
// than bid (the bid is already lost), gold while exactly on target, cream
|
|
// while still chasing. Scoring: exact match = 10 + bid, anything else = 0.
|
|
const guessColor =
|
|
guess === null ? '#d8cba6' : won > guess ? '#e08066' : won === guess ? '#e8c14a' : '#d8cba6';
|
|
// Oval: height a touch shorter than size so it reads as an ellipse. Width
|
|
// starts at `size` (a circle for short names) but grows with the name via
|
|
// fit-content + padding, up to a cap beyond which the name is ellipsised
|
|
// rather than wrapping (wrapping would break the fixed height/oval shape).
|
|
const height = Math.round(size * 0.78);
|
|
const hPad = Math.round(size * 0.16);
|
|
const maxWidth = Math.round(size * 2);
|
|
|
|
return (
|
|
<div
|
|
// Only the active player is highlighted (gold ring + glow) — colors come
|
|
// from the velvet-table palette tokens (tailwind.config.js), not literals.
|
|
className={`flex flex-col items-center justify-center rounded-full ${
|
|
active ? 'bg-circle-active border-2 border-gold' : 'bg-circle border-[1.5px] border-gold/20'
|
|
}`}
|
|
style={{
|
|
width: 'fit-content',
|
|
minWidth: size,
|
|
maxWidth,
|
|
height,
|
|
paddingLeft: hPad,
|
|
paddingRight: hPad,
|
|
boxShadow: '0 2px 10px rgba(0,0,0,.45)',
|
|
animation: active ? 'ar 2.2s ease-in-out infinite' : undefined,
|
|
}}
|
|
>
|
|
<span
|
|
className={`uppercase leading-tight text-center overflow-hidden text-ellipsis whitespace-nowrap max-w-full ${active ? 'text-gold' : 'text-green-circle'}`}
|
|
style={{
|
|
fontFamily: '"DM Sans",sans-serif',
|
|
fontSize: nameFont,
|
|
letterSpacing: '.09em',
|
|
fontWeight: 500,
|
|
}}
|
|
>
|
|
{name}
|
|
</span>
|
|
<span
|
|
className={`leading-none ${active ? 'text-gold-bright' : 'text-gold'}`}
|
|
style={{
|
|
fontFamily: '"Playfair Display",serif',
|
|
fontSize: valueFont,
|
|
fontWeight: active ? 700 : 400,
|
|
}}
|
|
>
|
|
{won}
|
|
{/* The bid matters as much as the tricks won — keep it nearly as big
|
|
and in readable cream; only the slash separator stays muted. */}
|
|
<span style={{ fontSize: valueFont * 0.7, color: '#b0a585' }}>/</span>
|
|
<span style={{ fontSize: valueFont , color: guessColor }}>{guess ?? '?'}</span>
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|