Skip to main content

logicaffeine_web/ui/hooks/
use_viewport_width.rs

1//! The page's width, kept current as the window resizes.
2
3use dioxus::prelude::*;
4
5/// The width assumed where there is no window to measure (the server render).
6pub const UNMEASURED_WIDTH: f64 = 1440.0;
7
8/// The document's client width in CSS pixels (the viewport without its scrollbar), updated
9/// on every window resize for as long as the calling component is mounted.
10pub fn use_viewport_width() -> Signal<f64> {
11    #[cfg(target_arch = "wasm32")]
12    {
13        use std::rc::Rc;
14        use wasm_bindgen::prelude::*;
15        use wasm_bindgen::JsCast;
16
17        fn measure() -> Option<f64> {
18            Some(web_sys::window()?.document()?.document_element()?.client_width() as f64)
19        }
20
21        let mut width = use_signal(|| measure().unwrap_or(UNMEASURED_WIDTH));
22        let on_resize = use_hook(move || {
23            let on_resize = Closure::<dyn FnMut()>::new(move || {
24                if let Some(w) = measure() {
25                    width.set(w);
26                }
27            });
28            if let Some(window) = web_sys::window() {
29                let _ = window.add_event_listener_with_callback("resize", on_resize.as_ref().unchecked_ref());
30            }
31            Rc::new(on_resize)
32        });
33        use_drop(move || {
34            if let Some(window) = web_sys::window() {
35                let _ = window.remove_event_listener_with_callback("resize", (*on_resize).as_ref().unchecked_ref());
36            }
37        });
38        width
39    }
40    #[cfg(not(target_arch = "wasm32"))]
41    {
42        use_signal(|| UNMEASURED_WIDTH)
43    }
44}