diff --git a/src/controls/button.rs b/src/controls/button.rs index a3d39fe..ee03f53 100644 --- a/src/controls/button.rs +++ b/src/controls/button.rs @@ -1,12 +1,11 @@ -use super::ControlRenderData; +use super::{BuilderFn, ControlRenderData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::RwSignal; use std::rc::Rc; use web_sys::MouseEvent; -type ButtonAction = dyn Fn(MouseEvent, &mut FD); +type ButtonAction = dyn Fn(MouseEvent, RwSignal) + 'static; -#[derive(Clone)] pub struct ButtonData { pub(crate) text: String, pub(crate) action: Option>>, @@ -19,21 +18,26 @@ impl Default for ButtonData { } } } +impl Clone for ButtonData { + fn clone(&self) -> Self { + ButtonData { + text: self.text.clone(), + action: self.action.clone(), + } + } +} -impl FormBuilder { - pub fn button( - mut self, - builder: impl Fn(ButtonBuilder) -> ButtonBuilder, - ) -> Self { +impl FormBuilder { + pub fn button(mut self, builder: impl BuilderFn, FD::Context>) -> Self { let button_builder = ButtonBuilder::new(); - let control = builder(button_builder); + let control = builder(button_builder, &self.cx); let render_data = ControlRenderData { data: control.data, styles: control.styles, }; - let render_fn = move |fs: &FS, fd: RwSignal| { + let render_fn = move |fs: &FD::Style, fd: RwSignal| { let view = fs.button(render_data, fd); (view, None) }; @@ -43,13 +47,12 @@ impl FormBuilder { } } -#[derive(Clone)] -pub struct ButtonBuilder { - pub(crate) styles: Vec, +pub struct ButtonBuilder { + pub(crate) styles: Vec<::StylingAttributes>, pub(crate) data: ButtonData, } -impl ButtonBuilder { +impl ButtonBuilder { fn new() -> Self { ButtonBuilder { styles: Vec::default(), @@ -57,7 +60,7 @@ impl ButtonBuilder { } } - pub fn style(mut self, style: FS::StylingAttributes) -> Self { + pub fn style(mut self, style: ::StylingAttributes) -> Self { self.styles.push(style); self } @@ -67,7 +70,7 @@ impl ButtonBuilder { self } - pub fn action(mut self, action: impl Fn(MouseEvent, &mut FD) + 'static) -> Self { + pub fn action(mut self, action: impl Fn(MouseEvent, RwSignal) + 'static) -> Self { self.data.action = Some(Rc::new(action)); self } diff --git a/src/controls/checkbox.rs b/src/controls/checkbox.rs index 67bd179..f74f714 100644 --- a/src/controls/checkbox.rs +++ b/src/controls/checkbox.rs @@ -1,7 +1,6 @@ -use leptos::{Signal, View}; - -use super::{ControlBuilder, ControlData, ControlRenderData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct CheckboxData { @@ -23,18 +22,16 @@ impl ControlData for CheckboxData { } } -impl FormBuilder { +impl FormBuilder { pub fn checkbox( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/controls/custom.rs b/src/controls/custom.rs index fc867b9..6555cba 100644 --- a/src/controls/custom.rs +++ b/src/controls/custom.rs @@ -1,11 +1,11 @@ -use super::{ControlBuilder, ControlData}; -use crate::{styles::FormStyle, FormBuilder, FormToolData}; +use super::{BuilderFn, ControlBuilder, ControlData}; +use crate::{FormBuilder, FormToolData}; -impl FormBuilder { +impl FormBuilder { pub fn custom( mut self, control_data: CC, - builder: impl Fn(ControlBuilder) -> ControlBuilder, + builder: impl Fn(ControlBuilder) -> ControlBuilder, ) -> Self { let control_builder = ControlBuilder::new(control_data); let control = builder(control_builder); @@ -15,7 +15,7 @@ impl FormBuilder { pub fn custom_default( self, - builder: impl Fn(ControlBuilder) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } diff --git a/src/controls/group.rs b/src/controls/group.rs index 27711ac..cced99c 100644 --- a/src/controls/group.rs +++ b/src/controls/group.rs @@ -1,17 +1,21 @@ use super::ValidationCb; -use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use crate::styles::FormStyle; +use crate::{form::FormToolData, form_builder::FormBuilder}; use leptos::{CollectView, RwSignal}; -impl FormBuilder { - pub fn group(mut self, builder: impl Fn(FormBuilder) -> FormBuilder) -> Self { - let mut group_builder = FormBuilder::new(); +impl FormBuilder { + pub fn group(mut self, builder: impl Fn(FormBuilder) -> FormBuilder) -> Self { + let mut group_builder = FormBuilder::new_group(self.cx); group_builder = builder(group_builder); + // Take context back + self.cx = group_builder.cx; + for validation in group_builder.validations { self.validations.push(validation); } - let render_fn = move |fs: &FS, fd: RwSignal| { + let render_fn = move |fs: &FD::Style, fd: RwSignal| { let (views, validation_cbs): (Vec<_>, Vec<_>) = group_builder .render_fns .into_iter() diff --git a/src/controls/heading.rs b/src/controls/heading.rs index 1f95191..4e22d4b 100644 --- a/src/controls/heading.rs +++ b/src/controls/heading.rs @@ -1,4 +1,4 @@ -use super::{ControlRenderData, VanityControlBuilder, VanityControlData}; +use super::{BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::View; @@ -17,18 +17,16 @@ impl VanityControlData for HeadingData { } } -impl FormBuilder { +impl FormBuilder { pub fn heading( self, - builder: impl Fn( - VanityControlBuilder, - ) -> VanityControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_vanity(builder) } } -impl VanityControlBuilder { +impl VanityControlBuilder { pub fn title(mut self, title: impl ToString) -> Self { self.data.title = title.to_string(); self diff --git a/src/controls/hidden.rs b/src/controls/hidden.rs index 88bcafc..11ffcf9 100644 --- a/src/controls/hidden.rs +++ b/src/controls/hidden.rs @@ -1,7 +1,6 @@ -use leptos::{Signal, View}; - -use super::{ControlBuilder, ControlData, ControlRenderData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct HiddenData; @@ -20,12 +19,10 @@ impl ControlData for HiddenData { } } -impl FormBuilder { +impl FormBuilder { pub fn hidden( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } diff --git a/src/controls/mod.rs b/src/controls/mod.rs index 5d8c113..59ecd9c 100644 --- a/src/controls/mod.rs +++ b/src/controls/mod.rs @@ -18,6 +18,7 @@ pub mod submit; pub mod text_area; pub mod text_input; +pub trait BuilderFn: Fn(B, &CX) -> B {} pub trait ValidationFn: Fn(&FDT) -> Result<(), String> + 'static {} pub trait ValidationCb: Fn() -> bool + 'static {} pub trait ParseFn: Fn(CR) -> Result + 'static {} @@ -30,6 +31,7 @@ pub trait RenderFn: } // implement the traits for all valid types +impl BuilderFn for T where T: Fn(B, &CX) -> B {} impl ValidationFn for T where T: Fn(&FDT) -> Result<(), String> + 'static {} impl ValidationCb for T where T: Fn() -> bool + 'static {} impl ParseFn for F where F: Fn(CR) -> Result + 'static {} @@ -75,18 +77,18 @@ pub struct ControlRenderData { } /// The data needed to render a read-only control of type `C`. -pub struct VanityControlBuilder { - pub(crate) style_attributes: Vec, +pub struct VanityControlBuilder { + pub(crate) style_attributes: Vec<::StylingAttributes>, pub(crate) data: C, pub(crate) getter: Option>>, } -pub(crate) struct BuiltVanityControlData { - pub(crate) render_data: ControlRenderData, +pub(crate) struct BuiltVanityControlData { + pub(crate) render_data: ControlRenderData, pub(crate) getter: Option>>, } -impl VanityControlBuilder { +impl VanityControlBuilder { /// Creates a new [`VanityControlBuilder`] with the given [`VanityControlData`]. pub(crate) fn new(data: C) -> Self { VanityControlBuilder { @@ -97,7 +99,7 @@ impl VanityControlBuilder } /// Builds the builder into the data needed to render the control. - pub(crate) fn build(self) -> BuiltVanityControlData { + pub(crate) fn build(self) -> BuiltVanityControlData { BuiltVanityControlData { render_data: ControlRenderData { data: self.data, @@ -108,13 +110,13 @@ impl VanityControlBuilder } /// Adds a styling attribute to this control. - pub fn style(mut self, attribute: FS::StylingAttributes) -> Self { + pub fn style(mut self, attribute: ::StylingAttributes) -> Self { self.style_attributes.push(attribute); self } } -impl VanityControlBuilder { +impl VanityControlBuilder { /// Sets the getter function. /// /// This function can get a string from the form data to be displayed @@ -151,8 +153,8 @@ impl Display for ControlBuildError { } /// The data returned fomr a control's build function. -pub(crate) struct BuiltControlData { - pub(crate) render_data: ControlRenderData, +pub(crate) struct BuiltControlData { + pub(crate) render_data: ControlRenderData, pub(crate) getter: Rc>, pub(crate) setter: Rc>, pub(crate) parse_fn: Box>, @@ -161,17 +163,17 @@ pub(crate) struct BuiltControlData { +pub struct ControlBuilder { pub(crate) getter: Option>>, pub(crate) setter: Option>>, pub(crate) parse_fn: Option>>, pub(crate) unparse_fn: Option>>, pub(crate) validation_fn: Option>>, - pub(crate) style_attributes: Vec, + pub(crate) style_attributes: Vec<::StylingAttributes>, pub data: C, } -impl ControlBuilder { +impl ControlBuilder { /// Creates a new [`ControlBuilder`] with the given [`ControlData`]. pub(crate) fn new(data: C) -> Self { ControlBuilder { @@ -188,7 +190,7 @@ impl ControlBuilder Result, ControlBuildError> { + pub(crate) fn build(self) -> Result, ControlBuildError> { let getter = match self.getter { Some(getter) => getter, None => return Err(ControlBuildError::MissingGetter), @@ -257,16 +259,15 @@ impl ControlBuilder Self { + pub fn style(mut self, attribute: ::StylingAttributes) -> Self { self.style_attributes.push(attribute); self } } -impl ControlBuilder +impl ControlBuilder where FD: FormToolData, - FS: FormStyle, C: ControlData, FDT: TryFrom<::ReturnType>, ::ReturnType>>::Error: ToString, @@ -289,10 +290,9 @@ where } } -impl ControlBuilder +impl ControlBuilder where FD: FormToolData, - FS: FormStyle, C: ControlData, FDT: FromStr + ToString, ::Err: ToString, @@ -333,7 +333,7 @@ where } } -impl ControlBuilder { +impl ControlBuilder { /// Sets the validation function for this control /// /// This allows you to check if the parsed value is a valid value. diff --git a/src/controls/output.rs b/src/controls/output.rs index d13500c..2e734c9 100644 --- a/src/controls/output.rs +++ b/src/controls/output.rs @@ -1,7 +1,8 @@ -use leptos::{Signal, View}; - -use super::{ControlRenderData, GetterVanityControlData, VanityControlBuilder, VanityControlData}; +use super::{ + BuilderFn, ControlRenderData, GetterVanityControlData, VanityControlBuilder, VanityControlData, +}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct OutputData; @@ -17,12 +18,10 @@ impl VanityControlData for OutputData { } impl GetterVanityControlData for OutputData {} -impl FormBuilder { +impl FormBuilder { pub fn output( self, - builder: impl Fn( - VanityControlBuilder, - ) -> VanityControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_vanity(builder) } diff --git a/src/controls/radio_buttons.rs b/src/controls/radio_buttons.rs index 6fa8961..2212a27 100644 --- a/src/controls/radio_buttons.rs +++ b/src/controls/radio_buttons.rs @@ -1,7 +1,6 @@ -use leptos::{Signal, View}; - -use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct RadioButtonsData { @@ -25,18 +24,16 @@ impl ControlData for RadioButtonsData { } impl ValidatedControlData for RadioButtonsData {} -impl FormBuilder { +impl FormBuilder { pub fn radio_buttons( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/controls/select.rs b/src/controls/select.rs index e86d268..cb84ce5 100644 --- a/src/controls/select.rs +++ b/src/controls/select.rs @@ -1,4 +1,4 @@ -use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; @@ -27,18 +27,16 @@ impl ControlData for SelectData { } impl ValidatedControlData for SelectData {} -impl FormBuilder { +impl FormBuilder { pub fn select( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/controls/slider.rs b/src/controls/slider.rs index 3cd5b5b..9f9cb50 100644 --- a/src/controls/slider.rs +++ b/src/controls/slider.rs @@ -1,9 +1,7 @@ -use std::ops::RangeInclusive; - -use leptos::{Signal, View}; - -use super::{ControlBuilder, ControlData, ControlRenderData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; +use std::ops::RangeInclusive; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SliderData { @@ -38,18 +36,16 @@ impl ControlData for SliderData { } } -impl FormBuilder { +impl FormBuilder { pub fn slider( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/controls/spacer.rs b/src/controls/spacer.rs index e65e864..6d19b23 100644 --- a/src/controls/spacer.rs +++ b/src/controls/spacer.rs @@ -1,7 +1,6 @@ -use leptos::{prelude::Signal, View}; - -use super::{ControlRenderData, VanityControlBuilder, VanityControlData}; +use super::{BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{prelude::Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SpacerData { @@ -18,18 +17,16 @@ impl VanityControlData for SpacerData { } } -impl FormBuilder { +impl FormBuilder { pub fn spacer( self, - builder: impl Fn( - VanityControlBuilder, - ) -> VanityControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_vanity(builder) } } -impl VanityControlBuilder { +impl VanityControlBuilder { pub fn height(mut self, height: impl ToString) -> Self { self.data.height = Some(height.to_string()); self diff --git a/src/controls/stepper.rs b/src/controls/stepper.rs index f874d6f..f52c662 100644 --- a/src/controls/stepper.rs +++ b/src/controls/stepper.rs @@ -1,9 +1,7 @@ -use std::ops::RangeInclusive; - -use leptos::{Signal, View}; - -use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; +use std::ops::RangeInclusive; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct StepperData { @@ -29,18 +27,16 @@ impl ControlData for StepperData { } impl ValidatedControlData for StepperData {} -impl FormBuilder { +impl FormBuilder { pub fn stepper( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/controls/submit.rs b/src/controls/submit.rs index 487799e..28a8c9b 100644 --- a/src/controls/submit.rs +++ b/src/controls/submit.rs @@ -1,7 +1,6 @@ -use leptos::{prelude::Signal, View}; - -use super::{ControlRenderData, VanityControlBuilder, VanityControlData}; +use super::{BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{prelude::Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SubmitData { @@ -18,18 +17,16 @@ impl VanityControlData for SubmitData { } } -impl FormBuilder { +impl FormBuilder { pub fn submit( self, - builder: impl Fn( - VanityControlBuilder, - ) -> VanityControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_vanity(builder) } } -impl VanityControlBuilder { +impl VanityControlBuilder { pub fn text(mut self, text: impl ToString) -> Self { self.data.text = text.to_string(); self diff --git a/src/controls/text_area.rs b/src/controls/text_area.rs index ea1214f..d16c06f 100644 --- a/src/controls/text_area.rs +++ b/src/controls/text_area.rs @@ -1,4 +1,4 @@ -use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; @@ -23,18 +23,16 @@ impl ControlData for TextAreaData { } impl ValidatedControlData for TextAreaData {} -impl FormBuilder { +impl FormBuilder { pub fn text_area( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn placeholder(mut self, placeholder: impl ToString) -> Self { self.data.placeholder = Some(placeholder.to_string()); self diff --git a/src/controls/text_input.rs b/src/controls/text_input.rs index dd62869..7bfc063 100644 --- a/src/controls/text_input.rs +++ b/src/controls/text_input.rs @@ -1,7 +1,6 @@ -use leptos::{Signal, View}; - -use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; +use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::{Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TextInputData { @@ -39,18 +38,16 @@ impl ControlData for TextInputData { } impl ValidatedControlData for TextInputData {} -impl FormBuilder { +impl FormBuilder { pub fn text_input( self, - builder: impl Fn( - ControlBuilder, - ) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { self.new_control(builder) } } -impl ControlBuilder { +impl ControlBuilder { pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/form.rs b/src/form.rs index 3d62d03..b8055a7 100644 --- a/src/form.rs +++ b/src/form.rs @@ -40,15 +40,16 @@ pub struct Form { impl Form { /// Gets the [`Validator`] for this form. - pub fn validator(self) -> FormValidator { + pub fn validator(&self) -> FormValidator { FormValidator { - validations: self.validations, + validations: self.validations.clone(), } } /// Validates the [`ToolFormData`], returning the result pub fn validate(&self) -> Result<(), String> { - self.fd.get_untracked().validate() + let validator = self.validator(); + validator.validate(&self.fd.get_untracked()) } /// Gets the view associated with this [`Form`]. @@ -80,6 +81,7 @@ impl IntoView for Form { /// to physically lay out a form, and how that data should be parsed and validated. pub trait FormToolData: Clone + 'static { type Style: FormStyle; + type Context: 'static; /// Defines how the form should be layed out and how the data should be parsed and validated. /// @@ -88,7 +90,7 @@ pub trait FormToolData: Clone + 'static { /// Uses the given form builder to specify what fields should be present /// in the form, what properties those fields should have, and how that /// data should be parsed and checked. - fn build_form(fb: FormBuilder) -> FormBuilder; + fn build_form(fb: FormBuilder) -> FormBuilder; /// Constructs a [`Form`] for this [`FormToolData`] type. /// @@ -96,8 +98,13 @@ pub trait FormToolData: Clone + 'static { /// [`Form`](leptos_router::Form) /// component. Call [`get_action_form`]\() to get the /// [`ActionForm`](leptos_router::ActionForm) version. - fn get_form(self, action: impl ToString, style: Self::Style) -> Form { - let builder = FormBuilder::new(); + fn get_form( + self, + action: impl ToString, + style: Self::Style, + context: Self::Context, + ) -> Form { + let builder = FormBuilder::new(context); let builder = Self::build_form(builder); builder.build_plain_form(action.to_string(), self, style) } @@ -112,13 +119,14 @@ pub trait FormToolData: Clone + 'static { self, action: Action>>, style: Self::Style, + context: Self::Context, ) -> Form where ServFn: DeserializeOwned + ServerFn + 'static, <>::Request as ClientReq>::FormData: From, { - let builder = FormBuilder::new(); + let builder = FormBuilder::new(context); let builder = Self::build_form(builder); builder.build_action_form(action, self, style) } @@ -131,18 +139,18 @@ pub trait FormToolData: Clone + 'static { /// /// However, the code to render the views are not configured out, it /// simply doesn't run, so the view needs to compile even on the server. - fn get_validator() -> FormValidator { - let builder = FormBuilder::new(); + fn get_validator(context: Self::Context) -> FormValidator { + let builder = FormBuilder::new(context); let builder = Self::build_form(builder); builder.validator() } /// Validates this [`FormToolData`] struct. /// - /// This is shorthand for creating a validator with [`get_validator`]\() - /// and then calling `validator.validate(&self)`. - fn validate(&self) -> Result<(), String> { - let validator = Self::get_validator(); + /// This is shorthand for creating a validator with [`get_validator`] + /// and then calling `validator.validate(&self, context)`. + fn validate(&self, context: Self::Context) -> Result<(), String> { + let validator = Self::get_validator(context); validator.validate(self) } } diff --git a/src/form_builder.rs b/src/form_builder.rs index de96f90..5d5a964 100644 --- a/src/form_builder.rs +++ b/src/form_builder.rs @@ -1,8 +1,8 @@ use crate::{ controls::{ - BuiltControlData, BuiltVanityControlData, ControlBuilder, ControlData, ControlRenderData, - FieldSetter, ParseFn, RenderFn, ValidationCb, ValidationFn, VanityControlBuilder, - VanityControlData, + BuilderFn, BuiltControlData, BuiltVanityControlData, ControlBuilder, ControlData, + ControlRenderData, FieldSetter, ParseFn, RenderFn, ValidationCb, ValidationFn, + VanityControlBuilder, VanityControlData, }, form::{Form, FormToolData, FormValidator}, styles::FormStyle, @@ -16,49 +16,70 @@ use serde::de::DeserializeOwned; use std::rc::Rc; use web_sys::{FormData, SubmitEvent}; +// TODO: FS, and CX may be uncessisary, as FS is the same as FD::Style +// and CX is the same as FD::Context. + /// A builder for laying out forms. /// /// This builder allows you to specify what components should make up the form. -pub struct FormBuilder { +/// +/// ### Type Parameters +/// - `FD`: FormToolData - Your form data +/// - `FS`: FormStyle - The form style you are using +/// - `CX`: Context - A user defined context that you are rendering the form in +pub struct FormBuilder { + pub(crate) cx: Rc, /// The list of [`ValidationFn`]s. pub(crate) validations: Vec>>, /// The list of functions that will render the form. - pub(crate) render_fns: Vec>>, + pub(crate) render_fns: Vec>>, /// The list of styling attributes applied on the form level - pub(crate) styles: Vec, + pub(crate) styles: Vec<::StylingAttributes>, } -impl FormBuilder { +impl FormBuilder { /// Creates a new [`FormBuilder`] - pub(crate) fn new() -> FormBuilder { + pub(crate) fn new(cx: FD::Context) -> Self { FormBuilder { + cx: Rc::new(cx), validations: Vec::new(), render_fns: Vec::new(), styles: Vec::new(), } } - pub fn style(mut self, style: FS::StylingAttributes) -> Self { + /// Creates a new [`FormBuilder`] with the given Rc'ed context, for + //// building a form group. + pub(crate) fn new_group(cx: Rc) -> Self { + FormBuilder { + cx, + validations: Vec::new(), + render_fns: Vec::new(), + styles: Vec::new(), + } + } + + pub fn style(mut self, style: ::StylingAttributes) -> Self { self.styles.push(style); self } pub(crate) fn new_vanity( mut self, - builder: impl Fn(VanityControlBuilder) -> VanityControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { let vanity_builder = VanityControlBuilder::new(C::default()); - let control = builder(vanity_builder); + let control = builder(vanity_builder, &self.cx); self.add_vanity(control); self } pub(crate) fn new_control( mut self, - builder: impl Fn(ControlBuilder) -> ControlBuilder, + builder: impl BuilderFn, FD::Context>, ) -> Self { let control_builder = ControlBuilder::new(C::default()); - let control = builder(control_builder); + let control = builder(control_builder, &self.cx); self.add_control(control); self } @@ -66,14 +87,14 @@ impl FormBuilder { // TODO: test this from a user context. A user adding a custom defined component. pub fn add_vanity( &mut self, - vanity_control: VanityControlBuilder, + vanity_control: VanityControlBuilder, ) { let BuiltVanityControlData { render_data, getter, } = vanity_control.build(); - let render_fn = move |fs: &FS, fd: RwSignal| { + let render_fn = move |fs: &FD::Style, 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) @@ -85,7 +106,7 @@ impl FormBuilder { // TODO: test this from a user context. A user adding a custom defined component. pub fn add_control( &mut self, - control: ControlBuilder, + control: ControlBuilder, ) { let built_control_data = match control.build() { Ok(c) => c, @@ -96,7 +117,7 @@ impl FormBuilder { self.validations.push(validation_fn.clone()); } - let render_fn = move |fs: &FS, fd: RwSignal| { + let render_fn = move |fs: &FD::Style, fd: RwSignal| { let (view, cb) = Self::build_control_view(fd, fs, built_control_data); (view, Some(cb)) }; @@ -106,8 +127,8 @@ impl FormBuilder { fn build_control_view( fd: RwSignal, - fs: &FS, - control_data: BuiltControlData, + fs: &FD::Style, + control_data: BuiltControlData, ) -> (View, Box) { let BuiltControlData { render_data, @@ -204,7 +225,7 @@ impl FormBuilder { self, action: Action>>, fd: FD, - fs: FS, + fs: FD::Style, ) -> Form where ServFn: DeserializeOwned + ServerFn + 'static, @@ -249,7 +270,7 @@ impl FormBuilder { } } - pub(crate) fn build_plain_form(self, url: String, fd: FD, fs: FS) -> Form { + pub(crate) fn build_plain_form(self, url: String, fd: FD, fs: FD::Style) -> Form { let fd = create_rw_signal(fd); let (views, validation_cbs): (Vec<_>, Vec<_>) = self diff --git a/src/styles/grid_form.rs b/src/styles/grid_form.rs index 3a76b16..b505db6 100644 --- a/src/styles/grid_form.rs +++ b/src/styles/grid_form.rs @@ -365,7 +365,7 @@ impl FormStyle for GridFormStyle { let action = control.data.action.clone(); let on_click = move |ev: MouseEvent| { if let Some(action) = action.clone() { - data_signal.update(|fd| action(ev, fd)); + action(ev, data_signal) } };