diff --git a/.idea/leptos-use.iml b/.idea/leptos-use.iml index ba7f802..0935529 100644 --- a/.idea/leptos-use.iml +++ b/.idea/leptos-use.iml @@ -12,6 +12,7 @@ + @@ -20,6 +21,7 @@ + diff --git a/Cargo.toml b/Cargo.toml index 6c58386..b8d2d67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,5 @@ repository = "https://github.com/Synphonyte/leptos-use" leptos = "0.3" web-sys = { version = "0.3", features = ["ScrollToOptions", "ScrollBehavior", "CssStyleDeclaration"] } wasm-bindgen = "0.2" -js-sys = "0.3" \ No newline at end of file +js-sys = "0.3" +default-struct-builder = "0.1" \ No newline at end of file diff --git a/examples/use_scroll/Cargo.toml b/examples/use_scroll/Cargo.toml new file mode 100644 index 0000000..4defd6f --- /dev/null +++ b/examples/use_scroll/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "use_scroll" +version = "0.1.0" +edition = "2021" + +[dependencies] +leptos = "0.3" +console_error_panic_hook = "0.1" +console_log = "1" +log = "0.4" +leptos-use = { path = "../.." } +web-sys = "0.3" + +[dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.0" diff --git a/examples/use_scroll/README.md b/examples/use_scroll/README.md new file mode 100644 index 0000000..c2cc20d --- /dev/null +++ b/examples/use_scroll/README.md @@ -0,0 +1,23 @@ +A simple example for `use_scroll`. + +If you don't have it installed already, install [Trunk](https://trunkrs.dev/) and [Tailwind](https://tailwindcss.com/docs/installation) +as well as the nightly toolchain for Rust and the wasm32-unknown-unknown target: + +```bash +cargo install trunk +npm install -D tailwindcss +rustup toolchain install nightly +rustup target add wasm32-unknown-unknown +``` + +Then, open two terminals. In the first one, run: + +``` +npx tailwindcss -i ./input.css -o ./style/output.css --watch +``` + +In the second one, run: + +```bash +trunk serve --open +``` \ No newline at end of file diff --git a/examples/use_scroll/Trunk.toml b/examples/use_scroll/Trunk.toml new file mode 100644 index 0000000..f521021 --- /dev/null +++ b/examples/use_scroll/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "./demo/" \ No newline at end of file diff --git a/examples/use_scroll/index.html b/examples/use_scroll/index.html new file mode 100644 index 0000000..ae249a6 --- /dev/null +++ b/examples/use_scroll/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/use_scroll/input.css b/examples/use_scroll/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/use_scroll/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/use_scroll/rust-toolchain.toml b/examples/use_scroll/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/examples/use_scroll/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/examples/use_scroll/src/main.rs b/examples/use_scroll/src/main.rs new file mode 100644 index 0000000..ab63782 --- /dev/null +++ b/examples/use_scroll/src/main.rs @@ -0,0 +1,18 @@ +use leptos::*; +use leptos_use::utils::demo_or_body; + +#[component] +fn Demo(cx: Scope) -> impl IntoView { + view! { cx, + + } +} + +fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to(demo_or_body(), |cx| { + view! { cx, } + }) +} diff --git a/examples/use_scroll/style/output.css b/examples/use_scroll/style/output.css new file mode 100644 index 0000000..3093b1b --- /dev/null +++ b/examples/use_scroll/style/output.css @@ -0,0 +1,93 @@ +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: +} \ No newline at end of file diff --git a/examples/use_scroll/tailwind.config.js b/examples/use_scroll/tailwind.config.js new file mode 100644 index 0000000..dc5562c --- /dev/null +++ b/examples/use_scroll/tailwind.config.js @@ -0,0 +1,13 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs"], + }, + theme: { + extend: {}, + }, + corePlugins: { + preflight: false, + }, + plugins: [], +} \ No newline at end of file diff --git a/src/use_debounce_fn.rs b/src/use_debounce_fn.rs index e71ffee..dece312 100644 --- a/src/use_debounce_fn.rs +++ b/src/use_debounce_fn.rs @@ -47,9 +47,8 @@ use leptos::MaybeSignal; /// // do something /// }, /// 1000.0, -/// DebounceOptions { -/// max_wait: Some(5000.0), -/// } +/// DebounceOptions::default() +/// .max_wait(Some(5000.0)), /// ); /// /// window_event_listener(resize, move |_| debounced_fn()); @@ -69,18 +68,17 @@ pub fn use_debounce_fn(func: F, ms: impl Into>) -> impl Fn() where F: FnOnce() + Clone + 'static, { - use_debounce_fn_with_options(func, ms, DebounceOptions::>::default()) + use_debounce_fn_with_options(func, ms, DebounceOptions::default()) } /// Version of [`use_debounce_fn`] with debounce options. See the docs for [`use_debounce_fn`] for how to use. -pub fn use_debounce_fn_with_options( +pub fn use_debounce_fn_with_options( func: F, ms: impl Into>, - options: DebounceOptions, + options: DebounceOptions, ) -> impl Fn() where F: FnOnce() + Clone + 'static, - W: Into>>, { create_filter_wrapper(debounce_filter(ms, options), func) } @@ -94,19 +92,18 @@ where F: FnOnce(Arg) + Clone + 'static, Arg: Clone + 'static, { - use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::>::default()) + use_debounce_fn_with_arg_and_options(func, ms, DebounceOptions::default()) } /// Version of [`use_debounce_fn_with_arg`] with debounce options. -pub fn use_debounce_fn_with_arg_and_options( +pub fn use_debounce_fn_with_arg_and_options( func: F, ms: impl Into>, - options: DebounceOptions, + options: DebounceOptions, ) -> impl Fn(Arg) + Clone where F: FnOnce(Arg) + Clone + 'static, Arg: Clone + 'static, - W: Into>>, { create_filter_wrapper_with_arg(debounce_filter(ms, options), func) } diff --git a/src/use_scroll.rs b/src/use_scroll.rs index 28c994a..25bd98f 100644 --- a/src/use_scroll.rs +++ b/src/use_scroll.rs @@ -2,6 +2,7 @@ use crate::core::ElementMaybeSignal; 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 default_struct_builder::DefaultBuilder; use leptos::ev::EventDescriptor; use leptos::*; use std::borrow::Cow; @@ -48,16 +49,24 @@ use wasm_bindgen::JsCast; /// # 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 { +/// x, +/// y, +/// set_x, +/// set_y, +/// is_scrolling, +/// arrived_state, +/// directions, +/// .. +/// } = use_scroll_with_options( +/// cx, +/// element, +/// UseScrollOptions::default().offset(ScrollOffset { /// top: 30.0, /// bottom: 30.0, /// right: 30.0, /// left: 30.0, -/// }, -/// ..Default::default() -/// }); +/// }), +/// ); /// # /// # view! { cx, /// #
"..."
@@ -107,10 +116,11 @@ use wasm_bindgen::JsCast; /// # /// let UseScrollReturn { /// x, y, set_x, set_y, .. -/// } = use_scroll_with_options(cx, element, UseScrollOptions { -/// behavior: ScrollBehavior::Smooth.into(), -/// ..Default::default() -/// }); +/// } = use_scroll_with_options( +/// cx, +/// element, +/// UseScrollOptions::default().behavior(ScrollBehavior::Smooth), +/// ); /// # /// # view! { cx, /// #
"..."
@@ -137,10 +147,11 @@ use wasm_bindgen::JsCast; /// /// let UseScrollReturn { /// x, y, set_x, set_y, .. -/// } = use_scroll_with_options(cx, element, UseScrollOptions { -/// behavior: behavior.into(), -/// ..Default::default() -/// }); +/// } = use_scroll_with_options( +/// cx, +/// element, +/// UseScrollOptions::default().behavior(behavior), +/// ); /// # /// # view! { cx, /// #
"..."
@@ -409,30 +420,45 @@ where const ARRIVED_STATE_THRESHOLD_PIXELS: f64 = 1.0; /// Options for [`use_scroll`]. -#[derive(Default)] +#[derive(DefaultBuilder)] pub struct UseScrollOptions { /// Throttle time in milliseconds for the scroll events. Defaults to 0 (disabled). - pub throttle: f64, + throttle: f64, /// After scrolling ends we wait idle + throttle milliseconds before we consider scrolling to have stopped. /// Defaults to 200. - pub idle: f64, + idle: f64, /// Threshold in pixels when we consider a side to have arrived (`UseScrollReturn::arrived_state`). - pub offset: ScrollOffset, + offset: ScrollOffset, /// Callback when scrolling is happening. - pub on_scroll: Box>, + on_scroll: Box>, /// Callback when scrolling stops (after `idle` + `throttle` milliseconds have passed). - pub on_stop: Box>, + on_stop: Box>, /// Options passed to the `addEventListener("scroll", ...)` call - pub event_listener_options: web_sys::AddEventListenerOptions, + event_listener_options: web_sys::AddEventListenerOptions, /// When changing the `x` or `y` signals this specifies the scroll behaviour. /// Can be `Auto` (= not smooth) or `Smooth`. Defaults to `Auto`. - pub behavior: MaybeSignal, + #[builder(into)] + behavior: MaybeSignal, +} + +impl Default for UseScrollOptions { + fn default() -> Self { + Self { + throttle: 0.0, + idle: 200.0, + offset: ScrollOffset::default(), + on_scroll: Box::new(|_| {}), + on_stop: Box::new(|_| {}), + event_listener_options: Default::default(), + behavior: Default::default(), + } + } } /// The scroll behavior. diff --git a/src/use_throttle_fn.rs b/src/use_throttle_fn.rs index bb289cf..8f5ec20 100644 --- a/src/use_throttle_fn.rs +++ b/src/use_throttle_fn.rs @@ -50,10 +50,9 @@ pub use crate::utils::ThrottleOptions; /// // do something, it will be called at most 1 time per second /// }, /// 1000.0, -/// ThrottleOptions { -/// leading: true, -/// trailing: true, -/// } +/// ThrottleOptions::default() +/// .leading(true) +/// .trailing(true), /// ); /// # view! { cx, } /// # } diff --git a/src/utils/filters/debounce.rs b/src/utils/filters/debounce.rs index 2c20a6d..f252cbb 100644 --- a/src/utils/filters/debounce.rs +++ b/src/utils/filters/debounce.rs @@ -1,32 +1,31 @@ use crate::utils::CloneableFnWithReturn; +use default_struct_builder::DefaultBuilder; use leptos::leptos_dom::helpers::TimeoutHandle; use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked}; use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::time::Duration; -pub struct DebounceOptions -where - W: Into>>, -{ +#[derive(Clone, DefaultBuilder)] +pub struct DebounceOptions { /// The maximum time allowed to be delayed before it's invoked. /// In milliseconds. - pub max_wait: W, + #[builder(into)] + pub max_wait: MaybeSignal>, } -impl Default for DebounceOptions> { +impl Default for DebounceOptions { fn default() -> Self { - Self { max_wait: None } + Self { + max_wait: Default::default(), + } } } -pub fn debounce_filter( +pub fn debounce_filter( ms: impl Into>, - options: DebounceOptions, -) -> impl Fn(Box>) -> Rc>> + Clone -where - W: Into>>, -{ + options: DebounceOptions, +) -> impl Fn(Box>) -> Rc>> + Clone { let timer = Rc::new(Cell::new(None::)); let max_timer = Rc::new(Cell::new(None::)); @@ -38,7 +37,7 @@ where }; let ms = ms.into(); - let max_wait_signal = options.max_wait.into(); + let max_wait_signal = options.max_wait; move |invoke: Box>| { let duration = ms.get_untracked(); diff --git a/src/utils/filters/throttle.rs b/src/utils/filters/throttle.rs index 37a9c86..85f2997 100644 --- a/src/utils/filters/throttle.rs +++ b/src/utils/filters/throttle.rs @@ -1,4 +1,5 @@ use crate::utils::CloneableFnWithReturn; +use default_struct_builder::DefaultBuilder; use js_sys::Date; use leptos::leptos_dom::helpers::TimeoutHandle; use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked}; @@ -7,7 +8,7 @@ use std::cmp::max; use std::rc::Rc; use std::time::Duration; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, DefaultBuilder)] pub struct ThrottleOptions { pub trailing: bool, pub leading: bool,