diff --git a/src/controls/mod.rs b/src/controls/mod.rs index 9a768e9..e748ee0 100644 --- a/src/controls/mod.rs +++ b/src/controls/mod.rs @@ -1,5 +1,5 @@ use crate::{form::FormData, styles::FormStyle}; -use leptos::View; +use leptos::{Signal, View}; pub mod heading; pub mod select; @@ -18,10 +18,13 @@ pub trait VanityControlData: 'static { } pub trait ControlData: 'static { - type ReturnType; + type ReturnType: Clone; // TODO: this should also return a getter for the data - fn build_control(fs: &FS, control: Control) -> View; + fn build_control( + fs: &FS, + control: Control, + ) -> (View, Signal); } pub struct VanityControl { diff --git a/src/controls/select.rs b/src/controls/select.rs index 944d331..b7331d8 100644 --- a/src/controls/select.rs +++ b/src/controls/select.rs @@ -1,4 +1,4 @@ -use leptos::View; +use leptos::{Signal, View}; use super::{Control, ControlBuilder, ControlData}; use crate::{ @@ -15,7 +15,10 @@ pub struct SelectData { impl ControlData for SelectData { type ReturnType = String; - fn build_control(fs: &FS, control: Control) -> View { + fn build_control( + fs: &FS, + control: Control, + ) -> (View, Signal) { fs.select(control) } } diff --git a/src/controls/text_area.rs b/src/controls/text_area.rs index b0f8baa..51690c9 100644 --- a/src/controls/text_area.rs +++ b/src/controls/text_area.rs @@ -1,10 +1,9 @@ -use leptos::View; - use super::{Control, ControlBuilder, ControlData}; use crate::{ form::{FormBuilder, FormData}, styles::FormStyle, }; +use leptos::{Signal, View}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct TextAreaData { @@ -15,7 +14,10 @@ pub struct TextAreaData { impl ControlData for TextAreaData { type ReturnType = String; - fn build_control(fs: &FS, control: Control) -> View { + fn build_control( + fs: &FS, + control: Control, + ) -> (View, Signal) { fs.text_area(control) } } diff --git a/src/controls/text_input.rs b/src/controls/text_input.rs index 36352b3..1483123 100644 --- a/src/controls/text_input.rs +++ b/src/controls/text_input.rs @@ -1,4 +1,4 @@ -use leptos::View; +use leptos::{Signal, View}; use super::{Control, ControlBuilder, ControlData}; use crate::{ @@ -30,7 +30,10 @@ impl Default for TextInputData { impl ControlData for TextInputData { type ReturnType = String; - fn build_control(fs: &FS, control: Control) -> View { + fn build_control( + fs: &FS, + control: Control, + ) -> (View, Signal) { fs.text_input(control) } } diff --git a/src/form.rs b/src/form.rs index d3cfc79..facf581 100644 --- a/src/form.rs +++ b/src/form.rs @@ -5,11 +5,16 @@ use crate::{ }, styles::FormStyle, }; -use leptos::{IntoView, View}; -use std::marker::PhantomData; +use leptos::{ + create_effect, create_signal, IntoView, ReadSignal, SignalGet, SignalSet, SignalUpdate, View, + WriteSignal, +}; + +pub struct Form {} pub struct FormBuilder { - _fd: PhantomData, + fd_get: ReadSignal, + fd_set: WriteSignal, fs: FS, validations: Vec>>, views: Vec, @@ -17,8 +22,10 @@ pub struct FormBuilder { impl FormBuilder { pub fn new(form_style: FS) -> FormBuilder { + let (fs_get, fs_set) = create_signal(FD::default()); FormBuilder { - _fd: PhantomData::default(), + fd_get, + fd_set, fs: form_style, validations: Vec::new(), views: Vec::new(), @@ -51,7 +58,26 @@ impl FormBuilder { } fn add_control(&mut self, control: Control) { - let view = ControlData::build_control(&self.fs, control); + let (validation_signal, validation_signal_set) = create_signal(Ok(())); + let (view, control_value) = ControlData::build_control(&self.fs, control); + // TODO: add a signal that triggers on submit to refresh the validation on_submit + // TODO: we might want a way to see if this is the first time this ran, which would + // prevent the form's validation to pop up before the user typed anything in + // TODO: add validation here that run on the input changing, and writes + // it to the fd signals + create_effect(move |last_value| { + let control_value = control_value.get(); + let mut validation_result; + self.fd_set.update(|v| { + validation_result = + (control.parse_fn)(control_value, v).and_then(|_| (*control.validation)(v)); + }); + // TODO: or this happened on a submit + if Some(validation_result) != last_value { + validation_signal_set.set(validation_result); + } + validation_result + }); self.views.push(view); } @@ -62,7 +88,7 @@ impl FormBuilder { } } -pub trait FormData: Default { +pub trait FormData: Default + Clone + 'static { // TODO: this should return a Form Object fn create_form() -> View; } diff --git a/src/styles/mod.rs b/src/styles/mod.rs index 64774ad..74280dd 100644 --- a/src/styles/mod.rs +++ b/src/styles/mod.rs @@ -5,21 +5,32 @@ pub use tw_grid::{TailwindGridFormStyle, TailwindGridStylingAttributes}; use crate::{ controls::{ heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData, - text_input::TextInputData, Control, VanityControl, + text_input::TextInputData, Control, ControlData, VanityControl, }, form::FormData, }; -use leptos::View; +use leptos::{Signal, View}; pub trait FormStyle: 'static { type StylingAttributes; // TODO: add form frame + // TODO: perhaps we don't want to send the full control type anymore. + // as the rendering shouldn't depend on parse or validate anymore. fn heading(&self, control: VanityControl) -> View; - fn text_input(&self, control: Control) -> View; - fn select(&self, control: Control) -> View; + fn text_input( + &self, + control: Control, + ) -> (View, Signal<::ReturnType>); + fn select( + &self, + control: Control, + ) -> (View, Signal<::ReturnType>); fn submit(&self, control: VanityControl) -> View; - fn text_area(&self, control: Control) -> View; + fn text_area( + &self, + control: Control, + ) -> (View, Signal<::ReturnType>); fn custom_component(&self, view: View) -> View; // TODO: add group } diff --git a/src/styles/tw_grid.rs b/src/styles/tw_grid.rs index aae77e5..1afd4fa 100644 --- a/src/styles/tw_grid.rs +++ b/src/styles/tw_grid.rs @@ -6,8 +6,7 @@ use crate::{ }, form::FormData, }; -use leptos::CollectView; -use leptos::{view, IntoView, View}; +use leptos::*; pub enum TailwindGridStylingAttributes { Width(u32), @@ -27,8 +26,13 @@ impl FormStyle for TailwindGridFormStyle { .into_view() } - fn text_input(&self, control: Control) -> View { - view! { + fn text_input( + &self, + control: Control, + ) -> (View, ReadSignal) { + let (read, write) = create_signal(String::new()); + + let view = view! {
- }.into_view() + }.into_view(); + (view, read) } fn select(&self, control: Control) -> View {