use std::rc::Rc;
use dioxus::{
html::geometry::{ClientPoint, PixelsRect},
prelude::*,
};
use dioxus_floating::{OffsetOptions, ScrollableView};
use crate::{DndItem, hooks::use_body_on_mouse_up, prelude::DraggableOverlay};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DndContext<T: DndItem> {
pub render: Callback<T, Element>,
pub placeholder_render: Option<Callback<(f64, f64), Element>>,
pub active: Signal<Option<T>>,
pub active_rect: Signal<Option<PixelsRect>>,
pub active_mounted: Signal<Option<Rc<MountedData>>>,
pub is_floating_ready: Signal<bool>,
pub mouse_pos: Signal<ClientPoint>,
pub start_pos: Signal<ClientPoint>,
pub grab_offset: Signal<OffsetOptions>,
pub key_gen: Callback<T, String>,
pub recalculate_rects: Signal<u64>,
}
impl<T: DndItem> DndContext<T> {
fn new(
key_gen: Callback<T, String>,
render: Callback<T, Element>,
placeholder_render: Option<Callback<(f64, f64), Element>>,
) -> Self {
Self {
key_gen,
render,
placeholder_render,
active: Signal::new(None),
active_mounted: Signal::new(None),
active_rect: Signal::new(None),
is_floating_ready: Signal::new(false),
mouse_pos: Signal::new(ClientPoint::zero()),
start_pos: Signal::new(ClientPoint::zero()),
grab_offset: Signal::new(OffsetOptions::default()),
recalculate_rects: Signal::new(0),
}
}
}
#[component]
pub fn DraggableView<T: DndItem>(
#[props(default)]
class: ReadSignal<String>,
children: Element,
key_gen: Callback<T, String>,
render: Callback<T, Element>,
placeholder_render: Option<Callback<(f64, f64), Element>>,
) -> Element {
let mut context =
use_context_provider(move || DndContext::new(key_gen, render, placeholder_render));
let mouseup = use_callback(move |_| {
context.is_floating_ready.set(false);
spawn(async move {
gloo_timers::future::TimeoutFuture::new(50).await;
context.active.set(None);
context.active_mounted.set(None);
context.active_rect.set(None);
context.start_pos.set(ClientPoint::zero());
});
});
use_body_on_mouse_up(mouseup);
rsx! {
ScrollableView { class,
style: if context.active.read().is_some() {
"user-select: none; -webkit-user-select: none;"
} else {
"user-select: auto; -webkit-user-select: auto;"
},
on_mouse_move: move |e: MouseEvent| {
context.mouse_pos.set(e.client_coordinates());
},
{children}
if let Some(active_item) = context.active.read().as_ref() {
DraggableOverlay::<T> { item: active_item.clone() }
}
}
}
}