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
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::Element;
use yew::prelude::*;
use super::use_raf_state;
use crate::web_sys_ext::{ResizeObserver, ResizeObserverEntry};
#[derive(PartialEq, Default, Clone)]
pub struct UseMeasureState {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
pub top: f64,
pub left: f64,
pub bottom: f64,
pub right: f64,
}
/// A sensor hook that tracks an HTML element's dimensions using the `ResizeObserver` API.
///
/// # Example
///
/// ```rust
/// # use yew::prelude::*;
/// #
/// use yew_hooks::prelude::*;
///
/// #[function_component(UseMeasure)]
/// fn measure() -> Html {
/// let node = use_node_ref();
/// let state = use_measure(node.clone());
///
/// html! {
/// <div ref={node}>
/// <b>{ " X: " }</b>
/// { state.x }
/// <b>{ " Y: " }</b>
/// { state.y }
/// <b>{ " Width: " }</b>
/// { state.width }
/// <b>{ " Height: " }</b>
/// { state.height }
/// <b>{ " Top: " }</b>
/// { state.top }
/// <b>{ " Left: " }</b>
/// { state.left }
/// <b>{ " Bottom: " }</b>
/// { state.bottom }
/// <b>{ " Right: " }</b>
/// { state.right }
/// </div>
/// }
/// }
/// ```
#[hook]
pub fn use_measure(node: NodeRef) -> UseMeasureState {
let state = use_raf_state(UseMeasureState::default);
{
let state = state.clone();
use_effect_with(node, move |node| {
let closure = Closure::wrap(Box::new(move |entries: Vec<ResizeObserverEntry>| {
for entry in &entries {
let rect = entry.content_rect();
state.set(UseMeasureState {
x: rect.x(),
y: rect.y(),
width: rect.width(),
height: rect.height(),
top: rect.top(),
left: rect.left(),
bottom: rect.bottom(),
right: rect.right(),
});
}
}) as Box<dyn Fn(Vec<ResizeObserverEntry>)>);
let observer = ResizeObserver::new(closure.as_ref().unchecked_ref()).unwrap_throw();
// Forget the closure to keep it alive
closure.forget();
if let Some(element) = &node.cast::<Element>() {
observer.observe(element);
}
move || observer.disconnect()
});
}
(*state).clone()
}