This commit is contained in:
Mitchell Marino 2024-05-20 09:51:01 -05:00
parent 68d6b857ed
commit 74407d6c0a
5 changed files with 72 additions and 42 deletions

View File

@ -90,24 +90,24 @@
// column widths // column widths
.w-full { .col-span-full {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.w-12 { .col-span-12 {
grid-column: span 12 / span 12; grid-column: span 12 / span 12;
} }
.w-6 { .col-span-6 {
grid-column: span 6 / span 6; grid-column: span 6 / span 6;
} }
.w-4 { .col-span-4 {
grid-column: span 4 / span 4; grid-column: span 4 / span 4;
} }
.w-3 { .col-span-3 {
grid-column: span 3 / span 3; grid-column: span 3 / span 3;
} }
.w-2 { .col-span-2 {
grid-column: span 2 / span 2; grid-column: span 2 / span 2;
} }
.w-1 { .col-span-1 {
grid-column: span 1 / span 1; grid-column: span 1 / span 1;
} }

View File

@ -53,6 +53,11 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
} }
impl<FD: FormData, FS: FormStyle, FDT> ControlBuilder<FD, FS, TextInputData, FDT> { impl<FD: FormData, FS: FormStyle, FDT> ControlBuilder<FD, FS, TextInputData, FDT> {
pub fn named(mut self, control_name: impl ToString) -> Self {
self.data.name = control_name.to_string();
self
}
pub fn placeholder(mut self, placeholder: impl ToString) -> Self { pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
self.data.placeholder = Some(placeholder.to_string()); self.data.placeholder = Some(placeholder.to_string());
self self

View File

@ -7,10 +7,8 @@ use crate::{
}, },
styles::FormStyle, styles::FormStyle,
}; };
use leptos::{ use leptos::*;
create_rw_signal, create_signal, IntoSignal, IntoView, RwSignal, SignalGet, SignalSet, use leptos_router::Form;
SignalUpdate, View,
};
pub struct Validator<FD: FormData> { pub struct Validator<FD: FormData> {
validations: Vec<Rc<dyn ValidationFn<FD>>>, validations: Vec<Rc<dyn ValidationFn<FD>>>,
@ -102,20 +100,8 @@ pub struct FormBuilder<FD: FormData, FS: FormStyle> {
} }
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> { impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
// TODO: remove the Default trait bound and bind it to this function only // TODO: remove the Default trait bound
fn new_full_builder(form_style: FS) -> FormBuilder<FD, FS> { fn new_full_builder(starting_data: FD, form_style: FS) -> FormBuilder<FD, FS> {
let fd = create_rw_signal(FD::default());
FormBuilder {
inner: FormBuilderInner::FullBuilder(FullFormBuilder {
fd,
fs: form_style,
validations: Vec::new(),
views: Vec::new(),
}),
}
}
fn new_full_builder_with(starting_data: FD, form_style: FS) -> FormBuilder<FD, FS> {
let fd = create_rw_signal(starting_data); let fd = create_rw_signal(starting_data);
FormBuilder { FormBuilder {
inner: FormBuilderInner::FullBuilder(FullFormBuilder { inner: FormBuilderInner::FullBuilder(FullFormBuilder {
@ -267,12 +253,38 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
) )
} }
fn build(self) -> Option<Form<FD>> { fn build(
self,
action_location: String,
action: Option<Action<FD, Result<(), ServerFnError>>>,
) -> Option<Form<FD>> {
let builder = match self.inner { let builder = match self.inner {
FormBuilderInner::FullBuilder(fb) => fb, FormBuilderInner::FullBuilder(fb) => fb,
FormBuilderInner::ValidationBuilder { validations: _ } => return None, FormBuilderInner::ValidationBuilder { validations: _ } => return None,
}; };
let view = builder.fs.form_frame(builder.views.into_view()); let validations = builder.validations.clone();
let validate_form = move || {
let fd = builder.fd.get();
validations.iter().all(|v| v(&fd).is_ok())
};
let elements = builder.fs.form_frame(builder.views.into_view());
let view = view! {
<Form action=action_location
on:submit=move |ev| {
if !validate_form() {
ev.prevent_default();
return;
}
if let Some(action) = action {
action.dispatch(builder.fd.get());
}
}>
{elements}
</Form>
}
.into_view();
Some(Form { Some(Form {
fd: builder.fd, fd: builder.fd,
@ -281,10 +293,10 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
}) })
} }
fn validator(self) -> Validator<FD> { fn validator(&self) -> Validator<FD> {
let validations = match self.inner { let validations = match &self.inner {
FormBuilderInner::FullBuilder(fb) => fb.validations, FormBuilderInner::FullBuilder(fb) => fb.validations.clone(),
FormBuilderInner::ValidationBuilder { validations } => validations, FormBuilderInner::ValidationBuilder { validations } => validations.clone(),
}; };
Validator { validations } Validator { validations }
} }
@ -309,16 +321,24 @@ pub trait FormData: Default + Clone + 'static {
/// Constructs a [`Form`] for this FormData type. /// Constructs a [`Form`] for this FormData type.
/// ///
/// The [`Form`] provides the way to render the form. /// The [`Form`] provides the way to render the form.
fn get_form(style: Self::Style) -> Form<Self> { fn get_form(self, action: impl ToString, style: Self::Style) -> Form<Self> {
let builder = FormBuilder::new_full_builder(style); let builder = FormBuilder::new_full_builder(self, style);
let builder = Self::build_form(builder); let builder = Self::build_form(builder);
builder.build().expect("builder should be full builder") builder
.build(action.to_string(), None)
.expect("builder should be full builder")
} }
fn get_form_with_starting_data(self, style: Self::Style) -> Form<Self> { fn get_action_form(
let builder = FormBuilder::new_full_builder_with(self, style); self,
action: Action<Self, Result<(), ServerFnError>>,
style: Self::Style,
) -> Form<Self> {
let builder = FormBuilder::new_full_builder(self, style);
let builder = Self::build_form(builder); let builder = Self::build_form(builder);
builder.build().expect("builder should be full builder") builder
.build(action.url().unwrap_or(String::new()), Some(action))
.expect("builder should be full builder")
} }
fn get_validator() -> Validator<Self> { fn get_validator() -> Validator<Self> {

View File

@ -4,7 +4,6 @@ use crate::controls::{
text_input::TextInputData, ControlData, ControlRenderData, text_input::TextInputData, ControlData, ControlRenderData,
}; };
use leptos::*; use leptos::*;
use leptos_router::Form;
pub enum GridFormStylingAttributes { pub enum GridFormStylingAttributes {
Width(u32), Width(u32),
@ -16,12 +15,11 @@ pub struct GridFormStyle;
impl FormStyle for GridFormStyle { impl FormStyle for GridFormStyle {
type StylingAttributes = GridFormStylingAttributes; type StylingAttributes = GridFormStylingAttributes;
// TODO: something about an on-submit thing
fn form_frame(&self, children: View) -> View { fn form_frame(&self, children: View) -> View {
view! { view! {
<Form action="" class="form_grid"> <div class="form_grid">
{children} {children}
</Form> </div>
} }
.into_view() .into_view()
} }
@ -118,7 +116,7 @@ impl FormStyle for GridFormStyle {
<input <input
type="submit" type="submit"
value=control.data.text value=control.data.text
class="w-full form_submit" class="col-span-full form_submit"
/> />
} }
.into_view() .into_view()

View File

@ -10,6 +10,13 @@ use leptos::{Signal, View};
pub trait FormStyle: Default + 'static { pub trait FormStyle: Default + 'static {
type StylingAttributes; type StylingAttributes;
/// Render any containing components for the form.
///
/// This allows you to wrap all the form components
/// in another component if neccisary.
///
/// Do NOT wrap it in an actual `form` element; any
/// wrapping should be done with `div` or similar elements.
fn form_frame(&self, children: View) -> View; fn form_frame(&self, children: View) -> View;
fn heading(&self, control: ControlRenderData<Self, HeadingData>) -> View; fn heading(&self, control: ControlRenderData<Self, HeadingData>) -> View;
fn text_input( fn text_input(