feat: implement checkboxlist component
This commit is contained in:
1 parent
f7ca1ecbc5
commit
9925e753d8
8 files changed
+125
-14
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
[target.'cfg(target_os = "linux")']
|
||||||
|
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
@@ -1 +0,0 @@
|
|||||||
edition = "2021"
|
|
||||||
+1
-1
@@ -60,7 +60,7 @@ pub fn App() -> impl IntoView {
|
|||||||
// <AuthErrorContext><AuthErrorView/></AuthErrorContext>
|
// <AuthErrorContext><AuthErrorView/></AuthErrorContext>
|
||||||
|
|
||||||
// sets the document title
|
// sets the document title
|
||||||
<Title text="Welcome to Leptos"/>
|
<Title text="R15 Scholarship App DEV"/>
|
||||||
|
|
||||||
// content for this welcome page
|
// content for this welcome page
|
||||||
<Router>
|
<Router>
|
||||||
|
|||||||
@@ -1,19 +1,91 @@
|
|||||||
use leptos::prelude::*;
|
use leptos::{leptos_dom::logging::console_log, prelude::*};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Checkbox(
|
pub fn Checkbox(
|
||||||
#[prop()] checked: ReadSignal<bool>,
|
#[prop()] checked: Signal<bool>,
|
||||||
#[prop()] on_change: Callback<bool>,
|
#[prop(into)] on_change: Callback<(), ()>,
|
||||||
#[prop()] label: String,
|
#[prop(into)] value: String
|
||||||
#[prop()] id: String
|
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let label_id = id.clone();
|
|
||||||
view! {
|
view! {
|
||||||
<label for=label_id>
|
<label for=value
|
||||||
|
class="flex items-center">
|
||||||
<input
|
<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"
|
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>
|
</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<_>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,4 +5,4 @@ mod text_field;
|
|||||||
mod select;
|
mod select;
|
||||||
mod checkboxes;
|
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::*};
|
||||||
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||||||
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client};
|
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client};
|
||||||
use crate::app::Unauthenticated;
|
use crate::app::Unauthenticated;
|
||||||
use crate::common::{StudentInfo, StudentInfoReactive, UserClaims};
|
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::leptos_dom::logging::console_log;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
|
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
|
||||||
@@ -154,6 +154,7 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
let reactive_info = StudentInfoReactive::new(submission);
|
let reactive_info = StudentInfoReactive::new(submission);
|
||||||
|
|
||||||
let select_value = RwSignal::new(String::from("Math"));
|
let select_value = RwSignal::new(String::from("Math"));
|
||||||
|
let chk_select = RwSignal::new(vec!["Testing 2".into()]);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<p>
|
<p>
|
||||||
@@ -184,9 +185,15 @@ pub fn HomePage() -> impl IntoView {
|
|||||||
value_list = vec!["Math".into(), "English".into(), "Science".into()]
|
value_list = vec!["Math".into(), "English".into(), "Science".into()]
|
||||||
value = select_value
|
value = select_value
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
<CheckboxList
|
||||||
|
selected = chk_select
|
||||||
|
items = vec!["Testing 1".into(), "Testing 2".into()] />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
on:click=move |_| {
|
on:click=move |_| {
|
||||||
|
console_log(format!("Found value {:?}", chk_select.get()).as_str());
|
||||||
submit_action.dispatch(CreateSampleSubmission {
|
submit_action.dispatch(CreateSampleSubmission {
|
||||||
student_info: reactive_info.capture(),
|
student_info: reactive_info.capture(),
|
||||||
subject: user_claims.get().unwrap().claims.subject.clone()
|
subject: user_claims.get().unwrap().claims.subject.clone()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use crate::components::{Loading, MultiEntry, MultiEntryData, MultiEntryMember, O
|
|||||||
use leptos::leptos_dom::logging::console_log;
|
use leptos::leptos_dom::logging::console_log;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
|
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ProviderEntry() -> impl IntoView {
|
pub fn ProviderEntry() -> impl IntoView {
|
||||||
|
|||||||
+33
-1
@@ -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 */
|
||||||
|
}
|
||||||
Reference in new issue
Block a user