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