From the second game of a series, a strip shows the series score and a box per game in the winner's colour, with the stores, who moved first and how it ended; a finished game opens its replay in a new tab. The game over card shows the series score too. Spectators see names.
79 lines
3.6 KiB
TypeScript
79 lines
3.6 KiB
TypeScript
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;
|