impulse_thaw/checkbox/
checkbox_group.rs1use crate::{FieldInjection, FieldValidationState, Rule};
2use leptos::{context::Provider, prelude::*};
3use std::{collections::HashSet, ops::Deref};
4use thaw_utils::{class_list, Model};
5
6#[component]
7pub fn CheckboxGroup(
8 #[prop(optional, into)] class: MaybeProp<String>,
9 #[prop(optional, into)] id: MaybeProp<String>,
10 #[prop(optional, into)]
13 name: MaybeProp<String>,
14 #[prop(optional, into)] rules: Vec<CheckboxGroupRule>,
15 #[prop(optional, into)]
17 value: Model<HashSet<String>>,
18 children: Children,
19) -> impl IntoView {
20 let (id, name) = FieldInjection::use_id_and_name(id, name);
21 let validate = Rule::validate(rules, value, name);
22 Effect::new(move |prev: Option<()>| {
23 value.with(|_| {});
24 if prev.is_some() {
25 validate.run(Some(CheckboxGroupRuleTrigger::Change));
26 }
27 });
28
29 view! {
30 <Provider value=CheckboxGroupInjection {
31 value,
32 name,
33 }>
34 <div class=class_list!["thaw-checkbox-group", class] id=id role="group">
35 {children()}
36 </div>
37 </Provider>
38 }
39}
40
41#[derive(Clone)]
42pub(crate) struct CheckboxGroupInjection {
43 pub value: Model<HashSet<String>>,
44 pub name: Signal<Option<String>>,
45}
46
47impl Copy for CheckboxGroupInjection {}
48
49impl CheckboxGroupInjection {
50 pub fn use_context() -> Option<Self> {
51 use_context()
52 }
53}
54
55#[derive(Debug, Default, PartialEq, Clone, Copy)]
56pub enum CheckboxGroupRuleTrigger {
57 #[default]
58 Change,
59}
60
61pub struct CheckboxGroupRule(Rule<HashSet<String>, CheckboxGroupRuleTrigger>);
62
63impl CheckboxGroupRule {
64 pub fn required(required: Signal<bool>) -> Self {
65 Self::validator(move |value, name| {
66 if required.get_untracked() && value.is_empty() {
67 let message = name.get_untracked().map_or_else(
68 || String::from("Please select!"),
69 |name| format!("Please select {name}!"),
70 );
71 Err(FieldValidationState::Error(message))
72 } else {
73 Ok(())
74 }
75 })
76 }
77
78 pub fn required_with_message(required: Signal<bool>, message: Signal<String>) -> Self {
79 Self::validator(move |value, _| {
80 if required.get_untracked() && value.is_empty() {
81 Err(FieldValidationState::Error(message.get_untracked()))
82 } else {
83 Ok(())
84 }
85 })
86 }
87
88 pub fn validator(
89 f: impl Fn(&HashSet<String>, Signal<Option<String>>) -> Result<(), FieldValidationState>
90 + Send
91 + Sync
92 + 'static,
93 ) -> Self {
94 Self(Rule::validator(f))
95 }
96
97 pub fn with_trigger(self, trigger: CheckboxGroupRuleTrigger) -> Self {
98 Self(Rule::with_trigger(self.0, trigger))
99 }
100}
101
102impl Deref for CheckboxGroupRule {
103 type Target = Rule<HashSet<String>, CheckboxGroupRuleTrigger>;
104
105 fn deref(&self) -> &Self::Target {
106 &self.0
107 }
108}