use std::rc::Rc;
use dioxus::html::MountedData;
use dioxus::prelude::*;
use crate::a11y::use_reduced_motion_css;
use crate::core::{Point, Rect};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
enum FlipPhase {
#[default]
Rest,
Invert(Point),
}
#[component]
pub fn FlipItem(
epoch: usize,
#[props(default = 200.0)]
duration: f64,
#[props(default = "ease".to_string())]
easing: String,
#[props(extends = div, extends = GlobalAttributes)] attributes: Vec<Attribute>,
children: Element,
) -> Element {
let mounted = use_signal(|| None::<Rc<MountedData>>);
let prev = use_signal(|| None::<Rect>);
let mut phase = use_signal(FlipPhase::default);
use_effect(use_reactive!(|epoch| {
let _ = epoch;
let Some(m) = mounted.peek().clone() else {
return;
};
let mut prev = prev;
spawn(async move {
if let Ok(r) = m.get_client_rect().await {
let now = Rect::new(r.origin.x, r.origin.y, r.size.width, r.size.height);
if let Some(old) = *prev.peek() {
let dx = old.x - now.x;
let dy = old.y - now.y;
if dx != 0.0 || dy != 0.0 {
phase.set(FlipPhase::Invert(Point::new(dx, dy)));
}
}
prev.set(Some(now));
}
});
}));
use_effect(move || {
if matches!(phase(), FlipPhase::Invert(_)) {
phase.set(FlipPhase::Rest);
}
});
let style = match phase() {
FlipPhase::Invert(d) => {
format!(
"transform: translate({}px, {}px); transition: none;",
d.x, d.y
)
}
FlipPhase::Rest => {
format!("transform: none; transition: transform {duration}ms {easing};")
}
};
let reduced_motion_css = use_reduced_motion_css();
rsx! {
{reduced_motion_css}
div {
style: "{style}",
"data-dnd-motion": true,
onmounted: move |evt: Event<MountedData>| {
let mut mounted = mounted;
mounted.set(Some(evt.data()));
},
..attributes,
{children}
}
}
}