From da84bdbb27d34f81611ac28af7dfd44e3a1846cc Mon Sep 17 00:00:00 2001 From: Mitchell M Date: Tue, 18 Jun 2024 12:57:56 -0500 Subject: [PATCH] Conditional Rendering WORKS! --- src/controls/button.rs | 37 +++++++++- src/controls/checkbox.rs | 5 +- src/controls/group.rs | 13 ++-- src/controls/heading.rs | 3 +- src/controls/hidden.rs | 3 +- src/controls/mod.rs | 14 ++-- src/controls/output.rs | 3 +- src/controls/radio_buttons.rs | 5 +- src/controls/select.rs | 5 +- src/controls/slider.rs | 6 +- src/controls/spacer.rs | 3 +- src/controls/stepper.rs | 6 +- src/controls/submit.rs | 3 +- src/controls/text_area.rs | 5 +- src/controls/text_input.rs | 6 +- src/form_builder.rs | 48 +++++++------ src/styles/grid_form.rs | 125 +++++++++++++++++----------------- src/styles/mod.rs | 44 ++++++------ 18 files changed, 194 insertions(+), 140 deletions(-) diff --git a/src/controls/button.rs b/src/controls/button.rs index 14bdd99..002eefd 100644 --- a/src/controls/button.rs +++ b/src/controls/button.rs @@ -1,6 +1,9 @@ -use super::{BuilderFn, ControlRenderData}; +use super::{BuilderFn, ControlRenderData, ShowWhenFn}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; +use leptos::view; use leptos::RwSignal; +use leptos::Show; +use leptos::Signal; use std::rc::Rc; use web_sys::MouseEvent; @@ -36,9 +39,24 @@ impl FormBuilder { data: control.data, styles: control.styles, }; + let show_when = control.show_when; - let render_fn = move |fs: &FD::Style, fd: RwSignal| { - let view = fs.button(render_data, fd); + let cx = self.cx.clone(); + let render_fn = move |fs: Rc, fd: RwSignal| { + let render_data = Rc::new(render_data); + // let cloned_fs = fs.clone(); + let view = move || fs.clone().button(render_data.clone(), fd); + let view = match show_when { + Some(when) => { + let when = move || when(fd.into(), cx.clone()); + view! { + + {view.clone()} + + } + } + None => view(), + }; (view, None) }; self.render_fns.push(Box::new(render_fn)); @@ -50,6 +68,7 @@ impl FormBuilder { pub struct ButtonBuilder { pub(crate) styles: Vec<::StylingAttributes>, pub(crate) data: ButtonData, + pub(crate) show_when: Option>>, } impl ButtonBuilder { @@ -57,9 +76,21 @@ impl ButtonBuilder { ButtonBuilder { styles: Vec::default(), data: ButtonData::default(), + show_when: None, } } + /// 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 + } + pub fn style(mut self, style: ::StylingAttributes) -> Self { self.styles.push(style); self diff --git a/src/controls/checkbox.rs b/src/controls/checkbox.rs index f22a433..8e97e69 100644 --- a/src/controls/checkbox.rs +++ b/src/controls/checkbox.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct CheckboxData { @@ -13,9 +14,9 @@ impl ControlData for CheckboxData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, _validation_state: Signal>, ) -> View { fs.checkbox(control, value_getter, value_setter) diff --git a/src/controls/group.rs b/src/controls/group.rs index cced99c..35298fa 100644 --- a/src/controls/group.rs +++ b/src/controls/group.rs @@ -1,4 +1,6 @@ -use super::ValidationCb; +use std::rc::Rc; + +use super::{ControlRenderData, ValidationCb}; use crate::styles::FormStyle; use crate::{form::FormToolData, form_builder::FormBuilder}; use leptos::{CollectView, RwSignal}; @@ -15,17 +17,20 @@ impl FormBuilder { self.validations.push(validation); } - let render_fn = move |fs: &FD::Style, fd: RwSignal| { + let render_fn = move |fs: Rc, fd: RwSignal| { let (views, validation_cbs): (Vec<_>, Vec<_>) = group_builder .render_fns .into_iter() - .map(|r_fn| r_fn(fs, fd)) + .map(|r_fn| r_fn(fs.clone(), fd)) .unzip(); - let view = fs.group(super::ControlRenderData { + let render_data = Rc::new(ControlRenderData { data: views.collect_view(), styles: group_builder.styles, }); + + let view = fs.group(render_data.clone()); + let validation_cb = move || { let mut success = true; for validation in validation_cbs.iter().flatten() { diff --git a/src/controls/heading.rs b/src/controls/heading.rs index bc0a7fa..680cf61 100644 --- a/src/controls/heading.rs +++ b/src/controls/heading.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::View; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct HeadingData { @@ -10,7 +11,7 @@ pub struct HeadingData { impl VanityControlData for HeadingData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, _value_getter: Option>, ) -> View { fs.heading(control) diff --git a/src/controls/hidden.rs b/src/controls/hidden.rs index babee60..ec774f2 100644 --- a/src/controls/hidden.rs +++ b/src/controls/hidden.rs @@ -3,6 +3,7 @@ use super::{ }; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct HiddenData { @@ -12,7 +13,7 @@ pub struct HiddenData { impl VanityControlData for HiddenData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Option>, ) -> View { fs.hidden(control, value_getter) diff --git a/src/controls/mod.rs b/src/controls/mod.rs index 47b2b25..9a0cc43 100644 --- a/src/controls/mod.rs +++ b/src/controls/mod.rs @@ -26,8 +26,8 @@ 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(&FS, RwSignal) -> (View, Option>) + 'static +pub trait RenderFn: + FnOnce(Rc, RwSignal) -> (View, Option>) + 'static { } @@ -40,8 +40,8 @@ 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(&FS, RwSignal) -> (View, Option>) + 'static +impl RenderFn for F where + F: FnOnce(Rc, RwSignal) -> (View, Option>) + 'static { } @@ -50,7 +50,7 @@ pub trait VanityControlData: 'static { /// Builds the control, returning the [`View`] that was built. fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Option>, ) -> View; } @@ -64,9 +64,9 @@ pub trait ControlData: 'static { /// Builds the control, returning the [`View`] that was built. fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View; } diff --git a/src/controls/output.rs b/src/controls/output.rs index 2e734c9..7962e16 100644 --- a/src/controls/output.rs +++ b/src/controls/output.rs @@ -3,6 +3,7 @@ use super::{ }; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct OutputData; @@ -10,7 +11,7 @@ pub struct OutputData; impl VanityControlData for OutputData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Option>, ) -> View { fs.output(control, value_getter) diff --git a/src/controls/radio_buttons.rs b/src/controls/radio_buttons.rs index b2a6c5d..fcc7d05 100644 --- a/src/controls/radio_buttons.rs +++ b/src/controls/radio_buttons.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct RadioButtonsData { @@ -14,9 +15,9 @@ impl ControlData for RadioButtonsData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View { fs.radio_buttons(control, value_getter, value_setter, validation_state) diff --git a/src/controls/select.rs b/src/controls/select.rs index 7a20b49..43e437c 100644 --- a/src/controls/select.rs +++ b/src/controls/select.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SelectData { @@ -17,9 +18,9 @@ impl ControlData for SelectData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View { fs.select(control, value_getter, value_setter, validation_state) diff --git a/src/controls/slider.rs b/src/controls/slider.rs index 24f8d8b..4a79b57 100644 --- a/src/controls/slider.rs +++ b/src/controls/slider.rs @@ -1,7 +1,7 @@ use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; -use std::ops::RangeInclusive; +use std::{ops::RangeInclusive, rc::Rc}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SliderData { @@ -27,9 +27,9 @@ impl ControlData for SliderData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View { fs.slider(control, value_getter, value_setter, validation_state) diff --git a/src/controls/spacer.rs b/src/controls/spacer.rs index b6bcafa..b5b800d 100644 --- a/src/controls/spacer.rs +++ b/src/controls/spacer.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{prelude::Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SpacerData { @@ -10,7 +11,7 @@ pub struct SpacerData { impl VanityControlData for SpacerData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, _value_getter: Option>, ) -> View { fs.spacer(control) diff --git a/src/controls/stepper.rs b/src/controls/stepper.rs index 975b8c0..401bd5a 100644 --- a/src/controls/stepper.rs +++ b/src/controls/stepper.rs @@ -1,7 +1,7 @@ use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; -use std::ops::RangeInclusive; +use std::{ops::RangeInclusive, rc::Rc}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct StepperData { @@ -17,9 +17,9 @@ impl ControlData for StepperData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View { fs.stepper(control, value_getter, value_setter, validation_state) diff --git a/src/controls/submit.rs b/src/controls/submit.rs index 8907894..f429de8 100644 --- a/src/controls/submit.rs +++ b/src/controls/submit.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlRenderData, VanityControlBuilder, VanityControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{prelude::Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct SubmitData { @@ -10,7 +11,7 @@ pub struct SubmitData { impl VanityControlData for SubmitData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, _value_getter: Option>, ) -> View { fs.submit(control) diff --git a/src/controls/text_area.rs b/src/controls/text_area.rs index 064bd64..34784ec 100644 --- a/src/controls/text_area.rs +++ b/src/controls/text_area.rs @@ -1,6 +1,7 @@ use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; +use std::rc::Rc; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct TextAreaData { @@ -13,9 +14,9 @@ impl ControlData for TextAreaData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View { fs.text_area(control, value_getter, value_setter, validation_state) diff --git a/src/controls/text_input.rs b/src/controls/text_input.rs index 0c38a64..ea8ae34 100644 --- a/src/controls/text_input.rs +++ b/src/controls/text_input.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use super::{BuilderFn, ControlBuilder, ControlData, ControlRenderData, ValidatedControlData}; use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle}; use leptos::{Signal, View}; @@ -28,9 +30,9 @@ impl ControlData for TextInputData { fn build_control( fs: &FS, - control: ControlRenderData, + control: Rc>, value_getter: Signal, - value_setter: Box, + value_setter: Rc, validation_state: Signal>, ) -> View { fs.text_input(control, value_getter, value_setter, validation_state) diff --git a/src/form_builder.rs b/src/form_builder.rs index db1c3a0..6dd8108 100644 --- a/src/form_builder.rs +++ b/src/form_builder.rs @@ -93,19 +93,21 @@ impl FormBuilder { } = vanity_control.build(); let cx = self.cx.clone(); - let render_fn = move |fs: &FD::Style, fd: RwSignal| { + let render_fn = move |fs: Rc, fd: RwSignal| { + let render_data = Rc::new(render_data); let value_getter = getter.map(|getter| (move || getter(fd.get())).into_signal()); - let view = VanityControlData::build_control(fs, render_data, value_getter); + let view = + move || VanityControlData::build_control(&*fs, render_data.clone(), value_getter); let view = match show_when { Some(when) => { let when = move || when(fd.into(), cx.clone()); view! { - {&view} + {view.clone()} } } - None => view, + None => view(), }; (view, None) }; @@ -128,7 +130,7 @@ impl FormBuilder { } let cx = self.cx.clone(); - let render_fn = move |fs: &FD::Style, fd: RwSignal| { + let render_fn = move |fs: Rc, fd: RwSignal| { let (view, cb) = Self::build_control_view(fd, fs, built_control_data, cx); (view, Some(cb)) }; @@ -138,7 +140,7 @@ impl FormBuilder { fn build_control_view( fd: RwSignal, - fs: &FD::Style, + fs: Rc, control_data: BuiltControlData, cx: Rc, ) -> (View, Box) { @@ -152,6 +154,7 @@ impl FormBuilder { show_when, } = control_data; + let render_data = Rc::new(render_data); let (validation_signal, validation_signal_set) = create_signal(Ok(())); let validation_fn_clone = validation_fn.clone(); let value_getter = move || { @@ -194,23 +197,25 @@ impl FormBuilder { fd, ); - let view = C::build_control( - fs, - render_data, - value_getter, - value_setter, - validation_signal.into(), - ); + let view = move || { + C::build_control( + &*fs, + render_data.clone(), + value_getter, + value_setter.clone(), + validation_signal.into(), + ) + }; let view = match show_when { Some(when) => { let when = move || when(fd.into(), cx.clone()); view! { - {&view} + {view.clone()} } } - None => view, + None => view(), }; (view, validation_cb) } @@ -221,7 +226,7 @@ impl FormBuilder { parse_fn: Box>, setter: Rc>, fd: RwSignal, - ) -> Box { + ) -> Rc { let value_setter = move |value| { let parsed = match parse_fn(value) { Ok(p) => { @@ -242,7 +247,7 @@ impl FormBuilder { // run validation (validation_cb)(); }; - Box::new(value_setter) + Rc::new(value_setter) } // TODO: impl build_formless function that builds the form without adding @@ -260,11 +265,12 @@ impl FormBuilder { From, { let fd = create_rw_signal(fd); + let fs = Rc::new(fs); let (views, validation_cbs): (Vec<_>, Vec<_>) = self .render_fns .into_iter() - .map(|r_fn| r_fn(&fs, fd)) + .map(|r_fn| r_fn(fs.clone(), fd)) .unzip(); let elements = fs.form_frame(ControlRenderData { @@ -310,11 +316,12 @@ impl FormBuilder { From, { let fd = create_rw_signal(fd); + let fs = Rc::new(fs); let (views, validation_cbs): (Vec<_>, Vec<_>) = self .render_fns .into_iter() - .map(|r_fn| r_fn(&fs, fd)) + .map(|r_fn| r_fn(fs.clone(), fd)) .unzip(); let elements = fs.form_frame(ControlRenderData { @@ -349,11 +356,12 @@ impl FormBuilder { 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); let (views, validation_cbs): (Vec<_>, Vec<_>) = self .render_fns .into_iter() - .map(|r_fn| r_fn(&fs, fd)) + .map(|r_fn| r_fn(fs.clone(), fd)) .unzip(); let elements = fs.form_frame(ControlRenderData { diff --git a/src/styles/grid_form.rs b/src/styles/grid_form.rs index fef04c6..03f9c1c 100644 --- a/src/styles/grid_form.rs +++ b/src/styles/grid_form.rs @@ -2,7 +2,8 @@ use super::FormStyle; use crate::{ controls::{ button::ButtonData, checkbox::CheckboxData, heading::HeadingData, hidden::HiddenData, - output::OutputData, select::SelectData, spacer::SpacerData, submit::SubmitData, + output::OutputData, radio_buttons::RadioButtonsData, select::SelectData, + slider::SliderData, spacer::SpacerData, stepper::StepperData, submit::SubmitData, text_area::TextAreaData, text_input::TextInputData, ControlData, ControlRenderData, }, FormToolData, @@ -25,22 +26,22 @@ impl FormStyle for GridFormStyle { view! {
{form.data}
}.into_view() } - fn heading(&self, control: ControlRenderData) -> View { + fn heading(&self, control: Rc>) -> View { view! {

{&control.data.title}

}.into_view() } fn text_input( &self, - control: ControlRenderData, + control: Rc>, value_getter: Signal<::ReturnType>, - value_setter: Box::ReturnType)>, + value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { // TODO: extract this to a common thing let mut width = 1; - for style in control.styles { + for style in control.styles.iter() { match style { - GridFormStylingAttributes::Width(w) => width = w, + GridFormStylingAttributes::Width(w) => width = *w, } } @@ -55,8 +56,8 @@ impl FormStyle for GridFormStyle { , + control: Rc>, value_getter: Signal<::ReturnType>, - value_setter: Box::ReturnType)>, + value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { let mut width = 1; - for style in control.styles { + for style in control.styles.iter() { match style { - GridFormStylingAttributes::Width(w) => width = w, + GridFormStylingAttributes::Width(w) => width = *w, } } let options_view = control .data .options - .into_iter() + .iter() .map(|(display, value)| { + let display = display.clone(); + let value = value.clone(); view! { } }) @@ -107,7 +110,7 @@ impl FormStyle for GridFormStyle { }.into_view() + fn submit(&self, control: Rc>) -> View { + view! { }.into_view() } fn text_area( &self, - control: ControlRenderData, + control: Rc>, value_getter: Signal<::ReturnType>, - value_setter: Box::ReturnType)>, + value_setter: Rc::ReturnType)>, validation_state: Signal>, ) -> View { view! { @@ -137,8 +140,8 @@ impl FormStyle for GridFormStyle { {move || format!("{:?}", validation_state.get())}