use crate::common::ValueType; use crate::components::utils::create_unique_id; use leptos::prelude::*; use std::collections::HashMap; use crate::components::ValidationState; use super::utils::use_selectable_list; /// # Chip Component /// /// This component defines a single chip for use in the `ChipsList` component. It functions almost /// identically to the `Checkbox` component. It should only be created from within its parent /// `CheckboxList` component. /// /// Example usage: /// ``` /// view! { /// /// } /// ``` #[component] pub fn Chip( #[prop(into)] display_text: Signal, #[prop(into)] value: Signal, #[prop(into)] name: Signal, #[prop(into)] on_change: Callback<(), ()>, #[prop()] checked: Signal, #[prop(optional, into)] disabled: Signal, ) -> impl IntoView { // Generate a unique id - ensure that the value doesn't contain spaces. // Without this id, checkbox/radio inputs can interfere with each other. let id = create_unique_id(&name.get(), &value.get_untracked()); view! { } } /// # Chips List Component /// /// This defines a list of `Chip` components. The list of values is created based on the data from the /// `data_map` prop, which is found using the given `data_member`. /// /// Any values selected from the list are stored within the `data_map` immediately as part of a callback /// passed to the `on_change` prop for each individual `Chip` component. #[component] pub fn ChipsList( /// The member in the data map that this input affects. #[prop(into)] data_member: Signal, /// The data map that contains the list of values. This will be edited as the input changes. #[prop()] data_map: RwSignal>, /// The list of strings that should be displayed in the list. #[prop(into)] displayed_text: Signal>, /// The list of values that should be displayed in the list. #[prop(into)] values: Signal>, /// Indicates if the input should be disabled. #[prop(optional, into)] disabled: Signal, /// The label for the header of the input. #[prop(optional, into)] label: String, /// Indicates if this input is required. #[prop(optional, into)] required: Signal, /// Determines if this list behaves like a checkbox or radio list. #[prop(default = true.into(), into)] allows_multiple: Signal, ) -> impl IntoView { let controller = use_selectable_list( data_member.clone(), data_map.clone(), required.clone() ); view! {
{label}
{move || displayed_text.get().into_iter().zip(values.get()) .into_iter() .map(|(text, value)| { let value = RwSignal::new(value); let checked = Signal::derive(move || { controller.selected_list.get().contains(&value.get()) }); let on_change = move || { // Update the data list by checking if the value is present in it. If so, // remove it. If not, add it. let value = value.get(); let list = controller.selected_list.get(); if list.contains(&value) { allows_multiple.get() .then(|| controller.selected_list.update(|list| list.retain(|v| v != &value))) .or_else(|| Some(controller.selected_list.set(Vec::new()))); } else { allows_multiple.get() .then(|| controller.selected_list.update(|list| list.push(value.clone()))) .or_else(|| Some(controller.selected_list.set(vec![value.clone()]))); } controller.dirty.set(true); }; view! { } }) .collect_view() }
{move || { match controller.error.get() { ValidationState::Invalid(msg) => msg, _ => "There is no error - should not see this message.".to_string() } }}
} }