#[doc(inline)]
pub use crate::list::{ActionDetail, ListIndex, SelectedDetail};
use crate::text_inputs::{
validity_state::ValidityStateJS, NativeValidityState, ValidityState, ValidityTransform,
};
use crate::utils::WeakComponentLink;
use crate::{bool_to_option, event_into_details, to_option_string};
use gloo::events::EventListener;
use wasm_bindgen::prelude::*;
use web_sys::Node;
use yew::prelude::*;
use yew::virtual_dom::AttrValue;
#[wasm_bindgen(module = "/build/mwc-select.js")]
extern "C" {
#[derive(Debug)]
#[wasm_bindgen(extends = Node)]
type Select;
#[wasm_bindgen(getter, static_method_of = Select)]
fn _dummy_loader() -> JsValue;
#[wasm_bindgen(method)]
fn select(this: &Select, index: usize);
#[wasm_bindgen(method, setter = validityTransform)]
fn set_validity_transform(
this: &Select,
val: &Closure<dyn Fn(String, NativeValidityState) -> ValidityStateJS>,
);
}
loader_hack!(Select);
pub struct MatSelect {
node_ref: NodeRef,
validity_transform_closure:
Option<Closure<dyn Fn(String, NativeValidityState) -> ValidityStateJS>>,
opened_listener: Option<EventListener>,
closed_listener: Option<EventListener>,
action_listener: Option<EventListener>,
selected_listener: Option<EventListener>,
}
#[derive(Properties, PartialEq, Clone)]
pub struct Props {
#[prop_or_default]
pub value: Option<AttrValue>,
#[prop_or_default]
pub label: Option<AttrValue>,
#[prop_or_default]
pub natural_menu_width: bool,
#[prop_or_default]
pub icon: Option<AttrValue>,
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub outlined: bool,
#[prop_or_default]
pub helper: Option<AttrValue>,
#[prop_or_default]
pub required: bool,
#[prop_or_default]
pub validation_message: Option<AttrValue>,
#[prop_or_default]
pub items: Option<AttrValue>,
#[prop_or(- 1)]
pub index: i64,
#[prop_or_default]
pub validity_transform: Option<ValidityTransform>,
#[prop_or_default]
pub validate_on_initial_render: bool,
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub select_link: WeakComponentLink<MatSelect>,
#[prop_or_default]
pub onopened: Callback<()>,
#[prop_or_default]
pub onclosed: Callback<()>,
#[prop_or_default]
pub onaction: Callback<ActionDetail>,
#[prop_or_default]
pub onselected: Callback<SelectedDetail>,
}
impl Component for MatSelect {
type Message = ();
type Properties = Props;
fn create(ctx: &Context<Self>) -> Self {
ctx.props()
.select_link
.borrow_mut()
.replace(ctx.link().clone());
Select::ensure_loaded();
Self {
node_ref: NodeRef::default(),
validity_transform_closure: None,
opened_listener: None,
closed_listener: None,
action_listener: None,
selected_listener: None,
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
html! {
<mwc-select
value={props.value.clone()}
label={props.label.clone()}
naturalMenuWidth={bool_to_option(props.natural_menu_width)}
icon={props.icon.clone()}
disabled={props.disabled}
outlined={bool_to_option(props.outlined)}
helper={props.helper.clone()}
required={props.required}
validationMessage={props.validation_message.clone()}
items={props.items.clone()}
index={to_option_string(props.index)}
validateOnInitialRender={bool_to_option(props.validate_on_initial_render)}
ref={self.node_ref.clone()}
>
{props.children.clone()}
</mwc-select>
}
}
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
self.opened_listener = None;
self.closed_listener = None;
self.action_listener = None;
self.selected_listener = None;
ctx.props()
.select_link
.borrow_mut()
.replace(ctx.link().clone());
true
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
let props = ctx.props();
let element = self.node_ref.cast::<Select>().unwrap();
if first_render {
if let Some(transform) = props.validity_transform.clone() {
self.validity_transform_closure = Some(Closure::wrap(Box::new(
move |s: String, v: NativeValidityState| -> ValidityStateJS {
transform.0(s, v).into()
},
)
as Box<dyn Fn(String, NativeValidityState) -> ValidityStateJS>));
element.set_validity_transform(self.validity_transform_closure.as_ref().unwrap());
}
}
if self.opened_listener.is_none() {
let onopened = props.onopened.clone();
self.opened_listener = Some(EventListener::new(&element, "opened", move |_| {
onopened.emit(())
}));
}
if self.closed_listener.is_none() {
let onclosed = props.onclosed.clone();
self.closed_listener = Some(EventListener::new(&element, "closed", move |_| {
onclosed.emit(())
}));
}
if self.action_listener.is_none() {
let on_action = props.onaction.clone();
self.action_listener = Some(EventListener::new(&element, "action", move |event| {
on_action.emit(ActionDetail::from(event_into_details(event)))
}));
}
if self.selected_listener.is_none() {
let on_selected = props.onselected.clone();
self.selected_listener = Some(EventListener::new(&element, "selected", move |event| {
on_selected.emit(SelectedDetail::from(event_into_details(event)))
}));
}
}
}
impl WeakComponentLink<MatSelect> {
pub fn select(&self, val: usize) {
let c = self
.borrow()
.as_ref()
.unwrap()
.get_component()
.unwrap()
.node_ref
.clone();
let select_element = c.cast::<Select>().unwrap();
select_element.select(val);
}
}
impl MatSelect {
pub fn validity_transform<F: Fn(String, NativeValidityState) -> ValidityState + 'static>(
func: F,
) -> ValidityTransform {
ValidityTransform::new(func)
}
}