use crate::{controls::ValidationFn, form_builder::FormBuilder, styles::FormStyle};
use ev::SubmitEvent;
use leptos::{
prelude::{AnyView, GetUntracked, IntoAny, RwSignal},
server::ServerAction,
server_fn::{
client::Client,
codec::{Json, PostUrl},
request::ClientReq,
Http, ServerFn,
},
*,
};
use serde::de::DeserializeOwned;
use std::sync::Arc;
use web_sys::FormData;
pub struct FormValidator<FD> {
pub(crate) validations: Vec<Arc<dyn ValidationFn<FD>>>,
}
impl<FD: FormToolData> FormValidator<FD> {
pub fn validate(&self, form_data: &FD) -> Result<(), String> {
for v in self.validations.iter() {
(*v)(form_data)?;
}
Ok(())
}
}
pub struct Form<FD: FormToolData> {
pub fd: RwSignal<FD>,
pub(crate) validations: Vec<Arc<dyn ValidationFn<FD>>>,
pub(crate) view: AnyView,
}
impl<FD: FormToolData> Form<FD> {
pub fn validator(&self) -> FormValidator<FD> {
FormValidator {
validations: self.validations.clone(),
}
}
pub fn validate(&self) -> Result<(), String> {
let validator = self.validator();
validator.validate(&self.fd.get_untracked())
}
pub fn to_parts(self) -> (RwSignal<FD>, FormValidator<FD>, AnyView) {
(
self.fd,
FormValidator {
validations: self.validations,
},
self.view,
)
}
}
impl<FD: FormToolData> IntoAny for Form<FD> {
fn into_any(self) -> AnyView {
self.view
}
}
pub trait FormToolData: Clone + Send + Sync + 'static {
type Style: FormStyle;
type Context: Send + Sync + 'static;
fn build_form(fb: FormBuilder<Self>) -> FormBuilder<Self>;
fn get_form<ServFn, F: Fn(SubmitEvent, RwSignal<Self>) + 'static>(
self,
action: ServerAction<ServFn>,
on_submit: F,
style: Self::Style,
context: Self::Context,
) -> Form<Self>
where
ServFn: DeserializeOwned
+ ServerFn<Protocol = Http<PostUrl, Json>>
+ From<Self>
+ Clone
+ Send
+ Sync
+ 'static,
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
From<FormData>,
ServFn::Output: Send + Sync + 'static,
ServFn::Error: Send + Sync + 'static,
<ServFn as ServerFn>::Client: Client<<ServFn as ServerFn>::Error>,
{
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.build_form(action, on_submit, self, style)
}
fn get_action_form<ServFn, F: Fn(SubmitEvent, RwSignal<Self>) + 'static>(
self,
action: ServerAction<ServFn>,
on_submit: F,
style: Self::Style,
context: Self::Context,
) -> Form<Self>
where
ServFn: DeserializeOwned
+ ServerFn<Protocol = Http<PostUrl, Json>>
+ From<Self>
+ Clone
+ Send
+ Sync
+ 'static,
<<ServFn::Client as Client<ServFn::Error>>::Request as ClientReq<ServFn::Error>>::FormData:
From<FormData>,
ServFn::Output: Send + Sync + 'static,
ServFn::Error: Send + Sync + 'static,
<ServFn as ServerFn>::Client: Client<<ServFn as ServerFn>::Error>,
{
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.build_action_form(action, on_submit, self, style)
}
fn get_plain_form<F: Fn(SubmitEvent, RwSignal<Self>) + 'static>(
self,
url: impl ToString,
on_submit: F,
style: Self::Style,
context: Self::Context,
) -> Form<Self> {
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.build_plain_form(url.to_string(), on_submit, self, style)
}
fn get_form_controls(self, style: Self::Style, context: Self::Context) -> Form<Self> {
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.build_form_controls(self, style)
}
fn get_validator(context: Self::Context) -> FormValidator<Self> {
let builder = FormBuilder::new(context);
let builder = Self::build_form(builder);
builder.validator()
}
fn validate(&self, context: Self::Context) -> Result<(), String> {
let validator = Self::get_validator(context);
validator.validate(self)
}
}