From 25499083effdec3b71e0c50766c0fee24cd278f9 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Sat, 27 May 2023 03:00:08 +0100 Subject: [PATCH] finished use_scroll implementation --- .idea/leptos-use.iml | 1 + Cargo.toml | 2 +- docs/book/post_build.py | 2 +- docs/book/src/SUMMARY.md | 4 + docs/book/src/extract_doc_comment.py | 50 ++- docs/book/src/getting_started/get_started.md | 2 +- docs/book/src/sensors/use_scroll.md | 2 +- src/lib.rs | 12 +- src/use_debounce_fn.rs | 16 +- src/use_event_listener.rs | 42 +- src/use_scroll.rs | 386 +++++++++++++++++-- src/use_throttle_fn.rs | 2 +- src/utils/filters/debounce.rs | 2 +- src/utils/filters/mod.rs | 19 +- src/utils/filters/throttle.rs | 2 +- 15 files changed, 475 insertions(+), 69 deletions(-) diff --git a/.idea/leptos-use.iml b/.idea/leptos-use.iml index a753bb6..ba7f802 100644 --- a/.idea/leptos-use.iml +++ b/.idea/leptos-use.iml @@ -18,6 +18,7 @@ + diff --git a/Cargo.toml b/Cargo.toml index 6eef86a..6c58386 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,6 @@ repository = "https://github.com/Synphonyte/leptos-use" [dependencies] leptos = "0.3" -web-sys = { version = "0.3", features = ["ScrollToOptions", "ScrollBehavior"] } +web-sys = { version = "0.3", features = ["ScrollToOptions", "ScrollBehavior", "CssStyleDeclaration"] } wasm-bindgen = "0.2" js-sys = "0.3" \ No newline at end of file diff --git a/docs/book/post_build.py b/docs/book/post_build.py index 81bb266..ce8afab 100644 --- a/docs/book/post_build.py +++ b/docs/book/post_build.py @@ -24,7 +24,7 @@ def build_and_copy_demo(category, md_name): example_output_path = os.path.join(example_dir, "dist") 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, dirs_exist_ok=True) diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index 57c0c9b..2b5eb9f 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -12,6 +12,10 @@ - [use_event_listener](browser/use_event_listener.md) +# Sensors + +- [use_scroll](sensors/use_scroll.md) + # Utilities - [use_debounce_fn](utilities/use_debounce_fn.md) diff --git a/docs/book/src/extract_doc_comment.py b/docs/book/src/extract_doc_comment.py index f796bb7..436fb18 100644 --- a/docs/book/src/extract_doc_comment.py +++ b/docs/book/src/extract_doc_comment.py @@ -5,11 +5,19 @@ import re def main(): name = sys.argv[1] file_name = f"../../../../src/{name}.rs" + + types = []; + with open(file_name) as f: in_code_block = False doc_comment_started = False + initial_doc_finished = False + 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 line = line.strip().replace("/// ", "").replace("///", "") if "```" in line: @@ -20,11 +28,33 @@ def main(): line = process_line(line, name) print(line) + 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"SourceDemoDocs") 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): @@ -37,7 +67,6 @@ def process_line(line, name): source
''' - else: result = re.sub(interal_doc_link_pattern, r"[`\1`](https://docs.rs/leptos-use/latest/leptos_use/fn.\1.html)", @@ -46,5 +75,20 @@ def process_line(line, name): 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__': main() diff --git a/docs/book/src/getting_started/get_started.md b/docs/book/src/getting_started/get_started.md index 3ed6d76..85fcfdf 100644 --- a/docs/book/src/getting_started/get_started.md +++ b/docs/book/src/getting_started/get_started.md @@ -8,7 +8,7 @@ cargo add leptos-use ## 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 diff --git a/docs/book/src/sensors/use_scroll.md b/docs/book/src/sensors/use_scroll.md index 88d257b..1e27f2a 100644 --- a/docs/book/src/sensors/use_scroll.md +++ b/docs/book/src/sensors/use_scroll.md @@ -1,3 +1,3 @@ -# use_event_listener +# use_scroll diff --git a/src/lib.rs b/src/lib.rs index adc221c..9fcce26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,15 @@ +//! Collection of essential Leptos utilities inspired by SolidJS USE / VueUse + pub mod core; -pub mod use_debounce_fn; -pub mod use_event_listener; -pub mod use_scroll; -pub mod use_throttle_fn; +mod use_debounce_fn; +mod use_event_listener; +mod use_scroll; +mod use_throttle_fn; pub mod utils; pub use use_debounce_fn::*; pub use use_event_listener::use_event_listener; pub use use_scroll::*; pub use use_throttle_fn::*; + +extern crate self as leptos_use; diff --git a/src/use_debounce_fn.rs b/src/use_debounce_fn.rs index 7e92bf1..e71ffee 100644 --- a/src/use_debounce_fn.rs +++ b/src/use_debounce_fn.rs @@ -1,6 +1,5 @@ -use crate::utils::{ - create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter, DebounceOptions, -}; +pub use crate::utils::DebounceOptions; +use crate::utils::{create_filter_wrapper, create_filter_wrapper_with_arg, debounce_filter}; use leptos::MaybeSignal; /// Debounce execution of a function. @@ -9,7 +8,7 @@ use leptos::MaybeSignal; /// /// ## 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 /// @@ -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. -pub fn use_debounce_fn_with_arg(func: F, ms: impl Into>) -> impl Fn(Arg) +pub fn use_debounce_fn_with_arg( + func: F, + ms: impl Into>, +) -> impl Fn(Arg) + Clone where F: FnOnce(Arg) + Clone + 'static, Arg: Clone + 'static, @@ -95,12 +97,12 @@ where use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::>::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( func: F, ms: impl Into>, options: DebounceOptions, -) -> impl Fn(Arg) +) -> impl Fn(Arg) + Clone where F: FnOnce(Arg) + Clone + 'static, Arg: Clone + 'static, diff --git a/src/use_event_listener.rs b/src/use_event_listener.rs index 130e2ed..58ca7a2 100644 --- a/src/use_event_listener.rs +++ b/src/use_event_listener.rs @@ -80,13 +80,35 @@ use wasm_bindgen::JsCast; /// To avoid that you can put the logic inside a /// [`create_effect`](https://docs.rs/leptos/latest/leptos/fn.create_effect.html) hook /// which only runs client side. -#[allow(unused_must_use)] pub fn use_event_listener( cx: Scope, target: El, event: Ev, handler: F, ) -> Box +where + Ev: EventDescriptor + 'static, + (Scope, El): Into>, + T: Into + Clone + 'static, + F: FnMut(::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( + cx: Scope, + target: El, + event: Ev, + handler: F, + options: web_sys::AddEventListenerOptions, +) -> Box where Ev: EventDescriptor + 'static, (Scope, El): Into>, @@ -112,8 +134,11 @@ where let cleanup_prev_element = if let Some(element) = element { let element = element.into(); - _ = element - .add_event_listener_with_callback(&event_name, closure_js.as_ref().unchecked_ref()); + _ = element.add_event_listener_with_callback_and_add_event_listener_options( + &event_name, + closure_js.as_ref().unchecked_ref(), + &options, + ); let clean = cleanup.clone(); Rc::new(RefCell::new(Box::new(move || { @@ -133,15 +158,18 @@ where if let Some(element) = element { let element = element.into(); - _ = element - .add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref()); + _ = element.add_event_listener_with_callback_and_add_event_listener_options( + &event_name, + closure.as_ref().unchecked_ref(), + &options, + ); let clean = cleanup.clone(); - cleanup_prev_el.replace(Box::new(move || { + let _ = cleanup_prev_el.replace(Box::new(move || { clean(&element); }) as Box); } else { - cleanup_prev_el.replace(Box::new(move || {}) as Box); + let _ = cleanup_prev_el.replace(Box::new(move || {}) as Box); } }); diff --git a/src/use_scroll.rs b/src/use_scroll.rs index 927d911..28c994a 100644 --- a/src/use_scroll.rs +++ b/src/use_scroll.rs @@ -1,14 +1,162 @@ use crate::core::ElementMaybeSignal; -use crate::use_debounce_fn_with_arg; -use crate::utils::{CloneableFn, CloneableFnWithArg, CloneableFnWithReturn}; +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 leptos::ev::EventDescriptor; 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 -/// 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; +/// Reactive scroll position and state. +/// ## Demo +/// +/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_scroll) +/// +/// ## 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, +///
"..."
+/// } +/// # } +/// ``` +/// +/// ### 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, +/// #
"..."
+/// # } +/// # } +/// ``` +/// +/// ### 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, +///
"..."
+/// +/// +/// } +/// # } +/// ``` +/// +/// ### 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, +/// #
"..."
+/// # } +/// # } +/// ``` +/// +/// 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, +/// #
"..."
+/// # } +/// # } +/// ``` +pub fn use_scroll(cx: Scope, element: El) -> UseScrollReturn +where + El: Clone, + (Scope, El): Into>, + T: Into + 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)] pub fn use_scroll_with_options( cx: Scope, @@ -16,25 +164,25 @@ pub fn use_scroll_with_options( options: UseScrollOptions, ) -> UseScrollReturn where + El: Clone, (Scope, El): Into>, T: Into + Clone + 'static, { - // TODO : implement - let (internal_x, set_internal_x) = create_signal(cx, 0.0); let (internal_y, set_internal_y) = create_signal(cx, 0.0); let signal = (cx, element).into(); let behavior = options.behavior; + let sig = signal.clone(); let scroll_to = move |x: Option, y: Option| { - let element = signal.get_untracked(); + let element = sig.get_untracked(); if let Some(element) = element { let element = element.into(); 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 { scroll_options.left(x); @@ -54,7 +202,8 @@ where let set_y = Box::new(move |y| scroll(None, Some(y))); 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, Directions { left: true, @@ -63,7 +212,7 @@ where bottom: false, }, ); - let (directions, set_directions) = create_signal( + let directions = create_rw_signal( cx, Directions { 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| { if !is_scrolling.get() { return; } set_is_scrolling(false); - set_directions.update(|directions| { + directions.update(|directions| { directions.left = false; directions.right = false; directions.top = false; directions.bottom = false; - on_stop(e); + on_stop.clone()(e); }); }; + let throttle = options.throttle; + 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::() && 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::()) + }); + + 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>, + web_sys::EventTarget, + _, + >( + cx, + target, + ev::scroll, + handler, + options.event_listener_options.clone(), + ); + } else { + let _ = use_event_listener_with_options::< + _, + Signal>, + web_sys::EventTarget, + _, + >( + cx, + target, + ev::scroll, + on_scroll_handler, + options.event_listener_options.clone(), + ); + } + + let _ = use_event_listener_with_options::< + _, + Signal>, + 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 { x: internal_x.into(), @@ -99,10 +398,18 @@ where is_scrolling: is_scrolling.into(), arrived_state: arrived_state.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`]. +#[derive(Default)] pub struct UseScrollOptions { /// Throttle time in milliseconds for the scroll events. Defaults to 0 (disabled). pub throttle: f64, @@ -115,7 +422,7 @@ pub struct UseScrollOptions { pub offset: ScrollOffset, /// Callback when scrolling is happening. - pub on_scroll: Box, + pub on_scroll: Box>, /// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed). pub on_stop: Box>, @@ -125,23 +432,11 @@ pub struct UseScrollOptions { /// When changing the `x` or `y` signals this specifies the scroll behaviour. /// Can be `Auto` (= not smooth) or `Smooth`. Defaults to `Auto`. - pub behavior: 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(), - } - } + pub behavior: MaybeSignal, } +/// The scroll behavior. +/// Can be `Auto` (= not smooth) or `Smooth`. Defaults to `Auto`. #[derive(Default, Copy, Clone)] pub enum ScrollBehavior { #[default] @@ -158,6 +453,7 @@ impl Into for ScrollBehavior { } } +/// The return value of [`use_scroll`]. pub struct UseScrollReturn { pub x: Signal, pub set_x: Box, @@ -166,6 +462,7 @@ pub struct UseScrollReturn { pub is_scrolling: Signal, pub arrived_state: Signal, pub directions: Signal, + pub measure: Box, } #[derive(Copy, Clone)] @@ -177,9 +474,32 @@ pub struct Directions { } #[derive(Default, Copy, Clone)] +/// Threshold in pixels when we consider a side to have arrived (`UseScrollReturn::arrived_state`). pub struct ScrollOffset { pub left: f64, pub top: f64, pub right: 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; +} diff --git a/src/use_throttle_fn.rs b/src/use_throttle_fn.rs index 7fef959..bb289cf 100644 --- a/src/use_throttle_fn.rs +++ b/src/use_throttle_fn.rs @@ -14,7 +14,7 @@ pub use crate::utils::ThrottleOptions; /// /// ## 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 /// diff --git a/src/utils/filters/debounce.rs b/src/utils/filters/debounce.rs index 576ff32..2c20a6d 100644 --- a/src/utils/filters/debounce.rs +++ b/src/utils/filters/debounce.rs @@ -23,7 +23,7 @@ impl Default for DebounceOptions> { pub fn debounce_filter( ms: impl Into>, options: DebounceOptions, -) -> impl Fn(Box>) -> Rc>> +) -> impl Fn(Box>) -> Rc>> + Clone where W: Into>>, { diff --git a/src/utils/filters/mod.rs b/src/utils/filters/mod.rs index c30b1d9..0eee596 100644 --- a/src/utils/filters/mod.rs +++ b/src/utils/filters/mod.rs @@ -8,21 +8,24 @@ use crate::utils::CloneableFnWithReturn; use std::cell::RefCell; use std::rc::Rc; -pub fn create_filter_wrapper(filter: Filter, func: F) -> impl Fn() +pub fn create_filter_wrapper(filter: Filter, func: F) -> impl Fn() + Clone where F: FnOnce() + Clone + 'static, - Filter: Fn(Box>) -> Rc>>, + Filter: Fn(Box>) -> Rc>> + Clone, { move || { filter(Box::new(func.clone())); } } -pub fn create_filter_wrapper_with_arg(filter: Filter, func: F) -> impl Fn(Arg) +pub fn create_filter_wrapper_with_arg( + filter: Filter, + func: F, +) -> impl Fn(Arg) + Clone where F: FnOnce(Arg) + Clone + 'static, Arg: Clone + 'static, - Filter: Fn(Box>) -> Rc>>, + Filter: Fn(Box>) -> Rc>> + Clone, { move |arg: Arg| { let func = func.clone(); @@ -33,11 +36,11 @@ where pub fn create_filter_wrapper_with_return( filter: Filter, func: F, -) -> impl Fn() -> Rc>> +) -> impl Fn() -> Rc>> + Clone where F: FnOnce() -> R + Clone + 'static, R: 'static, - Filter: Fn(Box>) -> Rc>>, + Filter: Fn(Box>) -> Rc>> + Clone, { move || filter(Box::new(func.clone())) } @@ -45,12 +48,12 @@ where pub fn create_filter_wrapper_with_return_and_arg( filter: Filter, func: F, -) -> impl Fn(Arg) -> Rc>> +) -> impl Fn(Arg) -> Rc>> + Clone where F: FnOnce(Arg) -> R + Clone + 'static, R: 'static, Arg: Clone + 'static, - Filter: Fn(Box>) -> Rc>>, + Filter: Fn(Box>) -> Rc>> + Clone, { move |arg: Arg| { let func = func.clone(); diff --git a/src/utils/filters/throttle.rs b/src/utils/filters/throttle.rs index a03b8e5..37a9c86 100644 --- a/src/utils/filters/throttle.rs +++ b/src/utils/filters/throttle.rs @@ -25,7 +25,7 @@ impl Default for ThrottleOptions { pub fn throttle_filter( ms: impl Into>, options: ThrottleOptions, -) -> impl Fn(Box>) -> Rc>> +) -> impl Fn(Box>) -> Rc>> + Clone where R: 'static, {