use crate::{ controls::{ BuiltControlData, BuiltVanityControlData, ControlBuilder, ControlData, ControlRenderData, FieldSetter, ParseFn, RenderFn, ValidationCb, ValidationFn, VanityControlBuilder, VanityControlData, }, form::{Form, FormToolData, FormValidator}, styles::FormStyle, }; use leptos::{ server_fn::{client::Client, codec::PostUrl, request::ClientReq, ServerFn}, *, }; use leptos_router::{ActionForm, Form}; use serde::de::DeserializeOwned; use std::rc::Rc; use web_sys::{FormData, SubmitEvent}; /// A builder for laying out forms. /// /// This builder allows you to specify what components should make up the form. pub struct FormBuilder { /// The list of [`ValidationFn`]s. pub(crate) validations: Vec>>, /// The list of functions that will render the form. pub(crate) render_fns: Vec>>, /// The list of styling attributes applied on the form level pub(crate) styles: Vec, } impl FormBuilder { /// Creates a new [`FormBuilder`] pub(crate) fn new() -> FormBuilder { FormBuilder { validations: Vec::new(), render_fns: Vec::new(), styles: Vec::new(), } } pub fn style(mut self, style: FS::StylingAttributes) -> Self { self.styles.push(style); self } pub(crate) fn new_vanity( mut self, builder: impl Fn(VanityControlBuilder) -> VanityControlBuilder, ) -> Self { let vanity_builder = VanityControlBuilder::new(C::default()); let control = builder(vanity_builder); self.add_vanity(control); self } pub(crate) fn new_control( mut self, builder: impl Fn(ControlBuilder) -> ControlBuilder, ) -> Self { let control_builder = ControlBuilder::new(C::default()); let control = builder(control_builder); self.add_control(control); self } // TODO: test this from a user context. A user adding a custom defined component. pub fn add_vanity( &mut self, vanity_control: VanityControlBuilder, ) { let BuiltVanityControlData { render_data, getter, } = vanity_control.build(); let render_fn = move |fs: &FS, fd: RwSignal| { let value_getter = getter.map(|getter| (move || getter(fd.get())).into_signal()); let view = VanityControlData::build_control(fs, render_data, value_getter); (view, None) }; self.render_fns.push(Box::new(render_fn)); } // TODO: test this from a user context. A user adding a custom defined component. pub fn add_control( &mut self, control: ControlBuilder, ) { let built_control_data = match control.build() { Ok(c) => c, Err(e) => panic!("Invalid Component: {}", e), }; if let Some(ref validation_fn) = built_control_data.validation_fn { self.validations.push(validation_fn.clone()); } let render_fn = move |fs: &FS, fd: RwSignal| { let (view, cb) = Self::build_control_view(fd, fs, built_control_data); (view, Some(cb)) }; self.render_fns.push(Box::new(render_fn)); } fn build_control_view( fd: RwSignal, fs: &FS, control_data: BuiltControlData, ) -> (View, Box) { let BuiltControlData { render_data, getter, setter, parse_fn, unparse_fn, validation_fn, } = control_data; let (validation_signal, validation_signal_set) = create_signal(Ok(())); let validation_fn_clone = validation_fn.clone(); let value_getter = move || { let fd = fd.get(); // rerun validation if it is failing if validation_signal.get_untracked().is_err() { if let Some(ref validation_fn) = validation_fn_clone { let validation_result = validation_fn(&fd); // if validation succeeds this time, resolve the validation error if validation_result.is_ok() { validation_signal_set.set(Ok(())); } } } unparse_fn(getter(fd)) }; let value_getter = value_getter.into_signal(); let validation_cb = move || { let validation_fn = validation_fn.as_ref(); let validation_fn = match validation_fn { Some(v) => v, None => return true, // No validation function, so validation passes }; let data = fd.get(); let validation_result = validation_fn(&data); let succeeded = validation_result.is_ok(); validation_signal_set.set(validation_result); succeeded }; let validation_cb = Box::new(validation_cb); let value_setter = Self::create_value_setter( validation_cb.clone(), validation_signal_set, parse_fn, setter, fd, ); let view = C::build_control( fs, render_data, value_getter, value_setter, validation_signal.into(), ); (view, validation_cb) } fn create_value_setter( validation_cb: Box bool + 'static>, validation_signal_set: WriteSignal>, parse_fn: Box>, setter: Rc>, fd: RwSignal, ) -> Box { let value_setter = move |value| { let parsed = match parse_fn(value) { Ok(p) => { validation_signal_set.set(Ok(())); p } Err(e) => { validation_signal_set.set(Err(e)); return; } }; // parse succeeded, update value and validate fd.update(|data| { setter(data, parsed); }); // run validation (validation_cb)(); }; Box::new(value_setter) } pub(crate) fn build_action_form( self, action: Action>>, fd: FD, fs: FS, ) -> Form where ServFn: DeserializeOwned + ServerFn + 'static, <>::Request as ClientReq>::FormData: From, { let fd = create_rw_signal(fd); let (views, validation_cbs): (Vec<_>, Vec<_>) = self .render_fns .into_iter() .map(|r_fn| r_fn(&fs, fd)) .unzip(); let elements = fs.form_frame(ControlRenderData { data: views.into_view(), styles: self.styles, }); let on_submit = move |ev: SubmitEvent| { let mut failed = false; for validation in validation_cbs.iter().flatten() { if !validation() { failed = true; } } if failed { ev.prevent_default(); } }; let view = view! { {elements} }; Form { fd, validations: self.validations, view, } } pub(crate) fn build_plain_form(self, url: String, fd: FD, fs: FS) -> Form { let fd = create_rw_signal(fd); let (views, validation_cbs): (Vec<_>, Vec<_>) = self .render_fns .into_iter() .map(|r_fn| r_fn(&fs, fd)) .unzip(); let elements = fs.form_frame(ControlRenderData { data: views.into_view(), styles: self.styles, }); let on_submit = move |ev: SubmitEvent| { let mut failed = false; for validation in validation_cbs.iter().flatten() { if !validation() { failed = true; } } if failed { ev.prevent_default(); } }; let view = view! {
{elements}
}; Form { fd, validations: self.validations, view, } } pub(crate) fn validator(&self) -> FormValidator { FormValidator { validations: self.validations.clone(), } } }