leptos_use/
use_element_hover.rs1use crate::core::IntoElementMaybeSignal;
2use crate::{use_event_listener_with_options, UseEventListenerOptions};
3use default_struct_builder::DefaultBuilder;
4use leptos::ev::{mouseenter, mouseleave};
5use leptos::leptos_dom::helpers::TimeoutHandle;
6use leptos::prelude::*;
7use leptos::reactive::wrappers::read::Signal;
8
9pub fn use_element_hover<El, M>(el: El) -> Signal<bool>
39where
40 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
41{
42 use_element_hover_with_options(el, UseElementHoverOptions::default())
43}
44
45#[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))]
47pub fn use_element_hover_with_options<El, M>(
48 el: El,
49 options: UseElementHoverOptions,
50) -> Signal<bool>
51where
52 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
53{
54 let UseElementHoverOptions {
55 delay_enter,
56 delay_leave,
57 } = options;
58
59 let (is_hovered, set_hovered) = signal(false);
60
61 let timer = StoredValue::new(None::<TimeoutHandle>);
62
63 let toggle = move |entering: bool| {
64 #[cfg(not(feature = "ssr"))]
65 {
66 let delay = if entering { delay_enter } else { delay_leave };
67
68 timer.update_value(|timer| {
69 if let Some(handle) = timer.take() {
70 handle.clear();
71 }
72 });
73
74 if delay > 0 {
75 timer.set_value(
76 set_timeout_with_handle(
77 move || set_hovered.set(entering),
78 std::time::Duration::from_millis(delay),
79 )
80 .ok(),
81 );
82 } else {
83 set_hovered.set(entering);
84 }
85 }
86 };
87
88 let listener_options = UseEventListenerOptions::default().passive(true);
89
90 let el = el.into_element_maybe_signal();
91
92 let _ =
93 use_event_listener_with_options(el, mouseenter, move |_| toggle(true), listener_options);
94
95 let _ =
96 use_event_listener_with_options(el, mouseleave, move |_| toggle(false), listener_options);
97
98 is_hovered.into()
99}
100
101#[derive(DefaultBuilder, Default)]
103pub struct UseElementHoverOptions {
104 delay_enter: u64,
106
107 delay_leave: u64,
109}