diff --git a/src/components/chips.rs b/src/components/chips.rs new file mode 100644 index 0000000..af335a1 --- /dev/null +++ b/src/components/chips.rs @@ -0,0 +1,146 @@ +use std::collections::HashMap; +use leptos::prelude::*; +use crate::components::ValueType; + +/// # 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)] value: String, + #[prop(into)] name: String, + #[prop(into)] on_change: Callback<(), ()>, + #[prop()] checked: Signal, + #[prop(optional)] disabled: RwSignal, +) -> 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 value_no_spaces = value.clone().chars() + .filter(|c| !c.is_whitespace()) + .collect::(); + let id = format!("{}-{}", name.clone(), value_no_spaces); + + 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( + #[prop(into)] data_member: String, + #[prop()] data_map: RwSignal>, + #[prop()] items: Vec, + #[prop(default = RwSignal::new(false))] disabled: RwSignal, + #[prop(optional, into)] label: String, +) -> impl IntoView { + view! { +
+ {label} +
+ {items + .into_iter() + .map(|item| { + let checked_signal = Signal::derive({ + let item_name = item.clone(); + let data_member = data_member.clone(); + move || { + data_map.get() + .get(&data_member) + .unwrap_or(&ValueType::List(None)) + .as_list() + .unwrap_or(Some(vec!())) + .unwrap_or(vec!()) + .contains(&ValueType::String(Some(item_name.clone()))) + } + }); + + let on_change = { + let item_name = item.clone(); + let data_member = data_member.clone(); + move || { + let result = ValueType::String(Some(item_name.clone())); + data_map.update(|map| { + // Attempt to get the value type from the hash map. + match map.get_mut(&data_member) { + Some(value_type) => { + // Attempt to get the value as a list. + match value_type.as_list().unwrap_or(None) { + Some(mut list) => { + // Attempt to find the selected value in the selected list. + // If it's present, remove it, or vice versa. + match list.iter().position(|val| *val == result) { + Some(index) => { list.remove(index); }, + None => { list.push(result); } + }; + // Update the existing value_type entry in the hash map. + *value_type = ValueType::List(Some(list)); + }, + // Insert a new value_type entry. + None => { *value_type = ValueType::List(Some(vec![result])); } + }; + }, + // Insert a new value_type entry. + None => { map.insert(data_member.clone(), ValueType::List(Some(vec![result]))); } + }; + }); + } + }; + + view! { + + } + }) + .collect_view() + } +
+
+ } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 50c55ce..2616640 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -7,8 +7,9 @@ mod radio; mod row; mod select; mod text_field; +mod chips; pub use self::{ action_button::*, checkbox::*, loading::*, multi_entry::*, panel::*, radio::*, row::*, - select::*, text_field::*, + select::*, text_field::*, chips::* }; diff --git a/src/pages/home_page.rs b/src/pages/home_page.rs index 4ff687e..4f0451f 100644 --- a/src/pages/home_page.rs +++ b/src/pages/home_page.rs @@ -9,9 +9,8 @@ use crate::common::{ExpandableInfo, UserClaims}; use crate::pages::UnauthenticatedPage; use crate::components::{ ActionButton, CheckboxList, Loading, MultiEntry, OutlinedTextField, Panel, RadioList, Row, - Select, + Select, ChipsList }; -use crate::input; use leptos::leptos_dom::logging::console_log; use leptos::prelude::*; use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated}; @@ -267,6 +266,35 @@ pub fn HomePage() -> impl IntoView { data_map=expandable_react.data /> + + + + + 20hrs" + ) + .into_iter().map(|s| s.to_owned()) + .collect() + disabled=elements_disabled + label="Required Community Involvement" +