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

+2
View File
@@ -0,0 +1,2 @@
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
-1
View File
@@ -1 +0,0 @@
edition = "2021"
+1 -1
View File
@@ -60,7 +60,7 @@ pub fn App() -> impl IntoView {
// <AuthErrorContext><AuthErrorView/></AuthErrorContext>
// sets the document title
<Title text="Welcome to Leptos"/>
<Title text="R15 Scholarship App DEV"/>
// content for this welcome page
<Router>
+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<_>>()
}
}
}
+1 -1
View File
@@ -5,4 +5,4 @@ mod text_field;
mod select;
mod checkboxes;
pub use self::{action_button::*, loading::*, multi_entry::*, text_field::*, select::*};
pub use self::{action_button::*, loading::*, multi_entry::*, text_field::*, select::*, checkboxes::*};
+8 -1
View File
@@ -3,7 +3,7 @@ use std::collections::HashMap;
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client};
use crate::app::Unauthenticated;
use crate::common::{StudentInfo, StudentInfoReactive, UserClaims};
use crate::components::{ActionButton, Loading, OutlinedTextField, Select};
use crate::components::{ActionButton, Loading, OutlinedTextField, Select, CheckboxList};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
@@ -154,6 +154,7 @@ pub fn HomePage() -> impl IntoView {
let reactive_info = StudentInfoReactive::new(submission);
let select_value = RwSignal::new(String::from("Math"));
let chk_select = RwSignal::new(vec!["Testing 2".into()]);
view! {
<p>
@@ -184,9 +185,15 @@ pub fn HomePage() -> impl IntoView {
value_list = vec!["Math".into(), "English".into(), "Science".into()]
value = select_value
/>
<div>
<CheckboxList
selected = chk_select
items = vec!["Testing 1".into(), "Testing 2".into()] />
</div>
<div>
<ActionButton
on:click=move |_| {
console_log(format!("Found value {:?}", chk_select.get()).as_str());
submit_action.dispatch(CreateSampleSubmission {
student_info: reactive_info.capture(),
subject: user_claims.get().unwrap().claims.subject.clone()
-1
View File
@@ -4,7 +4,6 @@ use crate::components::{Loading, MultiEntry, MultiEntryData, MultiEntryMember, O
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
use serde::{Deserialize, Serialize};
#[component]
pub fn ProviderEntry() -> impl IntoView {
+33 -1
View File
@@ -1 +1,33 @@
@import "tailwindcss";
@import "tailwindcss";
/* Check icon styling */
.checkbox-icon:checked::after {
content: '';
position: absolute;
top: 50%;
bottom: 50%;
width: 5px;
height: 10px;
border: solid #fff;
border-width: 0 3px 3px 0;
transform: rotate(45deg);
}
/* Checkbox styling */
.checkbox-box {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
border-radius: 2px;
}
/*
* Checkbox selected styling.
* Selects the first checkbox-icon class after a checked input element within
* this checkbox-box.
*/
.checkbox-box input:checked ~ .checkbox-icon {
background-color: var(--color-red-700); /* Use the tailwindcss variables */
}