mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-01-22 16:49:22 -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]
|
||||
leptos = "0.2"
|
||||
leptos = "0.3"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen = "0.2"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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::<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_scroll;
|
||||
|
||||
pub use use_event_listener::*;
|
||||
pub use use_scroll::*;
|
||||
|
|
|
@ -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::<web_sys::HtmlDivElement>(&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<Ev, El, Inner, F>(
|
||||
pub fn use_event_listener<Ev, El, T, F>(
|
||||
cx: Scope,
|
||||
target: El,
|
||||
event: Ev,
|
||||
|
@ -89,8 +90,8 @@ pub fn use_event_listener<Ev, El, Inner, F>(
|
|||
) -> Box<dyn Fn()>
|
||||
where
|
||||
Ev: EventDescriptor + 'static,
|
||||
El: Into<MaybeSignal<Option<Inner>>>,
|
||||
Inner: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
(Scope, El): Into<EventTargetMaybeSignal<T>>,
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
F: FnMut(<Ev as EventDescriptor>::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<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