1use dioxus::prelude::*;
6
7use crate::core::{element_point, use_dnd, use_zone_id, Point, ZoneId};
8
9#[derive(Debug, Clone, PartialEq)]
11pub struct CanvasDrop<T> {
12 pub payload: T,
13 pub position: Point,
16 pub pointer: Point,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct SnapGrid(pub f64);
23
24impl SnapGrid {
25 pub fn snap(&self, p: Point) -> Point {
26 if self.0 <= 0.0 {
27 return p;
28 }
29 Point::new(
30 (p.x / self.0).round() * self.0,
31 (p.y / self.0).round() * self.0,
32 )
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq)]
38pub struct Bounds {
39 pub width: f64,
40 pub height: f64,
41}
42
43impl Bounds {
44 pub fn clamp(&self, p: Point) -> Point {
45 Point::new(p.x.clamp(0.0, self.width), p.y.clamp(0.0, self.height))
46 }
47}
48
49#[component]
55pub fn CanvasDropZone<T: Clone + PartialEq + 'static>(
56 #[props(default)]
58 id: Option<ZoneId>,
59 #[props(default)]
61 snap: Option<SnapGrid>,
62 #[props(default)]
64 bounds: Option<Bounds>,
65 on_drop: EventHandler<CanvasDrop<T>>,
66 #[props(extends = div, extends = GlobalAttributes)] attributes: Vec<Attribute>,
67 children: Element,
68) -> Element {
69 let mut dnd = use_dnd::<T>();
70 let auto_id = use_zone_id();
71 let _zone_id = id.unwrap_or(auto_id);
72
73 rsx! {
74 div {
75 ondragover: move |evt: DragEvent| {
76 if dnd.dragging() {
77 evt.prevent_default();
78 }
79 },
80 ondrop: move |evt: DragEvent| {
81 evt.prevent_default();
82 let pointer = element_point(&evt);
83 let grab = dnd.grab();
84 if let Some((payload, _)) = dnd.take() {
85 let mut position = pointer - grab;
86 if let Some(g) = snap {
87 position = g.snap(position);
88 }
89 if let Some(b) = bounds {
90 position = b.clamp(position);
91 }
92 on_drop.call(CanvasDrop { payload, position, pointer });
93 }
94 },
95 ..attributes,
96 {children}
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn snap_and_clamp() {
107 let g = SnapGrid(10.0);
108 let p = g.snap(Point::new(14.9, 15.1));
109 assert_eq!((p.x, p.y), (10.0, 20.0));
110 assert_eq!(
111 SnapGrid(0.0).snap(Point::new(3.3, 4.4)),
112 Point::new(3.3, 4.4)
113 );
114
115 let b = Bounds {
116 width: 100.0,
117 height: 50.0,
118 };
119 let p = b.clamp(Point::new(-5.0, 999.0));
120 assert_eq!((p.x, p.y), (0.0, 50.0));
121 }
122}