Skip to content

Commit

Permalink
Adds use_visible hook (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
xfbs authored Mar 1, 2024
1 parent e31debd commit db2b9c4
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 16 deletions.
34 changes: 18 additions & 16 deletions crates/yew-hooks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,33 @@ js-sys = "0.3"
version = "0.3"
features = [
"BeforeUnloadEvent",
"Element",
"UrlSearchParams",
"WebSocket",
"MessageEvent",
"CloseEvent",
"BinaryType",
"DomRectReadOnly",
"Navigator",
"Geolocation",
"Blob",
"CloseEvent",
"Coordinates",
"Position",
"PositionError",
"PositionOptions",
"File",
"DataTransfer",
"DataTransferItem",
"DataTransferItemList",
"DomRectReadOnly",
"Element",
"File",
"Geolocation",
"HtmlCollection",
"HtmlLinkElement",
"HtmlMediaElement",
"IntersectionObserver",
"IntersectionObserverEntry",
"MessageEvent",
"Navigator",
"Position",
"PositionError",
"PositionOptions",
"StorageEvent",
"TimeRanges",
"Touch",
"TouchList",
"HtmlLinkElement",
"HtmlCollection",
"Blob",
"StorageEvent",
"UrlSearchParams",
"WebSocket",
]

[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions crates/yew-hooks/src/hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ mod use_title;
mod use_toggle;
mod use_unmount;
mod use_update;
mod use_visible;
mod use_websocket;
mod use_window_scroll;
mod use_window_size;
Expand Down Expand Up @@ -104,6 +105,7 @@ pub use use_title::*;
pub use use_toggle::*;
pub use use_unmount::*;
pub use use_update::*;
pub use use_visible::*;
pub use use_websocket::*;
pub use use_window_scroll::*;
pub use use_window_size::*;
69 changes: 69 additions & 0 deletions crates/yew-hooks/src/hooks/use_visible.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use wasm_bindgen::{
closure::Closure,
JsCast,
};
use web_sys::{IntersectionObserver, IntersectionObserverEntry};
use yew::{NodeRef, functional::*};
use crate::use_effect_once;


#[hook]
/// 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::use_visible;
///
/// #[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>
/// }
/// }
/// ```
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.
if (visible != *visible_clone) && (!sticky || !*visible_clone) {
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
}

0 comments on commit db2b9c4

Please sign in to comment.