Skip to main content

logicaffeine_web/ui/hooks/
use_media.rs

1//! Whether the page matches a media query, kept current as the window changes.
2
3use dioxus::prelude::*;
4
5/// Whether the window matches any of `queries` (named in [`crate::ui::responsive::media`]), updated
6/// whenever that changes for as long as the calling component is mounted. False where there is no
7/// window (the server render), which draws the desktop.
8pub fn use_media(queries: &'static [&'static str]) -> Signal<bool> {
9    #[cfg(target_arch = "wasm32")]
10    {
11        use std::rc::Rc;
12        use wasm_bindgen::prelude::*;
13        use wasm_bindgen::JsCast;
14
15        fn list(queries: &[&str]) -> Option<web_sys::MediaQueryList> {
16            web_sys::window()?.match_media(&queries.join(", ")).ok()?
17        }
18
19        let mut matches = use_signal(|| list(queries).is_some_and(|l| l.matches()));
20        let watched = use_hook(move || {
21            let query = list(queries);
22            let on_change = Closure::<dyn FnMut()>::new(move || {
23                if let Some(now) = list(queries).map(|l| l.matches()) {
24                    if *matches.peek() != now {
25                        matches.set(now);
26                    }
27                }
28            });
29            if let Some(query) = &query {
30                let _ = query.add_event_listener_with_callback("change", on_change.as_ref().unchecked_ref());
31            }
32            Rc::new((query, on_change))
33        });
34        use_drop(move || {
35            if let (Some(query), on_change) = (&watched.0, &watched.1) {
36                let _ = query.remove_event_listener_with_callback("change", on_change.as_ref().unchecked_ref());
37            }
38        });
39        matches
40    }
41    #[cfg(not(target_arch = "wasm32"))]
42    {
43        let _ = queries;
44        use_signal(|| false)
45    }
46}