mirror of
https://github.com/yewprint/yewprint
synced 2024-11-25 04:50:20 +00:00
HtmlSelect: fix handling of unchanged option selection (#167)
This commit is contained in:
parent
b71f69ade9
commit
f91f5810be
2 changed files with 53 additions and 35 deletions
|
@ -24,6 +24,7 @@ implicit-clone = "0.3.5"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
web-sys = { version = "0.3", features = ["DomRect", "Element", "Event", "HtmlSelectElement", "DomTokenList"] }
|
web-sys = { version = "0.3", features = ["DomRect", "Element", "Event", "HtmlSelectElement", "DomTokenList"] }
|
||||||
yew = "0.20"
|
yew = "0.20"
|
||||||
|
yew-callbacks = "0.2.1"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["yewprint-css", "yewprint-doc", "xtask"]
|
members = ["yewprint-css", "yewprint-doc", "xtask"]
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
use implicit_clone::{unsync::IArray, ImplicitClone};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::Icon;
|
use crate::Icon;
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use web_sys::HtmlSelectElement;
|
use web_sys::HtmlSelectElement;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct HtmlSelect<T: Clone + PartialEq + 'static> {
|
pub struct HtmlSelect<T: ImplicitClone + PartialEq + 'static> {
|
||||||
select_element: NodeRef,
|
select_element: NodeRef,
|
||||||
update_selected: bool,
|
cb: HtmlSelectMessageCallbacks<Self>,
|
||||||
phantom: PhantomData<T>,
|
phantom: std::marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||||
|
@ -33,35 +31,50 @@ pub struct HtmlSelectProps<T: ImplicitClone + PartialEq + 'static> {
|
||||||
pub class: Classes,
|
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 <input> HTML component. This means the
|
||||||
|
// actual value and the visible value might differ.
|
||||||
|
Untaint,
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ImplicitClone + PartialEq + 'static> Component for HtmlSelect<T> {
|
impl<T: ImplicitClone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
type Message = Event;
|
type Message = HtmlSelectMessage;
|
||||||
type Properties = HtmlSelectProps<T>;
|
type Properties = HtmlSelectProps<T>;
|
||||||
|
|
||||||
fn create(_ctx: &Context<Self>) -> Self {
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
select_element: NodeRef::default(),
|
select_element: NodeRef::default(),
|
||||||
update_selected: false,
|
cb: ctx.link().into(),
|
||||||
phantom: PhantomData,
|
phantom: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
let i = if let Some(select) = msg.target_dyn_into::<HtmlSelectElement>() {
|
match msg {
|
||||||
select.selected_index()
|
HtmlSelectMessage::OnChange(event) => {
|
||||||
} else {
|
let i = if let Some(select) = event.target_dyn_into::<HtmlSelectElement>() {
|
||||||
unreachable!("unexpected Event: {:?}", msg);
|
select.selected_index()
|
||||||
};
|
} else {
|
||||||
if i >= 0 {
|
unreachable!("unexpected Event: {:?}", event);
|
||||||
let i = i as usize;
|
};
|
||||||
let variant = ctx.props().options[i].0.clone();
|
if i >= 0 {
|
||||||
ctx.props().onchange.emit(variant);
|
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<Self>, _old_props: &Self::Properties) -> bool {
|
|
||||||
self.update_selected = true;
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
@ -104,7 +117,7 @@ impl<T: ImplicitClone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
<select
|
<select
|
||||||
value={String::new()}
|
value={String::new()}
|
||||||
disabled={*disabled}
|
disabled={*disabled}
|
||||||
onchange={ctx.link().callback(|x| x)}
|
onchange={self.cb.on_change()}
|
||||||
{title}
|
{title}
|
||||||
ref={self.select_element.clone()}
|
ref={self.select_element.clone()}
|
||||||
>
|
>
|
||||||
|
@ -116,15 +129,19 @@ impl<T: ImplicitClone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
|
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
|
||||||
if self.update_selected {
|
// NOTE: ensure correct option is selected
|
||||||
self.update_selected = false;
|
self.select_option(ctx);
|
||||||
if let Some(value) = ctx.props().value.as_ref() {
|
}
|
||||||
if let Some(select) = self.select_element.cast::<HtmlSelectElement>() {
|
}
|
||||||
if let Some(i) = ctx.props().options.iter().position(|(x, _)| &x == value) {
|
|
||||||
if let Ok(i) = i.try_into() {
|
impl<T: ImplicitClone + PartialEq + 'static> HtmlSelect<T> {
|
||||||
if select.selected_index() != i {
|
fn select_option(&self, ctx: &Context<Self>) {
|
||||||
select.set_selected_index(i);
|
if let Some(value) = ctx.props().value.as_ref() {
|
||||||
}
|
if let Some(select) = self.select_element.cast::<HtmlSelectElement>() {
|
||||||
|
if let Some(i) = ctx.props().options.iter().position(|(x, _)| &x == value) {
|
||||||
|
if let Ok(i) = i.try_into() {
|
||||||
|
if select.selected_index() != i {
|
||||||
|
select.set_selected_index(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue