From 961e2e4012d75e0739ffbe3ea343c8a5c64e94ef Mon Sep 17 00:00:00 2001 From: Adam Steinberg <43798340+AdamSteinberg1@users.noreply.github.com> Date: Fri, 10 Nov 2023 22:35:38 -0500 Subject: [PATCH] text entry --- Cargo.toml | 2 +- src/components/button_input.rs | 23 +++++++++++ src/components/guesser.rs | 69 +++++++++++++++++++------------- src/components/mod.rs | 2 + src/components/pokemon_image.rs | 2 +- src/components/pokemon_label.rs | 2 +- src/components/text_input.rs | 71 +++++++++++++++++++++++++++++++++ styles.css | 47 ++++++++++++++++++++++ 8 files changed, 187 insertions(+), 31 deletions(-) create mode 100644 src/components/button_input.rs create mode 100644 src/components/text_input.rs diff --git a/Cargo.toml b/Cargo.toml index 6b206ff..29a9327 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ getrandom = { version = "0.2.10", features = ["js"] } gloo = "0.10.0" itertools = "0.11.0" rand = "0.8.5" -web-sys = "0.3.64" +web-sys = { version = "0.3.64", features = ["DomTokenList"] } structmap = "0.1.6" structmap-derive = "0.1.6" wasm-bindgen = "0.2.87" diff --git a/src/components/button_input.rs b/src/components/button_input.rs new file mode 100644 index 0000000..2df7593 --- /dev/null +++ b/src/components/button_input.rs @@ -0,0 +1,23 @@ +use ::yew::prelude::*; + +#[derive(PartialEq, Properties)] +pub struct Props { + pub is_revealed: bool, + pub on_reveal: Callback, + pub on_new_pokemon: Callback, +} + +#[function_component] +pub fn ButtonInput( + Props { + is_revealed, + on_reveal, + on_new_pokemon, + }: &Props, +) -> Html { + if *is_revealed { + html! {} + } else { + html! {} + } +} diff --git a/src/components/guesser.rs b/src/components/guesser.rs index e618718..4e24d0e 100644 --- a/src/components/guesser.rs +++ b/src/components/guesser.rs @@ -1,10 +1,15 @@ use crate::{ - components::{pokemon_image::PokemonImage, pokemon_label::PokemonLabel}, + components::{ + button_input::ButtonInput, pokemon_image::PokemonImage, pokemon_label::PokemonLabel, + text_input::TextInput, + }, models::settings::Settings, util::fetch_pokemons, }; use ::yew::prelude::*; -use gloo::console::log; +use futures::TryFutureExt; +use gloo::console::error; +use std::rc::Rc; use yew::suspense::use_future; #[derive(PartialEq, Properties)] @@ -15,7 +20,15 @@ pub struct Props { #[function_component] pub fn Guesser(Props { settings }: &Props) -> HtmlResult { let is_name_revealed = use_state_eq(|| false); - let pokemons = use_future(fetch_pokemons)?; + let pokemons = use_future(|| fetch_pokemons().map_ok(Rc::new))?; + + let pokemons = match *pokemons { + Ok(ref pokemons) => Rc::clone(pokemons), + Err(ref e) => { + error!(format!("Error when fetching pokemon data:\n{:?}", e)); + return Ok(html! {

{"An error has occurred. 😢"}

}); + } + }; let on_reveal = { let is_name_revealed = is_name_revealed.clone(); @@ -26,35 +39,35 @@ pub fn Guesser(Props { settings }: &Props) -> HtmlResult { let on_new_pokemon = { let is_name_revealed = is_name_revealed.clone(); + let pokemons = Rc::clone(&pokemons); Callback::from(move |_| { is_name_revealed.set(false); + pokemons.next(); }) }; - match *pokemons { - Err(ref e) => { - log!(format!("Error when fetching pokemon data:\n{:?}", e)); - Ok(html! {

{"An error has occurred. 😢"}

}) - } - Ok(ref pokemons) => { - let pokemon = if *is_name_revealed { - pokemons.current() - } else { - pokemons.next() - }; - let next_pokemon = pokemons.peek(); - Ok(html! { - <> - - + let pokemon = pokemons.current(); + let next_pokemon = pokemons.peek(); + Ok(html! { + <> + + + if settings.text_entry { + if *is_name_revealed { - if *is_name_revealed { - - } else { - - } - - }) - } - } + } + + } else { + + + } + + }) } diff --git a/src/components/mod.rs b/src/components/mod.rs index 6821a94..7243c13 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,7 +1,9 @@ +pub mod button_input; pub mod guesser; pub mod pokemon_image; pub mod pokemon_label; pub mod repo_link; pub mod settings_menu; pub mod starburst; +pub mod text_input; pub mod title; diff --git a/src/components/pokemon_image.rs b/src/components/pokemon_image.rs index 5f29bb1..3b69897 100644 --- a/src/components/pokemon_image.rs +++ b/src/components/pokemon_image.rs @@ -4,7 +4,7 @@ use ::yew::prelude::*; #[derive(PartialEq, Properties)] pub struct Props { pub silhouette: bool, - pub image: String, + pub image: AttrValue, } #[function_component] diff --git a/src/components/pokemon_label.rs b/src/components/pokemon_label.rs index a64e4a4..5c2596a 100644 --- a/src/components/pokemon_label.rs +++ b/src/components/pokemon_label.rs @@ -3,7 +3,7 @@ use ::yew::prelude::*; #[derive(PartialEq, Properties)] pub struct Props { pub is_revealed: bool, - pub name: String, + pub name: AttrValue, pub id: u16, } diff --git a/src/components/text_input.rs b/src/components/text_input.rs new file mode 100644 index 0000000..92e71e7 --- /dev/null +++ b/src/components/text_input.rs @@ -0,0 +1,71 @@ +use ::yew::prelude::*; +use wasm_bindgen::JsCast; +use web_sys::HtmlElement; +use web_sys::HtmlInputElement; + +#[derive(PartialEq, Properties)] +pub struct Props { + pub is_revealed: bool, + pub on_reveal: Callback, + pub on_new_pokemon: Callback, + pub name: AttrValue, +} + +#[function_component] +pub fn TextInput( + Props { + name, + is_revealed, + on_reveal, + on_new_pokemon, + }: &Props, +) -> Html { + let on_submit = { + let name = name.clone(); + let on_reveal = on_reveal.clone(); + Callback::from(move |event: MouseEvent| { + let target: HtmlInputElement = event.target_unchecked_into(); + let text_input: HtmlInputElement = + target.previous_element_sibling().unwrap().unchecked_into(); + let guess = text_input.value(); + if guess.trim().eq_ignore_ascii_case(&name) { + on_reveal.emit(event); + } else { + text_input + .parent_element() + .map(|parent| parent.class_list().add_1("incorrect-guess")); + } + }) + }; + + let onkeydown = Callback::from(|event: KeyboardEvent| { + if event.key() == "Enter" { + let target: HtmlInputElement = event.target_unchecked_into(); + let target = if target.id() == "submit" { + target + } else { + target.next_element_sibling().unwrap().unchecked_into() + }; + target.click(); + } + }); + + let onanimationend = Callback::from(move |event: AnimationEvent| { + let target: HtmlElement = event.target_unchecked_into(); + let _ = target.class_list().remove_1("incorrect-guess"); + }); + + html! { + <> + if *is_revealed { + + } else { +
+ + +
+ + } + + } +} diff --git a/styles.css b/styles.css index 468159f..dab529a 100644 --- a/styles.css +++ b/styles.css @@ -158,4 +158,51 @@ p>span { border-color: var(--blue); border-width: 0.1rem; border-style: solid; +} + +@keyframes shake { + 0% { + transform: translate(0rem); + } + + 25% { + transform: translate(0.5rem); + } + + 75% { + transform: translate(-0.5rem); + } + + 100% { + transform: translate(0rem); + } +} + +.incorrect-guess { + animation: shake 0.2s ease-in-out 0s 2; +} + +.text-guess { + display: inline-block; + margin-bottom: 1.37rem; +} + +.text-guess>* { + font-size: 1.7em; + border-color: var(--blue); + border-radius: 0.3rem; + border-color: var(--blue); + border-width: 0.1rem; + border-style: solid; +} + +.text-guess>input[type=text] { + max-width: 50vw; + font-size: 2em; + text-align: center; +} + +.text-guess>input[type=button] { + color: var(--blue); + background-color: var(--yellow); } \ No newline at end of file