1#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
2
3use crate::core::{IntoElementMaybeSignal, Position};
4use crate::{use_event_listener_with_options, use_window, UseEventListenerOptions, UseWindow};
5use default_struct_builder::DefaultBuilder;
6use leptos::ev::{dragover, mousemove, touchend, touchmove, touchstart};
7use leptos::prelude::*;
8use std::convert::Infallible;
9use std::marker::PhantomData;
10use wasm_bindgen::{JsCast, JsValue};
11
12pub fn use_mouse() -> UseMouseReturn {
91 use_mouse_with_options(Default::default())
92}
93
94pub fn use_mouse_with_options<El, M, Ex>(options: UseMouseOptions<El, M, Ex>) -> UseMouseReturn
96where
97 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
98 Ex: UseMouseEventExtractor + Clone + 'static,
99{
100 let (x, set_x) = signal(options.initial_value.x);
101 let (y, set_y) = signal(options.initial_value.y);
102 let (source_type, set_source_type) = signal(UseMouseSourceType::Unset);
103
104 let mouse_handler = {
105 let coord_type = options.coord_type.clone();
106
107 move |event: web_sys::MouseEvent| {
108 let result = coord_type.extract_mouse_coords(&event);
109
110 if let Some((x, y)) = result {
111 set_x.set(x);
112 set_y.set(y);
113 set_source_type.set(UseMouseSourceType::Mouse);
114 }
115 }
116 };
117
118 let drag_handler = {
119 let mouse_handler = mouse_handler.clone();
120
121 move |event: web_sys::DragEvent| {
122 let js_value: &JsValue = event.as_ref();
123 mouse_handler(js_value.clone().unchecked_into::<web_sys::MouseEvent>());
124 }
125 };
126
127 let touch_handler = {
128 let coord_type = options.coord_type.clone();
129
130 move |event: web_sys::TouchEvent| {
131 let touches = event.touches();
132 if touches.length() > 0 {
133 let result = coord_type.extract_touch_coords(
134 &touches
135 .get(0)
136 .expect("Just checked that there's at least on touch"),
137 );
138
139 if let Some((x, y)) = result {
140 set_x.set(x);
141 set_y.set(y);
142 set_source_type.set(UseMouseSourceType::Touch);
143 }
144 }
145 }
146 };
147
148 let initial_value = options.initial_value;
149 let reset = move || {
150 set_x.set(initial_value.x);
151 set_y.set(initial_value.y);
152 };
153
154 #[cfg(not(feature = "ssr"))]
157 {
158 let target = options.target.into_element_maybe_signal();
159 let event_listener_options = UseEventListenerOptions::default().passive(true);
160
161 let _ = use_event_listener_with_options(
162 target,
163 mousemove,
164 mouse_handler,
165 event_listener_options,
166 );
167 let _ =
168 use_event_listener_with_options(target, dragover, drag_handler, event_listener_options);
169
170 if options.touch && !matches!(options.coord_type, UseMouseCoordType::Movement) {
171 let _ = use_event_listener_with_options(
172 target,
173 touchstart,
174 touch_handler.clone(),
175 event_listener_options,
176 );
177 let _ = use_event_listener_with_options(
178 target,
179 touchmove,
180 touch_handler,
181 event_listener_options,
182 );
183 if options.reset_on_touch_ends {
184 let _ = use_event_listener_with_options(
185 target,
186 touchend,
187 move |_| reset(),
188 event_listener_options,
189 );
190 }
191 }
192 }
193
194 UseMouseReturn {
195 x: x.into(),
196 y: y.into(),
197 set_x,
198 set_y,
199 source_type: source_type.into(),
200 }
201}
202
203#[derive(DefaultBuilder)]
204pub struct UseMouseOptions<El, M, Ex>
206where
207 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
208 Ex: UseMouseEventExtractor + Clone,
209{
210 coord_type: UseMouseCoordType<Ex>,
212
213 target: El,
215
216 touch: bool,
218
219 reset_on_touch_ends: bool,
221
222 initial_value: Position,
224
225 #[builder(skip)]
226 _marker: PhantomData<M>,
227}
228
229impl<M> Default for UseMouseOptions<UseWindow, M, Infallible>
230where
231 UseWindow: IntoElementMaybeSignal<web_sys::EventTarget, M>,
232{
233 fn default() -> Self {
234 Self {
235 coord_type: UseMouseCoordType::default(),
236 target: use_window(),
237 touch: true,
238 reset_on_touch_ends: false,
239 initial_value: Position { x: 0.0, y: 0.0 },
240 _marker: PhantomData,
241 }
242 }
243}
244
245#[derive(Clone)]
247pub enum UseMouseCoordType<E: UseMouseEventExtractor + Clone> {
248 Page,
249 Client,
250 Screen,
251 Movement,
252 Custom(E),
253}
254
255impl Default for UseMouseCoordType<Infallible> {
256 fn default() -> Self {
257 Self::Page
258 }
259}
260
261#[allow(unused_variables)]
263pub trait UseMouseEventExtractor {
264 fn extract_mouse_coords(&self, event: &web_sys::MouseEvent) -> Option<(f64, f64)> {
266 None
267 }
268
269 fn extract_touch_coords(&self, touch: &web_sys::Touch) -> Option<(f64, f64)> {
271 None
272 }
273}
274
275impl<E: UseMouseEventExtractor + Clone> UseMouseEventExtractor for UseMouseCoordType<E> {
276 fn extract_mouse_coords(&self, event: &web_sys::MouseEvent) -> Option<(f64, f64)> {
277 match self {
278 UseMouseCoordType::Page => Some((event.page_x() as f64, event.page_y() as f64)),
279 UseMouseCoordType::Client => Some((event.client_x() as f64, event.client_y() as f64)),
280 UseMouseCoordType::Screen => Some((event.screen_x() as f64, event.client_y() as f64)),
281 UseMouseCoordType::Movement => {
282 Some((event.movement_x() as f64, event.movement_y() as f64))
283 }
284 UseMouseCoordType::Custom(ref extractor) => extractor.extract_mouse_coords(event),
285 }
286 }
287
288 fn extract_touch_coords(&self, touch: &web_sys::Touch) -> Option<(f64, f64)> {
289 match self {
290 UseMouseCoordType::Page => Some((touch.page_x() as f64, touch.page_y() as f64)),
291 UseMouseCoordType::Client => Some((touch.client_x() as f64, touch.client_y() as f64)),
292 UseMouseCoordType::Screen => Some((touch.screen_x() as f64, touch.client_y() as f64)),
293 UseMouseCoordType::Movement => None,
294 UseMouseCoordType::Custom(ref extractor) => extractor.extract_touch_coords(touch),
295 }
296 }
297}
298
299impl UseMouseEventExtractor for Infallible {
300 fn extract_mouse_coords(&self, _: &web_sys::MouseEvent) -> Option<(f64, f64)> {
301 unreachable!()
302 }
303
304 fn extract_touch_coords(&self, _: &web_sys::Touch) -> Option<(f64, f64)> {
305 unreachable!()
306 }
307}
308
309pub struct UseMouseReturn {
311 pub x: Signal<f64>,
313 pub y: Signal<f64>,
315 pub set_x: WriteSignal<f64>,
317 pub set_y: WriteSignal<f64>,
319 pub source_type: Signal<UseMouseSourceType>,
321}
322
323#[derive(Copy, Clone, Debug, PartialEq, Eq)]
325pub enum UseMouseSourceType {
326 Mouse,
328 Touch,
330 Unset,
332}