feat: implement checkboxlist component

This commit is contained in:
alphafetus committed 2025-08-27 13:16:23 -04:00
1 parent f7ca1ecbc5
commit 9925e753d8
8 files changed
+125 -14

No files matched your search

+80 -8
View File
@@ -1,19 +1,91 @@
use leptos::prelude::*;
use leptos::{leptos_dom::logging::console_log, prelude::*};
#[component]
pub fn Checkbox(
#[prop()] checked: ReadSignal<bool>,
#[prop()] on_change: Callback<bool>,
#[prop()] label: String,
#[prop()] id: String
#[prop()] checked: Signal<bool>,
#[prop(into)] on_change: Callback<(), ()>,
#[prop(into)] value: String
) -> impl IntoView {
let label_id = id.clone();
view! {
<label for=label_id>
<label for=value
class="flex items-center">
<input
// Using the "peer" class links this input with the div "peer" at the same level.
// By using the "peer-checked" selector this div can toggle states based on this
// input's state (checked/unchecked)
class="
relative peer shrink-0
hidden"
type="checkbox"
id={id}
id={value.clone()}
on:change = move |_| on_change.run(())
prop:checked = checked
/>
// This div displays the box for the checkbox, and applies some styling to the child
// span element.
<div
class="relative inline-block h-5 w-5 m-1 rounded-sm border border-gray-300 bg-white
transition-bg duration-150 peer-checked:bg-red-700 peer-checked:border-red-700
[&>span]:opacity-0 peer-checked:[&>span]:opacity-100"
>
// This span is the check icon. It is centered within the div and has a border on the bottom
// and right sides, which is then rotated to appear like a checkmark.
<span
class="absolute left-1/2 top-1/2 block h-3 w-1.5 -translate-x-1/2 -translate-y-1/2
rotate-45 transform border-b-3 border-r-3
border-white transition-opacity duration-150"
></span>
</div>
<span>{value.clone()}</span>
</label>
}
}
#[component]
pub fn CheckboxList(
#[prop(default = RwSignal::new(vec![]))] selected: RwSignal<Vec<String>>,
#[prop()] items: Vec<String>
) -> impl IntoView {
view! {
// Checkbox elements go in here. Each one will have a derived signal
// that checks if the name/value of that checkbox is in the selected items
// vec. If so, display as selected. If not, clear that checkbox.
// Somehow create a derived signal??
{
items.into_iter().map(|item| {
let is_checked = {
let item_name = item.clone();
move || {
// Check if this checkbox should be selected
selected.get().contains(&item_name)
}
};
let checked_signal = Signal::derive(is_checked);
view! {
<Checkbox
checked = checked_signal
on_change = {
// Clone this value to avoid moving it into the closure
let item_name = item.clone();
move || {
selected.update(|list| {
// Check if the list contains this element. If so, remove it. If not, add it.
if let Some(index) = list.iter().position(|val| *val == item_name) {
list.remove(index);
} else {
list.push(item_name.clone());
}
});
}
}
// The actual selected values are tracked by this element, not by the checkboxes themselves.
value = item.clone() />
}
}).collect::<Vec<_>>()
}
}
}