vertigo_forms/select/
multi_select.rs

1use vertigo::{Computed, Css, DomNode, Value, bind, bind_rc, css, dom};
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> {
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                    options.render_list(
59                        |item| item.to_string(),
60                        bind!(toggle, |item| {
61                            let text_item = item.to_string();
62                            let on_click = bind!(toggle, item, |_| toggle(&item));
63                            let css = if value.contains(item) {
64                                css! {"
65                            border-style: inset;
66                            font-weight: bold;
67                            color: green;
68                        "}
69                            } else {
70                                Css::default()
71                            };
72                            dom! {
73                                <button {css} {on_click}>{text_item}</button>
74                            }
75                        })
76                    )
77                )
78            })
79        );
80
81        let list_css = css! {"
82            display: flex;
83            flex-wrap: wrap;
84        "};
85
86        dom! {
87            <div css={list_css}>
88                {list}
89            </div>
90        }
91    }
92}