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
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{IntersectionObserver, IntersectionObserverEntry};
use yew::prelude::*;
use super::use_effect_once;
/// Check if an element is visible. Internally, it uses an [`IntersectionObserver`] to receive
/// notifications from the browser whenever the visibility state of the node changes.
///
/// Setting the sticky bit makes this hook disconnect the observer once the element is visible, and
/// keep the visibility set to `true`, even when it becomes invisible. This is often desired
/// for lazy-loading components.
///
/// # Example
///
/// ```rust
/// # use yew::prelude::*;
/// #
/// use yew_hooks::prelude::*;
///
/// #[function_component]
/// fn MyComponent() -> Html {
/// let node = use_node_ref();
/// let visible = use_visible(node.clone(), false);
///
/// html! {
/// <div ref={node}>
/// if visible {
/// <p>{"I'm visible!"}</p>
/// } else {
/// <p>{"I'm invisible!"}</p>
/// }
/// </div>
/// }
/// }
/// ```
#[hook]
pub fn use_visible(node: NodeRef, sticky: bool) -> bool {
// code adapted from:
// https://stackoverflow.com/questions/1462138/event-listener-for-when-element-becomes-visible
let visible = use_state_eq(|| false);
let visible_clone = visible.clone();
use_effect_once(move || {
let closure = Closure::<dyn Fn(Vec<IntersectionObserverEntry>, IntersectionObserver)>::new(
move |entries: Vec<IntersectionObserverEntry>, observer: IntersectionObserver| {
// determine if any part of this node is visible.
let visible = entries.iter().any(|entry| entry.intersection_ratio() > 0.0);
// if the visibility changed, update the state.
visible_clone.set(visible);
// if this is sticky and it is currently visible, disconnect the observer.
if visible && sticky {
observer.disconnect();
}
},
)
.into_js_value();
let observer = IntersectionObserver::new(closure.dyn_ref().unwrap()).unwrap();
if let Some(node) = node.get() {
observer.observe(node.dyn_ref().unwrap());
}
move || observer.disconnect()
});
*visible
}