vertigo-forms 0.2.1

Building block for forms in vertigo
Documentation
use vertigo::{
    Computed, Css, DomNode, Value, bind, bind_rc, computed_tuple, css, dom, render::render_list,
    transaction,
};

/// Select component based on vector of `T` values,
/// which allows to have multiple options selected at once.
///
/// Example:
/// ```
/// use vertigo::{DomNode, dom, Value};
/// use vertigo_forms::MultiSelect;
///
/// let value = Value::new(vec!["foo".to_string()]);
/// let options = Value::new(
///     vec![
///         "foo".to_string(),
///         "bar".to_string(),
///         "baz".to_string(),
///     ]
/// );
///
/// dom! {
///     <MultiSelect
///         value={value.clone()}
///         options={options}
///     />
/// };
/// ```
pub struct MultiSelect<T: Clone + PartialEq + 'static> {
    pub value: Value<Vec<T>>,
    pub options: Computed<Vec<T>>,
}

impl<T> MultiSelect<T>
where
    T: Clone + From<String> + PartialEq + ToString + 'static,
{
    pub fn into_component(self) -> Self {
        self
    }

    pub fn mount(&self) -> DomNode {
        let Self { value, options } = self;
        let toggle = bind_rc!(value, |item: &T| {
            value.change(|value| {
                if let Some(idx) = value.iter().position(|i| i == item) {
                    value.remove(idx);
                } else {
                    value.push(item.clone());
                }
            });
        });

        let list = render_list(options, |item| item.to_string(), {
            let toggle = toggle.clone();
            let value = value.clone();
            move |item: &Computed<T>| {
                let item = item.clone();
                let text_item = item.map(|item| item.to_string());

                let on_click = bind!(toggle, item, |_| transaction(|ctx| toggle(&item.get(ctx))));

                let css = computed_tuple!(item, value).map(|(item, value)| {
                    if value.contains(&item) {
                        css! {"
                            border-style: inset;
                            font-weight: bold;
                            color: green;
                        "}
                    } else {
                        Css::default()
                    }
                });

                dom! {
                    <button {css} {on_click}>{text_item}</button>
                }
            }
        });

        let list_css = css! {"
            display: flex;
            flex-wrap: wrap;
        "};

        dom! {
            <div css={list_css}>
                {list}
            </div>
        }
    }
}