From 55e7fd5797b1856d506c63ba55da289d8246c334 Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Tue, 21 Oct 2025 09:02:11 -0400 Subject: [PATCH 1/6] chore: pc change (continue ChipsList component) --- src/components/chips.rs | 139 ++++++++++++++++++++++++++++++++++++++++ src/components/mod.rs | 3 +- src/pages/home_page.rs | 24 ++++++- 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/components/chips.rs diff --git a/src/components/chips.rs b/src/components/chips.rs new file mode 100644 index 0000000..a67d5cb --- /dev/null +++ b/src/components/chips.rs @@ -0,0 +1,139 @@ +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 { + 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 3770dae..4713d7c 100644 --- a/src/pages/home_page.rs +++ b/src/pages/home_page.rs @@ -7,7 +7,7 @@ use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValu use serde_dynamo::{from_item, to_item}; use crate::common::{ExpandableInfo, UserClaims}; -use crate::components::{ActionButton, CheckboxList, Loading, OutlinedTextField, Panel, RadioList, Row, Select}; +use crate::components::{ActionButton, CheckboxList, Loading, OutlinedTextField, Panel, RadioList, Row, Select, Chip, ChipsList}; use crate::pages::{UnauthenticatedPage}; use leptos::leptos_dom::logging::console_log; use leptos::prelude::*; @@ -263,6 +263,28 @@ pub fn HomePage() -> impl IntoView { data_map=expandable_react.data /> + + + + + + Date: Tue, 21 Oct 2025 17:24:52 -0400 Subject: [PATCH 2/6] fix: update id prop in Chip to be unique --- src/components/chips.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/chips.rs b/src/components/chips.rs index a67d5cb..7504971 100644 --- a/src/components/chips.rs +++ b/src/components/chips.rs @@ -28,13 +28,20 @@ pub fn Chip( #[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! { -