diff --git a/src/controls/button.rs b/src/controls/button.rs index bac3328..40c6d8b 100644 --- a/src/controls/button.rs +++ b/src/controls/button.rs @@ -9,6 +9,7 @@ use web_sys::MouseEvent; type ButtonAction = dyn Fn(MouseEvent, RwSignal) + 'static; +/// Data used for the button control. pub struct ButtonData { pub text: String, pub action: Option>>, @@ -31,18 +32,21 @@ impl Clone for ButtonData { } impl FormBuilder { + /// Builds a button and adds it to the form. pub fn button(self, builder: impl BuilderFn>) -> Self { let button_builder = ButtonBuilder::new(); let control = builder(button_builder); self.button_helper(control) } + /// Builds a button using the form's context and adds it to the form. pub fn button_cx(self, builder: impl BuilderCxFn, FD::Context>) -> Self { let button_builder = ButtonBuilder::new(); let control = builder(button_builder, self.cx.clone()); self.button_helper(control) } + /// The common functionality for adding a button. fn button_helper(mut self, control: ButtonBuilder) -> Self { let render_data = ControlRenderData { data: control.data, @@ -74,6 +78,7 @@ impl FormBuilder { } } +/// The struct that allows you to specify the attributes of the button. pub struct ButtonBuilder { pub(crate) styles: Vec<::StylingAttributes>, pub(crate) data: ButtonData, @@ -81,6 +86,7 @@ pub struct ButtonBuilder { } impl ButtonBuilder { + /// Creates a new [`ButtonBuilder`]. fn new() -> Self { ButtonBuilder { styles: Vec::default(), @@ -90,8 +96,6 @@ impl ButtonBuilder { } /// 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, @@ -100,16 +104,19 @@ impl ButtonBuilder { self } + /// Adds a styling attribute to the button. pub fn style(mut self, style: ::StylingAttributes) -> Self { self.styles.push(style); self } + /// Sets the text of the button. pub fn text(mut self, text: impl ToString) -> Self { self.data.text = text.to_string(); self } + /// Sets the action that is preformed when the button is clicked. 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 f7b91f6..38a5624 100644 --- a/src/controls/checkbox.rs +++ b/src/controls/checkbox.rs @@ -3,6 +3,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the checkbox control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct CheckboxData { pub name: String, @@ -24,6 +25,7 @@ impl ControlData for CheckboxData { } impl FormBuilder { + /// Builds a checkbox and adds it to the form. pub fn checkbox( self, builder: impl BuilderFn>, @@ -31,6 +33,7 @@ impl FormBuilder { self.new_control(builder) } + /// Builds a checkbox using the form's context and adds it to the form. pub fn checkbox_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -40,11 +43,20 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the checkbox. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. + /// + /// For checkbox controls, the value "checked" is sent or no key value + /// pair is sent. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self } + /// Sets the text of the checkbox's label. pub fn labeled(mut self, label: impl ToString) -> Self { self.data.label = Some(label.to_string()); self diff --git a/src/controls/custom.rs b/src/controls/custom.rs index 44f922e..5fe93c9 100644 --- a/src/controls/custom.rs +++ b/src/controls/custom.rs @@ -2,6 +2,7 @@ use super::{BuilderCxFn, BuilderFn, ControlBuilder, ControlData}; use crate::{FormBuilder, FormToolData}; impl FormBuilder { + /// Builds a custom component and adds it to the form. pub fn custom( mut self, control_data: CC, @@ -13,6 +14,8 @@ impl FormBuilder { self } + /// Builds a custom component using the form's context and adds it to the + /// form. pub fn custom_cx( mut self, control_data: CC, @@ -24,6 +27,8 @@ impl FormBuilder { self } + /// Builds a custom component, starting with the default + /// CustomControlData, and adds it to the form. pub fn custom_default( self, builder: impl BuilderFn>, @@ -31,6 +36,8 @@ impl FormBuilder { self.new_control(builder) } + /// Builds a custom component, starting with the default + /// CustomControlData using the form's context, and adds it to the form. pub fn custom_default_cx( self, builder: impl BuilderCxFn, FD::Context>, diff --git a/src/controls/group.rs b/src/controls/group.rs index 35298fa..fa0f2b5 100644 --- a/src/controls/group.rs +++ b/src/controls/group.rs @@ -6,6 +6,10 @@ use crate::{form::FormToolData, form_builder::FormBuilder}; use leptos::{CollectView, RwSignal}; impl FormBuilder { + /// Creates a form group. + /// + /// This creates a subsection of the form that controls can be added to + /// like a normal form. 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); @@ -30,6 +34,7 @@ impl FormBuilder { }); let view = fs.group(render_data.clone()); + // TODO: add conditional rendering let validation_cb = move || { let mut success = true; diff --git a/src/controls/heading.rs b/src/controls/heading.rs index 1586339..3f02b39 100644 --- a/src/controls/heading.rs +++ b/src/controls/heading.rs @@ -3,6 +3,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::View; use std::rc::Rc; +/// Data used for the heading control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct HeadingData { pub title: String, @@ -19,10 +20,12 @@ impl VanityControlData for HeadingData { } impl FormBuilder { + /// Builds a heading and adds it to the form. pub fn heading(self, builder: impl BuilderFn>) -> Self { self.new_vanity(builder) } + /// Builds a hehading using the form's context and adds it to the form. pub fn heading_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -32,6 +35,7 @@ impl FormBuilder { } impl VanityControlBuilder { + /// Sets the title of this heading. 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 49139f3..2cff073 100644 --- a/src/controls/hidden.rs +++ b/src/controls/hidden.rs @@ -6,6 +6,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the hidden control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct HiddenData { pub name: String, @@ -23,10 +24,19 @@ impl VanityControlData for HiddenData { impl GetterVanityControlData for HiddenData {} impl FormBuilder { + /// Builds a hidden form control and adds it to the form. + /// + /// This will be an input in the html form allowing you to insert some + /// data the you might not want the user modifying. pub fn hidden(self, builder: impl BuilderFn>) -> Self { self.new_vanity(builder) } + /// Builds a hidden form control using the form's context and adds it to + /// the form. + /// + /// This will be an input in the html form allowing you to insert some + /// data the you might not want the user modifying. pub fn hidden_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -36,6 +46,11 @@ impl FormBuilder { } impl VanityControlBuilder { + /// Sets the name of the hidden control. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self diff --git a/src/controls/output.rs b/src/controls/output.rs index c8f001c..a3dba1c 100644 --- a/src/controls/output.rs +++ b/src/controls/output.rs @@ -6,6 +6,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the output control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct OutputData; @@ -21,10 +22,19 @@ impl VanityControlData for OutputData { impl GetterVanityControlData for OutputData {} impl FormBuilder { + /// Builds an output form control and adds it to the form. + /// + /// This control allows you to output some text to the user based on the + /// form data. pub fn output(self, builder: impl BuilderFn>) -> Self { self.new_vanity(builder) } + /// Builds an output form control using the form's context and adds it to + /// the form. + /// + /// This control allows you to output some text to the user based on the + /// form data and form context. pub fn output_cx( self, builder: impl BuilderCxFn, FD::Context>, diff --git a/src/controls/radio_buttons.rs b/src/controls/radio_buttons.rs index b2b0611..398f998 100644 --- a/src/controls/radio_buttons.rs +++ b/src/controls/radio_buttons.rs @@ -5,11 +5,15 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the radio buttons control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct RadioButtonsData { pub name: String, pub label: Option, - pub options: Vec, + /// The options for the select. + /// + /// The first value is the string to display, the second is the value. + pub options: Vec<(String, String)>, } impl ControlData for RadioButtonsData { @@ -28,6 +32,7 @@ impl ControlData for RadioButtonsData { impl ValidatedControlData for RadioButtonsData {} impl FormBuilder { + /// Builds a radio buttons control and adds it to the form. pub fn radio_buttons( self, builder: impl BuilderFn>, @@ -35,6 +40,8 @@ impl FormBuilder { self.new_control(builder) } + /// Builds a radio buttons control using the form's context and adds it to + /// the form. pub fn radio_buttons_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -44,24 +51,60 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the radio button inputs. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self } + /// Sets the label for the radio button group. pub fn labeled(mut self, label: impl ToString) -> Self { self.data.label = Some(label.to_string()); self } + /// Adds the option to the radio button group. pub fn with_option(mut self, option: impl ToString) -> Self { - self.data.options.push(option.to_string()); + self.data + .options + .push((option.to_string(), option.to_string())); self } + /// Adds the option to the radio button group, specifying a different + /// value than what is displayed. + pub fn with_option_valued(mut self, display: impl ToString, value: impl ToString) -> Self { + self.data + .options + .push((display.to_string(), value.to_string())); + self + } + + /// Adds all the options in the provided iterator to the radio button + /// group. pub fn with_options(mut self, options: impl Iterator) -> Self { for option in options { - self.data.options.push(option.to_string()); + self.data + .options + .push((option.to_string(), option.to_string())); + } + self + } + + /// Adds all the (display_string, value) pairs in the provided iterator + /// to the radio button group. + pub fn with_options_valued( + mut self, + options: impl Iterator, + ) -> Self { + for option in options { + self.data + .options + .push((option.0.to_string(), option.1.to_string())); } self } diff --git a/src/controls/select.rs b/src/controls/select.rs index ee68c62..ad1d42f 100644 --- a/src/controls/select.rs +++ b/src/controls/select.rs @@ -5,6 +5,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the select control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SelectData { pub name: String, @@ -31,12 +32,16 @@ impl ControlData for SelectData { impl ValidatedControlData for SelectData {} impl FormBuilder { + /// Builds a select control and adds it to the form. pub fn select( self, builder: impl BuilderFn>, ) -> Self { self.new_control(builder) } + + /// Builds a select control using the form's context and adds it to the + /// form. pub fn select_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -46,16 +51,23 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the radio button inputs. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self } + /// Sets the label for the select. pub fn labeled(mut self, label: impl ToString) -> Self { self.data.label = Some(label.to_string()); self } + /// Adds the option to the select. pub fn with_option(mut self, option: impl ToString) -> Self { self.data .options @@ -63,6 +75,8 @@ impl ControlBuilder { self } + /// Adds the option to the select, specifying a different + /// value than what is displayed. pub fn with_option_valued(mut self, display: impl ToString, value: impl ToString) -> Self { self.data .options @@ -70,6 +84,7 @@ impl ControlBuilder { self } + /// Adds all the options in the provided iterator to the select. pub fn with_options(mut self, options: impl Iterator) -> Self { for option in options { self.data @@ -79,6 +94,8 @@ impl ControlBuilder { self } + /// Adds all the (display_string, value) pairs in the provided iterator + /// to the select. pub fn with_options_valued( mut self, options: impl Iterator, diff --git a/src/controls/slider.rs b/src/controls/slider.rs index 3b3ad95..57567f8 100644 --- a/src/controls/slider.rs +++ b/src/controls/slider.rs @@ -3,6 +3,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::{ops::RangeInclusive, rc::Rc}; +/// Data used for the slider control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SliderData { pub name: String, @@ -37,6 +38,7 @@ impl ControlData for SliderData { } impl FormBuilder { + /// Builds a slider (or range) control and adds it to the form. pub fn slider( self, builder: impl BuilderFn>, @@ -44,6 +46,8 @@ impl FormBuilder { self.new_control(builder) } + /// Bulids a slider (or range) control using the form's context and adds + /// it to the form. pub fn slider_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -53,26 +57,36 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the slider. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self } + /// Sets the label for the slider. pub fn labeled(mut self, label: impl ToString) -> Self { self.data.label = Some(label.to_string()); self } + /// Sets the minimum value for the slider. pub fn min(mut self, min: i32) -> Self { self.data.min = min; self } + /// Sets the maximum value for the slider. pub fn max(mut self, max: i32) -> Self { self.data.max = max; self } + /// Sets the minimum and maximum values for the slider by providing a + /// range. pub fn range(mut self, range: RangeInclusive) -> Self { self.data.min = *range.start(); self.data.max = *range.end(); diff --git a/src/controls/spacer.rs b/src/controls/spacer.rs index fe7d50a..3706a2a 100644 --- a/src/controls/spacer.rs +++ b/src/controls/spacer.rs @@ -3,6 +3,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{prelude::Signal, View}; use std::rc::Rc; +/// Data used for the spacer control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SpacerData { pub height: Option, @@ -19,10 +20,12 @@ impl VanityControlData for SpacerData { } impl FormBuilder { + /// Builds a spacer and adds it to the form. pub fn spacer(self, builder: impl BuilderFn>) -> Self { self.new_vanity(builder) } + /// Builds a spacer using the form's context and adds it to the form. pub fn spacer_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -32,6 +35,9 @@ impl FormBuilder { } impl VanityControlBuilder { + /// Sets the height of the spacer. + /// + /// This may or may not be respected based on the Style implementation. 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 aa83c56..8caf1d1 100644 --- a/src/controls/stepper.rs +++ b/src/controls/stepper.rs @@ -5,6 +5,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::{ops::RangeInclusive, rc::Rc}; +/// Data used for the stepper control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct StepperData { pub name: String, @@ -30,6 +31,7 @@ impl ControlData for StepperData { impl ValidatedControlData for StepperData {} impl FormBuilder { + /// Builds a stepper control and adds it to the form. pub fn stepper( self, builder: impl BuilderFn>, @@ -37,6 +39,8 @@ impl FormBuilder { self.new_control(builder) } + /// Builds a new stepper control using the form's context and adds it to + /// the form. pub fn stepper_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -46,31 +50,41 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the stepper control. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self } + /// Sets the label of the stepper. pub fn labeled(mut self, label: impl ToString) -> Self { self.data.label = Some(label.to_string()); self } + /// Sets the step ammount. pub fn step(mut self, step: i32) -> Self { self.data.step = Some(step); self } + /// Sets a minimum value. pub fn min(mut self, min: i32) -> Self { self.data.min = Some(min); self } + /// Sets a maximum value. pub fn max(mut self, max: i32) -> Self { self.data.max = Some(max); self } + /// Sets the minimum and maximum values using the range. pub fn range(mut self, range: RangeInclusive) -> Self { self.data.min = Some(*range.start()); self.data.max = Some(*range.end()); diff --git a/src/controls/submit.rs b/src/controls/submit.rs index 78b3c0a..4d61f08 100644 --- a/src/controls/submit.rs +++ b/src/controls/submit.rs @@ -3,6 +3,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{prelude::Signal, View}; use std::rc::Rc; +/// Data used for the submit button control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SubmitData { pub text: String, @@ -19,10 +20,13 @@ impl VanityControlData for SubmitData { } impl FormBuilder { + /// Builds a submit button and adds it to the form. pub fn submit(self, builder: impl BuilderFn>) -> Self { self.new_vanity(builder) } + /// Builds a submit button using the form's context and adds it to the + /// form. pub fn submit_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -32,6 +36,7 @@ impl FormBuilder { } impl VanityControlBuilder { + /// Sets the submit button's text. 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 bd0376b..0b81563 100644 --- a/src/controls/text_area.rs +++ b/src/controls/text_area.rs @@ -5,10 +5,13 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the text area control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct TextAreaData { pub name: String, pub placeholder: Option, + pub label: Option, + pub initial_text: String, } impl ControlData for TextAreaData { @@ -27,6 +30,7 @@ impl ControlData for TextAreaData { impl ValidatedControlData for TextAreaData {} impl FormBuilder { + /// Builds a text area control and adds it to the form. pub fn text_area( self, builder: impl BuilderFn>, @@ -34,6 +38,8 @@ impl FormBuilder { self.new_control(builder) } + /// Builds a text area control using the forms context and adds it to the + /// form. pub fn text_area_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -43,8 +49,31 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the text area. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. + pub fn named(mut self, control_name: impl ToString) -> Self { + self.data.name = control_name.to_string(); + self + } + + /// Sets the label for the text area. + pub fn labeled(mut self, label: impl ToString) -> Self { + self.data.label = Some(label.to_string()); + self + } + + /// Sets the placeholder for the text area. pub fn placeholder(mut self, placeholder: impl ToString) -> Self { self.data.placeholder = Some(placeholder.to_string()); self } + + /// Sets the intial_text for the text area. + pub fn initial_text(mut self, text: impl ToString) -> Self { + self.data.initial_text = text.to_string(); + self + } } diff --git a/src/controls/text_input.rs b/src/controls/text_input.rs index bfc0c8e..95160c2 100644 --- a/src/controls/text_input.rs +++ b/src/controls/text_input.rs @@ -5,6 +5,7 @@ use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; use std::rc::Rc; +/// Data used for the text input control. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TextInputData { pub name: String, @@ -42,6 +43,7 @@ impl ControlData for TextInputData { impl ValidatedControlData for TextInputData {} impl FormBuilder { + /// Builds a text input control and adds it to the form. pub fn text_input( self, builder: impl BuilderFn>, @@ -49,6 +51,8 @@ impl FormBuilder { self.new_control(builder) } + /// Builds a text input control using the form's context and adds it to + /// the form. pub fn text_input_cx( self, builder: impl BuilderCxFn, FD::Context>, @@ -58,28 +62,49 @@ impl FormBuilder { } impl ControlBuilder { + /// Sets the name of the text input. + /// + /// This is used for the html element's "name" attribute. + /// In forms, the name attribute is the key that the data is sent + /// with. pub fn named(mut self, control_name: impl ToString) -> Self { self.data.name = control_name.to_string(); self } + /// Sets the label for the text input. + pub fn labeled(mut self, label: impl ToString) -> Self { + self.data.label = Some(label.to_string()); + self + } + + /// Sets the placeholder for the text input. pub fn placeholder(mut self, placeholder: impl ToString) -> Self { self.data.placeholder = Some(placeholder.to_string()); self } - pub fn label(mut self, label: impl ToString) -> Self { - self.data.label = Some(label.to_string()); - self - } - + /// Sets the intial_text for the text input. pub fn initial_text(mut self, text: impl ToString) -> Self { self.data.initial_text = text.to_string(); self } + /// Sets the text input to be the "password" type. pub fn password(mut self) -> Self { self.data.input_type = "password"; self } + + /// Sets the text input to be the "date" type. + pub fn date(mut self) -> Self { + self.data.input_type = "date"; + self + } + + /// Sets the text input to be the specified type. + pub fn input_type(mut self, input_type: &'static str) -> Self { + self.data.input_type = input_type; + self + } } diff --git a/src/form.rs b/src/form.rs index f9288b8..4e5ad0b 100644 --- a/src/form.rs +++ b/src/form.rs @@ -28,7 +28,7 @@ impl FormValidator { } } -/// A constructed form object. +/// A constructed, rendered form object. /// /// With this, you can render the form, get the form data, or get /// a validator for the data. @@ -48,7 +48,7 @@ impl Form { } } - /// Validates the [`FormToolData`], returning the result + /// Validates the [`FormToolData`], returning the result. pub fn validate(&self) -> Result<(), String> { let validator = self.validator(); validator.validate(&self.fd.get_untracked()) @@ -79,13 +79,21 @@ impl IntoView for Form { /// A trait allowing a form to be built around its containing data. /// -/// This trait defines a function that can be used to build all the data needed -/// to physically lay out a form, and how that data should be parsed and validated. +/// This trait defines a function that can be used to build all the data +/// needed to physically lay out a form, and how that data should be parsed +/// and validated. pub trait FormToolData: Clone + 'static { + /// The style that this form uses. type Style: FormStyle; + /// The context that this form is rendered in. + /// + /// This will need to be provided when building a form or a validator. + /// Therefore, you will need to be able to replicate this context + /// on the client for rendering and the server for validating. type Context: 'static; - /// Defines how the form should be layed out and how the data should be parsed and validated. + /// Defines how the form should be laid out and how the data should be + /// parsed and validated. /// /// To construct a [`From`] object, use one of the `get_form` methods. /// @@ -100,12 +108,13 @@ pub trait FormToolData: Clone + 'static { /// [`ActionForm`](leptos_router::ActionForm) that sends the form data /// directly by calling the server function. /// - /// By doing this, we avoid doing the [`FromFormData`](leptos_router::FromFormData) + /// By doing this, we avoid doing the + /// [`FromFormData`](leptos_router::FromFormData) /// conversion. However, to support /// [Progressive Enhancement](https://book.leptos.dev/progressive_enhancement/index.html), - /// you should name the form elements to work with a plain ActionForm anyway. - /// If progresssive enhancement is not important to you, you may freely - /// use this version + /// you should name the form elements to work with a plain ActionForm + /// anyway. If progresssive enhancement is not important to you, you may + /// freely use this version. /// /// For the other ways to construct a [`Form`], see: /// - [`get_action_form`](Self::get_action_form) diff --git a/src/form_builder.rs b/src/form_builder.rs index 4784dd0..d3440db 100644 --- a/src/form_builder.rs +++ b/src/form_builder.rs @@ -19,11 +19,6 @@ use web_sys::{FormData, SubmitEvent}; /// A builder for laying out forms. /// /// This builder allows you to specify what components should make up the form. -/// -/// ### 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. @@ -56,11 +51,13 @@ impl FormBuilder { } } + /// Adds a styling attribute to the entire form. pub fn style(mut self, style: ::StylingAttributes) -> Self { self.styles.push(style); self } + /// Adds a new vanity control to the form. pub(crate) fn new_vanity( mut self, builder: impl BuilderFn>, @@ -71,6 +68,7 @@ impl FormBuilder { self } + /// Adds a new vanity control to the form using the form's context. pub(crate) fn new_vanity_cx( mut self, builder: impl BuilderCxFn, FD::Context>, @@ -81,6 +79,7 @@ impl FormBuilder { self } + /// Adds a new control to the form using the form's context. pub(crate) fn new_control( mut self, builder: impl BuilderFn>, @@ -91,6 +90,7 @@ impl FormBuilder { self } + /// Adds a new control to the form using the form's context. pub(crate) fn new_control_cx( mut self, builder: impl BuilderCxFn, FD::Context>, @@ -101,8 +101,8 @@ impl FormBuilder { self } - // TODO: test this from a user context. A user adding a custom defined component. - pub fn add_vanity( + /// Adds a vanity control to the form. + pub(crate) fn add_vanity( &mut self, vanity_control: VanityControlBuilder, ) { @@ -135,14 +135,20 @@ impl FormBuilder { 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( + /// Adds a control to the form. + pub(crate) fn add_control( &mut self, control: ControlBuilder, ) { let built_control_data = match control.build() { Ok(c) => c, - Err(e) => panic!("Invalid Component: {}", e), + Err(e) => { + let item_name = std::any::type_name::() + .rsplit("::") + .next() + .expect("split to have at least 1 element"); + panic!("Invalid Component ({}): {}", item_name, e) + } }; if let Some(validation_fn) = built_control_data.validation_fn.clone() { @@ -174,6 +180,8 @@ impl FormBuilder { self.render_fns.push(Box::new(render_fn)); } + /// Helper for building all the functions and everything needed to render + /// the view. fn build_control_view( fd: RwSignal, fs: Rc, @@ -271,6 +279,7 @@ impl FormBuilder { (view, validation_cb) } + /// Helper for creating a setter function. fn create_value_setter( validation_cb: Box bool + 'static>, validation_signal_set: WriteSignal>, @@ -301,6 +310,7 @@ impl FormBuilder { Rc::new(value_setter) } + /// Builds the direct send version of the form. pub(crate) fn build_form( self, action: Action>>, @@ -357,6 +367,7 @@ impl FormBuilder { } } + /// Builds the action form version of the form. pub(crate) fn build_action_form( self, action: Action>>, @@ -407,6 +418,7 @@ impl FormBuilder { } } + /// builds the plain form version of the form. pub(crate) fn build_plain_form(self, url: String, fd: FD, fs: FD::Style) -> Form { let fd = create_rw_signal(fd); let fs = Rc::new(fs); @@ -447,6 +459,7 @@ impl FormBuilder { } } + /// Creates a [`FormValidator`] from this builder. pub(crate) fn validator(&self) -> FormValidator { FormValidator { validations: self.validations.clone(), diff --git a/src/lib.rs b/src/lib.rs index 5ba52d7..31cc95f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,8 @@ +//! `leptos_form_tool` offers a declaritve way to create forms for +//! [leptos](https://leptos.dev/). +//! +//! To learn more, see the +//! [README.md](https://github.com/MitchellMarinoDev/leptos_form_tool/src/branch/main/README.md) pub mod controls; mod form; mod form_builder; diff --git a/src/styles/grid_form.rs b/src/styles/grid_form.rs index 03f9c1c..6bb8f85 100644 --- a/src/styles/grid_form.rs +++ b/src/styles/grid_form.rs @@ -12,22 +12,129 @@ use leptos::*; use std::rc::Rc; use web_sys::MouseEvent; -pub enum GridFormStylingAttributes { +/// Styling attributes for the [`GridFormStyle`]. +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum GFStyleAttr { + /// Set the width of the control out of 12. + /// Defaults to 12/12 (full width). Width(u32), + /// Adds a tooltip to the control. + /// This sets the html title attribute, which shows the text when the + /// user hovers their mouse over the control for a couple seconds. + Tooltip(String), } +/// A complete useable example for defining a form style. +/// +/// This can be used directly in by your form, or you can copy `grid_form.rs` +/// into your project and make any neccesary change. You will also want to +/// copy `grid_form.scss` from the git repo and put that in the `styles` +/// directory for your leptos project to get all the styling. #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct GridFormStyle; impl FormStyle for GridFormStyle { - type StylingAttributes = GridFormStylingAttributes; + type StylingAttributes = GFStyleAttr; fn form_frame(&self, form: ControlRenderData) -> View { view! {
{form.data}
}.into_view() } + /// A common function that wraps the given view in the styles + fn custom_component(&self, styles: &[Self::StylingAttributes], inner: View) -> View { + let mut width = 12; + let mut tooltip = None; + for style in styles.iter() { + match style { + GFStyleAttr::Width(w) => width = *w, + GFStyleAttr::Tooltip(t) => tooltip = Some(t), + } + } + + view! { +
+ {inner} +
+ } + .into_view() + } + + fn group(&self, group: Rc>) -> View { + let view = view! { +
+ {&group.data} +
+ } + .into_view(); + + self.custom_component(&group.styles, view) + } + + fn spacer(&self, control: Rc>) -> View { + self.custom_component( + &control.styles, + view! {
}.into_view(), + ) + } + fn heading(&self, control: Rc>) -> View { - view! {

{&control.data.title}

}.into_view() + self.custom_component( + &control.styles, + view! {

{&control.data.title}

}.into_view(), + ) + } + + fn submit(&self, control: Rc>) -> View { + self.custom_component( + &control.styles, + view! { } + .into_view(), + ) + } + + fn button( + &self, + control: Rc>>, + data_signal: RwSignal, + ) -> View { + let action = control.data.action.clone(); + let on_click = move |ev: MouseEvent| { + if let Some(action) = action.clone() { + action(ev, data_signal) + } + }; + + let view = view! { + + } + .into_view(); + + self.custom_component(&control.styles, view) + } + + fn output( + &self, + control: Rc>, + value_getter: Option>, + ) -> View { + let view = view! { {move || value_getter.map(|g| g.get())} }.into_view(); + self.custom_component(&control.styles, view) + } + + fn hidden( + &self, + control: Rc>, + value_getter: Option>, + ) -> View { + let value_getter = move || value_getter.map(|g| g.get()); + view! { } + .into_view() } fn text_input( @@ -37,38 +144,120 @@ impl FormStyle for GridFormStyle { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { - // TODO: extract this to a common thing - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } + let view = view! { +
+ + {move || validation_state.get().err()} +
+ } + .into_view(); - view! { -
+ self.custom_component(&control.styles, view) + } + + fn text_area( + &self, + control: Rc>, + value_getter: Signal<::ReturnType>, + value_setter: Rc::ReturnType)>, + validation_state: Signal>, + ) -> View { + let view = view! { +
{move || validation_state.get().err()}
- + - class="form_input" - class=("form_input_invalid", move || validation_state.get().is_err()) - />
} - .into_view() + .into_view(); + + self.custom_component(&control.styles, view) + } + + fn radio_buttons( + &self, + control: Rc>, + value_getter: Signal<::ReturnType>, + value_setter: Rc::ReturnType)>, + validation_state: Signal>, + ) -> View { + let buttons_view = control + .data + .options + .iter() + .map(|(display, value)| { + let value_setter = value_setter.clone(); + let display = display.clone(); + let value = value.clone(); + let value_clone = value.clone(); + let value_clone2 = value.clone(); + view! { + + + +
+ } + }) + .collect_view(); + + let view = view! { +
+ + {move || validation_state.get().err()} +
+
+ {buttons_view} +
+ } + .into_view(); + + self.custom_component(&control.styles, view) } fn select( @@ -78,13 +267,6 @@ impl FormStyle for GridFormStyle { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - let options_view = control .data .options @@ -100,8 +282,7 @@ impl FormStyle for GridFormStyle { }) .collect_view(); - view! { -
+ let view = view! {
} - .into_view() - } + .into_view(); - fn submit(&self, control: Rc>) -> View { - view! { }.into_view() - } - - fn text_area( - &self, - control: Rc>, - value_getter: Signal<::ReturnType>, - value_setter: Rc::ReturnType)>, - validation_state: Signal>, - ) -> View { - view! { -
- {move || format!("{:?}", validation_state.get())} - - -
- } - .into_view() - } - - fn custom_component(&self, view: View) -> View { - view - } - - fn hidden( - &self, - control: Rc>, - value_getter: Option>, - ) -> View { - let value_getter = move || value_getter.map(|g| g.get()); - view! { } - .into_view() - } - - fn radio_buttons( - &self, - control: Rc>, - value_getter: Signal<::ReturnType>, - value_setter: Rc::ReturnType)>, - validation_state: Signal>, - ) -> View { - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - let value_setter = Rc::new(value_setter); - let buttons_view = control - .data - .options - .iter() - .map(|o| { - let value_setter = value_setter.clone(); - let o_clone1 = o.clone(); - let o_clone2 = o.clone(); - view! { - - - -
- } - }) - .collect_view(); - - view! { -
-
- - {move || validation_state.get().err()} -
-
- {buttons_view} -
-
- } - .into_view() + self.custom_component(&control.styles, view) } fn checkbox( @@ -239,15 +312,7 @@ impl FormStyle for GridFormStyle { value_getter: Signal<::ReturnType>, value_setter: Rc::ReturnType)>, ) -> View { - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - view! { -
+ let view = view! { @@ -265,9 +330,10 @@ impl FormStyle for GridFormStyle { {control.data.label.as_ref()} -
} - .into_view() + .into_view(); + + self.custom_component(&control.styles, view) } fn stepper( @@ -277,15 +343,7 @@ impl FormStyle for GridFormStyle { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - view! { -
+ let view = view! {
} - .into_view() - } + .into_view(); - fn output( - &self, - _control: Rc>, - value_getter: Option>, - ) -> View { - view! {
{move || value_getter.map(|g| g.get())}
}.into_view() + self.custom_component(&control.styles, view) } fn slider( @@ -324,15 +375,7 @@ impl FormStyle for GridFormStyle { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - view! { -
+ let view = view! {
} - .into_view() - } + .into_view(); - fn button( - &self, - control: Rc>>, - data_signal: RwSignal, - ) -> View { - let mut width = 1; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - let action = control.data.action.clone(); - let on_click = move |ev: MouseEvent| { - if let Some(action) = action.clone() { - action(ev, data_signal) - } - }; - - view! { - - } - .into_view() - } - - fn group(&self, group: Rc>) -> View { - let mut width = 12; - for style in group.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - view! { -
- {&group.data} -
- } - .into_view() - } - - fn spacer(&self, control: Rc>) -> View { - let mut width = 12; - for style in control.styles.iter() { - match style { - GridFormStylingAttributes::Width(w) => width = *w, - } - } - - view! {
} - .into_view() + self.custom_component(&control.styles, view) } } diff --git a/src/styles/mod.rs b/src/styles/mod.rs index 1f3ae07..45824da 100644 --- a/src/styles/mod.rs +++ b/src/styles/mod.rs @@ -1,7 +1,5 @@ mod grid_form; -use std::rc::Rc; - use crate::{ controls::{ button::ButtonData, checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData, @@ -12,10 +10,19 @@ use crate::{ FormToolData, }; use leptos::{RwSignal, Signal, View}; +use std::rc::Rc; -pub use grid_form::{GridFormStyle, GridFormStylingAttributes}; +pub use grid_form::{GFStyleAttr, GridFormStyle}; +/// Defines a way to style a form. +/// +/// Provides methods for rendering all the controls. +/// This provider is in charge of figuring out what html elements should be +/// rendered and how they should be styled. pub trait FormStyle: 'static { + /// The type of styling attributes that this [`FormStyle`] takes. + /// + /// These styling attributes can be applied to any of the controls. type StylingAttributes; /// Render any containing components for the form. @@ -26,12 +33,68 @@ pub trait FormStyle: 'static { /// Do NOT wrap it in an actual `form` element; any /// wrapping should be done with `div` or similar elements. fn form_frame(&self, form: ControlRenderData) -> View; + + /// Wraps the view of a custom component. + /// + /// The rendering of the custom component is given by the `inner` view. + /// Here the styler has a chance wrap the view with other components, or + /// applying the styling attributes. + /// + /// This method does not need to be called by the custom component, but + /// the custom component may make use of this method for the + /// aforementioned reasons. + fn custom_component(&self, style: &[Self::StylingAttributes], inner: View) -> View; + + /// Renders a group. + /// + /// The inner view for the group's components is provided. + /// This method should wrap the group in any visual grouping elements, + /// and apply the styles. + fn group(&self, group: Rc>) -> View; + + /// Renders a spacer. + /// + /// See [`SpacerData`]. + fn spacer(&self, control: Rc>) -> View; + + /// Renders a heading for a section of the form. fn heading(&self, control: Rc>) -> View; + + /// Renders a submit button. + /// + /// See [`SubmitData`]. + fn submit(&self, control: Rc>) -> View; + + /// Renders a button. + /// + /// See [`BuuttonData`] + fn button( + &self, + control: Rc>>, + data_signal: RwSignal, + ) -> View; + + /// Renders some output text. + /// + /// See [`OutputData`]. + fn output( + &self, + control: Rc>, + value_getter: Option>, + ) -> View; + + /// Renders a input control that should be hidden from the user. + /// + /// See [`HiddenData`]. fn hidden( &self, control: Rc>, value_getter: Option>, ) -> View; + + /// Renders a text input control. + /// + /// See [`TextInputData`]. fn text_input( &self, control: Rc>, @@ -39,6 +102,10 @@ pub trait FormStyle: 'static { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View; + + /// Renders a text area control. + /// + /// See [`TextAreaData`]. fn text_area( &self, control: Rc>, @@ -46,6 +113,10 @@ pub trait FormStyle: 'static { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View; + + /// Renders a group of radio buttons. + /// + /// See [`RadioButtonsData`]. fn radio_buttons( &self, control: Rc>, @@ -53,6 +124,10 @@ pub trait FormStyle: 'static { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View; + + /// Renders a select (or dropdown) control. + /// + /// See [`SelectData`]. fn select( &self, control: Rc>, @@ -60,17 +135,20 @@ pub trait FormStyle: 'static { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View; - fn button( - &self, - control: Rc>>, - data_signal: RwSignal, - ) -> View; + + /// Renders a checkbox control. + /// + /// See [`CheckboxData`]. fn checkbox( &self, control: Rc>, value_getter: Signal<::ReturnType>, value_setter: Rc::ReturnType)>, ) -> View; + + /// Renders a stepper control. + /// + /// See [`StepperData`]. fn stepper( &self, control: Rc>, @@ -78,11 +156,10 @@ pub trait FormStyle: 'static { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View; - fn output( - &self, - control: Rc>, - value_getter: Option>, - ) -> View; + + /// Renders a slider control. + /// + /// See [`SliderData`]. fn slider( &self, control: Rc>, @@ -90,8 +167,4 @@ pub trait FormStyle: 'static { value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View; - fn submit(&self, control: Rc>) -> View; - fn custom_component(&self, view: View) -> View; - fn group(&self, group: Rc>) -> View; - fn spacer(&self, control: Rc>) -> View; }