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
40        move || {
41            clear_timeout(&timer);
42        }
43    });
44
45    let ms = ms.into();
46    let max_wait_signal = options.max_wait;
47
48    move |_invoke: Arc<dyn Fn() -> R>| {
49        let duration = ms.get_untracked();
50        let max_duration = max_wait_signal.get_untracked();
51
52        let last_return_val = Arc::clone(&last_return_value);
53        let invoke = move || {
54            #[cfg(debug_assertions)]
55            let zone = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
56
57            let return_value = _invoke();
58
59            #[cfg(debug_assertions)]
60            drop(zone);
61
62            let mut val_mut = last_return_val.lock().unwrap();
63            *val_mut = Some(return_value);
64        };
65
66        clear_timeout(&timer);
67
68        if duration <= 0.0 || max_duration.is_some_and(|d| d <= 0.0) {
69            clear_timeout(&max_timer);
70
71            invoke();
72            return Arc::clone(&last_return_value);
73        }
74
75        cfg_if! { if #[cfg(not(feature = "ssr"))] {
76            // Create the max_timer. Clears the regular timer on invoke
77            if let Some(max_duration) = max_duration {
78                let mut max_timer = max_timer.lock().unwrap();
79
80                if max_timer.is_none() {
81                    let timer = Arc::clone(&timer);
82                    let invok = invoke.clone();
83                    *max_timer = set_timeout_with_handle(
84                        move || {
85                            clear_timeout(&timer);
86                            invok();
87                        },
88                        Duration::from_millis(max_duration as u64),
89                    )
90                    .ok();
91                }
92            }
93
94            let max_timer = Arc::clone(&max_timer);
95
96            // Create the regular timer. Clears the max timer on invoke
97            *timer.lock().unwrap() = set_timeout_with_handle(
98                move || {
99                    clear_timeout(&max_timer);
100                    invoke();
101                },
102                Duration::from_millis(duration as u64),
103            )
104            .ok();
105        }}
106
107        Arc::clone(&last_return_value)
108    }
109}