impulse_thaw/select/
select.rs

1use super::{SelectRule, SelectSize};
2
3use crate::{icon::ChevronDownRegularIcon, FieldInjection, Rule, SelectRuleTrigger};
4use leptos::{html, prelude::*};
5use thaw_utils::{class_list, mount_style, Model};
6
7#[component]
8pub fn Select(
9    #[prop(optional, into)] class: MaybeProp<String>,
10    #[prop(optional, into)] id: MaybeProp<String>,
11    #[prop(optional, into)] rules: Vec<SelectRule>,
12    /// A string specifying a name for the input control.
13    /// This name is submitted along with the control's value when the form data is submitted.
14    #[prop(optional, into)]
15    name: MaybeProp<String>,
16    #[prop(optional, into)] value: Model<String>,
17    #[prop(optional, into)] default_value: Option<String>,
18    /// Whether the select is disabled.
19    #[prop(optional, into)]
20    disabled: Signal<bool>,
21    /// Matches the Input sizes.
22    #[prop(optional, into)]
23    size: Signal<SelectSize>,
24    children: Children,
25) -> impl IntoView {
26    mount_style("select", include_str!("./select.css"));
27    let (id, name) = FieldInjection::use_id_and_name(id, name);
28    let validate = Rule::validate(rules, value, name);
29    let select_ref = NodeRef::<html::Select>::new();
30    Effect::new(move |prev: Option<bool>| {
31        let Some(el) = select_ref.get() else {
32            return false;
33        };
34
35        let el_value = el.value();
36        if !prev.unwrap_or_default() {
37            if let Some(default_value) = default_value.as_ref() {
38                el.set_value(default_value);
39                value.set(default_value.clone());
40            } else {
41                value.set(el_value);
42            }
43            value.with(|_| {});
44            return true;
45        }
46        value.with(|value| {
47            if value != &el_value {
48                el.set_value(value);
49            }
50        });
51        true
52    });
53
54    let on_change = move |_| {
55        let Some(el) = select_ref.get() else {
56            return;
57        };
58        value.set(el.value());
59        validate.run(Some(SelectRuleTrigger::Change));
60    };
61
62    view! {
63        <span class=class_list![
64            "thaw-select",
65            ("thaw-select--disabled", move || disabled.get()),
66            move || format!("thaw-select--{}", size.get().as_str()),
67            class
68        ]>
69            <select
70                class="thaw-select__select"
71                id=id
72                name=name
73                node_ref=select_ref
74                disabled=disabled
75                on:change=on_change
76            >
77                {children()}
78            </select>
79            <span class="thaw-select__icon">
80                <ChevronDownRegularIcon />
81            </span>
82        </span>
83    }
84}