generated from mitchell/rust_template
before removing VanityControl and Control
This commit is contained in:
parent
fbe746702a
commit
80a967346c
@ -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> {
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/form.rs
38
src/form.rs
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user