use crate::{form::FormToolData, styles::FormStyle}; use leptos::{RwSignal, Signal, View}; use std::{fmt::Display, rc::Rc, str::FromStr}; pub mod button; pub mod checkbox; pub mod custom; pub mod group; pub mod heading; pub mod hidden; pub mod output; pub mod radio_buttons; pub mod select; pub mod slider; pub mod spacer; pub mod stepper; pub mod submit; pub mod text_area; pub mod text_input; pub trait BuilderFn: Fn(B, Rc) -> B {} pub trait ValidationFn: Fn(&FDT) -> Result<(), String> + 'static {} pub trait ValidationCb: Fn() -> bool + 'static {} pub trait ParseFn: Fn(CR) -> Result + 'static {} pub trait UnparseFn: Fn(FDT) -> CR + 'static {} pub trait FieldGetter: Fn(FD) -> FDT + 'static {} pub trait FieldSetter: Fn(&mut FD, FDT) + 'static {} pub trait ShowWhenFn: Fn(Signal, Rc) -> bool + 'static {} pub trait RenderFn: FnOnce(Rc, RwSignal) -> (View, Option>) + 'static { } // implement the traits for all valid types impl BuilderFn for T where T: Fn(B, Rc) -> 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 {} impl UnparseFn for F where F: Fn(FDT) -> CR + 'static {} impl FieldGetter for F where F: Fn(FD) -> FDT + 'static {} impl FieldSetter for F where F: Fn(&mut FD, FDT) + 'static {} impl ShowWhenFn for F where F: Fn(Signal, Rc) -> bool + 'static {} impl RenderFn for F where F: FnOnce(Rc, RwSignal) -> (View, Option>) + 'static { } /// A trait for the data needed to render an read-only control. pub trait VanityControlData: 'static { /// Builds the control, returning the [`View`] that was built. fn build_control( fs: &FS, control: Rc>, value_getter: Option>, ) -> View; } pub trait GetterVanityControlData: VanityControlData {} /// A trait for the data needed to render an interactive control. pub trait ControlData: 'static { /// This is the data type returned by this control. type ReturnType: Clone; /// Builds the control, returning the [`View`] that was built. fn build_control( fs: &FS, control: Rc>, value_getter: Signal, value_setter: Rc, validation_state: Signal>, ) -> View; } pub trait ValidatedControlData: ControlData {} /// The data needed to render a interactive control of type `C`. pub struct ControlRenderData { pub styles: Vec, pub data: C, } /// The data needed to render a read-only control of type `C`. pub struct VanityControlBuilder { pub(crate) style_attributes: Vec<::StylingAttributes>, pub(crate) data: C, pub(crate) getter: Option>>, pub(crate) show_when: Option>>, } pub(crate) struct BuiltVanityControlData { pub(crate) render_data: ControlRenderData, pub(crate) getter: Option>>, pub(crate) show_when: Option>>, } impl VanityControlBuilder { /// Creates a new [`VanityControlBuilder`] with the given [`VanityControlData`]. pub(crate) fn new(data: C) -> Self { VanityControlBuilder { data, style_attributes: Vec::new(), getter: None, show_when: None, } } /// Builds the builder into the data needed to render the control. pub(crate) fn build(self) -> BuiltVanityControlData { BuiltVanityControlData { render_data: ControlRenderData { data: self.data, styles: self.style_attributes, }, getter: self.getter, show_when: self.show_when, } } /// Sets the function to decide when to render the control. /// /// Validations for components that are not shown DO NOT run. pub fn show_when( mut self, when: impl Fn(Signal, Rc) -> bool + 'static, ) -> Self { self.show_when = Some(Box::new(when)); self } /// Adds a styling attribute to this control. pub fn style(mut self, attribute: ::StylingAttributes) -> Self { self.style_attributes.push(attribute); self } } impl VanityControlBuilder { /// Sets the getter function. /// /// This function can get a string from the form data to be displayed /// /// Setting this getter field is NOT required for vanity controls like this one. pub fn getter(mut self, getter: impl FieldGetter) -> Self { self.getter = Some(Rc::new(getter)); self } } /// The possibilities for errors when building a control. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub enum ControlBuildError { /// The getter field was not specified. MissingGetter, /// The setter field was not specified. MissingSetter, /// The parse function was not specified. MissingParseFn, /// The unparse function was not specified. MissingUnParseFn, } impl Display for ControlBuildError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let message = match self { ControlBuildError::MissingGetter => "missing getter function", ControlBuildError::MissingSetter => "missing setter function", ControlBuildError::MissingParseFn => "missing parse function", ControlBuildError::MissingUnParseFn => "missing unparse function", }; write!(f, "{}", message) } } /// The data returned fomr a control's build function. pub(crate) struct BuiltControlData { pub(crate) render_data: ControlRenderData, pub(crate) getter: Rc>, pub(crate) setter: Rc>, pub(crate) parse_fn: Box>, pub(crate) unparse_fn: Box>, pub(crate) validation_fn: Option>>, pub(crate) show_when: Option>>, } /// A builder for a interactive control. 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<::StylingAttributes>, pub(crate) show_when: Option>>, pub data: C, } impl ControlBuilder { /// Creates a new [`ControlBuilder`] with the given [`ControlData`]. pub(crate) fn new(data: C) -> Self { ControlBuilder { data, getter: None, setter: None, parse_fn: None, unparse_fn: None, validation_fn: None, style_attributes: Vec::new(), show_when: None, } } /// Builds the builder into the data needed to render the control. /// /// This fails if a required field was not specified. pub(crate) fn build(self) -> Result, ControlBuildError> { let getter = match self.getter { Some(getter) => getter, None => return Err(ControlBuildError::MissingGetter), }; let setter = match self.setter { Some(setter) => setter, None => return Err(ControlBuildError::MissingSetter), }; let parse_fn = match self.parse_fn { Some(parse_fn) => parse_fn, None => return Err(ControlBuildError::MissingParseFn), }; let unparse_fn = match self.unparse_fn { Some(unparse_fn) => unparse_fn, None => return Err(ControlBuildError::MissingUnParseFn), }; Ok(BuiltControlData { render_data: ControlRenderData { data: self.data, styles: self.style_attributes, }, getter, setter, parse_fn, unparse_fn, validation_fn: self.validation_fn, show_when: self.show_when, }) } /// Sets the function to decide when to render the control. /// /// Validations for components that are not shown DO NOT run. pub fn show_when( mut self, when: impl Fn(Signal, Rc) -> bool + 'static, ) -> Self { self.show_when = Some(Box::new(when)); self } /// Sets the getter function. /// /// This function should get the field from the form data /// for use in the form field. /// /// Setting this getter field is required. pub fn getter(mut self, getter: impl FieldGetter) -> Self { self.getter = Some(Rc::new(getter)); self } /// Sets the setter function. /// /// This function should get the field from the form data /// for use in the form field. /// /// Setting this setter field is required. pub fn setter(mut self, setter: impl FieldSetter) -> Self { self.setter = Some(Rc::new(setter)); self } /// Sets the parse functions to the ones given. /// /// The parse and unparse functions define how to turn what the user /// types in the form into what is stored in the form data struct and /// vice versa. pub fn parse_custom( mut self, parse_fn: impl ParseFn, unparse_fn: impl UnparseFn, ) -> Self { self.parse_fn = Some(Box::new(parse_fn)); self.unparse_fn = Some(Box::new(unparse_fn)); self } /// Adds a styling attribute to this control. pub fn style(mut self, attribute: ::StylingAttributes) -> Self { self.style_attributes.push(attribute); self } } impl ControlBuilder where FD: FormToolData, C: ControlData, FDT: TryFrom<::ReturnType>, ::ReturnType>>::Error: ToString, ::ReturnType: From, { /// Sets the parse functions to use the [`TryFrom`] and [`From`] traits /// for parsing and unparsing respectively. /// /// The parse and unparse functions define how to turn what the user /// types in the form into what is stored in the form data struct and /// vice versa. pub fn parse_from(mut self) -> Self { self.parse_fn = Some(Box::new(|control_return_value| { FDT::try_from(control_return_value).map_err(|e| e.to_string()) })); self.unparse_fn = Some(Box::new(|field| { ::ReturnType::from(field) })); self } } impl ControlBuilder where FD: FormToolData, C: ControlData, FDT: FromStr + ToString, ::Err: ToString, { /// Sets the parse functions to use the [`FromStr`] [`ToString`] and traits /// for parsing and unparsing respectively. To trim the string before parsing, /// see [`parse_trimed_string`]. /// /// The parse and unparse functions define how to turn what the user /// types in the form into what is stored in the form data struct and /// vice versa. pub fn parse_string(mut self) -> Self { self.parse_fn = Some(Box::new(|control_return_value| { control_return_value .parse::() .map_err(|e| e.to_string()) })); self.unparse_fn = Some(Box::new(|field| field.to_string())); self } /// Sets the parse functions to use the [`FromStr`] [`ToString`] and traits /// for parsing and unparsing respectively, similar to [`parse_string`]. /// However, this method trims the string before parsing. /// /// The parse and unparse functions define how to turn what the user /// types in the form into what is stored in the form data struct and /// vice versa. pub fn parse_trimmed(mut self) -> Self { self.parse_fn = Some(Box::new(|control_return_value| { control_return_value .trim() .parse::() .map_err(|e| e.to_string()) })); self.unparse_fn = Some(Box::new(|field| field.to_string())); self } } impl ControlBuilder { /// Sets the validation function for this control /// /// This allows you to check if the parsed value is a valid value. /// /// You are given the entire [`FormToolData`] struct, but you should only /// validate the field you are creating. You can use the other fields in /// the struct as context. /// /// Ex. You have a month and a day field in a form. You use the month /// field to help ensure that the day is a valid day of that month. pub fn validation_fn( mut self, validation_fn: impl Fn(&FD) -> Result<(), String> + 'static, ) -> Self { self.validation_fn = Some(Rc::new(validation_fn)); self } }