use dioxus::prelude::*;
use crate::core::{element_point, use_dnd, use_zone_id, Point, ZoneId};
#[derive(Debug, Clone, PartialEq)]
pub struct CanvasDrop<T> {
pub payload: T,
pub position: Point,
pub pointer: Point,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SnapGrid(pub f64);
impl SnapGrid {
pub fn snap(&self, p: Point) -> Point {
if self.0 <= 0.0 {
return p;
}
Point::new(
(p.x / self.0).round() * self.0,
(p.y / self.0).round() * self.0,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Bounds {
pub width: f64,
pub height: f64,
}
impl Bounds {
pub fn clamp(&self, p: Point) -> Point {
Point::new(p.x.clamp(0.0, self.width), p.y.clamp(0.0, self.height))
}
}
#[component]
pub fn CanvasDropZone<T: Clone + PartialEq + 'static>(
#[props(default)]
id: Option<ZoneId>,
#[props(default)]
snap: Option<SnapGrid>,
#[props(default)]
bounds: Option<Bounds>,
on_drop: EventHandler<CanvasDrop<T>>,
#[props(extends = div, extends = GlobalAttributes)] attributes: Vec<Attribute>,
children: Element,
) -> Element {
let mut dnd = use_dnd::<T>();
let auto_id = use_zone_id();
let _zone_id = id.unwrap_or(auto_id);
rsx! {
div {
ondragover: move |evt: DragEvent| {
if dnd.dragging() {
evt.prevent_default();
}
},
ondrop: move |evt: DragEvent| {
evt.prevent_default();
let pointer = element_point(&evt);
let grab = dnd.grab();
if let Some((payload, _)) = dnd.take() {
let mut position = pointer - grab;
if let Some(g) = snap {
position = g.snap(position);
}
if let Some(b) = bounds {
position = b.clamp(position);
}
on_drop.call(CanvasDrop { payload, position, pointer });
}
},
..attributes,
{children}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snap_and_clamp() {
let g = SnapGrid(10.0);
let p = g.snap(Point::new(14.9, 15.1));
assert_eq!((p.x, p.y), (10.0, 20.0));
assert_eq!(
SnapGrid(0.0).snap(Point::new(3.3, 4.4)),
Point::new(3.3, 4.4)
);
let b = Bounds {
width: 100.0,
height: 50.0,
};
let p = b.clamp(Point::new(-5.0, 999.0));
assert_eq!((p.x, p.y), (0.0, 50.0));
}
}