Merge branch 'leptos_dom_v2' of https://github.com/jquesada2016/leptos into leptos_dom_v2
This commit is contained in:
commit
8e1c165427
|
@ -137,7 +137,7 @@ features = [
|
|||
]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["web"]
|
||||
web = ["leptos_reactive/csr", "leptos/csr"]
|
||||
ssr = ["leptos_reactive/ssr", "leptos/ssr"]
|
||||
stable = ["leptos_reactive/stable"]
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{borrow::Cow, marker::PhantomData};
|
|||
use wasm_bindgen::convert::FromWasmAbi;
|
||||
|
||||
/// A trait for converting types into [web_sys events](web_sys).
|
||||
pub trait EventDescriptor {
|
||||
pub trait EventDescriptor: Clone {
|
||||
/// The [`web_sys`] event type, such as [`web_sys::MouseEvent`].
|
||||
type EventType: FromWasmAbi;
|
||||
|
||||
|
@ -23,6 +23,7 @@ pub trait EventDescriptor {
|
|||
|
||||
/// Overrides the [`EventDescriptor::bubbles`] method to always return
|
||||
/// `false`, which forces the event to not be globally delegated.
|
||||
#[derive(Clone)]
|
||||
pub struct Undelegated<Ev: EventDescriptor>(pub Ev);
|
||||
|
||||
impl<Ev: EventDescriptor> EventDescriptor for Undelegated<Ev> {
|
||||
|
@ -43,6 +44,15 @@ pub struct Custom<E: FromWasmAbi = web_sys::Event> {
|
|||
_event_type: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: FromWasmAbi> Clone for Custom<E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
_event_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: FromWasmAbi> EventDescriptor for Custom<E> {
|
||||
type EventType = E;
|
||||
|
||||
|
@ -74,6 +84,7 @@ macro_rules! generate_event_types {
|
|||
#[doc = stringify!($event)]
|
||||
#[doc = " event."]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct $event;
|
||||
|
||||
impl EventDescriptor for $event {
|
||||
|
|
|
@ -12,13 +12,13 @@ cfg_if! {
|
|||
use std::cell::LazyCell;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
/// Trait alias for the trait bounts on [`IntoElement`].
|
||||
pub trait IntoElementBounds:
|
||||
/// Trait alias for the trait bounts on [`ElementDescriptor`].
|
||||
pub trait ElementDescriptorBounds:
|
||||
fmt::Debug + AsRef<web_sys::HtmlElement> + Clone
|
||||
{
|
||||
}
|
||||
|
||||
impl<El> IntoElementBounds for El where
|
||||
impl<El> ElementDescriptorBounds for El where
|
||||
El: fmt::Debug + AsRef<web_sys::HtmlElement> + Clone
|
||||
{
|
||||
}
|
||||
|
@ -33,10 +33,10 @@ cfg_if! {
|
|||
only in the browser. Please use `leptos::is_server()` or \
|
||||
`leptos::is_browser()` to check where you're running.";
|
||||
|
||||
/// Trait alias for the trait bounts on [`IntoElement`].
|
||||
pub trait IntoElementBounds: fmt::Debug {}
|
||||
/// Trait alias for the trait bounts on [`ElementDescriptor`].
|
||||
pub trait ElementDescriptorBounds: fmt::Debug {}
|
||||
|
||||
impl<El> IntoElementBounds for El where El: fmt::Debug {}
|
||||
impl<El> ElementDescriptorBounds for El where El: fmt::Debug {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,21 +51,17 @@ use leptos_reactive::Scope;
|
|||
use std::{borrow::Cow, fmt};
|
||||
|
||||
/// Trait which allows creating an element tag.
|
||||
pub trait IntoElement: IntoElementBounds {
|
||||
pub trait ElementDescriptor: ElementDescriptorBounds {
|
||||
/// The name of the element, i.e., `div`, `p`, `custom-element`.
|
||||
fn name(&self) -> Cow<'static, str>;
|
||||
|
||||
/// Get a reference to the underlying [`web_sys::HtmlElement`].
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
fn get_element(&self) -> &web_sys::HtmlElement;
|
||||
|
||||
/// Determains if the tag is void, i.e., `<input>` and `<br>`.
|
||||
fn is_void(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// A unique `id` that should be generated for each new instance of
|
||||
/// this element, and be consitant for both SSR and CSR.
|
||||
/// this element, and be consistant for both SSR and CSR.
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
fn hydration_id(&self) -> &HydrationKey;
|
||||
}
|
||||
|
@ -137,16 +133,11 @@ impl std::convert::AsRef<web_sys::HtmlElement> for AnyElement {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoElement for AnyElement {
|
||||
impl ElementDescriptor for AnyElement {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
fn get_element(&self) -> &web_sys::HtmlElement {
|
||||
&self.element
|
||||
}
|
||||
|
||||
fn is_void(&self) -> bool {
|
||||
self.is_void
|
||||
}
|
||||
|
@ -183,16 +174,11 @@ impl std::convert::AsRef<web_sys::HtmlElement> for Custom {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Custom {
|
||||
impl ElementDescriptor for Custom {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
fn get_element(&self) -> &web_sys::HtmlElement {
|
||||
&self.element
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
fn hydration_id(&self) -> &HydrationKey {
|
||||
&self.id
|
||||
|
@ -203,7 +189,7 @@ cfg_if! {
|
|||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
/// Represents an HTML element.
|
||||
#[derive(Clone)]
|
||||
pub struct HtmlElement<El: IntoElement> {
|
||||
pub struct HtmlElement<El: ElementDescriptor> {
|
||||
pub(crate) cx: Scope,
|
||||
pub(crate) element: El,
|
||||
}
|
||||
|
@ -212,7 +198,7 @@ cfg_if! {
|
|||
/// Represents an HTML element.
|
||||
#[derive(educe::Educe, Clone)]
|
||||
#[educe(Debug)]
|
||||
pub struct HtmlElement<El: IntoElement> {
|
||||
pub struct HtmlElement<El: ElementDescriptor> {
|
||||
pub(crate) cx: Scope,
|
||||
pub(crate) element: El,
|
||||
#[educe(Debug(ignore))]
|
||||
|
@ -226,7 +212,10 @@ cfg_if! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<El> std::ops::Deref for HtmlElement<El> where El: IntoElement + std::ops::Deref {
|
||||
impl<El> std::ops::Deref for HtmlElement<El>
|
||||
where
|
||||
El: ElementDescriptor + std::ops::Deref,
|
||||
{
|
||||
type Target = <El as std::ops::Deref>::Target;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -238,7 +227,7 @@ impl<El> std::ops::Deref for HtmlElement<El> where El: IntoElement + std::ops::D
|
|||
}
|
||||
}
|
||||
|
||||
impl<El: IntoElement> HtmlElement<El> {
|
||||
impl<El: ElementDescriptor> HtmlElement<El> {
|
||||
fn new(cx: Scope, element: El) -> Self {
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
|
@ -287,7 +276,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
cx,
|
||||
element: AnyElement {
|
||||
name: element.name(),
|
||||
element: element.get_element().clone(),
|
||||
element: element.as_ref().clone(),
|
||||
is_void: element.is_void(),
|
||||
},
|
||||
}
|
||||
|
@ -324,7 +313,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
{
|
||||
self
|
||||
.element
|
||||
.get_element()
|
||||
.as_ref()
|
||||
.set_attribute(wasm_bindgen::intern("id"), &id)
|
||||
.unwrap();
|
||||
|
||||
|
@ -342,7 +331,10 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
}
|
||||
|
||||
/// Binds the element reference to [`NodeRef`].
|
||||
pub fn node_ref(self, node_ref: &NodeRef<Self>) -> Self where Self: Clone {
|
||||
pub fn node_ref(self, node_ref: &NodeRef<Self>) -> Self
|
||||
where
|
||||
Self: Clone,
|
||||
{
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
node_ref.load(&self);
|
||||
|
||||
|
@ -363,7 +355,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
{
|
||||
let el = self.element.get_element();
|
||||
let el = self.element.as_ref();
|
||||
let value = attr.into_attribute(self.cx);
|
||||
match value {
|
||||
Attribute::Fn(cx, f) => {
|
||||
|
@ -421,7 +413,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
{
|
||||
let el = self.element.get_element();
|
||||
let el = self.element.as_ref();
|
||||
let class_list = el.class_list();
|
||||
let value = class.into_class(self.cx);
|
||||
match value {
|
||||
|
@ -476,7 +468,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
{
|
||||
let name = name.into();
|
||||
let value = value.into_property(self.cx);
|
||||
let el = self.element.get_element();
|
||||
let el = self.element.as_ref();
|
||||
match value {
|
||||
Property::Fn(cx, f) => {
|
||||
let el = el.clone();
|
||||
|
@ -519,14 +511,10 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
let event_name = event.name();
|
||||
|
||||
if event.bubbles() {
|
||||
add_event_listener(
|
||||
self.element.get_element(),
|
||||
event_name,
|
||||
event_handler,
|
||||
);
|
||||
add_event_listener(self.element.as_ref(), event_name, event_handler);
|
||||
} else {
|
||||
add_event_listener_undelegated(
|
||||
self.element.get_element(),
|
||||
self.element.as_ref(),
|
||||
&event_name,
|
||||
event_handler,
|
||||
);
|
||||
|
@ -552,7 +540,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
{
|
||||
if !HydrationCtx::is_hydrating() {
|
||||
mount_child(MountKind::Append(self.element.get_element()), &child);
|
||||
mount_child(MountKind::Append(self.element.as_ref()), &child);
|
||||
}
|
||||
|
||||
self
|
||||
|
@ -569,7 +557,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<El: IntoElement> IntoView for HtmlElement<El> {
|
||||
impl<El: ElementDescriptor> IntoView for HtmlElement<El> {
|
||||
#[cfg_attr(debug_assertions, instrument(level = "trace", name = "<HtmlElement />", skip_all, fields(tag = %self.element.name())))]
|
||||
fn into_view(self, _: Scope) -> View {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -606,7 +594,7 @@ impl<El: IntoElement> IntoView for HtmlElement<El> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<El: IntoElement, const N: usize> IntoView for [HtmlElement<El>; N] {
|
||||
impl<El: ElementDescriptor, const N: usize> IntoView for [HtmlElement<El>; N] {
|
||||
#[cfg_attr(
|
||||
debug_assertions,
|
||||
instrument(level = "trace", name = "[HtmlElement; N]", skip_all)
|
||||
|
@ -618,13 +606,13 @@ impl<El: IntoElement, const N: usize> IntoView for [HtmlElement<El>; N] {
|
|||
}
|
||||
|
||||
/// Creates any custom element, such as `<my-element>`.
|
||||
pub fn custom<El: IntoElement>(cx: Scope, el: El) -> HtmlElement<Custom> {
|
||||
pub fn custom<El: ElementDescriptor>(cx: Scope, el: El) -> HtmlElement<Custom> {
|
||||
HtmlElement::new(
|
||||
cx,
|
||||
Custom {
|
||||
name: el.name(),
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
element: el.get_element().clone(),
|
||||
element: el.as_ref().clone(),
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
id: el.hydration_id().clone(),
|
||||
},
|
||||
|
@ -749,16 +737,11 @@ macro_rules! generate_html_tags {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoElement for [<$tag:camel $($trailing_)?>] {
|
||||
impl ElementDescriptor for [<$tag:camel $($trailing_)?>] {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
stringify!($tag).into()
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
fn get_element(&self) -> &web_sys::HtmlElement {
|
||||
&self.element
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
fn hydration_id(&self) -> &HydrationKey {
|
||||
&self.id
|
||||
|
|
|
@ -22,6 +22,8 @@ mod transparent;
|
|||
use cfg_if::cfg_if;
|
||||
pub use components::*;
|
||||
pub use events::typed as ev;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use events::{add_event_listener, add_event_listener_undelegated};
|
||||
pub use helpers::*;
|
||||
pub use html::*;
|
||||
pub use hydration::{HydrationCtx, HydrationKey};
|
||||
|
@ -36,7 +38,7 @@ use smallvec::SmallVec;
|
|||
pub use ssr::*;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use std::cell::LazyCell;
|
||||
use std::{borrow::Cow, fmt};
|
||||
use std::{borrow::Cow, cell::RefCell, fmt, rc::Rc};
|
||||
pub use transparent::*;
|
||||
pub use wasm_bindgen;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -230,13 +232,13 @@ impl IntoView for Element {
|
|||
|
||||
impl Element {
|
||||
#[track_caller]
|
||||
fn new<El: IntoElement>(el: El) -> Self {
|
||||
fn new<El: ElementDescriptor>(el: El) -> Self {
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
name: el.name(),
|
||||
element: el.get_element().clone(),
|
||||
element: el.as_ref().clone(),
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -499,6 +501,48 @@ impl View {
|
|||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an event listener, analogous to [`HtmlElement::on`].
|
||||
///
|
||||
/// This method will attach an event listener to **all** child
|
||||
/// [`HtmlElement`] children.
|
||||
pub fn on<E: ev::EventDescriptor + 'static>(
|
||||
self,
|
||||
event: E,
|
||||
event_handler: impl FnMut(E::EventType) + 'static,
|
||||
) -> Self {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
match &self {
|
||||
Self::Element(el) => {
|
||||
if event.bubbles() {
|
||||
add_event_listener(&el.element, event.name(), event_handler);
|
||||
} else {
|
||||
add_event_listener_undelegated(
|
||||
&el.element,
|
||||
&event.name(),
|
||||
event_handler,
|
||||
);
|
||||
}
|
||||
}
|
||||
Self::Component(c) => {
|
||||
let event_handler = Rc::new(RefCell::new(event_handler));
|
||||
|
||||
c.children.iter().cloned().for_each(|c| {
|
||||
let event_handler = event_handler.clone();
|
||||
|
||||
c.on(event.clone(), move |e| event_handler.borrow_mut()(e));
|
||||
});
|
||||
}
|
||||
Self::CoreComponent(c) => match c {
|
||||
CoreComponent::DynChild(_) => {}
|
||||
CoreComponent::Each(_) => {}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, instrument)]
|
||||
|
|
Loading…
Reference in New Issue