unified button logic between backend and frontend

feature/modern-browsers
Inga 🏳‍🌈 1 month ago
parent 15b63eefcf
commit de52499d6f
  1. 27
      src/backend/components/boardgame.tsx
  2. 6
      src/backend/main/index.tsx
  3. 37
      src/frontend/components/board-game.ts
  4. 10
      src/shared/display.ts
  5. 2
      tsconfig.json

@ -1,6 +1,6 @@
import { sequence } from "../../shared/array-utils.ts";
import { BoardgameStateType, CurrentOutcome, GameRules, Player } from "../../shared/datatypes.ts";
import { getCellDisplayData, getDisplayStates } from "../../shared/display.ts";
import { BoardgameStateType, CurrentOutcome, GameRules } from "../../shared/datatypes.ts";
import { ButtonValues, getButtonValues, getCellDisplayData, getDisplayStates } from "../../shared/display.ts";
const getSubmitAttributes = (key: string, targetState: BoardgameStateType) => ({
type: "submit" as const,
@ -30,15 +30,26 @@ const getCellHtml = ({
);
};
const getGenericButtonAttributes = (
kind: keyof ButtonValues,
{ key, buttonValues }: { key: string; buttonValues: ButtonValues },
) => ({
...getSubmitAttributes(key, buttonValues[kind]),
className: kind,
});
export const getBoardgameHtml = (key: string, gameState: BoardgameStateType, rules: GameRules) => {
const currentOutcome = gameState.board ? rules.getBoardOutcome(gameState.board) : CurrentOutcome.Undecided;
const buttonValues = getButtonValues(gameState);
const gameClasses = getDisplayStates(gameState, currentOutcome);
const gameActiveClassNames = Object.entries(gameClasses)
.filter(([, value]) => value)
.map(([name]) => name);
return (
<board-game track={key} class={gameActiveClassNames.join(" ")}>
<board-game track={key} className={gameActiveClassNames.join(" ")}>
<form method="post" is="progressive-form">
<p>
Current player: <span class="current-player-name">{gameState.currentPlayerName}</span>
@ -64,34 +75,34 @@ export const getBoardgameHtml = (key: string, gameState: BoardgameStateType, rul
<p class="when-autoplayer-x-disabled">
Currently X moves are made manually.{" "}
<button class="autoplayer-x-enable" {...getSubmitAttributes(key, gameState.withAutoPlayer(Player.X))}>
<button {...getGenericButtonAttributes("autoplayer-x-enable", { key, buttonValues })}>
Make computer play for X.
</button>
</p>
<p class="when-autoplayer-x-enabled">
Currently X moves are made by computer.{" "}
<button class="autoplayer-x-disable" {...getSubmitAttributes(key, gameState.withoutAutoPlayer(Player.X))}>
<button {...getGenericButtonAttributes("autoplayer-x-disable", { key, buttonValues })}>
Make them manually.
</button>
</p>
<p class="when-autoplayer-o-disabled">
Currently O moves are made manually.{" "}
<button class="autoplayer-o-enable" {...getSubmitAttributes(key, gameState.withAutoPlayer(Player.O))}>
<button {...getGenericButtonAttributes("autoplayer-o-enable", { key, buttonValues })}>
Make computer play for O.
</button>
</p>
<p class="when-autoplayer-o-enabled">
Currently O moves are made by computer.{" "}
<button class="autoplayer-o-disable" {...getSubmitAttributes(key, gameState.withoutAutoPlayer(Player.O))}>
<button {...getGenericButtonAttributes("autoplayer-o-disable", { key, buttonValues })}>
Make them manually.
</button>
</p>
<p>
<button type="submit" class="game-start" name={key} value={gameState.withEmptyBoard().serialize()}>
<button {...getGenericButtonAttributes("game-start", { key, buttonValues })}>
<span class="when-game-not-in-progress">Start game</span>
<span class="when-game-in-progress">Restart game</span>
</button>

@ -13,6 +13,11 @@ export const mainPageHandler: RequestHandler = (req, res) => {
return;
}
const board2 = handleBoardgame(req, res, "tictactoe2");
if (!board2) {
return;
}
sendHtml(
res,
<html>
@ -68,6 +73,7 @@ export const mainPageHandler: RequestHandler = (req, res) => {
</ul>
</section>
<section>{board1}</section>
<section>{board2}</section>
</body>
</html>,
);

@ -1,7 +1,7 @@
import { BoardgameState } from "../../shared/boardgame-state.ts";
import { getTargetGameState } from "../../shared/boardgame.ts";
import { CurrentOutcome, Player } from "../../shared/datatypes.ts";
import { getCellDisplayData, getDisplayStates } from "../../shared/display.ts";
import { CurrentOutcome } from "../../shared/datatypes.ts";
import { ButtonValues, getButtonValues, getCellDisplayData, getDisplayStates } from "../../shared/display.ts";
import { gamesRules } from "../../shared/rules.ts";
import { replaceLocation } from "../lib/navigation-utils.ts";
import { TrackingTools } from "../lib/query-tracking-utils.ts";
@ -24,37 +24,26 @@ export class BoardGameComponent extends HTMLElement {
return;
}
const currentOutcome = gameState.board ? rules.getBoardOutcome(gameState.board) : CurrentOutcome.Undecided;
const displayStates = getDisplayStates(gameState, currentOutcome);
for (const [className, shouldEnable] of Object.entries(displayStates)) {
this.classList.toggle(className, shouldEnable);
}
for (const playerNameElement of this.querySelectorAll(".current-player-name")) {
if ((playerNameElement as HTMLElement).innerText !== gameState.currentPlayerName) {
(playerNameElement as HTMLElement).innerText = gameState.currentPlayerName;
}
}
for (const button of this.querySelectorAll("button.autoplayer-x-enable")) {
(button as HTMLButtonElement).value = gameState.withAutoPlayer(Player.X).serialize();
}
for (const button of this.querySelectorAll("button.autoplayer-x-disable")) {
(button as HTMLButtonElement).value = gameState.withoutAutoPlayer(Player.X).serialize();
}
for (const button of this.querySelectorAll("button.autoplayer-o-enable")) {
(button as HTMLButtonElement).value = gameState.withAutoPlayer(Player.O).serialize();
const buttonValues = getButtonValues(gameState);
for (const button of this.querySelectorAll("button")) {
for (const className of button.classList) {
if (Object.hasOwn(buttonValues, className)) {
button.value = buttonValues[className as keyof ButtonValues].serialize();
}
}
}
for (const button of this.querySelectorAll("button.autoplayer-o-disable")) {
(button as HTMLButtonElement).value = gameState.withoutAutoPlayer(Player.O).serialize();
}
const currentOutcome = gameState.board ? rules.getBoardOutcome(gameState.board) : CurrentOutcome.Undecided;
for (const button of this.querySelectorAll("button.game-start")) {
(button as HTMLButtonElement).value = gameState.withEmptyBoard().serialize();
const displayStates = getDisplayStates(gameState, currentOutcome);
for (const [className, shouldEnable] of Object.entries(displayStates)) {
this.classList.toggle(className, shouldEnable);
}
for (const tbodyUntyped of this.querySelectorAll("tbody.game-board")) {

@ -43,3 +43,13 @@ export const getCellDisplayData = ({
text: formatSquareState(squareState),
};
};
export const getButtonValues = (gameState: BoardgameStateType) => ({
"autoplayer-x-enable": gameState.withAutoPlayer(Player.X),
"autoplayer-x-disable": gameState.withoutAutoPlayer(Player.X),
"autoplayer-o-enable": gameState.withAutoPlayer(Player.O),
"autoplayer-o-disable": gameState.withoutAutoPlayer(Player.O),
"game-start": gameState.withEmptyBoard(),
});
export type ButtonValues = ReturnType<typeof getButtonValues>;

@ -8,6 +8,6 @@
"jsxImportSource": "preact",
"module": "NodeNext",
"noEmit": true,
"target": "ES2020"
"target": "ES2022"
}
}

Loading…
Cancel
Save