From fbe746702a0b134b7928e5826ecea02d409bf77d Mon Sep 17 00:00:00 2001 From: Mitchell Marino Date: Sat, 16 Mar 2024 15:48:32 -0500 Subject: [PATCH] second round of impl --- src/controls/heading.rs | 37 +++++++----- src/controls/mod.rs | 114 +++++++++++++++++++++++++++++++++++++ src/controls/select.rs | 45 ++++++++------- src/controls/submit.rs | 37 +++++++----- src/controls/text_area.rs | 40 ++++++------- src/controls/text_input.rs | 43 +++++++------- src/form.rs | 68 ++++++++++++++++++++++ src/lib.rs | 62 +------------------- src/provider_impl.rs | 86 ---------------------------- src/styles/mod.rs | 25 ++++++++ src/styles/tw_grid.rs | 89 +++++++++++++++++++++++++++++ 11 files changed, 408 insertions(+), 238 deletions(-) create mode 100644 src/form.rs delete mode 100644 src/provider_impl.rs create mode 100644 src/styles/mod.rs create mode 100644 src/styles/tw_grid.rs diff --git a/src/controls/heading.rs b/src/controls/heading.rs index bba7f1a..1162186 100644 --- a/src/controls/heading.rs +++ b/src/controls/heading.rs @@ -1,30 +1,37 @@ use leptos::View; -use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider}; +use super::{VanityControl, VanityControlBuilder, VanityControlData}; +use crate::{ + form::{FormBuilder, FormData}, + styles::FormStyle, +}; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct HeadingData { pub(crate) title: String, } -impl HeadingData { - fn new(title: String) -> Self { - HeadingData { title } +impl VanityControlData for HeadingData { + fn build_control( + fs: &FS, + control: VanityControl, + ) -> View { + fs.heading(control) } } -impl ControlData for HeadingData { - fn build_control(self, fs: &FS, attributes: Vec) -> View { - fs.heading(self, attributes) +impl FormBuilder { + pub fn heading( + self, + builder: impl Fn(VanityControlBuilder) -> VanityControl, + ) -> Self { + self.new_vanity(builder) } } -impl FormBuilder { - pub fn heading(self, title: impl ToString) -> ControlBuilder { - ControlBuilder { - fb: self, - parse_fn: None, - style_attributes: Vec::new(), - data: HeadingData::new(title.to_string()), - } +impl VanityControlBuilder { + pub fn title(mut self, title: impl ToString) -> Self { + self.data.title = title.to_string(); + self } } diff --git a/src/controls/mod.rs b/src/controls/mod.rs index 959d1ae..9a768e9 100644 --- a/src/controls/mod.rs +++ b/src/controls/mod.rs @@ -1,5 +1,119 @@ +use crate::{form::FormData, styles::FormStyle}; +use leptos::View; + pub mod heading; pub mod select; pub mod submit; pub mod text_area; pub mod text_input; + +pub type ValidationFn = dyn Fn(&FD) -> Result<(), String> + 'static; +pub type ParseFn = dyn Fn(CReturnType, &mut FD) -> Result<(), String> + 'static; + +pub trait VanityControlData: 'static { + fn build_control( + fs: &FS, + control: VanityControl, + ) -> View; +} + +pub trait ControlData: 'static { + type ReturnType; + + // TODO: this should also return a getter for the data + fn build_control(fs: &FS, control: Control) -> View; +} + +pub struct VanityControl { + pub data: Box, + pub style: Vec, +} + +pub struct Control { + pub data: Box, + pub parse_fn: Box>, + pub validation: Box>, + pub style: Vec, +} +pub struct VanityControlBuilder { + style_attributes: Vec, + data: C, +} + +impl VanityControlBuilder { + pub(crate) fn new(data: C) -> Self { + VanityControlBuilder { + data, + style_attributes: Vec::new(), + } + } + + pub fn build(self) -> VanityControl { + self.into() + } +} +impl Into> + for VanityControlBuilder +{ + fn into(self) -> VanityControl { + VanityControl { + data: Box::new(self.data), + style: self.style_attributes, + } + } +} + +pub struct ControlBuilder { + parse_fn: Box>, + validation_fn: Box>, + style_attributes: Vec, + data: C, +} + +impl ControlBuilder { + pub(crate) fn new(data: C) -> Self { + ControlBuilder { + data, + parse_fn: Box::new(|_, _| Ok(())), + validation_fn: Box::new(|_| Ok(())), + style_attributes: Vec::new(), + } + } + + pub fn build(self) -> Control { + self.into() + } + + pub fn parse_fn( + mut self, + parse_fn: impl Fn(C::ReturnType, &mut FD) -> Result<(), String> + 'static, + ) -> Self { + self.parse_fn = Box::new(parse_fn) as Box>; + self + } + + pub fn validation_fn( + mut self, + validation_fn: impl Fn(&FD) -> Result<(), String> + 'static, + ) -> Self { + self.validation_fn = Box::new(validation_fn) as Box>; + self + } + + pub fn style(mut self, attribute: FS::StylingAttributes) -> Self { + self.style_attributes.push(attribute); + self + } +} +impl Into> + for ControlBuilder +{ + fn into(self) -> Control { + Control { + data: Box::new(self.data), + style: self.style_attributes, + parse_fn: self.parse_fn, + validation: self.validation_fn, + } + } +} diff --git a/src/controls/select.rs b/src/controls/select.rs index 4612b4d..944d331 100644 --- a/src/controls/select.rs +++ b/src/controls/select.rs @@ -1,41 +1,42 @@ use leptos::View; -use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider}; +use super::{Control, ControlBuilder, ControlData}; +use crate::{ + form::{FormBuilder, FormData}, + styles::FormStyle, +}; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SelectData { pub(crate) name: String, pub(crate) options: Vec, } -impl SelectData { - fn new(name: String) -> Self { - SelectData { - name, - options: Vec::new(), - } +impl ControlData for SelectData { + type ReturnType = String; + + fn build_control(fs: &FS, control: Control) -> View { + fs.select(control) } } -impl ControlData for SelectData { - fn build_control(self, fs: &FS, attributes: Vec) -> View { - fs.select(self, attributes) +impl FormBuilder { + pub fn select( + self, + builder: impl Fn(ControlBuilder) -> Control, + ) -> Self { + self.new_control(builder) } } -impl FormBuilder { - pub fn select(self, name: impl ToString) -> ControlBuilder { - ControlBuilder { - fb: self, - parse_fn: None, - style_attributes: Vec::new(), - data: SelectData::new(name.to_string()), - } - } -} - -impl ControlBuilder { +impl ControlBuilder { pub fn options(mut self, options: Vec) -> Self { self.data.options = options; self } + + pub fn and_option(mut self, option: impl ToString) -> Self { + self.data.options.push(option.to_string()); + self + } } diff --git a/src/controls/submit.rs b/src/controls/submit.rs index dd1330b..ba97d3d 100644 --- a/src/controls/submit.rs +++ b/src/controls/submit.rs @@ -1,30 +1,37 @@ use leptos::View; -use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider}; +use super::{VanityControl, VanityControlBuilder, VanityControlData}; +use crate::{ + form::{FormBuilder, FormData}, + styles::FormStyle, +}; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SubmitData { pub(crate) text: String, } -impl SubmitData { - fn new(text: String) -> Self { - SubmitData { text } +impl VanityControlData for SubmitData { + fn build_control( + fs: &FS, + control: VanityControl, + ) -> View { + fs.submit(control) } } -impl ControlData for SubmitData { - fn build_control(self, fs: &FS, attributes: Vec) -> View { - fs.submit(self, attributes) +impl FormBuilder { + pub fn submit( + self, + builder: impl Fn(VanityControlBuilder) -> VanityControl, + ) -> Self { + self.new_vanity(builder) } } -impl FormBuilder { - pub fn submit(self, text: impl ToString) -> ControlBuilder { - ControlBuilder { - fb: self, - parse_fn: None, - style_attributes: Vec::new(), - data: SubmitData::new(text.to_string()), - } +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 5684c9e..b0f8baa 100644 --- a/src/controls/text_area.rs +++ b/src/controls/text_area.rs @@ -1,39 +1,35 @@ use leptos::View; -use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider}; +use super::{Control, ControlBuilder, ControlData}; +use crate::{ + form::{FormBuilder, FormData}, + styles::FormStyle, +}; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct TextAreaData { pub(crate) name: String, pub(crate) placeholder: Option, } -impl TextAreaData { - fn new(name: String) -> Self { - TextAreaData { - name, - placeholder: None, - } +impl ControlData for TextAreaData { + type ReturnType = String; + + fn build_control(fs: &FS, control: Control) -> View { + fs.text_area(control) } } -impl ControlData for TextAreaData { - fn build_control(self, fs: &FS, attributes: Vec) -> View { - fs.text_area(self, attributes) +impl FormBuilder { + pub fn text_area( + self, + builder: impl Fn(ControlBuilder) -> Control, + ) -> Self { + self.new_control(builder) } } -impl FormBuilder { - pub fn text_area(self, name: impl ToString) -> ControlBuilder { - ControlBuilder { - fb: self, - parse_fn: None, - style_attributes: Vec::new(), - data: TextAreaData::new(name.to_string()), - } - } -} - -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 f05f3b5..36352b3 100644 --- a/src/controls/text_input.rs +++ b/src/controls/text_input.rs @@ -1,45 +1,50 @@ use leptos::View; -use crate::{ControlBuilder, ControlData, FormBuilder, FormData, FormStyleProvider}; +use super::{Control, ControlBuilder, ControlData}; +use crate::{ + form::{FormBuilder, FormData}, + styles::FormStyle, +}; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TextInputData { pub(crate) name: String, pub(crate) placeholder: Option, pub(crate) label: Option, - pub(crate) initital_text: String, + pub(crate) initial_text: String, pub(crate) input_type: &'static str, } -impl TextInputData { - fn new(name: String) -> Self { +impl Default for TextInputData { + fn default() -> Self { TextInputData { - name, + name: String::new(), placeholder: None, label: None, - initital_text: String::new(), + initial_text: String::new(), input_type: "input", } } } -impl ControlData for TextInputData { - fn build_control(self, fs: &FS, attributes: Vec) -> View { - fs.text_input(self, attributes) +impl ControlData for TextInputData { + type ReturnType = String; + + fn build_control(fs: &FS, control: Control) -> View { + fs.text_input(control) } } -impl FormBuilder { - pub fn text_input(self, name: impl ToString) -> ControlBuilder { - ControlBuilder { - fb: self, - parse_fn: None, - style_attributes: Vec::new(), - data: TextInputData::new(name.to_string()), - } +impl FormBuilder { + pub fn text_input( + self, + builder: impl Fn(ControlBuilder) -> Control, + ) -> 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 @@ -51,7 +56,7 @@ impl ControlBuilder } pub fn initial_text(mut self, text: impl ToString) -> Self { - self.data.initital_text = text.to_string(); + self.data.initial_text = text.to_string(); self } diff --git a/src/form.rs b/src/form.rs new file mode 100644 index 0000000..d3cfc79 --- /dev/null +++ b/src/form.rs @@ -0,0 +1,68 @@ +use crate::{ + controls::{ + Control, ControlBuilder, ControlData, ValidationFn, VanityControl, VanityControlBuilder, + VanityControlData, + }, + styles::FormStyle, +}; +use leptos::{IntoView, View}; +use std::marker::PhantomData; + +pub struct FormBuilder { + _fd: PhantomData, + fs: FS, + validations: Vec>>, + views: Vec, +} + +impl FormBuilder { + pub fn new(form_style: FS) -> FormBuilder { + FormBuilder { + _fd: PhantomData::default(), + fs: form_style, + validations: Vec::new(), + views: Vec::new(), + } + } + + pub(crate) fn new_vanity( + mut self, + builder: impl Fn(VanityControlBuilder) -> VanityControl, + ) -> 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) -> Control, + ) -> Self { + let control_builder = ControlBuilder::new(C::default()); + let control = builder(control_builder); + self.add_control(control); + self + } + + fn add_vanity(&mut self, vanity_control: VanityControl) { + let view = VanityControlData::build_control::(&self.fs, vanity_control); + self.views.push(view); + } + + fn add_control(&mut self, control: Control) { + let view = ControlData::build_control(&self.fs, control); + self.views.push(view); + } + + // TODO: this should return a Form object + // The Form should have `form_view()`, and `validate(&FD)` functions. + pub fn build(self) -> View { + self.views.into_view() + } +} + +pub trait FormData: Default { + // TODO: this should return a Form Object + fn create_form() -> View; +} diff --git a/src/lib.rs b/src/lib.rs index 2659700..7e42530 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,59 +1,3 @@ -use controls::{ - heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData, - text_input::TextInputData, -}; -use leptos::*; -use std::marker::PhantomData; - -mod controls; -mod provider_impl; - -pub trait FormStyleProvider { - type StylingAttributes; - - fn heading(&self, data: HeadingData, attributes: Vec) -> View; - fn text_input(&self, data: TextInputData, attributes: Vec) -> View; - fn select(&self, data: SelectData, attributes: Vec) -> View; - fn submit(&self, data: SubmitData, attributes: Vec) -> View; - fn text_area(&self, data: TextAreaData, attributes: Vec) -> View; - fn custom_component(&self, component: View) -> View; - // TODO: add group -} - -pub trait ControlData: 'static { - fn build_control(self, fs: &FS, attributes: Vec) -> View; -} - -pub struct FormBuilder { - _fd: PhantomData, - fs: FS, - controls: Vec<(Box>, Vec)>, -} - -pub trait FormData {} - -pub struct ControlBuilder> { - fb: FormBuilder, - parse_fn: Option>, - style_attributes: Vec, - data: C, -} - -impl> ControlBuilder { - pub fn parse_fn(mut self, parse_fn: Box) -> Self { - self.parse_fn = Some(parse_fn); - self - } - - pub fn style(mut self, attribute: FS::StylingAttributes) -> Self { - self.style_attributes.push(attribute); - self - } - - pub fn end(mut self) -> FormBuilder { - self.fb - .controls - .push((Box::new(self.data), self.style_attributes)); - self.fb - } -} +pub mod controls; +pub mod form; +pub mod styles; diff --git a/src/provider_impl.rs b/src/provider_impl.rs deleted file mode 100644 index 4c7b95a..0000000 --- a/src/provider_impl.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - controls::{select::SelectData, submit::SubmitData, text_area::TextAreaData}, - FormStyleProvider, HeadingData, TextInputData, -}; -use leptos::*; - -pub enum TailwindStylingAttibutes { - Width(u32), -} - -pub struct TailwindFormStyleProvider; - -impl FormStyleProvider for TailwindFormStyleProvider { - type StylingAttributes = TailwindFormStyleProvider; - - // fn label(&self, data: LabelData) -> View { - // view! { - // }.into_view() - // } - - fn heading(&self, data: HeadingData, attributes: Vec) -> View { - view! { -

- {&data.title} -

- } - .into_view() - } - - fn text_input(&self, data: TextInputData, attributes: Vec) -> View { - view! { - - - }.into_view() - } - - fn select(&self, data: SelectData, attributes: Vec) -> View { - view! { - - }.into_view() - } - - fn submit(&self, data: SubmitData, attributes: Vec) -> View { - view! { - - } - .into_view() - } - - fn text_area(&self, data: TextAreaData, attributes: Vec) -> View { - view! { -