yew_hooks/hooks/
use_size.rs

1use wasm_bindgen::{prelude::*, JsCast};
2use web_sys::Element;
3use yew::prelude::*;
4
5use super::use_raf_state;
6use crate::web_sys_ext::{ResizeObserver, ResizeObserverEntry};
7
8/// A sensor hook that tracks an HTML element's dimensions using the `ResizeObserver` API.
9///
10/// # Example
11///
12/// ```rust
13/// # use yew::prelude::*;
14/// #
15/// use yew_hooks::prelude::*;
16///
17/// #[function_component(UseSize)]
18/// fn size() -> Html {
19///     let node =  use_node_ref();
20///     let state = use_size(node.clone());
21///     
22///     html! {
23///         <div ref={node}>
24///             <b>{ " Width: " }</b>
25///             { state.0 }
26///             <b>{ " Height: " }</b>
27///             { state.1 }
28///         </div>
29///     }
30/// }
31/// ```
32#[hook]
33pub fn use_size(node: NodeRef) -> (u32, u32) {
34    let state = use_raf_state(|| (0, 0));
35
36    {
37        let state = state.clone();
38        use_effect_with(node, move |node| {
39            let closure = Closure::wrap(Box::new(move |entries: Vec<ResizeObserverEntry>| {
40                for entry in &entries {
41                    let element = entry.target();
42                    state.set((
43                        element.client_width() as u32,
44                        element.client_height() as u32,
45                    ));
46                }
47            }) as Box<dyn Fn(Vec<ResizeObserverEntry>)>);
48
49            let observer = ResizeObserver::new(closure.as_ref().unchecked_ref()).unwrap_throw();
50            // Forget the closure to keep it alive
51            closure.forget();
52
53            if let Some(element) = &node.cast::<Element>() {
54                observer.observe(element);
55            }
56
57            move || observer.disconnect()
58        });
59    }
60
61    *state
62}