mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-02-02 10:54:15 -05:00
refactored filters so now filter options are possible.
watch implements them
This commit is contained in:
parent
c8b3390d5b
commit
4b8641fef0
10 changed files with 333 additions and 96 deletions
|
@ -1,5 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.10
|
||||||
|
|
||||||
|
#### New Functions
|
||||||
|
- `use_local_storage`
|
||||||
|
|
||||||
|
#### Changed Functions
|
||||||
|
- `watch` has now variant `watch_with_options` which allows for debouncing and throttling
|
||||||
|
|
||||||
## 0.1.8/9
|
## 0.1.8/9
|
||||||
|
|
||||||
- Fixed documentation and doc tests running for functions behind `#[cfg(web_sys_unstable_apis)]`
|
- Fixed documentation and doc tests running for functions behind `#[cfg(web_sys_unstable_apis)]`
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
pub use crate::utils::DebounceOptions;
|
pub use crate::utils::DebounceOptions;
|
||||||
use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter};
|
use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter};
|
||||||
use leptos::MaybeSignal;
|
use leptos::MaybeSignal;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Debounce execution of a function.
|
/// Debounce execution of a function.
|
||||||
///
|
///
|
||||||
|
@ -26,7 +28,7 @@ use leptos::MaybeSignal;
|
||||||
/// 1000.0,
|
/// 1000.0,
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// window_event_listener(resize, move |_| debounced_fn());
|
/// window_event_listener(resize, move |_| { debounced_fn(); });
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -51,7 +53,7 @@ use leptos::MaybeSignal;
|
||||||
/// .max_wait(Some(5000.0)),
|
/// .max_wait(Some(5000.0)),
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// window_event_listener(resize, move |_| debounced_fn());
|
/// window_event_listener(resize, move |_| { debounced_fn(); });
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -64,46 +66,53 @@ use leptos::MaybeSignal;
|
||||||
/// ## Recommended Reading
|
/// ## Recommended Reading
|
||||||
///
|
///
|
||||||
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
||||||
pub fn use_debounce_fn<F>(func: F, ms: impl Into<MaybeSignal<f64>>) -> impl Fn() + Clone
|
pub fn use_debounce_fn<F, R>(
|
||||||
|
func: F,
|
||||||
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
|
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce() + Clone + 'static,
|
F: FnOnce() -> R + Clone + 'static,
|
||||||
|
R: 'static,
|
||||||
{
|
{
|
||||||
use_debounce_fn_with_options(func, ms, DebounceOptions::default())
|
use_debounce_fn_with_options(func, ms, DebounceOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_debounce_fn`] with debounce options. See the docs for [`use_debounce_fn`] for how to use.
|
/// Version of [`use_debounce_fn`] with debounce options. See the docs for [`use_debounce_fn`] for how to use.
|
||||||
pub fn use_debounce_fn_with_options<F>(
|
pub fn use_debounce_fn_with_options<F, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
options: DebounceOptions,
|
options: DebounceOptions,
|
||||||
) -> impl Fn() + Clone
|
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce() + Clone + 'static,
|
F: FnOnce() -> R + Clone + 'static,
|
||||||
|
R: 'static,
|
||||||
{
|
{
|
||||||
create_filter_wrapper(debounce_filter(ms, options), func)
|
create_filter_wrapper(Box::new(debounce_filter(ms, options)), func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_debounce_fn`] with an argument for the debounced function. See the docs for [`use_debounce_fn`] for how to use.
|
/// Version of [`use_debounce_fn`] with an argument for the debounced function. See the docs for [`use_debounce_fn`] for how to use.
|
||||||
pub fn use_debounce_fn_with_arg<F, Arg>(
|
pub fn use_debounce_fn_with_arg<F, Arg, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
) -> impl Fn(Arg) + Clone
|
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) + Clone + 'static,
|
F: FnOnce(Arg) -> R + Clone + 'static,
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
|
R: 'static,
|
||||||
{
|
{
|
||||||
use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::default())
|
use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_debounce_fn_with_arg`] with debounce options.
|
/// Version of [`use_debounce_fn_with_arg`] with debounce options.
|
||||||
pub fn use_debounce_fn_with_arg_and_options<F, Arg>(
|
pub fn use_debounce_fn_with_arg_and_options<F, Arg, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
options: DebounceOptions,
|
options: DebounceOptions,
|
||||||
) -> impl Fn(Arg) + Clone
|
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) + Clone + 'static,
|
F: FnOnce(Arg) -> R + Clone + 'static,
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
|
R: 'static,
|
||||||
{
|
{
|
||||||
create_filter_wrapper_with_arg(debounce_filter(ms, options), func)
|
create_filter_wrapper_with_arg(Box::new(debounce_filter(ms, options)), func)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::watch;
|
||||||
use crate::{use_resize_observer_with_options, UseResizeObserverOptions};
|
use crate::{use_resize_observer_with_options, UseResizeObserverOptions};
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use leptos_use::{watch_with_options, WatchOptions};
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ where
|
||||||
options.into(),
|
options.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = watch(
|
let _ = watch_with_options(
|
||||||
cx,
|
cx,
|
||||||
move || targ.get(),
|
move || targ.get(),
|
||||||
move |ele, _, _| {
|
move |ele, _, _| {
|
||||||
|
@ -151,7 +152,7 @@ where
|
||||||
set_height(0.0);
|
set_height(0.0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false,
|
WatchOptions::default().immediate(false),
|
||||||
);
|
);
|
||||||
|
|
||||||
UseElementSizeReturn {
|
UseElementSizeReturn {
|
||||||
|
|
|
@ -120,7 +120,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let stop = move || {
|
let stop = move || {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::utils::{
|
use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, throttle_filter};
|
||||||
create_filter_wrapper_with_return, create_filter_wrapper_with_return_and_arg, throttle_filter,
|
|
||||||
};
|
|
||||||
use leptos::MaybeSignal;
|
use leptos::MaybeSignal;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -66,7 +64,7 @@ pub use crate::utils::ThrottleOptions;
|
||||||
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
||||||
pub fn use_throttle_fn<F, R>(
|
pub fn use_throttle_fn<F, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Clone + 'static,
|
F: FnOnce() -> R + Clone + 'static,
|
||||||
|
@ -78,20 +76,20 @@ where
|
||||||
/// Version of [`use_throttle_fn`] with throttle options. See the docs for [`use_throttle_fn`] for how to use.
|
/// Version of [`use_throttle_fn`] with throttle options. See the docs for [`use_throttle_fn`] for how to use.
|
||||||
pub fn use_throttle_fn_with_options<F, R>(
|
pub fn use_throttle_fn_with_options<F, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
options: ThrottleOptions,
|
options: ThrottleOptions,
|
||||||
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Clone + 'static,
|
F: FnOnce() -> R + Clone + 'static,
|
||||||
R: 'static,
|
R: 'static,
|
||||||
{
|
{
|
||||||
create_filter_wrapper_with_return(throttle_filter(ms, options), func)
|
create_filter_wrapper(Box::new(throttle_filter(ms, options)), func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_throttle_fn`] with an argument for the throttled function. See the docs for [`use_throttle_fn`] for how to use.
|
/// Version of [`use_throttle_fn`] with an argument for the throttled function. See the docs for [`use_throttle_fn`] for how to use.
|
||||||
pub fn use_throttle_fn_with_arg<F, Arg, R>(
|
pub fn use_throttle_fn_with_arg<F, Arg, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) -> R + Clone + 'static,
|
F: FnOnce(Arg) -> R + Clone + 'static,
|
||||||
|
@ -104,7 +102,7 @@ where
|
||||||
/// Version of [`use_throttle_fn_with_arg`] with throttle options. See the docs for [`use_throttle_fn`] for how to use.
|
/// Version of [`use_throttle_fn_with_arg`] with throttle options. See the docs for [`use_throttle_fn`] for how to use.
|
||||||
pub fn use_throttle_fn_with_arg_and_options<F, Arg, R>(
|
pub fn use_throttle_fn_with_arg_and_options<F, Arg, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
options: ThrottleOptions,
|
options: ThrottleOptions,
|
||||||
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
|
@ -112,5 +110,5 @@ where
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
R: 'static,
|
R: 'static,
|
||||||
{
|
{
|
||||||
create_filter_wrapper_with_return_and_arg(throttle_filter(ms, options), func)
|
create_filter_wrapper_with_arg(Box::new(throttle_filter(ms, options)), func)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,27 +24,27 @@ impl<R: Default + 'static> Default for Box<dyn CloneableFnWithReturn<R>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CloneableFnWithReturnAndArg<R, Arg>: FnOnce(Arg) -> R {
|
pub trait CloneableFnWithArgAndReturn<Arg, R>: FnOnce(Arg) -> R {
|
||||||
fn clone_box(&self) -> Box<dyn CloneableFnWithReturnAndArg<R, Arg>>;
|
fn clone_box(&self) -> Box<dyn CloneableFnWithArgAndReturn<Arg, R>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, R, Arg> CloneableFnWithReturnAndArg<R, Arg> for F
|
impl<F, R, Arg> CloneableFnWithArgAndReturn<Arg, R> for F
|
||||||
where
|
where
|
||||||
F: FnMut(Arg) -> R + Clone + 'static,
|
F: FnMut(Arg) -> R + Clone + 'static,
|
||||||
R: 'static,
|
R: 'static,
|
||||||
{
|
{
|
||||||
fn clone_box(&self) -> Box<dyn CloneableFnWithReturnAndArg<R, Arg>> {
|
fn clone_box(&self) -> Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R, Arg> Clone for Box<dyn CloneableFnWithReturnAndArg<R, Arg>> {
|
impl<R, Arg> Clone for Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
(**self).clone_box()
|
(**self).clone_box()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Default + 'static, Arg> Default for Box<dyn CloneableFnWithReturnAndArg<R, Arg>> {
|
impl<R: Default + 'static, Arg> Default for Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Box::new(|_| Default::default())
|
Box::new(|_| Default::default())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Clone, DefaultBuilder)]
|
#[derive(Copy, Clone, DefaultBuilder)]
|
||||||
pub struct DebounceOptions {
|
pub struct DebounceOptions {
|
||||||
/// The maximum time allowed to be delayed before it's invoked.
|
/// The maximum time allowed to be delayed before it's invoked.
|
||||||
/// In milliseconds.
|
/// In milliseconds.
|
||||||
|
@ -22,12 +22,16 @@ impl Default for DebounceOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debounce_filter(
|
pub fn debounce_filter<R>(
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>>,
|
||||||
options: DebounceOptions,
|
options: DebounceOptions,
|
||||||
) -> impl Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>> + Clone {
|
) -> impl Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
|
where
|
||||||
|
R: 'static,
|
||||||
|
{
|
||||||
let timer = Rc::new(Cell::new(None::<TimeoutHandle>));
|
let timer = Rc::new(Cell::new(None::<TimeoutHandle>));
|
||||||
let max_timer = Rc::new(Cell::new(None::<TimeoutHandle>));
|
let max_timer = Rc::new(Cell::new(None::<TimeoutHandle>));
|
||||||
|
let last_return_value: Rc<RefCell<Option<R>>> = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
let clear_timeout = move |timer: &Rc<Cell<Option<TimeoutHandle>>>| {
|
let clear_timeout = move |timer: &Rc<Cell<Option<TimeoutHandle>>>| {
|
||||||
if let Some(handle) = timer.get() {
|
if let Some(handle) = timer.get() {
|
||||||
|
@ -39,11 +43,17 @@ pub fn debounce_filter(
|
||||||
let ms = ms.into();
|
let ms = ms.into();
|
||||||
let max_wait_signal = options.max_wait;
|
let max_wait_signal = options.max_wait;
|
||||||
|
|
||||||
move |invoke: Box<dyn CloneableFnWithReturn<()>>| {
|
move |_invoke: Box<dyn CloneableFnWithReturn<R>>| {
|
||||||
let duration = ms.get_untracked();
|
let duration = ms.get_untracked();
|
||||||
let max_duration = max_wait_signal.get_untracked();
|
let max_duration = max_wait_signal.get_untracked();
|
||||||
|
|
||||||
// TODO : return value like throttle_filter?
|
let last_return_val = Rc::clone(&last_return_value);
|
||||||
|
let invoke = move || {
|
||||||
|
let return_value = _invoke();
|
||||||
|
|
||||||
|
let mut val_mut = last_return_val.borrow_mut();
|
||||||
|
*val_mut = Some(return_value);
|
||||||
|
};
|
||||||
|
|
||||||
clear_timeout(&timer);
|
clear_timeout(&timer);
|
||||||
|
|
||||||
|
@ -51,7 +61,7 @@ pub fn debounce_filter(
|
||||||
clear_timeout(&max_timer);
|
clear_timeout(&max_timer);
|
||||||
|
|
||||||
invoke();
|
invoke();
|
||||||
return Rc::new(RefCell::new(None));
|
return Rc::clone(&last_return_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the max_timer. Clears the regular timer on invoke
|
// Create the max_timer. Clears the regular timer on invoke
|
||||||
|
@ -86,6 +96,6 @@ pub fn debounce_filter(
|
||||||
.ok(),
|
.ok(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Rc::new(RefCell::new(None))
|
Rc::clone(&last_return_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,145 @@
|
||||||
mod debounce;
|
mod debounce;
|
||||||
|
mod pausable;
|
||||||
mod throttle;
|
mod throttle;
|
||||||
|
|
||||||
pub use debounce::*;
|
pub use debounce::*;
|
||||||
|
pub use pausable::*;
|
||||||
pub use throttle::*;
|
pub use throttle::*;
|
||||||
|
|
||||||
use crate::utils::CloneableFnWithReturn;
|
use crate::utils::{CloneableFnWithArgAndReturn, CloneableFnWithReturn};
|
||||||
|
use leptos::MaybeSignal;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub fn create_filter_wrapper<F, Filter>(filter: Filter, func: F) -> impl Fn() + Clone
|
pub trait FilterFn<R>:
|
||||||
where
|
CloneableFnWithArgAndReturn<Box<dyn CloneableFnWithReturn<R>>, Rc<RefCell<Option<R>>>>
|
||||||
F: FnOnce() + Clone + 'static,
|
|
||||||
Filter: Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>> + Clone,
|
|
||||||
{
|
{
|
||||||
move || {
|
}
|
||||||
filter(Box::new(func.clone()));
|
|
||||||
|
impl<R, F> FilterFn<R> for F
|
||||||
|
where
|
||||||
|
F: Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone + 'static,
|
||||||
|
R: 'static,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Clone for Box<dyn FilterFn<R>> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
(*self).clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_filter_wrapper_with_arg<F, Arg, Filter>(
|
pub fn create_filter_wrapper<F, R>(
|
||||||
filter: Filter,
|
filter: Box<dyn FilterFn<R>>,
|
||||||
func: F,
|
|
||||||
) -> impl Fn(Arg) + Clone
|
|
||||||
where
|
|
||||||
F: FnOnce(Arg) + Clone + 'static,
|
|
||||||
Arg: Clone + 'static,
|
|
||||||
Filter: Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>> + Clone,
|
|
||||||
{
|
|
||||||
move |arg: Arg| {
|
|
||||||
let func = func.clone();
|
|
||||||
filter(Box::new(move || func(arg)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_filter_wrapper_with_return<F, R, Filter>(
|
|
||||||
filter: Filter,
|
|
||||||
func: F,
|
func: F,
|
||||||
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Clone + 'static,
|
F: FnOnce() -> R + Clone + 'static,
|
||||||
R: 'static,
|
R: 'static,
|
||||||
Filter: Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone,
|
|
||||||
{
|
{
|
||||||
move || filter(Box::new(func.clone()))
|
move || filter.clone()(Box::new(func.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_filter_wrapper_with_return_and_arg<F, Arg, R, Filter>(
|
pub fn create_filter_wrapper_with_arg<F, Arg, R>(
|
||||||
filter: Filter,
|
filter: Box<dyn FilterFn<R>>,
|
||||||
func: F,
|
func: F,
|
||||||
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) -> R + Clone + 'static,
|
F: FnOnce(Arg) -> R + Clone + 'static,
|
||||||
R: 'static,
|
R: 'static,
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
Filter: Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone,
|
|
||||||
{
|
{
|
||||||
move |arg: Arg| {
|
move |arg: Arg| {
|
||||||
let func = func.clone();
|
let func = func.clone();
|
||||||
filter(Box::new(move || func(arg)))
|
filter.clone()(Box::new(move || func(arg)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum FilterOptions {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Debounce {
|
||||||
|
ms: MaybeSignal<f64>,
|
||||||
|
options: DebounceOptions,
|
||||||
|
},
|
||||||
|
Throttle {
|
||||||
|
ms: MaybeSignal<f64>,
|
||||||
|
options: ThrottleOptions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterOptions {
|
||||||
|
pub fn filter_fn<R>(&self) -> Box<dyn FilterFn<R>>
|
||||||
|
where
|
||||||
|
R: 'static,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
FilterOptions::Debounce { ms, options } => {
|
||||||
|
Box::new(debounce_filter(ms.clone(), *options))
|
||||||
|
}
|
||||||
|
FilterOptions::Throttle { ms, options } => {
|
||||||
|
Box::new(throttle_filter(ms.clone(), *options))
|
||||||
|
}
|
||||||
|
FilterOptions::None => Box::new(|invoke: Box<dyn CloneableFnWithReturn<R>>| {
|
||||||
|
Rc::new(RefCell::new(Some(invoke())))
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! filter_builder_methods {
|
||||||
|
(
|
||||||
|
#[$filter_docs:meta]
|
||||||
|
$filter_field_name:ident
|
||||||
|
) => {
|
||||||
|
/// Debounce
|
||||||
|
#[$filter_docs]
|
||||||
|
/// by `ms` milliseconds.
|
||||||
|
pub fn debounce(self, ms: impl Into<MaybeSignal<f64>>) -> Self {
|
||||||
|
self.debounce_with_options(ms, DebounceOptions::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Debounce
|
||||||
|
#[$filter_docs]
|
||||||
|
/// by `ms` milliseconds with additional options.
|
||||||
|
pub fn debounce_with_options(
|
||||||
|
self,
|
||||||
|
ms: impl Into<MaybeSignal<f64>>,
|
||||||
|
options: DebounceOptions,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
$filter_field_name: FilterOptions::Debounce {
|
||||||
|
ms: ms.into(),
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Throttle
|
||||||
|
#[$filter_docs]
|
||||||
|
/// by `ms` milliseconds.
|
||||||
|
pub fn throttle(self, ms: impl Into<MaybeSignal<f64>>) -> Self {
|
||||||
|
self.throttle_with_options(ms, ThrottleOptions::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Throttle
|
||||||
|
#[$filter_docs]
|
||||||
|
/// by `ms` milliseconds with additional options.
|
||||||
|
pub fn throttle_with_options(
|
||||||
|
self,
|
||||||
|
ms: impl Into<MaybeSignal<f64>>,
|
||||||
|
options: ThrottleOptions,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
$filter_field_name: FilterOptions::Throttle {
|
||||||
|
ms: ms.into(),
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ where
|
||||||
let last_exec = Rc::new(Cell::new(0_f64));
|
let last_exec = Rc::new(Cell::new(0_f64));
|
||||||
let timer = Rc::new(Cell::new(None::<TimeoutHandle>));
|
let timer = Rc::new(Cell::new(None::<TimeoutHandle>));
|
||||||
let is_leading = Rc::new(Cell::new(true));
|
let is_leading = Rc::new(Cell::new(true));
|
||||||
let last_value: Rc<RefCell<Option<R>>> = Rc::new(RefCell::new(None));
|
let last_return_value: Rc<RefCell<Option<R>>> = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
let t = Rc::clone(&timer);
|
let t = Rc::clone(&timer);
|
||||||
let clear = move || {
|
let clear = move || {
|
||||||
|
@ -49,11 +49,11 @@ where
|
||||||
let duration = ms.get_untracked();
|
let duration = ms.get_untracked();
|
||||||
let elapsed = Date::now() - last_exec.get();
|
let elapsed = Date::now() - last_exec.get();
|
||||||
|
|
||||||
let last_val = Rc::clone(&last_value);
|
let last_return_val = Rc::clone(&last_return_value);
|
||||||
let invoke = move || {
|
let invoke = move || {
|
||||||
let return_value = _invoke();
|
let return_value = _invoke();
|
||||||
|
|
||||||
let mut val_mut = last_val.borrow_mut();
|
let mut val_mut = last_return_val.borrow_mut();
|
||||||
*val_mut = Some(return_value);
|
*val_mut = Some(return_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ where
|
||||||
if duration <= 0.0 {
|
if duration <= 0.0 {
|
||||||
last_exec.set(Date::now());
|
last_exec.set(Date::now());
|
||||||
invoke();
|
invoke();
|
||||||
return Rc::clone(&last_value);
|
return Rc::clone(&last_return_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if elapsed > duration && (options.leading || !is_leading.get()) {
|
if elapsed > duration && (options.leading || !is_leading.get()) {
|
||||||
|
@ -101,6 +101,6 @@ where
|
||||||
|
|
||||||
is_leading.set(false);
|
is_leading.set(false);
|
||||||
|
|
||||||
Rc::clone(&last_value)
|
Rc::clone(&last_return_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
165
src/watch.rs
165
src/watch.rs
|
@ -1,10 +1,12 @@
|
||||||
|
use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, FilterOptions};
|
||||||
|
use crate::{filter_builder_methods, DebounceOptions, ThrottleOptions};
|
||||||
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// A version of `create_effect` that listens to any dependency that is accessed inside `deps`.
|
/// A version of `create_effect` that listens to any dependency that is accessed inside `deps`.
|
||||||
/// Also a stop handler is returned.
|
/// Also a stop handler is returned.
|
||||||
/// If `immediate` is false, the `callback` will not run immediately but only after
|
|
||||||
/// the first change is detected of any signal that is accessed in `deps`.
|
|
||||||
/// The return value of `deps` is passed into `callback` as an argument together with the previous value
|
/// The return value of `deps` is passed into `callback` as an argument together with the previous value
|
||||||
/// and the previous value that the `callback` itself returned last time.
|
/// and the previous value that the `callback` itself returned last time.
|
||||||
///
|
///
|
||||||
|
@ -24,8 +26,7 @@ use std::cell::RefCell;
|
||||||
/// move |num, _, _| {
|
/// move |num, _, _| {
|
||||||
/// log!("number {}", num);
|
/// log!("number {}", num);
|
||||||
/// },
|
/// },
|
||||||
/// true,
|
/// ); // > "number 0"
|
||||||
/// );
|
|
||||||
///
|
///
|
||||||
/// set_num(1); // > "number 1"
|
/// set_num(1); // > "number 1"
|
||||||
///
|
///
|
||||||
|
@ -37,22 +38,123 @@ use std::cell::RefCell;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn watch<W, T, DFn, CFn>(
|
///
|
||||||
|
/// ## Immediate
|
||||||
|
///
|
||||||
|
/// If `immediate` is false, the `callback` will not run immediately but only after
|
||||||
|
/// the first change is detected of any signal that is accessed in `deps`.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos_use::{watch_with_options, WatchOptions};
|
||||||
|
/// #
|
||||||
|
/// # pub fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// let (num, set_num) = create_signal(cx, 0);
|
||||||
|
///
|
||||||
|
/// watch_with_options(
|
||||||
|
/// cx,
|
||||||
|
/// num,
|
||||||
|
/// move |num, _, _| {
|
||||||
|
/// log!("number {}", num);
|
||||||
|
/// },
|
||||||
|
/// WatchOptions::default().immediate(false),
|
||||||
|
/// ); // nothing happens
|
||||||
|
///
|
||||||
|
/// set_num(1); // > "number 1"
|
||||||
|
/// # view! { cx, }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Filters
|
||||||
|
///
|
||||||
|
/// The callback can be throttled or debounced. Please see [`watch_throttled`] and [`watch_debounced`] for details.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos_use::{watch_with_options, WatchOptions};
|
||||||
|
/// #
|
||||||
|
/// # pub fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// # let (num, set_num) = create_signal(cx, 0);
|
||||||
|
/// #
|
||||||
|
/// watch_with_options(
|
||||||
|
/// cx,
|
||||||
|
/// num,
|
||||||
|
/// move |num, _, _| {
|
||||||
|
/// log!("number {}", num);
|
||||||
|
/// },
|
||||||
|
/// WatchOptions::default().throttle(100.0), // there's also `throttle_with_options`
|
||||||
|
/// );
|
||||||
|
/// # view! { cx, }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos_use::{watch_with_options, WatchOptions};
|
||||||
|
/// #
|
||||||
|
/// # pub fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// # let (num, set_num) = create_signal(cx, 0);
|
||||||
|
/// #
|
||||||
|
/// watch_with_options(
|
||||||
|
/// cx,
|
||||||
|
/// num,
|
||||||
|
/// move |num, _, _| {
|
||||||
|
/// log!("number {}", num);
|
||||||
|
/// },
|
||||||
|
/// WatchOptions::default().debounce(100.0), // there's also `debounce_with_options`
|
||||||
|
/// );
|
||||||
|
/// # view! { cx, }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## See also
|
||||||
|
///
|
||||||
|
/// * [`watch_throttled`]
|
||||||
|
/// * [`watch_debounced`]
|
||||||
|
pub fn watch<W, T, DFn, CFn>(cx: Scope, deps: DFn, callback: CFn) -> impl Fn() + Clone
|
||||||
|
where
|
||||||
|
DFn: Fn() -> W + 'static,
|
||||||
|
CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
|
||||||
|
W: Clone + 'static,
|
||||||
|
T: Clone + 'static,
|
||||||
|
{
|
||||||
|
watch_with_options(cx, deps, callback, WatchOptions::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Version of `watch` that accepts `WatchOptions`. See [`watch`] for how to use.
|
||||||
|
pub fn watch_with_options<W, T, DFn, CFn>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
deps: DFn,
|
deps: DFn,
|
||||||
callback: CFn,
|
callback: CFn,
|
||||||
immediate: bool,
|
options: WatchOptions,
|
||||||
) -> impl Fn() + Clone
|
) -> impl Fn() + Clone
|
||||||
where
|
where
|
||||||
DFn: Fn() -> W + 'static,
|
DFn: Fn() -> W + 'static,
|
||||||
CFn: Fn(&W, Option<&W>, Option<T>) -> T + 'static,
|
CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
|
||||||
W: 'static,
|
W: Clone + 'static,
|
||||||
T: 'static,
|
T: Clone + 'static,
|
||||||
{
|
{
|
||||||
let (is_active, set_active) = create_signal(cx, true);
|
let (is_active, set_active) = create_signal(cx, true);
|
||||||
|
|
||||||
let prev_deps_value: RefCell<Option<W>> = RefCell::new(None);
|
let cur_deps_value: Rc<RefCell<Option<W>>> = Rc::new(RefCell::new(None));
|
||||||
let prev_callback_value: RefCell<Option<T>> = RefCell::new(None);
|
let prev_deps_value: Rc<RefCell<Option<W>>> = Rc::new(RefCell::new(None));
|
||||||
|
let prev_callback_value: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
|
let cur_val = Rc::clone(&cur_deps_value);
|
||||||
|
let prev_val = Rc::clone(&prev_deps_value);
|
||||||
|
let prev_cb_val = Rc::clone(&prev_callback_value);
|
||||||
|
let wrapped_callback = move || {
|
||||||
|
callback(
|
||||||
|
cur_val
|
||||||
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.expect("this will not be called before there is deps value"),
|
||||||
|
prev_val.borrow().as_ref(),
|
||||||
|
prev_cb_val.take(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let filtered_callback = create_filter_wrapper(options.filter.filter_fn(), wrapped_callback);
|
||||||
|
|
||||||
create_effect(cx, move |did_run_before| {
|
create_effect(cx, move |did_run_before| {
|
||||||
if !is_active() {
|
if !is_active() {
|
||||||
|
@ -61,17 +163,16 @@ where
|
||||||
|
|
||||||
let deps_value = deps();
|
let deps_value = deps();
|
||||||
|
|
||||||
if !immediate && did_run_before.is_none() {
|
if !options.immediate && did_run_before.is_none() {
|
||||||
prev_deps_value.replace(Some(deps_value));
|
prev_deps_value.replace(Some(deps_value));
|
||||||
return ();
|
return ();
|
||||||
}
|
}
|
||||||
|
|
||||||
let callback_value = callback(
|
cur_deps_value.replace(Some(deps_value.clone()));
|
||||||
&deps_value,
|
|
||||||
prev_deps_value.borrow().as_ref(),
|
let callback_value = filtered_callback().take();
|
||||||
prev_callback_value.take(),
|
|
||||||
);
|
prev_callback_value.replace(callback_value);
|
||||||
prev_callback_value.replace(Some(callback_value));
|
|
||||||
|
|
||||||
prev_deps_value.replace(Some(deps_value));
|
prev_deps_value.replace(Some(deps_value));
|
||||||
|
|
||||||
|
@ -82,3 +183,31 @@ where
|
||||||
set_active(false);
|
set_active(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options for `watch_with_options`
|
||||||
|
#[derive(DefaultBuilder)]
|
||||||
|
pub struct WatchOptions {
|
||||||
|
/// If `immediate` is false, the `callback` will not run immediately but only after
|
||||||
|
/// the first change is detected of any signal that is accessed in `deps`.
|
||||||
|
/// Defaults to `true`.
|
||||||
|
immediate: bool,
|
||||||
|
|
||||||
|
#[builder(skip)]
|
||||||
|
filter: FilterOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WatchOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
immediate: true,
|
||||||
|
filter: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WatchOptions {
|
||||||
|
filter_builder_methods!(
|
||||||
|
/// the watch callback
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue