diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ad089d..38f8cde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,14 @@ jobs: run: cargo fmt --check - name: Clippy run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings - - name: Run tests - run: cargo test --all-features + - name: Run tests (general) + run: cargo test --features math,docs,ssr,prost,serde + - name: Run tests (axum) + run: cargo test --features math,docs,ssr,prost,serde,axum --doc use_cookie::use_cookie + - name: Run tests (actix) + run: cargo test --features math,docs,ssr,prost,serde,actix --doc use_cookie::use_cookie -#### mdbook + #### mdbook - name: Install mdbook I uses: taiki-e/install-action@v2 with: @@ -59,7 +63,7 @@ jobs: - name: Deploy book to github pages id: deployment uses: actions/deploy-pages@v2 -##### mdbook end + ##### mdbook end - name: Publish crate leptos-use uses: katyo/publish-crates@v2 @@ -76,38 +80,3 @@ jobs: -F RELEASE.md -t "Version $RELEASE_VERSION" ${GITHUB_REF#refs/*/} - -# coverage: -# name: Coverage -# runs-on: ubuntu-latest -# -# steps: -# - name: Checkout sources -# uses: actions/checkout@v2 -# -# - name: Install rust -# uses: actions-rs/toolchain@v1 -# with: -# toolchain: stable -# profile: minimal -# override: true -# -# - name: Cache -# uses: Swatinem/rust-cache@v1 -# -# - name: Install cargo-tarpaulin -# uses: actions-rs/cargo@v1 -# with: -# command: install -# args: cargo-tarpaulin -# -# - name: Run cargo tarpaulin -# uses: actions-rs/cargo@v1 -# with: -# command: tarpaulin -# args: --output-dir coverage --out Lcov -# -# - name: Publish to Coveralls -# uses: coverallsapp/github-action@master -# with: -# github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f5500fb..d43d0b4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,5 +24,9 @@ jobs: run: cargo fmt --check - name: Clippy run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings - - name: Run tests - run: cargo test --all-features + - name: Run tests (general) + run: cargo test --features math,docs,ssr,prost,serde + - name: Run tests (axum) + run: cargo test --features math,docs,ssr,prost,serde,axum --doc use_cookie::use_cookie + - name: Run tests (actix) + run: cargo test --features math,docs,ssr,prost,serde,actix --doc use_cookie::use_cookie diff --git a/.gitignore b/.gitignore index f67d4f2..d7a25fc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ dist node_modules package*.json .vscode -.DS_Store \ No newline at end of file +.DS_Store +.ffizer \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d0d3d18..378b5b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,18 +3,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Unreleased] - ### New Functions 🚀 -- `use_webtransport` +- `use_broadcast_channel` +- `use_cookie` (thanks to @rakshith-ravi) +- `use_mouse_in_element` +- `use_device_pixel_ratio` (thanks to @mondeja) +- `use_element_bounding` ### Breaking Changes 🛠 +- The `leptos` version is now 0.6 - The trait `Codec` has been renamed to `StringCodec` and has been moved to `util::StringCodec`. - The struct `StringCodec` has been renamed to `FromToStringCodec` and has been moved to `util::FromToStringCodec`. - The structs `JsonCodec` and `ProstCodec` have been moved to `util` as well. +### Fixes 🍕 + +- Fixed `use_geolocation` SSR compile issue +- Fixed `use_intl_number_format` maximum fraction digits option + +### Changes 🔥 + +- The `UseMouseReturn` signals `x`, `y`, and `source_type` are now of type `Signal` instead of `ReadSignal`. +- You can now convert `leptos::html::HtmlElement` into `Element(s)MaybeSignal`. This should make functions a lot easier to use in directives. +- There's now a chapter in the book especially for `Element(s)MaybeSignal`. +- Throttled or debounced callbacks (in watch_* or *_fn) no longer are called after the containing scope was cleaned up. +- The document returned from `use_document` now supports the methods `query_selector` and `query_selector_all`. + ## [0.9.0] - 2023-12-06 ### New Functions 🚀 diff --git a/Cargo.toml b/Cargo.toml index f430716..2ca4ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,32 +13,38 @@ repository = "https://github.com/Synphonyte/leptos-use" homepage = "https://leptos-use.rs" [dependencies] +actix-web = { version = "4", optional = true, default-features = false } async-trait = "0.1" base64 = { version = "0.21", optional = true } cfg-if = "1" +cookie = { version = "0.18", features = ["percent-encode"] } default-struct-builder = "0.5" futures-util = "0.3" gloo-timers = { version = "0.3.0", features = ["futures"] } gloo-utils = { version = "0.2.0" } +http1 = { version = "1", optional = true, package = "http" } +http0_2 = { version = "0.2", optional = true, package = "http" } js-sys = "0.3" lazy_static = "1" -leptos = "0.5" +leptos = "0.6" +leptos_axum = { version = "0.6", optional = true } num = { version = "0.4", optional = true } paste = "1" prost = { version = "0.12", optional = true } rmp-serde = { version = "1.1", optional = true } serde = { version = "1", optional = true } serde_json = { version = "1", optional = true } -thiserror = "1.0" +thiserror = "1" wasm-bindgen = "0.2.88" wasm-bindgen-futures = "0.4" [dependencies.web-sys] -version = "0.3.65" +version = "0.3" features = [ "AddEventListenerOptions", "BinaryType", - "Coordinates", + "BroadcastChannel", + "Coordinates", "CloseEvent", "CssStyleDeclaration", "CustomEvent", @@ -55,7 +61,8 @@ features = [ "File", "FileList", "Geolocation", - "HtmlElement", + "HtmlDocument", + "HtmlElement", "HtmlLinkElement", "HtmlStyleElement", "IntersectionObserver", @@ -65,6 +72,7 @@ features = [ "MediaQueryList", "MediaStream", "MediaStreamTrack", + "MessageEvent", "MouseEvent", "MutationObserver", "MutationObserverInit", @@ -113,6 +121,8 @@ features = [ ] [features] +actix = ["dep:actix-web", "dep:http0_2"] +axum = ["dep:leptos_axum", "dep:http1"] docs = [] math = ["num"] prost = ["base64", "dep:prost"] diff --git a/README.md b/README.md index 7fa64c7..b28c69a 100644 --- a/README.md +++ b/README.md @@ -87,8 +87,10 @@ This will create the function file in the src directory, scaffold an example dir ## Leptos compatibility -| Crate version | Compatible Leptos version | -|----------------|---------------------------| -| <= 0.3 | 0.3 | -| 0.4, 0.5, 0.6 | 0.4 | -| 0.7, 0.8, 0.9 | 0.5 | +| Crate version | Compatible Leptos version | +|---------------|---------------------------| +| <= 0.3 | 0.3 | +| 0.4, 0.5, 0.6 | 0.4 | +| 0.7, 0.8, 0.9 | 0.5 | +| main | 0.6.0-beta | + diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index bd3899c..6ca246a 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -2,6 +2,7 @@ [Introduction](introduction.md) [Get Started](get_started.md) +[Element Parameters](element_parameters.md) [Server-Side Rendering](server_side_rendering.md) [Changelog](changelog.md) [Functions](functions.md) @@ -19,9 +20,11 @@ - [use_document_visibility](elements/use_document_visibility.md) - [use_draggable](elements/use_draggable.md) - [use_drop_zone](elements/use_drop_zone.md) +- [use_element_bounding](elements/use_element_bounding.md) - [use_element_size](elements/use_element_size.md) - [use_element_visibility](elements/use_element_visibility.md) - [use_intersection_observer](elements/use_intersection_observer.md) +- [use_mouse_in_element](elements/use_mouse_in_element.md) - [use_mutation_observer](elements/use_mutation_observer.md) - [use_resize_observer](elements/use_resize_observer.md) - [use_window](elements/use_window.md) @@ -31,7 +34,9 @@ # Browser - [use_breakpoints](browser/use_breakpoints.md) +- [use_broadcast_channel](browser/use_broadcast_channel.md) - [use_color_mode](browser/use_color_mode.md) +- [use_cookie](browser/use_cookie.md) - [use_css_var](browser/use_css_var.md) - [use_display_media](browser/use_display_media.md) - [use_event_listener](browser/use_event_listener.md) @@ -45,6 +50,7 @@ # Sensors - [on_click_outside](sensors/on_click_outside.md) +- [use_device_pixel_ratio](sensors/use_device_pixel_ratio.md) - [use_element_hover](sensors/use_element_hover.md) - [use_geolocation](sensors/use_geolocation.md) - [use_idle](sensors/use_idle.md) diff --git a/docs/book/src/browser/use_broadcast_channel.md b/docs/book/src/browser/use_broadcast_channel.md new file mode 100644 index 0000000..8a2393a --- /dev/null +++ b/docs/book/src/browser/use_broadcast_channel.md @@ -0,0 +1,3 @@ +# use_broadcast_channel + + diff --git a/docs/book/src/browser/use_cookie.md b/docs/book/src/browser/use_cookie.md new file mode 100644 index 0000000..b125e32 --- /dev/null +++ b/docs/book/src/browser/use_cookie.md @@ -0,0 +1,3 @@ +# use_cookie + + diff --git a/docs/book/src/element_parameters.md b/docs/book/src/element_parameters.md new file mode 100644 index 0000000..086e48c --- /dev/null +++ b/docs/book/src/element_parameters.md @@ -0,0 +1,95 @@ +# Element Parameters + +Many functions in this library operate on HTML/SVG elements. For example, the +function [`use_element_size`](elements/use_element_size.md) returns the width and height of an element: + +```rust +# use leptos::{*, html::Div}; +# use leptos_use::{use_element_size, UseElementSizeReturn}; +# +# #[component] +# pub fn Component() -> impl IntoView { +let el = create_node_ref::
(); + +let UseElementSizeReturn { width, height } = use_element_size(el); + +view! { +
+} +# } +``` + +In the example above we used a Leptos `NodeRef` to pass into the function. But that is not +the only way you can do that. All of these work as well: + +```rust +use_element_size(window().body()); // Option +use_element_size(window().body().unwrap()); // web_sys::Element +use_element_size("div > p.some-class"); // &str or String intepreted as CSS selector + +pub fn some_directive(el: HtmlElement) { + use_element_size(el); // leptos::html::HtmlElement +} +``` + +Signal of Strings: `Signal`, `ReadSignal`, `RwSignal`, `Memo`; also works with `&str`: + +```rust +let (str_signal, set_str_signal) = create_signal("div > p.some-class".to_string()); +use_element_size(str_signal); +``` + +Signals of Elements: `Signal`, `ReadSignal`, `RwSignal`, `Memo`; also works with `Option`: + +```rust +let (el_signal, set_el_signal) = create_signal(document().query_selector("div > p.some-class").unwrap()); +use_element_size(el_signal); +``` + +## How it works + +Looking at the source code of `use_element_size` you'll find sth like + +```rust +pub fn use_element_size(el: Into>) -> UseElementSizeReturn {} +``` + +All the above code works because there are `From` implementations for all of these +types for `ElementMaybeSignal`. + +## `ElementsMaybeSignal` + +Some functions work on one or more elements. Take [`use_resize_observer`](elements/use_resize_observer.md) for example. +This works very much the same way as described above but instead of `Into` +it takes an `Into` (note the plural). This means you can use it exactly in +the same ways as you saw with the singular `ElementMaybeSignal`. Only this time, when you use +`String` or `&str` it will be interpreted as CSS selector with `query_selector_all`. + +But you can also use it with containers. + +```rust +// Array of Option +use_resize_observer([window().body(), document().query_selector("div > p.some-class").unsrap()]); + +// Vec of &str. All of them will be interpreted as CSS selectors with query_selector_all() and the +// results will be merged into one Vec. +use_resize_observer(vec!["div > p.some-class", "p.some-class"]); + +// Slice of NodeRef +let node_ref1 = create_node_ref::
(); +let node_ref2 = create_node_ref::
(); +use_resize_observer(vec![node_ref1, node_ref2].as_slice()); +``` + +## Usage in Options + +Some functions have options that take `Element(s)MaybeSignal`. +They can be used in the same way. + +```rust +use_mouse_with_options( + UseMouseOptions::default().target("div > p.some-class") +); +``` + +See also ["Excluding Elements" in `on_click_outside`](elements/on_click_outside.md#excluding-elements). \ No newline at end of file diff --git a/docs/book/src/elements/use_element_bounding.md b/docs/book/src/elements/use_element_bounding.md new file mode 100644 index 0000000..ca3c015 --- /dev/null +++ b/docs/book/src/elements/use_element_bounding.md @@ -0,0 +1,3 @@ +# use_element_bounding + + diff --git a/docs/book/src/elements/use_mouse_in_element.md b/docs/book/src/elements/use_mouse_in_element.md new file mode 100644 index 0000000..0be1884 --- /dev/null +++ b/docs/book/src/elements/use_mouse_in_element.md @@ -0,0 +1,3 @@ +# use_mouse_in_element + + diff --git a/docs/book/src/sensors/use_device_pixel_ratio.md b/docs/book/src/sensors/use_device_pixel_ratio.md new file mode 100644 index 0000000..e032e92 --- /dev/null +++ b/docs/book/src/sensors/use_device_pixel_ratio.md @@ -0,0 +1,3 @@ +# use_device_pixel_ratio + + diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f1a3572..e4534a5 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -8,15 +8,19 @@ members = [ "use_abs", "use_active_element", "use_breakpoints", + "use_broadcast_channel", "use_ceil", "use_color_mode", + "use_cookie", "use_css_var", "use_cycle_list", "use_debounce_fn", + "use_device_pixel_ratio", "use_display_media", "use_document_visibility", "use_draggable", "use_drop_zone", + "use_element_bounding", "use_element_hover", "use_element_size", "use_element_visibility", @@ -32,6 +36,7 @@ members = [ "use_intl_number_format", "use_media_query", "use_mouse", + "use_mouse_in_element", "use_mutation_observer", "use_raf_fn", "use_resize_observer", diff --git a/examples/on_click_outside/Cargo.toml b/examples/on_click_outside/Cargo.toml index 76f570c..3313bf8 100644 --- a/examples/on_click_outside/Cargo.toml +++ b/examples/on_click_outside/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/rust-toolchain.toml b/examples/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/examples/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/examples/signal_debounced/Cargo.toml b/examples/signal_debounced/Cargo.toml index a14e69e..6f85a28 100644 --- a/examples/signal_debounced/Cargo.toml +++ b/examples/signal_debounced/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/signal_throttled/Cargo.toml b/examples/signal_throttled/Cargo.toml index 78525ed..73b2b91 100644 --- a/examples/signal_throttled/Cargo.toml +++ b/examples/signal_throttled/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/ssr/Cargo.toml b/examples/ssr/Cargo.toml index d37a754..28b1909 100644 --- a/examples/ssr/Cargo.toml +++ b/examples/ssr/Cargo.toml @@ -11,7 +11,7 @@ axum = { version = "0.6.4", optional = true } console_error_panic_hook = "0.1" console_log = "1" cfg-if = "1" -leptos = { version = "0.5", features = ["nightly"] } +leptos = { version = "0.6", features = ["nightly"] } leptos_axum = { version = "0.5", optional = true } leptos_meta = { version = "0.5", features = ["nightly"] } leptos_router = { version = "0.5", features = ["nightly"] } diff --git a/examples/ssr/src/app.rs b/examples/ssr/src/app.rs index cdfe5e3..491f896 100644 --- a/examples/ssr/src/app.rs +++ b/examples/ssr/src/app.rs @@ -5,8 +5,9 @@ use leptos_meta::*; use leptos_router::*; use leptos_use::storage::{use_local_storage, StringCodec}; use leptos_use::{ - use_color_mode, use_debounce_fn, use_event_listener, use_intl_number_format, use_timestamp, - use_window, ColorMode, UseColorModeReturn, UseIntlNumberFormatOptions, + use_color_mode, use_debounce_fn, use_event_listener, use_interval, use_intl_number_format, + use_preferred_dark, use_timestamp, use_window, ColorMode, UseColorModeReturn, + UseIntervalReturn, UseIntlNumberFormatOptions, }; #[component] @@ -67,6 +68,8 @@ fn HomePage() -> impl IntoView { let timestamp = use_timestamp(); + let is_dark_preferred = use_preferred_dark(); + view! {

Leptos-Use SSR Example

@@ -78,5 +81,24 @@ fn HomePage() -> impl IntoView {

{timestamp}

+

Dark preferred: {is_dark_preferred}

+ + } +} + +#[component] +pub fn LocalStorageTest() -> impl IntoView { + let UseIntervalReturn { counter, .. } = use_interval(1000); + logging::log!("test log"); + let (state, set_state, ..) = use_local_storage::("test-state"); + + view! { +

{counter}

+ } } diff --git a/examples/ssr/src/main.rs b/examples/ssr/src/main.rs index c08d00e..da34a9f 100644 --- a/examples/ssr/src/main.rs +++ b/examples/ssr/src/main.rs @@ -8,7 +8,7 @@ async fn main() { use leptos_use_ssr::app::*; use leptos_use_ssr::fileserv::file_and_error_handler; - simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging"); + simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging"); // Setting get_configuration(None) means we'll be using cargo-leptos's env values // For deployment these variables are: diff --git a/examples/use_abs/Cargo.toml b/examples/use_abs/Cargo.toml index 69755fa..8195e32 100644 --- a/examples/use_abs/Cargo.toml +++ b/examples/use_abs/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_active_element/Cargo.toml b/examples/use_active_element/Cargo.toml index 291f327..c19c400 100644 --- a/examples/use_active_element/Cargo.toml +++ b/examples/use_active_element/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_breakpoints/Cargo.toml b/examples/use_breakpoints/Cargo.toml index 99c6719..22b894b 100644 --- a/examples/use_breakpoints/Cargo.toml +++ b/examples/use_breakpoints/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_broadcast_channel/Cargo.toml b/examples/use_broadcast_channel/Cargo.toml new file mode 100644 index 0000000..d8414a1 --- /dev/null +++ b/examples/use_broadcast_channel/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "use_broadcast_channel" +version = "0.1.0" +edition = "2021" + +[dependencies] +leptos = { version = "0.5", features = ["nightly", "csr"] } +console_error_panic_hook = "0.1" +console_log = "1" +log = "0.4" +leptos-use = { path = "../..", features = ["docs"] } +web-sys = "0.3" + +[dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.0" diff --git a/examples/use_broadcast_channel/README.md b/examples/use_broadcast_channel/README.md new file mode 100644 index 0000000..7365df6 --- /dev/null +++ b/examples/use_broadcast_channel/README.md @@ -0,0 +1,23 @@ +A simple example for `use_broadcast_channel`. + +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 @tailwindcss/forms +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_broadcast_channel/Trunk.toml b/examples/use_broadcast_channel/Trunk.toml new file mode 100644 index 0000000..3e4be08 --- /dev/null +++ b/examples/use_broadcast_channel/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "/demo/" \ No newline at end of file diff --git a/examples/use_broadcast_channel/index.html b/examples/use_broadcast_channel/index.html new file mode 100644 index 0000000..ae249a6 --- /dev/null +++ b/examples/use_broadcast_channel/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/use_broadcast_channel/input.css b/examples/use_broadcast_channel/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/use_broadcast_channel/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/use_broadcast_channel/rust-toolchain.toml b/examples/use_broadcast_channel/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/examples/use_broadcast_channel/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/examples/use_broadcast_channel/src/main.rs b/examples/use_broadcast_channel/src/main.rs new file mode 100644 index 0000000..651183c --- /dev/null +++ b/examples/use_broadcast_channel/src/main.rs @@ -0,0 +1,20 @@ +use leptos::*; +use leptos_use::docs::demo_or_body; +use leptos_use::use_broadcast_channel; + +#[component] +fn Demo() -> impl IntoView { + + use_broadcast_channel(); + + view! { } +} + +fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to(demo_or_body(), || { + view! { } + }) +} diff --git a/examples/use_broadcast_channel/style/output.css b/examples/use_broadcast_channel/style/output.css new file mode 100644 index 0000000..ab5191f --- /dev/null +++ b/examples/use_broadcast_channel/style/output.css @@ -0,0 +1,289 @@ +[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple] { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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: ; +} + +.block { + display: block; +} + +.text-\[--brand-color\] { + color: var(--brand-color); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.opacity-75 { + opacity: 0.75; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + } +} \ No newline at end of file diff --git a/examples/use_broadcast_channel/tailwind.config.js b/examples/use_broadcast_channel/tailwind.config.js new file mode 100644 index 0000000..bc09f5e --- /dev/null +++ b/examples/use_broadcast_channel/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"], + }, + theme: { + extend: {}, + }, + corePlugins: { + preflight: false, + }, + plugins: [ + require('@tailwindcss/forms'), + ], +} \ No newline at end of file diff --git a/examples/use_ceil/Cargo.toml b/examples/use_ceil/Cargo.toml index 059b96b..d694257 100644 --- a/examples/use_ceil/Cargo.toml +++ b/examples/use_ceil/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_color_mode/Cargo.toml b/examples/use_color_mode/Cargo.toml index 0b58b63..ead2ef6 100644 --- a/examples/use_color_mode/Cargo.toml +++ b/examples/use_color_mode/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_cookie/Cargo.toml b/examples/use_cookie/Cargo.toml new file mode 100644 index 0000000..90f4bb2 --- /dev/null +++ b/examples/use_cookie/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "use_cookie" +version = "0.1.0" +edition = "2021" + +[dependencies] +leptos = { version = "0.6", features = ["nightly", "csr"] } +console_error_panic_hook = "0.1" +console_log = "1" +log = "0.4" +leptos-use = { path = "../..", features = ["docs"] } +web-sys = "0.3" + +[dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.0" diff --git a/examples/use_cookie/README.md b/examples/use_cookie/README.md new file mode 100644 index 0000000..aef10cd --- /dev/null +++ b/examples/use_cookie/README.md @@ -0,0 +1,23 @@ +A simple example for `use_cookie`. + +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 @tailwindcss/forms +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_cookie/Trunk.toml b/examples/use_cookie/Trunk.toml new file mode 100644 index 0000000..3e4be08 --- /dev/null +++ b/examples/use_cookie/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "/demo/" \ No newline at end of file diff --git a/examples/use_cookie/index.html b/examples/use_cookie/index.html new file mode 100644 index 0000000..ae249a6 --- /dev/null +++ b/examples/use_cookie/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/use_cookie/input.css b/examples/use_cookie/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/use_cookie/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/use_cookie/rust-toolchain.toml b/examples/use_cookie/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/examples/use_cookie/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/examples/use_cookie/src/main.rs b/examples/use_cookie/src/main.rs new file mode 100644 index 0000000..c6674f6 --- /dev/null +++ b/examples/use_cookie/src/main.rs @@ -0,0 +1,23 @@ +use leptos::*; +use leptos_use::docs::demo_or_body; +use leptos_use::use_cookie; + +#[component] +fn Demo() -> impl IntoView { + if let Some(cookie) = use_cookie("auth") { + view! {
"'auth' cookie set to " "`" {cookie.value().to_string()} "`"
} + .into_view() + } else { + view! {
"No 'auth' cookie set"
} + .into_view() + } +} + +fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to(demo_or_body(), || { + view! { } + }) +} diff --git a/examples/use_cookie/style/output.css b/examples/use_cookie/style/output.css new file mode 100644 index 0000000..ab5191f --- /dev/null +++ b/examples/use_cookie/style/output.css @@ -0,0 +1,289 @@ +[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple] { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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: ; +} + +.block { + display: block; +} + +.text-\[--brand-color\] { + color: var(--brand-color); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.opacity-75 { + opacity: 0.75; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + } +} \ No newline at end of file diff --git a/examples/use_cookie/tailwind.config.js b/examples/use_cookie/tailwind.config.js new file mode 100644 index 0000000..bc09f5e --- /dev/null +++ b/examples/use_cookie/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"], + }, + theme: { + extend: {}, + }, + corePlugins: { + preflight: false, + }, + plugins: [ + require('@tailwindcss/forms'), + ], +} \ No newline at end of file diff --git a/examples/use_css_var/Cargo.toml b/examples/use_css_var/Cargo.toml index 36dccaf..267c40e 100644 --- a/examples/use_css_var/Cargo.toml +++ b/examples/use_css_var/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_cycle_list/Cargo.toml b/examples/use_cycle_list/Cargo.toml index 2c954a7..c59b61c 100644 --- a/examples/use_cycle_list/Cargo.toml +++ b/examples/use_cycle_list/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_debounce_fn/Cargo.toml b/examples/use_debounce_fn/Cargo.toml index 8fd9049..d5f4621 100644 --- a/examples/use_debounce_fn/Cargo.toml +++ b/examples/use_debounce_fn/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_device_pixel_ratio/Cargo.toml b/examples/use_device_pixel_ratio/Cargo.toml new file mode 100644 index 0000000..5874c65 --- /dev/null +++ b/examples/use_device_pixel_ratio/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "use_device_pixel_ratio" +version = "0.1.0" +edition = "2021" + +[dependencies] +leptos = { version = "0.6", features = ["nightly", "csr"] } +console_error_panic_hook = "0.1" +console_log = "1" +log = "0.4" +leptos-use = { path = "../..", features = ["docs"] } +web-sys = "0.3" + +[dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.0" diff --git a/examples/use_device_pixel_ratio/README.md b/examples/use_device_pixel_ratio/README.md new file mode 100644 index 0000000..50693db --- /dev/null +++ b/examples/use_device_pixel_ratio/README.md @@ -0,0 +1,23 @@ +A simple example for `use_device_pixel_ratio`. + +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 @tailwindcss/forms +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_device_pixel_ratio/Trunk.toml b/examples/use_device_pixel_ratio/Trunk.toml new file mode 100644 index 0000000..3e4be08 --- /dev/null +++ b/examples/use_device_pixel_ratio/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "/demo/" \ No newline at end of file diff --git a/examples/use_device_pixel_ratio/index.html b/examples/use_device_pixel_ratio/index.html new file mode 100644 index 0000000..ae249a6 --- /dev/null +++ b/examples/use_device_pixel_ratio/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/use_device_pixel_ratio/input.css b/examples/use_device_pixel_ratio/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/use_device_pixel_ratio/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/use_device_pixel_ratio/rust-toolchain.toml b/examples/use_device_pixel_ratio/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/examples/use_device_pixel_ratio/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/examples/use_device_pixel_ratio/src/main.rs b/examples/use_device_pixel_ratio/src/main.rs new file mode 100644 index 0000000..fb3e6f7 --- /dev/null +++ b/examples/use_device_pixel_ratio/src/main.rs @@ -0,0 +1,24 @@ +use leptos::*; +use leptos_use::docs::demo_or_body; +use leptos_use::use_device_pixel_ratio; + +#[component] +fn Demo() -> impl IntoView { + let pixel_ratio = use_device_pixel_ratio(); + + view! { +
{move || format!("pixelRatio: {}", pixel_ratio())}
+

+ "Zoom in and out (or move the window to a screen with a different scaling factor) to see the value changes." +

+ } +} + +fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to(demo_or_body(), || { + view! { } + }) +} diff --git a/examples/use_device_pixel_ratio/style/output.css b/examples/use_device_pixel_ratio/style/output.css new file mode 100644 index 0000000..ab5191f --- /dev/null +++ b/examples/use_device_pixel_ratio/style/output.css @@ -0,0 +1,289 @@ +[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple] { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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: ; +} + +.block { + display: block; +} + +.text-\[--brand-color\] { + color: var(--brand-color); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.opacity-75 { + opacity: 0.75; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + } +} \ No newline at end of file diff --git a/examples/use_device_pixel_ratio/tailwind.config.js b/examples/use_device_pixel_ratio/tailwind.config.js new file mode 100644 index 0000000..bc09f5e --- /dev/null +++ b/examples/use_device_pixel_ratio/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"], + }, + theme: { + extend: {}, + }, + corePlugins: { + preflight: false, + }, + plugins: [ + require('@tailwindcss/forms'), + ], +} \ No newline at end of file diff --git a/examples/use_display_media/Cargo.toml b/examples/use_display_media/Cargo.toml index 32014f2..c7ffe6a 100644 --- a/examples/use_display_media/Cargo.toml +++ b/examples/use_display_media/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_document_visibility/Cargo.toml b/examples/use_document_visibility/Cargo.toml index 61607d2..cb9b8f1 100644 --- a/examples/use_document_visibility/Cargo.toml +++ b/examples/use_document_visibility/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_draggable/Cargo.toml b/examples/use_draggable/Cargo.toml index c56104c..e2e9a61 100644 --- a/examples/use_draggable/Cargo.toml +++ b/examples/use_draggable/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_drop_zone/Cargo.toml b/examples/use_drop_zone/Cargo.toml index 461f924..115dc9d 100644 --- a/examples/use_drop_zone/Cargo.toml +++ b/examples/use_drop_zone/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_element_bounding/Cargo.toml b/examples/use_element_bounding/Cargo.toml new file mode 100644 index 0000000..7620bcd --- /dev/null +++ b/examples/use_element_bounding/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "use_element_bounding" +version = "0.1.0" +edition = "2021" + +[dependencies] +leptos = { version = "0.6", features = ["nightly", "csr"] } +console_error_panic_hook = "0.1" +console_log = "1" +log = "0.4" +leptos-use = { path = "../..", features = ["docs"] } +web-sys = "0.3" + +[dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.0" diff --git a/examples/use_element_bounding/README.md b/examples/use_element_bounding/README.md new file mode 100644 index 0000000..fa29264 --- /dev/null +++ b/examples/use_element_bounding/README.md @@ -0,0 +1,23 @@ +A simple example for `use_element_bounding`. + +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 @tailwindcss/forms +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_element_bounding/Trunk.toml b/examples/use_element_bounding/Trunk.toml new file mode 100644 index 0000000..3e4be08 --- /dev/null +++ b/examples/use_element_bounding/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "/demo/" \ No newline at end of file diff --git a/examples/use_element_bounding/index.html b/examples/use_element_bounding/index.html new file mode 100644 index 0000000..ae249a6 --- /dev/null +++ b/examples/use_element_bounding/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/use_element_bounding/input.css b/examples/use_element_bounding/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/use_element_bounding/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/use_element_bounding/rust-toolchain.toml b/examples/use_element_bounding/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/examples/use_element_bounding/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/examples/use_element_bounding/src/main.rs b/examples/use_element_bounding/src/main.rs new file mode 100644 index 0000000..48ab2e4 --- /dev/null +++ b/examples/use_element_bounding/src/main.rs @@ -0,0 +1,54 @@ +use leptos::html::Textarea; +use leptos::*; +use leptos_use::docs::{demo_or_body, Note}; +use leptos_use::{use_element_bounding, UseElementBoundingReturn}; + +#[component] +fn Demo() -> impl IntoView { + let el = create_node_ref:: + } +} + +fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to(demo_or_body(), || { + view! { } + }) +} diff --git a/examples/use_element_bounding/style/output.css b/examples/use_element_bounding/style/output.css new file mode 100644 index 0000000..263b210 --- /dev/null +++ b/examples/use_element_bounding/style/output.css @@ -0,0 +1,351 @@ +[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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: ; +} + +.static { + position: static; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.h-\[175px\] { + height: 175px; +} + +.w-\[335px\] { + width: 335px; +} + +.resize { + resize: both; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.p-4 { + padding: 1rem; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.leading-10 { + line-height: 2.5rem; +} + +.text-\[--brand-color\] { + color: var(--brand-color); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.opacity-75 { + opacity: 0.75; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + } +} \ No newline at end of file diff --git a/examples/use_element_bounding/tailwind.config.js b/examples/use_element_bounding/tailwind.config.js new file mode 100644 index 0000000..bc09f5e --- /dev/null +++ b/examples/use_element_bounding/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"], + }, + theme: { + extend: {}, + }, + corePlugins: { + preflight: false, + }, + plugins: [ + require('@tailwindcss/forms'), + ], +} \ No newline at end of file diff --git a/examples/use_element_hover/Cargo.toml b/examples/use_element_hover/Cargo.toml index 8f11c26..9d90e62 100644 --- a/examples/use_element_hover/Cargo.toml +++ b/examples/use_element_hover/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_element_size/Cargo.toml b/examples/use_element_size/Cargo.toml index 3e070b8..daf02f0 100644 --- a/examples/use_element_size/Cargo.toml +++ b/examples/use_element_size/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_element_visibility/Cargo.toml b/examples/use_element_visibility/Cargo.toml index 7c6bf4c..50ba543 100644 --- a/examples/use_element_visibility/Cargo.toml +++ b/examples/use_element_visibility/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_event_listener/Cargo.toml b/examples/use_event_listener/Cargo.toml index 3b07dcd..653bb76 100644 --- a/examples/use_event_listener/Cargo.toml +++ b/examples/use_event_listener/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_favicon/Cargo.toml b/examples/use_favicon/Cargo.toml index 3d9be8b..260413a 100644 --- a/examples/use_favicon/Cargo.toml +++ b/examples/use_favicon/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_floor/Cargo.toml b/examples/use_floor/Cargo.toml index 41b04b7..c221e68 100644 --- a/examples/use_floor/Cargo.toml +++ b/examples/use_floor/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_geolocation/Cargo.toml b/examples/use_geolocation/Cargo.toml index c09d504..bb7c93e 100644 --- a/examples/use_geolocation/Cargo.toml +++ b/examples/use_geolocation/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_idle/Cargo.toml b/examples/use_idle/Cargo.toml index 8fbe4fc..1b412b4 100644 --- a/examples/use_idle/Cargo.toml +++ b/examples/use_idle/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_infinite_scroll/Cargo.toml b/examples/use_infinite_scroll/Cargo.toml index 93b5074..e55811a 100644 --- a/examples/use_infinite_scroll/Cargo.toml +++ b/examples/use_infinite_scroll/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_intersection_observer/Cargo.toml b/examples/use_intersection_observer/Cargo.toml index 0ab8584..b401615 100644 --- a/examples/use_intersection_observer/Cargo.toml +++ b/examples/use_intersection_observer/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_interval/Cargo.toml b/examples/use_interval/Cargo.toml index 1955d1a..a49f0cd 100644 --- a/examples/use_interval/Cargo.toml +++ b/examples/use_interval/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_interval_fn/Cargo.toml b/examples/use_interval_fn/Cargo.toml index 942ab19..86bc8b2 100644 --- a/examples/use_interval_fn/Cargo.toml +++ b/examples/use_interval_fn/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_intl_number_format/Cargo.toml b/examples/use_intl_number_format/Cargo.toml index dfafe71..7eadd70 100644 --- a/examples/use_intl_number_format/Cargo.toml +++ b/examples/use_intl_number_format/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_media_query/Cargo.toml b/examples/use_media_query/Cargo.toml index 4b47f0b..345aa87 100644 --- a/examples/use_media_query/Cargo.toml +++ b/examples/use_media_query/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_mouse/Cargo.toml b/examples/use_mouse/Cargo.toml index 7882fe0..1c91a04 100644 --- a/examples/use_mouse/Cargo.toml +++ b/examples/use_mouse/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_mouse_in_element/Cargo.toml b/examples/use_mouse_in_element/Cargo.toml new file mode 100644 index 0000000..ac23964 --- /dev/null +++ b/examples/use_mouse_in_element/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "use_mouse_in_element" +version = "0.1.0" +edition = "2021" + +[dependencies] +leptos = { version = "0.6", features = ["nightly", "csr"] } +console_error_panic_hook = "0.1" +console_log = "1" +log = "0.4" +leptos-use = { path = "../..", features = ["docs"] } +web-sys = "0.3" + +[dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3.0" diff --git a/examples/use_mouse_in_element/README.md b/examples/use_mouse_in_element/README.md new file mode 100644 index 0000000..a22cdeb --- /dev/null +++ b/examples/use_mouse_in_element/README.md @@ -0,0 +1,23 @@ +A simple example for `use_mouse_in_element`. + +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 @tailwindcss/forms +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_mouse_in_element/Trunk.toml b/examples/use_mouse_in_element/Trunk.toml new file mode 100644 index 0000000..3e4be08 --- /dev/null +++ b/examples/use_mouse_in_element/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "/demo/" \ No newline at end of file diff --git a/examples/use_mouse_in_element/index.html b/examples/use_mouse_in_element/index.html new file mode 100644 index 0000000..ae249a6 --- /dev/null +++ b/examples/use_mouse_in_element/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/use_mouse_in_element/input.css b/examples/use_mouse_in_element/input.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/examples/use_mouse_in_element/input.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/examples/use_mouse_in_element/rust-toolchain.toml b/examples/use_mouse_in_element/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/examples/use_mouse_in_element/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/examples/use_mouse_in_element/src/main.rs b/examples/use_mouse_in_element/src/main.rs new file mode 100644 index 0000000..a0ba237 --- /dev/null +++ b/examples/use_mouse_in_element/src/main.rs @@ -0,0 +1,53 @@ +use leptos::html::Div; +use leptos::*; +use leptos_use::docs::demo_or_body; +use leptos_use::{use_mouse_in_element, UseMouseInElementReturn}; + +#[component] +fn Demo() -> impl IntoView { + let el = create_node_ref::
(); + + let UseMouseInElementReturn { + x, + y, + source_type, + element_x, + element_y, + element_position_x, + element_position_y, + element_width, + element_height, + is_outside, + .. + } = use_mouse_in_element(el); + + view! { +
+
+
Hover me
+
+
    x: {x}
+    y: {y}
+    source_type: {move || format!("{:?}", source_type())}
+    element_x: {element_x}
+    element_y: {element_y}
+    element_position_x: {element_position_x}
+    element_position_y: {element_position_y}
+    element_width: {element_width}
+    element_height: {element_height}
+    is_outside: {is_outside}
+
+ } +} + +fn main() { + _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to(demo_or_body(), || { + view! { } + }) +} diff --git a/examples/use_mouse_in_element/style/output.css b/examples/use_mouse_in_element/style/output.css new file mode 100644 index 0000000..da9fc94 --- /dev/null +++ b/examples/use_mouse_in_element/style/output.css @@ -0,0 +1,352 @@ +[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --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: ; +} + +.static { + position: static; +} + +.m-auto { + margin: auto; +} + +.flex { + display: flex; +} + +.h-40 { + height: 10rem; +} + +.w-40 { + width: 10rem; +} + +.select-none { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.place-content-center { + place-content: center; +} + +.gap-4 { + gap: 1rem; +} + +.bg-gray-400\/20 { + background-color: rgb(156 163 175 / 0.2); +} + +.text-\[--brand-color\] { + color: var(--brand-color); +} + +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.opacity-75 { + opacity: 0.75; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-green-500 { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); + } +} \ No newline at end of file diff --git a/examples/use_mouse_in_element/tailwind.config.js b/examples/use_mouse_in_element/tailwind.config.js new file mode 100644 index 0000000..bc09f5e --- /dev/null +++ b/examples/use_mouse_in_element/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"], + }, + theme: { + extend: {}, + }, + corePlugins: { + preflight: false, + }, + plugins: [ + require('@tailwindcss/forms'), + ], +} \ No newline at end of file diff --git a/examples/use_mutation_observer/Cargo.toml b/examples/use_mutation_observer/Cargo.toml index 2f3dbe1..fcf12fd 100644 --- a/examples/use_mutation_observer/Cargo.toml +++ b/examples/use_mutation_observer/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_raf_fn/Cargo.toml b/examples/use_raf_fn/Cargo.toml index 305b65e..48b8cd9 100644 --- a/examples/use_raf_fn/Cargo.toml +++ b/examples/use_raf_fn/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_resize_observer/Cargo.toml b/examples/use_resize_observer/Cargo.toml index dc8b881..0ce7f4c 100644 --- a/examples/use_resize_observer/Cargo.toml +++ b/examples/use_resize_observer/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_round/Cargo.toml b/examples/use_round/Cargo.toml index 3956d90..6786fc8 100644 --- a/examples/use_round/Cargo.toml +++ b/examples/use_round/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_scroll/Cargo.toml b/examples/use_scroll/Cargo.toml index 1e73a51..ccb133a 100644 --- a/examples/use_scroll/Cargo.toml +++ b/examples/use_scroll/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_service_worker/Cargo.toml b/examples/use_service_worker/Cargo.toml index 878fa9a..8e3b8c7 100644 --- a/examples/use_service_worker/Cargo.toml +++ b/examples/use_service_worker/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_sorted/Cargo.toml b/examples/use_sorted/Cargo.toml index 6b5a699..05462ac 100644 --- a/examples/use_sorted/Cargo.toml +++ b/examples/use_sorted/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_storage/Cargo.toml b/examples/use_storage/Cargo.toml index 6686fe8..a4ba692 100644 --- a/examples/use_storage/Cargo.toml +++ b/examples/use_storage/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_throttle_fn/Cargo.toml b/examples/use_throttle_fn/Cargo.toml index d6e01f9..04d6006 100644 --- a/examples/use_throttle_fn/Cargo.toml +++ b/examples/use_throttle_fn/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_timestamp/Cargo.toml b/examples/use_timestamp/Cargo.toml index 76d8d40..a065eb4 100644 --- a/examples/use_timestamp/Cargo.toml +++ b/examples/use_timestamp/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_web_notification/Cargo.toml b/examples/use_web_notification/Cargo.toml index 26ee6c9..216f31e 100644 --- a/examples/use_web_notification/Cargo.toml +++ b/examples/use_web_notification/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_websocket/Cargo.toml b/examples/use_websocket/Cargo.toml index 64de6b0..2f1f0a5 100644 --- a/examples/use_websocket/Cargo.toml +++ b/examples/use_websocket/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_window_focus/Cargo.toml b/examples/use_window_focus/Cargo.toml index 611a50e..d157e75 100644 --- a/examples/use_window_focus/Cargo.toml +++ b/examples/use_window_focus/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/use_window_scroll/Cargo.toml b/examples/use_window_scroll/Cargo.toml index bdaf522..d8a1c48 100644 --- a/examples/use_window_scroll/Cargo.toml +++ b/examples/use_window_scroll/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/watch_debounced/Cargo.toml b/examples/watch_debounced/Cargo.toml index c088b01..8af3c77 100644 --- a/examples/watch_debounced/Cargo.toml +++ b/examples/watch_debounced/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/watch_pausable/Cargo.toml b/examples/watch_pausable/Cargo.toml index 50586e8..a14897d 100644 --- a/examples/watch_pausable/Cargo.toml +++ b/examples/watch_pausable/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/examples/watch_throttled/Cargo.toml b/examples/watch_throttled/Cargo.toml index 7d03eb4..e711c4f 100644 --- a/examples/watch_throttled/Cargo.toml +++ b/examples/watch_throttled/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -leptos = { version = "0.5", features = ["nightly", "csr"] } +leptos = { version = "0.6", features = ["nightly", "csr"] } console_error_panic_hook = "0.1" console_log = "1" log = "0.4" diff --git a/src/core/element_maybe_signal.rs b/src/core/element_maybe_signal.rs index a100b7e..7a047e5 100644 --- a/src/core/element_maybe_signal.rs +++ b/src/core/element_maybe_signal.rs @@ -1,6 +1,6 @@ use crate::{UseDocument, UseWindow}; use cfg_if::cfg_if; -use leptos::html::ElementDescriptor; +use leptos::html::{ElementDescriptor, HtmlElement}; use leptos::*; use std::marker::PhantomData; use std::ops::Deref; @@ -196,23 +196,37 @@ where } } -impl From> for ElementMaybeSignal -where - E: From + 'static, -{ - fn from(signal: Signal) -> Self { - cfg_if! { if #[cfg(feature = "ssr")] { - let _ = signal; - Self::Dynamic(Signal::derive(|| None)) - } else { - Self::Dynamic( - create_memo(move |_| document().query_selector(&signal.get()).unwrap_or_default()) - .into(), - ) - }} - } +macro_rules! impl_from_signal_string { + ($ty:ty) => { + impl From<$ty> for ElementMaybeSignal + where + E: From + 'static, + { + fn from(signal: $ty) -> Self { + cfg_if! { if #[cfg(feature = "ssr")] { + let _ = signal; + Self::Dynamic(Signal::derive(|| None)) + } else { + Self::Dynamic( + create_memo(move |_| document().query_selector(&signal.get()).unwrap_or_default()) + .into(), + ) + }} + } + } + }; } +impl_from_signal_string!(Signal); +impl_from_signal_string!(ReadSignal); +impl_from_signal_string!(RwSignal); +impl_from_signal_string!(Memo); + +impl_from_signal_string!(Signal<&str>); +impl_from_signal_string!(ReadSignal<&str>); +impl_from_signal_string!(RwSignal<&str>); +impl_from_signal_string!(Memo<&str>); + // From signal /////////////////////////////////////////////////////////////// macro_rules! impl_from_signal_option { @@ -274,3 +288,82 @@ macro_rules! impl_from_node_ref { impl_from_node_ref!(web_sys::EventTarget); impl_from_node_ref!(web_sys::Element); + +// From leptos::html::HTMLElement /////////////////////////////////////////////// + +macro_rules! impl_from_html_element { + ($ty:ty) => { + impl From> for ElementMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref, + { + fn from(value: HtmlElement) -> Self { + let el: &$ty = value.deref(); + Self::Static(Some(el.clone())) + } + } + }; +} + +impl_from_html_element!(web_sys::EventTarget); +impl_from_html_element!(web_sys::Element); + +// From Signal ///////////////////////////////////////// + +macro_rules! impl_from_signal_html_element { + ($signal:ty, $ty:ty) => { + impl From<$signal> for ElementMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref + Clone, + { + fn from(value: $signal) -> Self { + Self::Dynamic(Signal::derive(move || { + let value = value.get(); + let el: &$ty = value.deref(); + Some(el.clone()) + })) + } + } + }; +} + +impl_from_signal_html_element!(Signal>, web_sys::EventTarget); +impl_from_signal_html_element!(ReadSignal>, web_sys::EventTarget); +impl_from_signal_html_element!(RwSignal>, web_sys::EventTarget); +impl_from_signal_html_element!(Memo>, web_sys::EventTarget); + +impl_from_signal_html_element!(Signal>, web_sys::Element); +impl_from_signal_html_element!(ReadSignal>, web_sys::Element); +impl_from_signal_html_element!(RwSignal>, web_sys::Element); +impl_from_signal_html_element!(Memo>, web_sys::Element); + +// From Signal> ///////////////////////////////////////// + +macro_rules! impl_from_signal_html_element { + ($signal:ty, $ty:ty) => { + impl From<$signal> for ElementMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref + Clone, + { + fn from(value: $signal) -> Self { + Self::Dynamic(Signal::derive(move || { + let el: Option<$ty> = value.get().map(|el| el.deref().clone()); + el + })) + } + } + }; +} + +impl_from_signal_html_element!(Signal>>, web_sys::EventTarget); +impl_from_signal_html_element!( + ReadSignal>>, + web_sys::EventTarget +); +impl_from_signal_html_element!(RwSignal>>, web_sys::EventTarget); +impl_from_signal_html_element!(Memo>>, web_sys::EventTarget); + +impl_from_signal_html_element!(Signal>>, web_sys::Element); +impl_from_signal_html_element!(ReadSignal>>, web_sys::Element); +impl_from_signal_html_element!(RwSignal>>, web_sys::Element); +impl_from_signal_html_element!(Memo>>, web_sys::Element); diff --git a/src/core/elements_maybe_signal.rs b/src/core/elements_maybe_signal.rs index 2770d48..6a086af 100644 --- a/src/core/elements_maybe_signal.rs +++ b/src/core/elements_maybe_signal.rs @@ -180,6 +180,9 @@ where { fn from(target: &'a str) -> Self { cfg_if! { if #[cfg(feature = "ssr")] { + let _ = target; + Self::Static(vec![]) + } else { if let Ok(node_list) = document().query_selector_all(target) { let mut list = Vec::with_capacity(node_list.length() as usize); for i in 0..node_list.length() { @@ -191,9 +194,6 @@ where } else { Self::Static(vec![]) } - } else { - let _ = target; - Self::Static(vec![]) }} } } @@ -207,34 +207,48 @@ where } } -impl From> for ElementsMaybeSignal -where - E: From + 'static, -{ - fn from(signal: Signal) -> Self { - cfg_if! { if #[cfg(feature = "ssr")] { - Self::Dynamic( - create_memo(move |_| { - if let Ok(node_list) = document().query_selector_all(&signal.get()) { - let mut list = Vec::with_capacity(node_list.length() as usize); - for i in 0..node_list.length() { - let node = node_list.get(i).expect("checked the range"); - list.push(Some(node)); - } - list - } else { - vec![] - } - }) - .into(), - ) - } else { - let _ = signal; - Self::Dynamic(Signal::derive(Vec::new)) - }} - } +macro_rules! impl_from_signal_string { + ($ty:ty) => { + impl From<$ty> for ElementsMaybeSignal + where + E: From + 'static, + { + fn from(signal: $ty) -> Self { + cfg_if! { if #[cfg(feature = "ssr")] { + Self::Dynamic( + create_memo(move |_| { + if let Ok(node_list) = document().query_selector_all(&signal.get()) { + let mut list = Vec::with_capacity(node_list.length() as usize); + for i in 0..node_list.length() { + let node = node_list.get(i).expect("checked the range"); + list.push(Some(node)); + } + list + } else { + vec![] + } + }) + .into(), + ) + } else { + let _ = signal; + Self::Dynamic(Signal::derive(Vec::new)) + }} + } + } + }; } +impl_from_signal_string!(Signal); +impl_from_signal_string!(ReadSignal); +impl_from_signal_string!(RwSignal); +impl_from_signal_string!(Memo); + +impl_from_signal_string!(Signal<&str>); +impl_from_signal_string!(ReadSignal<&str>); +impl_from_signal_string!(RwSignal<&str>); +impl_from_signal_string!(Memo<&str>); + // From single signal /////////////////////////////////////////////////////////////// macro_rules! impl_from_signal_option { @@ -297,7 +311,26 @@ macro_rules! impl_from_node_ref { impl_from_node_ref!(web_sys::EventTarget); impl_from_node_ref!(web_sys::Element); -// From multiple static elements ////////////////////////////////////////////////////////////// +// From single leptos::html::HTMLElement /////////////////////////////////////////// + +macro_rules! impl_from_html_element { + ($ty:ty) => { + impl From> for ElementsMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref, + { + fn from(value: HtmlElement) -> Self { + let el: &$ty = value.deref(); + Self::Static(vec![Some(el.clone())]) + } + } + }; +} + +impl_from_html_element!(web_sys::EventTarget); +impl_from_html_element!(web_sys::Element); + +// From multiple static elements ////////////////////////////////////////////////////// impl From<&[T]> for ElementsMaybeSignal where @@ -317,6 +350,107 @@ where } } +impl From> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(target: Vec) -> Self { + Self::Static(target.iter().map(|t| Some(t.clone())).collect()) + } +} + +impl From>> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(target: Vec>) -> Self { + Self::Static(target.to_vec()) + } +} + +impl From<[T; C]> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(target: [T; C]) -> Self { + Self::Static(target.iter().map(|t| Some(t.clone())).collect()) + } +} + +impl From<[Option; C]> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(target: [Option; C]) -> Self { + Self::Static(target.to_vec()) + } +} + +// From multiple strings ////////////////////////////////////////////////////// + +macro_rules! impl_from_strings_inner { + ($el_ty:ty, $str_ty:ty, $target:ident) => { + Self::Static( + $target + .iter() + .filter_map(|sel: &$str_ty| -> Option>> { + cfg_if! { if #[cfg(feature = "ssr")] { + let _ = sel; + None + } else { + use wasm_bindgen::JsCast; + + if let Ok(node_list) = document().query_selector_all(sel) { + let mut list = Vec::with_capacity(node_list.length() as usize); + for i in 0..node_list.length() { + let node: $el_ty = node_list.get(i).expect("checked the range").unchecked_into(); + list.push(Some(node)); + } + + Some(list) + } else { + None + } + }} + }) + .flatten() + .collect(), + ) + }; +} + +macro_rules! impl_from_strings_with_container { + ($el_ty:ty, $str_ty:ty, $container_ty:ty) => { + impl From<$container_ty> for ElementsMaybeSignal<$el_ty, $el_ty> { + fn from(target: $container_ty) -> Self { + impl_from_strings_inner!($el_ty, $str_ty, target) + } + } + }; +} + +macro_rules! impl_from_strings { + ($el_ty:ty, $str_ty:ty) => { + impl_from_strings_with_container!($el_ty, $str_ty, Vec<$str_ty>); + impl_from_strings_with_container!($el_ty, $str_ty, &[$str_ty]); + impl From<[$str_ty; C]> for ElementsMaybeSignal<$el_ty, $el_ty> { + fn from(target: [$str_ty; C]) -> Self { + impl_from_strings_inner!($el_ty, $str_ty, target) + } + } + impl From<&[$str_ty; C]> for ElementsMaybeSignal<$el_ty, $el_ty> { + fn from(target: &[$str_ty; C]) -> Self { + impl_from_strings_inner!($el_ty, $str_ty, target) + } + } + }; +} + +impl_from_strings!(web_sys::Element, &str); +impl_from_strings!(web_sys::Element, String); +impl_from_strings!(web_sys::EventTarget, &str); +impl_from_strings!(web_sys::EventTarget, String); + // From signal of vec //////////////////////////////////////////////////////////////// impl From>> for ElementsMaybeSignal @@ -367,8 +501,77 @@ where } } +impl From>> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(list: Vec>) -> Self { + let list = list.clone(); + + Self::Dynamic(Signal::derive(move || { + list.iter().map(|t| Some(t.get())).collect() + })) + } +} + +impl From>>> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(list: Vec>>) -> Self { + let list = list.clone(); + + Self::Dynamic(Signal::derive(move || { + list.iter().map(|t| t.get()).collect() + })) + } +} + +impl From<[Signal; C]> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(list: [Signal; C]) -> Self { + let list = list.to_vec(); + + Self::Dynamic(Signal::derive(move || { + list.iter().map(|t| Some(t.get())).collect() + })) + } +} + +impl From<[Signal>; C]> for ElementsMaybeSignal +where + T: Into + Clone + 'static, +{ + fn from(list: [Signal>; C]) -> Self { + let list = list.to_vec(); + + Self::Dynamic(Signal::derive(move || { + list.iter().map(|t| t.get()).collect() + })) + } +} + // From multiple NodeRefs ////////////////////////////////////////////////////////////// +macro_rules! impl_from_multi_node_ref_inner { + ($ty:ty, $node_refs:ident) => { + Self::Dynamic(Signal::derive(move || { + $node_refs + .iter() + .map(|node_ref| { + node_ref.get().map(move |el| { + let el = el.into_any(); + let el: $ty = el.deref().clone().into(); + el + }) + }) + .collect() + })) + }; +} + macro_rules! impl_from_multi_node_ref { ($ty:ty) => { impl From<&[NodeRef]> for ElementsMaybeSignal<$ty, $ty> @@ -377,19 +580,27 @@ macro_rules! impl_from_multi_node_ref { { fn from(node_refs: &[NodeRef]) -> Self { let node_refs = node_refs.to_vec(); + impl_from_multi_node_ref_inner!($ty, node_refs) + } + } - Self::Dynamic(Signal::derive(move || { - node_refs - .iter() - .map(|node_ref| { - node_ref.get().map(move |el| { - let el = el.into_any(); - let el: $ty = el.deref().clone().into(); - el - }) - }) - .collect() - })) + impl From<[NodeRef; C]> for ElementsMaybeSignal<$ty, $ty> + where + R: ElementDescriptor + Clone + 'static, + { + fn from(node_refs: [NodeRef; C]) -> Self { + let node_refs = node_refs.to_vec(); + impl_from_multi_node_ref_inner!($ty, node_refs) + } + } + + impl From>> for ElementsMaybeSignal<$ty, $ty> + where + R: ElementDescriptor + Clone + 'static, + { + fn from(node_refs: Vec>) -> Self { + let node_refs = node_refs.clone(); + impl_from_multi_node_ref_inner!($ty, node_refs) } } }; @@ -398,6 +609,67 @@ macro_rules! impl_from_multi_node_ref { impl_from_multi_node_ref!(web_sys::EventTarget); impl_from_multi_node_ref!(web_sys::Element); +// From multiple leptos::html::HTMLElement ///////////////////////////////////////// + +macro_rules! impl_from_multi_html_element { + ($ty:ty) => { + impl From<&[HtmlElement]> for ElementsMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref, + { + fn from(value: &[HtmlElement]) -> Self { + Self::Static( + value + .iter() + .map(|el| { + let el: &$ty = el.deref(); + Some(el.clone()) + }) + .collect(), + ) + } + } + + impl From<[HtmlElement; C]> + for ElementsMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref, + { + fn from(value: [HtmlElement; C]) -> Self { + Self::Static( + value + .iter() + .map(|el| { + let el: &$ty = el.deref(); + Some(el.clone()) + }) + .collect(), + ) + } + } + + impl From>> for ElementsMaybeSignal<$ty, $ty> + where + HtmlEl: ElementDescriptor + std::ops::Deref, + { + fn from(value: Vec>) -> Self { + Self::Static( + value + .iter() + .map(|el| { + let el: &$ty = el.deref(); + Some(el.clone()) + }) + .collect(), + ) + } + } + }; +} + +impl_from_multi_html_element!(web_sys::EventTarget); +impl_from_multi_html_element!(web_sys::Element); + // From ElementMaybeSignal ////////////////////////////////////////////////////////////// impl From> for ElementsMaybeSignal diff --git a/src/lib.rs b/src/lib.rs index 1dd36ab..6471119 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,19 +19,23 @@ mod is_none; mod is_ok; mod is_some; mod on_click_outside; +mod use_broadcast_channel; mod signal_debounced; mod signal_throttled; mod use_active_element; mod use_breakpoints; mod use_color_mode; +mod use_cookie; mod use_css_var; mod use_cycle_list; mod use_debounce_fn; +mod use_device_pixel_ratio; mod use_display_media; mod use_document; mod use_document_visibility; mod use_draggable; mod use_drop_zone; +mod use_element_bounding; mod use_element_hover; mod use_element_size; mod use_element_visibility; @@ -46,6 +50,7 @@ mod use_interval_fn; mod use_intl_number_format; mod use_media_query; mod use_mouse; +mod use_mouse_in_element; mod use_mutation_observer; mod use_preferred_contrast; mod use_preferred_dark; @@ -74,19 +79,23 @@ pub use is_none::*; pub use is_ok::*; pub use is_some::*; pub use on_click_outside::*; +pub use use_broadcast_channel::*; pub use signal_debounced::*; pub use signal_throttled::*; pub use use_active_element::*; pub use use_breakpoints::*; pub use use_color_mode::*; +pub use use_cookie::*; pub use use_css_var::*; pub use use_cycle_list::*; pub use use_debounce_fn::*; +pub use use_device_pixel_ratio::*; pub use use_display_media::*; pub use use_document::*; pub use use_document_visibility::*; pub use use_draggable::*; pub use use_drop_zone::*; +pub use use_element_bounding::*; pub use use_element_hover::*; pub use use_element_size::*; pub use use_element_visibility::*; @@ -101,6 +110,7 @@ pub use use_interval_fn::*; pub use use_intl_number_format::*; pub use use_media_query::*; pub use use_mouse::*; +pub use use_mouse_in_element::*; pub use use_mutation_observer::*; pub use use_preferred_contrast::*; pub use use_preferred_dark::*; diff --git a/src/on_click_outside.rs b/src/on_click_outside.rs index 3d1a542..eddb6bf 100644 --- a/src/on_click_outside.rs +++ b/src/on_click_outside.rs @@ -27,7 +27,6 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] { /// /// ``` /// # use leptos::*; -/// # use leptos::ev::resize; /// # use leptos::logging::log; /// # use leptos::html::Div; /// # use leptos_use::on_click_outside; @@ -50,6 +49,33 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] { /// If you are targeting these browsers, we recommend you to include /// [this code snippet](https://gist.github.com/sibbng/13e83b1dd1b733317ce0130ef07d4efd) on your project. /// +/// ## Excluding Elements +/// +/// Use this to ignore clicks on certain elements. +/// +/// ``` +/// # use leptos::*; +/// # use leptos::logging::log; +/// # use leptos::html::Div; +/// # use leptos_use::{on_click_outside_with_options, OnClickOutsideOptions}; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// # let target = create_node_ref::
(); +/// # +/// on_click_outside_with_options( +/// target, +/// move |event| { log!("{:?}", event); }, +/// OnClickOutsideOptions::default().ignore(["input", "#some-id"]), +/// ); +/// # +/// # view! { +/// #
"Hello World"
+/// # } +/// # } +/// +/// ``` +/// /// ## Server-Side Rendering /// /// On the server this amounts to a no-op. @@ -230,12 +256,13 @@ where /// Options for [`on_click_outside_with_options`]. #[derive(Clone, DefaultBuilder)] +#[cfg_attr(feature = "ssr", allow(dead_code))] pub struct OnClickOutsideOptions where T: Into + Clone + 'static, { /// List of elementss that should not trigger the callback. Defaults to `[]`. - #[cfg_attr(feature = "ssr", allow(dead_code))] + #[builder(skip)] ignore: ElementsMaybeSignal, /// Use capturing phase for internal event listener. Defaults to `true`. @@ -257,3 +284,17 @@ where } } } + +impl OnClickOutsideOptions +where + T: Into + Clone + 'static, +{ + /// List of elementss that should not trigger the callback. Defaults to `[]`. + #[cfg_attr(feature = "ssr", allow(dead_code))] + pub fn ignore(self, ignore: impl Into>) -> Self { + Self { + ignore: ignore.into(), + ..self + } + } +} diff --git a/src/use_broadcast_channel.rs b/src/use_broadcast_channel.rs new file mode 100644 index 0000000..49b250a --- /dev/null +++ b/src/use_broadcast_channel.rs @@ -0,0 +1,69 @@ +use crate::storage::Codec; +use crate::use_supported; +use default_struct_builder::DefaultBuilder; +use leptos::*; + +/// +/// +/// ## Demo +/// +/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_broadcast_channel) +/// +/// ## Usage +/// +/// ``` +/// # use leptos::*; +/// # use leptos_use::use_broadcast_channel; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// use_broadcast_channel(); +/// # +/// # view! { } +/// # } +/// ``` +pub fn use_broadcast_channel(name: &str) -> UseBroadcastChannelReturn +where + C: Codec + Default + Clone, +{ + let is_supported = use_supported(|| JsValue::from("BroadcastChannel").js_in(&window())); + + let (is_closed, set_closed) = create_signal(false); + let (channel, set_channel) = create_signal(None::); + let (message, set_message) = create_signal(None::); + let (error, set_error) = create_signal(None::); + + let post = move |data: T| { + if let Some(channel) = channel.get_untracked() { + channel.post_message().ok(); + } + }; +} + +/// Return type of [`use_broadcast_channel`]. +pub struct UseBroadcastChannelReturn +where + PFn: Fn(T), + CFn: Fn(), +{ + /// `true` if this browser supports `BroadcastChannel`s. + is_supported: Signal, + + /// The broadcast channel that is wrapped by this function + channel: Signal>, + + /// Latest message received from the channel + message: Signal>, + + /// Sends a message through the channel + post: PFn, + + /// Closes the channel + close: CFn, + + /// Latest error as reported by the `messageerror` event. + error: Signal>, + + /// Wether the channel is closed + is_closed: Signal, +} diff --git a/src/use_cookie.rs b/src/use_cookie.rs new file mode 100644 index 0000000..ea9137e --- /dev/null +++ b/src/use_cookie.rs @@ -0,0 +1,160 @@ +use cookie::Cookie; +use default_struct_builder::DefaultBuilder; +use std::rc::Rc; + +/// Get a cookie by name, for both SSR and CSR +/// +/// ## Demo +/// +/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_cookie) +/// +/// ## Usage +/// +/// This provides you with the cookie that has been set. For more details on how to use the cookie provided, refer: https://docs.rs/cookie/0.18/cookie/struct.Cookie.html +/// +/// ``` +/// # use leptos::*; +/// # use leptos_use::use_cookie; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// if let Some(cookie) = use_cookie("auth") { +/// view! { +///
+/// format!("'auth' cookie set to `{}`", cookie.value()) +///
+/// }.into_view() +/// } else { +/// view! { +///
+/// "No 'auth' cookie set" +///
+/// }.into_view() +/// } +/// # } +/// ``` +/// +/// ## Server-Side Rendering +/// +/// This works equally well on the server or the client. +/// On the server this function gets the cookie from the HTTP request header. +/// +/// > If you're using `axum` you have to enable the `"axum"` feature in your Cargo.toml. +/// > In case it's `actix-web` enable the feature `"actix"`. +/// +/// ### Bring your own header +/// +/// In case you're neither using Axum nor Actix, or the default implementation is not to your liking, +/// you can provide your own way of reading the cookie header value. +/// +/// ``` +/// # use leptos::*; +/// # use leptos_use::{use_cookie_with_options, UseCookieOptions}; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// use_cookie_with_options("auth", UseCookieOptions::default().ssr_cookies_header_getter(|| { +/// #[cfg(feature = "ssr")] +/// { +/// "Somehow get the value of the cookie header as a string".to_owned() +/// } +/// })); +/// # view! {} +/// # } +/// ``` +pub fn use_cookie(cookie_name: &str) -> Option> { + use_cookie_with_options(cookie_name, UseCookieOptions::default()) +} + +/// Version of [`use_cookie`] that takes [`UseCookieOptions`]. +pub fn use_cookie_with_options( + cookie_name: &str, + options: UseCookieOptions, +) -> Option> { + let UseCookieOptions { + ssr_cookies_header_getter, + } = options; + + let cookie = read_cookies_string(ssr_cookies_header_getter); + + Cookie::split_parse_encoded(cookies) + .filter_map(|cookie| cookie.ok()) + .find(|cookie| cookie.name() == cookie_name) + .map(|cookie| cookie.into_owned()) +} + +/// Options for [`use_cookie_with_options`]. +#[derive(Clone, DefaultBuilder)] +pub struct UseCookieOptions { + /// Getter function to return the string value of the cookie header. + /// When you use one of the features "axum" or "actix" there's a valid default implementation provided. + ssr_cookies_header_getter: Box String>, +} + +impl Default for UseCookieOptions { + #[allow(dead_code)] + fn default() -> Self { + Self { + ssr_cookies_header_getter: Box::new(move || { + #[cfg(feature = "ssr")] + { + #[cfg(any(feature = "axum", feature = "actix"))] + use leptos::expect_context; + + #[cfg(all(feature = "actix", feature = "axum"))] + compile_error!("You cannot enable only one of features \"actix\" and \"axum\" at the same time"); + + #[cfg(feature = "actix")] + const COOKIE: http0_2::HeaderName = http0_2::header::COOKIE; + #[cfg(feature = "axum")] + const COOKIE: http1::HeaderName = http1::header::COOKIE; + + #[cfg(feature = "actix")] + type HeaderValue = http0_2::HeaderValue; + #[cfg(feature = "axum")] + type HeaderValue = http1::HeaderValue; + + #[cfg(any(feature = "axum", feature = "actix"))] + let headers; + #[cfg(feature = "actix")] + { + headers = expect_context::().headers().clone(); + } + #[cfg(feature = "axum")] + { + headers = expect_context::().headers; + } + + #[cfg(all(not(feature = "axum"), not(feature = "actix")))] + { + leptos::logging::warn!("If you're using use_cookie without the feature `axum` or `actix` enabled, you should provide the option `ssr_cookies_header_getter`"); + "".to_owned() + } + + #[cfg(any(feature = "axum", feature = "actix"))] + headers + .get(COOKIE) + .cloned() + .unwrap_or_else(|| HeaderValue::from_static("")) + .to_str() + .unwrap_or_default() + .to_owned() + } + }), + } + } +} + +fn read_cookies_string(ssr_cookies_header_getter: Box String>) -> String { + #[cfg(feature = "ssr")] + ssr_cookies_header_getter(); + + #[cfg(not(feature = "ssr"))] + { + use wasm_bindgen::JsCast; + + let js_value: wasm_bindgen::JsValue = leptos::document().into(); + let document: web_sys::HtmlDocument = js_value.unchecked_into(); + document.cookie().unwrap_or_default() + } +} diff --git a/src/use_debounce_fn.rs b/src/use_debounce_fn.rs index d512720..fb99da2 100644 --- a/src/use_debounce_fn.rs +++ b/src/use_debounce_fn.rs @@ -33,6 +33,8 @@ use std::rc::Rc; /// # } /// ``` /// +/// Please note that if the current component is cleaned up before the throttled callback is called, the throttled callback will not be called. +/// /// You can also pass options to [`use_debounce_fn_with_options`] with a maximum wait time, similar to /// [lodash debounce](https://lodash.com/docs/#debounce). /// diff --git a/src/use_device_pixel_ratio.rs b/src/use_device_pixel_ratio.rs new file mode 100644 index 0000000..e98b44c --- /dev/null +++ b/src/use_device_pixel_ratio.rs @@ -0,0 +1,60 @@ +use cfg_if::cfg_if; +use leptos::*; + +/// Reactive [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) +/// +/// > NOTE: there is no event listener for `window.devicePixelRatio` change. +/// > So this function uses the same mechanism as described in +/// > [this example](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes). +/// +/// ## Demo +/// +/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_device_pixel_ratio) +/// +/// ## Usage +/// +/// ``` +/// # use leptos::*; +/// # use leptos_use::use_device_pixel_ratio; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// let pixel_ratio = use_device_pixel_ratio(); +/// # +/// # view! { } +/// # } +/// ``` +/// +/// ## Server-Side Rendering +/// +/// On the server this function returns a Signal that is always `1.0`. +pub fn use_device_pixel_ratio() -> Signal { + cfg_if! { if #[cfg(feature = "ssr")] { + let pixel_ratio = Signal::derive(|| 1.0); + } else { + use crate::{use_event_listener_with_options, UseEventListenerOptions}; + use leptos::ev::change; + + let initial_pixel_ratio = window().device_pixel_ratio(); + let (pixel_ratio, set_pixel_ratio) = create_signal(initial_pixel_ratio); + + create_effect(move |_| { + let media = window().match_media( + &format!("(resolution: {}dppx)", pixel_ratio.get()) + ).unwrap(); + + _ = use_event_listener_with_options( + media, + change, + move |_| { + set_pixel_ratio.set(window().device_pixel_ratio()); + }, + UseEventListenerOptions::default() + .capture(false) + .passive(true) + .once(true), + ); + }); + }} + pixel_ratio.into() +} diff --git a/src/use_document.rs b/src/use_document.rs index d9afaac..dfbf8b7 100644 --- a/src/use_document.rs +++ b/src/use_document.rs @@ -4,6 +4,8 @@ use std::ops::Deref; use crate::core::impl_ssr_safe_method; #[cfg(not(feature = "ssr"))] use leptos::*; +use wasm_bindgen::JsValue; +use web_sys::NodeList; /// SSR safe `document()`. /// This returns just a new-type wrapper around `Option`. @@ -58,4 +60,13 @@ impl UseDocument { active_element(&self) -> Option; .unwrap_or_default() ); + + impl_ssr_safe_method!( + query_selector(&self, selector: &str) -> Result, JsValue>; + .unwrap_or(Ok(None)) + ); + + impl_ssr_safe_method!( + query_selector_all(&self, selectors: &str) -> Option> + ); } diff --git a/src/use_element_bounding.rs b/src/use_element_bounding.rs new file mode 100644 index 0000000..bb4e809 --- /dev/null +++ b/src/use_element_bounding.rs @@ -0,0 +1,238 @@ +use crate::core::ElementMaybeSignal; +use cfg_if::cfg_if; +use default_struct_builder::DefaultBuilder; +use leptos::*; + +/// Reactive [bounding box](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of an HTML element +/// +/// ## Demo +/// +/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_element_bounding) +/// +/// ## Usage +/// +/// ``` +/// # use leptos::*; +/// # use leptos::html::Div; +/// # use leptos_use::{use_element_bounding, UseElementBoundingReturn}; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// let el = create_node_ref::
(); +/// let UseElementBoundingReturn { +/// x, y, top,right,bottom,left, width, height, .. +/// } = use_element_bounding(el); +/// +/// view! {
} +/// # } +/// ``` +/// ## Server-Side Rendering +/// +/// On the server the returned signals always are `0.0` and `update` is a no-op. +pub fn use_element_bounding(target: El) -> UseElementBoundingReturn +where + El: Into> + Clone, + T: Into + Clone + 'static, +{ + use_element_bounding_with_options(target, UseElementBoundingOptions::default()) +} + +/// Version of [`use_element_bounding`] that takes a `UseElementBoundingOptions`. See [`use_element_bounding`] for how to use. +pub fn use_element_bounding_with_options( + target: El, + options: UseElementBoundingOptions, +) -> UseElementBoundingReturn +where + El: Into> + Clone, + T: Into + Clone + 'static, +{ + let (height, set_height) = create_signal(0.0); + let (width, set_width) = create_signal(0.0); + let (left, set_left) = create_signal(0.0); + let (right, set_right) = create_signal(0.0); + let (top, set_top) = create_signal(0.0); + let (bottom, set_bottom) = create_signal(0.0); + let (x, set_x) = create_signal(0.0); + let (y, set_y) = create_signal(0.0); + + cfg_if! { if #[cfg(feature = "ssr")] { + let _ = target; + let _ = options; + + let _ = set_height; + let _ = set_width; + let _ = set_left; + let _ = set_right; + let _ = set_top; + let _ = set_bottom; + let _ = set_x; + let _ = set_y; + + let update = move || (); + } else { + use leptos::ev::{resize, scroll}; + use crate::{ + use_event_listener_with_options, use_resize_observer, use_window, UseEventListenerOptions, + }; + + let UseElementBoundingOptions { + reset, + window_resize, + window_scroll, + immediate, + } = options; + + let target = target.into(); + + let update = { + let target = target.clone(); + + move || { + let el = target.get_untracked(); + + if let Some(el) = el { + let rect = el.into().get_bounding_client_rect(); + + set_height.set(rect.height()); + set_width.set(rect.width()); + set_left.set(rect.x()); + set_right.set(rect.x() + rect.width()); + set_top.set(rect.y()); + set_bottom.set(rect.y() + rect.height()); + set_x.set(rect.x()); + set_y.set(rect.y()); + } else if reset { + set_height.set(0.0); + set_width.set(0.0); + set_left.set(0.0); + set_right.set(0.0); + set_top.set(0.0); + set_bottom.set(0.0); + set_x.set(0.0); + set_y.set(0.0); + } + } + }; + + use_resize_observer(target.clone(), { + let update = update.clone(); + + move |_, _| { + update(); + } + }); + + let _ = watch( + move || target.get(), + { + let update = update.clone(); + move |_, _, _| { + update(); + } + }, + false, + ); + + if window_scroll { + let _ = use_event_listener_with_options( + use_window(), + scroll, + { + let update = update.clone(); + move |_| update() + }, + UseEventListenerOptions::default() + .capture(true) + .passive(true), + ); + } + + if window_resize { + let _ = use_event_listener_with_options( + use_window(), + resize, + { + let update = update.clone(); + move |_| update() + }, + UseEventListenerOptions::default().passive(true), + ); + } + + if immediate { + update(); + } + }} + + UseElementBoundingReturn { + height: height.into(), + width: width.into(), + left: left.into(), + right: right.into(), + top: top.into(), + bottom: bottom.into(), + x: x.into(), + y: y.into(), + update, + } +} + +/// Options for [`use_element_bounding_with_options`]. +#[derive(DefaultBuilder)] +pub struct UseElementBoundingOptions { + /// Reset values to 0 on component disposal + /// + /// Default: `true` + pub reset: bool, + + /// Listen to window resize event + /// + /// Default: `true` + pub window_resize: bool, + + /// Listen to window scroll event + /// + /// Default: `true` + pub window_scroll: bool, + + /// Immediately call update + /// + /// Default: `true` + pub immediate: bool, +} + +impl Default for UseElementBoundingOptions { + fn default() -> Self { + Self { + reset: true, + window_resize: true, + window_scroll: true, + immediate: true, + } + } +} + +/// Return type of [`use_element_bounding`]. +pub struct UseElementBoundingReturn +where + F: Fn() + Clone, +{ + /// Reactive version of [`BoudingClientRect.height`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/height) + pub height: Signal, + /// Reactive version of [`BoudingClientRect.width`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/width) + pub width: Signal, + /// Reactive version of [`BoudingClientRect.left`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/left) + pub left: Signal, + /// Reactive version of [`BoudingClientRect.right`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/right) + pub right: Signal, + /// Reactive version of [`BoudingClientRect.top`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/top) + pub top: Signal, + /// Reactive version of [`BoudingClientRect.bottom`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/bottom) + pub bottom: Signal, + /// Reactive version of [`BoudingClientRect.x`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/x) + pub x: Signal, + /// Reactive version of [`BoudingClientRect.y`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly/y) + pub y: Signal, + /// Function to re-evaluate `get_bounding_client_rect()` and update the signals. + pub update: F, +} diff --git a/src/use_element_size.rs b/src/use_element_size.rs index bc6a226..aa82d24 100644 --- a/src/use_element_size.rs +++ b/src/use_element_size.rs @@ -49,8 +49,7 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] { /// - [`use_resize_observer`] pub fn use_element_size(target: El) -> UseElementSizeReturn where - El: Clone, - El: Into>, + El: Into> + Clone, T: Into + Clone + 'static, { use_element_size_with_options(target, UseElementSizeOptions::default()) @@ -63,8 +62,7 @@ pub fn use_element_size_with_options( options: UseElementSizeOptions, ) -> UseElementSizeReturn where - El: Clone, - El: Into>, + El: Into> + Clone, T: Into + Clone + 'static, { let UseElementSizeOptions { box_, initial_size } = options; @@ -72,8 +70,8 @@ where let (width, set_width) = create_signal(initial_size.width); let (height, set_height) = create_signal(initial_size.height); - cfg_if! { if #[cfg(not(feature = "ssr"))] { - + #[cfg(not(feature = "ssr"))] + { let box_ = box_.unwrap_or(web_sys::ResizeObserverBoxOptions::ContentBox); let target = target.into(); @@ -128,7 +126,10 @@ where ); } } - } else if !box_size.is_null() && !box_size.is_undefined() && box_size.length() > 0 { + } else if !box_size.is_null() + && !box_size.is_undefined() + && box_size.length() > 0 + { let format_box_size = if box_size.is_array() { box_size.to_vec() } else { @@ -164,7 +165,7 @@ where }, WatchOptions::default().immediate(false), ); - }} + } UseElementSizeReturn { width: width.into(), diff --git a/src/use_geolocation.rs b/src/use_geolocation.rs index 034828f..2aeda83 100644 --- a/src/use_geolocation.rs +++ b/src/use_geolocation.rs @@ -1,9 +1,6 @@ -use crate::use_window; +use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::*; -use std::cell::Cell; -use std::rc::Rc; -use wasm_bindgen::prelude::*; /// Reactive [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API). /// It allows the user to provide their location to web applications if they so desire. For privacy reasons, @@ -32,6 +29,10 @@ use wasm_bindgen::prelude::*; /// # view! { } /// # } /// ``` +/// +/// ## Server-Side Rendering +/// +/// On the server all signals returns will always contain `None` and the functions do nothing. pub fn use_geolocation() -> UseGeolocationReturn { use_geolocation_with_options(UseGeolocationOptions::default()) } @@ -44,74 +45,89 @@ pub fn use_geolocation_with_options( let (error, set_error) = create_signal(None::); let (coords, set_coords) = create_signal(None::); - let update_position = move |position: web_sys::Position| { - set_located_at.set(Some(position.timestamp())); - set_coords.set(Some(position.coords())); - set_error.set(None); - }; + cfg_if! { if #[cfg(feature = "ssr")] { + let resume = || (); + let pause = || (); - let on_error = move |err: web_sys::PositionError| { - set_error.set(Some(err)); - }; + let _ = options; + let _ = set_located_at; + let _ = set_error; + let _ = set_coords; + } else { + use crate::use_window; + use std::cell::Cell; + use std::rc::Rc; + use wasm_bindgen::prelude::*; - let watch_handle = Rc::new(Cell::new(None::)); + let update_position = move |position: web_sys::Position| { + set_located_at.set(Some(position.timestamp())); + set_coords.set(Some(position.coords())); + set_error.set(None); + }; - let resume = { - let watch_handle = Rc::clone(&watch_handle); - let position_options = options.as_position_options(); + let on_error = move |err: web_sys::PositionError| { + set_error.set(Some(err)); + }; - move || { - let navigator = use_window().navigator(); - if let Some(navigator) = navigator { - if let Ok(geolocation) = navigator.geolocation() { - let update_position = - Closure::wrap(Box::new(update_position) as Box); - let on_error = - Closure::wrap(Box::new(on_error) as Box); + let watch_handle = Rc::new(Cell::new(None::)); - watch_handle.replace( - geolocation - .watch_position_with_error_callback_and_options( - update_position.as_ref().unchecked_ref(), - Some(on_error.as_ref().unchecked_ref()), - &position_options, - ) - .ok(), - ); + let resume = { + let watch_handle = Rc::clone(&watch_handle); + let position_options = options.as_position_options(); - update_position.forget(); - on_error.forget(); - } - } - } - }; - - if options.immediate { - resume(); - } - - let pause = { - let watch_handle = Rc::clone(&watch_handle); - - move || { - let navigator = use_window().navigator(); - if let Some(navigator) = navigator { - if let Some(handle) = watch_handle.take() { + move || { + let navigator = use_window().navigator(); + if let Some(navigator) = navigator { if let Ok(geolocation) = navigator.geolocation() { - geolocation.clear_watch(handle); + let update_position = + Closure::wrap(Box::new(update_position) as Box); + let on_error = + Closure::wrap(Box::new(on_error) as Box); + + watch_handle.replace( + geolocation + .watch_position_with_error_callback_and_options( + update_position.as_ref().unchecked_ref(), + Some(on_error.as_ref().unchecked_ref()), + &position_options, + ) + .ok(), + ); + + update_position.forget(); + on_error.forget(); } } } - } - }; + }; - on_cleanup({ - let pause = pause.clone(); - - move || { - pause(); + if options.immediate { + resume(); } - }); + + let pause = { + let watch_handle = Rc::clone(&watch_handle); + + move || { + let navigator = use_window().navigator(); + if let Some(navigator) = navigator { + if let Some(handle) = watch_handle.take() { + if let Ok(geolocation) = navigator.geolocation() { + geolocation.clear_watch(handle); + } + } + } + } + }; + + on_cleanup({ + let pause = pause.clone(); + + move || { + pause(); + } + }); + }} UseGeolocationReturn { coords: coords.into(), @@ -123,7 +139,8 @@ pub fn use_geolocation_with_options( } /// Options for [`use_geolocation_with_options`]. -#[derive(DefaultBuilder)] +#[derive(DefaultBuilder, Clone)] +#[allow(dead_code)] pub struct UseGeolocationOptions { /// If `true` the geolocation watch is started when this function is called. /// If `false` you have to call `resume` manually to start it. Defaults to `true`. @@ -159,6 +176,7 @@ impl Default for UseGeolocationOptions { } } +#[cfg(not(feature = "ssr"))] impl UseGeolocationOptions { fn as_position_options(&self) -> web_sys::PositionOptions { let UseGeolocationOptions { diff --git a/src/use_intl_number_format.rs b/src/use_intl_number_format.rs index ef74094..576651e 100644 --- a/src/use_intl_number_format.rs +++ b/src/use_intl_number_format.rs @@ -659,10 +659,12 @@ pub struct UseIntlNumberFormatOptions { maximum_fraction_digits: Option, /// The minimum number of significant digits to use. Possible values are from 1 to 21; the default is 1. - minimum_significant_digits: u8, + #[builder(into)] + minimum_significant_digits: Option, /// The maximum number of significant digits to use. Possible values are from 1 to 21; the default is 21. - maximum_significant_digits: u8, + #[builder(into)] + maximum_significant_digits: Option, } impl UseIntlNumberFormatOptions { @@ -700,8 +702,8 @@ impl Default for UseIntlNumberFormatOptions { minimum_integer_digits: 1, minimum_fraction_digits: None, maximum_fraction_digits: None, - minimum_significant_digits: 1, - maximum_significant_digits: 21, + minimum_significant_digits: None, + maximum_significant_digits: None, } } } @@ -785,16 +787,20 @@ impl From for js_sys::Object { ); } - let _ = Reflect::set( - &obj, - &"minimumSignificantDigits".into(), - &options.minimum_significant_digits.into(), - ); - let _ = Reflect::set( - &obj, - &"maximumSignificantDigits".into(), - &options.maximum_significant_digits.into(), - ); + if let Some(minimum_significant_digits) = options.minimum_significant_digits { + let _ = Reflect::set( + &obj, + &"minimumSignificantDigits".into(), + &minimum_significant_digits.into(), + ); + } + if let Some(maximum_significant_digits) = options.maximum_significant_digits { + let _ = Reflect::set( + &obj, + &"maximumSignificantDigits".into(), + &maximum_significant_digits.into(), + ); + } obj } diff --git a/src/use_mouse.rs b/src/use_mouse.rs index f81741a..74f943f 100644 --- a/src/use_mouse.rs +++ b/src/use_mouse.rs @@ -94,8 +94,7 @@ pub fn use_mouse() -> UseMouseReturn { /// Variant of [`use_mouse`] that accepts options. Please see [`use_mouse`] for how to use. pub fn use_mouse_with_options(options: UseMouseOptions) -> UseMouseReturn where - El: Clone, - El: Into>, + El: Into> + Clone, T: Into + Clone + 'static, Ex: UseMouseEventExtractor + Clone + 'static, { @@ -196,11 +195,11 @@ where }} UseMouseReturn { - x, - y, + x: x.into(), + y: y.into(), set_x, set_y, - source_type, + source_type: source_type.into(), } } @@ -306,15 +305,15 @@ impl UseMouseEventExtractor for UseMouseEventExtractorDefault {} /// Return type of [`use_mouse`]. pub struct UseMouseReturn { /// X coordinate of the mouse pointer / touch - pub x: ReadSignal, + pub x: Signal, /// Y coordinate of the mouse pointer / touch - pub y: ReadSignal, + pub y: Signal, /// Sets the value of `x`. This does not actually move the mouse cursor. pub set_x: WriteSignal, /// Sets the value of `y`. This does not actually move the mouse cursor. pub set_y: WriteSignal, /// Identifies the source of the reported coordinates - pub source_type: ReadSignal, + pub source_type: Signal, } /// Identifies the source of the reported coordinates diff --git a/src/use_mouse_in_element.rs b/src/use_mouse_in_element.rs new file mode 100644 index 0000000..29ad0f9 --- /dev/null +++ b/src/use_mouse_in_element.rs @@ -0,0 +1,251 @@ +use crate::core::{ElementMaybeSignal, Position}; +use crate::{ + use_mouse_with_options, use_window, UseMouseCoordType, UseMouseEventExtractor, + UseMouseEventExtractorDefault, UseMouseOptions, UseMouseReturn, UseMouseSourceType, UseWindow, +}; +use cfg_if::cfg_if; +use default_struct_builder::DefaultBuilder; +use leptos::*; +use std::marker::PhantomData; + +/// Reactive mouse position related to an element. +/// +/// ## Demo +/// +/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_mouse_in_element) +/// +/// ## Usage +/// +/// ``` +/// # use leptos::*; +/// # use leptos::html::Div; +/// # use leptos_use::{use_mouse_in_element, UseMouseInElementReturn}; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// let target = create_node_ref::
(); +/// let UseMouseInElementReturn { x, y, is_outside, .. } = use_mouse_in_element(target); +/// +/// view! { +///
+///

Hello world

+///
+/// } +/// # } +/// ``` +/// +/// ## Server-Side Rendering +/// +/// On the server this returns simple Signals with the `initial_value` for `x` and `y`, +/// no-op for `stop`, `is_outside = true` and `0.0` for the rest of the signals. +pub fn use_mouse_in_element(target: El) -> UseMouseInElementReturn +where + El: Into> + Clone, + T: Into + Clone + 'static, +{ + use_mouse_in_element_with_options(target, Default::default()) +} + +/// Version of [`use_mouse_in_element`] that takes a `UseMouseInElementOptions`. See [`use_mouse_in_element`] for how to use. +pub fn use_mouse_in_element_with_options( + target: El, + options: UseMouseInElementOptions, +) -> UseMouseInElementReturn +where + El: Into> + Clone, + T: Into + Clone + 'static, + OptEl: Into> + Clone, + OptT: Into + Clone + 'static, + OptEx: UseMouseEventExtractor + Clone + 'static, +{ + let UseMouseInElementOptions { + coord_type, + target: use_mouse_target, + touch, + reset_on_touch_ends, + initial_value, + handle_outside, + .. + } = options; + + let UseMouseReturn { + x, y, source_type, .. + } = use_mouse_with_options( + UseMouseOptions::default() + .coord_type(coord_type) + .target(use_mouse_target) + .touch(touch) + .reset_on_touch_ends(reset_on_touch_ends) + .initial_value(initial_value), + ); + + let (element_x, set_element_x) = create_signal(0.0); + let (element_y, set_element_y) = create_signal(0.0); + let (element_position_x, set_element_position_x) = create_signal(0.0); + let (element_position_y, set_element_position_y) = create_signal(0.0); + let (element_width, set_element_width) = create_signal(0.0); + let (element_height, set_element_height) = create_signal(0.0); + let (is_outside, set_outside) = create_signal(true); + + cfg_if! { if #[cfg(feature = "ssr")] { + let stop = || (); + + let _ = handle_outside; + + let _ = set_element_x; + let _ = set_element_y; + let _ = set_element_position_x; + let _ = set_element_position_y; + let _ = set_element_width; + let _ = set_element_height; + let _ = set_outside; + let _ = target; + } else { + use crate::use_event_listener; + use leptos::ev::mouseleave; + + let target = target.into(); + let window = window(); + + let stop = watch( + move || (target.get(), x.get(), y.get()), + move |(el, x, y), _, _| { + if let Some(el) = el { + let el: web_sys::Element = el.clone().into(); + let rect = el.get_bounding_client_rect(); + let left = rect.left(); + let top = rect.top(); + let width = rect.width(); + let height = rect.height(); + + set_element_position_x.set(left + window.page_x_offset().unwrap_or_default()); + set_element_position_y.set(top + window.page_y_offset().unwrap_or_default()); + + set_element_height.set(height); + set_element_width.set(width); + + let el_x = *x - element_position_x.get_untracked(); + let el_y = *y - element_position_y.get_untracked(); + + set_outside.set( + width == 0.0 + || height == 0.0 + || el_x <= 0.0 + || el_y <= 0.0 + || el_x > width + || el_y > height, + ); + + if handle_outside || !is_outside.get_untracked() { + set_element_x.set(el_x); + set_element_y.set(el_y); + } + } + }, + false, + ); + + let _ = use_event_listener(document(), mouseleave, move |_| set_outside.set(true)); + }} + + UseMouseInElementReturn { + x, + y, + source_type, + element_x: element_x.into(), + element_y: element_y.into(), + element_position_x: element_position_x.into(), + element_position_y: element_position_y.into(), + element_width: element_width.into(), + element_height: element_height.into(), + is_outside: is_outside.into(), + stop, + } +} + +/// Options for [`use_mouse_in_element_with_options`]. +#[derive(DefaultBuilder)] +pub struct UseMouseInElementOptions +where + El: Clone + Into>, + T: Into + Clone + 'static, + Ex: UseMouseEventExtractor + Clone, +{ + /// How to extract the x, y coordinates from mouse events or touches + coord_type: UseMouseCoordType, + + /// Listen events on `target` element. Defaults to `window` + target: El, + + /// Listen to `touchmove` events. Defaults to `true`. + touch: bool, + + /// Reset to initial value when `touchend` event fired. Defaults to `false` + reset_on_touch_ends: bool, + + /// Initial values. Defaults to `{x: 0.0, y: 0.0}`. + initial_value: Position, + + /// If `true` updates the `element_x` and `element_y` signals even if the + /// mouse is outside of the element. If `false` it doesn't update them when outside. + /// Defaults to `true`. + handle_outside: bool, + + #[builder(skip)] + _marker: PhantomData, +} + +impl Default + for UseMouseInElementOptions +{ + fn default() -> Self { + Self { + coord_type: UseMouseCoordType::::default(), + target: use_window(), + touch: true, + reset_on_touch_ends: false, + initial_value: Position { x: 0.0, y: 0.0 }, + handle_outside: true, + _marker: PhantomData, + } + } +} + +/// Return type of [`use_mouse_in_element`]. +pub struct UseMouseInElementReturn +where + F: Fn() + Clone, +{ + /// X coordinate of the mouse pointer / touch + pub x: Signal, + + /// Y coordinate of the mouse pointer / touch + pub y: Signal, + + /// Identifies the source of the reported coordinates + pub source_type: Signal, + + /// X coordinate of the pointer relative to the left edge of the element + pub element_x: Signal, + + /// Y coordinate of the pointer relative to the top edge of the element + pub element_y: Signal, + + /// X coordinate of the element relative to the left edge of the document + pub element_position_x: Signal, + + /// Y coordinate of the element relative to the top edge of the document + pub element_position_y: Signal, + + /// Width of the element + pub element_width: Signal, + + /// Height of the element + pub element_height: Signal, + + /// `true` if the mouse is outside of the element + pub is_outside: Signal, + + /// Stop watching + pub stop: F, +} diff --git a/src/use_resize_observer.rs b/src/use_resize_observer.rs index 07e5dd4..ce78e0d 100644 --- a/src/use_resize_observer.rs +++ b/src/use_resize_observer.rs @@ -75,12 +75,16 @@ where T: Into + Clone + 'static, F: FnMut(Vec, web_sys::ResizeObserver) + 'static, { - cfg_if! { if #[cfg(feature = "ssr")] { + #[cfg(feature = "ssr")] + { UseResizeObserverReturn { is_supported: Signal::derive(|| true), - stop: || {} + stop: || {}, } - } else { + } + + #[cfg(not(feature = "ssr"))] + { let closure_js = Closure::::new( move |entries: js_sys::Array, observer| { callback( @@ -121,20 +125,20 @@ where move |targets, _, _| { cleanup(); - if is_supported.get() && !targets.is_empty() { - let obs = - web_sys::ResizeObserver::new(closure_js.clone().as_ref().unchecked_ref()) - .expect("failed to create ResizeObserver"); + if is_supported.get_untracked() && !targets.is_empty() { + let obs = web_sys::ResizeObserver::new( + closure_js.clone().as_ref().unchecked_ref(), + ) + .expect("failed to create ResizeObserver"); for target in targets.iter().flatten() { let target: web_sys::Element = target.clone().into(); obs.observe_with_options(&target, &options.clone().into()); } - observer.replace(Some(obs)); } }, - false, + true, ) }; @@ -146,7 +150,7 @@ where on_cleanup(stop.clone()); UseResizeObserverReturn { is_supported, stop } - }} + } } /// Options for [`use_resize_observer_with_options`]. diff --git a/src/use_throttle_fn.rs b/src/use_throttle_fn.rs index 6652248..2f9e61c 100644 --- a/src/use_throttle_fn.rs +++ b/src/use_throttle_fn.rs @@ -36,6 +36,8 @@ pub use crate::utils::ThrottleOptions; /// # } /// ``` /// +/// Please note that if the current component is cleaned up before the throttled callback is called, the throttled callback will not be called. +/// /// You can provide options when you use [`use_throttle_fn_with_options`]. /// /// ``` diff --git a/src/utils/filters/debounce.rs b/src/utils/filters/debounce.rs index 6186960..9e5bc86 100644 --- a/src/utils/filters/debounce.rs +++ b/src/utils/filters/debounce.rs @@ -3,7 +3,7 @@ use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::leptos_dom::helpers::TimeoutHandle; -use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked}; +use leptos::{on_cleanup, set_timeout_with_handle, MaybeSignal, SignalGetUntracked}; use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::time::Duration; @@ -34,6 +34,14 @@ where } }; + on_cleanup({ + let timer = Rc::clone(&timer); + + move || { + clear_timeout(&timer); + } + }); + let ms = ms.into(); let max_wait_signal = options.max_wait; diff --git a/src/utils/filters/throttle.rs b/src/utils/filters/throttle.rs index ceda428..e4eb2c4 100644 --- a/src/utils/filters/throttle.rs +++ b/src/utils/filters/throttle.rs @@ -4,7 +4,7 @@ use crate::core::now; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::leptos_dom::helpers::TimeoutHandle; -use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked}; +use leptos::{on_cleanup, set_timeout_with_handle, MaybeSignal, SignalGetUntracked}; use std::cell::{Cell, RefCell}; use std::cmp::max; use std::rc::Rc; @@ -47,6 +47,8 @@ where } }; + on_cleanup(clear.clone()); + let ms = ms.into(); move |mut _invoke: Rc R>| { diff --git a/src/watch_debounced.rs b/src/watch_debounced.rs index 55d6b26..b52d2eb 100644 --- a/src/watch_debounced.rs +++ b/src/watch_debounced.rs @@ -32,6 +32,8 @@ use leptos::*; /// /// This really is only shorthand shorthand for `watch_with_options(deps, callback, WatchOptions::default().debounce(ms))`. /// +/// Please note that if the current component is cleaned up before the debounced callback is called, the debounced callback will not be called. +/// /// There's also `watch_debounced_with_options` where you can specify the other watch options (except `filter`). /// /// ``` diff --git a/src/watch_throttled.rs b/src/watch_throttled.rs index be45644..00497a3 100644 --- a/src/watch_throttled.rs +++ b/src/watch_throttled.rs @@ -31,6 +31,8 @@ use default_struct_builder::DefaultBuilder; /// /// This really is only shorthand shorthand for `watch_with_options(deps, callback, WatchOptions::default().throttle(ms))`. /// +/// Please note that if the current component is cleaned up before the throttled callback is called, the throttled callback will not be called. +/// /// There's also `watch_throttled_with_options` where you can specify the other watch options (except `filter`). /// /// ```