Compare commits

...

4 commits

Author SHA1 Message Date
5ff474a7ea
Nest render function in separate class
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-12-15 10:09:22 +01:00
babdc6104e
Rename SJDON to SuiWeb 2022-12-15 10:07:48 +01:00
6beda8d14f
Render game using SJDON 2022-12-15 10:06:49 +01:00
7b765db827
Add currentPlayer property to Game class 2022-12-15 10:04:00 +01:00
4 changed files with 150 additions and 135 deletions

80
src/js/Components.js Normal file
View file

@ -0,0 +1,80 @@
import { Constants } from "./Constants.js";
/**
* Gets a component which represents the specified {@link game `game`}.
*
* @param {import("./Game.js").Game} game
* The game represented in this app.
*
* @returns {NodeDescriptor}
* The rendered node.
*/
export function App(game)
{
return [
"div",
[
Board,
game.board
],
[
"div",
{ className: "log" },
game.winner ?
`Player ${Constants.PLAYER_NAMES[game.winner]} wins!` :
`It's player "${Constants.PLAYER_NAMES[game.currentPlayer]}"s turn`
]
];
}
/**
* Renders an element which represents the specified {@link board `board`}.
*
* @param {Board} board
* The board represented in this element.
*
* @returns {NodeDescriptor}
* The rendered element.
*/
export function Board(board)
{
let fields = board.flatMap((row) => row);
return [
"div",
{ className: "board" },
...fields.map(
(field) =>
{
return /** @type {NodeDescriptor} */([Field, field]);
}),
["div", { style: "clear: both;" }]
];
}
/**
* Renders an element which represents the specified {@link field `field`}.
*
* @param {CellOwner} field
* The field to represent.
*
* @returns {NodeDescriptor}
* The rendered element.
*/
export function Field(field)
{
return [
"div",
{ className: "field" },
...(
field !== "" ?
[
/** @type {NodeDescriptor} */
([
"div",
{ className: `piece ${Constants.PLAYER_NAMES[field]}` }
])
] :
[])
];
}

View file

@ -1,7 +1,6 @@
import { Constants } from "./Constants.js";
import { elt } from "./elt.js";
import { render } from "./SJDON.js";
import { App } from "./Components.js";
import { State } from "./State.js";
import { SuiWeb } from "./SuiWeb.js";
/**
* Represents a game.
@ -30,20 +29,6 @@ export class Game
*/
#state;
/**
* The actual board.
*
* @type {HTMLElement}
*/
#board;
/**
* The element containing a log message.
*
* @type {HTMLElement}
*/
#log;
/**
* The id of the element to add the board to.
*
@ -79,6 +64,14 @@ export class Game
return this.state.board;
}
/**
* Gets the current player.
*/
get currentPlayer()
{
return this.state.currentPlayer;
}
/**
* Gets the id of the player that is winning.
*
@ -159,22 +152,6 @@ export class Game
*/
initialize()
{
let container = document.getElementById(this.id);
this.#board = elt(
"div",
{
class: "board"
});
this.#log = elt(
"div",
{
class: "log"
});
container.appendChild(this.#board);
container.appendChild(this.#log);
this.draw();
}
@ -192,57 +169,9 @@ export class Game
*/
draw()
{
let board = this.#board;
board.innerHTML = "";
render(
[
"div",
...this.board.flatMap(
(row, y) =>
{
return row.map(
(cell, x) =>
{
return /** @type {NodeDescriptor} */ ([
"div",
{
className: "field",
onclick: () =>
{
if (this.addChip(x, y))
{
this.state.turnCount++;
this.draw();
}
}
},
...(
cell !== "" ?
[
/** @type {NodeDescriptor} */ (["div", { className: `piece ${Constants.PLAYER_NAMES[cell]}` }])
] :
[])
]);
});
}),
["div", { style: "clear: both;" }]
],
board);
this.#log.innerHTML = "";
render(
[
"div",
{
className: this.state.currentPlayer
},
this.winner ?
`Player ${Constants.PLAYER_NAMES[this.winner]} wins!` :
`It's player "${Constants.PLAYER_NAMES[this.state.currentPlayer]}"s turn`
],
this.#log);
let container = document.getElementById(this.id);
container.innerHTML = "";
SuiWeb.render([App, this], container);
}
/**

View file

@ -1,51 +0,0 @@
/**
* Renders the specified {@link data `data`} and appends it to the specified {@link element `element`}.
*
* @param {NodeDescriptor} data
* The node to render written in SJDON notation.
*
* @param {HTMLElement} element
* The element to add the rendered node to.
*/
export function render(data, element)
{
if (Array.isArray(data))
{
let descriptor = data[0];
let args = data.slice(1);
if (typeof descriptor === "function")
{
render(descriptor(...args), element);
}
else if (typeof descriptor === "string")
{
let result = element.ownerDocument.createElement(descriptor);
element.appendChild(result);
for (let arg of args)
{
if (typeof arg === "object" && !Array.isArray(arg))
{
Object.assign(result, arg);
}
else
{
render(arg, result);
}
}
}
else
{
throw new SyntaxError();
}
}
else if (typeof data === "string")
{
element.appendChild(element.ownerDocument.createTextNode(data));
}
else
{
throw new SyntaxError();
}
}

57
src/js/SuiWeb.js Normal file
View file

@ -0,0 +1,57 @@
/**
* Provides component for rendering elements written in SJDON notation.
*/
export class SuiWeb
{
/**
* Renders the specified {@link data `data`} and appends it to the specified {@link element `element`}.
*
* @param {NodeDescriptor} data
* The node to render written in SJDON notation.
*
* @param {HTMLElement} element
* The element to add the rendered node to.
*/
static render(data, element)
{
if (Array.isArray(data))
{
let descriptor = data[0];
let args = data.slice(1);
if (typeof descriptor === "function")
{
SuiWeb.render(descriptor(...args), element);
}
else if (typeof descriptor === "string")
{
let result = element.ownerDocument.createElement(descriptor);
element.appendChild(result);
for (let arg of args)
{
if (typeof arg === "object" && !Array.isArray(arg))
{
Object.assign(result, arg);
}
else
{
SuiWeb.render(arg, result);
}
}
}
else
{
throw new SyntaxError();
}
}
else if (typeof data === "string")
{
element.appendChild(element.ownerDocument.createTextNode(data));
}
else
{
throw new SyntaxError();
}
}
}