brackt/app/components/league/StatHelpers.tsx

105 lines
2.8 KiB
TypeScript
Raw Normal View History

/**
* Stacked stat column: label / value / delta. The delta slot is always rendered
* (a muted "—" placeholder when there is no change) so that every row reserves the
* same vertical space and the columns line up across the standings table.
*/
export function StatColumn({
label,
value,
delta,
reserveDelta = false,
}: {
label: string;
value: React.ReactNode;
/** Optional change indicator rendered on its own line below the value. */
delta?: React.ReactNode;
/**
* When true, the delta line is always rendered (a muted placeholder when there is
* no delta) so columns stay vertically aligned across rows. When false and no delta
* is provided, the line is omitted entirely.
*/
reserveDelta?: boolean;
}) {
const showDeltaLine = delta !== undefined || reserveDelta;
return (
<div className="text-right shrink-0 flex-1 sm:flex-none">
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
{label}
</p>
<div className="flex items-baseline justify-end">{value}</div>
{showDeltaLine && (
<div className="flex items-baseline justify-end leading-none mt-0.5">
{delta ?? <span className="text-xs text-muted-foreground/60"></span>}
</div>
)}
</div>
);
}
export function StatDivider({ className = "h-12" }: { className?: string } = {}) {
return <div className={`${className} w-px bg-border shrink-0 self-center`} />;
}
/**
* 7-day change indicator. `rank` deltas are unitless places; `points` deltas are
* rounded point totals. Positive = up (green ), negative = down (coral ).
*/
export function DeltaBadge({
delta,
kind,
}: {
delta: number;
kind: "rank" | "points";
}) {
const magnitude = kind === "rank" ? Math.abs(delta) : Math.abs(Math.round(delta));
const up = delta > 0;
const label =
kind === "rank"
? up
? `up ${magnitude}`
: `down ${magnitude}`
: up
? `+${magnitude} points`
: `-${magnitude} points`;
if (up) {
return (
<span className="text-xs font-semibold text-primary" aria-label={label}>
{magnitude}
</span>
);
}
return (
<span
className="text-xs font-semibold"
style={{ color: "var(--coral-accent, #ef4444)" }}
aria-label={label}
>
{magnitude}
</span>
);
}
export function RankingDisplay({
displayRank,
rankChange,
reserveDelta = false,
}: {
displayRank: string | number;
rankChange?: number;
reserveDelta?: boolean;
}) {
return (
<StatColumn
label="Ranking"
reserveDelta={reserveDelta}
value={<span className="text-2xl font-bold leading-none">{displayRank}</span>}
delta={
rankChange !== undefined && rankChange !== 0 ? (
<DeltaBadge delta={rankChange} kind="rank" />
) : undefined
}
/>
);
}