Compare commits

...

3 Commits

Author SHA1 Message Date
079c9bde53 lint 2024-06-12 16:46:03 -05:00
1e2709dc8c ControlRenderData no long has boxed data 2024-06-12 16:29:35 -05:00
64d2631140 clean up style trait 2024-06-12 16:21:09 -05:00
9 changed files with 51 additions and 52 deletions

View File

@ -4,10 +4,12 @@ use leptos::RwSignal;
use std::rc::Rc;
use web_sys::MouseEvent;
type ButtonAction<FD> = dyn Fn(MouseEvent, &mut FD);
#[derive(Clone)]
pub struct ButtonData<FD: FormToolData> {
pub(crate) text: String,
pub(crate) action: Option<Rc<dyn Fn(MouseEvent, &mut FD)>>,
pub(crate) action: Option<Rc<ButtonAction<FD>>>,
}
impl<FD: FormToolData> Default for ButtonData<FD> {
fn default() -> Self {
@ -27,7 +29,7 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
let control = builder(button_builder);
let render_data = ControlRenderData {
data: Box::new(control.data),
data: control.data,
styles: control.styles,
};

View File

@ -15,10 +15,13 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
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, fd))
.unzip();
let view = fs.group(views.collect_view(), group_builder.styles);
let view = fs.group(super::ControlRenderData {
data: views.collect_view(),
styles: group_builder.styles,
});
let validation_cb = move || {
let mut success = true;
for validation in validation_cbs.iter().flatten() {

View File

@ -16,7 +16,6 @@ impl VanityControlData for HeadingData {
fs.heading(control)
}
}
// TODO: impl GetterVanityControl
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn heading(

View File

@ -70,8 +70,8 @@ pub trait ValidatedControlData: ControlData {}
/// The data needed to render a interactive control of type `C`.
pub struct ControlRenderData<FS: FormStyle + ?Sized, C: ?Sized> {
pub data: Box<C>,
pub styles: Vec<FS::StylingAttributes>,
pub data: C,
}
/// The data needed to render a read-only control of type `C`.
@ -100,7 +100,7 @@ impl<FD: FormToolData, FS: FormStyle, C: VanityControlData> VanityControlBuilder
pub(crate) fn build(self) -> BuiltVanityControlData<FD, FS, C> {
BuiltVanityControlData {
render_data: ControlRenderData {
data: Box::new(self.data),
data: self.data,
styles: self.style_attributes,
},
getter: self.getter,
@ -208,7 +208,7 @@ impl<FD: FormToolData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS
Ok(BuiltControlData {
render_data: ControlRenderData {
data: Box::new(self.data),
data: self.data,
styles: self.style_attributes,
},
getter,

View File

@ -2,8 +2,6 @@ use super::{ControlBuilder, ControlData, ControlRenderData, ValidatedControlData
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{Signal, View};
// TODO: have an option to have a display string and a value string in the options field
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct SelectData {
pub(crate) name: String,

View File

@ -1,8 +1,8 @@
use crate::{
controls::{
BuiltControlData, BuiltVanityControlData, ControlBuilder, ControlData, FieldGetter,
FieldSetter, ParseFn, RenderFn, UnparseFn, ValidationCb, ValidationFn,
VanityControlBuilder, VanityControlData,
BuiltControlData, BuiltVanityControlData, ControlBuilder, ControlData, ControlRenderData,
FieldSetter, ParseFn, RenderFn, ValidationCb, ValidationFn, VanityControlBuilder,
VanityControlData,
},
form::{Form, FormToolData, FormValidator},
styles::FormStyle,
@ -87,33 +87,17 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
&mut self,
control: ControlBuilder<FD, FS, C, FDT>,
) {
let BuiltControlData {
render_data,
getter,
setter,
parse_fn,
unparse_fn,
validation_fn,
} = match control.build() {
let built_control_data = match control.build() {
Ok(c) => c,
Err(e) => panic!("Invalid Component: {}", e),
};
if let Some(ref validation_fn) = validation_fn {
if let Some(ref validation_fn) = built_control_data.validation_fn {
self.validations.push(validation_fn.clone());
}
let render_fn = move |fs: &FS, fd: RwSignal<FD>| {
let (view, cb) = Self::build_control_view(
fd,
fs,
getter,
setter,
unparse_fn,
parse_fn,
validation_fn,
render_data,
);
let (view, cb) = Self::build_control_view(fd, fs, built_control_data);
(view, Some(cb))
};
@ -123,13 +107,17 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
fn build_control_view<C: ControlData, FDT: 'static>(
fd: RwSignal<FD>,
fs: &FS,
getter: Rc<dyn FieldGetter<FD, FDT>>,
setter: Rc<dyn FieldSetter<FD, FDT>>,
unparse_fn: Box<dyn UnparseFn<<C as ControlData>::ReturnType, FDT>>,
parse_fn: Box<dyn ParseFn<<C as ControlData>::ReturnType, FDT>>,
validation_fn: Option<Rc<dyn ValidationFn<FD>>>,
render_data: crate::controls::ControlRenderData<FS, C>,
control_data: BuiltControlData<FD, FS, C, FDT>,
) -> (View, Box<dyn ValidationCb>) {
let BuiltControlData {
render_data,
getter,
setter,
parse_fn,
unparse_fn,
validation_fn,
} = control_data;
let (validation_signal, validation_signal_set) = create_signal(Ok(()));
let validation_fn_clone = validation_fn.clone();
let value_getter = move || {
@ -231,7 +219,10 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
.map(|r_fn| r_fn(&fs, fd))
.unzip();
let elements = fs.form_frame(views.into_view(), self.styles);
let elements = fs.form_frame(ControlRenderData {
data: views.into_view(),
styles: self.styles,
});
let on_submit = move |ev: SubmitEvent| {
let mut failed = false;
@ -267,7 +258,10 @@ impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
.map(|r_fn| r_fn(&fs, fd))
.unzip();
let elements = fs.form_frame(views.into_view(), self.styles);
let elements = fs.form_frame(ControlRenderData {
data: views.into_view(),
styles: self.styles,
});
let on_submit = move |ev: SubmitEvent| {
let mut failed = false;

View File

@ -21,8 +21,8 @@ pub struct GridFormStyle;
impl FormStyle for GridFormStyle {
type StylingAttributes = GridFormStylingAttributes;
fn form_frame(&self, children: View, _styles: Vec<Self::StylingAttributes>) -> View {
view! { <div class="form_grid">{children}</div> }.into_view()
fn form_frame(&self, form: ControlRenderData<Self, View>) -> View {
view! { <div class="form_grid">{form.data}</div> }.into_view()
}
fn heading(&self, control: ControlRenderData<Self, HeadingData>) -> View {
@ -111,8 +111,7 @@ impl FormStyle for GridFormStyle {
}
fn submit(&self, control: ControlRenderData<Self, SubmitData>) -> View {
view! { <input type="submit" value=control.data.text class="form_submit"/> }
.into_view()
view! { <input type="submit" value=control.data.text class="form_submit"/> }.into_view()
}
fn text_area(
@ -383,10 +382,9 @@ impl FormStyle for GridFormStyle {
.into_view()
}
// TODO: change this and form frame to use ControlRenderData
fn group(&self, inner: View, styles: Vec<Self::StylingAttributes>) -> View {
fn group(&self, group: ControlRenderData<Self, View>) -> View {
let mut width = 12;
for style in styles {
for style in group.styles {
match style {
GridFormStylingAttributes::Width(w) => width = w,
}
@ -394,7 +392,7 @@ impl FormStyle for GridFormStyle {
view! {
<div class="form_group form_grid" style:grid-column=format!("span {}", width)>
{inner}
{group.data}
</div>
}
.into_view()

View File

@ -23,7 +23,7 @@ pub trait FormStyle: 'static {
///
/// 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, style: Vec<Self::StylingAttributes>) -> View;
fn form_frame(&self, form: ControlRenderData<Self, View>) -> View;
fn heading(&self, control: ControlRenderData<Self, HeadingData>) -> View;
fn hidden(
&self,
@ -89,8 +89,7 @@ pub trait FormStyle: 'static {
validation_state: Signal<Result<(), String>>,
) -> View;
fn submit(&self, control: ControlRenderData<Self, SubmitData>) -> View;
// TODO: test custom component
fn custom_component(&self, view: View) -> View;
fn group(&self, inner: View, style: Vec<Self::StylingAttributes>) -> View;
fn group(&self, group: ControlRenderData<Self, View>) -> View;
fn spacer(&self, control: ControlRenderData<Self, SpacerData>) -> View;
}

View File

@ -1,6 +1,12 @@
use crate::{controls::ValidationFn, FormToolData};
use std::fmt::Display;
/// A function that validates a field.
///
/// This is similar to [`ValidationFn`](crate::controls::ValidationFn)
/// but takes a &str for the name of the field for improved error messages.
type ValidationBuilderFn<T> = dyn Fn(&str, &T) -> Result<(), String> + 'static;
/// A helper builder that allows you to specify a validation function
/// declaritivly
///
@ -14,7 +20,7 @@ pub struct ValidationBuilder<FD: FormToolData, T: 'static> {
/// The getter function for the field to validate.
field_fn: Box<dyn Fn(&FD) -> &T + 'static>,
/// The functions to be called when validating.
functions: Vec<Box<dyn Fn(&str, &T) -> Result<(), String> + 'static>>,
functions: Vec<Box<ValidationBuilderFn<T>>>,
}
impl<FD: FormToolData, T: 'static> ValidationBuilder<FD, T> {