Skip to main content

euv_ui/hook/use_reduced_motion/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`UseReducedMotion`].
4impl UseReducedMotion {
5    /// Creates a handle whose answer starts at `false`.
6    ///
7    /// # Returns
8    ///
9    /// - `UseReducedMotion` - The fresh handle.
10    pub(crate) fn new() -> Self {
11        Self {
12            reduced: Signal::create(false),
13            query: REDUCED_MOTION_QUERY,
14            teardown: Rc::new(RefCell::new(None)),
15        }
16    }
17
18    /// Returns the current answer as a snapshot.
19    ///
20    /// # Returns
21    ///
22    /// - `bool` - `true` when the user asked for reduced motion.
23    pub fn is_reduced(&self) -> bool {
24        self.get_reduced().get()
25    }
26
27    /// Subscribes to the query's `change` event and seeds the answer
28    /// with the browser's current one.
29    ///
30    /// On a host without a browser this is a no-op and the answer stays
31    /// `false`.
32    pub(crate) fn attach(&self) {
33        #[cfg(target_arch = "wasm32")]
34        {
35            let Some(window) = window() else {
36                return;
37            };
38            let Ok(Some(list)) = window.match_media(self.get_query()) else {
39                return;
40            };
41            let reduced: Signal<bool> = self.get_reduced();
42            reduced.set(list.matches());
43            let watched: MediaQueryList = list.clone();
44            let closure: Closure<dyn FnMut(Event)> =
45                Closure::wrap(Box::new(move |_event: Event| {
46                    reduced.set(watched.matches());
47                }));
48            let Ok(()) = list.add_event_listener_with_callback(
49                EVENT_REDUCED_MOTION_CHANGE,
50                closure.as_ref().unchecked_ref(),
51            ) else {
52                return;
53            };
54            *self.get_teardown().borrow_mut() = Some(Box::new(move || {
55                let _: Result<(), JsValue> = list.remove_event_listener_with_callback(
56                    EVENT_REDUCED_MOTION_CHANGE,
57                    closure.as_ref().unchecked_ref(),
58                );
59            }));
60        }
61    }
62
63    /// Removes the `change` listener.
64    pub(crate) fn detach(&self) {
65        let taken: Option<Box<dyn Fn()>> = self.get_teardown().borrow_mut().take();
66        if let Some(teardown) = taken {
67            teardown();
68        }
69    }
70}