1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::utils::Pausable;
use crate::{use_interval_fn_with_options, UseIntervalFnOptions};
use default_struct_builder::DefaultBuilder;
use std::rc::Rc;

use leptos::*;

/// Reactive counter increases on every interval.
///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_interval)
///
/// ## Usage
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_interval, UseIntervalReturn};
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// let UseIntervalReturn {
///     counter,
///     reset,
///     is_active,
///     pause,
///     resume
/// }  = use_interval( 200 );
/// # view! { }
/// # }
/// ```
///
/// ## Server-Side Rendering
///
/// On the server this function will simply be ignored.
pub fn use_interval<N>(
    interval: N,
) -> UseIntervalReturn<impl Fn() + Clone, impl Fn() + Clone, impl Fn() + Clone>
where
    N: Into<MaybeSignal<u64>>,
{
    use_interval_with_options(interval, UseIntervalOptions::default())
}

/// Version of [`use_interval`] that takes `UseIntervalOptions`. See [`use_interval`] for how to use.
pub fn use_interval_with_options<N>(
    interval: N,
    options: UseIntervalOptions,
) -> UseIntervalReturn<impl Fn() + Clone, impl Fn() + Clone, impl Fn() + Clone>
where
    N: Into<MaybeSignal<u64>>,
{
    let UseIntervalOptions {
        immediate,
        callback,
    } = options;

    let (counter, set_counter) = create_signal(0u64);

    let update = move || set_counter.update(|count| *count += 1);
    let reset = move || set_counter.set(0);

    let cb = move || {
        update();
        callback(counter.get());
    };

    let Pausable {
        is_active,
        pause,
        resume,
    } = use_interval_fn_with_options(
        cb,
        interval,
        UseIntervalFnOptions {
            immediate,
            immediate_callback: false,
        },
    );

    UseIntervalReturn {
        counter: counter.into(),
        reset,
        is_active,
        pause,
        resume,
    }
}

/// Options for [`use_interval_with_options`]
#[derive(DefaultBuilder)]
pub struct UseIntervalOptions {
    /// Start the timer immediately. Defaults to `true`.
    immediate: bool,

    /// Callback on every interval.
    callback: Rc<dyn Fn(u64)>,
}

impl Default for UseIntervalOptions {
    fn default() -> Self {
        Self {
            immediate: true,
            callback: Rc::new(|_: u64| {}),
        }
    }
}

/// Return type of [`use_interval`].
#[derive(DefaultBuilder)]
pub struct UseIntervalReturn<PauseFn, ResumeFn, ResetFn>
where
    PauseFn: Fn() + Clone,
    ResumeFn: Fn() + Clone,
    ResetFn: Fn() + Clone,
{
    /// Counter signal that increases by one every interval.
    pub counter: Signal<u64>,

    /// Reset the counter to zero
    pub reset: ResetFn,

    /// A Signal that indicates whether the counter is active. `false` when paused.
    pub is_active: Signal<bool>,

    /// Temporarily pause the counter
    pub pause: PauseFn,

    /// Resume the counter
    pub resume: ResumeFn,
}