Implement mockup of chips component #15

Merged
BobdaFett merged 7 commits from feature/chips-component into main 2025-10-23 15:56:48 +00:00
3 changed files with 164 additions and 2 deletions
Showing only changes of commit 55e7fd5797 - Show all commits

No files matched your search

+139
View File
@@ -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! {
/// <Chip
/// value="Some Value"
/// name="list_name"
/// on_change=move || { println!("Some work here.") }
/// checked=RwSignal::new(false)
/// disabled=RwSignal::new(false)
/// />
/// }
/// ```
#[component]
pub fn Chip(
#[prop(into)] value: String,
#[prop(into)] name: String,
#[prop(into)] on_change: Callback<(), ()>,
#[prop()] checked: Signal<bool>,
#[prop(optional)] disabled: RwSignal<bool>,
) -> impl IntoView {
view! {
<label for=value>
<input
type="checkbox"
class="relative peer shrink-0 hidden"
name=name.clone()
id=value.clone()
prop:checked=checked
on:change=move |_| on_change.run(())
disabled=disabled />
<div class="rounded-full p-2.5
font-bold cursor-pointer
bg-transparent border-1 border-gray-300
shadow-md/50 hover:shadow-md/70
peer-checked:bg-red-700 peer-checked:border-red-700 peer-checked:text-white
peer-disabled:peer-checked:bg-gray-700
peer-disabled:peer-checked:border-gray-700
peer-disabled:cursor-default
transition-all duration-100">
<span>{value.clone()}</span>
</div>
</label>
}
}
/// # 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<HashMap<String, ValueType>>,
#[prop()] items: Vec<String>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
#[prop(optional, into)] label: String,
) -> impl IntoView {
view! {
<div class="m-1.5 mt-0 mb-0">
<span class="font-bold">{label}</span>
<div class="flex flex-row gap-2 mt-1">
{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! {
<Chip
checked=checked_signal
name=data_member.clone()
on_change=on_change
value=item.clone()
disabled=disabled
/>
}
})
.collect_view()
}
</div>
</div>
}
}
+2 -1
View File
@@ -7,8 +7,9 @@ mod radio;
mod row; mod row;
mod select; mod select;
mod text_field; mod text_field;
mod chips;
pub use self::{ pub use self::{
action_button::*, checkbox::*, loading::*, multi_entry::*, panel::*, radio::*, row::*, action_button::*, checkbox::*, loading::*, multi_entry::*, panel::*, radio::*, row::*,
select::*, text_field::*, select::*, text_field::*, chips::*
}; };
+23 -1
View File
@@ -7,7 +7,7 @@ use aws_sdk_dynamodb::{Client, error::ProvideErrorMetadata, types::AttributeValu
use serde_dynamo::{from_item, to_item}; use serde_dynamo::{from_item, to_item};
use crate::common::{ExpandableInfo, UserClaims}; 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 crate::pages::{UnauthenticatedPage};
use leptos::leptos_dom::logging::console_log; use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*; use leptos::prelude::*;
@@ -263,6 +263,28 @@ pub fn HomePage() -> impl IntoView {
data_map=expandable_react.data data_map=expandable_react.data
/> />
</Row> </Row>
<Row>
<ChipsList
data_member="athletic_requirements"
data_map=expandable_react.data
items=vec!("Football", "Disc Golf", "Water Polo", "Testing")
.into_iter().map(|s| s.to_owned())
.collect()
disabled=elements_disabled
label="Athletics Requirements"
/>
</Row>
<Row>
<ChipsList
data_member="community_involvement"
data_map=expandable_react.data
items=vec!("Testing", "Again")
.into_iter().map(|s| s.to_owned())
.collect()
disabled=elements_disabled
label="Required Community Involvement"
/>
</Row>
<Row> <Row>
<ActionButton <ActionButton
on:click=move |_| { on:click=move |_| {