mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-01-23 00:59:22 -05:00
finished use_scroll implementation
This commit is contained in:
parent
bbe145a42b
commit
25499083ef
15 changed files with 475 additions and 69 deletions
1
.idea/leptos-use.iml
generated
1
.idea/leptos-use.iml
generated
|
@ -18,6 +18,7 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/dist" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/dist" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_throttle_fn/dist" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_throttle_fn/dist" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_throttle_fn/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_throttle_fn/target" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_debounce_fn/dist" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_debounce_fn/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_debounce_fn/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
|
@ -14,6 +14,6 @@ repository = "https://github.com/Synphonyte/leptos-use"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
leptos = "0.3"
|
leptos = "0.3"
|
||||||
web-sys = { version = "0.3", features = ["ScrollToOptions", "ScrollBehavior"] }
|
web-sys = { version = "0.3", features = ["ScrollToOptions", "ScrollBehavior", "CssStyleDeclaration"] }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
|
@ -24,7 +24,7 @@ def build_and_copy_demo(category, md_name):
|
||||||
example_output_path = os.path.join(example_dir, "dist")
|
example_output_path = os.path.join(example_dir, "dist")
|
||||||
target_path = os.path.join("book", category, name, "demo")
|
target_path = os.path.join("book", category, name, "demo")
|
||||||
|
|
||||||
print(f"Copying demo from {example_output_path} to {target_path}")
|
print(f"Copying demo from {example_output_path} -> {target_path}")
|
||||||
|
|
||||||
shutil.copytree(example_output_path, target_path,
|
shutil.copytree(example_output_path, target_path,
|
||||||
dirs_exist_ok=True)
|
dirs_exist_ok=True)
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
|
|
||||||
- [use_event_listener](browser/use_event_listener.md)
|
- [use_event_listener](browser/use_event_listener.md)
|
||||||
|
|
||||||
|
# Sensors
|
||||||
|
|
||||||
|
- [use_scroll](sensors/use_scroll.md)
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
|
|
||||||
- [use_debounce_fn](utilities/use_debounce_fn.md)
|
- [use_debounce_fn](utilities/use_debounce_fn.md)
|
||||||
|
|
|
@ -5,11 +5,19 @@ import re
|
||||||
def main():
|
def main():
|
||||||
name = sys.argv[1]
|
name = sys.argv[1]
|
||||||
file_name = f"../../../../src/{name}.rs"
|
file_name = f"../../../../src/{name}.rs"
|
||||||
|
|
||||||
|
types = [];
|
||||||
|
|
||||||
with open(file_name) as f:
|
with open(file_name) as f:
|
||||||
in_code_block = False
|
in_code_block = False
|
||||||
doc_comment_started = False
|
doc_comment_started = False
|
||||||
|
initial_doc_finished = False
|
||||||
|
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if line.startswith("///"):
|
if initial_doc_finished:
|
||||||
|
process_further_line(line, name, types)
|
||||||
|
|
||||||
|
elif line.startswith("///"):
|
||||||
doc_comment_started = True
|
doc_comment_started = True
|
||||||
line = line.strip().replace("/// ", "").replace("///", "")
|
line = line.strip().replace("/// ", "").replace("///", "")
|
||||||
if "```" in line:
|
if "```" in line:
|
||||||
|
@ -20,11 +28,33 @@ def main():
|
||||||
line = process_line(line, name)
|
line = process_line(line, name)
|
||||||
|
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
elif doc_comment_started:
|
elif doc_comment_started:
|
||||||
return
|
initial_doc_finished = True
|
||||||
|
|
||||||
|
add_types_paragraph(types)
|
||||||
|
add_source_paragraph(name)
|
||||||
|
|
||||||
|
|
||||||
|
def add_types_paragraph(types):
|
||||||
|
if types:
|
||||||
|
print("\n## Types\n")
|
||||||
|
print("\n".join(types))
|
||||||
|
|
||||||
|
|
||||||
|
def add_source_paragraph(name):
|
||||||
|
print("\n## Source\n")
|
||||||
|
|
||||||
|
source_url = f"https://github.com/Synphonyte/leptos-use/blob/main/src/{name}.rs"
|
||||||
|
demo_url = f"https://github.com/Synphonyte/leptos-use/tree/main/examples/{name}"
|
||||||
|
docs_url = f"https://docs.rs/leptos-use/latest/leptos_use/fn.{name}.html"
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"<a href=\"{source_url}\" target=\"_blank\">Source</a> • <a href=\"{demo_url}\" target=\"_blank\">Demo</a> • <a href=\"{docs_url}\" target=\"_blank\">Docs</a>")
|
||||||
|
|
||||||
|
|
||||||
interal_doc_link_pattern = re.compile(r"\[`([^]]+)\`](?!\()")
|
interal_doc_link_pattern = re.compile(r"\[`([^]]+)\`](?!\()")
|
||||||
|
ident_pattern = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*\b")
|
||||||
|
|
||||||
|
|
||||||
def process_line(line, name):
|
def process_line(line, name):
|
||||||
|
@ -37,7 +67,6 @@ def process_line(line, name):
|
||||||
<a class="demo-source" href="{example_link}/src/main.rs" target="_blank">source <i class="fa fa-github"></i></a>
|
<a class="demo-source" href="{example_link}/src/main.rs" target="_blank">source <i class="fa fa-github"></i></a>
|
||||||
<div id="demo-anchor"></div>
|
<div id="demo-anchor"></div>
|
||||||
</div>'''
|
</div>'''
|
||||||
|
|
||||||
else:
|
else:
|
||||||
result = re.sub(interal_doc_link_pattern,
|
result = re.sub(interal_doc_link_pattern,
|
||||||
r"[`\1`](https://docs.rs/leptos-use/latest/leptos_use/fn.\1.html)",
|
r"[`\1`](https://docs.rs/leptos-use/latest/leptos_use/fn.\1.html)",
|
||||||
|
@ -46,5 +75,20 @@ def process_line(line, name):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def process_further_line(line, name, types):
|
||||||
|
if line.startswith("pub enum"):
|
||||||
|
append_type(line, "enum", types)
|
||||||
|
elif line.startswith("pub struct"):
|
||||||
|
append_type(line, "struct", types)
|
||||||
|
|
||||||
|
|
||||||
|
def append_type(line, ty, types):
|
||||||
|
start_index = len(f"pub {ty} ")
|
||||||
|
m = re.search(ident_pattern, line[start_index:])
|
||||||
|
if m is not None:
|
||||||
|
ident = m.group(0)
|
||||||
|
types.append(f"- [`{ty} {ident}`](https://docs.rs/leptos-use/latest/leptos_use/{ty}.{ident}.html)")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -8,7 +8,7 @@ cargo add leptos-use
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
- [Examples Directory](https://github.com/Synphonyte/leptos-use/tree/master/examples)
|
- [Examples Directory](https://github.com/Synphonyte/leptos-use/tree/main/examples)
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# use_event_listener
|
# use_scroll
|
||||||
|
|
||||||
<!-- cmdrun python3 ../extract_doc_comment.py use_scroll -->
|
<!-- cmdrun python3 ../extract_doc_comment.py use_scroll -->
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,11 +1,15 @@
|
||||||
|
//! Collection of essential Leptos utilities inspired by SolidJS USE / VueUse
|
||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod use_debounce_fn;
|
mod use_debounce_fn;
|
||||||
pub mod use_event_listener;
|
mod use_event_listener;
|
||||||
pub mod use_scroll;
|
mod use_scroll;
|
||||||
pub mod use_throttle_fn;
|
mod use_throttle_fn;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub use use_debounce_fn::*;
|
pub use use_debounce_fn::*;
|
||||||
pub use use_event_listener::use_event_listener;
|
pub use use_event_listener::use_event_listener;
|
||||||
pub use use_scroll::*;
|
pub use use_scroll::*;
|
||||||
pub use use_throttle_fn::*;
|
pub use use_throttle_fn::*;
|
||||||
|
|
||||||
|
extern crate self as leptos_use;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::utils::{
|
pub use crate::utils::DebounceOptions;
|
||||||
create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter, DebounceOptions,
|
use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter};
|
||||||
};
|
|
||||||
use leptos::MaybeSignal;
|
use leptos::MaybeSignal;
|
||||||
|
|
||||||
/// Debounce execution of a function.
|
/// Debounce execution of a function.
|
||||||
|
@ -9,7 +8,7 @@ use leptos::MaybeSignal;
|
||||||
///
|
///
|
||||||
/// ## Demo
|
/// ## Demo
|
||||||
///
|
///
|
||||||
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/master/examples/use_debounce_fn)
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_debounce_fn)
|
||||||
///
|
///
|
||||||
/// ## Usage
|
/// ## Usage
|
||||||
///
|
///
|
||||||
|
@ -87,7 +86,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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>(func: F, ms: impl Into<MaybeSignal<f64>>) -> impl Fn(Arg)
|
pub fn use_debounce_fn_with_arg<F, Arg>(
|
||||||
|
func: F,
|
||||||
|
ms: impl Into<MaybeSignal<f64>>,
|
||||||
|
) -> impl Fn(Arg) + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) + Clone + 'static,
|
F: FnOnce(Arg) + Clone + 'static,
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
|
@ -95,12 +97,12 @@ where
|
||||||
use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::<Option<f64>>::default())
|
use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::<Option<f64>>::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_debounce_fn_with_arg`] with debounce options. See the docs for [`use_debounce_fn`] for how to use.
|
/// Version of [`use_debounce_fn_with_arg`] with debounce options.
|
||||||
pub fn use_debounce_fn_with_arg_and_options<F, Arg, W>(
|
pub fn use_debounce_fn_with_arg_and_options<F, Arg, W>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>>,
|
||||||
options: DebounceOptions<W>,
|
options: DebounceOptions<W>,
|
||||||
) -> impl Fn(Arg)
|
) -> impl Fn(Arg) + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) + Clone + 'static,
|
F: FnOnce(Arg) + Clone + 'static,
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
|
|
|
@ -80,13 +80,35 @@ use wasm_bindgen::JsCast;
|
||||||
/// To avoid that you can put the logic inside a
|
/// To avoid that you can put the logic inside a
|
||||||
/// [`create_effect`](https://docs.rs/leptos/latest/leptos/fn.create_effect.html) hook
|
/// [`create_effect`](https://docs.rs/leptos/latest/leptos/fn.create_effect.html) hook
|
||||||
/// which only runs client side.
|
/// which only runs client side.
|
||||||
#[allow(unused_must_use)]
|
|
||||||
pub fn use_event_listener<Ev, El, T, F>(
|
pub fn use_event_listener<Ev, El, T, F>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
target: El,
|
target: El,
|
||||||
event: Ev,
|
event: Ev,
|
||||||
handler: F,
|
handler: F,
|
||||||
) -> Box<dyn Fn()>
|
) -> Box<dyn Fn()>
|
||||||
|
where
|
||||||
|
Ev: EventDescriptor + 'static,
|
||||||
|
(Scope, El): Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
|
||||||
|
T: Into<web_sys::EventTarget> + Clone + 'static,
|
||||||
|
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
|
||||||
|
{
|
||||||
|
use_event_listener_with_options(
|
||||||
|
cx,
|
||||||
|
target,
|
||||||
|
event,
|
||||||
|
handler,
|
||||||
|
web_sys::AddEventListenerOptions::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Version of [`use_event_listener`] that takes `web_sys::AddEventListenerOptions`. See the docs for [`use_event_listener`] for how to use.
|
||||||
|
pub fn use_event_listener_with_options<Ev, El, T, F>(
|
||||||
|
cx: Scope,
|
||||||
|
target: El,
|
||||||
|
event: Ev,
|
||||||
|
handler: F,
|
||||||
|
options: web_sys::AddEventListenerOptions,
|
||||||
|
) -> Box<dyn Fn()>
|
||||||
where
|
where
|
||||||
Ev: EventDescriptor + 'static,
|
Ev: EventDescriptor + 'static,
|
||||||
(Scope, El): Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
|
(Scope, El): Into<ElementMaybeSignal<T, web_sys::EventTarget>>,
|
||||||
|
@ -112,8 +134,11 @@ where
|
||||||
let cleanup_prev_element = if let Some(element) = element {
|
let cleanup_prev_element = if let Some(element) = element {
|
||||||
let element = element.into();
|
let element = element.into();
|
||||||
|
|
||||||
_ = element
|
_ = element.add_event_listener_with_callback_and_add_event_listener_options(
|
||||||
.add_event_listener_with_callback(&event_name, closure_js.as_ref().unchecked_ref());
|
&event_name,
|
||||||
|
closure_js.as_ref().unchecked_ref(),
|
||||||
|
&options,
|
||||||
|
);
|
||||||
|
|
||||||
let clean = cleanup.clone();
|
let clean = cleanup.clone();
|
||||||
Rc::new(RefCell::new(Box::new(move || {
|
Rc::new(RefCell::new(Box::new(move || {
|
||||||
|
@ -133,15 +158,18 @@ where
|
||||||
if let Some(element) = element {
|
if let Some(element) = element {
|
||||||
let element = element.into();
|
let element = element.into();
|
||||||
|
|
||||||
_ = element
|
_ = element.add_event_listener_with_callback_and_add_event_listener_options(
|
||||||
.add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref());
|
&event_name,
|
||||||
|
closure.as_ref().unchecked_ref(),
|
||||||
|
&options,
|
||||||
|
);
|
||||||
|
|
||||||
let clean = cleanup.clone();
|
let clean = cleanup.clone();
|
||||||
cleanup_prev_el.replace(Box::new(move || {
|
let _ = cleanup_prev_el.replace(Box::new(move || {
|
||||||
clean(&element);
|
clean(&element);
|
||||||
}) as Box<dyn Fn()>);
|
}) as Box<dyn Fn()>);
|
||||||
} else {
|
} else {
|
||||||
cleanup_prev_el.replace(Box::new(move || {}) as Box<dyn Fn()>);
|
let _ = cleanup_prev_el.replace(Box::new(move || {}) as Box<dyn Fn()>);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,162 @@
|
||||||
use crate::core::ElementMaybeSignal;
|
use crate::core::ElementMaybeSignal;
|
||||||
use crate::use_debounce_fn_with_arg;
|
use crate::use_event_listener::use_event_listener_with_options;
|
||||||
use crate::utils::{CloneableFn, CloneableFnWithArg, CloneableFnWithReturn};
|
use crate::utils::CloneableFnWithArg;
|
||||||
|
use crate::{use_debounce_fn_with_arg, use_throttle_fn_with_arg_and_options, ThrottleOptions};
|
||||||
|
use leptos::ev::EventDescriptor;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
/// We have to check if the scroll amount is close enough to some threshold in order to
|
/// Reactive scroll position and state.
|
||||||
/// more accurately calculate arrivedState. This is because scrollTop/scrollLeft are non-rounded
|
/// ## Demo
|
||||||
/// numbers, while scrollHeight/scrollWidth and clientHeight/clientWidth are rounded.
|
///
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#determine_if_an_element_has_been_totally_scrolled
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_scroll)
|
||||||
const ARRIVED_STATE_THRESHOLD_PIXELS: f64 = 1.0;
|
///
|
||||||
|
/// ## Usage
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos::ev::resize;
|
||||||
|
/// # use leptos_use::{use_scroll, UseScrollReturn};
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// let element = create_node_ref(cx);
|
||||||
|
///
|
||||||
|
/// let UseScrollReturn {
|
||||||
|
/// x, y, set_x, set_y, is_scrolling, arrived_state, directions, ..
|
||||||
|
/// } = use_scroll(cx, element);
|
||||||
|
///
|
||||||
|
/// view! { cx,
|
||||||
|
/// <div node_ref=element>"..."</div>
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### With Offsets
|
||||||
|
///
|
||||||
|
/// You can provide offsets when you use [`use_scroll_with_options`].
|
||||||
|
/// These offsets are threshold in pixels when we a side is considered to have arrived. This is reflected in the return field `arrived_state`.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos::ev::resize;
|
||||||
|
/// # use leptos_use::{use_scroll_with_options, UseScrollReturn, UseScrollOptions, ScrollOffset};
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// # let element = create_node_ref(cx);
|
||||||
|
/// #
|
||||||
|
/// let UseScrollReturn {
|
||||||
|
/// x, y, set_x, set_y, is_scrolling, arrived_state, directions, ..
|
||||||
|
/// } = use_scroll_with_options(cx, element, UseScrollOptions {
|
||||||
|
/// offset: ScrollOffset {
|
||||||
|
/// top: 30.0,
|
||||||
|
/// bottom: 30.0,
|
||||||
|
/// right: 30.0,
|
||||||
|
/// left: 30.0,
|
||||||
|
/// },
|
||||||
|
/// ..Default::default()
|
||||||
|
/// });
|
||||||
|
/// #
|
||||||
|
/// # view! { cx,
|
||||||
|
/// # <div node_ref=element>"..."</div>
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Setting Scroll Position
|
||||||
|
///
|
||||||
|
/// Set the `x` and `y` values to make the element scroll to that position.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos::ev::resize;
|
||||||
|
/// # use leptos_use::{use_scroll, UseScrollReturn};
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// let element = create_node_ref(cx);
|
||||||
|
///
|
||||||
|
/// let UseScrollReturn {
|
||||||
|
/// x, y, set_x, set_y, ..
|
||||||
|
/// } = use_scroll(cx, element);
|
||||||
|
///
|
||||||
|
/// view! { cx,
|
||||||
|
/// <div node_ref=element>"..."</div>
|
||||||
|
/// <button on:click=move |_| set_x(x() + 10.0)>"Scroll right 10px"</button>
|
||||||
|
/// <button on:click=move |_| set_y(y() + 10.0)>"Scroll down 10px"</button>
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Smooth Scrolling
|
||||||
|
///
|
||||||
|
/// Set `behavior: smooth` to enable smooth scrolling. The `behavior` option defaults to `auto`,
|
||||||
|
/// which means no smooth scrolling. See the `behavior` option on
|
||||||
|
/// [Element.scrollTo](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo) for more information.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos::ev::resize;
|
||||||
|
/// # use leptos_use::{use_scroll_with_options, UseScrollReturn, UseScrollOptions, ScrollBehavior};
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// # let element = create_node_ref(cx);
|
||||||
|
/// #
|
||||||
|
/// let UseScrollReturn {
|
||||||
|
/// x, y, set_x, set_y, ..
|
||||||
|
/// } = use_scroll_with_options(cx, element, UseScrollOptions {
|
||||||
|
/// behavior: ScrollBehavior::Smooth.into(),
|
||||||
|
/// ..Default::default()
|
||||||
|
/// });
|
||||||
|
/// #
|
||||||
|
/// # view! { cx,
|
||||||
|
/// # <div node_ref=element>"..."</div>
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// or as a `Signal`:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos::ev::resize;
|
||||||
|
/// # use leptos_use::{use_scroll_with_options, UseScrollReturn, UseScrollOptions, ScrollBehavior};
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// # let element = create_node_ref(cx);
|
||||||
|
/// #
|
||||||
|
/// let (smooth, set_smoot) = create_signal(cx, false);
|
||||||
|
///
|
||||||
|
/// let behavior = Signal::derive(cx, move || {
|
||||||
|
/// if smooth() { ScrollBehavior::Smooth } else { ScrollBehavior::Auto }
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// let UseScrollReturn {
|
||||||
|
/// x, y, set_x, set_y, ..
|
||||||
|
/// } = use_scroll_with_options(cx, element, UseScrollOptions {
|
||||||
|
/// behavior: behavior.into(),
|
||||||
|
/// ..Default::default()
|
||||||
|
/// });
|
||||||
|
/// #
|
||||||
|
/// # view! { cx,
|
||||||
|
/// # <div node_ref=element>"..."</div>
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn use_scroll<El, T>(cx: Scope, element: El) -> UseScrollReturn
|
||||||
|
where
|
||||||
|
El: Clone,
|
||||||
|
(Scope, El): Into<ElementMaybeSignal<T, web_sys::Element>>,
|
||||||
|
T: Into<web_sys::Element> + Clone + 'static,
|
||||||
|
{
|
||||||
|
use_scroll_with_options(cx, element, Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Version of [`use_scroll`] with options. See [`use_scroll`] for how to use.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub fn use_scroll_with_options<El, T>(
|
pub fn use_scroll_with_options<El, T>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
|
@ -16,25 +164,25 @@ pub fn use_scroll_with_options<El, T>(
|
||||||
options: UseScrollOptions,
|
options: UseScrollOptions,
|
||||||
) -> UseScrollReturn
|
) -> UseScrollReturn
|
||||||
where
|
where
|
||||||
|
El: Clone,
|
||||||
(Scope, El): Into<ElementMaybeSignal<T, web_sys::Element>>,
|
(Scope, El): Into<ElementMaybeSignal<T, web_sys::Element>>,
|
||||||
T: Into<web_sys::Element> + Clone + 'static,
|
T: Into<web_sys::Element> + Clone + 'static,
|
||||||
{
|
{
|
||||||
// TODO : implement
|
|
||||||
|
|
||||||
let (internal_x, set_internal_x) = create_signal(cx, 0.0);
|
let (internal_x, set_internal_x) = create_signal(cx, 0.0);
|
||||||
let (internal_y, set_internal_y) = create_signal(cx, 0.0);
|
let (internal_y, set_internal_y) = create_signal(cx, 0.0);
|
||||||
|
|
||||||
let signal = (cx, element).into();
|
let signal = (cx, element).into();
|
||||||
let behavior = options.behavior;
|
let behavior = options.behavior;
|
||||||
|
|
||||||
|
let sig = signal.clone();
|
||||||
let scroll_to = move |x: Option<f64>, y: Option<f64>| {
|
let scroll_to = move |x: Option<f64>, y: Option<f64>| {
|
||||||
let element = signal.get_untracked();
|
let element = sig.get_untracked();
|
||||||
|
|
||||||
if let Some(element) = element {
|
if let Some(element) = element {
|
||||||
let element = element.into();
|
let element = element.into();
|
||||||
|
|
||||||
let mut scroll_options = web_sys::ScrollToOptions::new();
|
let mut scroll_options = web_sys::ScrollToOptions::new();
|
||||||
scroll_options.behavior(behavior.into());
|
scroll_options.behavior(behavior.get_untracked().into());
|
||||||
|
|
||||||
if let Some(x) = x {
|
if let Some(x) = x {
|
||||||
scroll_options.left(x);
|
scroll_options.left(x);
|
||||||
|
@ -54,7 +202,8 @@ where
|
||||||
let set_y = Box::new(move |y| scroll(None, Some(y)));
|
let set_y = Box::new(move |y| scroll(None, Some(y)));
|
||||||
|
|
||||||
let (is_scrolling, set_is_scrolling) = create_signal(cx, false);
|
let (is_scrolling, set_is_scrolling) = create_signal(cx, false);
|
||||||
let (arrived_state, set_arrived_state) = create_signal(
|
|
||||||
|
let arrived_state = create_rw_signal(
|
||||||
cx,
|
cx,
|
||||||
Directions {
|
Directions {
|
||||||
left: true,
|
left: true,
|
||||||
|
@ -63,7 +212,7 @@ where
|
||||||
bottom: false,
|
bottom: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let (directions, set_directions) = create_signal(
|
let directions = create_rw_signal(
|
||||||
cx,
|
cx,
|
||||||
Directions {
|
Directions {
|
||||||
left: false,
|
left: false,
|
||||||
|
@ -73,23 +222,173 @@ where
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let on_stop = options.on_stop;
|
let on_stop = options.on_stop.clone();
|
||||||
let on_scroll_end = move |e| {
|
let on_scroll_end = move |e| {
|
||||||
if !is_scrolling.get() {
|
if !is_scrolling.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_is_scrolling(false);
|
set_is_scrolling(false);
|
||||||
set_directions.update(|directions| {
|
directions.update(|directions| {
|
||||||
directions.left = false;
|
directions.left = false;
|
||||||
directions.right = false;
|
directions.right = false;
|
||||||
directions.top = false;
|
directions.top = false;
|
||||||
directions.bottom = false;
|
directions.bottom = false;
|
||||||
on_stop(e);
|
on_stop.clone()(e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
let throttle = options.throttle;
|
||||||
|
|
||||||
let on_scroll_end_debounced =
|
let on_scroll_end_debounced =
|
||||||
use_debounce_fn_with_arg(on_scroll_end, options.throttle + options.idle);
|
use_debounce_fn_with_arg(on_scroll_end.clone(), throttle + options.idle);
|
||||||
|
|
||||||
|
let offset = options.offset;
|
||||||
|
|
||||||
|
let set_arrived_state = move |target: web_sys::Element| {
|
||||||
|
let style = window()
|
||||||
|
.get_computed_style(&target)
|
||||||
|
.expect("failed to get computed style");
|
||||||
|
|
||||||
|
if let Some(style) = style {
|
||||||
|
let display = style
|
||||||
|
.get_property_value("display")
|
||||||
|
.expect("failed to get display");
|
||||||
|
let flex_direction = style
|
||||||
|
.get_property_value("flex-direction")
|
||||||
|
.expect("failed to get flex-direction");
|
||||||
|
|
||||||
|
let scroll_left = target.scroll_left() as f64;
|
||||||
|
let scroll_left_abs = scroll_left.abs();
|
||||||
|
|
||||||
|
directions.update(|directions| {
|
||||||
|
directions.left = scroll_left < internal_x.get();
|
||||||
|
directions.right = scroll_left > internal_x.get();
|
||||||
|
});
|
||||||
|
|
||||||
|
let left = scroll_left_abs <= offset.left;
|
||||||
|
let right = scroll_left_abs + target.client_width() as f64
|
||||||
|
>= target.scroll_width() as f64 - offset.right - ARRIVED_STATE_THRESHOLD_PIXELS;
|
||||||
|
|
||||||
|
arrived_state.update(|arrived_state| {
|
||||||
|
if display == "flex" && flex_direction == "row-reverse" {
|
||||||
|
arrived_state.left = right;
|
||||||
|
arrived_state.right = left;
|
||||||
|
} else {
|
||||||
|
arrived_state.left = left;
|
||||||
|
arrived_state.right = right;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
set_internal_x(scroll_left);
|
||||||
|
|
||||||
|
let mut scroll_top = target.scroll_top() as f64;
|
||||||
|
|
||||||
|
// patch for mobile compatibility
|
||||||
|
if target == document().unchecked_into::<web_sys::Element>() && scroll_top == 0.0 {
|
||||||
|
scroll_top = document().body().expect("failed to get body").scroll_top() as f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scroll_top_abs = scroll_top.abs();
|
||||||
|
|
||||||
|
directions.update(|directions| {
|
||||||
|
directions.top = scroll_top < internal_y.get();
|
||||||
|
directions.bottom = scroll_top > internal_y.get();
|
||||||
|
});
|
||||||
|
|
||||||
|
let top = scroll_top_abs <= offset.top;
|
||||||
|
let bottom = scroll_top_abs + target.client_height() as f64
|
||||||
|
>= target.scroll_height() as f64 - offset.bottom - ARRIVED_STATE_THRESHOLD_PIXELS;
|
||||||
|
|
||||||
|
// reverse columns and rows behave exactly the other way around,
|
||||||
|
// bottom is treated as top and top is treated as the negative version of bottom
|
||||||
|
arrived_state.update(|arrived_state| {
|
||||||
|
if display == "flex" && flex_direction == "column-reverse" {
|
||||||
|
arrived_state.top = bottom;
|
||||||
|
arrived_state.bottom = top;
|
||||||
|
} else {
|
||||||
|
arrived_state.top = top;
|
||||||
|
arrived_state.bottom = bottom;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
set_internal_y(scroll_top);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_scroll = options.on_scroll.clone();
|
||||||
|
|
||||||
|
let on_scroll_handler = move |e: web_sys::Event| {
|
||||||
|
let target: web_sys::Element = event_target(&e);
|
||||||
|
|
||||||
|
set_arrived_state(target);
|
||||||
|
set_is_scrolling(true);
|
||||||
|
on_scroll_end_debounced.clone()(e.clone());
|
||||||
|
on_scroll.clone()(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
let sig = signal.clone();
|
||||||
|
let target = Signal::derive(cx, move || {
|
||||||
|
let element = sig.get();
|
||||||
|
element.map(|element| element.into().unchecked_into::<web_sys::EventTarget>())
|
||||||
|
});
|
||||||
|
|
||||||
|
if throttle >= 0.0 {
|
||||||
|
let handler = move |e: web_sys::Event| {
|
||||||
|
let _ = use_throttle_fn_with_arg_and_options(
|
||||||
|
on_scroll_handler.clone(),
|
||||||
|
throttle,
|
||||||
|
ThrottleOptions {
|
||||||
|
trailing: true,
|
||||||
|
leading: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let _ = use_event_listener_with_options::<
|
||||||
|
_,
|
||||||
|
Signal<Option<web_sys::EventTarget>>,
|
||||||
|
web_sys::EventTarget,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
|
cx,
|
||||||
|
target,
|
||||||
|
ev::scroll,
|
||||||
|
handler,
|
||||||
|
options.event_listener_options.clone(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let _ = use_event_listener_with_options::<
|
||||||
|
_,
|
||||||
|
Signal<Option<web_sys::EventTarget>>,
|
||||||
|
web_sys::EventTarget,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
|
cx,
|
||||||
|
target,
|
||||||
|
ev::scroll,
|
||||||
|
on_scroll_handler,
|
||||||
|
options.event_listener_options.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = use_event_listener_with_options::<
|
||||||
|
_,
|
||||||
|
Signal<Option<web_sys::EventTarget>>,
|
||||||
|
web_sys::EventTarget,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
|
cx,
|
||||||
|
target,
|
||||||
|
scrollend,
|
||||||
|
on_scroll_end,
|
||||||
|
options.event_listener_options,
|
||||||
|
);
|
||||||
|
|
||||||
|
let measure = Box::new(move || {
|
||||||
|
let el = signal.get_untracked();
|
||||||
|
if let Some(el) = el {
|
||||||
|
let el = el.into();
|
||||||
|
set_arrived_state(el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
UseScrollReturn {
|
UseScrollReturn {
|
||||||
x: internal_x.into(),
|
x: internal_x.into(),
|
||||||
|
@ -99,10 +398,18 @@ where
|
||||||
is_scrolling: is_scrolling.into(),
|
is_scrolling: is_scrolling.into(),
|
||||||
arrived_state: arrived_state.into(),
|
arrived_state: arrived_state.into(),
|
||||||
directions: directions.into(),
|
directions: directions.into(),
|
||||||
|
measure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We have to check if the scroll amount is close enough to some threshold in order to
|
||||||
|
/// more accurately calculate arrivedState. This is because scrollTop/scrollLeft are non-rounded
|
||||||
|
/// numbers, while scrollHeight/scrollWidth and clientHeight/clientWidth are rounded.
|
||||||
|
/// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#determine_if_an_element_has_been_totally_scrolled
|
||||||
|
const ARRIVED_STATE_THRESHOLD_PIXELS: f64 = 1.0;
|
||||||
|
|
||||||
/// Options for [`use_scroll`].
|
/// Options for [`use_scroll`].
|
||||||
|
#[derive(Default)]
|
||||||
pub struct UseScrollOptions {
|
pub struct UseScrollOptions {
|
||||||
/// Throttle time in milliseconds for the scroll events. Defaults to 0 (disabled).
|
/// Throttle time in milliseconds for the scroll events. Defaults to 0 (disabled).
|
||||||
pub throttle: f64,
|
pub throttle: f64,
|
||||||
|
@ -115,7 +422,7 @@ pub struct UseScrollOptions {
|
||||||
pub offset: ScrollOffset,
|
pub offset: ScrollOffset,
|
||||||
|
|
||||||
/// Callback when scrolling is happening.
|
/// Callback when scrolling is happening.
|
||||||
pub on_scroll: Box<dyn CloneableFn>,
|
pub on_scroll: Box<dyn CloneableFnWithArg<web_sys::Event>>,
|
||||||
|
|
||||||
/// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed).
|
/// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed).
|
||||||
pub on_stop: Box<dyn CloneableFnWithArg<web_sys::Event>>,
|
pub on_stop: Box<dyn CloneableFnWithArg<web_sys::Event>>,
|
||||||
|
@ -125,23 +432,11 @@ pub struct UseScrollOptions {
|
||||||
|
|
||||||
/// When changing the `x` or `y` signals this specifies the scroll behaviour.
|
/// When changing the `x` or `y` signals this specifies the scroll behaviour.
|
||||||
/// Can be `Auto` (= not smooth) or `Smooth`. Defaults to `Auto`.
|
/// Can be `Auto` (= not smooth) or `Smooth`. Defaults to `Auto`.
|
||||||
pub behavior: ScrollBehavior,
|
pub behavior: MaybeSignal<ScrollBehavior>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UseScrollOptions {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
throttle: 0.0,
|
|
||||||
idle: 200.0,
|
|
||||||
offset: Default::default(),
|
|
||||||
on_scroll: Default::default(),
|
|
||||||
on_stop: Default::default(),
|
|
||||||
event_listener_options: Default::default(),
|
|
||||||
behavior: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The scroll behavior.
|
||||||
|
/// Can be `Auto` (= not smooth) or `Smooth`. Defaults to `Auto`.
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
pub enum ScrollBehavior {
|
pub enum ScrollBehavior {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -158,6 +453,7 @@ impl Into<web_sys::ScrollBehavior> for ScrollBehavior {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The return value of [`use_scroll`].
|
||||||
pub struct UseScrollReturn {
|
pub struct UseScrollReturn {
|
||||||
pub x: Signal<f64>,
|
pub x: Signal<f64>,
|
||||||
pub set_x: Box<dyn Fn(f64)>,
|
pub set_x: Box<dyn Fn(f64)>,
|
||||||
|
@ -166,6 +462,7 @@ pub struct UseScrollReturn {
|
||||||
pub is_scrolling: Signal<bool>,
|
pub is_scrolling: Signal<bool>,
|
||||||
pub arrived_state: Signal<Directions>,
|
pub arrived_state: Signal<Directions>,
|
||||||
pub directions: Signal<Directions>,
|
pub directions: Signal<Directions>,
|
||||||
|
pub measure: Box<dyn Fn()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -177,9 +474,32 @@ pub struct Directions {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone)]
|
||||||
|
/// Threshold in pixels when we consider a side to have arrived (`UseScrollReturn::arrived_state`).
|
||||||
pub struct ScrollOffset {
|
pub struct ScrollOffset {
|
||||||
pub left: f64,
|
pub left: f64,
|
||||||
pub top: f64,
|
pub top: f64,
|
||||||
pub right: f64,
|
pub right: f64,
|
||||||
pub bottom: f64,
|
pub bottom: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO : remove when leptos merges PR https://github.com/leptos-rs/leptos/pull/1105
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct scrollend;
|
||||||
|
|
||||||
|
impl EventDescriptor for scrollend {
|
||||||
|
type EventType = web_sys::Event;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn name(&self) -> Cow<'static, str> {
|
||||||
|
"scrollend".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||||
|
"$$$scrollend".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
const BUBBLES: bool = false;
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub use crate::utils::ThrottleOptions;
|
||||||
///
|
///
|
||||||
/// ## Demo
|
/// ## Demo
|
||||||
///
|
///
|
||||||
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/master/examples/use_throttle_fn)
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_throttle_fn)
|
||||||
///
|
///
|
||||||
/// ## Usage
|
/// ## Usage
|
||||||
///
|
///
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl Default for DebounceOptions<Option<f64>> {
|
||||||
pub fn debounce_filter<W>(
|
pub fn debounce_filter<W>(
|
||||||
ms: impl Into<MaybeSignal<f64>>,
|
ms: impl Into<MaybeSignal<f64>>,
|
||||||
options: DebounceOptions<W>,
|
options: DebounceOptions<W>,
|
||||||
) -> impl Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>>
|
) -> impl Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>> + Clone
|
||||||
where
|
where
|
||||||
W: Into<MaybeSignal<Option<f64>>>,
|
W: Into<MaybeSignal<Option<f64>>>,
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,21 +8,24 @@ use crate::utils::CloneableFnWithReturn;
|
||||||
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()
|
pub fn create_filter_wrapper<F, Filter>(filter: Filter, func: F) -> impl Fn() + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce() + Clone + 'static,
|
F: FnOnce() + Clone + 'static,
|
||||||
Filter: Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>>,
|
Filter: Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>> + Clone,
|
||||||
{
|
{
|
||||||
move || {
|
move || {
|
||||||
filter(Box::new(func.clone()));
|
filter(Box::new(func.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_filter_wrapper_with_arg<F, Arg, Filter>(filter: Filter, func: F) -> impl Fn(Arg)
|
pub fn create_filter_wrapper_with_arg<F, Arg, Filter>(
|
||||||
|
filter: Filter,
|
||||||
|
func: F,
|
||||||
|
) -> impl Fn(Arg) + Clone
|
||||||
where
|
where
|
||||||
F: FnOnce(Arg) + Clone + 'static,
|
F: FnOnce(Arg) + Clone + 'static,
|
||||||
Arg: Clone + 'static,
|
Arg: Clone + 'static,
|
||||||
Filter: Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>>,
|
Filter: Fn(Box<dyn CloneableFnWithReturn<()>>) -> Rc<RefCell<Option<()>>> + Clone,
|
||||||
{
|
{
|
||||||
move |arg: Arg| {
|
move |arg: Arg| {
|
||||||
let func = func.clone();
|
let func = func.clone();
|
||||||
|
@ -33,11 +36,11 @@ where
|
||||||
pub fn create_filter_wrapper_with_return<F, R, Filter>(
|
pub fn create_filter_wrapper_with_return<F, R, Filter>(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
func: F,
|
func: F,
|
||||||
) -> impl Fn() -> Rc<RefCell<Option<R>>>
|
) -> 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>>>,
|
Filter: Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone,
|
||||||
{
|
{
|
||||||
move || filter(Box::new(func.clone()))
|
move || filter(Box::new(func.clone()))
|
||||||
}
|
}
|
||||||
|
@ -45,12 +48,12 @@ where
|
||||||
pub fn create_filter_wrapper_with_return_and_arg<F, Arg, R, Filter>(
|
pub fn create_filter_wrapper_with_return_and_arg<F, Arg, R, Filter>(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
func: F,
|
func: F,
|
||||||
) -> impl Fn(Arg) -> Rc<RefCell<Option<R>>>
|
) -> 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>>>,
|
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();
|
||||||
|
|
|
@ -25,7 +25,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>>>
|
) -> impl Fn(Box<dyn CloneableFnWithReturn<R>>) -> Rc<RefCell<Option<R>>> + Clone
|
||||||
where
|
where
|
||||||
R: 'static,
|
R: 'static,
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue