dioxus_dnd_kit/
context.rs1use std::rc::Rc;
2
3use dioxus::{
4 html::geometry::{ClientPoint, PixelsRect},
5 prelude::*,
6};
7use dioxus_floating::{OffsetOptions, ScrollableView};
8
9use crate::{
10 DndItem,
11 hooks::{use_body_on_mouse_up, use_window_on_resize},
12 prelude::DraggableOverlay,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq)]
17pub struct DndContext<T: DndItem> {
18 pub render: Callback<T, Element>,
20 pub placeholder_render: Option<Callback<(f64, f64), Element>>,
22 pub active: Signal<Option<T>>,
24 pub active_rect: Signal<Option<PixelsRect>>,
26 pub active_mounted: Signal<Option<Rc<MountedData>>>,
28 pub is_floating_ready: Signal<bool>,
30 pub mouse_pos: Signal<ClientPoint>,
32 pub start_pos: Signal<ClientPoint>,
34 pub grab_offset: Signal<OffsetOptions>,
36 pub key_gen: Callback<T, String>,
38 pub recalculate_rects: Signal<u64>,
40}
41
42impl<T: DndItem> DndContext<T> {
43 fn new(
44 key_gen: Callback<T, String>,
45 render: Callback<T, Element>,
46 placeholder_render: Option<Callback<(f64, f64), Element>>,
47 ) -> Self {
48 Self {
49 key_gen,
50 render,
51 placeholder_render,
52 active: Signal::new(None),
53 active_mounted: Signal::new(None),
54 active_rect: Signal::new(None),
55 is_floating_ready: Signal::new(false),
56 mouse_pos: Signal::new(ClientPoint::zero()),
57 start_pos: Signal::new(ClientPoint::zero()),
58 grab_offset: Signal::new(OffsetOptions::default()),
59 recalculate_rects: Signal::new(0),
60 }
61 }
62}
63
64#[component]
74pub fn DraggableView<T: DndItem>(
75 #[props(default)]
77 class: ReadSignal<String>,
78
79 children: Element,
81
82 key_gen: Callback<T, String>,
84
85 render: Callback<T, Element>,
87
88 placeholder_render: Option<Callback<(f64, f64), Element>>,
91) -> Element {
92 let mut context =
93 use_context_provider(move || DndContext::new(key_gen, render, placeholder_render));
94
95 let mouseup = use_callback(move |_| {
96 context.is_floating_ready.set(false);
97 spawn(async move {
98 gloo_timers::future::TimeoutFuture::new(50).await;
99 context.active.set(None);
100 context.active_mounted.set(None);
101 context.active_rect.set(None);
102 context.start_pos.set(ClientPoint::zero());
103 });
104 });
105 use_body_on_mouse_up(mouseup);
106 use_window_on_resize(use_callback(move |_| {
107 context.recalculate_rects.with_mut(|r| *r += 1);
108 }));
109
110 rsx! {
111 ScrollableView { class,
112 style: if context.active.read().is_some() {
113 "user-select: none; -webkit-user-select: none;"
114 } else {
115 "user-select: auto; -webkit-user-select: auto;"
116 },
117 on_mouse_move: move |e: MouseEvent| {
118 context.mouse_pos.set(e.client_coordinates());
119 },
120 {children}
125 if let Some(active_item) = context.active.read().as_ref() {
126 DraggableOverlay::<T> { item: active_item.clone() }
127 }
128 }
129 }
130}