nova_forms/
context.rs

1use crate::{FormData, QueryString};
2use leptos::*;
3use serde::{de::DeserializeOwned, Serialize};
4use std::{collections::HashMap, str::FromStr};
5
6#[derive(Debug, Clone, Copy)]
7pub struct InputContext(RwSignal<InputData>);
8
9#[derive(Debug, Clone, Copy)]
10struct InputData {
11    error: bool,
12    disabled: bool,
13    validate: ReadSignal<bool>,
14    set_validate: WriteSignal<bool>,
15    qs: QueryString,
16    form_data: FormData,
17}
18
19impl InputContext {
20    pub fn new(bind: QueryString) -> Self {
21        let parent_group = use_context::<GroupContext>();
22        let form_data = expect_context::<FormData>();
23
24        let (validate, set_validate) = create_signal(false);
25        let input = InputContext(RwSignal::new(InputData {
26            error: false,
27            disabled: false,
28            validate,
29            set_validate,
30            qs: parent_group.map(|parent_group| parent_group.qs()).unwrap_or_default().join(bind),
31            form_data,
32        }));
33
34        if let Some(parent_group) = parent_group {
35            parent_group.register_input(&bind, input);
36            on_cleanup(move || {
37                parent_group.deregister(&bind);
38            });    
39        }
40
41        input
42    }
43
44    pub fn set_error(&self, has_error: bool) {
45        self.0.update(|input| {
46            input.error = has_error;
47        });
48    }
49
50    pub fn error(&self) -> bool {
51        self.0.get().error
52    }
53
54    pub fn set_disabled(&self, disabled: bool) {
55        self.0.update(|input| {
56            input.disabled = disabled;
57        });
58    }
59
60    pub fn disabled(&self) -> bool {
61        self.0.get().disabled
62    }
63
64    pub fn validate(&self) {
65        self.0.get().set_validate.set(true);
66    }
67
68    pub fn validate_signal(&self) -> Signal<bool> {
69        self.0.get_untracked().validate.into()
70    }
71
72    pub fn qs(&self) -> QueryString {
73        self.0.get_untracked().qs
74    }
75
76    pub fn raw_value(&self) -> Signal<String> {
77        self.0.get_untracked().form_data.raw_value(self.qs())
78    }
79
80    pub fn set_raw_value<S: Into<String>>(&self, value: S) {
81        self.0.get_untracked().form_data.set_raw_value(self.qs(), value);
82    }
83
84    pub fn value<T: FromStr>(&self) -> Signal<Result<T, T::Err>> {
85        self.0.get_untracked().form_data.value(self.qs())
86    }
87
88    pub fn set_value<T: ToString>(&self, value: T) {
89        self.0.get_untracked().form_data.set_value(self.qs(), value);
90    }
91}
92
93#[derive(Debug, Clone, Copy)]
94pub struct GroupContext(RwSignal<GroupData>);
95
96#[derive(Debug, Clone)]
97struct GroupData {
98    inputs: HashMap<QueryString, Node>,
99    qs: QueryString,
100    form_data: FormData,
101}
102
103impl GroupContext {
104    pub fn new(bind: QueryString) -> Self {
105        let parent_group = use_context::<GroupContext>();
106        let form_data = expect_context::<FormData>();
107
108        let group = GroupContext(RwSignal::new(GroupData {
109            inputs: HashMap::new(),
110            qs: parent_group.map(|parent_group| parent_group.qs()).unwrap_or_default().join(bind),
111            form_data
112        }));
113
114        if let Some(parent_group) = parent_group {
115            parent_group.register_group(&bind, group);
116            on_cleanup(move || {
117                parent_group.deregister(&bind);
118            });    
119        }
120
121        group
122    }
123
124    pub fn qs(&self) -> QueryString {
125        self.0.get_untracked().qs
126    }
127
128    fn register_input(&self, qs: &QueryString, input: InputContext) {
129        self.0.update(|data| {
130            data.inputs.insert(*qs, Node::Input(input));
131        });
132    }
133
134    fn register_group(&self, qs: &QueryString, group: GroupContext) {
135        self.0.update(|data| {
136            data.inputs.insert(*qs, Node::Group(group));
137        });
138    }
139
140    pub fn deregister(&self, qs: &QueryString) {
141        self.0.update(|data| {
142            data.inputs.remove(qs);
143        });
144    }
145
146    pub fn set_disabled(&self, disabled: bool) {
147        self.0.update(|data| {
148            for node in data.inputs.values_mut() {
149                match node {
150                    Node::Input(input) => input.set_disabled(disabled),
151                    Node::Group(group) => group.set_disabled(disabled),
152                }
153            }
154        });
155    }
156
157    pub fn error(&self) -> bool {
158        self.0.get().inputs.values().any(|node| {
159            match node {
160                Node::Input(input) => input.error(),
161                Node::Group(group) => group.error(),
162            }
163        })
164    }
165
166    pub fn validate(&self) {
167        self.0.update(|data| {
168            for node in data.inputs.values_mut() {
169                match node {
170                    Node::Input(input) => {
171                        input.validate();
172                    }
173                    Node::Group(group) => {
174                        group.validate();
175                    }
176                }
177            }
178        });
179    }
180
181    pub fn value<T: DeserializeOwned + PartialEq>(&self) -> Signal<Option<T>> {
182        self.0.get_untracked().form_data.values(self.qs())
183    }
184
185    pub fn set_value<T: Serialize>(&self, values: T) {
186        self.0.get_untracked().form_data.set_values(self.qs(), values);
187    }
188
189    pub fn len(&self) -> Option<usize> {
190        self.0.get_untracked().form_data.len(self.qs())
191    }
192}
193
194#[derive(Debug, Clone)]
195pub enum Node {
196    Input(InputContext),
197    Group(GroupContext),
198}