mancala/apps/frontend/src/components/game/SeriesStrip.tsx

79 lines
3.6 KiB
TypeScript
Raw Normal View History

import * as React from "react";
import { FunctionComponent } from "react";
import { useTranslation } from "react-i18next";
import { getSeriesScore, Seat, SeriesGame } from "@mancala/core";
// The most boxes the strip shows; earlier games collapse into a "+N" box.
const MAX_BOXES = 8;
// Above the board from the second game of a series (docs/specs/rematch, R3): the series score and one box per game,
// each with its winner's colour, the stores from the bottom player's side, a dot for who moved first and how it
// ended. Finished games open their replay in a new tab; the game being played is marked.
const SeriesStrip: FunctionComponent<{
// Finished games, oldest first.
games: SeriesGame[],
// A game of the series is being played after them.
hasCurrentGame: boolean,
bottomSeat: Seat,
bottomName: string,
topName: string,
}> = ({ games, hasCurrentGame, bottomSeat, bottomName, topName }) => {
const { t } = useTranslation();
const score = getSeriesScore(games, bottomSeat);
const finishedBoxes = MAX_BOXES - (hasCurrentGame ? 1 : 0);
const hiddenCount = games.length > finishedBoxes ? games.length - finishedBoxes + 1 : 0;
const shown = games.slice(hiddenCount);
const renderGame = (game: SeriesGame, index: number) => {
const number = hiddenCount + index + 1;
const bottomStore = bottomSeat === "player1" ? game.player1Store : game.player2Store;
const topStore = bottomSeat === "player1" ? game.player2Store : game.player1Store;
const winner = game.result === "player1" || game.result === "player2"
? (game.result === bottomSeat ? "mine" : "theirs") : "draw";
const ending = game.endReason === "timeout" ? t("EndedOnTime") : game.endReason === "left" ? t("EndedByLeaving") : undefined;
const bottomFirst = game.firstSeat === bottomSeat;
const firstMark = <span className="sfirst" title={t("MovedFirst")} />;
const label = [
t("SeriesGameNumber", { number }),
`${bottomName} ${bottomStore} – ${topStore} ${topName}`,
`${t("MovedFirst")}: ${bottomFirst ? bottomName : topName}`,
ending,
].filter(Boolean).join(", ");
return (
<li key={game.gameId}>
<a className={"sg " + winner} href={`/game/${game.gameId}`} target="_blank" rel="noopener" aria-label={label}>
<span className="sn">{ending ? `${number} · ${ending}` : number}</span>
<span className="ss">{bottomFirst && firstMark}{bottomStore}–{topStore}{!bottomFirst && firstMark}</span>
</a>
</li>
);
};
return (
<nav className="series" aria-label={t("Series")}>
<div className="sscore">
<span className="slabel">{t("Series")}</span>
<span className="sname">{bottomName}</span>
<b>{score.wins} – {score.losses}</b>
<span className="sname">{topName}</span>
</div>
<ol className="sgames">
{hiddenCount > 0 && (
<li><span className="sg more" title={t("SeriesEarlierGames", { count: hiddenCount })}>+{hiddenCount}</span></li>
)}
{shown.map(renderGame)}
{hasCurrentGame && (
<li aria-current="true">
<span className="sg now">
<span className="sn">{games.length + 1}</span>
<span className="ss">{t("SeriesCurrentGame")}</span>
</span>
</li>
)}
</ol>
</nav>
);
};
export default SeriesStrip;