Skip to main content

euv_ui/hook/use_intersection_observer/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`UseIntersectionObserver`].
4impl UseIntersectionObserver {
5    /// Creates a handle with the visibility starting at `false`.
6    ///
7    /// # Returns
8    ///
9    /// - `UseIntersectionObserver` - The fresh handle.
10    pub(crate) fn new() -> Self {
11        Self {
12            target: NodeRef::new(),
13            visible: Signal::create(false),
14            ratio: Signal::create(0.0),
15            teardown: Rc::new(RefCell::new(None)),
16        }
17    }
18
19    /// Returns the current visibility as a snapshot.
20    ///
21    /// # Returns
22    ///
23    /// - `bool` - `true` when the element is intersecting the viewport.
24    pub fn is_visible(&self) -> bool {
25        self.get_visible().get()
26    }
27
28    /// Returns the current visible fraction as a snapshot.
29    ///
30    /// # Returns
31    ///
32    /// - `f64` - The visible fraction, from 0 to 1.
33    pub fn current_ratio(&self) -> f64 {
34        self.get_ratio().get()
35    }
36
37    /// Creates the observer and starts watching the element.
38    ///
39    /// The element does not exist while the first render is being
40    /// built, so the `observe` call is deferred until after the DOM
41    /// has been committed.
42    pub(crate) fn attach(&self) {
43        #[cfg(target_arch = "wasm32")]
44        {
45            let visible: Signal<bool> = self.get_visible();
46            let ratio: Signal<f64> = self.get_ratio();
47            let closure: Closure<dyn FnMut(Array)> =
48                Closure::wrap(Box::new(move |entries: Array| {
49                    for entry in entries.iter() {
50                        let Ok(entry) = entry.dyn_into::<IntersectionObserverEntry>() else {
51                            continue;
52                        };
53                        visible.set(entry.is_intersecting());
54                        ratio.set(entry.intersection_ratio());
55                    }
56                }));
57            let Ok(observer) = IntersectionObserver::new(closure.as_ref().unchecked_ref()) else {
58                return;
59            };
60            let target: NodeRef<Element> = self.get_target().clone();
61            let observed: IntersectionObserver = observer.clone();
62            after_mount(move || {
63                if let Some(element) = target.get_cloned() {
64                    observed.observe(&element);
65                }
66            });
67            let disconnect: Rc<RefCell<Option<IntersectionObserver>>> =
68                Rc::new(RefCell::new(Some(observer)));
69            *self.get_teardown().borrow_mut() = Some(Box::new(move || {
70                if let Some(observer) = disconnect.borrow_mut().take() {
71                    observer.disconnect();
72                }
73            }));
74        }
75    }
76
77    /// Disconnects the observer.
78    pub(crate) fn detach(&self) {
79        let taken: Option<Box<dyn Fn()>> = self.get_teardown().borrow_mut().take();
80        if let Some(teardown) = taken {
81            teardown();
82        }
83    }
84}