diff --git a/thaw/src/checkbox/checkbox_group.rs b/thaw/src/checkbox/checkbox_group.rs index b7f3ac6..b9007fb 100644 --- a/thaw/src/checkbox/checkbox_group.rs +++ b/thaw/src/checkbox/checkbox_group.rs @@ -1,18 +1,38 @@ +use crate::{FieldInjection, FieldValidationState, Rule, RuleValueIsEmpty}; use leptos::{context::Provider, prelude::*}; -use std::collections::HashSet; +use std::{collections::HashSet, ops::Deref}; use thaw_utils::{class_list, Model}; #[component] pub fn CheckboxGroup( #[prop(optional, into)] class: MaybeProp, + #[prop(optional, into)] id: MaybeProp, + /// A string specifying a name for the input control. + /// This name is submitted along with the control's value when the form data is submitted. + #[prop(optional, into)] + name: MaybeProp, + #[prop(optional, into)] rules: Vec, /// Sets the value of the checkbox group. #[prop(optional, into)] value: Model>, children: Children, ) -> impl IntoView { + let (id, name) = FieldInjection::use_id_and_name(id, name); + let validate = Rule::validate(rules, value, name); + Effect::new(move |prev: Option<()>| { + value.with(|_| {}); + if prev.is_some() { + validate.run(Some(CheckboxGroupRuleTrigger::Change)); + } + }); + view! { - -
+ +
{children()}
@@ -20,7 +40,10 @@ pub fn CheckboxGroup( } #[derive(Clone)] -pub(crate) struct CheckboxGroupInjection(pub Model>); +pub(crate) struct CheckboxGroupInjection { + pub value: Model>, + pub name: Signal>, +} impl Copy for CheckboxGroupInjection {} @@ -29,3 +52,50 @@ impl CheckboxGroupInjection { use_context() } } + +#[derive(Debug, Default, PartialEq, Clone, Copy)] +pub enum CheckboxGroupRuleTrigger { + #[default] + Change, +} + +pub struct CheckboxGroupRule(Rule, CheckboxGroupRuleTrigger>); + +impl CheckboxGroupRule { + pub fn required(required: MaybeSignal) -> Self { + Self(Rule::required(required)) + } + + pub fn required_with_message( + required: MaybeSignal, + message: MaybeSignal, + ) -> Self { + Self(Rule::required_with_message(required, message)) + } + + pub fn validator( + f: impl Fn(&HashSet) -> Result<(), FieldValidationState> + Send + Sync + 'static, + ) -> Self { + Self(Rule::validator(f)) + } + + pub fn with_trigger(mut self, trigger: CheckboxGroupRuleTrigger) -> Self { + self.0.trigger = trigger; + + self + } +} + +impl Deref for CheckboxGroupRule { + type Target = Rule, CheckboxGroupRuleTrigger>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl RuleValueIsEmpty for HashSet { + fn is_empty(&self) -> bool { + self.is_empty() + } +} diff --git a/thaw/src/checkbox/mod.rs b/thaw/src/checkbox/mod.rs index 6f35610..57ba33d 100644 --- a/thaw/src/checkbox/mod.rs +++ b/thaw/src/checkbox/mod.rs @@ -1,6 +1,6 @@ mod checkbox_group; -pub use checkbox_group::CheckboxGroup; +pub use checkbox_group::{CheckboxGroup, CheckboxGroupRule, CheckboxGroupRuleTrigger}; use checkbox_group::CheckboxGroupInjection; use leptos::{html, prelude::*}; @@ -30,7 +30,7 @@ pub fn Checkbox( let Some(group) = group.as_ref() else { return None; }; - group.0.with(|group_value| { + group.value.with(|group_value| { item_value.with_value(|value| { let Some(value) = value else { return None; @@ -44,11 +44,11 @@ pub fn Checkbox( let input = input_ref.get_untracked().unwrap(); if group_checked.get_untracked().is_some() { if input.checked() { - group.as_ref().unwrap().0.update(move |group_value| { + group.as_ref().unwrap().value.update(move |group_value| { group_value.insert(item_value.get_value().unwrap()); }); } else { - group.as_ref().unwrap().0.update(move |group_value| { + group.as_ref().unwrap().value.update(move |group_value| { item_value.with_value(|value| { group_value.remove(value.as_ref().unwrap()); }); @@ -71,6 +71,8 @@ pub fn Checkbox( class="thaw-checkbox__input" type="checkbox" id=id.clone() + name=move || group.map(|g| g.name.get()).flatten() + value=item_value.get_value() checked=checked node_ref=input_ref on:change=on_change diff --git a/thaw/src/field/docs/mod.md b/thaw/src/field/docs/mod.md index 3b68611..8feb84b 100644 --- a/thaw/src/field/docs/mod.md +++ b/thaw/src/field/docs/mod.md @@ -33,6 +33,11 @@ view! { + + + + +