Merge branch 'leptos_dom_v2' of https://github.com/jquesada2016/leptos into leptos_dom_v2

This commit is contained in:
Greg Johnston 2022-12-04 22:11:00 -05:00
commit 7c298272d3
4 changed files with 250 additions and 61 deletions

View file

@ -33,18 +33,20 @@ features = [
"Text",
# Events we cast to in leptos_macro -- added here so we don't force users to import them
"MouseEvent",
"AnimationEvent",
"ClipboardEvent",
"CompositionEvent",
"DragEvent",
"FocusEvent",
"KeyboardEvent",
"ProgressEvent",
"WheelEvent",
"InputEvent",
"SubmitEvent",
"AnimationEvent",
"KeyboardEvent",
"MouseEvent",
"PointerEvent",
"ProgressEvent",
"SubmitEvent",
"TouchEvent",
"TransitionEvent",
"WheelEvent",
]
[features]

View file

@ -0,0 +1,178 @@
//! Collection of typed events.
use std::{borrow::Cow, marker::PhantomData};
use wasm_bindgen::convert::FromWasmAbi;
use crate::IntoElement;
/// A trait for converting types into [web_sys events](web_sys).
pub trait EventDescriptor {
/// The [`web_sys`] event type, such as [`web_sys::MouseEvent`].
type EventType: FromWasmAbi;
/// The name of the event, such as `click` or `mouseover`.
fn name(&self) -> Cow<'static, str>;
/// Indicates if this event bubbles. For example, `click` bubbles,
/// but `focus` does not.
///
/// If this method returns true, then the event will be delegated globally,
/// otherwise, event listeners will be directly attached to the element.
fn bubbles(&self) -> bool {
true
}
}
/// Overrides the [`EventDescriptor::bubbles`] method to always return
/// `false`, which forces the event to not be globally delegated.
pub struct Undelegated<Ev: EventDescriptor>(Ev);
impl<Ev: EventDescriptor> EventDescriptor for Undelegated<Ev> {
type EventType = Ev::EventType;
fn name(&self) -> Cow<'static, str> {
self.0.name()
}
fn bubbles(&self) -> bool {
false
}
}
/// A custom event.
pub struct Custom<E: FromWasmAbi = web_sys::Event> {
name: Cow<'static, str>,
_event_type: PhantomData<E>,
}
impl<E: FromWasmAbi> EventDescriptor for Custom<E> {
type EventType = E;
fn name(&self) -> Cow<'static, str> {
self.name.clone()
}
}
impl<E: FromWasmAbi> Custom<E> {
/// Creates a custom event type that can be used within
/// [`HtmlElement::on`](crate::HtmlElement::on), for events
/// which are not covered in the [`ev`](crate::ev) module.
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
_event_type: PhantomData,
}
}
}
macro_rules! generate_event_types {
[$([$web_sys_event:ident, [$($event:ident),* $(,)?]]),* $(,)?] => {
paste::paste! {
$(
$(
#[doc = "The "]
#[doc = stringify!([<$event:lower>])]
#[doc = "event."]
pub struct $event;
impl EventDescriptor for $event {
type EventType = web_sys::MouseEvent;
fn name(&self) -> Cow<'static, str> {
concat!("on", stringify!([<$event:lower>])).into()
}
}
)*
)*
}
};
}
generate_event_types![
// ClipboardEvent is unstable
[Event, [Copy, Cut, Paste]],
[
CompositionEvent,
[CompositionEnd, CompositionStart, CompositionUpdate]
],
[KeyboardEvent, [KeyDown, Keypress, Keyup]],
[FocusEvent, [Focus, FocusOut, FocusIn, Blur]],
[FormEvent, [Change, Input, Invalid, Reset, Submit]],
[
MouseEvent,
[
Click,
ContextMenu,
DoubleClick,
DblClick,
Drag,
DragEnd,
DragEnter,
DragExit,
DragLeave,
DragOver,
DragStart,
Drop,
MouseDown,
MouseEnter,
MouseLeave,
MouseMove,
MouseOut,
MouseOver,
MouseUp,
]
],
[ScrollEvent, [Scroll]],
[
PointerEvent,
[
PointerDown,
PointerMove,
PointerUp,
PointerCancel,
GotPointerCapture,
LostPointerCapture,
PointerEnter,
PointerLeave,
PointerOver,
PointerOut,
]
],
[SelectionEvent, [Select]],
[TouchEvent, [TouchCancel, TouchEnd, TouchMove, TouchStart]],
[WheelEvent, [Wheel]],
[
MediaEvent,
[
Abort,
CanPlay,
CanPlayThrough,
DurationChange,
Emptied,
Encrypted,
Ended,
Error,
LoadedData,
LoadedMetadata,
LoadStart,
Pause,
Play,
Playing,
Progress,
RateChange,
Seeked,
Seeking,
Stalled,
Suspend,
TimeUpdate,
VolumeChange,
Waiting,
]
],
[
AnimationEvent,
[AnimationStart, AnimationEnd, AnimationIteration,]
],
[TransitionEvent, [TransitionEnd]],
[ToggleEvent, [Toggle]]
];

View file

@ -1,6 +1,9 @@
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::events::*;
use crate::{components::DynChild, Element, Fragment, IntoNode, Node, Text};
use crate::{
components::DynChild, ev::EventDescriptor, Element, Fragment, IntoNode, Node,
Text,
};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::{mount_child, MountKind};
use cfg_if::cfg_if;
@ -369,31 +372,21 @@ impl<El: IntoElement> HtmlElement<El> {
/// Adds an event listener to this element.
#[track_caller]
pub fn on<E>(self, event_name: impl Into<Cow<'static, str>>, event_handler: impl FnMut(E) + 'static) -> Self
where E: FromWasmAbi + 'static,
{
pub fn on<E: EventDescriptor + 'static>(
self,
event: E,
event_handler: impl FnMut(E::EventType) + 'static,
) -> Self {
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
let event_name = event_name.into();
add_event_listener_undelegated(self.element.get_element(), &event_name, event_handler);
} else {
_ = event_name;
_ = event_handler;
}
}
let event_name = event.name();
self
}
if event.bubbles() {
add_event_listener(self.element.get_element(), event_name, event_handler);
} else {
add_event_listener_undelegated(self.element.get_element(), &event_name, event_handler);
}
/// Adds an event listener to this element, using event delegation.
#[track_caller]
pub fn on_delegated<E>(self, event_name: impl Into<Cow<'static, str>>, event_handler: impl FnMut(E) + 'static) -> Self
where E: FromWasmAbi + 'static,
{
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
let event_name = event_name.into();
add_event_listener(self.element.get_element(), event_name, event_handler);
} else {
_ = event_name;
_ = event_handler;
@ -409,7 +402,7 @@ impl<El: IntoElement> HtmlElement<El> {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
{
let child = child.into_node(self.cx);
mount_child(MountKind::Append(self.element.get_element()), &child)
}
}
@ -576,11 +569,16 @@ use crate::macro_helpers::*;
use leptos_reactive::create_render_effect;
impl<El: IntoElement> HtmlElement<El> {
#[doc(hidden)]
#[track_caller]
pub fn _attr(mut self, cx: Scope, name: impl Into<Cow<'static, str>>, attr: impl IntoAttribute) -> Self {
#[track_caller]
pub fn _attr(
mut self,
cx: Scope,
name: impl Into<Cow<'static, str>>,
attr: impl IntoAttribute,
) -> Self {
let name = name.into();
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
let el = self.element.get_element();
let value = attr.into_attribute(cx);
match value {
@ -597,15 +595,15 @@ impl<El: IntoElement> HtmlElement<El> {
_ => attribute_expression(el, &name, value),
};
self
}
else {
}
else {
let mut attr = attr.into_attribute(cx);
while let Attribute::Fn(f) = attr {
attr = f();
}
match attr {
Attribute::String(value) => self.attr(name, value),
Attribute::Bool(include) => if include {
Attribute::Bool(include) => if include {
self.attr_bool(name)
} else {
self
@ -617,16 +615,21 @@ impl<El: IntoElement> HtmlElement<El> {
}
_ => unreachable!()
}
}
}
}
}
}
#[doc(hidden)]
#[track_caller]
pub fn _class(mut self, cx: Scope, name: impl Into<Cow<'static, str>>, class: impl IntoClass) -> Self {
#[track_caller]
pub fn _class(
mut self,
cx: Scope,
name: impl Into<Cow<'static, str>>,
class: impl IntoClass,
) -> Self {
let name = name.into();
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
let el = self.element.get_element();
let class_list = el.class_list();
let value = class.into_class(cx);
@ -643,22 +646,27 @@ impl<El: IntoElement> HtmlElement<El> {
Class::Value(value) => class_expression(&class_list, &name, value),
};
self
}
else {
}
else {
let mut class = class.into_class(cx);
match class {
Class::Value(include) => self.class_bool(name, include),
Class::Fn(f) => self.class_bool(name, f())
}
}
}
}
}
}
#[doc(hidden)]
#[track_caller]
pub fn _prop(mut self, cx: Scope, name: impl Into<Cow<'static, str>>, value: impl IntoProperty) -> Self {
#[track_caller]
pub fn _prop(
mut self,
cx: Scope,
name: impl Into<Cow<'static, str>>,
value: impl IntoProperty,
) -> Self {
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
let name = name.into();
let value = value.into_property(cx);
let el = self.element.get_element();
@ -687,20 +695,20 @@ impl<El: IntoElement> HtmlElement<El> {
}
}
#[doc(hidden)]
#[track_caller]
pub fn _child(mut self, cx: Scope, child: impl IntoChild) -> Self {
let child = child.into_child(cx);
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
#[doc(hidden)]
#[track_caller]
pub fn _child(mut self, cx: Scope, child: impl IntoChild) -> Self {
let child = child.into_child(cx);
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
mount_child(MountKind::Append(self.element.get_element()), &child.into_node(cx))
}
else {
}
else {
self.children.push(Box::new(move |cx| child.into_node(cx)));
}
}
self
}
}
self
}
}

View file

@ -19,6 +19,7 @@ mod node_ref;
use cfg_if::cfg_if;
pub use components::*;
pub use events::typed as ev;
pub use html::*;
pub use node_ref::*;
pub use logging::*;