1use crate::core::{IntoElementMaybeSignal, Position};
2use crate::{
3 UseMouseCoordType, UseMouseEventExtractor, UseMouseOptions, UseMouseReturn, UseMouseSourceType,
4 UseWindow, use_mouse_with_options, use_window,
5};
6use default_struct_builder::DefaultBuilder;
7use leptos::prelude::*;
8use std::convert::Infallible;
9use std::marker::PhantomData;
10
11pub fn use_mouse_in_element<El, M>(
49 target: El,
50) -> UseMouseInElementReturn<impl Fn() + Clone + Send + Sync>
51where
52 El: IntoElementMaybeSignal<web_sys::Element, M>,
53{
54 use_mouse_in_element_with_options(target, Default::default())
55}
56
57pub fn use_mouse_in_element_with_options<El, M, OptEl, OptM, OptEx>(
59 target: El,
60 options: UseMouseInElementOptions<OptEl, OptM, OptEx>,
61) -> UseMouseInElementReturn<impl Fn() + Clone + Send + Sync>
62where
63 El: IntoElementMaybeSignal<web_sys::Element, M>,
64 OptEl: IntoElementMaybeSignal<web_sys::EventTarget, OptM>,
65 OptEx: UseMouseEventExtractor + Clone + 'static,
66{
67 let UseMouseInElementOptions {
68 coord_type,
69 target: use_mouse_target,
70 touch,
71 reset_on_touch_ends,
72 initial_value,
73 handle_outside,
74 ..
75 } = options;
76
77 let UseMouseReturn {
78 x, y, source_type, ..
79 } = use_mouse_with_options(
80 UseMouseOptions::default()
81 .coord_type(coord_type)
82 .target(use_mouse_target)
83 .touch(touch)
84 .reset_on_touch_ends(reset_on_touch_ends)
85 .initial_value(initial_value),
86 );
87
88 let (element_x, set_element_x) = signal(0.0);
89 let (element_y, set_element_y) = signal(0.0);
90 let (element_position_x, set_element_position_x) = signal(0.0);
91 let (element_position_y, set_element_position_y) = signal(0.0);
92 let (element_width, set_element_width) = signal(0.0);
93 let (element_height, set_element_height) = signal(0.0);
94 let (is_outside, set_outside) = signal(true);
95
96 let stop;
97
98 #[cfg(feature = "ssr")]
99 {
100 stop = || ();
101
102 let _ = handle_outside;
103
104 let _ = set_element_x;
105 let _ = set_element_y;
106 let _ = set_element_position_x;
107 let _ = set_element_position_y;
108 let _ = set_element_width;
109 let _ = set_element_height;
110 let _ = set_outside;
111 let _ = target;
112 }
113
114 #[cfg(not(feature = "ssr"))]
115 {
116 use crate::{sendwrap_fn, use_event_listener};
117 use leptos::ev::mouseleave;
118
119 let target = target.into_element_maybe_signal();
120 let window = window();
121
122 let effect = Effect::watch(
123 move || (target.get(), x.get(), y.get()),
124 move |(el, x, y), _, _| {
125 if let Some(el) = el {
126 let el = el.clone();
127 let rect = el.get_bounding_client_rect();
128 let left = rect.left();
129 let top = rect.top();
130 let width = rect.width();
131 let height = rect.height();
132
133 set_element_position_x.set(left + window.page_x_offset().unwrap_or_default());
134 set_element_position_y.set(top + window.page_y_offset().unwrap_or_default());
135
136 set_element_height.set(height);
137 set_element_width.set(width);
138
139 let el_x = *x - element_position_x.get_untracked();
140 let el_y = *y - element_position_y.get_untracked();
141
142 set_outside.set(
143 width == 0.0
144 || height == 0.0
145 || el_x <= 0.0
146 || el_y <= 0.0
147 || el_x > width
148 || el_y > height,
149 );
150
151 if handle_outside || !is_outside.get_untracked() {
152 set_element_x.set(el_x);
153 set_element_y.set(el_y);
154 }
155 }
156 },
157 false,
158 );
159
160 stop = sendwrap_fn!(move || effect.stop());
161
162 let _ = use_event_listener(document(), mouseleave, move |_| set_outside.set(true));
163 }
164
165 UseMouseInElementReturn {
166 x,
167 y,
168 source_type,
169 element_x: element_x.into(),
170 element_y: element_y.into(),
171 element_position_x: element_position_x.into(),
172 element_position_y: element_position_y.into(),
173 element_width: element_width.into(),
174 element_height: element_height.into(),
175 is_outside: is_outside.into(),
176 stop,
177 }
178}
179
180#[derive(DefaultBuilder)]
182pub struct UseMouseInElementOptions<El, M, Ex>
183where
184 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
185 Ex: UseMouseEventExtractor + Clone,
186{
187 coord_type: UseMouseCoordType<Ex>,
189
190 target: El,
192
193 touch: bool,
195
196 reset_on_touch_ends: bool,
198
199 initial_value: Position,
201
202 handle_outside: bool,
206
207 #[builder(skip)]
208 _marker: PhantomData<M>,
209}
210
211impl<M> Default for UseMouseInElementOptions<UseWindow, M, Infallible>
212where
213 UseWindow: IntoElementMaybeSignal<web_sys::EventTarget, M>,
214{
215 fn default() -> Self {
216 Self {
217 coord_type: UseMouseCoordType::default(),
218 target: use_window(),
219 touch: true,
220 reset_on_touch_ends: false,
221 initial_value: Position { x: 0.0, y: 0.0 },
222 handle_outside: true,
223 _marker: PhantomData,
224 }
225 }
226}
227
228pub struct UseMouseInElementReturn<F>
230where
231 F: Fn() + Clone + Send + Sync,
232{
233 pub x: Signal<f64>,
235
236 pub y: Signal<f64>,
238
239 pub source_type: Signal<UseMouseSourceType>,
241
242 pub element_x: Signal<f64>,
244
245 pub element_y: Signal<f64>,
247
248 pub element_position_x: Signal<f64>,
250
251 pub element_position_y: Signal<f64>,
253
254 pub element_width: Signal<f64>,
256
257 pub element_height: Signal<f64>,
259
260 pub is_outside: Signal<bool>,
262
263 pub stop: F,
265}