module restructuring for 0.7
This commit is contained in:
parent
21dd7e9c76
commit
3406446ebd
|
@ -1,10 +1,6 @@
|
||||||
use crate::{
|
use crate::{children::Children, component, prelude::*, IntoView};
|
||||||
children::Children, component, from_form_data::FromFormData, prelude::*,
|
use leptos_dom::helpers::window;
|
||||||
IntoView,
|
use leptos_server::{ServerAction, ServerMultiAction};
|
||||||
};
|
|
||||||
use leptos_dom::{events::submit, helpers::window};
|
|
||||||
use leptos_server::{ArcServerMultiAction, ServerAction, ServerMultiAction};
|
|
||||||
use reactive_graph::actions::{ArcMultiAction, MultiAction};
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use server_fn::{
|
use server_fn::{
|
||||||
client::Client, codec::PostUrl, request::ClientReq, ServerFn, ServerFnError,
|
client::Client, codec::PostUrl, request::ClientReq, ServerFn, ServerFnError,
|
||||||
|
@ -14,11 +10,17 @@ use tachys::{
|
||||||
html::{
|
html::{
|
||||||
attribute::any_attribute::AnyAttribute,
|
attribute::any_attribute::AnyAttribute,
|
||||||
element::{form, Form},
|
element::{form, Form},
|
||||||
|
event::submit,
|
||||||
},
|
},
|
||||||
reactive_graph::node_ref::NodeRef,
|
reactive_graph::node_ref::NodeRef,
|
||||||
renderer::dom::Dom,
|
renderer::dom::Dom,
|
||||||
};
|
};
|
||||||
use web_sys::{FormData, SubmitEvent};
|
use thiserror::Error;
|
||||||
|
use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};
|
||||||
|
use web_sys::{
|
||||||
|
Event, FormData, HtmlButtonElement, HtmlFormElement, HtmlInputElement,
|
||||||
|
SubmitEvent,
|
||||||
|
};
|
||||||
|
|
||||||
/// Automatically turns a server [Action](leptos_server::Action) into an HTML
|
/// Automatically turns a server [Action](leptos_server::Action) into an HTML
|
||||||
/// [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
|
/// [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
|
||||||
|
@ -252,3 +254,85 @@ pub(crate) fn resolve_redirect_url(loc: &str) -> Option<web_sys::Url> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to deserialize a type from form data. This can be used for client-side
|
||||||
|
/// validation during form submission.
|
||||||
|
pub trait FromFormData
|
||||||
|
where
|
||||||
|
Self: Sized + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
/// Tries to deserialize the data, given only the `submit` event.
|
||||||
|
fn from_event(ev: &web_sys::Event) -> Result<Self, FromFormDataError>;
|
||||||
|
|
||||||
|
/// Tries to deserialize the data, given the actual form data.
|
||||||
|
fn from_form_data(
|
||||||
|
form_data: &web_sys::FormData,
|
||||||
|
) -> Result<Self, serde_qs::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum FromFormDataError {
|
||||||
|
#[error("Could not find <form> connected to event.")]
|
||||||
|
MissingForm(Event),
|
||||||
|
#[error("Could not create FormData from <form>: {0:?}")]
|
||||||
|
FormData(JsValue),
|
||||||
|
#[error("Deserialization error: {0:?}")]
|
||||||
|
Deserialization(serde_qs::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromFormData for T
|
||||||
|
where
|
||||||
|
T: serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn from_event(ev: &Event) -> Result<Self, FromFormDataError> {
|
||||||
|
let submit_ev = ev.unchecked_ref();
|
||||||
|
let form_data = form_data_from_event(submit_ev)?;
|
||||||
|
Self::from_form_data(&form_data)
|
||||||
|
.map_err(FromFormDataError::Deserialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_form_data(
|
||||||
|
form_data: &web_sys::FormData,
|
||||||
|
) -> Result<Self, serde_qs::Error> {
|
||||||
|
let data =
|
||||||
|
web_sys::UrlSearchParams::new_with_str_sequence_sequence(form_data)
|
||||||
|
.unwrap_throw();
|
||||||
|
let data = data.to_string().as_string().unwrap_or_default();
|
||||||
|
serde_qs::Config::new(5, false).deserialize_str::<Self>(&data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn form_data_from_event(
|
||||||
|
ev: &SubmitEvent,
|
||||||
|
) -> Result<FormData, FromFormDataError> {
|
||||||
|
let submitter = ev.submitter();
|
||||||
|
let mut submitter_name_value = None;
|
||||||
|
let opt_form = match &submitter {
|
||||||
|
Some(el) => {
|
||||||
|
if let Some(form) = el.dyn_ref::<HtmlFormElement>() {
|
||||||
|
Some(form.clone())
|
||||||
|
} else if let Some(input) = el.dyn_ref::<HtmlInputElement>() {
|
||||||
|
submitter_name_value = Some((input.name(), input.value()));
|
||||||
|
Some(ev.target().unwrap().unchecked_into())
|
||||||
|
} else if let Some(button) = el.dyn_ref::<HtmlButtonElement>() {
|
||||||
|
submitter_name_value = Some((button.name(), button.value()));
|
||||||
|
Some(ev.target().unwrap().unchecked_into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => ev.target().map(|form| form.unchecked_into()),
|
||||||
|
};
|
||||||
|
match opt_form.as_ref().map(FormData::new_with_form) {
|
||||||
|
None => Err(FromFormDataError::MissingForm(ev.clone().into())),
|
||||||
|
Some(Err(e)) => Err(FromFormDataError::FormData(e)),
|
||||||
|
Some(Ok(form_data)) => {
|
||||||
|
if let Some((name, value)) = submitter_name_value {
|
||||||
|
form_data
|
||||||
|
.append_with_str(&name, &value)
|
||||||
|
.map_err(FromFormDataError::FormData)?;
|
||||||
|
}
|
||||||
|
Ok(form_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,88 +1,2 @@
|
||||||
use thiserror::Error;
|
|
||||||
use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};
|
|
||||||
use web_sys::{
|
|
||||||
Event, FormData, HtmlButtonElement, HtmlFormElement, HtmlInputElement,
|
|
||||||
SubmitEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Tries to deserialize a type from form data. This can be used for client-side
|
|
||||||
/// validation during form submission.
|
|
||||||
pub trait FromFormData
|
|
||||||
where
|
|
||||||
Self: Sized + serde::de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
/// Tries to deserialize the data, given only the `submit` event.
|
|
||||||
fn from_event(ev: &web_sys::Event) -> Result<Self, FromFormDataError>;
|
|
||||||
|
|
||||||
/// Tries to deserialize the data, given the actual form data.
|
|
||||||
fn from_form_data(
|
|
||||||
form_data: &web_sys::FormData,
|
|
||||||
) -> Result<Self, serde_qs::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum FromFormDataError {
|
|
||||||
#[error("Could not find <form> connected to event.")]
|
|
||||||
MissingForm(Event),
|
|
||||||
#[error("Could not create FormData from <form>: {0:?}")]
|
|
||||||
FormData(JsValue),
|
|
||||||
#[error("Deserialization error: {0:?}")]
|
|
||||||
Deserialization(serde_qs::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromFormData for T
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
fn from_event(ev: &Event) -> Result<Self, FromFormDataError> {
|
|
||||||
let submit_ev = ev.unchecked_ref();
|
|
||||||
let form_data = form_data_from_event(submit_ev)?;
|
|
||||||
Self::from_form_data(&form_data)
|
|
||||||
.map_err(FromFormDataError::Deserialization)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_form_data(
|
|
||||||
form_data: &web_sys::FormData,
|
|
||||||
) -> Result<Self, serde_qs::Error> {
|
|
||||||
let data =
|
|
||||||
web_sys::UrlSearchParams::new_with_str_sequence_sequence(form_data)
|
|
||||||
.unwrap_throw();
|
|
||||||
let data = data.to_string().as_string().unwrap_or_default();
|
|
||||||
serde_qs::Config::new(5, false).deserialize_str::<Self>(&data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn form_data_from_event(
|
|
||||||
ev: &SubmitEvent,
|
|
||||||
) -> Result<FormData, FromFormDataError> {
|
|
||||||
let submitter = ev.submitter();
|
|
||||||
let mut submitter_name_value = None;
|
|
||||||
let opt_form = match &submitter {
|
|
||||||
Some(el) => {
|
|
||||||
if let Some(form) = el.dyn_ref::<HtmlFormElement>() {
|
|
||||||
Some(form.clone())
|
|
||||||
} else if let Some(input) = el.dyn_ref::<HtmlInputElement>() {
|
|
||||||
submitter_name_value = Some((input.name(), input.value()));
|
|
||||||
Some(ev.target().unwrap().unchecked_into())
|
|
||||||
} else if let Some(button) = el.dyn_ref::<HtmlButtonElement>() {
|
|
||||||
submitter_name_value = Some((button.name(), button.value()));
|
|
||||||
Some(ev.target().unwrap().unchecked_into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => ev.target().map(|form| form.unchecked_into()),
|
|
||||||
};
|
|
||||||
match opt_form.as_ref().map(FormData::new_with_form) {
|
|
||||||
None => Err(FromFormDataError::MissingForm(ev.clone().into())),
|
|
||||||
Some(Err(e)) => Err(FromFormDataError::FormData(e)),
|
|
||||||
Some(Ok(form_data)) => {
|
|
||||||
if let Some((name, value)) = submitter_name_value {
|
|
||||||
form_data
|
|
||||||
.append_with_str(&name, &value)
|
|
||||||
.map_err(FromFormDataError::FormData)?;
|
|
||||||
}
|
|
||||||
Ok(form_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
use leptos_dom::events::{on, EventDescriptor, On};
|
|
||||||
use std::future::Future;
|
|
||||||
use tachys::{
|
use tachys::{
|
||||||
html::attribute::{global::OnAttribute, Attribute},
|
|
||||||
hydration::Cursor,
|
hydration::Cursor,
|
||||||
renderer::{dom::Dom, DomRenderer, Renderer},
|
renderer::dom::Dom,
|
||||||
ssr::StreamBuilder,
|
ssr::StreamBuilder,
|
||||||
view::{
|
view::{Position, PositionState, Render, RenderHtml},
|
||||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
|
||||||
RenderHtml,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct View<T>(T)
|
pub struct View<T>(T)
|
||||||
|
|
|
@ -146,40 +146,82 @@
|
||||||
|
|
||||||
extern crate self as leptos;
|
extern crate self as leptos;
|
||||||
|
|
||||||
|
/// Exports all the core types of the library.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
// Traits
|
||||||
|
// These should always be exported from the prelude
|
||||||
pub use crate::suspense_component::FutureViewExt;
|
pub use crate::suspense_component::FutureViewExt;
|
||||||
pub use reactive_graph::prelude::*;
|
pub use reactive_graph::prelude::*;
|
||||||
pub use tachys::prelude::*;
|
pub use tachys::prelude::*;
|
||||||
|
|
||||||
|
// Structs
|
||||||
|
// In the future, maybe we should remove this blanket export
|
||||||
|
// However, it is definitely useful relative to looking up every struct etc.
|
||||||
|
mod export_types {
|
||||||
|
#[cfg(feature = "nonce")]
|
||||||
|
pub use crate::nonce::*;
|
||||||
|
pub use crate::{
|
||||||
|
callback::*, children::*, component::*, context::*,
|
||||||
|
control_flow::*, error::*, form::*, hydration::*, into_view::*,
|
||||||
|
};
|
||||||
|
pub use leptos_config::*;
|
||||||
|
pub use leptos_dom::*;
|
||||||
|
pub use leptos_macro::*;
|
||||||
|
pub use leptos_server::*;
|
||||||
|
pub use reactive_graph::*;
|
||||||
|
pub use server_fn::*;
|
||||||
|
pub use tachys;
|
||||||
|
}
|
||||||
|
pub use export_types::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod action_form;
|
/// Components used for working with HTML forms, like `<ActionForm>`.
|
||||||
pub use action_form::*;
|
pub mod form;
|
||||||
|
|
||||||
|
/// A standard way to wrap functions and closures to pass them to components.
|
||||||
pub mod callback;
|
pub mod callback;
|
||||||
|
|
||||||
|
/// Types that can be passed as the `children` prop of a component.
|
||||||
pub mod children;
|
pub mod children;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Traits used to implement component constructors.
|
||||||
pub mod component;
|
pub mod component;
|
||||||
mod error_boundary;
|
mod error_boundary;
|
||||||
pub use error_boundary::*;
|
|
||||||
|
/// Tools for handling errors.
|
||||||
|
pub mod error {
|
||||||
|
pub use crate::error_boundary::*;
|
||||||
|
pub use throw_error::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Control-flow components like `<Show>` and `<For>`.
|
||||||
|
pub mod control_flow {
|
||||||
|
pub use crate::{for_loop::*, show::*};
|
||||||
|
}
|
||||||
mod for_loop;
|
mod for_loop;
|
||||||
mod hydration_scripts;
|
mod show;
|
||||||
|
|
||||||
|
/// Components to enable server-side rendering and client-side hydration.
|
||||||
|
pub mod hydration;
|
||||||
|
|
||||||
|
/// Utilities for exporting nonces to be used for a Content Security Policy.
|
||||||
#[cfg(feature = "nonce")]
|
#[cfg(feature = "nonce")]
|
||||||
pub mod nonce;
|
pub mod nonce;
|
||||||
mod show;
|
|
||||||
|
/// Components to load asynchronous data.
|
||||||
|
pub mod suspense {
|
||||||
|
pub use crate::{suspense_component::*, transition::*};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod suspense_component;
|
mod suspense_component;
|
||||||
|
|
||||||
|
/// Types for reactive string properties for components.
|
||||||
pub mod text_prop;
|
pub mod text_prop;
|
||||||
mod transition;
|
mod transition;
|
||||||
pub use for_loop::*;
|
pub use leptos_macro;
|
||||||
pub use hydration_scripts::*;
|
|
||||||
pub use leptos_macro::*;
|
|
||||||
pub use reactive_graph::{
|
|
||||||
self,
|
|
||||||
signal::{arc_signal, create_signal, signal},
|
|
||||||
};
|
|
||||||
pub use server_fn;
|
pub use server_fn;
|
||||||
pub use show::*;
|
|
||||||
pub use suspense_component::{Suspend, Suspense};
|
|
||||||
pub use throw_error as error;
|
|
||||||
pub use transition::*;
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use typed_builder;
|
pub use typed_builder;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -188,35 +230,24 @@ mod into_view;
|
||||||
pub use into_view::IntoView;
|
pub use into_view::IntoView;
|
||||||
pub use leptos_dom;
|
pub use leptos_dom;
|
||||||
pub use tachys;
|
pub use tachys;
|
||||||
|
/// Tools to mount an application to the DOM, or to hydrate it from server-rendered HTML.
|
||||||
pub mod mount;
|
pub mod mount;
|
||||||
pub use any_spawner::Executor;
|
|
||||||
pub use leptos_config as config;
|
pub use leptos_config as config;
|
||||||
#[cfg(feature = "hydrate")]
|
|
||||||
pub use mount::hydrate_body;
|
|
||||||
pub use mount::mount_to_body;
|
|
||||||
pub use oco_ref as oco;
|
pub use oco_ref as oco;
|
||||||
pub mod from_form_data;
|
mod from_form_data;
|
||||||
|
pub use reactive_graph as reactive;
|
||||||
pub mod signals {
|
|
||||||
pub use reactive_graph::signal::{
|
|
||||||
arc_signal, signal, ArcReadSignal, ArcRwSignal, ArcWriteSignal,
|
|
||||||
ReadSignal, RwSignal, WriteSignal,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Provide and access data along the reactive graph, sharing data without directly passing arguments.
|
||||||
pub mod context {
|
pub mod context {
|
||||||
pub use reactive_graph::owner::{provide_context, use_context};
|
pub use reactive_graph::owner::{provide_context, use_context};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod ev {
|
|
||||||
pub use tachys::html::event::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod html {
|
|
||||||
pub use tachys::html::element::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use leptos_server as server;
|
pub use leptos_server as server;
|
||||||
|
/// HTML element types.
|
||||||
|
pub use tachys::html::element as html;
|
||||||
|
/// HTML event types.
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use tachys::html::event as ev;
|
||||||
|
|
||||||
/// Utilities for simple isomorphic logging to the console or terminal.
|
/// Utilities for simple isomorphic logging to the console or terminal.
|
||||||
pub mod logging {
|
pub mod logging {
|
||||||
|
|
|
@ -240,12 +240,13 @@ pub trait FutureViewExt: Sized {
|
||||||
|
|
||||||
impl<F> FutureViewExt for F where F: Future + Sized {}
|
impl<F> FutureViewExt for F where F: Future + Sized {}
|
||||||
|
|
||||||
|
/* // TODO remove in favor of Suspend()?
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! suspend {
|
macro_rules! suspend {
|
||||||
($fut:expr) => {
|
($fut:expr) => {
|
||||||
move || $crate::prelude::FutureViewExt::wait(async move { $fut })
|
move || $crate::prelude::FutureViewExt::wait(async move { $fut })
|
||||||
};
|
};
|
||||||
}
|
}*/
|
||||||
|
|
||||||
pub struct Suspend<Fut>(pub Fut);
|
pub struct Suspend<Fut>(pub Fut);
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
//! The DOM implementation for `leptos`.
|
//! DOM helpers for Leptos.
|
||||||
|
|
||||||
use reactive_graph::owner::Owner;
|
|
||||||
use tachys::{
|
|
||||||
dom::body,
|
|
||||||
renderer::dom::Dom,
|
|
||||||
view::{Mountable, Render},
|
|
||||||
};
|
|
||||||
use web_sys::HtmlElement;
|
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub use tachys::html::event as events;
|
|
||||||
|
|
||||||
/// Utilities for simple isomorphic logging to the console or terminal.
|
/// Utilities for simple isomorphic logging to the console or terminal.
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -462,6 +462,7 @@ generate_event_types! {
|
||||||
|
|
||||||
// Export `web_sys` event types
|
// Export `web_sys` event types
|
||||||
use super::attribute::NextAttribute;
|
use super::attribute::NextAttribute;
|
||||||
|
#[doc(no_inline)]
|
||||||
pub use web_sys::{
|
pub use web_sys::{
|
||||||
AnimationEvent, BeforeUnloadEvent, CompositionEvent, CustomEvent,
|
AnimationEvent, BeforeUnloadEvent, CompositionEvent, CustomEvent,
|
||||||
DeviceMotionEvent, DeviceOrientationEvent, DragEvent, ErrorEvent, Event,
|
DeviceMotionEvent, DeviceOrientationEvent, DragEvent, ErrorEvent, Event,
|
||||||
|
|
Loading…
Reference in New Issue