migrated away from CloneableFn... traits

This commit is contained in:
Maccesch 2023-08-04 15:58:03 +01:00
parent 1e896a0778
commit 09dee407c7
23 changed files with 210 additions and 415 deletions

View file

@ -26,9 +26,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The options `reconnect_limit` and `reconnect_interval` now take a `u64` instead of `Option<u64>` to improve DX. - The options `reconnect_limit` and `reconnect_interval` now take a `u64` instead of `Option<u64>` to improve DX.
- The option `manual` has been renamed to `immediate` to make it more consistent with other functions. - The option `manual` has been renamed to `immediate` to make it more consistent with other functions.
To port please note that `immediate` is the inverse of `manual` (`immediate` = `!manual`). To port please note that `immediate` is the inverse of `manual` (`immediate` = `!manual`).
- `use_color_mode`:
- The optional `on_changed` handler parameters have changed slightly. Please refer to the docs for more details.
- Throttled or debounced functions cannot be `FnOnce` anymore.
- All traits `ClonableFn...` have been removed.
### Other Changes 🔥 ### Other Changes 🔥
- Callbacks in options don't require to be cloneable anymore
- Callback in `use_raf_fn` doesn't require to be cloneable anymore - Callback in `use_raf_fn` doesn't require to be cloneable anymore
## [0.6.2] - 2023-08-03 ## [0.6.2] - 2023-08-03

View file

@ -16,7 +16,7 @@ homepage = "https://leptos-use.rs"
leptos = "0.5.0-alpha" leptos = "0.5.0-alpha"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
js-sys = "0.3" js-sys = "0.3"
default-struct-builder = "0.4" default-struct-builder = "0.5"
num = { version = "0.4", optional = true } num = { version = "0.4", optional = true }
serde = { version = "1", optional = true } serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true } serde_json = { version = "1", optional = true }

View file

@ -1,8 +1,9 @@
use crate::filter_builder_methods; use crate::filter_builder_methods;
use crate::storage::{StorageType, UseStorageError, UseStorageOptions}; use crate::storage::{StorageType, UseStorageError, UseStorageOptions};
use crate::utils::{CloneableFnWithArg, DebounceOptions, FilterOptions, ThrottleOptions}; use crate::utils::{DebounceOptions, FilterOptions, ThrottleOptions};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::*; use leptos::*;
use std::rc::Rc;
macro_rules! use_specific_storage { macro_rules! use_specific_storage {
($(#[$outer:meta])* ($(#[$outer:meta])*
@ -58,8 +59,7 @@ pub struct UseSpecificStorageOptions<T> {
/// Defaults to simply returning the stored value. /// Defaults to simply returning the stored value.
merge_defaults: fn(&str, &T) -> String, merge_defaults: fn(&str, &T) -> String,
/// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`]. /// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`].
#[builder(into)] on_error: Rc<dyn Fn(UseStorageError)>,
on_error: Box<dyn CloneableFnWithArg<UseStorageError>>,
/// Debounce or throttle the writing to storage whenever the value changes. /// Debounce or throttle the writing to storage whenever the value changes.
filter: FilterOptions, filter: FilterOptions,
@ -71,7 +71,7 @@ impl<T> Default for UseSpecificStorageOptions<T> {
listen_to_storage_changes: true, listen_to_storage_changes: true,
write_defaults: true, write_defaults: true,
merge_defaults: |stored_value, _default_value| stored_value.to_string(), merge_defaults: |stored_value, _default_value| stored_value.to_string(),
on_error: Box::new(|_| ()), on_error: Rc::new(|_| ()),
filter: Default::default(), filter: Default::default(),
} }
} }

View file

@ -1,7 +1,7 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
use crate::core::MaybeRwSignal; use crate::core::MaybeRwSignal;
use crate::utils::{CloneableFn, CloneableFnWithArg, FilterOptions}; use crate::utils::FilterOptions;
use crate::{ use crate::{
filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions, filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions,
ThrottleOptions, WatchOptions, WatchPausableReturn, ThrottleOptions, WatchOptions, WatchPausableReturn,
@ -12,6 +12,7 @@ use js_sys::Reflect;
use leptos::*; use leptos::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Error; use serde_json::Error;
use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::{JsCast, JsValue};
@ -190,18 +191,18 @@ where
let raw_init = data.get_untracked(); let raw_init = data.get_untracked();
cfg_if! { if #[cfg(feature = "ssr")] { cfg_if! { if #[cfg(feature = "ssr")] {
let remove: Box<dyn CloneableFn> = Box::new(|| {}); let remove: Rc<dyn Fn()> = Rc::new(|| {});
} else { } else {
let storage = storage_type.into_storage(); let storage = storage_type.into_storage();
let remove: Box<dyn CloneableFn> = match storage { let remove: Rc<dyn Fn()> = match storage {
Ok(Some(storage)) => { Ok(Some(storage)) => {
let write = { let write = {
let on_error = on_error.clone(); let on_error = on_error.clone();
let storage = storage.clone(); let storage = storage.clone();
let key = key.to_string(); let key = key.to_string();
move |v: &T| { Rc::new(move |v: &T| {
match serde_json::to_string(&v) { match serde_json::to_string(&v) {
Ok(ref serialized) => match storage.get_item(&key) { Ok(ref serialized) => match storage.get_item(&key) {
Ok(old_value) => { Ok(old_value) => {
@ -240,7 +241,7 @@ where
on_error.clone()(UseStorageError::SerializationError(e)); on_error.clone()(UseStorageError::SerializationError(e));
} }
} }
} })
}; };
let read = { let read = {
@ -249,51 +250,53 @@ where
let key = key.to_string(); let key = key.to_string();
let raw_init = raw_init.clone(); let raw_init = raw_init.clone();
move |event_detail: Option<StorageEventDetail>| -> Option<T> { Rc::new(
let serialized_init = match serde_json::to_string(&raw_init) { move |event_detail: Option<StorageEventDetail>| -> Option<T> {
Ok(serialized) => Some(serialized), let serialized_init = match serde_json::to_string(&raw_init) {
Err(e) => { Ok(serialized) => Some(serialized),
on_error.clone()(UseStorageError::DefaultSerializationError(e));
None
}
};
let raw_value = if let Some(event_detail) = event_detail {
event_detail.new_value
} else {
match storage.get_item(&key) {
Ok(raw_value) => match raw_value {
Some(raw_value) => Some(merge_defaults(&raw_value, &raw_init)),
None => serialized_init.clone(),
},
Err(e) => { Err(e) => {
on_error.clone()(UseStorageError::StorageAccessError(e)); on_error.clone()(UseStorageError::DefaultSerializationError(e));
None None
} }
} };
};
match raw_value { let raw_value = if let Some(event_detail) = event_detail {
Some(raw_value) => match serde_json::from_str(&raw_value) { event_detail.new_value
Ok(v) => Some(v), } else {
Err(e) => { match storage.get_item(&key) {
on_error.clone()(UseStorageError::SerializationError(e)); Ok(raw_value) => match raw_value {
None Some(raw_value) => Some(merge_defaults(&raw_value, &raw_init)),
} None => serialized_init.clone(),
}, },
None => { Err(e) => {
if let Some(serialized_init) = &serialized_init { on_error.clone()(UseStorageError::StorageAccessError(e));
if write_defaults { None
if let Err(e) = storage.set_item(&key, serialized_init) {
on_error(UseStorageError::StorageAccessError(e));
}
} }
} }
};
Some(raw_init) match raw_value {
Some(raw_value) => match serde_json::from_str(&raw_value) {
Ok(v) => Some(v),
Err(e) => {
on_error.clone()(UseStorageError::SerializationError(e));
None
}
},
None => {
if let Some(serialized_init) = &serialized_init {
if write_defaults {
if let Err(e) = storage.set_item(&key, serialized_init) {
on_error(UseStorageError::StorageAccessError(e));
}
}
}
Some(raw_init.clone())
}
} }
} },
} )
}; };
let WatchPausableReturn { let WatchPausableReturn {
@ -311,15 +314,15 @@ where
let storage = storage.clone(); let storage = storage.clone();
let raw_init = raw_init.clone(); let raw_init = raw_init.clone();
move |event_detail: Option<StorageEventDetail>| { Rc::new(move |event_detail: Option<StorageEventDetail>| {
if let Some(event_detail) = &event_detail { if let Some(event_detail) = &event_detail {
if event_detail.storage_area != Some(storage) { if event_detail.storage_area != Some(storage.clone()) {
return; return;
} }
match &event_detail.key { match &event_detail.key {
None => { None => {
set_data.set(raw_init); set_data.set(raw_init.clone());
return; return;
} }
Some(event_key) => { Some(event_key) => {
@ -343,21 +346,25 @@ where
} else { } else {
resume_watch(); resume_watch();
} }
} })
}; };
let upd = update.clone(); let update_from_custom_event = {
let update_from_custom_event = let update = Rc::clone(&update);
move |event: web_sys::CustomEvent| upd.clone()(Some(event.into()));
let upd = update.clone(); move |event: web_sys::CustomEvent| update(Some(event.into()))
let update_from_storage_event = };
move |event: web_sys::StorageEvent| upd.clone()(Some(event.into()));
let update_from_storage_event = {
let update = Rc::clone(&update);
move |event: web_sys::StorageEvent| update(Some(event.into()))
};
if listen_to_storage_changes { if listen_to_storage_changes {
let _ = use_event_listener(window(), ev::storage, update_from_storage_event); let _ = use_event_listener(window(), ev::storage, update_from_storage_event);
let _ = use_event_listener( let _ = use_event_listener(
window(), window(),
ev::Custom::new(CUSTOM_STORAGE_EVENT_NAME), ev::Custom::new(CUSTOM_STORAGE_EVENT_NAME),
update_from_custom_event, update_from_custom_event,
); );
@ -367,22 +374,22 @@ where
let k = key.to_string(); let k = key.to_string();
Box::new(move || { Rc::new(move || {
let _ = storage.remove_item(&k); let _ = storage.remove_item(&k);
}) })
} }
Err(e) => { Err(e) => {
on_error(UseStorageError::NoStorage(e)); on_error(UseStorageError::NoStorage(e));
Box::new(move || {}) Rc::new(move || {})
} }
_ => { _ => {
// do nothing // do nothing
Box::new(move || {}) Rc::new(move || {})
} }
}; };
}} }}
(data, set_data, move || remove.clone()()) (data, set_data, move || remove())
} }
#[derive(Clone)] #[derive(Clone)]
@ -461,7 +468,7 @@ pub struct UseStorageOptions<T> {
/// Defaults to simply returning the stored value. /// Defaults to simply returning the stored value.
pub(crate) merge_defaults: fn(&str, &T) -> String, pub(crate) merge_defaults: fn(&str, &T) -> String,
/// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`]. /// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`].
pub(crate) on_error: Box<dyn CloneableFnWithArg<UseStorageError>>, pub(crate) on_error: Rc<dyn Fn(UseStorageError)>,
/// Debounce or throttle the writing to storage whenever the value changes. /// Debounce or throttle the writing to storage whenever the value changes.
pub(crate) filter: FilterOptions, pub(crate) filter: FilterOptions,
@ -474,7 +481,7 @@ impl<T> Default for UseStorageOptions<T> {
listen_to_storage_changes: true, listen_to_storage_changes: true,
write_defaults: true, write_defaults: true,
merge_defaults: |stored_value, _default_value| stored_value.to_string(), merge_defaults: |stored_value, _default_value| stored_value.to_string(),
on_error: Box::new(|_| ()), on_error: Rc::new(|_| ()),
filter: Default::default(), filter: Default::default(),
} }
} }

View file

@ -7,11 +7,11 @@ use std::fmt::{Display, Formatter};
use crate::core::StorageType; use crate::core::StorageType;
use crate::use_preferred_dark; use crate::use_preferred_dark;
use crate::utils::CloneableFnWithArg;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::*; use leptos::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
/// Reactive color mode (dark / light / customs) with auto data persistence. /// Reactive color mode (dark / light / customs) with auto data persistence.
@ -217,10 +217,7 @@ where
}; };
let on_changed = move |mode: ColorMode| { let on_changed = move |mode: ColorMode| {
on_changed(UseColorModeOnChangeArgs { on_changed(mode, Rc::new(default_on_changed.clone()));
mode,
default_handler: Box::new(default_on_changed.clone()),
});
}; };
create_effect({ create_effect({
@ -333,16 +330,6 @@ impl From<String> for ColorMode {
} }
} }
/// Arguments to [`UseColorModeOptions::on_changed`]
#[derive(Clone)]
pub struct UseColorModeOnChangeArgs {
/// The color mode to change to.
pub mode: ColorMode,
/// The default handler that would have been called if the `on_changed` handler had not been specified.
pub default_handler: Box<dyn CloneableFnWithArg<ColorMode>>,
}
#[derive(DefaultBuilder)] #[derive(DefaultBuilder)]
pub struct UseColorModeOptions<El, T> pub struct UseColorModeOptions<El, T>
where where
@ -367,7 +354,10 @@ where
/// Custom handler that is called on updates. /// Custom handler that is called on updates.
/// If specified this will override the default behavior. /// If specified this will override the default behavior.
/// To get the default behaviour back you can call the provided `default_handler` function. /// To get the default behaviour back you can call the provided `default_handler` function.
on_changed: Box<dyn CloneableFnWithArg<UseColorModeOnChangeArgs>>, /// It takes two parameters:
/// - `mode: ColorMode`: The color mode to change to.
/// -`default_handler: Rc<dyn Fn(ColorMode)>`: The default handler that would have been called if the `on_changed` handler had not been specified.
on_changed: OnChangedFn,
/// When provided, `useStorage` will be skipped. /// When provided, `useStorage` will be skipped.
/// Storage requires the *create feature* **`storage`** to be enabled. /// Storage requires the *create feature* **`storage`** to be enabled.
@ -411,6 +401,8 @@ where
_marker: PhantomData<T>, _marker: PhantomData<T>,
} }
type OnChangedFn = Rc<dyn Fn(ColorMode, Rc<dyn Fn(ColorMode)>)>;
impl Default for UseColorModeOptions<&'static str, web_sys::Element> { impl Default for UseColorModeOptions<&'static str, web_sys::Element> {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -418,9 +410,7 @@ impl Default for UseColorModeOptions<&'static str, web_sys::Element> {
attribute: "class".into(), attribute: "class".into(),
initial_value: ColorMode::Auto.into(), initial_value: ColorMode::Auto.into(),
custom_modes: vec![], custom_modes: vec![],
on_changed: Box::new(move |args: UseColorModeOnChangeArgs| { on_changed: Rc::new(move |mode, default_handler| (default_handler)(mode)),
(args.default_handler)(args.mode)
}),
storage_signal: None, storage_signal: None,
storage_key: "leptos-use-color-scheme".into(), storage_key: "leptos-use-color-scheme".into(),
storage: StorageType::default(), storage: StorageType::default(),

View file

@ -77,7 +77,7 @@ pub fn use_debounce_fn<F, R>(
ms: impl Into<MaybeSignal<f64>> + 'static, 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: Fn() -> R + Clone + 'static,
R: 'static, R: 'static,
{ {
use_debounce_fn_with_options(func, ms, DebounceOptions::default()) use_debounce_fn_with_options(func, ms, DebounceOptions::default())
@ -90,10 +90,10 @@ pub fn use_debounce_fn_with_options<F, R>(
options: DebounceOptions, options: DebounceOptions,
) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone ) -> impl Fn() -> Rc<RefCell<Option<R>>> + Clone
where where
F: FnOnce() -> R + Clone + 'static, F: Fn() -> R + Clone + 'static,
R: 'static, R: 'static,
{ {
create_filter_wrapper(Box::new(debounce_filter(ms, options)), func) create_filter_wrapper(Rc::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.
@ -102,7 +102,7 @@ pub fn use_debounce_fn_with_arg<F, Arg, R>(
ms: impl Into<MaybeSignal<f64>> + 'static, 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: Fn(Arg) -> R + Clone + 'static,
Arg: Clone + 'static, Arg: Clone + 'static,
R: 'static, R: 'static,
{ {
@ -116,9 +116,9 @@ pub fn use_debounce_fn_with_arg_and_options<F, Arg, R>(
options: DebounceOptions, options: DebounceOptions,
) -> 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: Fn(Arg) -> R + Clone + 'static,
Arg: Clone + 'static, Arg: Clone + 'static,
R: 'static, R: 'static,
{ {
create_filter_wrapper_with_arg(Box::new(debounce_filter(ms, options)), func) create_filter_wrapper_with_arg(Rc::new(debounce_filter(ms, options)), func)
} }

View file

@ -1,10 +1,10 @@
use crate::core::{ElementMaybeSignal, MaybeRwSignal, PointerType, Position}; use crate::core::{ElementMaybeSignal, MaybeRwSignal, PointerType, Position};
use crate::use_event_listener_with_options; use crate::use_event_listener_with_options;
use crate::utils::{CloneableFnMutWithArg, CloneableFnWithArgAndReturn};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::ev::{pointerdown, pointermove, pointerup}; use leptos::ev::{pointerdown, pointermove, pointerup};
use leptos::*; use leptos::*;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::PointerEvent; use web_sys::PointerEvent;
@ -134,7 +134,7 @@ where
x: event.client_x() as f64 - rect.left(), x: event.client_x() as f64 - rect.left(),
y: event.client_y() as f64 - rect.top(), y: event.client_y() as f64 - rect.top(),
}; };
if !on_start.clone()(UseDraggableCallbackArgs { if !on_start(UseDraggableCallbackArgs {
position, position,
event: event.clone(), event: event.clone(),
}) { }) {
@ -159,7 +159,7 @@ where
y: event.client_y() as f64 - start_position.y, y: event.client_y() as f64 - start_position.y,
}; };
set_position.set(position); set_position.set(position);
on_move.clone()(UseDraggableCallbackArgs { on_move(UseDraggableCallbackArgs {
position, position,
event: event.clone(), event: event.clone(),
}); });
@ -176,7 +176,7 @@ where
return; return;
} }
set_start_position.set(None); set_start_position.set(None);
on_end.clone()(UseDraggableCallbackArgs { on_end(UseDraggableCallbackArgs {
position: position.get_untracked(), position: position.get_untracked(),
event: event.clone(), event: event.clone(),
}); });
@ -253,13 +253,13 @@ where
initial_value: MaybeRwSignal<Position>, initial_value: MaybeRwSignal<Position>,
/// Callback when the dragging starts. Return `false` to prevent dragging. /// Callback when the dragging starts. Return `false` to prevent dragging.
on_start: Box<dyn CloneableFnWithArgAndReturn<UseDraggableCallbackArgs, bool>>, on_start: Rc<dyn Fn(UseDraggableCallbackArgs) -> bool>,
/// Callback during dragging. /// Callback during dragging.
on_move: Box<dyn CloneableFnMutWithArg<UseDraggableCallbackArgs>>, on_move: Rc<dyn Fn(UseDraggableCallbackArgs)>,
/// Callback when dragging end. /// Callback when dragging end.
on_end: Box<dyn CloneableFnMutWithArg<UseDraggableCallbackArgs>>, on_end: Rc<dyn Fn(UseDraggableCallbackArgs)>,
#[builder(skip)] #[builder(skip)]
_marker1: PhantomData<DragT>, _marker1: PhantomData<DragT>,
@ -284,9 +284,9 @@ impl Default
handle: None, handle: None,
pointer_types: vec![PointerType::Mouse, PointerType::Touch, PointerType::Pen], pointer_types: vec![PointerType::Mouse, PointerType::Touch, PointerType::Pen],
initial_value: MaybeRwSignal::default(), initial_value: MaybeRwSignal::default(),
on_start: Box::new(|_| true), on_start: Rc::new(|_| true),
on_move: Box::new(|_| {}), on_move: Rc::new(|_| {}),
on_end: Box::new(|_| {}), on_end: Rc::new(|_| {}),
_marker1: PhantomData, _marker1: PhantomData,
_marker2: PhantomData, _marker2: PhantomData,
} }

View file

@ -1,9 +1,10 @@
use crate::core::ElementMaybeSignal; use crate::core::ElementMaybeSignal;
use crate::use_event_listener; use crate::use_event_listener;
use crate::utils::CloneableFnMutWithArg;
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::ev::{dragenter, dragleave, dragover, drop}; use leptos::ev::{dragenter, dragleave, dragover, drop};
use leptos::*; use leptos::*;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
/// Create a zone where files can be dropped. /// Create a zone where files can be dropped.
/// ///
@ -65,10 +66,10 @@ where
T: Into<web_sys::EventTarget> + Clone + 'static, T: Into<web_sys::EventTarget> + Clone + 'static,
{ {
let UseDropZoneOptions { let UseDropZoneOptions {
mut on_drop, on_drop,
mut on_enter, on_enter,
mut on_leave, on_leave,
mut on_over, on_over,
} = options; } = options;
let (is_over_drop_zone, set_over_drop_zone) = create_signal(false); let (is_over_drop_zone, set_over_drop_zone) = create_signal(false);
@ -147,16 +148,33 @@ where
} }
/// Options for [`use_drop_zone_with_options`]. /// Options for [`use_drop_zone_with_options`].
#[derive(DefaultBuilder, Default, Clone, Debug)] #[derive(DefaultBuilder, Clone)]
pub struct UseDropZoneOptions { pub struct UseDropZoneOptions {
/// Event handler for the [`drop`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event) event /// Event handler for the [`drop`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event) event
on_drop: Box<dyn CloneableFnMutWithArg<UseDropZoneEvent>>, on_drop: Rc<dyn Fn(UseDropZoneEvent)>,
/// Event handler for the [`dragenter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event) event /// Event handler for the [`dragenter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event) event
on_enter: Box<dyn CloneableFnMutWithArg<UseDropZoneEvent>>, on_enter: Rc<dyn Fn(UseDropZoneEvent)>,
/// Event handler for the [`dragleave`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragleave_event) event /// Event handler for the [`dragleave`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragleave_event) event
on_leave: Box<dyn CloneableFnMutWithArg<UseDropZoneEvent>>, on_leave: Rc<dyn Fn(UseDropZoneEvent)>,
/// Event handler for the [`dragover`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event) event /// Event handler for the [`dragover`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event) event
on_over: Box<dyn CloneableFnMutWithArg<UseDropZoneEvent>>, on_over: Rc<dyn Fn(UseDropZoneEvent)>,
}
impl Default for UseDropZoneOptions {
fn default() -> Self {
Self {
on_drop: Rc::new(|_| {}),
on_enter: Rc::new(|_| {}),
on_leave: Rc::new(|_| {}),
on_over: Rc::new(|_| {}),
}
}
}
impl Debug for UseDropZoneOptions {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "UseDropZoneOptions")
}
} }
/// Event passed as argument to the event handler functions of `UseDropZoneOptions`. /// Event passed as argument to the event handler functions of `UseDropZoneOptions`.

View file

@ -1,6 +1,7 @@
use crate::utils::{CloneableFnWithArg, Pausable}; use crate::utils::Pausable;
use crate::{use_interval_fn_with_options, UseIntervalFnOptions}; use crate::{use_interval_fn_with_options, UseIntervalFnOptions};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use std::rc::Rc;
use leptos::*; use leptos::*;
@ -61,7 +62,7 @@ where
let cb = move || { let cb = move || {
update(); update();
callback.clone()(counter.get()); callback(counter.get());
}; };
let Pausable { let Pausable {
@ -93,14 +94,14 @@ pub struct UseIntervalOptions {
immediate: bool, immediate: bool,
/// Callback on every interval. /// Callback on every interval.
callback: Box<dyn CloneableFnWithArg<u64>>, callback: Rc<dyn Fn(u64)>,
} }
impl Default for UseIntervalOptions { impl Default for UseIntervalOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
immediate: true, immediate: true,
callback: Box::new(|_: u64| {}), callback: Rc::new(|_: u64| {}),
} }
} }
} }

View file

@ -1,7 +1,6 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
use crate::use_event_listener; use crate::use_event_listener;
use crate::utils::CloneableFnMutWithArg;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use leptos::ev::change; use leptos::ev::change;
use leptos::*; use leptos::*;
@ -48,8 +47,7 @@ pub fn use_media_query(query: impl Into<MaybeSignal<String>>) -> Signal<bool> {
let media_query: Rc<RefCell<Option<web_sys::MediaQueryList>>> = Rc::new(RefCell::new(None)); let media_query: Rc<RefCell<Option<web_sys::MediaQueryList>>> = Rc::new(RefCell::new(None));
let remove_listener: RemoveListener = Rc::new(RefCell::new(None)); let remove_listener: RemoveListener = Rc::new(RefCell::new(None));
let listener: Rc<RefCell<Box<dyn CloneableFnMutWithArg<web_sys::Event>>>> = let listener = Rc::new(RefCell::new(Rc::new(|_| {}) as Rc<dyn Fn(web_sys::Event)>));
Rc::new(RefCell::new(Box::new(|_| {})));
let cleanup = { let cleanup = {
let remove_listener = Rc::clone(&remove_listener); let remove_listener = Rc::clone(&remove_listener);
@ -65,7 +63,7 @@ pub fn use_media_query(query: impl Into<MaybeSignal<String>>) -> Signal<bool> {
let cleanup = cleanup.clone(); let cleanup = cleanup.clone();
let listener = Rc::clone(&listener); let listener = Rc::clone(&listener);
move || { Rc::new(move || {
cleanup(); cleanup();
let mut media_query = media_query.borrow_mut(); let mut media_query = media_query.borrow_mut();
@ -74,21 +72,22 @@ pub fn use_media_query(query: impl Into<MaybeSignal<String>>) -> Signal<bool> {
if let Some(media_query) = media_query.as_ref() { if let Some(media_query) = media_query.as_ref() {
set_matches.set(media_query.matches()); set_matches.set(media_query.matches());
let listener = Rc::clone(&*listener.borrow());
remove_listener.replace(Some(Box::new(use_event_listener( remove_listener.replace(Some(Box::new(use_event_listener(
media_query.clone(), media_query.clone(),
change, change,
listener.borrow().clone(), move |e| listener(e),
)))); ))));
} else { } else {
set_matches.set(false); set_matches.set(false);
} }
} })
}; };
{ {
let update = update.clone(); let update = Rc::clone(&update);
listener listener.replace(Rc::new(move |_| update()) as Rc<dyn Fn(web_sys::Event)>);
.replace(Box::new(move |_| update()) as Box<dyn CloneableFnMutWithArg<web_sys::Event>>);
} }
create_effect(move |_| update()); create_effect(move |_| update());

View file

@ -1,11 +1,11 @@
use crate::core::ElementMaybeSignal; use crate::core::ElementMaybeSignal;
use crate::use_event_listener::use_event_listener_with_options; use crate::use_event_listener::use_event_listener_with_options;
use crate::utils::CloneableFnWithArg;
use crate::{use_debounce_fn_with_arg, use_throttle_fn_with_arg_and_options, ThrottleOptions}; use crate::{use_debounce_fn_with_arg, use_throttle_fn_with_arg_and_options, ThrottleOptions};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::ev::EventDescriptor; use leptos::ev::EventDescriptor;
use leptos::*; use leptos::*;
use std::borrow::Cow; use std::borrow::Cow;
use std::rc::Rc;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
/// Reactive scroll position and state. /// Reactive scroll position and state.
@ -232,7 +232,7 @@ where
}); });
let on_scroll_end = { let on_scroll_end = {
let on_stop = options.on_stop.clone(); let on_stop = Rc::clone(&options.on_stop);
move |e| { move |e| {
if !is_scrolling.get_untracked() { if !is_scrolling.get_untracked() {
@ -328,7 +328,7 @@ where
}; };
let on_scroll_handler = { let on_scroll_handler = {
let on_scroll = options.on_scroll.clone(); let on_scroll = Rc::clone(&options.on_scroll);
move |e: web_sys::Event| { move |e: web_sys::Event| {
let target: web_sys::Element = event_target(&e); let target: web_sys::Element = event_target(&e);
@ -441,10 +441,10 @@ pub struct UseScrollOptions {
offset: ScrollOffset, offset: ScrollOffset,
/// Callback when scrolling is happening. /// Callback when scrolling is happening.
on_scroll: Box<dyn CloneableFnWithArg<web_sys::Event>>, on_scroll: Rc<dyn Fn(web_sys::Event)>,
/// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed). /// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed).
on_stop: Box<dyn CloneableFnWithArg<web_sys::Event>>, on_stop: Rc<dyn Fn(web_sys::Event)>,
/// Options passed to the `addEventListener("scroll", ...)` call /// Options passed to the `addEventListener("scroll", ...)` call
event_listener_options: web_sys::AddEventListenerOptions, event_listener_options: web_sys::AddEventListenerOptions,
@ -461,8 +461,8 @@ impl Default for UseScrollOptions {
throttle: 0.0, throttle: 0.0,
idle: 200.0, idle: 200.0,
offset: ScrollOffset::default(), offset: ScrollOffset::default(),
on_scroll: Box::new(|_| {}), on_scroll: Rc::new(|_| {}),
on_stop: Box::new(|_| {}), on_stop: Rc::new(|_| {}),
event_listener_options: Default::default(), event_listener_options: Default::default(),
behavior: Default::default(), behavior: Default::default(),
} }

View file

@ -73,7 +73,7 @@ pub fn use_throttle_fn<F, R>(
ms: impl Into<MaybeSignal<f64>> + 'static, 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: Fn() -> R + Clone + 'static,
R: 'static, R: 'static,
{ {
use_throttle_fn_with_options(func, ms, Default::default()) use_throttle_fn_with_options(func, ms, Default::default())
@ -86,10 +86,10 @@ pub fn use_throttle_fn_with_options<F, R>(
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: Fn() -> R + Clone + 'static,
R: 'static, R: 'static,
{ {
create_filter_wrapper(Box::new(throttle_filter(ms, options)), func) create_filter_wrapper(Rc::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.
@ -98,7 +98,7 @@ pub fn use_throttle_fn_with_arg<F, Arg, R>(
ms: impl Into<MaybeSignal<f64>> + 'static, 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: Fn(Arg) -> R + Clone + 'static,
Arg: Clone + 'static, Arg: Clone + 'static,
R: 'static, R: 'static,
{ {
@ -112,9 +112,9 @@ pub fn use_throttle_fn_with_arg_and_options<F, Arg, R>(
options: ThrottleOptions, options: ThrottleOptions,
) -> 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: Fn(Arg) -> R + Clone + 'static,
Arg: Clone + 'static, Arg: Clone + 'static,
R: 'static, R: 'static,
{ {
create_filter_wrapper_with_arg(Box::new(throttle_filter(ms, options)), func) create_filter_wrapper_with_arg(Rc::new(throttle_filter(ms, options)), func)
} }

View file

@ -1,7 +1,6 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))] #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
use cfg_if::cfg_if; use cfg_if::cfg_if;
use core::fmt;
use leptos::{leptos_dom::helpers::TimeoutHandle, *}; use leptos::{leptos_dom::helpers::TimeoutHandle, *};
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration; use std::time::Duration;
@ -12,8 +11,6 @@ use js_sys::Array;
use wasm_bindgen::{prelude::*, JsCast, JsValue}; use wasm_bindgen::{prelude::*, JsCast, JsValue};
use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket}; use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
use crate::utils::CloneableFnWithArg;
/// Creating and managing a [Websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) connection. /// Creating and managing a [Websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) connection.
/// ///
/// ## Demo /// ## Demo
@ -102,15 +99,24 @@ pub fn use_websocket_with_options(
> { > {
let url = url.to_string(); let url = url.to_string();
let UseWebSocketOptions {
on_open,
on_message,
on_message_bytes,
on_error,
on_close,
reconnect_limit,
reconnect_interval,
immediate,
protocols,
} = options;
let (ready_state, set_ready_state) = create_signal(ConnectionReadyState::Closed); let (ready_state, set_ready_state) = create_signal(ConnectionReadyState::Closed);
let (message, set_message) = create_signal(None); let (message, set_message) = create_signal(None);
let (message_bytes, set_message_bytes) = create_signal(None); let (message_bytes, set_message_bytes) = create_signal(None);
let ws_ref: StoredValue<Option<WebSocket>> = store_value(None); let ws_ref: StoredValue<Option<WebSocket>> = store_value(None);
let reconnect_limit = options.reconnect_limit;
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(None); let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(None);
let immediate = options.immediate;
let reconnect_times_ref: StoredValue<u64> = store_value(0); let reconnect_times_ref: StoredValue<u64> = store_value(0);
let unmounted_ref = store_value(false); let unmounted_ref = store_value(false);
@ -118,14 +124,6 @@ pub fn use_websocket_with_options(
let connect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(None); let connect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(None);
cfg_if! { if #[cfg(not(feature = "ssr"))] { cfg_if! { if #[cfg(not(feature = "ssr"))] {
let on_open_ref = store_value(options.on_open);
let on_message_ref = store_value(options.on_message);
let on_message_bytes_ref = store_value(options.on_message_bytes);
let on_error_ref = store_value(options.on_error);
let on_close_ref = store_value(options.on_close);
let reconnect_interval = options.reconnect_interval;
let protocols = options.protocols;
let reconnect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(None); let reconnect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(None);
reconnect_ref.set_value({ reconnect_ref.set_value({
@ -181,13 +179,14 @@ pub fn use_websocket_with_options(
// onopen handler // onopen handler
{ {
let on_open = Rc::clone(&on_open);
let onopen_closure = Closure::wrap(Box::new(move |e: Event| { let onopen_closure = Closure::wrap(Box::new(move |e: Event| {
if unmounted_ref.get_value() { if unmounted_ref.get_value() {
return; return;
} }
let callback = on_open_ref.get_value(); on_open(e);
callback(e);
set_ready_state.set(ConnectionReadyState::Open); set_ready_state.set(ConnectionReadyState::Open);
}) as Box<dyn FnMut(Event)>); }) as Box<dyn FnMut(Event)>);
@ -198,6 +197,9 @@ pub fn use_websocket_with_options(
// onmessage handler // onmessage handler
{ {
let on_message = Rc::clone(&on_message);
let on_message_bytes = Rc::clone(&on_message_bytes);
let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| { let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| {
if unmounted_ref.get_value() { if unmounted_ref.get_value() {
return; return;
@ -211,8 +213,7 @@ pub fn use_websocket_with_options(
}, },
|txt| { |txt| {
let txt = String::from(&txt); let txt = String::from(&txt);
let callback = on_message_ref.get_value(); on_message(txt.clone());
callback(txt.clone());
set_message.set(Some(txt)); set_message.set(Some(txt));
}, },
@ -221,8 +222,7 @@ pub fn use_websocket_with_options(
|array_buffer| { |array_buffer| {
let array = js_sys::Uint8Array::new(&array_buffer); let array = js_sys::Uint8Array::new(&array_buffer);
let array = array.to_vec(); let array = array.to_vec();
let callback = on_message_bytes_ref.get_value(); on_message_bytes(array.clone());
callback(array.clone());
set_message_bytes.set(Some(array)); set_message_bytes.set(Some(array));
}, },
@ -235,6 +235,8 @@ pub fn use_websocket_with_options(
// onerror handler // onerror handler
{ {
let on_error = Rc::clone(&on_error);
let onerror_closure = Closure::wrap(Box::new(move |e: Event| { let onerror_closure = Closure::wrap(Box::new(move |e: Event| {
if unmounted_ref.get_value() { if unmounted_ref.get_value() {
return; return;
@ -244,8 +246,7 @@ pub fn use_websocket_with_options(
reconnect(); reconnect();
} }
let callback = on_error_ref.get_value(); on_error(e);
callback(e);
set_ready_state.set(ConnectionReadyState::Closed); set_ready_state.set(ConnectionReadyState::Closed);
}) as Box<dyn FnMut(Event)>); }) as Box<dyn FnMut(Event)>);
@ -255,6 +256,8 @@ pub fn use_websocket_with_options(
// onclose handler // onclose handler
{ {
let on_close = Rc::clone(&on_close);
let onclose_closure = Closure::wrap(Box::new(move |e: CloseEvent| { let onclose_closure = Closure::wrap(Box::new(move |e: CloseEvent| {
if unmounted_ref.get_value() { if unmounted_ref.get_value() {
return; return;
@ -264,8 +267,7 @@ pub fn use_websocket_with_options(
reconnect(); reconnect();
} }
let callback = on_close_ref.get_value(); on_close(e);
callback(e);
set_ready_state.set(ConnectionReadyState::Closed); set_ready_state.set(ConnectionReadyState::Closed);
}) })
@ -348,15 +350,15 @@ pub fn use_websocket_with_options(
#[derive(DefaultBuilder)] #[derive(DefaultBuilder)]
pub struct UseWebSocketOptions { pub struct UseWebSocketOptions {
/// `WebSocket` connect callback. /// `WebSocket` connect callback.
on_open: Box<dyn CloneableFnWithArg<Event>>, on_open: Rc<dyn Fn(Event)>,
/// `WebSocket` message callback for text. /// `WebSocket` message callback for text.
on_message: Box<dyn CloneableFnWithArg<String>>, on_message: Rc<dyn Fn(String)>,
/// `WebSocket` message callback for binary. /// `WebSocket` message callback for binary.
on_message_bytes: Box<dyn CloneableFnWithArg<Vec<u8>>>, on_message_bytes: Rc<dyn Fn(Vec<u8>)>,
/// `WebSocket` error callback. /// `WebSocket` error callback.
on_error: Box<dyn CloneableFnWithArg<Event>>, on_error: Rc<dyn Fn(Event)>,
/// `WebSocket` close callback. /// `WebSocket` close callback.
on_close: Box<dyn CloneableFnWithArg<CloseEvent>>, on_close: Rc<dyn Fn(CloseEvent)>,
/// Retry times. Defaults to 3. /// Retry times. Defaults to 3.
reconnect_limit: u64, reconnect_limit: u64,
/// Retry interval in ms. Defaults to 3000. /// Retry interval in ms. Defaults to 3000.
@ -372,11 +374,11 @@ pub struct UseWebSocketOptions {
impl Default for UseWebSocketOptions { impl Default for UseWebSocketOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
on_open: Box::new(|_| {}), on_open: Rc::new(|_| {}),
on_message: Box::new(|_| {}), on_message: Rc::new(|_| {}),
on_message_bytes: Box::new(|_| {}), on_message_bytes: Rc::new(|_| {}),
on_error: Box::new(|_| {}), on_error: Rc::new(|_| {}),
on_close: Box::new(|_| {}), on_close: Rc::new(|_| {}),
reconnect_limit: 3, reconnect_limit: 3,
reconnect_interval: 3000, reconnect_interval: 3000,
immediate: true, immediate: true,

View file

@ -1,43 +0,0 @@
mod mut_;
mod mut_with_arg;
mod with_arg;
mod with_arg_and_return;
mod with_return;
pub use mut_::*;
pub use mut_with_arg::*;
use std::fmt::Debug;
pub use with_arg::*;
pub use with_arg_and_return::*;
pub use with_return::*;
pub trait CloneableFn: FnOnce() {
fn clone_box(&self) -> Box<dyn CloneableFn>;
}
impl<F> CloneableFn for F
where
F: FnOnce() + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn CloneableFn> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn CloneableFn> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl Default for Box<dyn CloneableFn> {
fn default() -> Self {
Box::new(|| {})
}
}
impl Debug for Box<dyn CloneableFn> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Box<dyn CloneableFn>",)
}
}

View file

@ -1,32 +0,0 @@
use std::fmt::Debug;
pub trait CloneableFnMut: FnMut() {
fn clone_box(&self) -> Box<dyn CloneableFnMut>;
}
impl<F> CloneableFnMut for F
where
F: FnMut() + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn CloneableFnMut> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn CloneableFnMut> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl Default for Box<dyn CloneableFnMut> {
fn default() -> Self {
Box::new(|| {})
}
}
impl Debug for Box<dyn CloneableFnMut> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Box<dyn CloneableFnMut>",)
}
}

View file

@ -1,36 +0,0 @@
use std::fmt::Debug;
pub trait CloneableFnMutWithArg<Arg>: FnMut(Arg) {
fn clone_box(&self) -> Box<dyn CloneableFnMutWithArg<Arg>>;
}
impl<F, Arg> CloneableFnMutWithArg<Arg> for F
where
F: FnMut(Arg) + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn CloneableFnMutWithArg<Arg>> {
Box::new(self.clone())
}
}
impl<Arg> Clone for Box<dyn CloneableFnMutWithArg<Arg>> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl<Arg> Default for Box<dyn CloneableFnMutWithArg<Arg>> {
fn default() -> Self {
Box::new(|_| {})
}
}
impl<Arg> Debug for Box<dyn CloneableFnMutWithArg<Arg>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Box<dyn CloneableFnMutWithArg<{}>>",
std::any::type_name::<Arg>()
)
}
}

View file

@ -1,36 +0,0 @@
use std::fmt::Debug;
pub trait CloneableFnWithArg<Arg>: FnOnce(Arg) {
fn clone_box(&self) -> Box<dyn CloneableFnWithArg<Arg>>;
}
impl<F, Arg> CloneableFnWithArg<Arg> for F
where
F: FnOnce(Arg) + Clone + 'static,
{
fn clone_box(&self) -> Box<dyn CloneableFnWithArg<Arg>> {
Box::new(self.clone())
}
}
impl<Arg> Clone for Box<dyn CloneableFnWithArg<Arg>> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl<Arg> Default for Box<dyn CloneableFnWithArg<Arg>> {
fn default() -> Self {
Box::new(|_| {})
}
}
impl<Arg> Debug for Box<dyn CloneableFnWithArg<Arg>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Box<dyn CloneableFnWithArg<{}>>",
std::any::type_name::<Arg>()
)
}
}

View file

@ -1,38 +0,0 @@
use std::fmt::Debug;
pub trait CloneableFnWithArgAndReturn<Arg, R>: FnOnce(Arg) -> R {
fn clone_box(&self) -> Box<dyn CloneableFnWithArgAndReturn<Arg, R>>;
}
impl<F, R, Arg> CloneableFnWithArgAndReturn<Arg, R> for F
where
F: FnMut(Arg) -> R + Clone + 'static,
R: 'static,
{
fn clone_box(&self) -> Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
Box::new(self.clone())
}
}
impl<R, Arg> Clone for Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl<R: Default + 'static, Arg> Default for Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
fn default() -> Self {
Box::new(|_| Default::default())
}
}
impl<R, Arg> Debug for Box<dyn CloneableFnWithArgAndReturn<Arg, R>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Box<dyn CloneableFnWithArgAndReturn<{}, {}>>",
std::any::type_name::<Arg>(),
std::any::type_name::<R>()
)
}
}

View file

@ -1,37 +0,0 @@
use std::fmt::Debug;
pub trait CloneableFnWithReturn<R>: FnOnce() -> R {
fn clone_box(&self) -> Box<dyn CloneableFnWithReturn<R>>;
}
impl<F, R> CloneableFnWithReturn<R> for F
where
F: FnOnce() -> R + Clone + 'static,
R: 'static,
{
fn clone_box(&self) -> Box<dyn CloneableFnWithReturn<R>> {
Box::new(self.clone())
}
}
impl<R> Clone for Box<dyn CloneableFnWithReturn<R>> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl<R: Default + 'static> Default for Box<dyn CloneableFnWithReturn<R>> {
fn default() -> Self {
Box::new(|| Default::default())
}
}
impl<R> Debug for Box<dyn CloneableFnWithReturn<R>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Box<dyn CloneableFnWithReturn<{}>>",
std::any::type_name::<R>()
)
}
}

View file

@ -1,6 +1,5 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))] #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
use crate::utils::CloneableFnWithReturn;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::leptos_dom::helpers::TimeoutHandle; use leptos::leptos_dom::helpers::TimeoutHandle;
@ -20,7 +19,7 @@ pub struct DebounceOptions {
pub fn debounce_filter<R>( pub fn debounce_filter<R>(
ms: impl Into<MaybeSignal<f64>>, ms: impl Into<MaybeSignal<f64>>,
options: DebounceOptions, options: DebounceOptions,
) -> impl Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone ) -> impl Fn(Rc<dyn Fn() -> R>) -> Rc<RefCell<Option<R>>> + Clone
where where
R: 'static, R: 'static,
{ {
@ -38,7 +37,7 @@ where
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<R>>| { move |_invoke: Rc<dyn Fn() -> 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();

View file

@ -4,40 +4,39 @@ mod throttle;
pub use debounce::*; pub use debounce::*;
pub use throttle::*; pub use throttle::*;
use crate::utils::{CloneableFnWithArgAndReturn, CloneableFnWithReturn};
use leptos::MaybeSignal; use leptos::MaybeSignal;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
macro_rules! BoxFilterFn { macro_rules! RcFilterFn {
($R:ident) => { ($R:ident) => {
Box<dyn CloneableFnWithArgAndReturn<Box<dyn CloneableFnWithReturn<$R>>, Rc<RefCell<Option<$R>>>>> Rc<dyn Fn(Rc<dyn Fn() -> $R>) -> Rc<RefCell<Option<$R>>>>
} }
} }
pub fn create_filter_wrapper<F, R>( pub fn create_filter_wrapper<F, R>(
filter: BoxFilterFn!(R), filter: RcFilterFn!(R),
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: Fn() -> R + Clone + 'static,
R: 'static, R: 'static,
{ {
move || filter.clone()(Box::new(func.clone())) move || Rc::clone(&filter)(Rc::new(func.clone()))
} }
pub fn create_filter_wrapper_with_arg<F, Arg, R>( pub fn create_filter_wrapper_with_arg<F, Arg, R>(
filter: BoxFilterFn!(R), filter: RcFilterFn!(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: Fn(Arg) -> R + Clone + 'static,
R: 'static, R: 'static,
Arg: Clone + 'static, Arg: Clone + 'static,
{ {
move |arg: Arg| { move |arg: Arg| {
let func = func.clone(); let func = func.clone();
filter.clone()(Box::new(move || func(arg))) Rc::clone(&filter)(Rc::new(move || func(arg.clone())))
} }
} }
@ -57,16 +56,16 @@ pub enum FilterOptions {
} }
impl FilterOptions { impl FilterOptions {
pub fn filter_fn<R>(&self) -> BoxFilterFn!(R) pub fn filter_fn<R>(&self) -> RcFilterFn!(R)
where where
R: 'static, R: 'static,
{ {
match self { match self {
FilterOptions::Debounce { ms, options } => Box::new(debounce_filter(*ms, *options)), FilterOptions::Debounce { ms, options } => Rc::new(debounce_filter(*ms, *options)),
FilterOptions::Throttle { ms, options } => Box::new(throttle_filter(*ms, *options)), FilterOptions::Throttle { ms, options } => Rc::new(throttle_filter(*ms, *options)),
FilterOptions::None => Box::new(|invoke: Box<dyn CloneableFnWithReturn<R>>| { FilterOptions::None => {
Rc::new(RefCell::new(Some(invoke()))) Rc::new(|invoke: Rc<dyn Fn() -> R>| Rc::new(RefCell::new(Some(invoke()))))
}), }
} }
} }
} }

View file

@ -1,6 +1,5 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))] #![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
use crate::utils::CloneableFnWithReturn;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use js_sys::Date; use js_sys::Date;
@ -31,7 +30,7 @@ impl Default for ThrottleOptions {
pub fn throttle_filter<R>( pub fn throttle_filter<R>(
ms: impl Into<MaybeSignal<f64>>, ms: impl Into<MaybeSignal<f64>>,
options: ThrottleOptions, options: ThrottleOptions,
) -> impl Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone ) -> impl Fn(Rc<dyn Fn() -> R>) -> Rc<RefCell<Option<R>>> + Clone
where where
R: 'static, R: 'static,
{ {
@ -50,7 +49,7 @@ where
let ms = ms.into(); let ms = ms.into();
move |mut _invoke: Box<dyn CloneableFnWithReturn<R>>| { move |mut _invoke: Rc<dyn Fn() -> R>| {
let duration = ms.get_untracked(); let duration = ms.get_untracked();
let elapsed = Date::now() - last_exec.get(); let elapsed = Date::now() - last_exec.get();

View file

@ -1,4 +1,3 @@
mod clonable_fn;
mod filters; mod filters;
mod is; mod is;
mod js_value_from_to_string; mod js_value_from_to_string;
@ -6,7 +5,6 @@ mod pausable;
mod signal_filtered; mod signal_filtered;
mod use_derive_signal; mod use_derive_signal;
pub use clonable_fn::*;
pub use filters::*; pub use filters::*;
pub use is::*; pub use is::*;
pub(crate) use js_value_from_to_string::*; pub(crate) use js_value_from_to_string::*;