1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
use wasm_bindgen::UnwrapThrowExt;
use web_sys::HtmlSelectElement;
use yew::prelude::*;
use crate::Size;
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct SelectProps {
/// The `name` attribute for this form element.
pub name: String,
/// The controlled value of this form element.
pub value: String,
/// The callback to be used for propagating changes to this element's value.
pub update: Callback<String>,
/// The `option` & `optgroup` tags of this select component.
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub classes: Classes,
/// The size of this component.
#[prop_or_default]
pub size: Option<Size>,
/// Display a loading spinner within this component.
#[prop_or_default]
pub loading: bool,
/// Disable this component.
#[prop_or_default]
pub disabled: bool,
}
/// A wrapper around an HTML `select` tag.
///
/// [https://bulma.io/documentation/form/select/](https://bulma.io/documentation/form/select/)
///
/// All YBC form components are controlled components. This means that the value of the field must
/// be provided from a parent component, and changes to this component are propagated to the parent
/// component via callback.
///
/// **NOTE WELL:** not all browsers will honor the value of the select element's value on initial
/// load. So if you have an initial `value` set for this component, ensure that the corresponding
/// option element also has the `selected=true` attribute.
#[function_component(Select)]
pub fn select(props: &SelectProps) -> Html {
let class = classes!(
"select",
props.classes.clone(),
props.size.as_ref().map(|size| size.to_string()),
props.loading.then_some("is-loading"),
);
let onchange = props.update.reform(|ev: web_sys::Event| {
let select: HtmlSelectElement = ev.target_dyn_into().expect_throw("event target should be a select");
select.value()
});
html! {
<div {class}>
<select
name={props.name.clone()}
value={props.value.clone()}
disabled={props.disabled}
{onchange}
>
{props.children.clone()}
</select>
</div>
}
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
#[derive(Properties, Clone, PartialEq)]
pub struct MultiSelectProps {
/// The `name` attribute for this form element.
pub name: String,
/// The controlled value of this form element.
pub value: Vec<String>,
/// The callback to be used for propagating changes to this element's value.
pub update: Callback<Vec<String>>,
/// The `option` & `optgroup` tags of this select component.
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub classes: Classes,
/// The size of this component.
#[prop_or_default]
pub size: Option<Size>,
/// Size of the list to display.
#[prop_or_else(|| 4)]
pub list_size: u32,
/// Display a loading spinner within this component.
#[prop_or_default]
pub loading: bool,
/// Disable this component.
#[prop_or_default]
pub disabled: bool,
}
/// A wrapper around an HTML `select` tag with the `multiple=true` attribute.
///
/// [https://bulma.io/documentation/form/select/](https://bulma.io/documentation/form/select/)
///
/// All YBC form components are controlled components. This means that the value of the field must
/// be provided from a parent component, and changes to this component are propagated to the parent
/// component via callback.
///
/// **NOTE WELL:** not all browsers will honor the value of the select element's value on initial
/// load. So if you have an initial `value` set for this component, ensure that the corresponding
/// option element also has the `selected=true` attribute.
#[function_component(MultiSelect)]
pub fn multi_select(props: &MultiSelectProps) -> Html {
let class = classes!(
"select",
"is-multiple",
props.classes.clone(),
props.size.as_ref().map(|size| size.to_string()),
props.loading.then_some("is-loading"),
);
let size = props.list_size.to_string();
let onchange = props.update.reform(|ev: web_sys::Event| {
let select: HtmlSelectElement = ev.target_dyn_into().expect_throw("event target should be a select");
let opts = select.selected_options();
(0..opts.length())
.into_iter()
.filter_map(|idx| opts.item(idx))
.filter_map(|elem| elem.get_attribute("value").or_else(|| elem.text_content()))
.collect::<Vec<_>>()
});
html! {
<div {class}>
<select
multiple=true
size={size}
name={props.name.clone()}
value={props.value.join(",")}
disabled={props.disabled}
{onchange}
>
{props.children.clone()}
</select>
</div>
}
}