Add typed event handlers

This commit is contained in:
Safx 2022-11-23 22:33:50 +09:00
parent 7c79cb1b1f
commit 701a12ab46
3 changed files with 92 additions and 8 deletions

View File

@ -1,6 +1,7 @@
use std::time::Duration;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue, UnwrapThrowExt};
use wasm_bindgen::convert::FromWasmAbi;
use crate::{debug_warn, event_delegation, is_server};
@ -250,12 +251,12 @@ pub fn set_interval(
}
/// Adds an event listener to the target DOM element using implicit event delegation.
pub fn add_event_listener(
pub fn add_event_listener<E>(
target: &web_sys::Element,
event_name: &'static str,
cb: impl FnMut(web_sys::Event) + 'static,
) {
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(web_sys::Event)>).into_js_value();
cb: impl FnMut(E) + 'static,
) where E: FromWasmAbi + 'static {
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
let key = event_delegation::event_delegation_key(event_name);
_ = js_sys::Reflect::set(target, &JsValue::from_str(&key), &cb);
event_delegation::add_event_listener(event_name);

View File

@ -18,8 +18,9 @@ quote = "1"
syn = { version = "1", features = ["full", "parsing", "extra-traits"] }
syn-rsx = "0.9"
uuid = { version = "1", features = ["v4"] }
leptos_dom = { path = "../leptos_dom", version = "0.0.17" }
leptos_reactive = { path = "../leptos_reactive", version = "0.0.17" }
leptos_dom = { path = "../leptos_dom", version = "0.0.18" }
leptos_reactive = { path = "../leptos_reactive", version = "0.0.18" }
lazy_static = "1.4"
[dev-dependencies]
log = "0.4"

View File

@ -1,3 +1,4 @@
use std::collections::HashMap;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, ExprPath};
@ -20,6 +21,79 @@ const NON_BUBBLING_EVENTS: [&str; 11] = [
"loadend",
];
lazy_static::lazy_static! {
// Specialized event type
// https://github.com/yewstack/yew/blob/d422b533ea19a09cddf9b31ecd6cd5e5ce35ce3f/packages/yew/src/html/listener/events.rs
static ref EVENTS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("auxclick", "MouseEvent");
m.insert("click", "MouseEvent");
m.insert("contextmenu", "MouseEvent");
m.insert("dblclick", "MouseEvent");
m.insert("drag", "DragEvent");
m.insert("dragend", "DragEvent");
m.insert("dragenter", "DragEvent");
m.insert("dragexit", "DragEvent");
m.insert("dragleave", "DragEvent");
m.insert("dragover", "DragEvent");
m.insert("dragstart", "DragEvent");
m.insert("drop", "DragEvent");
m.insert("blur", "FocusEvent");
m.insert("focus", "FocusEvent");
m.insert("focusin", "FocusEvent");
m.insert("focusout", "FocusEvent");
m.insert("keydown", "KeyboardEvent");
m.insert("keypress", "KeyboardEvent");
m.insert("keyup", "KeyboardEvent");
m.insert("loadstart", "ProgressEvent");
m.insert("progress", "ProgressEvent");
m.insert("loadend", "ProgressEvent");
m.insert("mousedown", "MouseEvent");
m.insert("mouseenter", "MouseEvent");
m.insert("mouseleave", "MouseEvent");
m.insert("mousemove", "MouseEvent");
m.insert("mouseout", "MouseEvent");
m.insert("mouseover", "MouseEvent");
m.insert("mouseup", "MouseEvent");
m.insert("wheel", "WheelEvent");
m.insert("input", "InputEvent");
m.insert("submit", "SubmitEvent");
m.insert("animationcancel", "AnimationEvent");
m.insert("animationend", "AnimationEvent");
m.insert("animationiteration", "AnimationEvent");
m.insert("animationstart", "AnimationEvent");
m.insert("gotpointercapture", "PointerEvent");
m.insert("lostpointercapture", "PointerEvent");
m.insert("pointercancel", "PointerEvent");
m.insert("pointerdown", "PointerEvent");
m.insert("pointerenter", "PointerEvent");
m.insert("pointerleave", "PointerEvent");
m.insert("pointermove", "PointerEvent");
m.insert("pointerout", "PointerEvent");
m.insert("pointerover", "PointerEvent");
m.insert("pointerup", "PointerEvent");
m.insert("touchcancel", "TouchEvent");
m.insert("touchend", "TouchEvent");
m.insert("transitioncancel", "TransitionEvent");
m.insert("transitionend", "TransitionEvent");
m.insert("transitionrun", "TransitionEvent");
m.insert("transitionstart", "TransitionEvent");
m
};
}
pub(crate) fn render_view(cx: &Ident, nodes: &[Node], mode: Mode) -> TokenStream {
let template_uid = Ident::new(
&format!("TEMPLATE_{}", Uuid::new_v4().simple()),
@ -514,9 +588,13 @@ fn attr_to_tokens(
expressions.push(quote_spanned! {
span => ::leptos::add_event_listener_undelegated(#el_id.unchecked_ref(), #name, #handler);
});
} else if let Some(event_type) = EVENTS.get(&name.as_str()).map(|&e| e.parse::<TokenStream>().unwrap_or_default()) {
expressions.push(quote_spanned! {
span => ::leptos::add_event_listener::<#event_type>(#el_id.unchecked_ref(), #name, #handler);
});
} else {
expressions.push(quote_spanned! {
span => ::leptos::add_event_listener(#el_id.unchecked_ref(), #name, #handler);
span => ::leptos::add_event_listener::<Event>(#el_id.unchecked_ref(), #name, #handler);
});
}
} else {
@ -1033,9 +1111,13 @@ fn create_component(cx: &Ident, node: &NodeElement, mode: Mode) -> TokenStream {
Some(quote_spanned! {
span => ::leptos::add_event_listener_undelegated(#component_name.unchecked_ref(), #event_name, #handler);
})
} else if let Some(event_type) = EVENTS.get(event_name).map(|&e| e.parse::<TokenStream>().unwrap_or_default()) {
Some(quote_spanned! {
span => ::leptos::add_event_listener::<#event_type>(#component_name.unchecked_ref(), #event_name, #handler);
})
} else {
Some(quote_spanned! {
span => ::leptos::add_event_listener(#component_name.unchecked_ref(), #event_name, #handler)
span => ::leptos::add_event_listener::<Event>(#component_name.unchecked_ref(), #event_name, #handler)
})
}
}