mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-02-02 10:54:15 -05:00
more ergonomic use_event_listener
This commit is contained in:
parent
b72acc8f65
commit
fa4455d7c9
7 changed files with 106 additions and 86 deletions
|
@ -13,6 +13,6 @@ repository = "https://github.com/Synphonyte/leptos-use"
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
leptos = "0.2"
|
leptos = "0.3"
|
||||||
web-sys = "0.3"
|
web-sys = "0.3"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
leptos = "0.2"
|
leptos = "0.3"
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
console_log = "1"
|
console_log = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use leptos::ev::click;
|
use leptos::ev::click;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_use::use_event_listener_ref;
|
use leptos_use::use_event_listener;
|
||||||
use web_sys::HtmlDivElement;
|
use web_sys::HtmlDivElement;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Demo(cx: Scope) -> impl IntoView {
|
fn Demo(cx: Scope) -> impl IntoView {
|
||||||
let element = create_node_ref(cx);
|
let element = create_node_ref(cx);
|
||||||
|
|
||||||
let _ = use_event_listener_ref(cx, element, click, |evt| {
|
let _ = use_event_listener(cx, element, click, |evt| {
|
||||||
log!(
|
log!(
|
||||||
"click from element {:?}",
|
"click from element {:?}",
|
||||||
event_target::<HtmlDivElement>(&evt)
|
event_target::<HtmlDivElement>(&evt)
|
||||||
|
|
84
src/core/event_target_maybe_signal.rs
Normal file
84
src/core/event_target_maybe_signal.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use leptos::html::ElementDescriptor;
|
||||||
|
use leptos::*;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
pub enum EventTargetMaybeSignal<T>
|
||||||
|
where
|
||||||
|
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||||
|
{
|
||||||
|
Static(Option<T>),
|
||||||
|
Dynamic(Signal<Option<T>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<(Scope, T)> for EventTargetMaybeSignal<T>
|
||||||
|
where
|
||||||
|
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||||
|
{
|
||||||
|
fn from(value: (Scope, T)) -> Self {
|
||||||
|
EventTargetMaybeSignal::Static(Some(value.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<(Scope, Option<T>)> for EventTargetMaybeSignal<T>
|
||||||
|
where
|
||||||
|
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||||
|
{
|
||||||
|
fn from(target: (Scope, Option<T>)) -> Self {
|
||||||
|
EventTargetMaybeSignal::Static(target.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_from_signal_option {
|
||||||
|
($ty:ty) => {
|
||||||
|
impl<T> From<(Scope, $ty)> for EventTargetMaybeSignal<T>
|
||||||
|
where
|
||||||
|
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||||
|
{
|
||||||
|
fn from(target: (Scope, $ty)) -> Self {
|
||||||
|
EventTargetMaybeSignal::Dynamic(target.1.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_signal_option!(Signal<Option<T>>);
|
||||||
|
impl_from_signal_option!(ReadSignal<Option<T>>);
|
||||||
|
impl_from_signal_option!(RwSignal<Option<T>>);
|
||||||
|
impl_from_signal_option!(Memo<Option<T>>);
|
||||||
|
|
||||||
|
macro_rules! impl_from_signal {
|
||||||
|
($ty:ty) => {
|
||||||
|
impl<T> From<(Scope, $ty)> for EventTargetMaybeSignal<T>
|
||||||
|
where
|
||||||
|
T: Into<web_sys::EventTarget> + 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<T>);
|
||||||
|
impl_from_signal!(ReadSignal<T>);
|
||||||
|
impl_from_signal!(RwSignal<T>);
|
||||||
|
impl_from_signal!(Memo<T>);
|
||||||
|
|
||||||
|
impl<R> From<(Scope, NodeRef<R>)> for EventTargetMaybeSignal<web_sys::EventTarget>
|
||||||
|
where
|
||||||
|
R: ElementDescriptor + Clone + 'static,
|
||||||
|
{
|
||||||
|
fn from(target: (Scope, NodeRef<R>)) -> 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
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
3
src/core/mod.rs
Normal file
3
src/core/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod event_target_maybe_signal;
|
||||||
|
|
||||||
|
pub use event_target_maybe_signal::*;
|
|
@ -1,3 +1,6 @@
|
||||||
|
pub mod core;
|
||||||
mod use_event_listener;
|
mod use_event_listener;
|
||||||
|
mod use_scroll;
|
||||||
|
|
||||||
pub use use_event_listener::*;
|
pub use use_event_listener::*;
|
||||||
|
pub use use_scroll::*;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::core::EventTargetMaybeSignal;
|
||||||
use leptos::ev::EventDescriptor;
|
use leptos::ev::EventDescriptor;
|
||||||
use leptos::html::ElementDescriptor;
|
use leptos::html::ElementDescriptor;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -19,7 +20,7 @@ use wasm_bindgen::JsCast;
|
||||||
///
|
///
|
||||||
/// #[component]
|
/// #[component]
|
||||||
/// fn Demo(cx: Scope) -> impl IntoView {
|
/// fn Demo(cx: Scope) -> impl IntoView {
|
||||||
/// use_event_listener(cx, Some(document()), visibilitychange, |evt| {
|
/// use_event_listener(cx, document(), visibilitychange, |evt| {
|
||||||
/// log!("{:?}", 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
|
/// 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::*;
|
||||||
/// use leptos::ev::click;
|
/// use leptos::ev::click;
|
||||||
/// use leptos_use::use_event_listener_ref;
|
/// use leptos_use::use_event_listener;
|
||||||
///
|
///
|
||||||
/// #[component]
|
/// #[component]
|
||||||
/// fn Demo(cx: Scope) -> impl IntoView {
|
/// fn Demo(cx: Scope) -> impl IntoView {
|
||||||
/// let element = create_node_ref(cx);
|
/// 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::<web_sys::HtmlDivElement>(&evt));
|
/// log!("click from element {:?}", event_target::<web_sys::HtmlDivElement>(&evt));
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
|
@ -66,7 +67,7 @@ use wasm_bindgen::JsCast;
|
||||||
/// #
|
/// #
|
||||||
/// # #[component]
|
/// # #[component]
|
||||||
/// # fn Demo(cx: Scope) -> impl IntoView {
|
/// # 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());
|
/// 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
|
/// To avoid that you can put the logic inside a [`create_effect`](leptos::create_effect) hook
|
||||||
/// which only runs client side.
|
/// which only runs client side.
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
pub fn use_event_listener<Ev, El, Inner, F>(
|
pub fn use_event_listener<Ev, El, T, F>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
target: El,
|
target: El,
|
||||||
event: Ev,
|
event: Ev,
|
||||||
|
@ -89,8 +90,8 @@ pub fn use_event_listener<Ev, El, Inner, F>(
|
||||||
) -> Box<dyn Fn()>
|
) -> Box<dyn Fn()>
|
||||||
where
|
where
|
||||||
Ev: EventDescriptor + 'static,
|
Ev: EventDescriptor + 'static,
|
||||||
El: Into<MaybeSignal<Option<Inner>>>,
|
(Scope, El): Into<EventTargetMaybeSignal<T>>,
|
||||||
Inner: Into<web_sys::EventTarget> + Clone + 'static,
|
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||||
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
|
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
|
||||||
{
|
{
|
||||||
let event_name = event.name();
|
let event_name = event.name();
|
||||||
|
@ -105,8 +106,8 @@ where
|
||||||
|
|
||||||
let event_name = event.name();
|
let event_name = event.name();
|
||||||
|
|
||||||
match target.into() {
|
match (cx, target).into() {
|
||||||
MaybeSignal::Static(element) => {
|
EventTargetMaybeSignal::Static(element) => {
|
||||||
if let Some(element) = element {
|
if let Some(element) = element {
|
||||||
let element = element.into();
|
let element = element.into();
|
||||||
_ = element.add_event_listener_with_callback(
|
_ = element.add_event_listener_with_callback(
|
||||||
|
@ -124,7 +125,7 @@ where
|
||||||
Box::new(|| {})
|
Box::new(|| {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaybeSignal::Dynamic(signal) => {
|
EventTargetMaybeSignal::Dynamic(signal) => {
|
||||||
let element = signal.get_untracked();
|
let element = signal.get_untracked();
|
||||||
|
|
||||||
let cleanup_prev_element = if let Some(element) = element {
|
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<Ev, El, F>(
|
|
||||||
cx: Scope,
|
|
||||||
target: NodeRef<El>,
|
|
||||||
event: Ev,
|
|
||||||
handler: F,
|
|
||||||
) -> Box<dyn Fn()>
|
|
||||||
where
|
|
||||||
Ev: EventDescriptor + 'static,
|
|
||||||
El: ElementDescriptor + Clone,
|
|
||||||
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
|
|
||||||
{
|
|
||||||
let event_name = event.name();
|
|
||||||
let closure_js = Closure::wrap(Box::new(handler) as Box<dyn FnMut(_)>).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<dyn Fn()>))
|
|
||||||
} else {
|
|
||||||
Rc::new(RefCell::new(Box::new(move || {}) as Box<dyn Fn()>))
|
|
||||||
};
|
|
||||||
|
|
||||||
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<dyn Fn()>);
|
|
||||||
} else {
|
|
||||||
cleanup_prev_el.replace(Box::new(move || {}) as Box<dyn Fn()>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let cleanup_fn = move || cleanup_prev_element.borrow()();
|
|
||||||
on_cleanup(cx, cleanup_fn.clone());
|
|
||||||
|
|
||||||
Box::new(cleanup_fn)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue