1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::borrow::Cow;
use gloo::events::{EventListener, EventListenerOptions};
use gloo::utils::window;
use wasm_bindgen::JsValue;
use yew::prelude::*;
use super::use_latest;
/// A hook that subscribes a callback to events.
///
/// # Example
///
/// ```rust
/// # use yew::prelude::*;
/// # use log::debug;
/// #
/// use yew_hooks::prelude::*;
///
/// #[function_component(UseEvent)]
/// fn event() -> Html {
/// let button = use_node_ref();
///
/// use_event(button.clone(), "click", move |_: MouseEvent| {
/// debug!("Clicked!");
/// });
///
/// html! {
/// <>
/// <button ref={button}>{ "Click me!" }</button>
/// </>
/// }
/// }
/// ```
#[hook]
pub fn use_event<T, F, E>(node: NodeRef, event_type: T, callback: F)
where
T: Into<Cow<'static, str>>,
F: Fn(E) + 'static,
E: From<JsValue>,
{
let callback = use_latest(callback);
use_effect_with((node, event_type.into()), move |(node, event_type)| {
let window = window();
let node = node.get();
// If we cannot get the wrapped `Node`, then we use `Window` as the default target of the event.
let target = node.as_deref().map_or(&*window, |t| t);
// We should only set passive event listeners for `touchstart` and `touchmove`.
// See here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners
let listener =
if event_type == "touchstart" || event_type == "touchmove" || event_type == "scroll" {
Some(EventListener::new(
target,
event_type.clone(),
move |event| {
(*callback.current())(JsValue::from(event).into());
},
))
} else {
Some(EventListener::new_with_options(
target,
event_type.clone(),
EventListenerOptions::enable_prevent_default(),
move |event| {
(*callback.current())(JsValue::from(event).into());
},
))
};
move || drop(listener)
});
}
/// A hook that subscribes a callback to events only for window.
/// If you want to specify an event target, use [`use_event`].
///
/// # Example
///
/// ```rust
/// # use yew::prelude::*;
/// # use log::debug;
/// #
/// use yew_hooks::prelude::*;
///
/// #[function_component(UseEvent)]
/// fn event() -> Html {
/// use_event_with_window("keypress", move |e: KeyboardEvent| {
/// debug!("{} is pressed!", e.key());
/// });
///
/// html! {
/// <>
/// { "Press any key on your awesome keyboard!" }
/// </>
/// }
/// }
/// ```
#[hook]
pub fn use_event_with_window<T, F, E>(event_type: T, callback: F)
where
T: Into<Cow<'static, str>>,
F: Fn(E) + 'static,
E: From<JsValue>,
{
let callback = use_latest(callback);
use_effect_with(event_type.into(), move |event_type: &Cow<'static, str>| {
let event_type = event_type.clone();
let window = window();
let listener = if &*event_type == "touchstart"
|| &*event_type == "touchmove"
|| &*event_type == "scroll"
{
Some(EventListener::new(&window, event_type, move |event| {
(*callback.current())(JsValue::from(event).into());
}))
} else {
Some(EventListener::new_with_options(
&window,
event_type,
EventListenerOptions::enable_prevent_default(),
move |event| {
(*callback.current())(JsValue::from(event).into());
},
))
};
move || drop(listener)
});
}