Skip to main content

vertigo_forms/select/
multi_select.rs

1use vertigo::{Computed, Css, DomNode, Value, bind, bind_rc, css, dom, render::render_list};
2
3/// Select component based on vector of `T` values,
4/// which allows to have multiple options selected at once.
5///
6/// Example:
7/// ```
8/// use vertigo::{DomNode, dom, Value};
9/// use vertigo_forms::MultiSelect;
10///
11/// let value = Value::new(vec!["foo".to_string()]);
12/// let options = Value::new(
13///     vec![
14///         "foo".to_string(),
15///         "bar".to_string(),
16///         "baz".to_string(),
17///     ]
18/// );
19///
20/// dom! {
21///     <MultiSelect
22///         value={value.clone()}
23///         options={options}
24///     />
25/// };
26/// ```
27pub struct MultiSelect<T: Clone + PartialEq + 'static> {
28    pub value: Value<Vec<T>>,
29    pub options: Computed<Vec<T>>,
30}
31
32impl<T> MultiSelect<T>
33where
34    T: Clone + From<String> + PartialEq + ToString + 'static,
35{
36    pub fn into_component(self) -> Self {
37        self
38    }
39
40    pub fn mount(&self) -> DomNode {
41        let Self { value, options } = self;
42        let toggle = bind_rc!(value, |item: &T| {
43            value.change(|value| {
44                if let Some(idx) = value.iter().position(|i| i == item) {
45                    value.remove(idx);
46                } else {
47                    value.push(item.clone());
48                }
49            });
50        });
51
52        let list = bind!(
53            options,
54            toggle,
55            value.render_value(move |value| {
56                bind!(
57                    toggle,
58                    render_list(
59                        &options,
60                        |item| item.to_string(),
61                        bind!(toggle, |item| {
62                            let text_item = item.to_string();
63                            let on_click = bind!(toggle, item, |_| toggle(&item));
64                            let css = if value.contains(item) {
65                                css! {"
66                            border-style: inset;
67                            font-weight: bold;
68                            color: green;
69                        "}
70                            } else {
71                                Css::default()
72                            };
73                            dom! {
74                                <button {css} {on_click}>{text_item}</button>
75                            }
76                        })
77                    )
78                )
79            })
80        );
81
82        let list_css = css! {"
83            display: flex;
84            flex-wrap: wrap;
85        "};
86
87        dom! {
88            <div css={list_css}>
89                {list}
90            </div>
91        }
92    }
93}