Skip to main content

euv_ui/hook/use_media_query/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`UseMediaQuery`].
4impl UseMediaQuery {
5    /// Creates a handle for `query` with the match result starting at
6    /// `false`.
7    ///
8    /// # Arguments
9    ///
10    /// - `&'static str` - The media query text.
11    ///
12    /// # Returns
13    ///
14    /// - `UseMediaQuery` - The fresh handle.
15    pub(crate) fn new(query: &'static str) -> Self {
16        Self {
17            matches: Signal::create(false),
18            query,
19            teardown: Rc::new(RefCell::new(None)),
20        }
21    }
22
23    /// Returns the current match result as a snapshot.
24    ///
25    /// # Returns
26    ///
27    /// - `bool` - `true` when the query currently matches.
28    pub fn is_matching(&self) -> bool {
29        self.get_matches().get()
30    }
31
32    /// Subscribes to the query's `change` event and seeds the match
33    /// result with the browser's current answer.
34    ///
35    /// On a host without a browser this is a no-op and the match result
36    /// stays `false`, so a component that reads the signal renders the
37    /// same way in `cargo test` as it does in a browser that has no
38    /// matching device.
39    pub(crate) fn attach(&self) {
40        #[cfg(target_arch = "wasm32")]
41        {
42            let Some(window) = window() else {
43                return;
44            };
45            let Ok(Some(list)) = window.match_media(self.get_query()) else {
46                return;
47            };
48            let matches: Signal<bool> = self.get_matches();
49            matches.set(list.matches());
50            let watched: MediaQueryList = list.clone();
51            let closure: Closure<dyn FnMut(Event)> =
52                Closure::wrap(Box::new(move |_event: Event| {
53                    matches.set(watched.matches());
54                }));
55            let Ok(()) = list.add_event_listener_with_callback(
56                EVENT_MEDIA_QUERY_CHANGE,
57                closure.as_ref().unchecked_ref(),
58            ) else {
59                return;
60            };
61            *self.get_teardown().borrow_mut() = Some(Box::new(move || {
62                let _: Result<(), JsValue> = list.remove_event_listener_with_callback(
63                    EVENT_MEDIA_QUERY_CHANGE,
64                    closure.as_ref().unchecked_ref(),
65                );
66            }));
67        }
68    }
69
70    /// Removes the `change` listener.
71    pub(crate) fn detach(&self) {
72        let taken: Option<Box<dyn Fn()>> = self.get_teardown().borrow_mut().take();
73        if let Some(teardown) = taken {
74            teardown();
75        }
76    }
77}