1use {
2 crate::{
3 css::{
4 err_el,
5 ATTR_LABEL,
6 CSS_CLASS_SMALL_INPUT,
7 CSS_CLASS_TEXT,
8 },
9 FormElements,
10 FormState,
11 FormWith,
12 },
13 gloo::timers::callback::Timeout,
14 rooting::{
15 el,
16 El,
17 },
18 std::{
19 cell::RefCell,
20 convert::Infallible,
21 fmt::Display,
22 str::FromStr,
23 },
24 wasm_bindgen::JsCast,
25 web_sys::{
26 HtmlInputElement,
27 HtmlTextAreaElement,
28 },
29};
30
31#[derive(Clone)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct BigString(pub String);
35
36impl FromStr for BigString {
37 type Err = Infallible;
38
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 return Ok(BigString(s.to_string()));
41 }
42}
43
44struct BigStringFormState {
45 el: El,
46}
47
48impl FormState<BigString> for BigStringFormState {
49 fn parse(&self) -> Result<BigString, ()> {
50 return Ok(BigString(self.el.raw().dyn_ref::<HtmlTextAreaElement>().unwrap().value()));
51 }
52}
53
54impl<C> FormWith<C> for BigString {
55 fn new_form_with_(
56 _context: &C,
57 field: &str,
58 from: Option<&Self>,
59 _depth: usize,
60 ) -> (FormElements, Box<dyn FormState<Self>>) {
61 let textarea =
62 el("div")
63 .classes(&[CSS_CLASS_SMALL_INPUT, CSS_CLASS_TEXT])
64 .attr(ATTR_LABEL, field)
65 .attr("contenteditable", "plaintext-only")
66 .text(from.map(|x| x.0.as_str()).unwrap_or(""));
67 return (FormElements {
68 error: None,
69 elements: vec![textarea.clone()],
70 }, Box::new(BigStringFormState { el: textarea }));
71 }
72}
73
74pub struct FromStrFormState {
76 el: El,
77 error_el: El,
78}
79
80impl FromStrFormState {
81 pub fn new<
82 E: Display,
83 T: FromStr<Err = E>,
84 >(label: &str, initial_value: &str) -> (FormElements, Box<dyn FormState<T>>) {
85 let error_el = err_el();
86 let input_el =
87 el("div")
88 .classes(&[CSS_CLASS_SMALL_INPUT, CSS_CLASS_TEXT])
89 .attr(ATTR_LABEL, label)
90 .attr("contenteditable", "plaintext-only")
91 .attr("value", initial_value)
92 .on("input", {
93 let error_el = error_el.clone();
94 let debounce = RefCell::new(None);
95 move |ev| {
96 error_el.ref_text("");
97 let text = ev.target().unwrap().dyn_ref::<HtmlInputElement>().unwrap().value();
98 *debounce.borrow_mut() = Some(Timeout::new(300, {
99 let error_el = error_el.clone();
100 move || {
101 if text.len() >= 1 {
102 match T::from_str(&text) {
103 Err(e) => {
104 error_el.ref_text(&e.to_string());
105 return;
106 },
107 _ => { },
108 }
109 }
110 }
111 }));
112 }
113 });
114 return (FormElements {
115 error: Some(error_el.clone()),
116 elements: vec![input_el.clone()],
117 }, Box::new(FromStrFormState {
118 el: input_el,
119 error_el: error_el,
120 }));
121 }
122}
123
124impl<E: Display, T: FromStr<Err = E>> FormState<T> for FromStrFormState {
125 fn parse(&self) -> Result<T, ()> {
126 match T::from_str(&self.el.raw().dyn_ref::<HtmlInputElement>().unwrap().value()) {
127 Ok(v) => {
128 self.error_el.ref_text("");
129 return Ok(v);
130 },
131 Err(e) => {
132 self.error_el.ref_text(&e.to_string());
133 return Err(());
134 },
135 }
136 }
137}
138
139impl<C> FormWith<C> for String {
140 fn new_form_with_(
141 _context: &C,
142 field: &str,
143 from: Option<&Self>,
144 _depth: usize,
145 ) -> (FormElements, Box<dyn FormState<Self>>) {
146 return FromStrFormState::new::<_, String>(field, from.as_ref().map(|x| x.as_str()).unwrap_or(""));
147 }
148}
149
150impl<C> FormWith<C> for u8 {
151 fn new_form_with_(
152 _context: &C,
153 field: &str,
154 from: Option<&Self>,
155 _depth: usize,
156 ) -> (FormElements, Box<dyn FormState<Self>>) {
157 return FromStrFormState::new::<_, Self>(
158 field,
159 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
160 );
161 }
162}
163
164impl<C> FormWith<C> for u16 {
165 fn new_form_with_(
166 _context: &C,
167 field: &str,
168 from: Option<&Self>,
169 _depth: usize,
170 ) -> (FormElements, Box<dyn FormState<Self>>) {
171 return FromStrFormState::new::<_, Self>(
172 field,
173 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
174 );
175 }
176}
177
178impl<C> FormWith<C> for u32 {
179 fn new_form_with_(
180 _context: &C,
181 field: &str,
182 from: Option<&Self>,
183 _depth: usize,
184 ) -> (FormElements, Box<dyn FormState<Self>>) {
185 return FromStrFormState::new::<_, Self>(
186 field,
187 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
188 );
189 }
190}
191
192impl<C> FormWith<C> for u64 {
193 fn new_form_with_(
194 _context: &C,
195 field: &str,
196 from: Option<&Self>,
197 _depth: usize,
198 ) -> (FormElements, Box<dyn FormState<Self>>) {
199 return FromStrFormState::new::<_, Self>(
200 field,
201 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
202 );
203 }
204}
205
206impl<C> FormWith<C> for i8 {
207 fn new_form_with_(
208 _context: &C,
209 field: &str,
210 from: Option<&Self>,
211 _depth: usize,
212 ) -> (FormElements, Box<dyn FormState<Self>>) {
213 return FromStrFormState::new::<_, Self>(
214 field,
215 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
216 );
217 }
218}
219
220impl<C> FormWith<C> for i16 {
221 fn new_form_with_(
222 _context: &C,
223 field: &str,
224 from: Option<&Self>,
225 _depth: usize,
226 ) -> (FormElements, Box<dyn FormState<Self>>) {
227 return FromStrFormState::new::<_, Self>(
228 field,
229 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
230 );
231 }
232}
233
234impl<C> FormWith<C> for i32 {
235 fn new_form_with_(
236 _context: &C,
237 field: &str,
238 from: Option<&Self>,
239 _depth: usize,
240 ) -> (FormElements, Box<dyn FormState<Self>>) {
241 return FromStrFormState::new::<_, Self>(
242 field,
243 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
244 );
245 }
246}
247
248impl<C> FormWith<C> for i64 {
249 fn new_form_with_(
250 _context: &C,
251 field: &str,
252 from: Option<&Self>,
253 _depth: usize,
254 ) -> (FormElements, Box<dyn FormState<Self>>) {
255 return FromStrFormState::new::<_, Self>(
256 field,
257 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
258 );
259 }
260}
261
262impl<C> FormWith<C> for f32 {
263 fn new_form_with_(
264 _context: &C,
265 field: &str,
266 from: Option<&Self>,
267 _depth: usize,
268 ) -> (FormElements, Box<dyn FormState<Self>>) {
269 return FromStrFormState::new::<_, Self>(
270 field,
271 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
272 );
273 }
274}
275
276impl<C> FormWith<C> for f64 {
277 fn new_form_with_(
278 _context: &C,
279 field: &str,
280 from: Option<&Self>,
281 _depth: usize,
282 ) -> (FormElements, Box<dyn FormState<Self>>) {
283 return FromStrFormState::new::<_, Self>(
284 field,
285 from.map(|x| x.to_string()).as_ref().map(|x| x.as_str()).unwrap_or(""),
286 );
287 }
288}