use crate::common::ValueType; use crate::components::utils::create_unique_id; use leptos::prelude::*; use std::collections::HashMap; use super::utils::use_selectable_list; use crate::components::ValidationState; /// # Checkbox Component /// This component should only be used from within a [`CheckboxList`] component. /// /// It creates a single checkbox with a specific value. Indicating if it is checked is done /// _externally_ from the component, and requires some sort of signal. The component itself /// does not track whether it is checked, it only runs the `on_change` callback. /// /// Example usage (from within a [`CheckboxList`]: /// ``` /// let disabled_signal = RwSignal::new(false); /// let checked_signal = Signal::derive(move || { /// // It's recommended to use a derived signal of some sort, but if you want to create /// // a manually controlled RwSignal, that's fine too. /// true /// }); /// /// view! { /// /// } /// ``` #[component] pub fn Checkbox( /// A [`Signal`] indicating whether the checkbox is checked. #[prop()] checked: Signal, /// A callback that is run when the checkbox is checked and unchecked. #[prop(into)] on_change: Callback<(), ()>, /// The value of the checkbox. #[prop(into)] value: Signal, /// The name of the checkbox. This is used to group checkboxes together, so every checkbox /// in a group should have the same name. #[prop(into)] name: Signal, /// A [`Signal`] indicating whether the checkbox is disabled. #[prop(into)] disabled: Signal, ) -> impl IntoView { let id = create_unique_id(&name.get(), &value.get_untracked()); view! { } } /// # Checkbox List Component /// This component creates a list of checkboxes, each with a specific value. The values are passed /// through a prop rather than manually creating children. /// /// Example usage: /// ``` /// let items = vec! [ /// "Item 1", /// "Item 2", /// "Item 3" /// ]; /// /// view! { /// /// } /// ``` #[component] pub fn CheckboxList( /// The name that this list should use. This doesn't need to be specified all the time, but is /// for use in elements that may treat this list as a sub-input (components like the [`MultiEntry`] /// do this) #[prop(optional, into)] name: Signal, /// The data member that this list should edit/display from the `data_map`. #[prop(into)] data_member: Signal, /// A [`Signal`] allowing access to a data store. #[prop()] data_map: RwSignal>, /// The available choices for this list. #[prop()] items: Vec, /// A [`Signal`] indicating whether the checkbox list is disabled. #[prop(optional, into)] disabled: Signal, /// A label for the list. Shown like a field name in a form. #[prop(optional, into)] label: String, /// A [`Signal`] indicating whether the list is required. Used for validation. #[prop(optional, into)] required: Signal ) -> impl IntoView { let controller = use_selectable_list( data_member.clone(), data_map.clone(), required.clone() ); //#region Render Logic view! {
{label} {items .into_iter() .map(|item| { let item = Signal::derive({ let cloned = item.clone(); move || cloned.clone() }); let checked = Signal::derive(move || { controller.selected_list.get().contains(&item.get()) }); let on_change = move || { controller .selected_list .update(|list| { let item = item.get(); if list.contains(&item) { list.retain(|val| *val != item); } else { list.push(item); } }); controller.dirty.set(true); }; // Update the data list by checking if the value is present in it. If so, // remove it. If not, add it. view! { } }) .collect_view()}
{move || { match controller.error.get() { ValidationState::Invalid(msg) => msg, _ => "There is no error - should not see this message.".to_string(), } }}
} //#endregion }