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}