diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
index b1e6e05..cb67468 100644
--- a/.github/workflows/cd.yml
+++ b/.github/workflows/cd.yml
@@ -23,20 +23,27 @@ jobs:
components: rustfmt, clippy, rust-src
- name: Cache
uses: Swatinem/rust-cache@v2
+
- name: Check function count badge
run: python3 docs/generate_count_badge.py --check
- name: Check version in docs
run: python3 docs/add_version_to_docs.py --check
+
- name: Check formatting
run: cargo fmt --check
- name: Clippy
run: cargo clippy --features docs,math --tests -- -D warnings
+
- name: Run tests (general)
run: cargo test --features math,docs,ssr
- - name: Run tests (axum)
- run: cargo test --features math,docs,ssr,axum --doc use_cookie::use_cookie
- - name: Run tests (actix)
- run: cargo test --features math,docs,ssr,actix --doc use_cookie::use_cookie
+ - name: Run tests (axum) use_cookie
+ run: cargo test --features math,docs,ssr,axum --doc use_cookie
+ - name: Run tests (axum) use_locale
+ run: cargo test --features math,docs,ssr,axum --doc use_locale
+ - name: Run tests (actix) use_cookie
+ run: cargo test --features math,docs,ssr,actix --doc use_cookie
+ - name: Run tests (actix) use_locale
+ run: cargo test --features math,docs,ssr,actix --doc use_locale
#### mdbook
- name: Install mdbook I
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 68ee5cf..1ae502a 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,5 +1,11 @@
on:
pull_request:
+ branches:
+ - main
+ paths:
+ - "**"
+ - "!/*.md"
+ - "!/**.md"
workflow_dispatch:
name: Tests
@@ -20,13 +26,18 @@ jobs:
components: rustfmt, clippy, rust-src
- name: Cache
uses: Swatinem/rust-cache@v2
- - name: Check formatting
- run: cargo fmt --check
- - name: Clippy
- run: cargo clippy --features docs,math --tests -- -D warnings
+
- name: Run tests (general)
run: cargo test --features math,docs,ssr
- - name: Run tests (axum)
- run: cargo test --features math,docs,ssr,axum --doc use_cookie::use_cookie
- - name: Run tests (actix)
- run: cargo test --features math,docs,ssr,actix --doc use_cookie::use_cookie
+
+ - name: Run tests (axum) use_cookie
+ run: cargo test --features math,docs,ssr,axum --doc use_cookie
+
+ - name: Run tests (axum) use_locale
+ run: cargo test --features math,docs,ssr,axum --doc use_locale
+
+ - name: Run tests (actix) use_cookie
+ run: cargo test --features math,docs,ssr,actix --doc use_cookie
+
+ - name: Run tests (actix) use_locale
+ run: cargo test --features math,docs,ssr,actix --doc use_locale
diff --git a/.idea/leptos-use.iml b/.idea/leptos-use.iml
index b65a8a2..a9c24d6 100644
--- a/.idea/leptos-use.iml
+++ b/.idea/leptos-use.iml
@@ -78,6 +78,8 @@
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cba1230..1dea579 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,56 @@
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] -
+
+### New Functions 🚀
+
+- `use_prefers_reduced_motion`
+
+## [0.12.0] - 2024-08-14
+
+> Make sure you also update `cargo-leptos` to the latest version if you use that.
+
+### Breaking Changes 🛠
+
+- Updated to web_sys 0.3.70 which unfortunately is breaking some things.
+- `use_clipboard` doesn't need the unstable flags anymore.
+- `use_locale` now uses `unic_langid::LanguageIdentifier` and proper locale matching (thanks to @mondeja).
+- Removed `UseMouseEventExtractorDefault` and reworked `UseMouseCoordType` (thanks to @carloskiki)
+- `use_preferred_dark` and `use_color_mode` now try to read the `Sec-CH-Prefers-Color-Scheme` header in SSR. This brings
+ the necessity to enable an additional feature for them (`axum` / `actix` / `spin`).
+
+### Fixes 🍕
+
+- Fixed the codec chapter in the book to refer to crate `codee`.
+
+## [0.11.4] - 2024-08-12
+
+### New Features 🚀
+
+- `use_web_notification` now supports the options `renotify`, `silent` and `image` (thanks to @hcandelaria).
+- `sync_signal` no supports the options `assign_ltr` and `assign_rtl`.
+
+## [0.11.3] - 2024-07-31
+
+### Fix 🍕
+
+- Made `use_timeout_fn` SSR-safe
+
+## [0.11.2] - 2024-07-30
+
+### Change 🔥
+
+- `use_locale` has now a supported locale list.
+
+## (yanked) [0.11.1] - 2024-07-28
+
+### New Functions 🚀
+
+- `use_locale` (thanks to @BrandonDyer64)
+- `use_locales` (thanks to @BrandonDyer64)
+- `header` – Standard implementations for reading a header on the server.
+
## [0.11.0] - 2024-07-27
### Breaking Changes 🛠
@@ -37,7 +87,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
the DOM controlled by a value from storage. This leads to hydration errors which can be fixed by setting this new
option to `true`.
- `cookie::SameSite` is now re-exported
-- Changing the signal returned by `use_cookie` now tries and changes the headers during SSR.
+- Changing the signal returned by `use_cookie` now tries and changes the headers during SSR.
- New book chapter about codecs
- The macro `use_derive_signal!` is now exported (thanks to @mscofield0).
@@ -72,7 +122,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The new `UseWebSocketOptions::on_message` takes a `&T`.
- `UseWebSocketOptions::on_error` now takes a `UseWebSocketError` instead of a `web_sys::Event`.
- `use_storage` now always saves the default value to storage if the key doesn't exist yet.
-- Renamed `BreakpointsSematic` to `BreakpointsSemantic` and `breakpoints_sematic` to `breakpoints_semantic`
+- Renamed `BreakpointsSematic` to `BreakpointsSemantic` and `breakpoints_sematic` to `breakpoints_semantic`
(note the `n`) (thanks to @mondeja).
### Fixes 🍕
diff --git a/Cargo.toml b/Cargo.toml
index 619a4a7..55093b3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,10 +1,10 @@
[package]
name = "leptos-use"
-version = "0.11.0"
+version = "0.12.0"
edition = "2021"
authors = ["Marc-Stefan Cassola"]
categories = ["gui", "web-programming"]
-description = "Collection of essential Leptos utilities inspired by SolidJS USE / VueUse"
+description = "Collection of essential Leptos utilities inspired by React-Use / VueUse / SolidJS-USE"
exclude = ["examples/", "tests/"]
keywords = ["leptos", "utilities"]
license = "MIT OR Apache-2.0"
@@ -14,14 +14,14 @@ homepage = "https://leptos-use.rs"
[dependencies]
actix-web = { version = "4", optional = true, default-features = false }
-async-trait = "0.1"
+async-trait = { version = "0.1", optional = true }
cfg-if = "1"
-codee = "0.1"
-cookie = { version = "0.18", features = ["percent-encode"] }
+codee = { version = "0.1", optional = true }
+cookie = { version = "0.18", features = ["percent-encode"], optional = true }
default-struct-builder = "0.5"
-futures-util = "0.3"
-gloo-timers = { version = "0.3", features = ["futures"] }
-gloo-utils = { version = "0.2" }
+futures-util = { version = "0.3", optional = true }
+gloo-timers = { version = "0.3", optional = true, features = ["futures"] }
+gloo-utils = { version = "0.2", optional = true }
http1 = { version = "1", optional = true, package = "http" }
http0_2 = { version = "0.2", optional = true, package = "http" }
js-sys = "0.3"
@@ -34,116 +34,317 @@ num = { version = "0.4", optional = true }
paste = "1"
send_wrapper = "0.6.0"
thiserror = "1"
-wasm-bindgen = "0.2.92"
+unic-langid = { version = "0.9", optional = true }
+wasm-bindgen = "=0.2.93"
wasm-bindgen-futures = "0.4"
-
-[dependencies.web-sys]
-version = "0.3"
-features = [
- "AddEventListenerOptions",
- "BinaryType",
- "BroadcastChannel",
- "Coordinates",
- "Clipboard",
- "CloseEvent",
- "CssStyleDeclaration",
- "CustomEvent",
- "CustomEventInit",
- "DisplayMediaStreamConstraints",
- "DomRect",
- "DomRectReadOnly",
- "DataTransfer",
- "DragEvent",
- "Element",
- "EventListener",
- "EventListenerOptions",
- "EventSource",
- "EventSourceInit",
- "EventTarget",
- "File",
- "FileList",
- "Geolocation",
- "HtmlDocument",
- "HtmlElement",
- "HtmlLinkElement",
- "HtmlStyleElement",
- "IntersectionObserver",
- "IntersectionObserverInit",
- "IntersectionObserverEntry",
- "Location",
- "MediaDevices",
- "MediaQueryList",
- "MediaStream",
- "MediaStreamConstraints",
- "MediaStreamTrack",
- "MessageEvent",
- "MouseEvent",
- "MutationObserver",
- "MutationObserverInit",
- "MutationRecord",
- "Navigator",
- "NodeList",
- "Notification",
- "NotificationDirection",
- "NotificationOptions",
- "NotificationPermission",
- "Permissions",
- "PermissionState",
- "PermissionStatus",
- "PointerEvent",
- "Position",
- "PositionError",
- "PositionOptions",
- "ReadableStream",
- "ReadableStreamDefaultReader",
- "ReadableStreamGetReaderOptions",
- "ReadableStreamReaderMode",
- "ResizeObserver",
- "ResizeObserverBoxOptions",
- "ResizeObserverEntry",
- "ResizeObserverOptions",
- "ResizeObserverSize",
- "ScrollBehavior",
- "ScrollToOptions",
- "ServiceWorker",
- "ServiceWorkerContainer",
- "ServiceWorkerRegistration",
- "ServiceWorkerState",
- "Storage",
- "StorageEvent",
- "Touch",
- "TouchEvent",
- "TouchList",
- "Url",
- "UrlSearchParams",
- "VisibilityState",
- "WebSocket",
- "WebTransport",
- "WebTransportOptions",
- "WebTransportDatagramDuplexStream",
- "WebTransportBidirectionalStream",
- "Window",
- "WebTransportReceiveStream",
- "WebTransportSendStream",
- "WritableStream",
- "WritableStreamDefaultWriter",
-]
+web-sys = { version = "=0.3.70", optional = true }
[dev-dependencies]
+codee = { version = "0.1", features = ["json_serde", "msgpack_serde", "base64", "prost"] }
getrandom = { version = "0.2", features = ["js"] }
leptos_meta = { git = "https://github.com/leptos-rs/leptos" }
rand = "0.8"
-codee = { version = "0.1", features = ["json_serde", "msgpack_serde", "base64", "prost"] }
serde = { version = "1", features = ["derive"] }
+unic-langid = { version = "0.9", features = ["macros"] }
[features]
+default = [
+ "is_err",
+ "is_none",
+ "is_ok",
+ "is_some",
+ "on_click_outside",
+ "signal_debounced",
+ "signal_throttled",
+ "storage",
+ "sync_signal",
+ "use_active_element",
+ "use_breakpoints",
+ "use_broadcast_channel",
+ "use_clipboard",
+ "use_color_mode",
+ "use_cookie",
+ "use_css_var",
+ "use_cycle_list",
+ "use_debounce_fn",
+ "use_device_orientation",
+ "use_device_pixel_ratio",
+ "use_display_media",
+ "use_document",
+ "use_document_visibility",
+ "use_draggable",
+ "use_drop_zone",
+ "use_element_bounding",
+ "use_element_hover",
+ "use_element_size",
+ "use_element_visibility",
+ "use_event_listener",
+ "use_event_source",
+ "use_favicon",
+ "use_geolocation",
+ "use_idle",
+ "use_infinite_scroll",
+ "use_intersection_observer",
+ "use_interval",
+ "use_interval_fn",
+ "use_intl_number_format",
+ "use_locale",
+ "use_locales",
+ "use_media_query",
+ "use_mouse",
+ "use_mouse_in_element",
+ "use_mutation_observer",
+ "use_permission",
+ "use_preferred_contrast",
+ "use_preferred_dark",
+ "use_prefers_reduced_motion",
+ "use_raf_fn",
+ "use_resize_observer",
+ "use_scroll",
+ "use_service_worker",
+ "use_sorted",
+ "use_supported",
+ "use_throttle_fn",
+ "use_timeout_fn",
+ "use_timestamp",
+ "use_to_string",
+ "use_user_media",
+ "use_web_notification",
+ "use_websocket",
+ "use_window",
+ "use_window_focus",
+ "use_window_scroll",
+ "watch_debounced",
+ "watch_pausable",
+ "watch_throttled",
+ "watch_with_options",
+ "whenever"
+]
actix = ["dep:actix-web", "dep:leptos_actix", "dep:http0_2"]
axum = ["dep:leptos_axum", "dep:http1"]
-docs = []
+docs = ["dep:web-sys"]
+element = ["use_document", "use_window", "dep:web-sys", "web-sys/EventTarget"]
+is = ["use_window"]
+is_err = []
+is_none = []
+is_ok = []
+is_some = []
math = ["num"]
+on_click_outside = ["use_event_listener", "is"]
+signal_debounced = ["use_debounce_fn"]
+signal_throttled = ["use_throttle_fn"]
spin = ["dep:leptos-spin", "dep:http1"]
ssr = []
+storage = [
+ "use_event_listener",
+ "use_window",
+ "watch_with_options",
+ "dep:web-sys",
+ "dep:codee",
+ "web-sys/CustomEventInit",
+ "web-sys/Storage"
+]
+sync_signal = []
+use_active_element = ["use_event_listener"]
+use_breakpoints = ["use_media_query"]
+use_broadcast_channel = [
+ "use_event_listener",
+ "use_supported",
+ "dep:codee",
+ "web-sys/BroadcastChannel",
+]
+use_clipboard = [
+ "use_event_listener",
+ "use_permission",
+ "use_supported",
+ "use_timeout_fn",
+ "web-sys/Clipboard",
+]
+use_color_mode = [
+ "use_cookie",
+ "use_cycle_list",
+ "use_preferred_dark",
+ "storage",
+ "sync_signal"
+]
+use_cookie = [
+ "use_broadcast_channel",
+ "watch_pausable",
+ "dep:cookie",
+ "web-sys/HtmlDocument",
+]
+use_css_var = [
+ "use_mutation_observer",
+ "watch_with_options",
+]
+use_cycle_list = []
+use_debounce_fn = []
+use_device_orientation = ["use_event_listener", "use_supported"]
+use_device_pixel_ratio = ["use_event_listener", "web-sys/MediaQueryList"]
+use_display_media = [
+ "use_window",
+ "web-sys/DisplayMediaStreamConstraints",
+ "web-sys/MediaDevices",
+ "web-sys/MediaStream",
+ "web-sys/MediaStreamTrack",
+]
+use_document = [
+ "dep:web-sys",
+ "web-sys/VisibilityState",
+]
+use_document_visibility = ["use_event_listener", "web-sys/VisibilityState"]
+use_draggable = ["use_event_listener", "web-sys/DomRect"]
+use_drop_zone = [
+ "use_event_listener",
+ "web-sys/DataTransfer",
+ "web-sys/File",
+ "web-sys/FileList"
+]
+use_element_bounding = [
+ "use_event_listener",
+ "use_resize_observer",
+ "web-sys/DomRect",
+]
+use_element_hover = ["use_event_listener"]
+use_element_size = [
+ "use_resize_observer",
+ "watch_with_options",
+ "web-sys/ResizeObserverSize",
+]
+use_element_visibility = [
+ "use_intersection_observer",
+ "web-sys/DomRect",
+]
+use_event_listener = [
+ "element",
+ "watch_with_options",
+ "dep:web-sys",
+ "web-sys/EventTarget",
+ "web-sys/EventListenerOptions"
+]
+use_event_source = [
+ "use_event_listener",
+ "web-sys/EventSource",
+ "web-sys/EventSourceInit",
+ "dep:codee",
+]
+use_favicon = []
+use_geolocation = [
+ "use_window",
+ "web-sys/Coordinates",
+ "web-sys/Geolocation",
+ "web-sys/Position",
+ "web-sys/PositionError",
+ "web-sys/PositionOptions",
+]
+use_idle = [
+ "use_event_listener",
+ "use_document",
+ "use_timestamp",
+]
+use_infinite_scroll = [
+ "use_element_visibility",
+ "use_scroll",
+ "dep:gloo-timers",
+ "dep:futures-util",
+]
+use_intersection_observer = [
+ "element",
+ "watch_with_options",
+ "web-sys/IntersectionObserver",
+ "web-sys/IntersectionObserverEntry",
+ "web-sys/IntersectionObserverInit",
+]
+use_interval = ["use_interval_fn"]
+use_interval_fn = []
+use_intl_number_format = []
+use_locale = ["use_locales", "dep:unic-langid"]
+use_locales = ["use_event_listener", "use_window"]
+use_media_query = ["use_event_listener"]
+use_mouse = [
+ "element",
+ "use_event_listener",
+ "use_window",
+ "web-sys/Touch",
+ "web-sys/TouchList",
+]
+use_mouse_in_element = [
+ "use_mouse",
+ "web-sys/DomRect",
+]
+use_mutation_observer = [
+ "element",
+ "use_supported",
+ "web-sys/MutationObserver",
+ "web-sys/MutationObserverInit",
+ "web-sys/MutationRecord",
+]
+use_permission = [
+ "use_event_listener",
+ "web-sys/Permissions",
+ "web-sys/PermissionState",
+ "web-sys/PermissionStatus",
+]
+use_preferred_contrast = ["use_media_query"]
+use_preferred_dark = ["use_media_query"]
+use_prefers_reduced_motion = ["use_media_query"]
+use_raf_fn = []
+use_resize_observer = [
+ "element",
+ "use_supported",
+ "web-sys/DomRectReadOnly",
+ "web-sys/ResizeObserver",
+ "web-sys/ResizeObserverBoxOptions",
+ "web-sys/ResizeObserverEntry",
+ "web-sys/ResizeObserverOptions",
+]
+use_scroll = [
+ "element",
+ "use_event_listener",
+ "use_debounce_fn",
+ "use_throttle_fn",
+ "web-sys/ScrollBehavior",
+ "web-sys/ScrollToOptions",
+]
+use_service_worker = [
+ "use_window",
+ "web-sys/ServiceWorker",
+ "web-sys/ServiceWorkerContainer",
+ "web-sys/ServiceWorkerRegistration"
+]
+use_sorted = []
+use_supported = []
+use_throttle_fn = []
+use_timeout_fn = []
+use_timestamp = ["use_interval_fn", "use_raf_fn"]
+use_to_string = []
+use_user_media = [
+ "use_window",
+ "web-sys/MediaDevices",
+ "web-sys/MediaStream",
+ "web-sys/MediaStreamConstraints",
+ "web-sys/MediaStreamTrack",
+]
+use_web_notification = [
+ "use_supported",
+ "use_window",
+ "use_event_listener",
+ "web-sys/Notification",
+ "web-sys/NotificationOptions",
+ "web-sys/NotificationPermission",
+ "web-sys/NotificationDirection",
+ "web-sys/VisibilityState"
+]
+use_websocket = ["dep:codee"]
+use_window = ["use_document", "dep:web-sys", "web-sys/Navigator", "web-sys/MediaQueryList"]
+use_window_focus = ["use_event_listener"]
+use_window_scroll = ["use_event_listener", "use_window"]
wasm_ssr = []
+watch_debounced = ["watch_with_options"]
+watch_pausable = ["watch_with_options"]
+watch_throttled = ["watch_with_options"]
+watch_with_options = []
+whenever = []
[package.metadata.docs.rs]
features = ["math", "docs", "ssr"]
diff --git a/README.md b/README.md
index b432e0f..de776b8 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
-
+
@@ -87,9 +87,9 @@ 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 |
-| 0.10, 0.11 | 0.6 |
+| 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 |
+| 0.10, 0.11, 0.12 | 0.6 |
diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index 502a1a6..ebbb19f 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -48,6 +48,7 @@
- [use_permission](browser/use_permission.md)
- [use_preferred_contrast](browser/use_preferred_contrast.md)
- [use_preferred_dark](browser/use_preferred_dark.md)
+- [use_prefers_reduced_motion](browser/use_prefers_reduced_motion.md)
- [use_service_worker](browser/use_service_worker.md)
- [use_user_media](browser/use_user_media.md)
- [use_web_notification](browser/use_web_notification.md)
@@ -99,6 +100,7 @@
# Utilities
+- [header](utilities/header.md)
- [is_err](utilities/is_err.md)
- [is_none](utilities/is_none.md)
- [is_ok](utilities/is_ok.md)
@@ -113,6 +115,8 @@
# Intl
- [use_intl_number_format](intl/use_intl_number_format.md)
+- [use_locale](intl/use_locale.md)
+- [use_locales](intl/use_locales.md)
# @Math
diff --git a/docs/book/src/browser/use_prefers_reduced_motion.md b/docs/book/src/browser/use_prefers_reduced_motion.md
new file mode 100644
index 0000000..d6aa3ae
--- /dev/null
+++ b/docs/book/src/browser/use_prefers_reduced_motion.md
@@ -0,0 +1,3 @@
+# use_prefers_reduced_motion
+
+
diff --git a/docs/book/src/codecs.md b/docs/book/src/codecs.md
index 2e54fa2..45b2219 100644
--- a/docs/book/src/codecs.md
+++ b/docs/book/src/codecs.md
@@ -1,9 +1,9 @@
# Encoding and Decoding Data
Several functions encode and decode data for storing it and/or sending it over the network. To do this, codecs
-located at [`src/utils/codecs`](https://github.com/Synphonyte/leptos-use/tree/main/src/utils/codecs) are used. They
-implement the traits [`Encoder`](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/mod.rs#L9) with the
-method `encode` and [`Decoder`](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/mod.rs#L17) with the
+from the crate [`codee`](https://docs.rs/codee/latest/codee/) are used. They
+implement the traits [`Encoder`](https://docs.rs/codee/latest/codee/trait.Encoder.html) with the
+method `encode` and [`Decoder`](https://docs.rs/codee/latest/codee/trait.Decoder.html) with the
method `decode`.
There are two types of codecs: One that encodes as binary data (`Vec[u8]`) and another type that encodes as
@@ -11,26 +11,8 @@ strings (`String`). There is also an adapter
[`Base64`](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/string/base64.rs) that can be used to
wrap a binary codec and make it a string codec by representing the binary data as a base64 string.
-## Available Codecs
-
-### String Codecs
-
-- [**`FromToStringCodec`
- **](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/string/from_to_string.rs)
-- [**`JsonSerdeCodec`**](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/string/json_serde.rs)**
-
-### Binary Codecs
-
-- [**`FromToBytesCodec`**](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/binary/from_to_bytes.rs)
-- [**`BincodeSerdeCodec`**](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/binary/bincode_serde.rs)
-- [**`MsgpackSerdeCodec`**](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/binary/msgpack_serde.rs)
-
-### Adapters
-
-- [**`Base64`**](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/string/base64.rs) —
- Wraps a binary codec and make it a string codec by representing the binary data as a base64 string.
-- [**`OptionCodec`**](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/option.rs) —
- Wraps a string codec that encodes `T` to create a codec that encodes `Option`.
+Please check the documentation of [`codee`](https://docs.rs/codee/latest/codee/) for more details and a list of all
+available codecs.
## Example
@@ -41,6 +23,7 @@ format. Since cookies can only store strings, we have to use string codecs here.
# use leptos::*;
# use leptos_use::use_cookie;
# use serde::{Deserialize, Serialize};
+# use codee::string::JsonCodec;
# #[component]
# pub fn App(cx: Scope) -> impl IntoView {
@@ -57,100 +40,13 @@ let (cookie, set_cookie) = use_cookie::("my-state-cookie");
## Custom Codecs
-If you don't find a suitable codecs for your needs, you can implement your own; it's straightforward! If you want to
-create a string codec, you can look
-at [`JsonSerdeCodec`](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/string/json_serde.rs).
-In case it's a binary codec, have a look
-at [`BincodeSerdeCodec`](https://github.com/Synphonyte/leptos-use/blob/main/src/utils/codecs/binary/bincode_serde.rs).
+If you don't find a suitable codec for your needs, you can implement your own; it's straightforward!
+If you want to create a string codec, you can look at
+[`JsonSerdeCodec`](https://docs.rs/codee/latest/src/codee/string/json_serde.rs.html).
+In case it's a binary codec, have a look at
+[`BincodeSerdeCodec`](https://docs.rs/codee/latest/src/codee/binary/bincode_serde.rs.html).
## Versioning
-Versioning is the process of handling long-term data that can outlive our code.
-
-For example, we could have a settings struct whose members change over time. We might eventually
-add timezone support, and we might then remove support for a thousands separator for numbers.
-Each change results in a new possible version of the stored data. If we stored these settings
-in browser storage, we would need to handle all possible versions of the data format that can
-occur. If we don't offer versioning, then all settings could revert to the default every time we
-encounter an old format.
-
-How best to handle versioning depends on the codec involved:
-
-- The `FromToStringCodec` can avoid versioning entirely by keeping
- to primitive types. In our example above, we could have decomposed the settings struct into
- separate timezone and number separator fields. These would be encoded as strings and stored as
- two separate key-value fields in the browser rather than a single field. If a field is missing,
- then the value intentionally would fall back to the default without interfering with the other
- field.
-
-- The `ProstCodec` uses [Protocol buffers](https://protobuf.dev/overview/)
- designed to solve the problem of long-term storage. It provides semantics for versioning that
- are not present in JSON or other formats.
-
-- The codecs that use serde under the hood can rely on serde or by
- providing their own manual version handling. See the next sections for more details.
-
-### Rely on `serde`
-
-A simple way to avoid complex versioning is to rely on serde's [field attributes](https://serde.rs/field-attrs.html)
-such as [`serde(default)`](https://serde.rs/field-attrs.html#default)
-and [`serde(rename = "...")`](https://serde.rs/field-attrs.html#rename).
-
-### Manual Version Handling
-
-We look at the example of the `JsonSerdeCodec` in this section.
-
-To implement version handling, we parse the JSON generically then transform the
-resulting `JsValue` before decoding it into our struct again.
-
-Let's look at an example.
-
- ```rust,noplayground
- # use leptos::*;
- # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage, UseStorageOptions};
- # use serde::{Deserialize, Serialize};
- # use serde_json::json;
- # use leptos_use::utils::{Encoder, Decoder};
- #
- # pub fn Demo() -> impl IntoView {
- #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
- pub struct MyState {
- pub hello: String,
- // This field was added in a later version
- pub greeting: String,
- }
-
- pub struct MyStateCodec;
-
- impl Encoder for MyStateCodec {
- type Error = serde_json::Error;
- type Encoded = String;
-
- fn encode(val: &MyState) -> Result {
- serde_json::to_string(val)
- }
- }
-
- impl Decoder for MyStateCodec {
- type Error = serde_json::Error;
- type Encoded = str;
-
- fn decode(stored_value: &Self::Encoded) -> Result {
- let mut val: serde_json::Value = serde_json::from_str(stored_value)?;
- // add "greeting": "Hello" to the object if it's missing
- if let Some(obj) = val.as_object_mut() {
- if !obj.contains_key("greeting") {
- obj.insert("greeting".to_string(), json!("Hello"));
- }
- serde_json::from_value(val)
- } else {
- Ok(MyState::default())
- }
- }
- }
-
- // Then use it like the following just as any other codec.
- let (get, set, remove) = use_local_storage::("my-struct-key");
- # view! { }
- # }
- ```
+For a discussion on how to implement versioning please refer to the
+[relevant section in the docs for `codee`](https://docs.rs/codee/latest/codee/index.html#versioning).
\ No newline at end of file
diff --git a/docs/book/src/intl/use_locale.md b/docs/book/src/intl/use_locale.md
new file mode 100644
index 0000000..56b32d7
--- /dev/null
+++ b/docs/book/src/intl/use_locale.md
@@ -0,0 +1,3 @@
+# use_locale
+
+
diff --git a/docs/book/src/intl/use_locales.md b/docs/book/src/intl/use_locales.md
new file mode 100644
index 0000000..084b0fd
--- /dev/null
+++ b/docs/book/src/intl/use_locales.md
@@ -0,0 +1,3 @@
+# use_locales
+
+
diff --git a/docs/book/src/introduction.md b/docs/book/src/introduction.md
index cf0098a..8bcc1c1 100644
--- a/docs/book/src/introduction.md
+++ b/docs/book/src/introduction.md
@@ -12,6 +12,6 @@
-
+
\ No newline at end of file
diff --git a/docs/book/src/utilities/header.md b/docs/book/src/utilities/header.md
new file mode 100644
index 0000000..2bd2d1f
--- /dev/null
+++ b/docs/book/src/utilities/header.md
@@ -0,0 +1,3 @@
+# header
+
+
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index f428959..187bcf3 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -38,6 +38,8 @@ members = [
"use_interval",
"use_interval_fn",
"use_intl_number_format",
+ "use_locale",
+ "use_locales",
"use_media_query",
"use_mouse",
"use_mouse_in_element",
@@ -45,6 +47,7 @@ members = [
"use_not",
"use_or",
"use_permission",
+ "use_prefers_reduced_motion",
"use_raf_fn",
"use_resize_observer",
"use_round",
diff --git a/examples/on_click_outside/Cargo.toml b/examples/on_click_outside/Cargo.toml
index 3313bf8..c6d6279 100644
--- a/examples/on_click_outside/Cargo.toml
+++ b/examples/on_click_outside/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["on_click_outside", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/signal_debounced/Cargo.toml b/examples/signal_debounced/Cargo.toml
index 6f85a28..9411da3 100644
--- a/examples/signal_debounced/Cargo.toml
+++ b/examples/signal_debounced/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["signal_debounced", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/signal_throttled/Cargo.toml b/examples/signal_throttled/Cargo.toml
index 73b2b91..2a14d8a 100644
--- a/examples/signal_throttled/Cargo.toml
+++ b/examples/signal_throttled/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["signal_throttled", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/ssr/Cargo.toml b/examples/ssr/Cargo.toml
index 5d71626..33e9575 100644
--- a/examples/ssr/Cargo.toml
+++ b/examples/ssr/Cargo.toml
@@ -16,17 +16,31 @@ leptos = { version = "0.6", features = ["nightly"] }
leptos_axum = { version = "0.6", optional = true }
leptos_meta = { version = "0.6", features = ["nightly"] }
leptos_router = { version = "0.6", features = ["nightly"] }
-leptos-use = { path = "../.." }
log = "0.4"
simple_logger = "4"
tokio = { version = "1", features = ["full"], optional = true }
tower = { version = "0.4", optional = true }
+tower-default-headers = { git = "https://github.com/banool/tower-default-headers-rs" }
tower-http = { version = "0.5", features = ["fs"], optional = true }
-wasm-bindgen = "0.2.92"
+wasm-bindgen = "=0.2.93"
thiserror = "1.0.38"
tracing = { version = "0.1.37", optional = true }
http = "1"
+[dependencies.leptos-use]
+path = "../.."
+features = [
+ "use_cookie",
+ "use_color_mode",
+ "use_debounce_fn",
+ "use_event_listener",
+ "use_interval",
+ "use_intl_number_format",
+ "use_locales",
+ "use_timestamp",
+ "storage"
+]
+
[features]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
ssr = [
diff --git a/examples/ssr/src/app.rs b/examples/ssr/src/app.rs
index fed3766..e0380d0 100644
--- a/examples/ssr/src/app.rs
+++ b/examples/ssr/src/app.rs
@@ -1,15 +1,15 @@
use crate::error_template::{AppError, ErrorTemplate};
+use codee::string::FromToStringCodec;
use leptos::ev::{keypress, KeyboardEvent};
use leptos::prelude::*;
use leptos_meta::*;
use leptos_router::*;
use leptos_use::storage::{use_local_storage, use_local_storage_with_options, UseStorageOptions};
-use codee::string::FromToStringCodec;
use leptos_use::{
use_color_mode_with_options, use_cookie_with_options, use_debounce_fn, use_event_listener,
- use_interval, use_intl_number_format, use_preferred_dark, use_timestamp, use_window, ColorMode,
- UseColorModeOptions, UseColorModeReturn, UseCookieOptions, UseIntervalReturn,
- UseIntlNumberFormatOptions,
+ use_interval, use_intl_number_format, use_locales, use_preferred_dark, use_timestamp,
+ use_window, ColorMode, UseColorModeOptions, UseColorModeReturn, UseCookieOptions,
+ UseIntervalReturn, UseIntlNumberFormatOptions,
};
#[component]
@@ -83,12 +83,13 @@ fn HomePage() -> impl IntoView {
.default_value(Some("Bogus string".to_owned())),
);
+ let locales = use_locales();
+
view! {
Leptos-Use SSR Example
Click Me: {count}
- Locale zh-Hans-CN-u-nu-hanidec: {zh_count}
Press any key: {key}
Debounced called: {debounce_value}
Color mode: {move || format!("{:?}", mode.get())}
@@ -99,7 +100,10 @@ fn HomePage() -> impl IntoView {
Dark preferred: {is_dark_preferred}
Test cookie: {move || test_cookie().unwrap_or("".to_string())}
+ {move || format!("Locales:\n {}", locales().join("\n "))}
+ Locale zh-Hans-CN-u-nu-hanidec: {zh_count}
+
0 }>
Greater than 0
diff --git a/examples/ssr/src/main.rs b/examples/ssr/src/main.rs
index 911c6d4..5fd9c6e 100644
--- a/examples/ssr/src/main.rs
+++ b/examples/ssr/src/main.rs
@@ -2,11 +2,13 @@
#[tokio::main]
async fn main() {
use axum::{routing::post, Router};
+ use http::{HeaderMap, HeaderName, HeaderValue};
use leptos::logging::log;
use leptos::prelude::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use leptos_use_ssr::app::*;
use leptos_use_ssr::fileserv::file_and_error_handler;
+ use tower_default_headers::DefaultHeadersLayer;
simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging");
@@ -20,12 +22,19 @@ async fn main() {
let addr = leptos_options.site_addr;
let routes = generate_route_list(|| view! { });
+ let mut default_headers = HeaderMap::new();
+ let color_header = HeaderValue::from_static("Sec-CH-Prefers-Color-Scheme");
+ default_headers.insert(HeaderName::from_static("accept-ch"), color_header.clone());
+ default_headers.insert(HeaderName::from_static("vary"), color_header.clone());
+ default_headers.insert(HeaderName::from_static("critical-ch"), color_header);
+
// build our application with a route
let app = Router::new()
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
.leptos_routes(&leptos_options, routes, || view! { })
.fallback(file_and_error_handler)
- .with_state(leptos_options);
+ .with_state(leptos_options)
+ .layer(DefaultHeadersLayer::new(default_headers));
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
log!("listening on http://{}", &addr);
diff --git a/examples/sync_signal/Cargo.toml b/examples/sync_signal/Cargo.toml
index 0eae2b1..c072a2b 100644
--- a/examples/sync_signal/Cargo.toml
+++ b/examples/sync_signal/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["sync_signal", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_active_element/Cargo.toml b/examples/use_active_element/Cargo.toml
index c19c400..e5c31a5 100644
--- a/examples/use_active_element/Cargo.toml
+++ b/examples/use_active_element/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_active_element", "docs"] }
web-sys = { version = "0.3", features = ["HtmlElement", "DomStringMap"] }
[dev-dependencies]
diff --git a/examples/use_breakpoints/Cargo.toml b/examples/use_breakpoints/Cargo.toml
index 22b894b..b022945 100644
--- a/examples/use_breakpoints/Cargo.toml
+++ b/examples/use_breakpoints/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_breakpoints", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_broadcast_channel/Cargo.toml b/examples/use_broadcast_channel/Cargo.toml
index a7bf756..3cfcd3f 100644
--- a/examples/use_broadcast_channel/Cargo.toml
+++ b/examples/use_broadcast_channel/Cargo.toml
@@ -9,7 +9,7 @@ codee = "0.1"
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_broadcast_channel", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_clipboard/.cargo/config.toml b/examples/use_clipboard/.cargo/config.toml
deleted file mode 100644
index 8467175..0000000
--- a/examples/use_clipboard/.cargo/config.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-[build]
-rustflags = ["--cfg=web_sys_unstable_apis"]
diff --git a/examples/use_clipboard/Cargo.toml b/examples/use_clipboard/Cargo.toml
index d85ad83..43cba10 100644
--- a/examples/use_clipboard/Cargo.toml
+++ b/examples/use_clipboard/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_clipboard", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_color_mode/Cargo.toml b/examples/use_color_mode/Cargo.toml
index ead2ef6..26874f0 100644
--- a/examples/use_color_mode/Cargo.toml
+++ b/examples/use_color_mode/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_color_mode", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_cookie/Cargo.toml b/examples/use_cookie/Cargo.toml
index c645550..2974155 100644
--- a/examples/use_cookie/Cargo.toml
+++ b/examples/use_cookie/Cargo.toml
@@ -9,7 +9,7 @@ codee = "0.1"
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_cookie", "docs"] }
rand = "0.8"
getrandom = { version = "0.2", features = ["js"] }
web-sys = "0.3"
diff --git a/examples/use_css_var/Cargo.toml b/examples/use_css_var/Cargo.toml
index 267c40e..97d6fa6 100644
--- a/examples/use_css_var/Cargo.toml
+++ b/examples/use_css_var/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_css_var", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_cycle_list/Cargo.toml b/examples/use_cycle_list/Cargo.toml
index c59b61c..77f6c25 100644
--- a/examples/use_cycle_list/Cargo.toml
+++ b/examples/use_cycle_list/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_cycle_list", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_debounce_fn/Cargo.toml b/examples/use_debounce_fn/Cargo.toml
index d5f4621..c3f7b9e 100644
--- a/examples/use_debounce_fn/Cargo.toml
+++ b/examples/use_debounce_fn/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_debounce_fn", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_device_orientation/Cargo.toml b/examples/use_device_orientation/Cargo.toml
index 9a4c64a..5fec238 100644
--- a/examples/use_device_orientation/Cargo.toml
+++ b/examples/use_device_orientation/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_device_orientation", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_device_pixel_ratio/Cargo.toml b/examples/use_device_pixel_ratio/Cargo.toml
index 5874c65..98efdde 100644
--- a/examples/use_device_pixel_ratio/Cargo.toml
+++ b/examples/use_device_pixel_ratio/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_device_pixel_ratio", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_display_media/Cargo.toml b/examples/use_display_media/Cargo.toml
index c7ffe6a..f5f4b79 100644
--- a/examples/use_display_media/Cargo.toml
+++ b/examples/use_display_media/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_display_media", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_document_visibility/Cargo.toml b/examples/use_document_visibility/Cargo.toml
index cb9b8f1..8d5b019 100644
--- a/examples/use_document_visibility/Cargo.toml
+++ b/examples/use_document_visibility/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_document_visibility", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_draggable/Cargo.toml b/examples/use_draggable/Cargo.toml
index e2e9a61..8427afb 100644
--- a/examples/use_draggable/Cargo.toml
+++ b/examples/use_draggable/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_draggable", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_drop_zone/Cargo.toml b/examples/use_drop_zone/Cargo.toml
index 115dc9d..aa05fca 100644
--- a/examples/use_drop_zone/Cargo.toml
+++ b/examples/use_drop_zone/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_drop_zone", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_element_bounding/Cargo.toml b/examples/use_element_bounding/Cargo.toml
index 7620bcd..8ef72c3 100644
--- a/examples/use_element_bounding/Cargo.toml
+++ b/examples/use_element_bounding/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_element_bounding", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_element_hover/Cargo.toml b/examples/use_element_hover/Cargo.toml
index 9d90e62..9be576c 100644
--- a/examples/use_element_hover/Cargo.toml
+++ b/examples/use_element_hover/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_element_hover", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_element_size/Cargo.toml b/examples/use_element_size/Cargo.toml
index daf02f0..4e71f62 100644
--- a/examples/use_element_size/Cargo.toml
+++ b/examples/use_element_size/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_element_size", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_element_visibility/Cargo.toml b/examples/use_element_visibility/Cargo.toml
index 50ba543..3506775 100644
--- a/examples/use_element_visibility/Cargo.toml
+++ b/examples/use_element_visibility/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_element_visibility", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_event_listener/Cargo.toml b/examples/use_event_listener/Cargo.toml
index 653bb76..d0e75c1 100644
--- a/examples/use_event_listener/Cargo.toml
+++ b/examples/use_event_listener/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../.." }
+leptos-use = { path = "../..", features = ["use_event_listener"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_favicon/Cargo.toml b/examples/use_favicon/Cargo.toml
index 260413a..38d4f05 100644
--- a/examples/use_favicon/Cargo.toml
+++ b/examples/use_favicon/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_favicon", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_geolocation/Cargo.toml b/examples/use_geolocation/Cargo.toml
index bb7c93e..4ded1d2 100644
--- a/examples/use_geolocation/Cargo.toml
+++ b/examples/use_geolocation/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_geolocation", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_idle/Cargo.toml b/examples/use_idle/Cargo.toml
index 1b412b4..0e8d92a 100644
--- a/examples/use_idle/Cargo.toml
+++ b/examples/use_idle/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_idle", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_infinite_scroll/Cargo.toml b/examples/use_infinite_scroll/Cargo.toml
index e55811a..52cafcb 100644
--- a/examples/use_infinite_scroll/Cargo.toml
+++ b/examples/use_infinite_scroll/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_infinite_scroll", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_intersection_observer/Cargo.toml b/examples/use_intersection_observer/Cargo.toml
index b401615..b8c5d1a 100644
--- a/examples/use_intersection_observer/Cargo.toml
+++ b/examples/use_intersection_observer/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_intersection_observer", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_interval/Cargo.toml b/examples/use_interval/Cargo.toml
index a49f0cd..cd02ec5 100644
--- a/examples/use_interval/Cargo.toml
+++ b/examples/use_interval/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_interval", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_interval_fn/Cargo.toml b/examples/use_interval_fn/Cargo.toml
index 86bc8b2..96de322 100644
--- a/examples/use_interval_fn/Cargo.toml
+++ b/examples/use_interval_fn/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs", "math"] }
+leptos-use = { path = "../..", features = ["use_interval_fn", "docs", "math"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_intl_number_format/Cargo.toml b/examples/use_intl_number_format/Cargo.toml
index 7eadd70..5ed97da 100644
--- a/examples/use_intl_number_format/Cargo.toml
+++ b/examples/use_intl_number_format/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_intl_number_format", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_locale/Cargo.toml b/examples/use_locale/Cargo.toml
new file mode 100644
index 0000000..1c592fe
--- /dev/null
+++ b/examples/use_locale/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "use_locale"
+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 = ["use_locale", "docs"] }
+unic-langid = { version = "0.9", features = ["macros"] }
+web-sys = "0.3"
+
+[dev-dependencies]
+wasm-bindgen = "0.2"
+wasm-bindgen-test = "0.3.0"
diff --git a/examples/use_locale/README.md b/examples/use_locale/README.md
new file mode 100644
index 0000000..2398484
--- /dev/null
+++ b/examples/use_locale/README.md
@@ -0,0 +1,23 @@
+A simple example for `use_locale`.
+
+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_locale/Trunk.toml b/examples/use_locale/Trunk.toml
new file mode 100644
index 0000000..3e4be08
--- /dev/null
+++ b/examples/use_locale/Trunk.toml
@@ -0,0 +1,2 @@
+[build]
+public_url = "/demo/"
\ No newline at end of file
diff --git a/examples/use_locale/index.html b/examples/use_locale/index.html
new file mode 100644
index 0000000..ae249a6
--- /dev/null
+++ b/examples/use_locale/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/examples/use_locale/input.css b/examples/use_locale/input.css
new file mode 100644
index 0000000..bd6213e
--- /dev/null
+++ b/examples/use_locale/input.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/examples/use_locale/rust-toolchain.toml b/examples/use_locale/rust-toolchain.toml
new file mode 100644
index 0000000..271800c
--- /dev/null
+++ b/examples/use_locale/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"
\ No newline at end of file
diff --git a/examples/use_locale/src/main.rs b/examples/use_locale/src/main.rs
new file mode 100644
index 0000000..a0fe2a6
--- /dev/null
+++ b/examples/use_locale/src/main.rs
@@ -0,0 +1,22 @@
+use leptos::*;
+use leptos_use::docs::demo_or_body;
+use leptos_use::use_locale;
+use unic_langid::langid_slice;
+
+#[component]
+fn Demo() -> impl IntoView {
+ let locale = use_locale(langid_slice!["en", "de", "fr"]);
+
+ view! {
+ Locale: {move || locale.get().to_string()}
+ }
+}
+
+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_locale/style/output.css b/examples/use_locale/style/output.css
new file mode 100644
index 0000000..6063b89
--- /dev/null
+++ b/examples/use_locale/style/output.css
@@ -0,0 +1,330 @@
+[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: ;
+ --tw-contain-size: ;
+ --tw-contain-layout: ;
+ --tw-contain-paint: ;
+ --tw-contain-style: ;
+}
+
+::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: ;
+ --tw-contain-size: ;
+ --tw-contain-layout: ;
+ --tw-contain-paint: ;
+ --tw-contain-style: ;
+}
+
+.static {
+ position: static;
+}
+
+.font-bold {
+ font-weight: 700;
+}
+
+.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_locale/tailwind.config.js b/examples/use_locale/tailwind.config.js
new file mode 100644
index 0000000..bc09f5e
--- /dev/null
+++ b/examples/use_locale/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_locales/Cargo.toml b/examples/use_locales/Cargo.toml
new file mode 100644
index 0000000..c98524c
--- /dev/null
+++ b/examples/use_locales/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "use_locales"
+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 = ["use_locales", "docs"] }
+web-sys = "0.3"
+
+[dev-dependencies]
+wasm-bindgen = "0.2"
+wasm-bindgen-test = "0.3.0"
diff --git a/examples/use_locales/README.md b/examples/use_locales/README.md
new file mode 100644
index 0000000..f1827be
--- /dev/null
+++ b/examples/use_locales/README.md
@@ -0,0 +1,23 @@
+A simple example for `use_locales`.
+
+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_locales/Trunk.toml b/examples/use_locales/Trunk.toml
new file mode 100644
index 0000000..3e4be08
--- /dev/null
+++ b/examples/use_locales/Trunk.toml
@@ -0,0 +1,2 @@
+[build]
+public_url = "/demo/"
\ No newline at end of file
diff --git a/examples/use_locales/index.html b/examples/use_locales/index.html
new file mode 100644
index 0000000..ae249a6
--- /dev/null
+++ b/examples/use_locales/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/examples/use_locales/input.css b/examples/use_locales/input.css
new file mode 100644
index 0000000..bd6213e
--- /dev/null
+++ b/examples/use_locales/input.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/examples/use_locales/rust-toolchain.toml b/examples/use_locales/rust-toolchain.toml
new file mode 100644
index 0000000..271800c
--- /dev/null
+++ b/examples/use_locales/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"
\ No newline at end of file
diff --git a/examples/use_locales/src/main.rs b/examples/use_locales/src/main.rs
new file mode 100644
index 0000000..dfa206b
--- /dev/null
+++ b/examples/use_locales/src/main.rs
@@ -0,0 +1,21 @@
+use leptos::*;
+use leptos_use::docs::demo_or_body;
+use leptos_use::use_locales;
+
+#[component]
+fn Demo() -> impl IntoView {
+ let locales = use_locales();
+
+ view! {
+ {move || format!("Locales:\n {}", locales().join("\n "))}
+ }
+}
+
+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_locales/style/output.css b/examples/use_locales/style/output.css
new file mode 100644
index 0000000..ab5191f
--- /dev/null
+++ b/examples/use_locales/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_locales/tailwind.config.js b/examples/use_locales/tailwind.config.js
new file mode 100644
index 0000000..bc09f5e
--- /dev/null
+++ b/examples/use_locales/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_media_query/Cargo.toml b/examples/use_media_query/Cargo.toml
index 345aa87..a57d7fe 100644
--- a/examples/use_media_query/Cargo.toml
+++ b/examples/use_media_query/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_media_query", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_mouse/Cargo.toml b/examples/use_mouse/Cargo.toml
index 1c91a04..cb2dea3 100644
--- a/examples/use_mouse/Cargo.toml
+++ b/examples/use_mouse/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_mouse", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_mouse_in_element/Cargo.toml b/examples/use_mouse_in_element/Cargo.toml
index ac23964..2823591 100644
--- a/examples/use_mouse_in_element/Cargo.toml
+++ b/examples/use_mouse_in_element/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_mouse_in_element", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_mutation_observer/Cargo.toml b/examples/use_mutation_observer/Cargo.toml
index fcf12fd..c85533a 100644
--- a/examples/use_mutation_observer/Cargo.toml
+++ b/examples/use_mutation_observer/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_mutation_observer", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_permission/Cargo.toml b/examples/use_permission/Cargo.toml
index 17f2388..f03e633 100644
--- a/examples/use_permission/Cargo.toml
+++ b/examples/use_permission/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_permission", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_prefers_reduced_motion/Cargo.toml b/examples/use_prefers_reduced_motion/Cargo.toml
new file mode 100644
index 0000000..3cdeb8d
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "use_prefers_reduced_motion"
+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 = ["use_prefers_reduced_motion", "docs"] }
+web-sys = "0.3"
+
+[dev-dependencies]
+wasm-bindgen = "0.2"
+wasm-bindgen-test = "0.3.0"
diff --git a/examples/use_prefers_reduced_motion/README.md b/examples/use_prefers_reduced_motion/README.md
new file mode 100644
index 0000000..95c5eea
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/README.md
@@ -0,0 +1,23 @@
+A simple example for `use_prefers_reduced_motion`.
+
+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_prefers_reduced_motion/Trunk.toml b/examples/use_prefers_reduced_motion/Trunk.toml
new file mode 100644
index 0000000..3e4be08
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/Trunk.toml
@@ -0,0 +1,2 @@
+[build]
+public_url = "/demo/"
\ No newline at end of file
diff --git a/examples/use_prefers_reduced_motion/index.html b/examples/use_prefers_reduced_motion/index.html
new file mode 100644
index 0000000..ae249a6
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/examples/use_prefers_reduced_motion/input.css b/examples/use_prefers_reduced_motion/input.css
new file mode 100644
index 0000000..b5c61c9
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/input.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/examples/use_prefers_reduced_motion/rust-toolchain.toml b/examples/use_prefers_reduced_motion/rust-toolchain.toml
new file mode 100644
index 0000000..271800c
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"
\ No newline at end of file
diff --git a/examples/use_prefers_reduced_motion/src/main.rs b/examples/use_prefers_reduced_motion/src/main.rs
new file mode 100644
index 0000000..2a6adfc
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/src/main.rs
@@ -0,0 +1,29 @@
+use leptos::*;
+use leptos_use::docs::{demo_or_body, BooleanDisplay};
+use leptos_use::use_prefers_reduced_motion;
+
+#[component]
+fn Demo() -> impl IntoView {
+ let is_reduced_motion_preferred = use_prefers_reduced_motion();
+
+ 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_prefers_reduced_motion/style/output.css b/examples/use_prefers_reduced_motion/style/output.css
new file mode 100644
index 0000000..7db8e42
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/style/output.css
@@ -0,0 +1,326 @@
+[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: ;
+ --tw-contain-size: ;
+ --tw-contain-layout: ;
+ --tw-contain-paint: ;
+ --tw-contain-style: ;
+}
+
+::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: ;
+ --tw-contain-size: ;
+ --tw-contain-layout: ;
+ --tw-contain-paint: ;
+ --tw-contain-style: ;
+}
+
+.static {
+ position: static;
+}
+
+.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));
+ }
+}
diff --git a/examples/use_prefers_reduced_motion/tailwind.config.js b/examples/use_prefers_reduced_motion/tailwind.config.js
new file mode 100644
index 0000000..bc09f5e
--- /dev/null
+++ b/examples/use_prefers_reduced_motion/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_raf_fn/Cargo.toml b/examples/use_raf_fn/Cargo.toml
index 48b8cd9..e9c4c57 100644
--- a/examples/use_raf_fn/Cargo.toml
+++ b/examples/use_raf_fn/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_raf_fn", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_resize_observer/Cargo.toml b/examples/use_resize_observer/Cargo.toml
index 0ce7f4c..8ba4ed2 100644
--- a/examples/use_resize_observer/Cargo.toml
+++ b/examples/use_resize_observer/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_resize_observer", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_scroll/Cargo.toml b/examples/use_scroll/Cargo.toml
index ccb133a..25874cd 100644
--- a/examples/use_scroll/Cargo.toml
+++ b/examples/use_scroll/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_scroll", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_service_worker/Cargo.toml b/examples/use_service_worker/Cargo.toml
index 8e3b8c7..f074dd0 100644
--- a/examples/use_service_worker/Cargo.toml
+++ b/examples/use_service_worker/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_service_worker", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_sorted/Cargo.toml b/examples/use_sorted/Cargo.toml
index 05462ac..8ff335f 100644
--- a/examples/use_sorted/Cargo.toml
+++ b/examples/use_sorted/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_sorted", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_storage/Cargo.toml b/examples/use_storage/Cargo.toml
index 5174789..b8b2285 100644
--- a/examples/use_storage/Cargo.toml
+++ b/examples/use_storage/Cargo.toml
@@ -8,7 +8,7 @@ codee = { version = "0.1", features = ["json_serde"] }
console_error_panic_hook = "0.1"
console_log = "1"
leptos = { version = "0.6", features = ["nightly", "csr"] }
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["storage", "docs"] }
log = "0.4"
serde = "1.0.163"
web-sys = "0.3"
diff --git a/examples/use_throttle_fn/Cargo.toml b/examples/use_throttle_fn/Cargo.toml
index 04d6006..d39fc8d 100644
--- a/examples/use_throttle_fn/Cargo.toml
+++ b/examples/use_throttle_fn/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_throttle_fn", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_timeout_fn/Cargo.toml b/examples/use_timeout_fn/Cargo.toml
index 9b0cfa4..608afac 100644
--- a/examples/use_timeout_fn/Cargo.toml
+++ b/examples/use_timeout_fn/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_timeout_fn", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_timestamp/Cargo.toml b/examples/use_timestamp/Cargo.toml
index a065eb4..5e0a16f 100644
--- a/examples/use_timestamp/Cargo.toml
+++ b/examples/use_timestamp/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_timestamp", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_user_media/Cargo.toml b/examples/use_user_media/Cargo.toml
index ffd38b8..170c4af 100644
--- a/examples/use_user_media/Cargo.toml
+++ b/examples/use_user_media/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_user_media", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_web_notification/Cargo.toml b/examples/use_web_notification/Cargo.toml
index 216f31e..f499927 100644
--- a/examples/use_web_notification/Cargo.toml
+++ b/examples/use_web_notification/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_web_notification", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_web_notification/src/main.rs b/examples/use_web_notification/src/main.rs
index cf2f310..241ce18 100644
--- a/examples/use_web_notification/src/main.rs
+++ b/examples/use_web_notification/src/main.rs
@@ -14,7 +14,7 @@ fn Demo() -> impl IntoView {
.title("Hello World from leptos-use")
.direction(NotificationDirection::Auto)
.language("en")
- // .renotify(true)
+ .renotify(true)
.tag("test"),
);
diff --git a/examples/use_websocket/Cargo.toml b/examples/use_websocket/Cargo.toml
index 2eb5095..26ed9a6 100644
--- a/examples/use_websocket/Cargo.toml
+++ b/examples/use_websocket/Cargo.toml
@@ -9,7 +9,7 @@ codee = { version = "0.1", features = ["msgpack_serde"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_websocket", "docs"] }
serde = { version = "1", features = ["derive"] }
web-sys = "0.3"
diff --git a/examples/use_webtransport/Cargo.toml b/examples/use_webtransport/Cargo.toml
index 5291647..680dc93 100644
--- a/examples/use_webtransport/Cargo.toml
+++ b/examples/use_webtransport/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_web_notification", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_window_focus/Cargo.toml b/examples/use_window_focus/Cargo.toml
index d157e75..ff9224a 100644
--- a/examples/use_window_focus/Cargo.toml
+++ b/examples/use_window_focus/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_window_focus", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/use_window_scroll/Cargo.toml b/examples/use_window_scroll/Cargo.toml
index d8a1c48..43092e2 100644
--- a/examples/use_window_scroll/Cargo.toml
+++ b/examples/use_window_scroll/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["use_window_scroll", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/watch_debounced/Cargo.toml b/examples/watch_debounced/Cargo.toml
index 8af3c77..b7d1872 100644
--- a/examples/watch_debounced/Cargo.toml
+++ b/examples/watch_debounced/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["watch_debounced", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/watch_pausable/Cargo.toml b/examples/watch_pausable/Cargo.toml
index a14897d..548f01a 100644
--- a/examples/watch_pausable/Cargo.toml
+++ b/examples/watch_pausable/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["watch_pausable", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/examples/watch_throttled/Cargo.toml b/examples/watch_throttled/Cargo.toml
index e711c4f..d69a67a 100644
--- a/examples/watch_throttled/Cargo.toml
+++ b/examples/watch_throttled/Cargo.toml
@@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
-leptos-use = { path = "../..", features = ["docs"] }
+leptos-use = { path = "../..", features = ["watch_throttled", "docs"] }
web-sys = "0.3"
[dev-dependencies]
diff --git a/src/core/mod.rs b/src/core/mod.rs
index 3fe5ca3..0b51baa 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -1,26 +1,32 @@
mod connection_ready_state;
mod datetime;
mod direction;
+#[cfg(feature = "element")]
mod element_maybe_signal;
+#[cfg(feature = "element")]
mod elements_maybe_signal;
mod maybe_rw_signal;
mod pointer_type;
mod position;
+mod reconnect_limit;
mod size;
mod ssr_safe_method;
-mod storage;
+#[cfg(feature = "use_color_mode")]
pub(crate) mod url;
mod use_rw_signal;
pub use connection_ready_state::*;
pub(crate) use datetime::*;
pub use direction::*;
+#[cfg(feature = "element")]
pub use element_maybe_signal::*;
+#[cfg(feature = "element")]
pub use elements_maybe_signal::*;
pub use maybe_rw_signal::*;
pub use pointer_type::*;
pub use position::*;
+pub use reconnect_limit::*;
pub use size::*;
+#[allow(unused_imports)]
pub(crate) use ssr_safe_method::*;
-pub use storage::*;
pub use use_rw_signal::*;
diff --git a/src/core/reconnect_limit.rs b/src/core/reconnect_limit.rs
new file mode 100644
index 0000000..5d28b2c
--- /dev/null
+++ b/src/core/reconnect_limit.rs
@@ -0,0 +1,20 @@
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum ReconnectLimit {
+ Infinite,
+ Limited(u64),
+}
+
+impl Default for ReconnectLimit {
+ fn default() -> Self {
+ ReconnectLimit::Limited(3)
+ }
+}
+
+impl ReconnectLimit {
+ pub fn is_exceeded_by(self, times: u64) -> bool {
+ match self {
+ ReconnectLimit::Infinite => false,
+ ReconnectLimit::Limited(limit) => times >= limit,
+ }
+ }
+}
diff --git a/src/core/ssr_safe_method.rs b/src/core/ssr_safe_method.rs
index 9ffa513..da46452 100644
--- a/src/core/ssr_safe_method.rs
+++ b/src/core/ssr_safe_method.rs
@@ -1,3 +1,5 @@
+#![allow(unused_macros, unused_imports)]
+
macro_rules! impl_ssr_safe_method {
(
$(#[$attr:meta])*
diff --git a/src/core/storage.rs b/src/core/storage.rs
deleted file mode 100644
index e78e3f9..0000000
--- a/src/core/storage.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use leptos::prelude::*;
-use wasm_bindgen::JsValue;
-
-/// Local or session storage or a custom store that is a `web_sys::Storage`.
-#[derive(Default)]
-pub enum StorageType {
- #[default]
- Local,
- Session,
- Custom(web_sys::Storage),
-}
-
-impl StorageType {
- pub fn into_storage(self) -> Result, JsValue> {
- match self {
- StorageType::Local => window().local_storage(),
- StorageType::Session => window().session_storage(),
- StorageType::Custom(storage) => Ok(Some(storage)),
- }
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 6e85934..a8c46b1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,146 +7,291 @@ pub mod core;
pub mod docs;
#[cfg(feature = "math")]
pub mod math;
+#[cfg(feature = "storage")]
pub mod storage;
pub mod utils;
+pub use core::ReconnectLimit;
+
// #[cfg(web_sys_unstable_apis)]
// mod use_webtransport;
// #[cfg(web_sys_unstable_apis)]
// pub use use_webtransport::*;
-#[cfg(web_sys_unstable_apis)]
-mod use_clipboard;
-#[cfg(web_sys_unstable_apis)]
-pub use use_clipboard::*;
-
+#[cfg(feature = "is_err")]
mod is_err;
+#[cfg(feature = "is_none")]
mod is_none;
+#[cfg(feature = "is_ok")]
mod is_ok;
+#[cfg(feature = "is_some")]
mod is_some;
+#[cfg(feature = "on_click_outside")]
mod on_click_outside;
+#[cfg(feature = "signal_debounced")]
mod signal_debounced;
+#[cfg(feature = "signal_throttled")]
mod signal_throttled;
+#[cfg(feature = "sync_signal")]
mod sync_signal;
+#[cfg(feature = "use_active_element")]
// mod use_active_element;
+#[cfg(feature = "use_breakpoints")]
mod use_breakpoints;
+#[cfg(feature = "use_broadcast_channel")]
mod use_broadcast_channel;
+#[cfg(feature = "use_clipboard")]
+mod use_clipboard;
+#[cfg(feature = "use_color_mode")]
mod use_color_mode;
+#[cfg(feature = "use_cookie")]
mod use_cookie;
+#[cfg(feature = "use_css_var")]
mod use_css_var;
+#[cfg(feature = "use_cycle_list")]
mod use_cycle_list;
+#[cfg(feature = "use_debounce_fn")]
mod use_debounce_fn;
+#[cfg(feature = "use_device_orientation")]
mod use_device_orientation;
+#[cfg(feature = "use_device_pixel_ratio")]
mod use_device_pixel_ratio;
+#[cfg(feature = "use_display_media")]
mod use_display_media;
+#[cfg(feature = "use_document")]
mod use_document;
+#[cfg(feature = "use_document_visibility")]
mod use_document_visibility;
+#[cfg(feature = "use_draggable")]
mod use_draggable;
+#[cfg(feature = "use_drop_zone")]
mod use_drop_zone;
+#[cfg(feature = "use_element_bounding")]
mod use_element_bounding;
+#[cfg(feature = "use_element_hover")]
mod use_element_hover;
+#[cfg(feature = "use_element_size")]
mod use_element_size;
+#[cfg(feature = "use_element_visibility")]
mod use_element_visibility;
+#[cfg(feature = "use_event_listener")]
mod use_event_listener;
+#[cfg(feature = "use_event_source")]
mod use_event_source;
+#[cfg(feature = "use_favicon")]
mod use_favicon;
+#[cfg(feature = "use_geolocation")]
mod use_geolocation;
+#[cfg(feature = "use_idle")]
mod use_idle;
+#[cfg(feature = "use_infinite_scroll")]
mod use_infinite_scroll;
+#[cfg(feature = "use_intersection_observer")]
mod use_intersection_observer;
+#[cfg(feature = "use_interval")]
mod use_interval;
+#[cfg(feature = "use_interval_fn")]
mod use_interval_fn;
+#[cfg(feature = "use_intl_number_format")]
mod use_intl_number_format;
+#[cfg(feature = "use_locale")]
+mod use_locale;
+#[cfg(feature = "use_locales")]
+mod use_locales;
+#[cfg(feature = "use_media_query")]
mod use_media_query;
+#[cfg(feature = "use_mouse")]
mod use_mouse;
+#[cfg(feature = "use_mouse_in_element")]
mod use_mouse_in_element;
+#[cfg(feature = "use_mutation_observer")]
mod use_mutation_observer;
+#[cfg(feature = "use_permission")]
mod use_permission;
+#[cfg(feature = "use_preferred_contrast")]
mod use_preferred_contrast;
+#[cfg(feature = "use_preferred_dark")]
mod use_preferred_dark;
+#[cfg(feature = "use_prefers_reduced_motion")]
+mod use_prefers_reduced_motion;
+#[cfg(feature = "use_raf_fn")]
mod use_raf_fn;
+#[cfg(feature = "use_resize_observer")]
mod use_resize_observer;
+#[cfg(feature = "use_scroll")]
mod use_scroll;
+#[cfg(feature = "use_service_worker")]
mod use_service_worker;
+#[cfg(feature = "use_sorted")]
mod use_sorted;
+#[cfg(feature = "use_supported")]
mod use_supported;
+#[cfg(feature = "use_throttle_fn")]
mod use_throttle_fn;
+#[cfg(feature = "use_timeout_fn")]
mod use_timeout_fn;
+#[cfg(feature = "use_timestamp")]
mod use_timestamp;
+#[cfg(feature = "use_to_string")]
mod use_to_string;
+#[cfg(feature = "use_user_media")]
mod use_user_media;
+#[cfg(feature = "use_web_notification")]
mod use_web_notification;
+#[cfg(feature = "use_websocket")]
mod use_websocket;
+#[cfg(feature = "use_window")]
mod use_window;
+#[cfg(feature = "use_window_focus")]
mod use_window_focus;
+#[cfg(feature = "use_window_scroll")]
mod use_window_scroll;
+#[cfg(feature = "watch_debounced")]
mod watch_debounced;
+#[cfg(feature = "watch_pausable")]
mod watch_pausable;
+#[cfg(feature = "watch_throttled")]
mod watch_throttled;
+#[cfg(feature = "watch_with_options")]
mod watch_with_options;
+#[cfg(feature = "whenever")]
mod whenever;
+#[cfg(feature = "is_err")]
pub use is_err::*;
+#[cfg(feature = "is_none")]
pub use is_none::*;
+#[cfg(feature = "is_ok")]
pub use is_ok::*;
+#[cfg(feature = "is_some")]
pub use is_some::*;
+#[cfg(feature = "on_click_outside")]
pub use on_click_outside::*;
+#[cfg(feature = "signal_debounced")]
pub use signal_debounced::*;
+#[cfg(feature = "signal_throttled")]
pub use signal_throttled::*;
+#[cfg(feature = "sync_signal")]
pub use sync_signal::*;
+#[cfg(feature = "use_active_element")]
// pub use use_active_element::*;
+#[cfg(feature = "use_breakpoints")]
pub use use_breakpoints::*;
+#[cfg(feature = "use_broadcast_channel")]
pub use use_broadcast_channel::*;
+#[cfg(feature = "use_clipboard")]
+pub use use_clipboard::*;
+#[cfg(feature = "use_color_mode")]
pub use use_color_mode::*;
+#[cfg(feature = "use_cookie")]
pub use use_cookie::*;
+#[cfg(feature = "use_css_var")]
pub use use_css_var::*;
+#[cfg(feature = "use_cycle_list")]
pub use use_cycle_list::*;
+#[cfg(feature = "use_debounce_fn")]
pub use use_debounce_fn::*;
+#[cfg(feature = "use_device_orientation")]
pub use use_device_orientation::*;
+#[cfg(feature = "use_device_pixel_ratio")]
pub use use_device_pixel_ratio::*;
+#[cfg(feature = "use_display_media")]
pub use use_display_media::*;
+#[cfg(feature = "use_document")]
pub use use_document::*;
+#[cfg(feature = "use_document_visibility")]
pub use use_document_visibility::*;
+#[cfg(feature = "use_draggable")]
pub use use_draggable::*;
+#[cfg(feature = "use_drop_zone")]
pub use use_drop_zone::*;
+#[cfg(feature = "use_element_bounding")]
pub use use_element_bounding::*;
+#[cfg(feature = "use_element_hover")]
pub use use_element_hover::*;
+#[cfg(feature = "use_element_size")]
pub use use_element_size::*;
+#[cfg(feature = "use_element_visibility")]
pub use use_element_visibility::*;
+#[cfg(feature = "use_event_listener")]
pub use use_event_listener::*;
+#[cfg(feature = "use_event_source")]
pub use use_event_source::*;
+#[cfg(feature = "use_favicon")]
pub use use_favicon::*;
+#[cfg(feature = "use_geolocation")]
pub use use_geolocation::*;
+#[cfg(feature = "use_idle")]
pub use use_idle::*;
+#[cfg(feature = "use_infinite_scroll")]
pub use use_infinite_scroll::*;
+#[cfg(feature = "use_intersection_observer")]
pub use use_intersection_observer::*;
+#[cfg(feature = "use_interval")]
pub use use_interval::*;
+#[cfg(feature = "use_interval_fn")]
pub use use_interval_fn::*;
+#[cfg(feature = "use_intl_number_format")]
pub use use_intl_number_format::*;
+#[cfg(feature = "use_locale")]
+pub use use_locale::*;
+#[cfg(feature = "use_locales")]
+pub use use_locales::*;
+#[cfg(feature = "use_media_query")]
pub use use_media_query::*;
+#[cfg(feature = "use_mouse")]
pub use use_mouse::*;
+#[cfg(feature = "use_mouse_in_element")]
pub use use_mouse_in_element::*;
+#[cfg(feature = "use_mutation_observer")]
pub use use_mutation_observer::*;
+#[cfg(feature = "use_permission")]
pub use use_permission::*;
+#[cfg(feature = "use_preferred_contrast")]
pub use use_preferred_contrast::*;
+#[cfg(feature = "use_preferred_dark")]
pub use use_preferred_dark::*;
+#[cfg(feature = "use_prefers_reduced_motion")]
+pub use use_prefers_reduced_motion::*;
+#[cfg(feature = "use_raf_fn")]
pub use use_raf_fn::*;
+#[cfg(feature = "use_resize_observer")]
pub use use_resize_observer::*;
+#[cfg(feature = "use_scroll")]
pub use use_scroll::*;
+#[cfg(feature = "use_service_worker")]
pub use use_service_worker::*;
+#[cfg(feature = "use_sorted")]
pub use use_sorted::*;
+#[cfg(feature = "use_supported")]
pub use use_supported::*;
+#[cfg(feature = "use_throttle_fn")]
pub use use_throttle_fn::*;
+#[cfg(feature = "use_timeout_fn")]
pub use use_timeout_fn::*;
+#[cfg(feature = "use_timestamp")]
pub use use_timestamp::*;
+#[cfg(feature = "use_to_string")]
pub use use_to_string::*;
+#[cfg(feature = "use_user_media")]
+pub use use_user_media::*;
+#[cfg(feature = "use_web_notification")]
pub use use_web_notification::*;
+#[cfg(feature = "use_websocket")]
pub use use_websocket::*;
+#[cfg(feature = "use_window")]
pub use use_window::*;
+#[cfg(feature = "use_window_focus")]
pub use use_window_focus::*;
+#[cfg(feature = "use_window_scroll")]
pub use use_window_scroll::*;
+#[cfg(feature = "watch_debounced")]
pub use watch_debounced::*;
+#[cfg(feature = "watch_pausable")]
pub use watch_pausable::*;
+#[cfg(feature = "watch_throttled")]
pub use watch_throttled::*;
+#[cfg(feature = "watch_with_options")]
pub use watch_with_options::*;
+#[cfg(feature = "whenever")]
pub use whenever::*;
diff --git a/src/storage/mod.rs b/src/storage/mod.rs
index 54f79db..5930028 100644
--- a/src/storage/mod.rs
+++ b/src/storage/mod.rs
@@ -2,7 +2,28 @@ mod use_local_storage;
mod use_session_storage;
mod use_storage;
-pub use crate::core::StorageType;
pub use use_local_storage::*;
pub use use_session_storage::*;
pub use use_storage::*;
+
+use leptos::window;
+use wasm_bindgen::JsValue;
+
+/// Local or session storage or a custom store that is a `web_sys::Storage`.
+#[derive(Default)]
+pub enum StorageType {
+ #[default]
+ Local,
+ Session,
+ Custom(web_sys::Storage),
+}
+
+impl StorageType {
+ pub fn into_storage(self) -> Result , JsValue> {
+ match self {
+ StorageType::Local => window().local_storage(),
+ StorageType::Session => window().session_storage(),
+ StorageType::Custom(storage) => Ok(Some(storage)),
+ }
+ }
+}
diff --git a/src/storage/use_storage.rs b/src/storage/use_storage.rs
index a542494..58378c0 100644
--- a/src/storage/use_storage.rs
+++ b/src/storage/use_storage.rs
@@ -1,7 +1,4 @@
-use crate::{
- core::{MaybeRwSignal, StorageType},
- utils::FilterOptions,
-};
+use crate::{core::MaybeRwSignal, storage::StorageType, utils::FilterOptions};
use codee::{CodecError, Decoder, Encoder};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::wrappers::read::Signal;
@@ -217,8 +214,8 @@ where
queue_microtask(move || {
// TODO : better to use a BroadcastChannel (use_broadcast_channel)?
// Note: we cannot construct a full StorageEvent so we _must_ rely on a custom event
- let mut custom = web_sys::CustomEventInit::new();
- custom.detail(&JsValue::from_str(&key));
+ let custom = web_sys::CustomEventInit::new();
+ custom.set_detail(&JsValue::from_str(&key));
let result = window()
.dispatch_event(
&web_sys::CustomEvent::new_with_event_init_dict(
diff --git a/src/sync_signal.rs b/src/sync_signal.rs
index c22c0ee..0984308 100644
--- a/src/sync_signal.rs
+++ b/src/sync_signal.rs
@@ -168,6 +168,8 @@ where
direction,
transform_ltr,
transform_rtl,
+ assign_ltr,
+ assign_rtl,
} = options;
let left = left.into();
@@ -183,7 +185,7 @@ where
let new_value = (*transform_ltr)(new_value);
if right.with_untracked(|right| right != &new_value) {
- right.update(|right| *right = new_value);
+ right.update(|right| assign_ltr(right, new_value));
}
},
immediate,
@@ -197,7 +199,7 @@ where
let new_value = (*transform_rtl)(new_value);
if left.with_untracked(|left| left != &new_value) {
- left.update(|left| *left = new_value);
+ left.update(|left| assign_rtl(left, new_value));
}
},
immediate,
@@ -221,6 +223,8 @@ pub enum SyncDirection {
Both,
}
+pub type AssignFn = Rc;
+
/// Options for [`sync_signal_with_options`].
#[derive(DefaultBuilder)]
pub struct SyncSignalOptions {
@@ -241,6 +245,16 @@ pub struct SyncSignalOptions {
/// Defaults to identity.
#[builder(skip)]
transform_rtl: Rc L>,
+
+ /// Assigns the left signal to the right signal.
+ /// Defaults to `*r = l`.
+ #[builder(skip)]
+ assign_ltr: AssignFn,
+
+ /// Assigns the right signal to the left signal.
+ /// Defaults to `*l = r`.
+ #[builder(skip)]
+ assign_rtl: AssignFn,
}
impl SyncSignalOptions {
@@ -262,6 +276,23 @@ impl SyncSignalOptions {
}
}
+ /// Assigns the left signal to the right signal.
+ /// Defaults to `*r = l`.
+ pub fn assign_ltr(self, assign_ltr: impl Fn(&mut R, R) + 'static) -> Self {
+ Self {
+ assign_ltr: Rc::new(assign_ltr),
+ ..self
+ }
+ }
+
+ /// Assigns the right signal to the left signal.
+ /// Defaults to `*l = r`.
+ pub fn assign_rtl(self, assign_rtl: impl Fn(&mut L, L) + 'static) -> Self {
+ Self {
+ assign_rtl: Rc::new(assign_rtl),
+ ..self
+ }
+ }
/// Initializes options with transforms
pub fn with_transforms(
transform_ltr: impl Fn(&L) -> R + 'static,
@@ -272,6 +303,8 @@ impl SyncSignalOptions {
direction: SyncDirection::Both,
transform_ltr: Rc::new(transform_ltr),
transform_rtl: Rc::new(transform_rtl),
+ assign_ltr: Rc::new(|right, left| *right = left),
+ assign_rtl: Rc::new(|left, right| *left = right),
}
}
}
@@ -287,6 +320,8 @@ where
direction: SyncDirection::Both,
transform_ltr: Rc::new(|x| x.clone().into()),
transform_rtl: Rc::new(|x| x.clone().into()),
+ assign_ltr: Rc::new(|right, left| *right = left),
+ assign_rtl: Rc::new(|left, right| *left = right),
}
}
}
diff --git a/src/use_clipboard.rs b/src/use_clipboard.rs
index 56cf77a..3469391 100644
--- a/src/use_clipboard.rs
+++ b/src/use_clipboard.rs
@@ -11,9 +11,6 @@ use leptos::prelude::*;
/// [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API).
/// Without user permission, reading or altering the clipboard contents is not permitted.
///
-/// > This function requires `--cfg=web_sys_unstable_apis` to be activated as
-/// > [described in the wasm-bindgen guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html).
-///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_clipboard)
@@ -80,10 +77,9 @@ pub fn use_clipboard_with_options(
let update_text = move |_| {
if is_supported.get() {
leptos::spawn::spawn_local(async move {
- if let Some(clipboard) = window().navigator().clipboard() {
- if let Ok(text) = js_fut!(clipboard.read_text()).await {
- set_text.set(text.as_string());
- }
+ let clipboard = window().navigator().clipboard();
+ if let Ok(text) = js_fut!(clipboard.read_text()).await {
+ set_text.set(text.as_string());
}
})
}
@@ -103,12 +99,11 @@ pub fn use_clipboard_with_options(
let value = value.to_owned();
leptos::spawn::spawn_local(async move {
- if let Some(clipboard) = window().navigator().clipboard() {
- if js_fut!(clipboard.write_text(&value)).await.is_ok() {
- set_text.set(Some(value));
- set_copied.set(true);
- start(());
- }
+ let clipboard = window().navigator().clipboard();
+ if js_fut!(clipboard.write_text(&value)).await.is_ok() {
+ set_text.set(Some(value));
+ set_copied.set(true);
+ start(());
}
});
}
diff --git a/src/use_color_mode.rs b/src/use_color_mode.rs
index 04bf944..8d32466 100644
--- a/src/use_color_mode.rs
+++ b/src/use_color_mode.rs
@@ -1,8 +1,11 @@
use crate::core::url;
-use crate::core::StorageType;
use crate::core::{ElementMaybeSignal, MaybeRwSignal};
-use crate::storage::{use_storage_with_options, UseStorageOptions};
-use crate::{sync_signal_with_options, use_cookie, use_preferred_dark, SyncSignalOptions};
+use crate::storage::{use_storage_with_options, StorageType, UseStorageOptions};
+use crate::utils::get_header;
+use crate::{
+ sync_signal_with_options, use_cookie, use_preferred_dark_with_options, SyncSignalOptions,
+ UsePreferredDarkOptions,
+};
use codee::string::FromToStringCodec;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::wrappers::read::Signal;
@@ -84,7 +87,7 @@ use wasm_bindgen::JsCast;
/// # }
/// ```
///
-/// ### Cookies
+/// ### Cookie
///
/// To persist color mode in a cookie, use `use_cookie_with_options` and specify `.cookie_enabled(true)`.
///
@@ -113,9 +116,24 @@ use wasm_bindgen::JsCast;
///
/// ## Server-Side Rendering
///
-/// On the server this will by default return `ColorMode::Light`. Persistence with storage is disabled.
+/// On the server this will try to read the
+/// [`Sec-CH-Prefers-Color-Scheme` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
+/// to determine the color mode. If the header is not present it will return `ColorMode::Light`.
+/// Please have a look at the linked documentation above for that header to see browser support
+/// as well as potential server requirements.
///
-/// If `cookie_enabled` is set to `true`, cookies will be used and if present this value will be used
+/// > 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"`, for `spin` enable `"spin"`.
+///
+/// ### Bring your own header
+///
+/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
+/// you can provide your own way of reading the color scheme header value using the option
+/// [`crate::UseColorModeOptions::ssr_color_header_getter`].
+///
+/// ### Cookie
+///
+/// If `cookie_enabled` is set to `true`, a cookie will be used and if present this value will be used
/// on the server as well as on the client. Please note that you have to add the `axum` or `actix`
/// feature as described in [`fn@crate::use_cookie`].
///
@@ -152,6 +170,7 @@ where
emit_auto,
transition_enabled,
listen_to_storage_changes,
+ ssr_color_header_getter,
_marker,
} = options;
@@ -163,7 +182,9 @@ where
])
.collect();
- let preferred_dark = use_preferred_dark();
+ let preferred_dark = use_preferred_dark_with_options(UsePreferredDarkOptions {
+ ssr_color_header_getter,
+ });
let system = Signal::derive(move || {
if preferred_dark.get() {
@@ -472,6 +493,14 @@ where
/// Defaults to true.
listen_to_storage_changes: bool,
+ /// Getter function to return the string value of the
+ /// [`Sec-CH-Prefers-Color-Scheme`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
+ /// header.
+ /// When you use one of the features `"axum"`, `"actix"` or `"spin"` there's a valid default
+ /// implementation provided.
+ #[allow(dead_code)]
+ ssr_color_header_getter: Rc Option>,
+
#[builder(skip)]
_marker: PhantomData,
}
@@ -497,6 +526,13 @@ impl Default for UseColorModeOptions<&'static str, web_sys::Element> {
emit_auto: false,
transition_enabled: false,
listen_to_storage_changes: true,
+ ssr_color_header_getter: Rc::new(move || {
+ get_header!(
+ HeaderName::from_static("sec-ch-prefers-color-scheme"),
+ use_locale,
+ ssr_color_header_getter
+ )
+ }),
_marker: PhantomData,
}
}
diff --git a/src/use_cookie.rs b/src/use_cookie.rs
index ca33237..ff46a2c 100644
--- a/src/use_cookie.rs
+++ b/src/use_cookie.rs
@@ -1,6 +1,7 @@
#![allow(clippy::too_many_arguments)]
use crate::core::now;
+use crate::utils::get_header;
use codee::{CodecError, Decoder, Encoder};
use cookie::time::{Duration, OffsetDateTime};
pub use cookie::SameSite;
@@ -99,7 +100,7 @@ use std::sync::Arc;
///
/// ### Bring your own header
///
-/// In case you're neither using Axum nor Actix, or the default implementation is not to your liking,
+/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
/// you can provide your own way of reading and writing the cookie header value.
///
/// ```
@@ -494,79 +495,8 @@ impl Default for UseCookieOptions {
domain: None,
path: None,
same_site: None,
- ssr_cookies_header_getter: Arc::new(move || {
- #[cfg(feature = "ssr")]
- {
- #[cfg(all(feature = "actix", feature = "axum"))]
- compile_error!("You can only enable one of features \"actix\" and \"axum\" at the same time");
-
- #[cfg(all(feature = "actix", feature = "spin"))]
- compile_error!("You can only enable one of features \"actix\" and \"spin\" at the same time");
-
- #[cfg(all(feature = "axum", feature = "spin"))]
- compile_error!("You can only enable one of features \"axum\" and \"spin\" at the same time");
-
- #[cfg(feature = "actix")]
- const COOKIE: http0_2::HeaderName = http0_2::header::COOKIE;
- #[cfg(any(feature = "axum", feature = "spin"))]
- 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", feature = "spin"))]
- let headers;
- #[cfg(feature = "actix")]
- {
- headers = use_context::()
- .map(|req| req.headers().clone());
- }
- #[cfg(feature = "axum")]
- {
- headers = use_context::().map(|parts| parts.headers);
- }
- #[cfg(feature = "spin")]
- {
- headers = use_context::()
- .map(|parts| parts.headers().clone());
- }
-
- #[cfg(all(
- not(feature = "axum"),
- not(feature = "actix"),
- not(feature = "spin")
- ))]
- {
- warn!("If you're using use_cookie without the feature `axum`, `actix` or `spin` enabled, you should provide the option `ssr_cookies_header_getter`");
- None
- }
-
- #[cfg(any(feature = "axum", feature = "actix"))]
- {
- headers.map(|headers| {
- headers
- .get(COOKIE)
- .cloned()
- .unwrap_or_else(|| HeaderValue::from_static(""))
- .to_str()
- .unwrap_or_default()
- .to_owned()
- })
- }
- #[cfg(feature = "spin")]
- {
- headers.and_then(|headers| {
- headers
- .iter()
- .find(|(key, _)| **key == COOKIE)
- .and_then(|(_, value)| String::from_utf8(value.to_vec()).ok())
- })
- }
- }
- #[cfg(not(feature = "ssr"))]
- None
+ ssr_cookies_header_getter: Rc::new(move || {
+ get_header!(COOKIE, use_cookie, ssr_cookies_header_getter)
}),
ssr_set_cookie: Arc::new(|cookie: &Cookie| {
#[cfg(feature = "ssr")]
diff --git a/src/use_display_media.rs b/src/use_display_media.rs
index 2ff5a2a..e84326f 100644
--- a/src/use_display_media.rs
+++ b/src/use_display_media.rs
@@ -132,9 +132,9 @@ async fn create_media(audio: bool) -> Result {
.ok_or_else(|| JsValue::from_str("Failed to access window.navigator"))
.and_then(|n| n.media_devices())?;
- let mut constraints = web_sys::DisplayMediaStreamConstraints::new();
+ let constraints = web_sys::DisplayMediaStreamConstraints::new();
if audio {
- constraints.audio(&JsValue::from(true));
+ constraints.set_audio(&JsValue::from(true));
}
let promise = media.get_display_media_with_constraints(&constraints)?;
diff --git a/src/use_document.rs b/src/use_document.rs
index ac1d379..c6b2fe7 100644
--- a/src/use_document.rs
+++ b/src/use_document.rs
@@ -1,11 +1,15 @@
use cfg_if::cfg_if;
+use js_sys::Function;
use std::ops::Deref;
use crate::core::impl_ssr_safe_method;
#[cfg(not(feature = "ssr"))]
use leptos::prelude::*;
use wasm_bindgen::JsValue;
-use web_sys::NodeList;
+use web_sys::{
+ Document, Element, HtmlCollection, HtmlElement, HtmlHeadElement, Location, NodeList,
+ VisibilityState, Window,
+};
/// SSR safe `document()`.
/// This returns just a new-type wrapper around `Option`.
@@ -39,10 +43,10 @@ pub fn use_document() -> UseDocument {
/// Return type of [`use_document`].
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct UseDocument(Option);
+pub struct UseDocument(Option);
impl Deref for UseDocument {
- type Target = Option;
+ type Target = Option;
fn deref(&self) -> &Self::Target {
&self.0
}
@@ -51,22 +55,205 @@ impl Deref for UseDocument {
impl UseDocument {
impl_ssr_safe_method!(
/// Returns `Some(Document)` in the Browser. `None` otherwise.
- body(&self) -> Option;
+ body(&self) -> Option;
.unwrap_or_default()
);
impl_ssr_safe_method!(
/// Returns the active (focused) `Some(web_sys::Element)` in the Browser. `None` otherwise.
- active_element(&self) -> Option;
+ active_element(&self) -> Option;
.unwrap_or_default()
);
impl_ssr_safe_method!(
- query_selector(&self, selector: &str) -> Result, JsValue>;
+ query_selector(&self, selector: &str) -> Result , JsValue>;
.unwrap_or(Ok(None))
);
impl_ssr_safe_method!(
query_selector_all(&self, selectors: &str) -> Option>
);
+
+ impl_ssr_safe_method!(
+ url(&self) -> Option>
+ );
+
+ impl_ssr_safe_method!(
+ document_uri(&self) -> Option>
+ );
+
+ impl_ssr_safe_method!(
+ compat_mode(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ character_set(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ charset(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ input_encoding(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ content_type(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ document_element(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ location(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ referrer(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ last_modified(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ ready_state(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ title(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ dir(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ head(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ images(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ embeds(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ plugins(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ links(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ forms(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ scripts(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ default_view(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onreadystatechange(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onbeforescriptexecute(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onafterscriptexecute(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onselectionchange(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ current_script(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ anchors(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ applets(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ fullscreen(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ fullscreen_enabled(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ onfullscreenchange(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onfullscreenerror(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onpointerlockchange(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ onpointerlockerror(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ /// Hides on server by default
+ hidden(&self) -> bool;
+ .unwrap_or(true)
+ );
+
+ impl_ssr_safe_method!(
+ visibility_state(&self) -> Option
+ );
+
+ impl_ssr_safe_method!(
+ onvisibilitychange(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ selected_style_sheet_set(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ last_style_sheet_set(&self) -> Option;
+ .unwrap_or_default()
+ );
+
+ impl_ssr_safe_method!(
+ preferred_style_sheet_set(&self) -> Option;
+ .unwrap_or_default()
+ );
}
diff --git a/src/use_event_listener.rs b/src/use_event_listener.rs
index 463cc76..c4020de 100644
--- a/src/use_event_listener.rs
+++ b/src/use_event_listener.rs
@@ -237,11 +237,11 @@ impl UseEventListenerOptions {
passive,
} = self;
- let mut options = web_sys::AddEventListenerOptions::new();
- options.capture(*capture);
- options.once(*once);
+ let options = web_sys::AddEventListenerOptions::new();
+ options.set_capture(*capture);
+ options.set_once(*once);
if let Some(passive) = passive {
- options.passive(*passive);
+ options.set_passive(*passive);
}
options
diff --git a/src/use_event_source.rs b/src/use_event_source.rs
index 438602d..4366372 100644
--- a/src/use_event_source.rs
+++ b/src/use_event_source.rs
@@ -186,8 +186,8 @@ where
return;
}
- let mut event_src_opts = web_sys::EventSourceInit::new();
- event_src_opts.with_credentials(with_credentials);
+ let event_src_opts = web_sys::EventSourceInit::new();
+ event_src_opts.set_with_credentials(with_credentials);
let es = web_sys::EventSource::new_with_event_source_init_dict(&url, &event_src_opts)
.unwrap_throw();
diff --git a/src/use_geolocation.rs b/src/use_geolocation.rs
index 8b6ff05..a92be6b 100644
--- a/src/use_geolocation.rs
+++ b/src/use_geolocation.rs
@@ -185,10 +185,10 @@ impl UseGeolocationOptions {
..
} = self;
- let mut options = web_sys::PositionOptions::new();
- options.enable_high_accuracy(*enable_high_accuracy);
- options.maximum_age(*maximum_age);
- options.timeout(*timeout);
+ let options = web_sys::PositionOptions::new();
+ options.set_enable_high_accuracy(*enable_high_accuracy);
+ options.set_maximum_age(*maximum_age);
+ options.set_timeout(*timeout);
options
}
diff --git a/src/use_intersection_observer.rs b/src/use_intersection_observer.rs
index 7a724b7..9aebe3a 100644
--- a/src/use_intersection_observer.rs
+++ b/src/use_intersection_observer.rs
@@ -155,8 +155,9 @@ where
return;
}
- let mut options = web_sys::IntersectionObserverInit::new();
- options.root_margin(&root_margin).threshold(
+ let options = web_sys::IntersectionObserverInit::new();
+ options.set_root_margin(&root_margin);
+ options.set_threshold(
&thresholds
.iter()
.copied()
@@ -166,7 +167,7 @@ where
if let Some(Some(root)) = root {
let root: web_sys::Element = root.clone().into();
- options.root(Some(&root));
+ options.set_root(Some(&root));
}
let obs = web_sys::IntersectionObserver::new_with_options(
diff --git a/src/use_locale.rs b/src/use_locale.rs
new file mode 100644
index 0000000..76b1274
--- /dev/null
+++ b/src/use_locale.rs
@@ -0,0 +1,91 @@
+use crate::{use_locales_with_options, UseLocalesOptions};
+use leptos::*;
+use unic_langid::LanguageIdentifier;
+
+/// Reactive locale matching.
+///
+/// Returns the first matching locale given by [`fn@crate::use_locales`] that is also found in
+/// the `supported` list. In case there is no match, then the first locale in `supported` will be
+/// returned.
+///
+/// > If `supported` is empty, this function will panic!
+///
+/// Matching is done by using the [`fn@unic_langid::LanguageIdentifier::matches`] method.
+///
+/// ## Demo
+///
+/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_locale)
+///
+/// ## Usage
+///
+/// ```
+/// # use leptos::*;
+/// # use leptos_use::use_locale;
+/// use unic_langid::langid_slice;
+/// #
+/// # #[component]
+/// # fn Demo() -> impl IntoView {
+/// let locale = use_locale(langid_slice!["en", "de", "fr"]);
+/// #
+/// # view! { }
+/// # }
+/// ```
+///
+/// ## Server-Side Rendering
+///
+/// See [`fn@crate::use_locales`]
+pub fn use_locale(supported: S) -> Signal
+where
+ S: IntoIterator,
+ S::Item: AsRef,
+{
+ use_locale_with_options(supported, UseLocaleOptions::default())
+}
+
+/// Version of [`fn@crate::use_locale`] that takes a `UseLocaleOptions`. See [`fn@crate::use_locale`] for how to use.
+pub fn use_locale_with_options(
+ supported: S,
+ options: UseLocaleOptions,
+) -> Signal
+where
+ S: IntoIterator,
+ S::Item: AsRef,
+{
+ let client_locales = use_locales_with_options(options);
+
+ let supported = supported
+ .into_iter()
+ .map(|l| l.as_ref().clone())
+ .collect::>();
+
+ const EMPTY_ERR_MSG: &str = "Empty supported list. You have to provide at least one locale in the `supported` parameter";
+
+ assert!(!supported.is_empty(), "{}", EMPTY_ERR_MSG);
+
+ Signal::derive(move || {
+ let supported = supported.clone();
+
+ client_locales.with(|client_locales| {
+ let mut first_supported = None;
+
+ for s in supported {
+ if first_supported.is_none() {
+ first_supported = Some(s.clone());
+ }
+
+ for client_locale in client_locales {
+ let client_locale: LanguageIdentifier = client_locale
+ .parse()
+ .expect("Client should provide a list of valid unicode locales");
+ if client_locale.matches(&s, true, true) {
+ return s;
+ }
+ }
+ }
+
+ unreachable!("{}", EMPTY_ERR_MSG);
+ })
+ })
+}
+
+pub type UseLocaleOptions = UseLocalesOptions;
diff --git a/src/use_locales.rs b/src/use_locales.rs
new file mode 100644
index 0000000..8762dc5
--- /dev/null
+++ b/src/use_locales.rs
@@ -0,0 +1,114 @@
+use crate::utils::get_header;
+use default_struct_builder::DefaultBuilder;
+use leptos::*;
+use std::rc::Rc;
+
+/// Reactive locales.
+///
+/// If called on the client-side this function returns the value of
+/// [`navigator.languages`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages)
+/// and listens for changes to that property.
+///
+/// See "Server-Side Rendering" below.
+///
+/// ## Demo
+///
+/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_locales)
+///
+/// ## Usage
+///
+/// ```
+/// # use leptos::*;
+/// # use leptos_use::use_locales;
+/// #
+/// # #[component]
+/// # fn Demo() -> impl IntoView {
+/// let locales = use_locales();
+/// #
+/// # view! { }
+/// # }
+/// ```
+///
+/// ## Server-Side Rendering
+///
+/// On the server this returns the parsed value of the `accept-language` 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"`, for `spin` enable `"spin"`.
+///
+/// ### Bring your own header
+///
+/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
+/// you can provide your own way of reading the language header value using the option
+/// [`crate::UseLocalesOptions::ssr_lang_header_getter`].
+pub fn use_locales() -> Signal> {
+ use_locales_with_options(UseLocalesOptions::default())
+}
+
+/// Version of [`fn@crate::use_locales`] that takes a `UseLocalesOptions`. See [`fn@crate::use_locales`] for how to use.
+pub fn use_locales_with_options(options: UseLocalesOptions) -> Signal> {
+ #[cfg(not(feature = "ssr"))]
+ {
+ let _ = options;
+
+ let read_navigator_languages = || {
+ let window = crate::use_window();
+ let navigator = window.navigator();
+ navigator
+ .map(|navigator| navigator.languages().to_vec())
+ .unwrap_or_default()
+ .into_iter()
+ .filter_map(|x| x.as_string())
+ .collect::>()
+ };
+
+ let (locales, set_locales) = create_signal(read_navigator_languages());
+
+ let _ = crate::use_event_listener(crate::use_window(), ev::languagechange, move |_| {
+ set_locales.update(|locales| *locales = read_navigator_languages());
+ });
+
+ locales.into()
+ }
+
+ #[cfg(feature = "ssr")]
+ {
+ let UseLocalesOptions {
+ ssr_lang_header_getter,
+ } = options;
+
+ let accept_language = ssr_lang_header_getter().unwrap_or_default();
+
+ let locales = accept_language
+ .split(',')
+ .map(|locale| {
+ locale
+ .split_once(';')
+ .map(|x| x.0)
+ .unwrap_or(locale)
+ .to_owned()
+ })
+ .collect::>();
+
+ Signal::derive(move || locales.clone())
+ }
+}
+
+/// Options for [`fn@crate::use_locales_with_options`].
+#[derive(DefaultBuilder)]
+pub struct UseLocalesOptions {
+ /// Getter function to return the string value of the accept languange header.
+ /// When you use one of the features `"axum"`, `"actix"` or `"spin"` there's a valid default implementation provided.
+ #[allow(dead_code)]
+ ssr_lang_header_getter: Rc Option>,
+}
+
+impl Default for UseLocalesOptions {
+ fn default() -> Self {
+ Self {
+ ssr_lang_header_getter: Rc::new(move || {
+ get_header!(ACCEPT_LANGUAGE, use_locale, ssr_lang_header_getter)
+ }),
+ }
+ }
+}
diff --git a/src/use_media_query.rs b/src/use_media_query.rs
index 7ddf1f0..0bfee2e 100644
--- a/src/use_media_query.rs
+++ b/src/use_media_query.rs
@@ -39,6 +39,7 @@ use std::rc::Rc;
///
/// * [`fn@crate::use_preferred_dark`]
/// * [`fn@crate::use_preferred_contrast`]
+/// * [`fn@crate::use_prefers_reduced_motion`]
pub fn use_media_query(query: impl Into>) -> Signal {
let query = query.into();
diff --git a/src/use_mouse.rs b/src/use_mouse.rs
index faf58eb..92768ca 100644
--- a/src/use_mouse.rs
+++ b/src/use_mouse.rs
@@ -5,8 +5,8 @@ use crate::{use_event_listener_with_options, use_window, UseEventListenerOptions
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::ev::{dragover, mousemove, touchend, touchmove, touchstart};
-use leptos::prelude::wrappers::read::Signal;
use leptos::prelude::*;
+use std::convert::Infallible;
use std::marker::PhantomData;
use wasm_bindgen::{JsCast, JsValue};
@@ -231,10 +231,10 @@ where
_marker: PhantomData,
}
-impl Default for UseMouseOptions {
+impl Default for UseMouseOptions {
fn default() -> Self {
Self {
- coord_type: UseMouseCoordType::::default(),
+ coord_type: UseMouseCoordType::default(),
target: use_window(),
touch: true,
reset_on_touch_ends: false,
@@ -254,7 +254,7 @@ pub enum UseMouseCoordType {
Custom(E),
}
-impl Default for UseMouseCoordType {
+impl Default for UseMouseCoordType {
fn default() -> Self {
Self::Page
}
@@ -298,10 +298,15 @@ impl UseMouseEventExtractor for UseMouseCoord
}
}
-#[derive(Clone)]
-pub struct UseMouseEventExtractorDefault;
+impl UseMouseEventExtractor for Infallible {
+ fn extract_mouse_coords(&self, _: &web_sys::MouseEvent) -> Option<(f64, f64)> {
+ unreachable!()
+ }
-impl UseMouseEventExtractor for UseMouseEventExtractorDefault {}
+ fn extract_touch_coords(&self, _: &web_sys::Touch) -> Option<(f64, f64)> {
+ unreachable!()
+ }
+}
/// Return type of [`use_mouse`].
pub struct UseMouseReturn {
diff --git a/src/use_mouse_in_element.rs b/src/use_mouse_in_element.rs
index 94121cd..e01f994 100644
--- a/src/use_mouse_in_element.rs
+++ b/src/use_mouse_in_element.rs
@@ -1,12 +1,12 @@
use crate::core::{ElementMaybeSignal, Position};
use crate::{
- use_mouse_with_options, use_window, UseMouseCoordType, UseMouseEventExtractor,
- UseMouseEventExtractorDefault, UseMouseOptions, UseMouseReturn, UseMouseSourceType, UseWindow,
+ use_mouse_with_options, use_window, UseMouseCoordType, UseMouseEventExtractor, UseMouseOptions,
+ UseMouseReturn, UseMouseSourceType, UseWindow,
};
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
-use leptos::prelude::wrappers::read::Signal;
use leptos::prelude::*;
+use std::convert::Infallible;
use std::marker::PhantomData;
/// Reactive mouse position related to an element.
@@ -196,12 +196,10 @@ where
_marker: PhantomData,
}
-impl Default
- for UseMouseInElementOptions
-{
+impl Default for UseMouseInElementOptions {
fn default() -> Self {
Self {
- coord_type: UseMouseCoordType::::default(),
+ coord_type: UseMouseCoordType::default(),
target: use_window(),
touch: true,
reset_on_touch_ends: false,
diff --git a/src/use_mutation_observer.rs b/src/use_mutation_observer.rs
index 2333399..abf97f6 100644
--- a/src/use_mutation_observer.rs
+++ b/src/use_mutation_observer.rs
@@ -216,20 +216,20 @@ impl From for web_sys::MutationObserverInit {
character_data_old_value,
} = val;
- let mut init = Self::new();
+ let init = Self::new();
- init.subtree(subtree)
- .child_list(child_list)
- .attributes(attributes)
- .attribute_old_value(attribute_old_value)
- .character_data_old_value(character_data_old_value);
+ init.set_subtree(subtree);
+ init.set_child_list(child_list);
+ init.set_attributes(attributes);
+ init.set_attribute_old_value(attribute_old_value);
+ init.set_character_data_old_value(character_data_old_value);
if let Some(attribute_filter) = attribute_filter {
let array = js_sys::Array::from_iter(attribute_filter.into_iter().map(JsValue::from));
- init.attribute_filter(array.unchecked_ref());
+ init.set_attribute_filter(array.unchecked_ref());
}
if let Some(character_data) = character_data {
- init.character_data(character_data);
+ init.set_character_data(character_data);
}
init
diff --git a/src/use_preferred_contrast.rs b/src/use_preferred_contrast.rs
index 3d979b5..feb2856 100644
--- a/src/use_preferred_contrast.rs
+++ b/src/use_preferred_contrast.rs
@@ -28,6 +28,7 @@ use std::fmt::Display;
///
/// * [`fn@crate::use_media_query`]
/// * [`fn@crate::use_preferred_dark`]
+/// * [`fn@crate::use_prefers_reduced_motion`]
pub fn use_preferred_contrast() -> Signal {
let is_more = use_media_query("(prefers-contrast: more)");
let is_less = use_media_query("(prefers-contrast: less)");
@@ -55,7 +56,6 @@ pub enum PreferredContrast {
#[default]
NoPreference,
}
-
impl Display for PreferredContrast {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
diff --git a/src/use_preferred_dark.rs b/src/use_preferred_dark.rs
index 0db93fa..0a42bad 100644
--- a/src/use_preferred_dark.rs
+++ b/src/use_preferred_dark.rs
@@ -1,5 +1,7 @@
-use crate::use_media_query;
+use crate::utils::get_header;
+use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
+use std::rc::Rc;
/// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
///
@@ -20,12 +22,67 @@ use leptos::prelude::*;
///
/// ## Server-Side Rendering
///
-/// On the server this functions returns a Signal that is always `false`.
+/// On the server this will try to read the
+/// [`Sec-CH-Prefers-Color-Scheme` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
+/// to determine the color mode. If the header is not present it will return `ColorMode::Light`.
+/// Please have a look at the linked documentation above for that header to see browser support
+/// as well as potential server requirements.
+///
+/// > 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"`, for `spin` enable `"spin"`.
+///
+/// ### Bring your own header
+///
+/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
+/// you can provide your own way of reading the color scheme header value using the option
+/// [`crate::UsePreferredDarkOptions::ssr_color_header_getter`].
///
/// ## See also
///
/// * [`fn@crate::use_media_query`]
/// * [`fn@crate::use_preferred_contrast`]
+/// * [`fn@crate::use_prefers_reduced_motion`]
pub fn use_preferred_dark() -> Signal {
- use_media_query("(prefers-color-scheme: dark)")
+ use_preferred_dark_with_options(Default::default())
+}
+
+/// Version of [`fn@crate::use_preferred_dark`] that accepts a `UsePreferredDarkOptions`.
+pub fn use_preferred_dark_with_options(options: UsePreferredDarkOptions) -> Signal {
+ #[cfg(not(feature = "ssr"))]
+ {
+ let _ = options;
+
+ crate::use_media_query("(prefers-color-scheme: dark)")
+ }
+
+ #[cfg(feature = "ssr")]
+ {
+ Signal::derive(move || (options.ssr_color_header_getter)() == Some("dark".to_string()))
+ }
+}
+
+/// Options for [`fn@crate::use_preferred_dark_with_options`].
+#[derive(DefaultBuilder)]
+pub struct UsePreferredDarkOptions {
+ /// Getter function to return the string value of the
+ /// [`Sec-CH-Prefers-Color-Scheme`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
+ /// header.
+ /// When you use one of the features `"axum"`, `"actix"` or `"spin"` there's a valid default
+ /// implementation provided.
+ #[allow(dead_code)]
+ pub(crate) ssr_color_header_getter: Rc Option>,
+}
+
+impl Default for UsePreferredDarkOptions {
+ fn default() -> Self {
+ Self {
+ ssr_color_header_getter: Rc::new(move || {
+ get_header!(
+ HeaderName::from_static("sec-ch-prefers-color-scheme"),
+ use_locale,
+ ssr_color_header_getter
+ )
+ }),
+ }
+ }
}
diff --git a/src/use_prefers_reduced_motion.rs b/src/use_prefers_reduced_motion.rs
new file mode 100644
index 0000000..b1afd55
--- /dev/null
+++ b/src/use_prefers_reduced_motion.rs
@@ -0,0 +1,102 @@
+use crate::utils::get_header;
+use default_struct_builder::DefaultBuilder;
+use leptos::*;
+use std::rc::Rc;
+
+/// Reactive [reduced motions preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion).
+///
+/// ## Demo
+///
+/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_prefers_reduced_motion)
+///
+/// ## Usage
+///
+/// ```
+/// # use leptos::*;
+/// # use leptos_use::use_prefers_reduced_motion;
+/// # use leptos_use::docs::BooleanDisplay;
+/// #
+/// # #[component]
+/// # fn Demo() -> impl IntoView {
+/// let is_reduced_motion_preferred = use_prefers_reduced_motion();
+///
+/// view! {
+///
+/// }
+/// # }
+/// ```
+///
+/// ## Server-Side Rendering
+///
+/// On the server this will try to read the
+/// [`Sec-CH-Prefers-Reduced-Motion` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Reduced-Motion)
+/// to indicate the preference for animations to be displayed with reduced motion.
+/// Please have a look at the linked documentation above to see browser support
+/// as well as potential serve requirements.
+///
+/// > 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"`, for `spin` enable `"spin"`.
+///
+/// ### Bring your own header
+///
+/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your
+/// liking, you can provide your own way of reading the reduced motion header value using the option
+/// [`crate::UsePrefersReducedMotionOptions::ssr_motion_header_getter`].
+///
+/// ## See also
+///
+/// * [`fn@crate::use_media_query`]
+/// * [`fn@crate::use_preferred_contrast`]
+/// * [`fn@crate::use_preferred_dark`]
+pub fn use_prefers_reduced_motion() -> Signal {
+ use_prefers_reduced_motion_with_options(UsePrefersReducedMotionOptions::default())
+}
+
+/// Version of [`fn@crate::use_prefers_reduced_motion`] that takes a `UsePrefersReducedMotionOptions`. See [`fn@crate::use_prefers_reduced_motion`] for how to use.
+pub fn use_prefers_reduced_motion_with_options(
+ options: UsePrefersReducedMotionOptions,
+) -> Signal {
+ #[cfg(not(feature = "ssr"))]
+ {
+ let _ = options;
+ crate::use_media_query("(prefers-reduced-motion: reduce)")
+ }
+ #[cfg(feature = "ssr")]
+ {
+ Signal::derive(move || (options.ssr_motion_header_getter)() == Some("reduce".to_string()))
+ }
+}
+
+/// Options for [`fn@crate::use_prefers_reduced_motion_with_options`].
+#[derive(DefaultBuilder)]
+pub struct UsePrefersReducedMotionOptions {
+ /// Getter function to return the string value of the
+ /// [`Sec-CH-Prefers-Reduced-Motion`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Reduced-Motion)
+ /// header.
+ /// When you use one of the features `"axum"`, `"actix"` or `"spin"` there's a valid default
+ /// implementation provided.
+ #[allow(dead_code)]
+ pub(crate) ssr_motion_header_getter: Rc Option>,
+}
+
+impl Default for UsePrefersReducedMotionOptions {
+ fn default() -> Self {
+ Self {
+ ssr_motion_header_getter: Rc::new(move || {
+ get_header!(
+ HeaderName::from_static("sec-ch-prefers-reduced-motion"),
+ use_locale,
+ ssr_motion_header_getter
+ )
+ }),
+ }
+ }
+}
diff --git a/src/use_resize_observer.rs b/src/use_resize_observer.rs
index 4bdb478..6682580 100644
--- a/src/use_resize_observer.rs
+++ b/src/use_resize_observer.rs
@@ -172,8 +172,8 @@ pub struct UseResizeObserverOptions {
impl From for web_sys::ResizeObserverOptions {
fn from(val: UseResizeObserverOptions) -> Self {
- let mut options = web_sys::ResizeObserverOptions::new();
- options.box_(
+ let options = web_sys::ResizeObserverOptions::new();
+ options.set_box(
val.box_
.unwrap_or(web_sys::ResizeObserverBoxOptions::ContentBox),
);
diff --git a/src/use_scroll.rs b/src/use_scroll.rs
index a1f28c6..dc09bdd 100644
--- a/src/use_scroll.rs
+++ b/src/use_scroll.rs
@@ -233,14 +233,14 @@ where
if let Some(element) = element {
let element = element.into();
- let mut scroll_options = web_sys::ScrollToOptions::new();
- scroll_options.behavior(behavior.get_untracked().into());
+ let scroll_options = web_sys::ScrollToOptions::new();
+ scroll_options.set_behavior(behavior.get_untracked().into());
if let Some(x) = x {
- scroll_options.left(x);
+ scroll_options.set_left(x);
}
if let Some(y) = y {
- scroll_options.top(y);
+ scroll_options.set_top(y);
}
element.scroll_to_with_scroll_to_options(&scroll_options);
diff --git a/src/use_timeout_fn.rs b/src/use_timeout_fn.rs
index 558f93b..ca548d8 100644
--- a/src/use_timeout_fn.rs
+++ b/src/use_timeout_fn.rs
@@ -1,6 +1,5 @@
use leptos::leptos_dom::helpers::TimeoutHandle;
use leptos::prelude::diagnostics::SpecialNonReactiveZone;
-use leptos::prelude::wrappers::read::Signal;
use leptos::prelude::*;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
@@ -32,6 +31,11 @@ use std::time::Duration;
/// # view! { }
/// # }
/// ```
+///
+/// ## Server-Side Rendering
+///
+/// On the server the callback will never be run. The returned functions are all no-ops and
+/// `is_pending` will always be `false`.
pub fn use_timeout_fn(
callback: CbFn,
delay: D,
@@ -45,59 +49,82 @@ where
let (is_pending, set_pending) = signal(false);
- let timer = Arc::new(Mutex::new(None::));
+ let start;
+ let stop;
- let clear = {
- let timer = Arc::clone(&timer);
+ #[cfg(not(feature = "ssr"))]
+ {
+ use leptos::leptos_dom::helpers::TimeoutHandle;
+ use std::cell::Cell;
+ use std::rc::Rc;
+ use std::time::Duration;
- move || {
- let timer = timer.lock().unwrap();
- if let Some(timer) = *timer {
- timer.clear();
+ let delay = delay.into();
+
+ let timer = Arc::new(Mutex::new(None::));
+
+ let clear = {
+ let timer = Arc::clone(&timer);
+
+ move || {
+ let timer = timer.lock().unwrap();
+ if let Some(timer) = *timer {
+ timer.clear();
+ }
}
- }
- };
+ };
- let stop = {
- let clear = clear.clone();
+ stop = {
+ let clear = clear.clone();
- move || {
- set_pending.set(false);
- clear();
- }
- };
+ move || {
+ set_pending.set(false);
+ clear();
+ }
+ };
- let start = {
- let timer = Arc::clone(&timer);
- let callback = callback.clone();
+ start = {
+ let timer = Arc::clone(&timer);
+ let callback = callback.clone();
- move |arg: Arg| {
- set_pending.set(true);
+ move |arg: Arg| {
+ set_pending.set(true);
- let handle = set_timeout_with_handle(
- {
- let timer = Arc::clone(&timer);
- let callback = callback.clone();
+ let handle = set_timeout_with_handle(
+ {
+ let timer = Arc::clone(&timer);
+ let callback = callback.clone();
- move || {
- set_pending.set(false);
- *timer.lock().unwrap() = None;
+ move || {
+ set_pending.set(false);
+ *timer.lock().unwrap() = None;
- #[cfg(debug_assertions)]
- let _z = SpecialNonReactiveZone::enter();
+ #[cfg(debug_assertions)]
+ let _z = SpecialNonReactiveZone::enter();
- callback(arg);
- }
- },
- Duration::from_millis(delay.get_untracked() as u64),
- )
- .ok();
+ callback(arg);
+ }
+ },
+ Duration::from_millis(delay.get_untracked() as u64),
+ )
+ .ok();
- *timer.lock().unwrap() = handle;
- }
- };
+ *timer.lock().unwrap() = handle;
+ }
+ };
- on_cleanup(clear);
+ on_cleanup(clear);
+ }
+
+ #[cfg(feature = "ssr")]
+ {
+ let _ = set_pending;
+ let _ = callback;
+ let _ = delay;
+
+ start = move |_: Arg| ();
+ stop = move || ();
+ }
UseTimeoutFnReturn {
is_pending: is_pending.into(),
diff --git a/src/use_user_media.rs b/src/use_user_media.rs
index 64d2513..d6a4705 100644
--- a/src/use_user_media.rs
+++ b/src/use_user_media.rs
@@ -136,12 +136,12 @@ async fn create_media(video: bool, audio: bool) -> Result for web_sys::NotificationDirection {
/// See [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/notification) for more info.
///
/// The following implementations are missing:
-/// - `renotify`
-/// - `vibrate`
-/// - `silent`
-/// - `image`
+/// - `vibrate`
#[derive(DefaultBuilder, Clone)]
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub struct UseWebNotificationOptions {
@@ -253,14 +251,25 @@ pub struct UseWebNotificationOptions {
#[builder(into)]
icon: Option,
+ /// The URL of the image to be displayed as part of the notification as specified
+ /// in the constructor's options parameter.
+ #[builder(into)]
+ image: Option,
+
/// A boolean value indicating that a notification should remain active until the
/// user clicks or dismisses it, rather than closing automatically.
require_interaction: bool,
- // /// A boolean value specifying whether the user should be notified after a new notification replaces an old one.
- // /// The default is `false`, which means they won't be notified. If `true`, then `tag` also must be set.
- // #[builder(into)]
- // renotify: bool,
+ /// A boolean value specifying whether the user should be notified after a new notification replaces an old one.
+ /// The default is `false`, which means they won't be notified. If `true`, then `tag` also must be set.
+ #[builder(into)]
+ renotify: bool,
+
+ /// A boolean value specifying whether the notification should be silent, regardless of the device settings.
+ /// The default is `false`, which means the notification is not silent. If `true`, then the notification will be silent.
+ #[builder(into)]
+ silent: Option,
+
/// Called when the user clicks on displayed `Notification`.
on_click: Rc,
@@ -284,8 +293,10 @@ impl Default for UseWebNotificationOptions {
language: None,
tag: None,
icon: None,
+ image: None,
require_interaction: false,
- // renotify: false,
+ renotify: false,
+ silent: None,
on_click: Rc::new(|_| {}),
on_close: Rc::new(|_| {}),
on_error: Rc::new(|_| {}),
@@ -296,27 +307,31 @@ impl Default for UseWebNotificationOptions {
impl From<&UseWebNotificationOptions> for web_sys::NotificationOptions {
fn from(options: &UseWebNotificationOptions) -> Self {
- let mut web_sys_options = Self::new();
+ let web_sys_options = Self::new();
- web_sys_options
- .dir(options.direction.into())
- .require_interaction(options.require_interaction);
- // .renotify(options.renotify);
+ web_sys_options.set_dir(options.direction.into());
+ web_sys_options.set_require_interaction(options.require_interaction);
+ web_sys_options.set_renotify(options.renotify);
+ web_sys_options.set_silent(options.silent);
if let Some(body) = &options.body {
- web_sys_options.body(body);
+ web_sys_options.set_body(body);
}
if let Some(icon) = &options.icon {
- web_sys_options.icon(icon);
+ web_sys_options.set_icon(icon);
+ }
+
+ if let Some(image) = &options.image {
+ web_sys_options.set_image(image);
}
if let Some(language) = &options.language {
- web_sys_options.lang(language);
+ web_sys_options.set_lang(language);
}
if let Some(tag) = &options.tag {
- web_sys_options.tag(tag);
+ web_sys_options.set_tag(tag);
}
web_sys_options
@@ -328,9 +343,7 @@ impl From<&UseWebNotificationOptions> for web_sys::NotificationOptions {
/// See [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/notification) for more info.
///
/// The following implementations are missing:
-/// - `vibrate`
-/// - `silent`
-/// - `image`
+/// - `vibrate`
#[derive(DefaultBuilder, Default)]
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub struct ShowOptions {
@@ -365,46 +378,65 @@ pub struct ShowOptions {
#[builder(into)]
icon: Option,
+ /// The URL of the image to be displayed as part of the notification as specified
+ /// in the constructor's options parameter.
+ #[builder(into)]
+ image: Option,
+
/// A boolean value indicating that a notification should remain active until the
/// user clicks or dismisses it, rather than closing automatically.
#[builder(into)]
require_interaction: Option,
- // /// A boolean value specifying whether the user should be notified after a new notification replaces an old one.
- // /// The default is `false`, which means they won't be notified. If `true`, then `tag` also must be set.
- // #[builder(into)]
- // renotify: Option,
+
+ /// A boolean value specifying whether the user should be notified after a new notification replaces an old one.
+ /// The default is `false`, which means they won't be notified. If `true`, then `tag` also must be set.
+ #[builder(into)]
+ renotify: Option,
+
+ /// A boolean value specifying whether the notification should be silent, regardless of the device settings.
+ /// The default is `false`, which means the notification is not silent. If `true`, then the notification will be silent.
+ #[builder(into)]
+ silent: Option,
}
#[cfg(not(feature = "ssr"))]
impl ShowOptions {
fn override_notification_options(&self, options: &mut web_sys::NotificationOptions) {
if let Some(direction) = self.direction {
- options.dir(direction.into());
+ options.set_dir(direction.into());
}
if let Some(require_interaction) = self.require_interaction {
- options.require_interaction(require_interaction);
+ options.set_require_interaction(require_interaction);
}
if let Some(body) = &self.body {
- options.body(body);
+ options.set_body(body);
}
if let Some(icon) = &self.icon {
- options.icon(icon);
+ options.set_icon(icon);
+ }
+
+ if let Some(image) = &self.image {
+ options.set_image(image);
}
if let Some(language) = &self.language {
- options.lang(language);
+ options.set_lang(language);
}
if let Some(tag) = &self.tag {
- options.tag(tag);
+ options.set_tag(tag);
}
- // if let Some(renotify) = &self.renotify {
- // options.renotify(renotify);
- // }
+ if let Some(renotify) = self.renotify {
+ options.set_renotify(renotify);
+ }
+
+ if let Some(silent) = self.silent {
+ options.set_silent(Some(silent));
+ }
}
}
@@ -437,7 +469,7 @@ impl From for NotificationPermission {
web_sys::NotificationPermission::Default => Self::Default,
web_sys::NotificationPermission::Granted => Self::Granted,
web_sys::NotificationPermission::Denied => Self::Denied,
- web_sys::NotificationPermission::__Nonexhaustive => Self::Default,
+ _ => Self::Default,
}
}
}
diff --git a/src/use_websocket.rs b/src/use_websocket.rs
index e658a81..74f28fd 100644
--- a/src/use_websocket.rs
+++ b/src/use_websocket.rs
@@ -6,7 +6,7 @@ use std::sync::{atomic::AtomicBool, Arc};
use std::time::Duration;
use thiserror::Error;
-use crate::core::ConnectionReadyState;
+use crate::{core::ConnectionReadyState, ReconnectLimit};
use codee::{
CodecError, Decoder, Encoder, HybridCoderError, HybridDecoder, HybridEncoder, IsBinary,
};
@@ -609,26 +609,6 @@ where
send,
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum ReconnectLimit {
- Infinite,
- Limited(u64),
-}
-
-impl Default for ReconnectLimit {
- fn default() -> Self {
- ReconnectLimit::Limited(3)
- }
-}
-
-impl ReconnectLimit {
- pub fn is_exceeded_by(self, times: u64) -> bool {
- match self {
- ReconnectLimit::Infinite => false,
- ReconnectLimit::Limited(limit) => times >= limit,
- }
- }
-}
type ArcFnBytes = Arc;
diff --git a/src/utils/header.rs b/src/utils/header.rs
new file mode 100644
index 0000000..0e9d6cd
--- /dev/null
+++ b/src/utils/header.rs
@@ -0,0 +1,76 @@
+#[cfg(feature = "actix")]
+use http0_2::HeaderName;
+#[cfg(any(feature = "axum", feature = "spin"))]
+use http1::HeaderName;
+use leptos::*;
+
+/// Get the value of the header with the given name.
+///
+/// This function is only meant to be used on the server.
+/// So it is only defined when the feature `"ssr"` is enabled together with one of the
+/// features `"axum"`, `"actix"` or `"spin"`.
+///
+/// ## Example
+///
+/// ```ignore
+/// # use leptos_use::utils::header;
+/// #
+/// let content_len = header(http::header::CONTENT_LENGTH);
+/// ```
+pub fn header(name: N) -> Option
+where
+ N: Into,
+{
+ let name = name.into();
+
+ #[cfg(all(feature = "actix", feature = "axum"))]
+ compile_error!("You can only enable one of features \"actix\" and \"axum\" at the same time");
+
+ #[cfg(all(feature = "actix", feature = "spin"))]
+ compile_error!("You can only enable one of features \"actix\" and \"spin\" at the same time");
+
+ #[cfg(all(feature = "axum", feature = "spin"))]
+ compile_error!("You can only enable one of features \"axum\" and \"spin\" at the same time");
+
+ #[cfg(feature = "actix")]
+ type HeaderValue = http0_2::HeaderValue;
+ #[cfg(feature = "axum")]
+ type HeaderValue = http1::HeaderValue;
+
+ #[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
+ let headers;
+ #[cfg(feature = "actix")]
+ {
+ headers = use_context::().map(|req| req.headers().clone());
+ }
+ #[cfg(feature = "axum")]
+ {
+ headers = use_context::().map(|parts| parts.headers);
+ }
+ #[cfg(feature = "spin")]
+ {
+ headers = use_context::().map(|parts| parts.headers().clone());
+ }
+
+ #[cfg(any(feature = "axum", feature = "actix"))]
+ {
+ headers.map(|headers| {
+ headers
+ .get(name)
+ .cloned()
+ .unwrap_or_else(|| HeaderValue::from_static(""))
+ .to_str()
+ .unwrap_or_default()
+ .to_owned()
+ })
+ }
+ #[cfg(feature = "spin")]
+ {
+ headers.and_then(|headers| {
+ headers
+ .iter()
+ .find(|(key, _)| **key == name)
+ .and_then(|(_, value)| String::from_utf8(value.to_vec()).ok())
+ })
+ }
+}
diff --git a/src/utils/header_macro.rs b/src/utils/header_macro.rs
new file mode 100644
index 0000000..2664f78
--- /dev/null
+++ b/src/utils/header_macro.rs
@@ -0,0 +1,43 @@
+#![allow(unused_macros, unused_imports)]
+
+macro_rules! get_header {
+ (
+ $header_name:expr,
+ $function_name:ident,
+ $option_name:ident
+ $(,)?
+ ) => {
+ if cfg!(feature = "ssr") {
+ #[cfg(all(
+ not(feature = "axum"),
+ not(feature = "actix"),
+ not(feature = "spin")
+ ))]
+ {
+ leptos::logging::warn!(
+ "If you're using `{}` with SSR but without any of the features `axum`, `actix` or `spin` enabled, you have to provide the option `{}`",
+ stringify!($function_name),
+ stringify!($option_name)
+ );
+ return None;
+ }
+
+ #[cfg(feature = "actix")]
+ #[allow(unused_imports)]
+ use http0_2::{HeaderName, header::*};
+ #[cfg(any(feature = "axum", feature = "spin"))]
+ #[allow(unused_imports)]
+ use http1::{HeaderName, header::*};
+
+ #[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
+ {
+ let header_name = $header_name;
+ crate::utils::header(header_name)
+ }
+ } else {
+ None
+ }
+ };
+}
+
+pub(crate) use get_header;
diff --git a/src/utils/js_value_from_to_string.rs b/src/utils/js_value_from_to_string.rs
index f371c96..4149c4c 100644
--- a/src/utils/js_value_from_to_string.rs
+++ b/src/utils/js_value_from_to_string.rs
@@ -1,3 +1,5 @@
+#![allow(unused_macros, unused_imports)]
+
macro_rules! js_value_from_to_string {
($name:ident) => {
impl From<$name> for JsValue {
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 906f4e1..76b179a 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1,4 +1,11 @@
mod filters;
+#[cfg(all(
+ feature = "ssr",
+ any(feature = "axum", feature = "actix", feature = "spin")
+))]
+mod header;
+mod header_macro;
+#[cfg(feature = "is")]
mod is;
mod js;
mod js_value_from_to_string;
@@ -7,7 +14,17 @@ mod signal_filtered;
mod use_derive_signal;
pub use filters::*;
+#[cfg(all(
+ feature = "ssr",
+ any(feature = "axum", feature = "actix", feature = "spin")
+))]
+pub use header::*;
+#[allow(unused_imports)]
+pub(crate) use header_macro::*;
+#[cfg(feature = "is")]
pub use is::*;
+#[allow(unused_imports)]
pub(crate) use js_value_from_to_string::*;
pub use pausable::*;
+#[allow(unused_imports)]
pub(crate) use signal_filtered::*;
diff --git a/src/utils/signal_filtered.rs b/src/utils/signal_filtered.rs
index ad9ebe7..52c1144 100644
--- a/src/utils/signal_filtered.rs
+++ b/src/utils/signal_filtered.rs
@@ -1,3 +1,5 @@
+#![allow(unused_macros, unused_imports)]
+
macro_rules! signal_filtered {
(
$(#[$outer:meta])*
diff --git a/src/watch_debounced.rs b/src/watch_debounced.rs
index 61491cd..3a9b1e8 100644
--- a/src/watch_debounced.rs
+++ b/src/watch_debounced.rs
@@ -1,4 +1,4 @@
-use crate::{watch_with_options, DebounceOptions, WatchOptions};
+use crate::{watch_with_options, utils::DebounceOptions, WatchOptions};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
diff --git a/src/watch_throttled.rs b/src/watch_throttled.rs
index fc71f46..d4e8bfe 100644
--- a/src/watch_throttled.rs
+++ b/src/watch_throttled.rs
@@ -1,4 +1,4 @@
-use crate::{watch_with_options, ThrottleOptions, WatchOptions};
+use crate::{watch_with_options, utils::ThrottleOptions, WatchOptions};
use default_struct_builder::DefaultBuilder;
/// A throttled version of `leptos::watch`.
diff --git a/template/src/{{ module }}/{{ function_name }}.ffizer.hbs.rs b/template/src/{{ module }}/{{ function_name }}.ffizer.hbs.rs
index d3ed23c..1248179 100644
--- a/template/src/{{ module }}/{{ function_name }}.ffizer.hbs.rs
+++ b/template/src/{{ module }}/{{ function_name }}.ffizer.hbs.rs
@@ -22,19 +22,23 @@ use leptos::prelude::*;
/// #
/// # view! { }
/// # }
-/// ```{{#if feature}}
+/// ```
+///
+/// ## Server-Side Rendering
+/// {{#if feature}}
// #[doc(cfg(feature = "{{feature}}"))]{{/if}}
+
pub fn {{ function_name }}() -> {{ to_pascal_case function_name }}Return {
{{ function_name }}_with_options({{ to_pascal_case function_name }}Options::default())
}
-/// Version of [`{{ function_name }}`] that takes a `{{ to_pascal_case function_name }}Options`. See [`{{ function_name }}`] for how to use.{{#if feature}}
+/// Version of [`fn@crate::{{ function_name }}`] that takes a `{{ to_pascal_case function_name }}Options`. See [`fn@crate::{{ function_name }}`] for how to use.{{#if feature}}
// #[doc(cfg(feature = "{{feature}}"))]{{/if}}
pub fn {{ function_name }}_with_options(options: {{ to_pascal_case function_name }}Options) -> {{ to_pascal_case function_name }}Return {
{{ to_pascal_case function_name }}Return {}
}
-/// Options for [`{{ function_name }}_with_options`].{{#if feature}}
+/// Options for [`fn@crate::{{ function_name }}_with_options`].{{#if feature}}
// #[doc(cfg(feature = "{{feature}}"))]{{/if}}
#[derive(DefaultBuilder)]
pub struct {{ to_pascal_case function_name }}Options {}
@@ -45,6 +49,6 @@ impl Default for {{ to_pascal_case function_name }}Options {
}
}
-/// Return type of [`{{ function_name }}`].{{#if feature}}
+/// Return type of [`fn@crate::{{ function_name }}`].{{#if feature}}
// #[doc(cfg(feature = "{{feature}}"))]{{/if}}
pub struct {{ to_pascal_case function_name }}Return {}
\ No newline at end of file