mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-01-22 16:49:22 -05:00
improved docs and example
This commit is contained in:
parent
fa4455d7c9
commit
fc4198897c
3 changed files with 167 additions and 70 deletions
|
@ -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::<HtmlDivElement>(&evt)
|
||||
"click from element '{:?}'",
|
||||
event_target::<web_sys::HtmlElement>(&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 {
|
|||
</p>
|
||||
<Show
|
||||
when=move || cond()
|
||||
fallback=move |cx| view! { cx, <div node_ref=element>"Condition false [click me]"</div> }
|
||||
fallback=move |cx| view! { cx,
|
||||
<a node_ref=element href="#">
|
||||
"Condition"
|
||||
<b>" false "</b>
|
||||
"[click me]"
|
||||
</a>
|
||||
}
|
||||
>
|
||||
<div node_ref=element>"Condition true [click me]"</div>
|
||||
<a node_ref=element href="#">
|
||||
"Condition "
|
||||
<b>"true"</b>
|
||||
" [click me]"
|
||||
</a>
|
||||
</Show>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T>` where `T` is the web_sys element,
|
||||
/// * a `Signal<T>` where `T` is the web_sys element,
|
||||
/// * a `Signal<Option<T>>` where `T` is the web_sys element,
|
||||
/// * a `NodeRef`
|
||||
/// into a function. Used for example in [`use_event_listener`].
|
||||
pub enum EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
|
@ -10,6 +17,103 @@ where
|
|||
Dynamic(Signal<Option<T>>),
|
||||
}
|
||||
|
||||
impl<T> Default for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::Static(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Self::Static(t.clone()),
|
||||
Self::Dynamic(s) => Self::Dynamic(*s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalGet<Option<T>> for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
{
|
||||
fn get(&self) -> Option<T> {
|
||||
match self {
|
||||
Self::Static(t) => t.clone(),
|
||||
Self::Dynamic(s) => s.get(),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get(&self) -> Option<Option<T>> {
|
||||
match self {
|
||||
Self::Static(t) => Some(t.clone()),
|
||||
Self::Dynamic(s) => s.try_get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWith<Option<T>> for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
{
|
||||
fn with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::Static(t) => f(t),
|
||||
Self::Dynamic(s) => s.with(f),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
|
||||
match self {
|
||||
Self::Static(t) => Some(f(t)),
|
||||
Self::Dynamic(s) => s.try_with(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWithUntracked<Option<T>> for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
{
|
||||
fn with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::Static(t) => f(t),
|
||||
Self::Dynamic(s) => s.with_untracked(f),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
|
||||
match self {
|
||||
Self::Static(t) => Some(f(t)),
|
||||
Self::Dynamic(s) => s.try_with_untracked(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalGetUntracked<Option<T>> for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
{
|
||||
fn get_untracked(&self) -> Option<T> {
|
||||
match self {
|
||||
Self::Static(t) => t.clone(),
|
||||
Self::Dynamic(s) => s.get_untracked(),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get_untracked(&self) -> Option<Option<T>> {
|
||||
match self {
|
||||
Self::Static(t) => Some(t.clone()),
|
||||
Self::Dynamic(s) => s.try_get_untracked(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<(Scope, T)> for EventTargetMaybeSignal<T>
|
||||
where
|
||||
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||
|
|
|
@ -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<Ev, El, T, F>(
|
||||
|
@ -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<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 = 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<dyn Fn()>);
|
||||
} else {
|
||||
cleanup_prev_el.replace(Box::new(move || {}) as Box<dyn Fn()>);
|
||||
}
|
||||
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<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 = 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<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)
|
||||
}
|
||||
}
|
||||
Box::new(cleanup_fn)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue