Add context #27

Merged
mitchell merged 3 commits from feature/context into main 2024-06-12 23:45:35 +00:00
17 changed files with 56 additions and 33 deletions
Showing only changes of commit 4c6b6fa920 - Show all commits

View File

@ -20,7 +20,7 @@ impl<FD: FormToolData> Default for ButtonData<FD> {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn button(
mut self,
builder: impl Fn(ButtonBuilder<FD, FS>) -> ButtonBuilder<FD, FS>,

View File

@ -23,7 +23,7 @@ impl ControlData for CheckboxData {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn checkbox<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -1,7 +1,7 @@
use super::{ControlBuilder, ControlData};
use crate::{styles::FormStyle, FormBuilder, FormToolData};
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn custom<CC: ControlData, FDT: Clone + PartialEq + 'static>(
mut self,
control_data: CC,

View File

@ -2,11 +2,17 @@ use super::ValidationCb;
use crate::{form::FormToolData, form_builder::FormBuilder, styles::FormStyle};
use leptos::{CollectView, RwSignal};
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn group(mut self, builder: impl Fn(FormBuilder<FD, FS>) -> FormBuilder<FD, FS>) -> Self {
let mut group_builder = FormBuilder::new();
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn group(
mut self,
builder: impl Fn(FormBuilder<FD, FS, CX>) -> FormBuilder<FD, FS, CX>,
) -> Self {
let mut group_builder = FormBuilder::new(self.cx);
group_builder = builder(group_builder);
// Take context back
self.cx = group_builder.cx;
for validation in group_builder.validations {
self.validations.push(validation);
}

View File

@ -17,7 +17,7 @@ impl VanityControlData for HeadingData {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn heading(
self,
builder: impl Fn(

View File

@ -20,7 +20,7 @@ impl ControlData for HiddenData {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn hidden<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -17,7 +17,7 @@ impl VanityControlData for OutputData {
}
impl GetterVanityControlData for OutputData {}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn output(
self,
builder: impl Fn(

View File

@ -25,7 +25,7 @@ impl ControlData for RadioButtonsData {
}
impl ValidatedControlData for RadioButtonsData {}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn radio_buttons<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -27,7 +27,7 @@ impl ControlData for SelectData {
}
impl ValidatedControlData for SelectData {}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn select<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -38,7 +38,7 @@ impl ControlData for SliderData {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn slider<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -18,7 +18,7 @@ impl VanityControlData for SpacerData {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn spacer(
self,
builder: impl Fn(

View File

@ -29,7 +29,7 @@ impl ControlData for StepperData {
}
impl ValidatedControlData for StepperData {}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn stepper<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -18,7 +18,7 @@ impl VanityControlData for SubmitData {
}
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn submit(
self,
builder: impl Fn(

View File

@ -23,7 +23,7 @@ impl ControlData for TextAreaData {
}
impl ValidatedControlData for TextAreaData {}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn text_area<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -39,7 +39,7 @@ impl ControlData for TextInputData {
}
impl ValidatedControlData for TextInputData {}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
pub fn text_input<FDT: Clone + PartialEq + 'static>(
self,
builder: impl Fn(

View File

@ -40,15 +40,16 @@ pub struct Form<FD: FormToolData> {
impl<FD: FormToolData> Form<FD> {
/// Gets the [`Validator`] for this form.
pub fn validator(self) -> FormValidator<FD> {
pub fn validator(&self) -> FormValidator<FD> {
FormValidator {
validations: self.validations,
validations: self.validations.clone(),
}
}
/// Validates the [`ToolFormData`], returning the result
pub fn validate(&self) -> Result<(), String> {
self.fd.get_untracked().validate()
let validator = self.validator();
validator.validate(&self.fd.get_untracked())
}
/// Gets the view associated with this [`Form`].
@ -80,6 +81,7 @@ impl<FD: FormToolData> IntoView for Form<FD> {
/// to physically lay out a form, and how that data should be parsed and validated.
pub trait FormToolData: Clone + 'static {
type Style: FormStyle;
type Context: 'static;
/// Defines how the form should be layed out and how the data should be parsed and validated.
///
@ -88,7 +90,9 @@ pub trait FormToolData: Clone + 'static {
/// Uses the given form builder to specify what fields should be present
/// in the form, what properties those fields should have, and how that
/// data should be parsed and checked.
fn build_form(fb: FormBuilder<Self, Self::Style>) -> FormBuilder<Self, Self::Style>;
fn build_form(
fb: FormBuilder<Self, Self::Style, Self::Context>,
) -> FormBuilder<Self, Self::Style, Self::Context>;
/// Constructs a [`Form`] for this [`FormToolData`] type.
///
@ -96,8 +100,13 @@ pub trait FormToolData: Clone + 'static {
/// [`Form`](leptos_router::Form)
/// component. Call [`get_action_form`]\() to get the
/// [`ActionForm`](leptos_router::ActionForm) version.
fn get_form(self, action: impl ToString, style: Self::Style) -> Form<Self> {
let builder = FormBuilder::new();
fn get_form(
self,
action: impl ToString,
style: Self::Style,
context: Self::Context,
) -> Form<Self> {
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.build_plain_form(action.to_string(), self, style)
}
@ -112,13 +121,14 @@ pub trait FormToolData: Clone + 'static {
self,
action: Action<ServFn, Result<ServFn::Output, ServerFnError<ServFn::Error>>>,
style: Self::Style,
context: Self::Context,
) -> Form<Self>
where
ServFn: DeserializeOwned + ServerFn<InputEncoding = PostUrl> + 'static,
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
From<FormData>,
{
let builder = FormBuilder::new();
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.build_action_form(action, self, style)
}
@ -131,18 +141,18 @@ pub trait FormToolData: Clone + 'static {
///
/// However, the code to render the views are not configured out, it
/// simply doesn't run, so the view needs to compile even on the server.
fn get_validator() -> FormValidator<Self> {
let builder = FormBuilder::new();
fn get_validator(context: Self::Context) -> FormValidator<Self> {
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.validator()
}
/// Validates this [`FormToolData`] struct.
///
/// This is shorthand for creating a validator with [`get_validator`]\()
/// and then calling `validator.validate(&self)`.
fn validate(&self) -> Result<(), String> {
let validator = Self::get_validator();
/// This is shorthand for creating a validator with [`get_validator`]
/// and then calling `validator.validate(&self, context)`.
fn validate(&self, context: Self::Context) -> Result<(), String> {
let validator = Self::get_validator(context);
validator.validate(self)
}
}

View File

@ -19,7 +19,13 @@ use web_sys::{FormData, SubmitEvent};
/// A builder for laying out forms.
///
/// This builder allows you to specify what components should make up the form.
pub struct FormBuilder<FD: FormToolData, FS: FormStyle> {
///
/// ### Type Parameters
/// - `FD`: FormToolData - Your form data
/// - `FS`: FormStyle - The form style you are using
/// - `CX`: Context - A user defined context that you are rendering the form in
pub struct FormBuilder<FD: FormToolData, FS: FormStyle, CX: 'static> {
pub(crate) cx: CX,
/// The list of [`ValidationFn`]s.
pub(crate) validations: Vec<Rc<dyn ValidationFn<FD>>>,
/// The list of functions that will render the form.
@ -28,10 +34,11 @@ pub struct FormBuilder<FD: FormToolData, FS: FormStyle> {
pub(crate) styles: Vec<FS::StylingAttributes>,
}
impl<FD: FormToolData, FS: FormStyle> FormBuilder<FD, FS> {
impl<FD: FormToolData, FS: FormStyle, CX: 'static> FormBuilder<FD, FS, CX> {
/// Creates a new [`FormBuilder`]
pub(crate) fn new() -> FormBuilder<FD, FS> {
pub(crate) fn new(cx: CX) -> Self {
FormBuilder {
cx,
validations: Vec::new(),
render_fns: Vec::new(),
styles: Vec::new(),