Add UI for tile exchange
This commit is contained in:
parent
4ba4c15e23
commit
b790e48bc4
5 changed files with 154 additions and 6 deletions
3
ui/package-lock.json
generated
3
ui/package-lock.json
generated
|
@ -19,7 +19,8 @@
|
|||
},
|
||||
"../pkg": {
|
||||
"name": "word_grid",
|
||||
"version": "0.1.0"
|
||||
"version": "0.1.0",
|
||||
"license": "AGPL-3"
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.22.5",
|
||||
|
|
|
@ -2,7 +2,7 @@ import {useEffect, useRef} from "react";
|
|||
|
||||
export function Modal(props: {
|
||||
isOpen: boolean;
|
||||
setClosed: () => void;
|
||||
setOpen: (isOpen: boolean) => void;
|
||||
children: any;
|
||||
}){
|
||||
|
||||
|
@ -27,7 +27,7 @@ export function Modal(props: {
|
|||
// we manually trigger a close anyway because the children can get updated before the dialog gets closed otherwise
|
||||
// @ts-ignore
|
||||
ref.current.close();
|
||||
props.setClosed();
|
||||
props.setOpen(false);
|
||||
}}>✕</button>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import * as React from "react";
|
||||
import {GameWasm, Letter as LetterData, MyResult, PlayedTile, PlayerAndScore, Tray, WordResult} from "word_grid";
|
||||
import {ChangeEvent, JSX, useEffect, useMemo, useReducer, useRef, useState} from "react";
|
||||
import {Modal} from "./Modal";
|
||||
import {addNTimes} from "./utils";
|
||||
|
||||
export interface Settings {
|
||||
trayLength: number;
|
||||
|
@ -181,8 +183,6 @@ export function Game(props: {wasm: GameWasm, settings: Settings}) {
|
|||
}
|
||||
|
||||
const [playerLetters, trayDispatch] = useReducer(movePlayableLetters, []);
|
||||
|
||||
|
||||
const [logInfo, logDispatch] = useReducer(addLogInfo, []);
|
||||
|
||||
const [turnCount, setTurnCount] = useState<number>(1);
|
||||
|
@ -202,6 +202,7 @@ export function Game(props: {wasm: GameWasm, settings: Settings}) {
|
|||
|
||||
const logDivRef = useRef(null);
|
||||
|
||||
const [isTileExchangeOpen, setIsTileExchangeOpen] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Effect is to keep the log window scrolled down
|
||||
|
@ -214,6 +215,7 @@ export function Game(props: {wasm: GameWasm, settings: Settings}) {
|
|||
|
||||
|
||||
return <>
|
||||
<TileExchangeModal playerLetters={playerLetters} isOpen={isTileExchangeOpen} setOpen={setIsTileExchangeOpen} />
|
||||
<div className="board-log">
|
||||
<Grid cellTypes={cellTypes} playerLetters={playerLetters} boardLetters={boardLetters} dispatch={trayDispatch}/>
|
||||
<div className="message-log">
|
||||
|
@ -273,6 +275,7 @@ export function Game(props: {wasm: GameWasm, settings: Settings}) {
|
|||
|
||||
|
||||
}}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points ✅` : "Check"}</button>
|
||||
<button onClick={(e) => {setIsTileExchangeOpen(true);}}>Open Tile Exchange</button>
|
||||
</>;
|
||||
|
||||
|
||||
|
@ -465,4 +468,115 @@ function Scores(props: {playerScores: Array<PlayerAndScore>}){
|
|||
return <div className="scoring">
|
||||
{elements}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
function TileExchangeModal(props: {playerLetters: PlayableLetterData[], isOpen: boolean, setOpen: (isOpen: boolean) => void}) {
|
||||
|
||||
function clearExchangeTiles() {
|
||||
const array: boolean[] = [];
|
||||
addNTimes(array, false, props.playerLetters.length);
|
||||
return array;
|
||||
}
|
||||
|
||||
const [tilesToExchange, setTilesToExchange] = useState<boolean[]>(clearExchangeTiles);
|
||||
|
||||
let tilesExchangedSelected = 0;
|
||||
for (let i of tilesToExchange) {
|
||||
if(i) {
|
||||
tilesExchangedSelected++;
|
||||
}
|
||||
}
|
||||
|
||||
return <Modal isOpen={props.isOpen} setOpen={(isOpen) => {
|
||||
setTilesToExchange(clearExchangeTiles());
|
||||
props.setOpen(isOpen);
|
||||
}}>
|
||||
<div className="tile-exchange-dialog">
|
||||
<div className="instructions">
|
||||
Click on each tile you'd like to exchange. You currently have {tilesExchangedSelected} tiles selected.
|
||||
</div>
|
||||
<div className="selection-buttons">
|
||||
<button onClick={(e) => {
|
||||
const array: boolean[] = [];
|
||||
addNTimes(array, true, props.playerLetters.length);
|
||||
setTilesToExchange(array);
|
||||
}}>Select All</button>
|
||||
<button onClick={(e) => {
|
||||
setTilesToExchange(clearExchangeTiles());
|
||||
}}>Select None</button>
|
||||
</div>
|
||||
|
||||
<TilesExchangedTray
|
||||
tray={props.playerLetters}
|
||||
selectedArray={tilesToExchange}
|
||||
setSelectedArray={setTilesToExchange}
|
||||
trayLength={props.playerLetters.length}
|
||||
/>
|
||||
<div className="finish-buttons">
|
||||
<button onClick={() => {
|
||||
setTilesToExchange(clearExchangeTiles());
|
||||
props.setOpen(false);
|
||||
}}>Cancel</button>
|
||||
<button disabled = {tilesExchangedSelected == 0} onClick={(e) => {
|
||||
// TODO exchange code
|
||||
|
||||
}}>Exchange</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>;
|
||||
|
||||
}
|
||||
|
||||
function TilesExchangedTray(props: {
|
||||
tray: Array<PlayableLetterData>,
|
||||
trayLength: number,
|
||||
selectedArray: Array<boolean>,
|
||||
setSelectedArray: (x: Array<boolean>) => void,
|
||||
}){
|
||||
|
||||
const divContent = [];
|
||||
for(let i = 0; i<props.trayLength; i++){
|
||||
const tileData = props.tray[i];
|
||||
|
||||
const toggleFunction = () => {
|
||||
props.selectedArray[i] = !props.selectedArray[i];
|
||||
props.setSelectedArray(props.selectedArray.slice());
|
||||
}
|
||||
|
||||
if(tileData != null){
|
||||
divContent.push(
|
||||
<DummyExchangeTile key={i} letter={tileData} isSelected={props.selectedArray[i]} onClick={toggleFunction}/>
|
||||
);
|
||||
} else{
|
||||
divContent.push(<span key={i} />);
|
||||
}
|
||||
}
|
||||
|
||||
return <div className="tray">
|
||||
{divContent}
|
||||
</div>;
|
||||
|
||||
}
|
||||
|
||||
function DummyExchangeTile(props: {
|
||||
letter: LetterData,
|
||||
isSelected: boolean,
|
||||
onClick: () => void,
|
||||
}){
|
||||
|
||||
let textClassName = "text";
|
||||
if(props.letter.is_blank) {
|
||||
textClassName += " prev-blank";
|
||||
}
|
||||
|
||||
let letterClassName = "letter";
|
||||
if(props.isSelected){
|
||||
letterClassName += ' selected-tile';
|
||||
}
|
||||
|
||||
|
||||
return <div className={letterClassName} onClick={props.onClick}>
|
||||
<div className={textClassName}>{props.letter.text}</div>
|
||||
<div className="letter-points">{props.letter.points}</div>
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -188,4 +188,31 @@ dialog {
|
|||
}
|
||||
}
|
||||
|
||||
.tile-exchange-dialog {
|
||||
|
||||
.selection-buttons {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
button {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.finish-buttons {
|
||||
.selection-buttons();
|
||||
}
|
||||
|
||||
.tray-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.selected-tile {
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
6
ui/src/utils.ts
Normal file
6
ui/src/utils.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
export function addNTimes<T>(array: T[], toAdd: T, times: number) {
|
||||
for (let i=0; i<times; i++) {
|
||||
array.push(toAdd);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue