before removing VanityControl and Control

This commit is contained in:
Mitchell Marino 2024-03-17 14:25:06 -05:00
parent fbe746702a
commit 80a967346c
7 changed files with 82 additions and 26 deletions

View File

@ -1,5 +1,5 @@
use crate::{form::FormData, styles::FormStyle}; use crate::{form::FormData, styles::FormStyle};
use leptos::View; use leptos::{Signal, View};
pub mod heading; pub mod heading;
pub mod select; pub mod select;
@ -18,10 +18,13 @@ pub trait VanityControlData: 'static {
} }
pub trait ControlData: 'static { pub trait ControlData: 'static {
type ReturnType; type ReturnType: Clone;
// TODO: this should also return a getter for the data // TODO: this should also return a getter for the data
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View; fn build_control<FD: FormData, FS: FormStyle>(
fs: &FS,
control: Control<FD, FS, Self>,
) -> (View, Signal<Self::ReturnType>);
} }
pub struct VanityControl<FS: FormStyle + ?Sized, C: VanityControlData + ?Sized> { pub struct VanityControl<FS: FormStyle + ?Sized, C: VanityControlData + ?Sized> {

View File

@ -1,4 +1,4 @@
use leptos::View; use leptos::{Signal, View};
use super::{Control, ControlBuilder, ControlData}; use super::{Control, ControlBuilder, ControlData};
use crate::{ use crate::{
@ -15,7 +15,10 @@ pub struct SelectData {
impl ControlData for SelectData { impl ControlData for SelectData {
type ReturnType = String; type ReturnType = String;
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View { fn build_control<FD: FormData, FS: FormStyle>(
fs: &FS,
control: Control<FD, FS, Self>,
) -> (View, Signal<Self::ReturnType>) {
fs.select(control) fs.select(control)
} }
} }

View File

@ -1,10 +1,9 @@
use leptos::View;
use super::{Control, ControlBuilder, ControlData}; use super::{Control, ControlBuilder, ControlData};
use crate::{ use crate::{
form::{FormBuilder, FormData}, form::{FormBuilder, FormData},
styles::FormStyle, styles::FormStyle,
}; };
use leptos::{Signal, View};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct TextAreaData { pub struct TextAreaData {
@ -15,7 +14,10 @@ pub struct TextAreaData {
impl ControlData for TextAreaData { impl ControlData for TextAreaData {
type ReturnType = String; type ReturnType = String;
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View { fn build_control<FD: FormData, FS: FormStyle>(
fs: &FS,
control: Control<FD, FS, Self>,
) -> (View, Signal<Self::ReturnType>) {
fs.text_area(control) fs.text_area(control)
} }
} }

View File

@ -1,4 +1,4 @@
use leptos::View; use leptos::{Signal, View};
use super::{Control, ControlBuilder, ControlData}; use super::{Control, ControlBuilder, ControlData};
use crate::{ use crate::{
@ -30,7 +30,10 @@ impl Default for TextInputData {
impl ControlData for TextInputData { impl ControlData for TextInputData {
type ReturnType = String; type ReturnType = String;
fn build_control<FD: FormData, FS: FormStyle>(fs: &FS, control: Control<FD, FS, Self>) -> View { fn build_control<FD: FormData, FS: FormStyle>(
fs: &FS,
control: Control<FD, FS, Self>,
) -> (View, Signal<Self::ReturnType>) {
fs.text_input(control) fs.text_input(control)
} }
} }

View File

@ -5,11 +5,16 @@ use crate::{
}, },
styles::FormStyle, styles::FormStyle,
}; };
use leptos::{IntoView, View}; use leptos::{
use std::marker::PhantomData; create_effect, create_signal, IntoView, ReadSignal, SignalGet, SignalSet, SignalUpdate, View,
WriteSignal,
};
pub struct Form {}
pub struct FormBuilder<FD: FormData, FS: FormStyle> { pub struct FormBuilder<FD: FormData, FS: FormStyle> {
_fd: PhantomData<FD>, fd_get: ReadSignal<FD>,
fd_set: WriteSignal<FD>,
fs: FS, fs: FS,
validations: Vec<Box<ValidationFn<FD>>>, validations: Vec<Box<ValidationFn<FD>>>,
views: Vec<View>, views: Vec<View>,
@ -17,8 +22,10 @@ pub struct FormBuilder<FD: FormData, FS: FormStyle> {
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> { impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn new(form_style: FS) -> FormBuilder<FD, FS> { pub fn new(form_style: FS) -> FormBuilder<FD, FS> {
let (fs_get, fs_set) = create_signal(FD::default());
FormBuilder { FormBuilder {
_fd: PhantomData::default(), fd_get,
fd_set,
fs: form_style, fs: form_style,
validations: Vec::new(), validations: Vec::new(),
views: Vec::new(), views: Vec::new(),
@ -51,7 +58,26 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
} }
fn add_control<C: ControlData>(&mut self, control: Control<FD, FS, C>) { fn add_control<C: ControlData>(&mut self, control: Control<FD, FS, C>) {
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); self.views.push(view);
} }
@ -62,7 +88,7 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
} }
} }
pub trait FormData: Default { pub trait FormData: Default + Clone + 'static {
// TODO: this should return a Form Object // TODO: this should return a Form Object
fn create_form() -> View; fn create_form() -> View;
} }

View File

@ -5,21 +5,32 @@ pub use tw_grid::{TailwindGridFormStyle, TailwindGridStylingAttributes};
use crate::{ use crate::{
controls::{ controls::{
heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData, heading::HeadingData, select::SelectData, submit::SubmitData, text_area::TextAreaData,
text_input::TextInputData, Control, VanityControl, text_input::TextInputData, Control, ControlData, VanityControl,
}, },
form::FormData, form::FormData,
}; };
use leptos::View; use leptos::{Signal, View};
pub trait FormStyle: 'static { pub trait FormStyle: 'static {
type StylingAttributes; type StylingAttributes;
// TODO: add form frame // 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<Self, HeadingData>) -> View; fn heading(&self, control: VanityControl<Self, HeadingData>) -> View;
fn text_input<FD: FormData>(&self, control: Control<FD, Self, TextInputData>) -> View; fn text_input<FD: FormData>(
fn select<FD: FormData>(&self, control: Control<FD, Self, SelectData>) -> View; &self,
control: Control<FD, Self, TextInputData>,
) -> (View, Signal<<TextInputData as ControlData>::ReturnType>);
fn select<FD: FormData>(
&self,
control: Control<FD, Self, SelectData>,
) -> (View, Signal<<SelectData as ControlData>::ReturnType>);
fn submit(&self, control: VanityControl<Self, SubmitData>) -> View; fn submit(&self, control: VanityControl<Self, SubmitData>) -> View;
fn text_area<FD: FormData>(&self, control: Control<FD, Self, TextAreaData>) -> View; fn text_area<FD: FormData>(
&self,
control: Control<FD, Self, TextAreaData>,
) -> (View, Signal<<TextAreaData as ControlData>::ReturnType>);
fn custom_component(&self, view: View) -> View; fn custom_component(&self, view: View) -> View;
// TODO: add group // TODO: add group
} }

View File

@ -6,8 +6,7 @@ use crate::{
}, },
form::FormData, form::FormData,
}; };
use leptos::CollectView; use leptos::*;
use leptos::{view, IntoView, View};
pub enum TailwindGridStylingAttributes { pub enum TailwindGridStylingAttributes {
Width(u32), Width(u32),
@ -27,8 +26,13 @@ impl FormStyle for TailwindGridFormStyle {
.into_view() .into_view()
} }
fn text_input<FD: FormData>(&self, control: Control<FD, Self, TextInputData>) -> View { fn text_input<FD: FormData>(
view! { &self,
control: Control<FD, Self, TextInputData>,
) -> (View, ReadSignal<String>) {
let (read, write) = create_signal(String::new());
let view = view! {
<div> <div>
<label <label
for={&control.data.name} for={&control.data.name}
@ -43,10 +47,14 @@ impl FormStyle for TailwindGridFormStyle {
name=control.data.name name=control.data.name
placeholder=control.data.placeholder placeholder=control.data.placeholder
value=control.data.initial_text value=control.data.initial_text
on:change=move |ev| {
write.set(event_target_value(&ev));
}
class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400" class="block w-full bg-gray-100 border-2 border-gray-300 text-gray-700 py-2 px-4 rounded-lg leading-tight focus:outline-none focus:bg-white focus:border-sky-400"
/> />
</div> </div>
}.into_view() }.into_view();
(view, read)
} }
fn select<FD: FormData>(&self, control: Control<FD, Self, SelectData>) -> View { fn select<FD: FormData>(&self, control: Control<FD, Self, SelectData>) -> View {