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>
37where
38 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
39{
40 use_element_hover_with_options(el, UseElementHoverOptions::default())
41}
42
43#[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))]
46pub fn use_element_hover_with_options<El, M>(
47 el: El,
48 options: UseElementHoverOptions,
49) -> Signal<bool>
50where
51 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
52{
53 let UseElementHoverOptions {
54 delay_enter,
55 delay_leave,
56 } = options;
57
58 let (is_hovered, set_hovered) = signal(false);
59
60 let timer = StoredValue::new(None::<TimeoutHandle>);
61
62 let toggle = move |entering: bool| {
63 #[cfg(not(feature = "ssr"))]
64 {
65 let delay = if entering { delay_enter } else { delay_leave };
66
67 timer.update_value(|timer| {
68 if let Some(handle) = timer.take() {
69 handle.clear();
70 }
71 });
72
73 if delay > 0 {
74 timer.set_value(
75 set_timeout_with_handle(
76 move || set_hovered.set(entering),
77 std::time::Duration::from_millis(delay),
78 )
79 .ok(),
80 );
81 } else {
82 set_hovered.set(entering);
83 }
84 }
85 };
86
87 let listener_options = UseEventListenerOptions::default().passive(true);
88
89 let el = el.into_element_maybe_signal();
90
91 let _ =
92 use_event_listener_with_options(el, mouseenter, move |_| toggle(true), listener_options);
93
94 let _ =
95 use_event_listener_with_options(el, mouseleave, move |_| toggle(false), listener_options);
96
97 is_hovered.into()
98}
99
100#[derive(DefaultBuilder, Default)]
102pub struct UseElementHoverOptions {
103 delay_enter: u64,
105
106 delay_leave: u64,
108}