1use crate::{form::FormToolData, styles::FormStyle};
5use leptos::{
6 prelude::{AnyView, RwSignal, Signal},
7 reactive::wrappers::write::SignalSetter,
8};
9use std::{
10 fmt::Display,
11 marker::{Send, Sync},
12 str::FromStr,
13 sync::Arc,
14};
15
16pub mod button;
17pub mod checkbox;
18pub mod custom;
19pub mod group;
20pub mod heading;
21pub mod hidden;
22pub mod output;
23pub mod radio_buttons;
24pub mod select;
25pub mod slider;
26pub mod spacer;
27pub mod stepper;
28pub mod submit;
29pub mod text_area;
30pub mod text_input;
31
32pub trait BuilderFn<B>: Fn(B) -> B {}
33pub trait BuilderCxFn<B, CX>: Fn(B, Arc<CX>) -> B {}
34pub trait ValidationFn<FDT: ?Sized>:
35 Fn(&FDT) -> Result<(), String> + Send + Sync + 'static
36{
37}
38pub trait ValidationCb: Fn() -> bool + 'static {}
39pub trait ParseFn<CR, FDT>: Fn(CR) -> Result<FDT, String> + Send + Sync + 'static {}
40pub trait UnparseFn<CR, FDT>: Fn(FDT) -> CR + 'static {}
41pub trait FieldGetter<FD, FDT>: Fn(&FD) -> FDT + Send + Sync + 'static {}
42pub trait FieldSetter<FD, FDT>: Fn(&mut FD, FDT) + Send + Sync + 'static {}
43pub trait ShowWhenFn<FD: Send + Sync + 'static, CX: Send + Sync>:
44 Fn(Signal<FD>, Arc<CX>) -> bool + Send + Sync
45{
46}
47pub trait RenderFn<FS, FD: 'static>:
48 FnOnce(Arc<FS>, RwSignal<FD>) -> (AnyView, Option<Box<dyn ValidationCb>>) + 'static
49{
50}
51
52impl<B, T> BuilderFn<B> for T where T: Fn(B) -> B {}
54impl<B, CX, T> BuilderCxFn<B, CX> for T where T: Fn(B, Arc<CX>) -> B {}
55impl<FDT, T> ValidationFn<FDT> for T where T: Fn(&FDT) -> Result<(), String> + Send + Sync + 'static {}
56impl<T> ValidationCb for T where T: Fn() -> bool + 'static {}
57impl<CR, FDT, F> ParseFn<CR, FDT> for F where
58 F: Fn(CR) -> Result<FDT, String> + Send + Sync + 'static
59{
60}
61impl<CR, FDT, F> UnparseFn<CR, FDT> for F where F: Fn(FDT) -> CR + 'static {}
62impl<FD, FDT, F> FieldGetter<FD, FDT> for F where F: Fn(&FD) -> FDT + Send + Sync + 'static {}
63impl<FD, FDT, F> FieldSetter<FD, FDT> for F where F: Fn(&mut FD, FDT) + Send + Sync + 'static {}
64impl<FD: Send + Sync + 'static, CX: Send + Sync, F> ShowWhenFn<FD, CX> for F where
65 F: Fn(Signal<FD>, Arc<CX>) -> bool + Send + Sync
66{
67}
68impl<FS, FD: 'static, F> RenderFn<FS, FD> for F where
69 F: FnOnce(Arc<FS>, RwSignal<FD>) -> (AnyView, Option<Box<dyn ValidationCb>>) + 'static
70{
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
75pub enum ValidationState {
76 #[default]
78 Passed,
79 ParseError(String),
81 ValidationError(String),
83}
84impl ValidationState {
85 pub fn msg(&self) -> Option<&String> {
87 match self {
88 ValidationState::Passed => None,
89 ValidationState::ParseError(e) => Some(e),
90 ValidationState::ValidationError(e) => Some(e),
91 }
92 }
93 pub fn take_msg(self) -> Option<String> {
95 match self {
96 ValidationState::Passed => None,
97 ValidationState::ParseError(e) => Some(e),
98 ValidationState::ValidationError(e) => Some(e),
99 }
100 }
101
102 pub fn is_passed(&self) -> bool {
104 matches!(self, ValidationState::Passed)
105 }
106 pub fn is_err(&self) -> bool {
108 !self.is_passed()
109 }
110
111 pub fn is_parse_err(&self) -> bool {
113 matches!(self, ValidationState::ParseError(_))
114 }
115
116 pub fn is_validation_err(&self) -> bool {
118 matches!(self, ValidationState::ValidationError(_))
119 }
120}
121
122#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
124pub enum UpdateEvent {
125 OnFocusout,
126 OnInput,
127 #[default]
128 OnChange,
129}
130
131pub trait VanityControlData<FD: FormToolData>: Clone + Send + Sync + 'static {
133 fn render_control<FS: FormStyle>(
135 fs: &FS,
136 fd: RwSignal<FD>,
137 control: ControlRenderData<FS, Self>,
138 value_getter: Option<Signal<String>>,
139 ) -> AnyView;
140}
141pub trait GetterVanityControlData<FD: FormToolData>: VanityControlData<FD> {}
142
143pub trait ControlData<FD: FormToolData>: Clone + Send + Sync + 'static {
145 type ReturnType: Clone + Send + Sync;
147
148 fn render_control<FS: FormStyle>(
150 fs: &FS,
151 fd: RwSignal<FD>,
152 control: ControlRenderData<FS, Self>,
153 value_getter: Signal<Self::ReturnType>,
154 value_setter: SignalSetter<Self::ReturnType>,
155 validation_state: Signal<ValidationState>,
156 ) -> AnyView;
157}
158pub trait ValidatedControlData<FD: FormToolData>: ControlData<FD> {}
159
160pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
162 pub styles: Vec<FS::StylingAttributes>,
163 pub data: C,
164}
165impl<FS, C> Clone for ControlRenderData<FS, C>
166where
167 FS: FormStyle + ?Sized,
168 C: Clone,
169{
170 fn clone(&self) -> Self {
171 ControlRenderData {
172 styles: self.styles.clone(),
173 data: self.data.clone(),
174 }
175 }
176}
177
178pub struct VanityControlBuilder<FD: FormToolData, C: VanityControlData<FD>> {
180 pub(crate) style_attributes: Vec<<FD::Style as FormStyle>::StylingAttributes>,
181 pub data: C,
182 pub(crate) getter: Option<Arc<dyn FieldGetter<FD, String>>>,
183 pub(crate) show_when: Option<Arc<dyn ShowWhenFn<FD, FD::Context>>>,
184}
185
186pub(crate) struct BuiltVanityControlData<FD: FormToolData, C: VanityControlData<FD>> {
187 pub(crate) render_data: ControlRenderData<FD::Style, C>,
188 pub(crate) getter: Option<Arc<dyn FieldGetter<FD, String>>>,
189 pub(crate) show_when: Option<Arc<dyn ShowWhenFn<FD, FD::Context>>>,
190}
191
192impl<FD: FormToolData, C: VanityControlData<FD>> VanityControlBuilder<FD, C> {
193 pub(crate) fn new(data: C) -> Self {
195 VanityControlBuilder {
196 data,
197 style_attributes: Vec::new(),
198 getter: None,
199 show_when: None,
200 }
201 }
202
203 pub(crate) fn build(self) -> BuiltVanityControlData<FD, C> {
205 BuiltVanityControlData {
206 render_data: ControlRenderData {
207 data: self.data,
208 styles: self.style_attributes,
209 },
210 getter: self.getter,
211 show_when: self.show_when,
212 }
213 }
214
215 pub fn show_when(
219 mut self,
220 when: impl Fn(Signal<FD>, Arc<FD::Context>) -> bool + Send + Sync + 'static,
221 ) -> Self {
222 self.show_when = Some(Arc::new(when));
223 self
224 }
225
226 pub fn style(mut self, attribute: <FD::Style as FormStyle>::StylingAttributes) -> Self {
228 self.style_attributes.push(attribute);
229 self
230 }
231}
232
233impl<FD: FormToolData, C: GetterVanityControlData<FD>> VanityControlBuilder<FD, C> {
234 pub fn getter(mut self, getter: impl FieldGetter<FD, String>) -> Self {
240 self.getter = Some(Arc::new(getter));
241 self
242 }
243}
244
245#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
247pub enum ControlBuildError {
248 MissingGetter,
250 MissingSetter,
252 MissingParseFn,
254 MissingUnParseFn,
256}
257impl Display for ControlBuildError {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 let message = match self {
260 ControlBuildError::MissingGetter => "missing getter function",
261 ControlBuildError::MissingSetter => "missing setter function",
262 ControlBuildError::MissingParseFn => "missing parse function",
263 ControlBuildError::MissingUnParseFn => "missing unparse function",
264 };
265 write!(f, "{}", message)
266 }
267}
268
269pub(crate) struct BuiltControlData<FD: FormToolData, C: ControlData<FD>, FDT> {
271 pub(crate) render_data: ControlRenderData<FD::Style, C>,
272 pub(crate) getter: Arc<dyn FieldGetter<FD, FDT>>,
273 pub(crate) setter: Arc<dyn FieldSetter<FD, FDT>>,
274 pub(crate) parse_fn: Box<dyn ParseFn<C::ReturnType, FDT>>,
275 pub(crate) unparse_fn: Box<dyn UnparseFn<C::ReturnType, FDT>>,
276 pub(crate) validation_fn: Option<Arc<dyn ValidationFn<FD>>>,
277 pub(crate) show_when: Option<Arc<dyn ShowWhenFn<FD, FD::Context>>>,
278}
279
280pub struct ControlBuilder<FD: FormToolData, C: ControlData<FD>, FDT> {
282 pub(crate) getter: Option<Arc<dyn FieldGetter<FD, FDT>>>,
283 pub(crate) setter: Option<Arc<dyn FieldSetter<FD, FDT>>>,
284 pub(crate) parse_fn: Option<Box<dyn ParseFn<C::ReturnType, FDT>>>,
285 pub(crate) unparse_fn: Option<Box<dyn UnparseFn<C::ReturnType, FDT>>>,
286 pub(crate) validation_fn: Option<Arc<dyn ValidationFn<FD>>>,
287 pub(crate) style_attributes: Vec<<FD::Style as FormStyle>::StylingAttributes>,
288 pub(crate) show_when: Option<Arc<dyn ShowWhenFn<FD, FD::Context>>>,
289 pub data: C,
290}
291
292impl<FD: FormToolData, C: ControlData<FD>, FDT> ControlBuilder<FD, C, FDT> {
293 pub(crate) fn new(data: C) -> Self {
295 ControlBuilder {
296 data,
297 getter: None,
298 setter: None,
299 parse_fn: None,
300 unparse_fn: None,
301 validation_fn: None,
302 style_attributes: Vec::new(),
303 show_when: None,
304 }
305 }
306
307 pub(crate) fn build(self) -> Result<BuiltControlData<FD, C, FDT>, ControlBuildError> {
311 let getter = match self.getter {
312 Some(getter) => getter,
313 None => return Err(ControlBuildError::MissingGetter),
314 };
315 let setter = match self.setter {
316 Some(setter) => setter,
317 None => return Err(ControlBuildError::MissingSetter),
318 };
319 let parse_fn = match self.parse_fn {
320 Some(parse_fn) => parse_fn,
321 None => return Err(ControlBuildError::MissingParseFn),
322 };
323 let unparse_fn = match self.unparse_fn {
324 Some(unparse_fn) => unparse_fn,
325 None => return Err(ControlBuildError::MissingUnParseFn),
326 };
327
328 Ok(BuiltControlData {
329 render_data: ControlRenderData {
330 data: self.data,
331 styles: self.style_attributes,
332 },
333 getter,
334 setter,
335 parse_fn,
336 unparse_fn,
337 validation_fn: self.validation_fn,
338 show_when: self.show_when,
339 })
340 }
341
342 pub fn show_when(
346 mut self,
347 when: impl Fn(Signal<FD>, Arc<FD::Context>) -> bool + Send + Sync + 'static,
348 ) -> Self {
349 self.show_when = Some(Arc::new(when));
350 self
351 }
352
353 pub fn getter(mut self, getter: impl FieldGetter<FD, FDT>) -> Self {
360 self.getter = Some(Arc::new(getter));
361 self
362 }
363
364 pub fn setter(mut self, setter: impl FieldSetter<FD, FDT>) -> Self {
371 self.setter = Some(Arc::new(setter));
372 self
373 }
374
375 pub fn parse_custom(
381 mut self,
382 parse_fn: impl ParseFn<C::ReturnType, FDT>,
383 unparse_fn: impl UnparseFn<C::ReturnType, FDT>,
384 ) -> Self {
385 self.parse_fn = Some(Box::new(parse_fn));
386 self.unparse_fn = Some(Box::new(unparse_fn));
387 self
388 }
389
390 pub fn style(mut self, attribute: <FD::Style as FormStyle>::StylingAttributes) -> Self {
392 self.style_attributes.push(attribute);
393 self
394 }
395}
396
397impl<FD, C, FDT> ControlBuilder<FD, C, FDT>
398where
399 FD: FormToolData,
400 C: ControlData<FD>,
401 FDT: TryFrom<<C as ControlData<FD>>::ReturnType>,
402 <FDT as TryFrom<<C as ControlData<FD>>::ReturnType>>::Error: ToString,
403 <C as ControlData<FD>>::ReturnType: From<FDT>,
404{
405 pub fn parse_from(mut self) -> Self {
412 self.parse_fn = Some(Box::new(|control_return_value| {
413 FDT::try_from(control_return_value).map_err(|e| e.to_string())
414 }));
415 self.unparse_fn = Some(Box::new(|field| {
416 <C as ControlData<FD>>::ReturnType::from(field)
417 }));
418 self
419 }
420}
421
422impl<FD, C, FDT> ControlBuilder<FD, C, FDT>
423where
424 FD: FormToolData,
425 C: ControlData<FD>,
426 FDT: TryFrom<<C as ControlData<FD>>::ReturnType>,
427 <C as ControlData<FD>>::ReturnType: From<FDT>,
428{
429 pub fn parse_from_msg(mut self, msg: impl ToString + Send + Sync + 'static) -> Self {
436 self.parse_fn = Some(Box::new(move |control_return_value| {
437 FDT::try_from(control_return_value).map_err(|_| msg.to_string())
438 }));
439 self.unparse_fn = Some(Box::new(|field| {
440 <C as ControlData<FD>>::ReturnType::from(field)
441 }));
442 self
443 }
444}
445
446impl<FD, C, FDT> ControlBuilder<FD, C, FDT>
447where
448 FD: FormToolData,
449 C: ControlData<FD, ReturnType = String>,
450 FDT: FromStr + ToString,
451 <FDT as FromStr>::Err: ToString,
452{
453 pub fn parse_string(mut self) -> Self {
461 self.parse_fn = Some(Box::new(|control_return_value| {
462 control_return_value
463 .parse::<FDT>()
464 .map_err(|e| e.to_string())
465 }));
466 self.unparse_fn = Some(Box::new(|field| field.to_string()));
467 self
468 }
469
470 pub fn parse_trimmed(mut self) -> Self {
479 self.parse_fn = Some(Box::new(|control_return_value| {
480 control_return_value
481 .trim()
482 .parse::<FDT>()
483 .map_err(|e| e.to_string())
484 }));
485 self.unparse_fn = Some(Box::new(|field| field.to_string()));
486 self
487 }
488
489 pub fn parse_string_msg(mut self, msg: impl ToString + Send + Sync + 'static) -> Self {
498 self.parse_fn = Some(Box::new(move |control_return_value| {
499 control_return_value
500 .parse::<FDT>()
501 .map_err(|_| msg.to_string())
502 }));
503 self.unparse_fn = Some(Box::new(|field| field.to_string()));
504 self
505 }
506
507 pub fn parse_trimmed_msg(mut self, msg: impl ToString + Send + Sync + 'static) -> Self {
517 self.parse_fn = Some(Box::new(move |control_return_value| {
518 control_return_value
519 .trim()
520 .parse::<FDT>()
521 .map_err(|_| msg.to_string())
522 }));
523 self.unparse_fn = Some(Box::new(|field| field.to_string()));
524 self
525 }
526}
527
528impl<FD, C, FDT> ControlBuilder<FD, C, Option<FDT>>
529where
530 FD: FormToolData,
531 C: ControlData<FD, ReturnType = String>,
532 FDT: FromStr + ToString,
533{
534 pub fn parse_optional(mut self) -> Self {
546 self.parse_fn = Some(Box::new(|control_return_value| {
547 Ok(control_return_value.parse::<FDT>().ok())
548 }));
549 self.unparse_fn = Some(Box::new(|field| {
550 field.map(|v| v.to_string()).unwrap_or_default()
551 }));
552 self
553 }
554
555 pub fn parse_optional_trimmed(mut self) -> Self {
564 self.parse_fn = Some(Box::new(|control_return_value| {
565 Ok(control_return_value.trim().parse::<FDT>().ok())
566 }));
567 self.unparse_fn = Some(Box::new(|field| {
568 field.map(|v| v.to_string()).unwrap_or_default()
569 }));
570 self
571 }
572}
573
574impl<FD, C, FDT> ControlBuilder<FD, C, FDT>
575where
576 FD: FormToolData,
577 C: ControlData<FD, ReturnType = String>,
578 FDT: FromStr + ToString + Default,
579{
580 pub fn parse_or_default(mut self) -> Self {
591 self.parse_fn = Some(Box::new(|control_return_value| {
592 Ok(control_return_value.parse::<FDT>().unwrap_or_default())
593 }));
594 self.unparse_fn = Some(Box::new(|field| field.to_string()));
595 self
596 }
597
598 pub fn parse_trimmed_or_default(mut self) -> Self {
607 self.parse_fn = Some(Box::new(|control_return_value| {
608 Ok(control_return_value
609 .trim()
610 .parse::<FDT>()
611 .unwrap_or_default())
612 }));
613 self.unparse_fn = Some(Box::new(|field| field.to_string()));
614 self
615 }
616}
617
618impl<FD: FormToolData, C: ValidatedControlData<FD>, FDT> ControlBuilder<FD, C, FDT> {
619 pub fn validation_fn(
630 mut self,
631 validation_fn: impl Fn(&FD) -> Result<(), String> + Send + Sync + 'static,
632 ) -> Self {
633 self.validation_fn = Some(Arc::new(validation_fn));
634 self
635 }
636}