Compare commits
2 Commits
1fbef7644b
...
d521d69eac
Author | SHA1 | Date |
---|---|---|
kirbylife | d521d69eac | |
kirbylife | 8387179bd0 |
|
@ -1 +1,5 @@
|
||||||
/target
|
/target
|
||||||
|
|
||||||
|
/dist
|
||||||
|
|
||||||
|
/.idea
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,4 +13,5 @@ log = "0.4.20"
|
||||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||||
wasm-bindgen = "0.2.87"
|
wasm-bindgen = "0.2.87"
|
||||||
wasm-logger = "0.2.0"
|
wasm-logger = "0.2.0"
|
||||||
|
web-sys = "0.3.64"
|
||||||
yew = { version = "0.20.0", features = ["csr"] }
|
yew = { version = "0.20.0", features = ["csr"] }
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Yew App</title>
|
<title>Yew App</title>
|
||||||
|
<link rel="stylesheet" href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
|
||||||
<link data-trunk rel="css" href="static/css/styles.css" />
|
<link data-trunk rel="css" href="static/css/styles.css" />
|
||||||
|
<link data-trunk rel="css" href="static/css/normalize.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hola mundo</h1>
|
<h1>Hola mundo</h1>
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
|
use crate::consts::{AttemptField, Status};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct AttemptRowProps {
|
pub struct AttemptRowProps {
|
||||||
pub answer: AttrValue,
|
pub attempt: Vec<AttemptField>,
|
||||||
pub text: AttrValue,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn AttemptRow(props: &AttemptRowProps) -> Html {
|
pub fn AttemptRow(props: &AttemptRowProps) -> Html {
|
||||||
let AttemptRowProps { answer, text } = props;
|
let AttemptRowProps { attempt } = props;
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
{ for answer.chars().zip(text.chars()).map(|(ans, att)| html! { <p class={
|
{ for attempt.iter().map(|att| html_nested! {
|
||||||
if ans == att { "correct" } else if answer.contains(att) { "almost" } else { "missed" }
|
<p
|
||||||
} >{ att }</p> }) }
|
class={
|
||||||
|
match att.status {
|
||||||
|
Status::Found => "correct",
|
||||||
|
Status::Almost => "almost",
|
||||||
|
Status::NotFound => "missed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>{ att.char_field }</p>
|
||||||
|
})}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
use yew::prelude::*;
|
||||||
|
use crate::components::{EmptyRow, CurrentRow, AttemptRow};
|
||||||
|
use crate::consts::{Attempts, MAX_ATTEMPTS};
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct BoardProps {
|
||||||
|
pub attempts: Attempts,
|
||||||
|
pub attempt_index: usize,
|
||||||
|
pub current_attempt: AttrValue,
|
||||||
|
pub onkeypress: Callback<KeyboardEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn Board(props: &BoardProps) -> Html {
|
||||||
|
let BoardProps {
|
||||||
|
attempts,
|
||||||
|
attempt_index,
|
||||||
|
current_attempt,
|
||||||
|
onkeypress
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div class="board" onkeyup={onkeypress} tabindex="0">
|
||||||
|
{
|
||||||
|
for (0..MAX_ATTEMPTS).map(|i| {
|
||||||
|
if i < *attempt_index {
|
||||||
|
html! { <AttemptRow attempt={ attempts.fields[i].clone() } /> }
|
||||||
|
} else if i == *attempt_index {
|
||||||
|
html! { <CurrentRow text={ current_attempt } /> }
|
||||||
|
} else {
|
||||||
|
html! { <EmptyRow /> }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::consts::{Key, KeyboardKeyType, VirtualKey};
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct KeyboardProps {
|
||||||
|
pub onclick: Callback<Key>,
|
||||||
|
pub keyboard: UseStateHandle<Vec<VirtualKey>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn Keyboard(props: &KeyboardProps) -> Html {
|
||||||
|
let handle_click = |k: Key| {
|
||||||
|
let onclick = props.onclick.clone();
|
||||||
|
|
||||||
|
Callback::from(move |_| {
|
||||||
|
onclick.emit(k);
|
||||||
|
}).clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div class="keyboard"> {
|
||||||
|
for (props.keyboard).iter().map(|vk: &VirtualKey| {
|
||||||
|
match vk.key {
|
||||||
|
KeyboardKeyType::CharKey(c) => html! { <button onclick={ handle_click(Key::CharKey(c)) }> { c } </button> },
|
||||||
|
KeyboardKeyType::Backspace => html! { <button onclick={ handle_click(Key::Backspace) } class="key-big"><i class="las la-undo"></i></button> },
|
||||||
|
KeyboardKeyType::Enter => html! { <button onclick={ handle_click(Key::Enter) } class="key-big"> { "Enviar" } </button> }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} </div>
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,13 @@
|
||||||
pub use crate::components::attempt_row::AttemptRow;
|
pub use crate::components::attempt_row::AttemptRow;
|
||||||
pub use crate::components::current_row::CurrentRow;
|
pub use crate::components::current_row::CurrentRow;
|
||||||
pub use crate::components::empty_row::EmptyRow;
|
pub use crate::components::empty_row::EmptyRow;
|
||||||
|
pub use crate::components::keyboard::Keyboard;
|
||||||
pub use crate::components::result_board::ResultBoard;
|
pub use crate::components::result_board::ResultBoard;
|
||||||
|
pub use crate::components::board::Board;
|
||||||
|
|
||||||
|
mod board;
|
||||||
mod attempt_row;
|
mod attempt_row;
|
||||||
mod current_row;
|
mod current_row;
|
||||||
mod empty_row;
|
mod empty_row;
|
||||||
|
mod keyboard;
|
||||||
mod result_board;
|
mod result_board;
|
||||||
|
|
|
@ -1,33 +1,31 @@
|
||||||
|
use crate::consts::GameResult;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
use crate::consts::{Attempts, Status};
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct ResultBoardProps {
|
pub struct ResultBoardProps {
|
||||||
pub answer: AttrValue,
|
pub attempts: Attempts,
|
||||||
pub attempts: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn ResultBoard(props: &ResultBoardProps) -> Html {
|
pub fn ResultBoard(props: &ResultBoardProps) -> Html {
|
||||||
let ResultBoardProps { answer, attempts } = props;
|
let ResultBoardProps {
|
||||||
let answer_chars = answer.chars();
|
attempts,
|
||||||
|
} = props;
|
||||||
let mut result = String::default();
|
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class="result-board"> {
|
<div class="result-board"> {
|
||||||
for attempts.iter().map(|attempt| {
|
for attempts.fields.iter().map(|attempt| {
|
||||||
html! {
|
html_nested! {
|
||||||
<> {
|
<p class={ "result-board" }> {
|
||||||
for answer.chars().zip(attempt.chars()).map(| (ans, att)| {
|
for attempt.iter().map(| att| {
|
||||||
if ans == att {
|
match att.status {
|
||||||
html! { <span> { "🟩" } </span> }
|
Status::Found => html_nested! { <> { "🟩" } </> },
|
||||||
} else if answer.contains(att) {
|
Status::Almost => html_nested! { <span>{ "🟨" }</span> },
|
||||||
html! { <span>{ "🟨" }</span> }
|
Status::NotFound => html_nested! { <span>{ "⬜" }</span> }
|
||||||
} else {
|
|
||||||
html! { <span>{ "⬜" }</span> }
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} </>
|
} </p>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} </div>
|
} </div>
|
||||||
|
|
|
@ -1,10 +1,66 @@
|
||||||
pub const MAX_ATTEMPTS: usize = 6;
|
use yew::html::IntoPropValue;
|
||||||
|
|
||||||
pub enum Key {
|
pub const MAX_ATTEMPTS: usize = 6;
|
||||||
|
pub const WORD_LEN: usize = 5;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum KeyboardKeyType {
|
||||||
Enter,
|
Enter,
|
||||||
// 13
|
|
||||||
Backspace,
|
Backspace,
|
||||||
|
CharKey(char),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum Status {
|
||||||
|
NotFound,
|
||||||
|
Found,
|
||||||
|
Almost,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct AttemptField {
|
||||||
|
pub char_field: char,
|
||||||
|
pub status: Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Attempts {
|
||||||
|
pub fields: Vec<Vec<AttemptField>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attempts {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Attempts {
|
||||||
|
fields: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct VirtualKey {
|
||||||
|
pub key: KeyboardKeyType,
|
||||||
|
pub status: Option<Status>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtualKey {
|
||||||
|
pub fn from_charkey(charkey: char) -> Self {
|
||||||
|
VirtualKey {
|
||||||
|
key: KeyboardKeyType::CharKey(charkey),
|
||||||
|
status: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_key(key: KeyboardKeyType) -> Self {
|
||||||
|
VirtualKey { key, status: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Key {
|
||||||
|
// 13
|
||||||
|
Enter,
|
||||||
// 8
|
// 8
|
||||||
|
Backspace,
|
||||||
CharKey(char),
|
CharKey(char),
|
||||||
Ignored,
|
Ignored,
|
||||||
}
|
}
|
||||||
|
@ -21,7 +77,8 @@ impl From<u32> for Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Result {
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum GameResult {
|
||||||
Win,
|
Win,
|
||||||
Fail,
|
Fail,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub use use_board::use_board;
|
||||||
|
pub use use_board::UseBoardHandle;
|
||||||
|
|
||||||
|
mod use_board;
|
|
@ -0,0 +1,114 @@
|
||||||
|
use crate::consts::{AttemptField, Attempts, GameResult, Key, VirtualKey, MAX_ATTEMPTS};
|
||||||
|
use crate::services::words::{get_word_of_the_day, WORDS};
|
||||||
|
use crate::utils::{evaluate_status, new_empty_virtual_keyboard};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
type AttemptRow = Attempts;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub struct UseBoardHandle {
|
||||||
|
pub current_attempt: UseStateHandle<String>,
|
||||||
|
pub attempts: UseStateHandle<AttemptRow>,
|
||||||
|
pub virtual_keyboard: UseStateHandle<Vec<VirtualKey>>,
|
||||||
|
pub result: UseStateHandle<Option<GameResult>>,
|
||||||
|
pub attempt_index: Rc<RefCell<usize>>,
|
||||||
|
answer: Rc<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UseBoardHandle {
|
||||||
|
pub fn send_key(&self, k: Key) {
|
||||||
|
let current_attempt_len = self.current_attempt.chars().count();
|
||||||
|
|
||||||
|
if self.result.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match k {
|
||||||
|
Key::CharKey(c) => {
|
||||||
|
if current_attempt_len >= 5 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_attempt = (*self.current_attempt).clone();
|
||||||
|
new_attempt.push(c);
|
||||||
|
self.current_attempt.set(new_attempt);
|
||||||
|
}
|
||||||
|
Key::Backspace => {
|
||||||
|
if current_attempt_len == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_attempt = (*self.current_attempt).clone();
|
||||||
|
// This unwrap is safe because the potential failura it's already covered
|
||||||
|
new_attempt.pop().unwrap();
|
||||||
|
self.current_attempt.set(new_attempt);
|
||||||
|
}
|
||||||
|
Key::Enter => {
|
||||||
|
if current_attempt_len < 5 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !WORDS.contains(&*self.current_attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_attempts = (*self.attempts).clone();
|
||||||
|
let new_attempt_row = (*self.current_attempt)
|
||||||
|
.clone()
|
||||||
|
.chars()
|
||||||
|
.zip((*self.answer).chars())
|
||||||
|
.map(|(att, ans)| {
|
||||||
|
let status = evaluate_status(ans, att, (*self.answer).clone());
|
||||||
|
AttemptField {
|
||||||
|
char_field: att,
|
||||||
|
status,
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
new_attempts.fields.push(new_attempt_row);
|
||||||
|
|
||||||
|
self.attempts.set(new_attempts);
|
||||||
|
*self.attempt_index.borrow_mut() += 1;
|
||||||
|
|
||||||
|
if (*self.current_attempt) == (*self.answer) {
|
||||||
|
self.result.set(Some(GameResult::Win));
|
||||||
|
} else if *self.attempt_index.borrow() == MAX_ATTEMPTS {
|
||||||
|
self.result.set(Some(GameResult::Fail));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_attempt.set(String::default());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hook]
|
||||||
|
pub fn use_board() -> UseBoardHandle {
|
||||||
|
let current_attempt = use_state(|| "".to_string());
|
||||||
|
let attempts: UseStateHandle<Attempts> = use_state(|| Attempts::new());
|
||||||
|
let attempt_index = use_mut_ref(|| 0usize);
|
||||||
|
// let answer = use_memo(|_| get_word_of_the_day(), None::<()>);
|
||||||
|
let answer = use_memo(|_| "ABEJA".to_owned(), None::<()>);
|
||||||
|
let virtual_keyboard = use_state(|| new_empty_virtual_keyboard().into());
|
||||||
|
let result = use_state(|| None::<GameResult>);
|
||||||
|
|
||||||
|
let send_key = {
|
||||||
|
let current_attempt = current_attempt.clone();
|
||||||
|
let current_attempt_len = current_attempt.chars().count();
|
||||||
|
let attempts = attempts.clone();
|
||||||
|
let answer = (*answer).clone();
|
||||||
|
let attempt_index = attempt_index.clone();
|
||||||
|
let result = result.clone();
|
||||||
|
|
||||||
|
move |k: Key| {}
|
||||||
|
};
|
||||||
|
UseBoardHandle {
|
||||||
|
current_attempt,
|
||||||
|
attempts,
|
||||||
|
attempt_index,
|
||||||
|
virtual_keyboard,
|
||||||
|
result,
|
||||||
|
answer,
|
||||||
|
}
|
||||||
|
}
|
130
src/main.rs
130
src/main.rs
|
@ -1,131 +1,61 @@
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::components::AttemptRow;
|
use crate::components::{AttemptRow, Board};
|
||||||
use crate::components::CurrentRow;
|
use crate::components::CurrentRow;
|
||||||
use crate::components::EmptyRow;
|
use crate::components::EmptyRow;
|
||||||
|
use crate::components::Keyboard;
|
||||||
use crate::components::ResultBoard;
|
use crate::components::ResultBoard;
|
||||||
use crate::consts::Key;
|
use crate::consts::{Key, MAX_ATTEMPTS};
|
||||||
use crate::consts::Result;
|
use crate::hooks::use_board;
|
||||||
use crate::consts::MAX_ATTEMPTS;
|
|
||||||
use crate::services::words::{get_all_words, get_word_of_the_day};
|
|
||||||
use chrono::Datelike;
|
|
||||||
|
|
||||||
use gloo::console;
|
use gloo::console;
|
||||||
|
|
||||||
mod components;
|
mod components;
|
||||||
mod consts;
|
mod consts;
|
||||||
|
mod hooks;
|
||||||
mod services;
|
mod services;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
fn App() -> Html {
|
fn App() -> Html {
|
||||||
let answer = use_state(|| get_word_of_the_day());
|
let board = use_board();
|
||||||
let all_the_words = use_memo(|_| get_all_words(), 0);
|
|
||||||
let attempts: UseStateHandle<Vec<String>> = use_state(|| vec![String::default(); MAX_ATTEMPTS]);
|
|
||||||
let attempt_index = use_state(|| 0usize);
|
|
||||||
let current_attempt = use_state(|| String::new());
|
|
||||||
let result = use_state(|| None::<Result>);
|
|
||||||
|
|
||||||
let onkeypress = {
|
let onkeypress = {
|
||||||
console::log!(chrono::Utc::now().year());
|
let board = board.clone();
|
||||||
let current = current_attempt.clone();
|
|
||||||
let answer = answer.clone();
|
|
||||||
let answer_str = answer.clone().to_string();
|
|
||||||
let current_str = current.clone().to_string();
|
|
||||||
let attempts = attempts.clone();
|
|
||||||
let result = result.clone();
|
|
||||||
let index = attempt_index.clone();
|
|
||||||
|
|
||||||
move |event: KeyboardEvent| {
|
|
||||||
console::log!(&event);
|
|
||||||
|
|
||||||
if result.is_some() {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Callback::from(move |event: KeyboardEvent| {
|
||||||
let key_code = event.key_code();
|
let key_code = event.key_code();
|
||||||
|
board.send_key(Key::from(key_code));
|
||||||
|
console::log!(&event);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
match Key::from(key_code) {
|
let onclick = {
|
||||||
Key::CharKey(c) => {
|
let board = board.clone();
|
||||||
if current.chars().count() >= 5 {
|
|
||||||
return ();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_attempt = current_str.clone();
|
Callback::from(move |key| {
|
||||||
new_attempt.push(c);
|
board.send_key(key);
|
||||||
console::log!(&new_attempt);
|
})
|
||||||
current.set(new_attempt);
|
|
||||||
}
|
|
||||||
Key::Backspace => {
|
|
||||||
if current.is_empty() {
|
|
||||||
return ();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_attempt = current_str.clone();
|
|
||||||
// This unwrap is safe because the potential failura it's already covered
|
|
||||||
// by the upper if
|
|
||||||
new_attempt.pop().unwrap();
|
|
||||||
console::log!(&new_attempt);
|
|
||||||
current.set(new_attempt);
|
|
||||||
}
|
|
||||||
Key::Enter => {
|
|
||||||
if current.chars().count() != 5 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !all_the_words.contains(¤t) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_attempts = attempts.clone().to_vec();
|
|
||||||
new_attempts[*index] = current_str.clone();
|
|
||||||
|
|
||||||
attempts.set(new_attempts);
|
|
||||||
current.set(String::default());
|
|
||||||
index.set(*index + 1);
|
|
||||||
|
|
||||||
if ¤t_str == &answer_str {
|
|
||||||
result.set(Some(Result::Win));
|
|
||||||
} else if *index == MAX_ATTEMPTS {
|
|
||||||
result.set(Some(Result::Fail));
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
<h1>{ "Worsdle" }</h1>
|
<h1>{ "Worsdle" }</h1>
|
||||||
<div class="board" onkeyup={onkeypress} tabindex="0">
|
<Board
|
||||||
{
|
attempts={ (*board.attempts).clone() }
|
||||||
for attempts.clone().iter().enumerate().map(|(i, attempt)| {
|
attempt_index={ *board.attempt_index.borrow() }
|
||||||
let current = current_attempt.clone().to_string();
|
current_attempt={ (*board.current_attempt).clone() }
|
||||||
let attempt = attempt.clone();
|
onkeypress={ onkeypress }
|
||||||
|
/>
|
||||||
if i < *attempt_index {
|
<div>
|
||||||
let answer = answer.clone().to_string();
|
|
||||||
|
|
||||||
html! { <AttemptRow text={attempt} answer={answer} /> }
|
|
||||||
} else if i == *attempt_index {
|
|
||||||
html! { <CurrentRow text={ current } /> }
|
|
||||||
} else {
|
|
||||||
html! { <EmptyRow /> }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class={ format!("result {}", if result.is_some() { "" } else { "hidden" }) } >
|
|
||||||
<span> {
|
<span> {
|
||||||
match *result {
|
match (*board.result).clone() {
|
||||||
Some(Result::Win) => "Ganaste!!",
|
Some(res) => html! { <ResultBoard attempts={(*board.attempts).clone()} /> },
|
||||||
Some(Result::Fail) => "Perdiste :c",
|
_ => html! { <Keyboard
|
||||||
_ => "No deberías ver esto"
|
keyboard={ (board.virtual_keyboard).clone() }
|
||||||
|
onclick={ onclick } /> }
|
||||||
}
|
}
|
||||||
} </span>
|
} </span>
|
||||||
< ResultBoard answer={ answer.to_string().clone() } attempts={(*attempts).clone()} />
|
|
||||||
<p> { "Vuelve mañana para otro desafío :)" } </p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
use crate::consts::{KeyboardKeyType, Status, VirtualKey};
|
||||||
|
|
||||||
|
pub fn new_empty_virtual_keyboard() -> [VirtualKey; 29] {
|
||||||
|
[
|
||||||
|
VirtualKey::from_charkey('Q'),
|
||||||
|
VirtualKey::from_charkey('W'),
|
||||||
|
VirtualKey::from_charkey('E'),
|
||||||
|
VirtualKey::from_charkey('R'),
|
||||||
|
VirtualKey::from_charkey('T'),
|
||||||
|
VirtualKey::from_charkey('Y'),
|
||||||
|
VirtualKey::from_charkey('U'),
|
||||||
|
VirtualKey::from_charkey('I'),
|
||||||
|
VirtualKey::from_charkey('O'),
|
||||||
|
VirtualKey::from_charkey('P'),
|
||||||
|
VirtualKey::from_charkey('A'),
|
||||||
|
VirtualKey::from_charkey('S'),
|
||||||
|
VirtualKey::from_charkey('D'),
|
||||||
|
VirtualKey::from_charkey('F'),
|
||||||
|
VirtualKey::from_charkey('G'),
|
||||||
|
VirtualKey::from_charkey('H'),
|
||||||
|
VirtualKey::from_charkey('J'),
|
||||||
|
VirtualKey::from_charkey('K'),
|
||||||
|
VirtualKey::from_charkey('L'),
|
||||||
|
VirtualKey::from_charkey('Ñ'),
|
||||||
|
VirtualKey::from_key(KeyboardKeyType::Backspace),
|
||||||
|
VirtualKey::from_charkey('Z'),
|
||||||
|
VirtualKey::from_charkey('X'),
|
||||||
|
VirtualKey::from_charkey('C'),
|
||||||
|
VirtualKey::from_charkey('V'),
|
||||||
|
VirtualKey::from_charkey('B'),
|
||||||
|
VirtualKey::from_charkey('N'),
|
||||||
|
VirtualKey::from_charkey('M'),
|
||||||
|
VirtualKey::from_key(KeyboardKeyType::Enter),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate_status(ans: char, attr: char, answer: String) -> Status {
|
||||||
|
if ans == attr {
|
||||||
|
Status::Found
|
||||||
|
} else if answer.contains(attr) {
|
||||||
|
Status::Almost
|
||||||
|
} else {
|
||||||
|
Status::NotFound
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,349 @@
|
||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -82,3 +82,16 @@ body {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.keyboard {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(20, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard > button {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard > button.key-big {
|
||||||
|
grid-column: span 3;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue