diff --git a/Cargo.toml b/Cargo.toml index 370335f..6aa3a86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ implicit-clone = "0.3.5" wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["DomRect", "Element", "Event", "HtmlSelectElement", "DomTokenList"] } yew = "0.20" +yew-callbacks = "0.2.1" [workspace] members = ["yewprint-css", "yewprint-doc", "xtask"] diff --git a/src/html_select.rs b/src/html_select.rs index 1dbb532..110b39e 100644 --- a/src/html_select.rs +++ b/src/html_select.rs @@ -1,15 +1,13 @@ -use implicit_clone::{unsync::IArray, ImplicitClone}; -use std::marker::PhantomData; - use crate::Icon; +use implicit_clone::{unsync::IArray, ImplicitClone}; use web_sys::HtmlSelectElement; use yew::prelude::*; #[derive(Debug)] -pub struct HtmlSelect { +pub struct HtmlSelect { select_element: NodeRef, - update_selected: bool, - phantom: PhantomData, + cb: HtmlSelectMessageCallbacks, + phantom: std::marker::PhantomData, } #[derive(Debug, Clone, PartialEq, Properties)] @@ -33,35 +31,50 @@ pub struct HtmlSelectProps { pub class: Classes, } +#[derive(Debug, yew_callbacks::Callbacks)] +pub enum HtmlSelectMessage { + OnChange(Event), + // NOTE: the component becomes tainted when a value is selected because there is no way to + // prevent the selection of the option in an HTML component. This means the + // actual value and the visible value might differ. + Untaint, +} + impl Component for HtmlSelect { - type Message = Event; + type Message = HtmlSelectMessage; type Properties = HtmlSelectProps; - fn create(_ctx: &Context) -> Self { + fn create(ctx: &Context) -> Self { Self { select_element: NodeRef::default(), - update_selected: false, - phantom: PhantomData, + cb: ctx.link().into(), + phantom: Default::default(), } } fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { - let i = if let Some(select) = msg.target_dyn_into::() { - select.selected_index() - } else { - unreachable!("unexpected Event: {:?}", msg); - }; - if i >= 0 { - let i = i as usize; - let variant = ctx.props().options[i].0.clone(); - ctx.props().onchange.emit(variant); + match msg { + HtmlSelectMessage::OnChange(event) => { + let i = if let Some(select) = event.target_dyn_into::() { + select.selected_index() + } else { + unreachable!("unexpected Event: {:?}", event); + }; + if i >= 0 { + let i = i as usize; + let variant = ctx.props().options[i].0.clone(); + ctx.props().onchange.emit(variant); + // NOTE: register option selection update for later even if the parent + // component is not going to re-render + ctx.link().send_message(HtmlSelectMessage::Untaint); + } + false + } + HtmlSelectMessage::Untaint => { + self.select_option(ctx); + false + } } - false - } - - fn changed(&mut self, _ctx: &Context, _old_props: &Self::Properties) -> bool { - self.update_selected = true; - true } fn view(&self, ctx: &Context) -> Html { @@ -104,7 +117,7 @@ impl Component for HtmlSelect {