Skip to main content

leptos_use/utils/filters/
debounce.rs

1#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
2
3use cfg_if::cfg_if;
4use default_struct_builder::DefaultBuilder;
5use leptos::leptos_dom::helpers::TimeoutHandle;
6use leptos::prelude::*;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10#[derive(Copy, Clone, DefaultBuilder, Default)]
11pub struct DebounceOptions {
12    /// The maximum time allowed to be delayed before it's invoked.
13    /// In milliseconds.
14    #[builder(into)]
15    pub max_wait: Signal<Option<f64>>,
16}
17
18pub fn debounce_filter<R>(
19    ms: impl Into<Signal<f64>>,
20    options: DebounceOptions,
21) -> impl Fn(Arc<dyn Fn() -> R>) -> Arc<Mutex<Option<R>>> + Clone
22where
23    R: 'static,
24{
25    let timer = Arc::new(Mutex::new(None::<TimeoutHandle>));
26    let max_timer = Arc::new(Mutex::new(None::<TimeoutHandle>));
27    let last_return_value: Arc<Mutex<Option<R>>> = Arc::new(Mutex::new(None));
28
29    let clear_timeout = move |timer: &Arc<Mutex<Option<TimeoutHandle>>>| {
30        let mut timer = timer.lock().unwrap();
31        if let Some(handle) = *timer {
32            handle.clear();
33            *timer = None;
34        }
35    };
36
37    on_cleanup({
38        let timer = Arc::clone(&timer);
39        let max_timer = Arc::clone(&max_timer);
40
41        move || {
42            clear_timeout(&timer);
43            clear_timeout(&max_timer);
44        }
45    });
46
47    let ms = ms.into();
48    let max_wait_signal = options.max_wait;
49
50    move |_invoke: Arc<dyn Fn() -> R>| {
51        let duration = ms.get_untracked();
52        let max_duration = max_wait_signal.get_untracked();
53
54        let last_return_val = Arc::clone(&last_return_value);
55        let invoke = move || {
56            #[cfg(debug_assertions)]
57            let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
58
59            let return_value = _invoke();
60
61            #[cfg(debug_assertions)]
62            drop(zone);
63
64            let mut val_mut = last_return_val.lock().unwrap();
65            *val_mut = Some(return_value);
66        };
67
68        clear_timeout(&timer);
69
70        if duration <= 0.0 || max_duration.is_some_and(|d| d <= 0.0) {
71            clear_timeout(&max_timer);
72
73            invoke();
74            return Arc::clone(&last_return_value);
75        }
76
77        cfg_if! { if #[cfg(not(feature = "ssr"))] {
78            // Create the max_timer. Clears the regular timer on invoke
79            if let Some(max_duration) = max_duration {
80                let max_timer_slot = Arc::clone(&max_timer);
81                let mut max_timer = max_timer.lock().unwrap();
82
83                if max_timer.is_none() {
84                    let timer = Arc::clone(&timer);
85                    let invok = invoke.clone();
86                    *max_timer = set_timeout_with_handle(
87                        move || {
88                            clear_timeout(&timer);
89                            // This handle has just fired. Release the slot so
90                            // that the next burst can arm a new max_wait timer,
91                            // before running the callback which may itself call
92                            // the debounced function again.
93                            *max_timer_slot.lock().unwrap() = None;
94                            invok();
95                        },
96                        Duration::from_millis((max_duration as u64).min(i32::MAX as u64)),
97                    )
98                    .ok();
99                }
100            }
101
102            let max_timer = Arc::clone(&max_timer);
103
104            // Create the regular timer. Clears the max timer on invoke
105            *timer.lock().unwrap() = set_timeout_with_handle(
106                move || {
107                    clear_timeout(&max_timer);
108                    invoke();
109                },
110                Duration::from_millis((duration as u64).min(i32::MAX as u64)),
111            )
112            .ok();
113        }}
114
115        Arc::clone(&last_return_value)
116    }
117}