diff --git a/examples/use_event_listener/src/main.rs b/examples/use_event_listener/src/main.rs
index fb24e5d..2af1d59 100644
--- a/examples/use_event_listener/src/main.rs
+++ b/examples/use_event_listener/src/main.rs
@@ -1,17 +1,24 @@
-use leptos::ev::click;
+use leptos::ev::{click, keydown};
+use leptos::html::AnyElement;
use leptos::*;
use leptos_use::use_event_listener;
use web_sys::HtmlDivElement;
#[component]
fn Demo(cx: Scope) -> impl IntoView {
+ let _ = use_event_listener(cx, window(), keydown, |evt| {
+ log!("window keydown: '{}'", evt.key());
+ });
+
let element = create_node_ref(cx);
let _ = use_event_listener(cx, element, click, |evt| {
log!(
- "click from element {:?}",
- event_target::(&evt)
+ "click from element '{:?}'",
+ event_target::(&evt).inner_text()
);
+ evt.stop_propagation();
+ evt.prevent_default();
});
let (cond, set_cond) = create_signal(cx, true);
@@ -29,9 +36,19 @@ fn Demo(cx: Scope) -> impl IntoView {
"Condition false [click me]" }
+ fallback=move |cx| view! { cx,
+
+ "Condition"
+ " false "
+ "[click me]"
+
+ }
>
- "Condition true [click me]"
+
+ "Condition "
+ "true"
+ " [click me]"
+
}
}
diff --git a/src/core/event_target_maybe_signal.rs b/src/core/event_target_maybe_signal.rs
index 8ef20e3..7a15f7a 100644
--- a/src/core/event_target_maybe_signal.rs
+++ b/src/core/event_target_maybe_signal.rs
@@ -2,6 +2,13 @@ use leptos::html::ElementDescriptor;
use leptos::*;
use std::ops::Deref;
+/// Used as an argument type to make it easily possible to pass either
+/// * a `web_sys` element that implements `EventTarget`,
+/// * an `Option` where `T` is the web_sys element,
+/// * a `Signal` where `T` is the web_sys element,
+/// * a `Signal>` where `T` is the web_sys element,
+/// * a `NodeRef`
+/// into a function. Used for example in [`use_event_listener`].
pub enum EventTargetMaybeSignal
where
T: Into + Clone + 'static,
@@ -10,6 +17,103 @@ where
Dynamic(Signal>),
}
+impl Default for EventTargetMaybeSignal
+where
+ T: Into + Clone + 'static,
+{
+ fn default() -> Self {
+ Self::Static(None)
+ }
+}
+
+impl Clone for EventTargetMaybeSignal
+where
+ T: Into + Clone + 'static,
+{
+ fn clone(&self) -> Self {
+ match self {
+ Self::Static(t) => Self::Static(t.clone()),
+ Self::Dynamic(s) => Self::Dynamic(*s),
+ }
+ }
+}
+
+impl SignalGet> for EventTargetMaybeSignal
+where
+ T: Into + Clone + 'static,
+{
+ fn get(&self) -> Option {
+ match self {
+ Self::Static(t) => t.clone(),
+ Self::Dynamic(s) => s.get(),
+ }
+ }
+
+ fn try_get(&self) -> Option> {
+ match self {
+ Self::Static(t) => Some(t.clone()),
+ Self::Dynamic(s) => s.try_get(),
+ }
+ }
+}
+
+impl SignalWith> for EventTargetMaybeSignal
+where
+ T: Into + Clone + 'static,
+{
+ fn with(&self, f: impl FnOnce(&Option) -> O) -> O {
+ match self {
+ Self::Static(t) => f(t),
+ Self::Dynamic(s) => s.with(f),
+ }
+ }
+
+ fn try_with(&self, f: impl FnOnce(&Option) -> O) -> Option {
+ match self {
+ Self::Static(t) => Some(f(t)),
+ Self::Dynamic(s) => s.try_with(f),
+ }
+ }
+}
+
+impl SignalWithUntracked> for EventTargetMaybeSignal
+where
+ T: Into + Clone + 'static,
+{
+ fn with_untracked(&self, f: impl FnOnce(&Option) -> O) -> O {
+ match self {
+ Self::Static(t) => f(t),
+ Self::Dynamic(s) => s.with_untracked(f),
+ }
+ }
+
+ fn try_with_untracked(&self, f: impl FnOnce(&Option) -> O) -> Option {
+ match self {
+ Self::Static(t) => Some(f(t)),
+ Self::Dynamic(s) => s.try_with_untracked(f),
+ }
+ }
+}
+
+impl SignalGetUntracked> for EventTargetMaybeSignal
+where
+ T: Into + Clone + 'static,
+{
+ fn get_untracked(&self) -> Option {
+ match self {
+ Self::Static(t) => t.clone(),
+ Self::Dynamic(s) => s.get_untracked(),
+ }
+ }
+
+ fn try_get_untracked(&self) -> Option> {
+ match self {
+ Self::Static(t) => Some(t.clone()),
+ Self::Dynamic(s) => s.try_get_untracked(),
+ }
+ }
+}
+
impl From<(Scope, T)> for EventTargetMaybeSignal
where
T: Into + Clone + 'static,
diff --git a/src/use_event_listener.rs b/src/use_event_listener.rs
index e5918a8..196d832 100644
--- a/src/use_event_listener.rs
+++ b/src/use_event_listener.rs
@@ -28,7 +28,7 @@ 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
+/// You can also pass a [`leptos::NodeRef`] as the event target, `use_event_listener` will unregister the previous event and register
/// the new one when you change the target.
///
/// ```
@@ -79,7 +79,7 @@ use wasm_bindgen::JsCast;
///
/// Note if your components also run in SSR (Server Side Rendering), you might get errors
/// because DOM APIs like document and window are not available outside of the browser.
-/// To avoid that you can put the logic inside a [`create_effect`](leptos::create_effect) hook
+/// To avoid that you can put the logic inside a [`leptos::create_effect`] hook
/// which only runs client side.
#[allow(unused_must_use)]
pub fn use_event_listener(
@@ -106,72 +106,48 @@ where
let event_name = event.name();
- match (cx, target).into() {
- EventTargetMaybeSignal::Static(element) => {
- if let Some(element) = element {
- let element = element.into();
- _ = element.add_event_listener_with_callback(
- &event_name,
- closure_js.as_ref().unchecked_ref(),
- );
+ let signal = (cx, target).into();
- let cleanup_fn = move || {
- cleanup(&element);
- };
- on_cleanup(cx, cleanup_fn.clone());
+ let element = signal.get_untracked();
- Box::new(cleanup_fn)
- } else {
- Box::new(|| {})
- }
+ let cleanup_prev_element = if let Some(element) = element {
+ let element = element.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 = signal.get();
+
+ if let Some(element) = element {
+ let element = element.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);
}
- EventTargetMaybeSignal::Dynamic(signal) => {
- let element = signal.get_untracked();
+ });
- let cleanup_prev_element = if let Some(element) = element {
- let element = element.into();
+ let cleanup_fn = move || cleanup_prev_element.borrow()();
+ on_cleanup(cx, cleanup_fn.clone());
- _ = 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 = signal();
-
- if let Some(element) = element {
- let element = element.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)
- }
- }
+ Box::new(cleanup_fn)
}