Initial commit

This commit is contained in:
2023-01-26 17:21:14 +01:00
commit fe671b8682
17 changed files with 1011 additions and 0 deletions

13
board-frontend/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "board-frontend"
version = "0.1.0"
edition = "2021"
description = "A web app built with Yew"
keywords = ["yew", "trunk"]
categories = ["gui", "wasm", "web-programming"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
yew = { version="0.20", features=["csr"] }
board-shared = { path = "../board-shared" }
gloo-dialogs = "0.1.1"

View File

@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Scrabble des chiffres</title>
<link data-trunk rel="sass" href="index.scss" />
</head>
<body></body>
</html>

71
board-frontend/index.scss Normal file
View File

@@ -0,0 +1,71 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
main {
max-width: 1280px;
margin: 0 auto;
padding: 1rem;
text-align: center;
}
.board {
display: flex;
flex-direction: column;
}
.board-row {
display: flex;
}
.cell, .tile {
width: 24px;
height: 24px;
border-radius: 4px;
display: flex;
justify-content: center;
flex-direction: column;
}
.cell {
background-color: #888888;
margin: 4px;
}
.button, .tile {
background-color: #535bf2;
}
.button:hover, .tile:hover {
background-color: #646cff;
}
.button {
padding: 0.25rem 1rem;
border: 0;
}
.hand {
display: flex;
justify-content: space-around;
}

123
board-frontend/src/app.rs Normal file
View File

@@ -0,0 +1,123 @@
use crate::hand_view::HandView;
use crate::tile_view::PlacedTileView;
use board_shared::board::Board;
use board_shared::expr::is_valid_guess;
use board_shared::game::Game;
use board_shared::tile::Tile;
use gloo_dialogs::alert;
use yew::prelude::*;
enum SelectedTile {
InHand(usize),
Equals,
None,
}
#[derive(Properties, PartialEq)]
struct BoardViewProps {
board: Board,
on_click: Callback<(usize, usize)>,
}
#[function_component(BoardView)]
fn board_view(BoardViewProps { board, on_click }: &BoardViewProps) -> Html {
html! {
<table class="board">
{ (0..25).map(|x| html! {
<tr class="board-row">
{ (0..25).map(|y| html! {
<PlacedTileView x={x} y={y} key={x} tile={board.get(x, y)} on_click={on_click.clone()} />
}).collect::<Html>() }
</tr>
}).collect::<Html>() }
</table>
}
}
#[function_component(App)]
pub fn app() -> Html {
let game = use_state(Game::default);
let current_game = use_state(Game::default);
let selected_tile = use_state(|| SelectedTile::None);
let on_tile_click = {
let game = current_game.clone();
let selected_tile = selected_tile.clone();
Callback::from(move |(x, y)| {
if let SelectedTile::InHand(idx) = *selected_tile {
let mut in_hand = game.in_hand.clone();
let tile = in_hand.tiles.remove(idx);
let mut board = game.board.clone();
board.set(x, y, tile);
game.set(Game { board, in_hand });
selected_tile.set(SelectedTile::None);
} else if let SelectedTile::Equals = *selected_tile {
let mut board = game.board.clone();
board.set(x, y, Tile::Equals);
game.set(Game {
board,
in_hand: game.in_hand.clone(),
});
selected_tile.set(SelectedTile::None);
}
})
};
let on_tile_select = {
let selected_tile = selected_tile.clone();
Callback::from(move |idx| {
selected_tile.set(SelectedTile::InHand(idx));
})
};
let on_equals_select = {
Callback::from(move |_| {
selected_tile.set(SelectedTile::Equals);
})
};
let on_continue_click = {
let current_game = current_game.clone();
Callback::from(move |_| {
let diff = game.board.difference(&current_game.board);
if let Some(true) = Board::is_contiguous(&diff) {
if let Ok(true) = is_valid_guess(&current_game.board, &diff) {
alert("Valid move!");
let mut in_hand = current_game.in_hand.clone();
in_hand.complete();
game.set(Game {
board: current_game.board.clone(),
in_hand: in_hand.clone()
});
current_game.set(Game {
board: current_game.board.clone(),
in_hand,
});
} else {
alert("Invalid move! (invalid expressions)");
current_game.set(Game {
board: game.board.clone(),
in_hand: game.in_hand.clone(),
});
}
} else {
if !diff.is_empty() {
alert("Invalid move! (not contiguous)");
}
let mut in_hand = game.in_hand.clone();
in_hand.complete();
current_game.set(Game {
board: game.board.clone(),
in_hand,
});
}
})
};
html! {
<main>
<BoardView board={current_game.board.clone()} on_click={on_tile_click} />
<HandView hand={current_game.in_hand.clone()} on_select={on_tile_select} />
<div class="row">
<button onclick={on_equals_select} class="button">{"="}</button>
<button onclick={on_continue_click} class="button">{if current_game.in_hand.tiles.is_empty() { "Start" } else { "Continue" }}</button>
</div>
</main>
}
}

View File

@@ -0,0 +1,22 @@
use crate::tile_view::TileView;
use board_shared::game::Hand;
use yew::prelude::*;
use yew::{html, Callback, Html};
#[derive(Properties, PartialEq)]
pub struct HandViewProps {
pub hand: Hand,
pub on_select: Callback<usize>,
}
#[function_component(HandView)]
pub fn hand_view(HandViewProps { hand, on_select }: &HandViewProps) -> Html {
let on_select = on_select.clone();
html! {
<div class="hand">
{ hand.tiles.iter().enumerate().map(|(i, tile)| html! {
<TileView tile={*tile} key={i} idx={i} on_select={on_select.clone()} />
}).collect::<Html>() }
</div>
}
}

View File

@@ -0,0 +1,9 @@
mod app;
mod hand_view;
mod tile_view;
use app::App;
fn main() {
yew::Renderer::<App>::new().render();
}

View File

@@ -0,0 +1,59 @@
use board_shared::tile::Tile;
use yew::prelude::*;
use yew::{html, Callback, Html};
#[derive(Properties, PartialEq)]
pub struct PlacedTileViewProps {
pub x: usize,
pub y: usize,
pub tile: Option<Tile>,
pub on_click: Callback<(usize, usize)>,
}
#[function_component(PlacedTileView)]
pub fn placed_tile_view(
PlacedTileViewProps {
x,
y,
tile,
on_click,
}: &PlacedTileViewProps,
) -> Html {
let x = *x;
let y = *y;
let on_select = {
let on_click = on_click.clone();
Callback::from(move |_| on_click.emit((x, y)))
};
html! {
<td class="cell" key={y} onclick={on_select}>{ tile.map(|tile| {
html! { tile }
}).unwrap_or_else(|| {
html! { "" }
})}</td>
}
}
#[derive(Properties, PartialEq)]
pub struct TileViewProps {
pub tile: Tile,
pub on_select: Callback<usize>,
pub idx: usize,
}
#[function_component(TileView)]
pub fn tile_view(
TileViewProps {
tile,
on_select,
idx,
}: &TileViewProps,
) -> Html {
let on_select = on_select.clone();
let idx = *idx;
html! {
<div class="tile" onclick={Callback::from(move |_| {
on_select.emit(idx)
})}>{ tile }</div>
}
}