dioxus_use_gesture/
lib.rs1use dioxus::prelude::*;
2use std::{mem, rc::Rc};
3use wasm_bindgen::{prelude::Closure, JsCast};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum DragState {
7 Move,
8 End,
9}
10
11#[derive(Default)]
12struct State {
13 mounted: Option<Rc<MountedData>>,
14 on_pointer_down: Option<Closure<dyn FnMut(web_sys::PointerEvent)>>,
15 on_pointer_move: Option<Closure<dyn FnMut(web_sys::PointerEvent)>>,
16 on_pointer_up: Option<Closure<dyn FnMut(web_sys::PointerEvent)>>,
17 start: Option<(f32, f32)>,
18}
19
20pub fn use_drag<'a, T>(cx: Scope<'a, T>, f: impl FnMut(DragState, f32, f32) + 'a) -> UseDrag {
21 let state_ref = use_ref(cx, || State::default());
22
23 let handler_cell: Rc<RefCell<dyn FnMut(DragState, f32, f32) + 'a>> = Rc::new(RefCell::new(f));
24 let handler_cell: Rc<RefCell<dyn FnMut(DragState, f32, f32) + 'static>> =
25 unsafe { mem::transmute(handler_cell) };
26
27 let state_ref_clone = state_ref.clone();
28 use_effect(
29 cx,
30 &state_ref.read().mounted.is_some(),
31 move |_| async move {
32 let mut state = state_ref_clone.write();
33 if let Some(mounted) = state.mounted.clone() {
34 let element = mounted
35 .get_raw_element()
36 .unwrap()
37 .downcast_ref::<web_sys::Element>()
38 .unwrap();
39
40 let callback_state_ref = state_ref_clone.clone();
41 let handler_cell_clone = handler_cell.clone();
42 state.on_pointer_move = Some(Closure::new(move |event: web_sys::PointerEvent| {
43 if let Some((start_x, start_y)) = callback_state_ref.read().start {
44 handler_cell_clone.borrow_mut()(
45 DragState::Move,
46 event.client_x() as f32 - start_x,
47 event.client_y() as f32 - start_y,
48 );
49 }
50 }));
51 element
52 .add_event_listener_with_callback(
53 "pointermove",
54 state
55 .on_pointer_move
56 .as_ref()
57 .unwrap()
58 .as_ref()
59 .unchecked_ref(),
60 )
61 .unwrap();
62
63 let callback_state_ref = state_ref_clone.clone();
64 let callback_mounted = mounted.clone();
65 state.on_pointer_down = Some(Closure::new(move |event: web_sys::PointerEvent| {
66 let element = callback_mounted
67 .get_raw_element()
68 .unwrap()
69 .downcast_ref::<web_sys::Element>()
70 .unwrap();
71 let rect = element.get_bounding_client_rect();
72
73 callback_state_ref.write().start = Some((
74 event.client_x() as f32 - rect.left() as f32,
75 event.client_y() as f32 - rect.top() as f32,
76 ));
77 }));
78 element
79 .add_event_listener_with_callback(
80 "pointerdown",
81 state
82 .on_pointer_down
83 .as_ref()
84 .unwrap()
85 .as_ref()
86 .unchecked_ref(),
87 )
88 .unwrap();
89
90 let callback_state_ref = state_ref_clone.clone();
91 state.on_pointer_up = Some(Closure::new(move |event: web_sys::PointerEvent| {
92 handler_cell.borrow_mut()(
93 DragState::End,
94 event.client_x() as _,
95 event.client_y() as _,
96 );
97 callback_state_ref.write().start.take();
98 }));
99 element
100 .add_event_listener_with_callback(
101 "pointerup",
102 state
103 .on_pointer_up
104 .as_ref()
105 .unwrap()
106 .as_ref()
107 .unchecked_ref(),
108 )
109 .unwrap();
110 }
111 },
112 );
113
114 let state_ref_clone = state_ref.clone();
115 use_on_unmount(cx, move || {
116 let mut state = state_ref_clone.write();
117 if let Some(mounted) = state.mounted.clone() {
118 let element = mounted
119 .get_raw_element()
120 .unwrap()
121 .downcast_ref::<web_sys::Element>()
122 .unwrap();
123
124 remove_listener(element, "pointerdown", &mut state.on_pointer_down);
125 remove_listener(element, "pointermove", &mut state.on_pointer_move);
126 remove_listener(element, "pointerup", &mut state.on_pointer_up);
127 }
128 });
129
130 UseDrag {
131 element_ref: state_ref.clone(),
132 }
133}
134
135fn remove_listener(
136 element: &web_sys::Element,
137 name: &str,
138 cell: &mut Option<Closure<dyn FnMut(web_sys::PointerEvent)>>,
139) {
140 if let Some(f) = cell.take() {
141 element
142 .remove_event_listener_with_callback(name, f.as_ref().unchecked_ref())
143 .unwrap();
144 }
145}
146
147pub struct UseDrag {
148 element_ref: UseRef<State>,
149}
150
151impl UseDrag {
152 pub fn mount(&self, data: Rc<MountedData>) {
153 self.element_ref.write().mounted = Some(data);
154 }
155}