From fa4455d7c93c9086f6cfb18f08ffe5e0bfb31133 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Sun, 14 May 2023 22:45:38 +0100 Subject: [PATCH] more ergonomic use_event_listener --- Cargo.toml | 2 +- examples/use_event_listener/Cargo.toml | 2 +- examples/use_event_listener/src/main.rs | 4 +- src/core/event_target_maybe_signal.rs | 84 ++++++++++++++++++++++ src/core/mod.rs | 3 + src/lib.rs | 3 + src/use_event_listener.rs | 94 ++++--------------------- 7 files changed, 106 insertions(+), 86 deletions(-) create mode 100644 src/core/event_target_maybe_signal.rs create mode 100644 src/core/mod.rs diff --git a/Cargo.toml b/Cargo.toml index f41ea7e..deec265 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,6 @@ repository = "https://github.com/Synphonyte/leptos-use" [dependencies] -leptos = "0.2" +leptos = "0.3" web-sys = "0.3" wasm-bindgen = "0.2" diff --git a/examples/use_event_listener/Cargo.toml b/examples/use_event_listener/Cargo.toml index 26b59e5..cb4011a 100644 --- a/examples/use_event_listener/Cargo.toml +++ b/examples/use_event_listener/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = "0.2" +leptos = "0.3" console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_event_listener/src/main.rs b/examples/use_event_listener/src/main.rs index 4ef345f..fb24e5d 100644 --- a/examples/use_event_listener/src/main.rs +++ b/examples/use_event_listener/src/main.rs @@ -1,13 +1,13 @@ use leptos::ev::click; use leptos::*; -use leptos_use::use_event_listener_ref; +use leptos_use::use_event_listener; use web_sys::HtmlDivElement; #[component] fn Demo(cx: Scope) -> impl IntoView { let element = create_node_ref(cx); - let _ = use_event_listener_ref(cx, element, click, |evt| { + let _ = use_event_listener(cx, element, click, |evt| { log!( "click from element {:?}", event_target::(&evt) diff --git a/src/core/event_target_maybe_signal.rs b/src/core/event_target_maybe_signal.rs new file mode 100644 index 0000000..8ef20e3 --- /dev/null +++ b/src/core/event_target_maybe_signal.rs @@ -0,0 +1,84 @@ +use leptos::html::ElementDescriptor; +use leptos::*; +use std::ops::Deref; + +pub enum EventTargetMaybeSignal +where + T: Into + Clone + 'static, +{ + Static(Option), + Dynamic(Signal>), +} + +impl From<(Scope, T)> for EventTargetMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(value: (Scope, T)) -> Self { + EventTargetMaybeSignal::Static(Some(value.1)) + } +} + +impl From<(Scope, Option)> for EventTargetMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(target: (Scope, Option)) -> Self { + EventTargetMaybeSignal::Static(target.1) + } +} + +macro_rules! impl_from_signal_option { + ($ty:ty) => { + impl From<(Scope, $ty)> for EventTargetMaybeSignal + where + T: Into + Clone + 'static, + { + fn from(target: (Scope, $ty)) -> Self { + EventTargetMaybeSignal::Dynamic(target.1.into()) + } + } + }; +} + +impl_from_signal_option!(Signal>); +impl_from_signal_option!(ReadSignal>); +impl_from_signal_option!(RwSignal>); +impl_from_signal_option!(Memo>); + +macro_rules! impl_from_signal { + ($ty:ty) => { + impl From<(Scope, $ty)> for EventTargetMaybeSignal + where + T: Into + Clone + 'static, + { + fn from(target: (Scope, $ty)) -> Self { + let (cx, signal) = target; + + EventTargetMaybeSignal::Dynamic(Signal::derive(cx, move || Some(signal.get()))) + } + } + }; +} + +impl_from_signal!(Signal); +impl_from_signal!(ReadSignal); +impl_from_signal!(RwSignal); +impl_from_signal!(Memo); + +impl From<(Scope, NodeRef)> for EventTargetMaybeSignal +where + R: ElementDescriptor + Clone + 'static, +{ + fn from(target: (Scope, NodeRef)) -> Self { + let (cx, node_ref) = target; + + EventTargetMaybeSignal::Dynamic(Signal::derive(cx, move || { + node_ref.get().map(move |el| { + let el = el.into_any(); + let el: web_sys::EventTarget = el.deref().clone().into(); + el + }) + })) + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..c70dc1d --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,3 @@ +mod event_target_maybe_signal; + +pub use event_target_maybe_signal::*; diff --git a/src/lib.rs b/src/lib.rs index 700bd93..ad91616 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +pub mod core; mod use_event_listener; +mod use_scroll; pub use use_event_listener::*; +pub use use_scroll::*; diff --git a/src/use_event_listener.rs b/src/use_event_listener.rs index 0961af9..e5918a8 100644 --- a/src/use_event_listener.rs +++ b/src/use_event_listener.rs @@ -1,3 +1,4 @@ +use crate::core::EventTargetMaybeSignal; use leptos::ev::EventDescriptor; use leptos::html::ElementDescriptor; use leptos::*; @@ -19,7 +20,7 @@ use wasm_bindgen::JsCast; /// /// #[component] /// fn Demo(cx: Scope) -> impl IntoView { -/// use_event_listener(cx, Some(document()), visibilitychange, |evt| { +/// use_event_listener(cx, document(), visibilitychange, |evt| { /// log!("{:?}", evt); /// }); /// @@ -28,18 +29,18 @@ use wasm_bindgen::JsCast; /// ``` /// /// You can also pass a [NodeRef](leptos::NodeRef) as the event target, `use_event_listener` will unregister the previous event and register -/// the new one when you change the target. (For now you have to use `use_event_listener_ref` to do that) +/// the new one when you change the target. /// /// ``` /// use leptos::*; /// use leptos::ev::click; -/// use leptos_use::use_event_listener_ref; +/// use leptos_use::use_event_listener; /// /// #[component] /// fn Demo(cx: Scope) -> impl IntoView { /// let element = create_node_ref(cx); /// -/// use_event_listener_ref(cx, element, click, |evt| { +/// use_event_listener(cx, element, click, |evt| { /// log!("click from element {:?}", event_target::(&evt)); /// }); /// @@ -66,7 +67,7 @@ use wasm_bindgen::JsCast; /// # /// # #[component] /// # fn Demo(cx: Scope) -> impl IntoView { -/// let cleanup = use_event_listener(cx, Some(document()), keydown, |evt: KeyboardEvent| { +/// let cleanup = use_event_listener(cx, document().body(), keydown, |evt: KeyboardEvent| { /// log!("{}", &evt.key()); /// }); /// @@ -81,7 +82,7 @@ use wasm_bindgen::JsCast; /// To avoid that you can put the logic inside a [`create_effect`](leptos::create_effect) hook /// which only runs client side. #[allow(unused_must_use)] -pub fn use_event_listener( +pub fn use_event_listener( cx: Scope, target: El, event: Ev, @@ -89,8 +90,8 @@ pub fn use_event_listener( ) -> Box where Ev: EventDescriptor + 'static, - El: Into>>, - Inner: Into + Clone + 'static, + (Scope, El): Into>, + T: Into + Clone + 'static, F: FnMut(::EventType) + 'static, { let event_name = event.name(); @@ -105,8 +106,8 @@ where let event_name = event.name(); - match target.into() { - MaybeSignal::Static(element) => { + match (cx, target).into() { + EventTargetMaybeSignal::Static(element) => { if let Some(element) = element { let element = element.into(); _ = element.add_event_listener_with_callback( @@ -124,7 +125,7 @@ where Box::new(|| {}) } } - MaybeSignal::Dynamic(signal) => { + EventTargetMaybeSignal::Dynamic(signal) => { let element = signal.get_untracked(); let cleanup_prev_element = if let Some(element) = element { @@ -174,74 +175,3 @@ where } } } - -/// Version for using with [NodeRef](leptos::NodeRef). See [use_event_listener] for how to use. -#[allow(unused_must_use)] -pub fn use_event_listener_ref( - cx: Scope, - target: NodeRef, - event: Ev, - handler: F, -) -> Box -where - Ev: EventDescriptor + 'static, - El: ElementDescriptor + Clone, - F: FnMut(::EventType) + 'static, -{ - let event_name = event.name(); - let closure_js = Closure::wrap(Box::new(handler) as Box).into_js_value(); - - let closure = closure_js.clone(); - let cleanup_fn = move |element: &web_sys::EventTarget| { - let _ = element - .remove_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref()); - }; - let cleanup = cleanup_fn.clone(); - - let event_name = event.name(); - - let element = target.get(); - - let cleanup_prev_element = if let Some(element) = element { - let element = element.into_any(); - let element: web_sys::EventTarget = element.deref().clone().into(); - - _ = element - .add_event_listener_with_callback(&event_name, closure_js.as_ref().unchecked_ref()); - - let clean = cleanup.clone(); - Rc::new(RefCell::new(Box::new(move || { - clean(&element); - }) as Box)) - } else { - Rc::new(RefCell::new(Box::new(move || {}) as Box)) - }; - - let cleanup_prev_el = Rc::clone(&cleanup_prev_element); - let closure = closure_js.clone(); - create_effect(cx, move |_| { - cleanup_prev_el.borrow()(); - - let element = target.get(); - - if let Some(element) = element { - let element = element.into_any(); - let element: web_sys::EventTarget = element.deref().clone().into(); - - _ = element - .add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref()); - - let clean = cleanup.clone(); - cleanup_prev_el.replace(Box::new(move || { - clean(&element); - }) as Box); - } else { - cleanup_prev_el.replace(Box::new(move || {}) as Box); - } - }); - - let cleanup_fn = move || cleanup_prev_element.borrow()(); - on_cleanup(cx, cleanup_fn.clone()); - - Box::new(cleanup_fn) -}