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}