dioxus_html/events/
visible.rs

1use std::{
2    fmt::{Display, Formatter},
3    time::SystemTime,
4};
5
6pub struct VisibleData {
7    inner: Box<dyn HasVisibleData>,
8}
9
10impl<E: HasVisibleData> From<E> for VisibleData {
11    fn from(e: E) -> Self {
12        Self { inner: Box::new(e) }
13    }
14}
15
16impl VisibleData {
17    /// Create a new VisibleData
18    pub fn new(inner: impl HasVisibleData + 'static) -> Self {
19        Self {
20            inner: Box::new(inner),
21        }
22    }
23
24    /// Get the bounds rectangle of the target element
25    pub fn get_bounding_client_rect(&self) -> VisibleResult<PixelsRect> {
26        self.inner.get_bounding_client_rect()
27    }
28
29    /// Get the ratio of the intersectionRect to the boundingClientRect
30    pub fn get_intersection_ratio(&self) -> VisibleResult<f64> {
31        self.inner.get_intersection_ratio()
32    }
33
34    /// Get the rect representing the target's visible area
35    pub fn get_intersection_rect(&self) -> VisibleResult<PixelsRect> {
36        self.inner.get_intersection_rect()
37    }
38
39    /// Get if the target element intersects with the intersection observer's root
40    pub fn is_intersecting(&self) -> VisibleResult<bool> {
41        self.inner.is_intersecting()
42    }
43
44    /// Get the rect for the intersection observer's root
45    pub fn get_root_bounds(&self) -> VisibleResult<PixelsRect> {
46        self.inner.get_root_bounds()
47    }
48
49    /// Get a timestamp indicating the time at which the intersection was recorded
50    pub fn get_time(&self) -> VisibleResult<SystemTime> {
51        self.inner.get_time()
52    }
53
54    /// Downcast this event to a concrete event type
55    pub fn downcast<T: 'static>(&self) -> Option<&T> {
56        self.inner.as_any().downcast_ref::<T>()
57    }
58}
59
60impl std::fmt::Debug for VisibleData {
61    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
62        f.debug_struct("VisibleData")
63            .field(
64                "bounding_client_rect",
65                &self.inner.get_bounding_client_rect(),
66            )
67            .field("intersection_ratio", &self.inner.get_intersection_ratio())
68            .field("intersection_rect", &self.inner.get_intersection_rect())
69            .field("is_intersecting", &self.inner.is_intersecting())
70            .field("root_bounds", &self.inner.get_root_bounds())
71            .field("time", &self.inner.get_time())
72            .finish()
73    }
74}
75
76impl PartialEq for VisibleData {
77    fn eq(&self, _: &Self) -> bool {
78        true
79    }
80}
81
82#[cfg(feature = "serialize")]
83#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
84pub struct DOMRect {
85    bottom: f64, // The bottom coordinate value of the DOMRect (usually the same as y + height)
86    height: f64, // The height of the DOMRect
87    left: f64,   // The left coordinate value of the DOMRect (usually the same as x)
88    right: f64,  // The right coordinate value of the DOMRect (usually the same as x + width)
89    top: f64,    // The top coordinate value of the DOMRect (usually the same as y)
90    width: f64,  // The width of the DOMRect
91    x: f64,      // The x coordinate of the DOMRect's origin
92    y: f64,      // The y coordinate of the DOMRect's origin
93}
94
95#[cfg(feature = "serialize")]
96impl From<PixelsRect> for DOMRect {
97    fn from(rect: PixelsRect) -> Self {
98        let x = rect.origin.x;
99        let y = rect.origin.y;
100        let height = rect.height();
101        let width = rect.width();
102
103        Self {
104            bottom: y + height,
105            height,
106            left: x,
107            right: x + width,
108            top: y,
109            width,
110            x,
111            y,
112        }
113    }
114}
115
116#[cfg(feature = "serialize")]
117impl From<&DOMRect> for PixelsRect {
118    fn from(rect: &DOMRect) -> Self {
119        PixelsRect::new(
120            euclid::Point2D::new(rect.x, rect.y),
121            euclid::Size2D::new(rect.width, rect.height),
122        )
123    }
124}
125
126#[cfg(feature = "serialize")]
127/// A serialized version of VisibleData
128#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
129pub struct SerializedVisibleData {
130    pub bounding_client_rect: DOMRect,
131    pub intersection_ratio: f64,
132    pub intersection_rect: DOMRect,
133    pub is_intersecting: bool,
134    pub root_bounds: DOMRect,
135    pub time_ms: u128,
136}
137
138#[cfg(feature = "serialize")]
139impl SerializedVisibleData {
140    /// Create a new SerializedVisibleData
141    pub fn new(
142        bounding_client_rect: DOMRect,
143        intersection_ratio: f64,
144        intersection_rect: DOMRect,
145        is_intersecting: bool,
146        root_bounds: DOMRect,
147        time_ms: u128,
148    ) -> Self {
149        Self {
150            bounding_client_rect,
151            intersection_ratio,
152            intersection_rect,
153            is_intersecting,
154            root_bounds,
155            time_ms,
156        }
157    }
158}
159
160#[cfg(feature = "serialize")]
161impl From<&VisibleData> for SerializedVisibleData {
162    fn from(data: &VisibleData) -> Self {
163        Self::new(
164            data.get_bounding_client_rect().unwrap().into(),
165            data.get_intersection_ratio().unwrap(),
166            data.get_intersection_rect().unwrap().into(),
167            data.is_intersecting().unwrap(),
168            data.get_root_bounds().unwrap().into(),
169            data.get_time().unwrap().elapsed().unwrap().as_millis(),
170        )
171    }
172}
173
174#[cfg(feature = "serialize")]
175impl HasVisibleData for SerializedVisibleData {
176    /// Get the bounds rectangle of the target element
177    fn get_bounding_client_rect(&self) -> VisibleResult<PixelsRect> {
178        Ok((&self.bounding_client_rect).into())
179    }
180
181    /// Get the ratio of the intersectionRect to the boundingClientRect
182    fn get_intersection_ratio(&self) -> VisibleResult<f64> {
183        Ok(self.intersection_ratio)
184    }
185
186    /// Get the rect representing the target's visible area
187    fn get_intersection_rect(&self) -> VisibleResult<PixelsRect> {
188        Ok((&self.intersection_rect).into())
189    }
190
191    /// Get if the target element intersects with the intersection observer's root
192    fn is_intersecting(&self) -> VisibleResult<bool> {
193        Ok(self.is_intersecting)
194    }
195
196    /// Get the rect for the intersection observer's root
197    fn get_root_bounds(&self) -> VisibleResult<PixelsRect> {
198        Ok((&self.root_bounds).into())
199    }
200
201    /// Get a timestamp indicating the time at which the intersection was recorded
202    fn get_time(&self) -> VisibleResult<SystemTime> {
203        Ok(SystemTime::UNIX_EPOCH + std::time::Duration::from_millis(self.time_ms as u64))
204    }
205
206    fn as_any(&self) -> &dyn std::any::Any {
207        self
208    }
209}
210
211#[cfg(feature = "serialize")]
212impl serde::Serialize for VisibleData {
213    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
214        SerializedVisibleData::from(self).serialize(serializer)
215    }
216}
217
218#[cfg(feature = "serialize")]
219impl<'de> serde::Deserialize<'de> for VisibleData {
220    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
221        let data = SerializedVisibleData::deserialize(deserializer)?;
222        Ok(Self {
223            inner: Box::new(data),
224        })
225    }
226}
227
228pub trait HasVisibleData: std::any::Any {
229    /// Get the bounds rectangle of the target element
230    fn get_bounding_client_rect(&self) -> VisibleResult<PixelsRect> {
231        Err(VisibleError::NotSupported)
232    }
233
234    /// Get the ratio of the intersectionRect to the boundingClientRect
235    fn get_intersection_ratio(&self) -> VisibleResult<f64> {
236        Err(VisibleError::NotSupported)
237    }
238
239    /// Get the rect representing the target's visible area
240    fn get_intersection_rect(&self) -> VisibleResult<PixelsRect> {
241        Err(VisibleError::NotSupported)
242    }
243
244    /// Get if the target element intersects with the intersection observer's root
245    fn is_intersecting(&self) -> VisibleResult<bool> {
246        Err(VisibleError::NotSupported)
247    }
248
249    /// Get the rect for the intersection observer's root
250    fn get_root_bounds(&self) -> VisibleResult<PixelsRect> {
251        Err(VisibleError::NotSupported)
252    }
253
254    /// Get a timestamp indicating the time at which the intersection was recorded
255    fn get_time(&self) -> VisibleResult<SystemTime> {
256        Err(VisibleError::NotSupported)
257    }
258
259    /// return self as Any
260    fn as_any(&self) -> &dyn std::any::Any;
261}
262
263use dioxus_core::Event;
264
265use crate::geometry::PixelsRect;
266
267pub type VisibleEvent = Event<VisibleData>;
268
269impl_event! {
270    VisibleData;
271
272    /// onvisible
273    onvisible
274}
275
276/// The VisibleResult type for the VisibleData
277pub type VisibleResult<T> = Result<T, VisibleError>;
278
279#[derive(Debug)]
280/// The error type for the VisibleData
281#[non_exhaustive]
282pub enum VisibleError {
283    /// The renderer does not support the requested operation
284    NotSupported,
285    /// The element was not found
286    OperationFailed(Box<dyn std::error::Error>),
287    /// The target element had no associated ElementId
288    NoElementId,
289}
290
291impl Display for VisibleError {
292    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
293        match self {
294            VisibleError::NotSupported => {
295                write!(f, "The renderer does not support the requested operation")
296            }
297            VisibleError::OperationFailed(e) => {
298                write!(f, "The operation failed: {}", e)
299            }
300            VisibleError::NoElementId => {
301                write!(f, "The target had no associated ElementId")
302            }
303        }
304    }
305}
306
307impl std::error::Error for VisibleError {}