mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-02-02 10:54:15 -05:00
added SSR feature
This commit is contained in:
parent
e2dcc81958
commit
bc94577ec6
58 changed files with 1475 additions and 719 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: cargo fmt --check
|
run: cargo fmt --check
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --all-features --tests -- -D warnings
|
run: cargo clippy --features storage,docs,math --tests -- -D warnings
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --all-features
|
run: cargo test --all-features
|
||||||
|
|
||||||
|
|
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -23,6 +23,6 @@ jobs:
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: cargo fmt --check
|
run: cargo fmt --check
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --all-features --tests -- -D warnings
|
run: cargo clippy --features storage,docs,math --tests -- -D warnings
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --all-features
|
run: cargo test --all-features
|
||||||
|
|
2
.idea/leptos-use.iml
generated
2
.idea/leptos-use.iml
generated
|
@ -43,6 +43,7 @@
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_document_visibility/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_document_visibility/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_window_focus/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_window_focus/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_window_scroll/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_window_scroll/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples/ssr/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_intl_number_format/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_intl_number_format/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_websocket/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_websocket/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/testtest/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/testtest/src" isTestSource="false" />
|
||||||
|
@ -69,6 +70,7 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_breakpoints/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_breakpoints/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_element_visibility/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_element_visibility/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_intersection_observer/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_intersection_observer/target" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/examples/ssr/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/testtest/target" />
|
<excludeFolder url="file://$MODULE_DIR$/testtest/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
|
@ -22,6 +22,7 @@ serde = { version = "1", optional = true }
|
||||||
serde_json = { version = "1", optional = true }
|
serde_json = { version = "1", optional = true }
|
||||||
paste = "1"
|
paste = "1"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
|
cfg-if = "1"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
@ -66,6 +67,7 @@ features = [
|
||||||
docs = []
|
docs = []
|
||||||
math = ["num"]
|
math = ["num"]
|
||||||
storage = ["serde", "serde_json", "web-sys/StorageEvent"]
|
storage = ["serde", "serde_json", "web-sys/StorageEvent"]
|
||||||
|
ssr = []
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
|
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
|
||||||
|
<a href="https://leptos-use.rs/server_side_rendering.html"><img src="https://img.shields.io/badge/-SSR-%236a214b" alt="SSR"></a>
|
||||||
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
|
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
|
||||||
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-41%20functions-%23EF3939" alt="41 Functions" /></a>
|
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-41%20functions-%23EF3939" alt="41 Functions" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
[Introduction](introduction.md)
|
[Introduction](introduction.md)
|
||||||
[Get Started](get_started.md)
|
[Get Started](get_started.md)
|
||||||
[Functions](functions.md)
|
[Functions](functions.md)
|
||||||
|
[Server-Side Rendering](server_side_rendering.md)
|
||||||
[Changelog](changelog.md)
|
[Changelog](changelog.md)
|
||||||
|
|
||||||
# @Storage
|
# @Storage
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
|
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
|
||||||
|
<a href="https://leptos-use.rs/server_side_rendering.html"><img src="https://img.shields.io/badge/-SSR-%236a214b" alt="SSR"></a>
|
||||||
<a href="./get_started.html"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
|
<a href="./get_started.html"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
|
||||||
<a href="./functions.html"><img src="https://img.shields.io/badge/-41%20functions-%23EF3939" alt="41 Functions" /></a>
|
<a href="./functions.html"><img src="https://img.shields.io/badge/-41%20functions-%23EF3939" alt="41 Functions" /></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
49
docs/book/src/server_side_rendering.md
Normal file
49
docs/book/src/server_side_rendering.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Server-Side Rendering
|
||||||
|
|
||||||
|
When using together with server-side rendering (SSR) you have to enable the feature `ssr` similar to
|
||||||
|
how you do it for `leptos`.
|
||||||
|
|
||||||
|
In your Cargo.toml file add the following:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
...
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hydrate = [
|
||||||
|
"leptos/hydrate",
|
||||||
|
...
|
||||||
|
]
|
||||||
|
ssr = [
|
||||||
|
...
|
||||||
|
"leptos/ssr",
|
||||||
|
...
|
||||||
|
"leptos-use/ssr" # add this
|
||||||
|
]
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Please see the `ssr` example in the examples folder for a simple working demonstration.
|
||||||
|
|
||||||
|
Many functions work differently on the server and on the client. If that's the case you will
|
||||||
|
find information about these differences in their respective docs under the section "Server-Side Rendering".
|
||||||
|
If you don't find that section, it means that the function works exactly the same on both, the client
|
||||||
|
and the server.
|
||||||
|
|
||||||
|
## Functions with Target Elements
|
||||||
|
|
||||||
|
A lot of functions like `use_event_listener` and `use_element_size` are only useful when a target HTML/SVG element is
|
||||||
|
available. This is not the case on the server. You can simply wrap them in `create_effect` which will cause them to
|
||||||
|
be only called in the browser.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
create_effect(
|
||||||
|
cx,
|
||||||
|
move |_| {
|
||||||
|
// window() doesn't work on the server
|
||||||
|
use_event_listener(cx, window(), "resize", move |_| {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
|
@ -1,6 +1,2 @@
|
||||||
[build]
|
[build]
|
||||||
rustflags = ["--cfg=web_sys_unstable_apis", "--cfg=has_std"]
|
rustflags = ["--cfg=web_sys_unstable_apis", "--cfg=has_std"]
|
||||||
|
|
||||||
[unstable]
|
|
||||||
build-std = ["std", "panic_abort", "core", "alloc"]
|
|
||||||
build-std-features = ["panic_immediate_abort"]
|
|
|
@ -38,6 +38,10 @@ members = [
|
||||||
"watch_throttled",
|
"watch_throttled",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
exclude = [
|
||||||
|
"ssr"
|
||||||
|
]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "leptos-use-examples"
|
name = "leptos-use-examples"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
14
examples/ssr/.gitignore
vendored
Normal file
14
examples/ssr/.gitignore
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
pkg
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# node e2e test tools and outputs
|
||||||
|
node_modules/
|
||||||
|
test-results/
|
||||||
|
end2end/playwright-report/
|
||||||
|
playwright/.cache/
|
100
examples/ssr/Cargo.toml
Normal file
100
examples/ssr/Cargo.toml
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
[package]
|
||||||
|
name = "start-axum"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { version = "0.6.4", optional = true }
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
console_log = "1"
|
||||||
|
cfg-if = "1"
|
||||||
|
leptos = { version = "0.4", features = ["nightly"] }
|
||||||
|
leptos_axum = { version = "0.4", optional = true }
|
||||||
|
leptos_meta = { version = "0.4", features = ["nightly"] }
|
||||||
|
leptos_router = { version = "0.4", features = ["nightly"] }
|
||||||
|
leptos-use = { path = "../..", features = ["storage"] }
|
||||||
|
log = "0.4"
|
||||||
|
simple_logger = "4"
|
||||||
|
tokio = { version = "1.25.0", optional = true }
|
||||||
|
tower = { version = "0.4.13", optional = true }
|
||||||
|
tower-http = { version = "0.4", features = ["fs"], optional = true }
|
||||||
|
wasm-bindgen = "=0.2.87"
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
tracing = { version = "0.1.37", optional = true }
|
||||||
|
http = "0.2.8"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||||
|
ssr = [
|
||||||
|
"dep:axum",
|
||||||
|
"dep:tokio",
|
||||||
|
"dep:tower",
|
||||||
|
"dep:tower-http",
|
||||||
|
"dep:leptos_axum",
|
||||||
|
"leptos/ssr",
|
||||||
|
"leptos_meta/ssr",
|
||||||
|
"leptos_router/ssr",
|
||||||
|
"dep:tracing",
|
||||||
|
"leptos-use/ssr"
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata.cargo-all-features]
|
||||||
|
denylist = ["axum", "tokio", "tower", "tower-http", "leptos_axum"]
|
||||||
|
skip_feature_sets = [["ssr", "hydrate"]]
|
||||||
|
|
||||||
|
[package.metadata.leptos]
|
||||||
|
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
||||||
|
output-name = "start-axum"
|
||||||
|
|
||||||
|
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
|
||||||
|
site-root = "target/site"
|
||||||
|
|
||||||
|
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
|
||||||
|
# Defaults to pkg
|
||||||
|
site-pkg-dir = "pkg"
|
||||||
|
|
||||||
|
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
|
||||||
|
style-file = "style/main.scss"
|
||||||
|
# Assets source dir. All files found here will be copied and synchronized to site-root.
|
||||||
|
# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir.
|
||||||
|
#
|
||||||
|
# Optional. Env: LEPTOS_ASSETS_DIR.
|
||||||
|
assets-dir = "public"
|
||||||
|
|
||||||
|
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
|
||||||
|
site-addr = "127.0.0.1:3000"
|
||||||
|
|
||||||
|
# The port to use for automatic reload monitoring
|
||||||
|
reload-port = 3001
|
||||||
|
|
||||||
|
# The browserlist query used for optimizing the CSS.
|
||||||
|
browserquery = "defaults"
|
||||||
|
|
||||||
|
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
|
||||||
|
watch = false
|
||||||
|
|
||||||
|
# The environment Leptos will run in, usually either "DEV" or "PROD"
|
||||||
|
env = "DEV"
|
||||||
|
|
||||||
|
# The features to use when compiling the bin target
|
||||||
|
#
|
||||||
|
# Optional. Can be over-ridden with the command line parameter --bin-features
|
||||||
|
bin-features = ["ssr"]
|
||||||
|
|
||||||
|
# If the --no-default-features flag should be used when compiling the bin target
|
||||||
|
#
|
||||||
|
# Optional. Defaults to false.
|
||||||
|
bin-default-features = false
|
||||||
|
|
||||||
|
# The features to use when compiling the lib target
|
||||||
|
#
|
||||||
|
# Optional. Can be over-ridden with the command line parameter --lib-features
|
||||||
|
lib-features = ["hydrate"]
|
||||||
|
|
||||||
|
# If the --no-default-features flag should be used when compiling the lib target
|
||||||
|
#
|
||||||
|
# Optional. Defaults to false.
|
||||||
|
lib-default-features = false
|
56
examples/ssr/README.md
Normal file
56
examples/ssr/README.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Leptos-Use SSR Example
|
||||||
|
|
||||||
|
## Running the example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo leptos watch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installing Additional Tools
|
||||||
|
|
||||||
|
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||||
|
|
||||||
|
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||||
|
2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||||
|
3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||||
|
4. `npm install -g sass` - install `dart-sass` (should be optional in future
|
||||||
|
|
||||||
|
## Compiling for Release
|
||||||
|
```bash
|
||||||
|
cargo leptos build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
Will generate your server binary in target/server/release and your site package in target/site
|
||||||
|
|
||||||
|
## Testing Your Project
|
||||||
|
```bash
|
||||||
|
cargo leptos end-to-end
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo leptos end-to-end --release
|
||||||
|
```
|
||||||
|
|
||||||
|
Cargo-leptos uses Playwright as the end-to-end test tool.
|
||||||
|
Tests are located in end2end/tests directory.
|
||||||
|
|
||||||
|
## Executing a Server on a Remote Machine Without the Toolchain
|
||||||
|
After running a `cargo leptos build --release` the minimum files needed are:
|
||||||
|
|
||||||
|
1. The server binary located in `target/server/release`
|
||||||
|
2. The `site` directory and all files within located in `target/site`
|
||||||
|
|
||||||
|
Copy these files to your remote server. The directory structure should be:
|
||||||
|
```text
|
||||||
|
start-axum
|
||||||
|
site/
|
||||||
|
```
|
||||||
|
Set the following environment variables (updating for your project as needed):
|
||||||
|
```text
|
||||||
|
LEPTOS_OUTPUT_NAME="start-axum"
|
||||||
|
LEPTOS_SITE_ROOT="site"
|
||||||
|
LEPTOS_SITE_PKG_DIR="pkg"
|
||||||
|
LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
||||||
|
LEPTOS_RELOAD_PORT="3001"
|
||||||
|
```
|
||||||
|
Finally, run the server binary.
|
BIN
examples/ssr/public/favicon.ico
Normal file
BIN
examples/ssr/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
2
examples/ssr/rust-toolchain.toml
Normal file
2
examples/ssr/rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
79
examples/ssr/src/app.rs
Normal file
79
examples/ssr/src/app.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use crate::error_template::{AppError, ErrorTemplate};
|
||||||
|
use leptos::ev::{keypress, KeyboardEvent};
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_meta::*;
|
||||||
|
use leptos_router::*;
|
||||||
|
use leptos_use::storage::use_local_storage;
|
||||||
|
use leptos_use::{
|
||||||
|
use_debounce_fn, use_event_listener, use_intl_number_format, UseIntlNumberFormatOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn App(cx: Scope) -> impl IntoView {
|
||||||
|
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||||
|
provide_meta_context(cx);
|
||||||
|
|
||||||
|
view! {
|
||||||
|
cx,
|
||||||
|
|
||||||
|
<Stylesheet id="leptos" href="/pkg/start-axum.css"/>
|
||||||
|
|
||||||
|
<Title text="Leptos-Use SSR Example"/>
|
||||||
|
|
||||||
|
<Router fallback=|cx| {
|
||||||
|
let mut outside_errors = Errors::default();
|
||||||
|
outside_errors.insert_with_default_key(AppError::NotFound);
|
||||||
|
view! { cx,
|
||||||
|
<ErrorTemplate outside_errors/>
|
||||||
|
}
|
||||||
|
.into_view(cx)
|
||||||
|
}>
|
||||||
|
<main>
|
||||||
|
<Routes>
|
||||||
|
<Route path="" view=|cx| view! { cx, <HomePage/> }/>
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
</Router>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the home page of your application.
|
||||||
|
#[component]
|
||||||
|
fn HomePage(cx: Scope) -> impl IntoView {
|
||||||
|
// Creates a reactive value to update the button
|
||||||
|
let (count, set_count, _) = use_local_storage(cx, "count-state", 0);
|
||||||
|
let on_click = move |_| set_count.update(|count| *count += 1);
|
||||||
|
|
||||||
|
let nf = use_intl_number_format(
|
||||||
|
UseIntlNumberFormatOptions::default().locale("zh-Hans-CN-u-nu-hanidec"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let zh_count = nf.format::<i32>(cx, count);
|
||||||
|
|
||||||
|
let (key, set_key) = create_signal(cx, "".to_string());
|
||||||
|
|
||||||
|
create_effect(cx, move |_| {
|
||||||
|
// window() doesn't work on the server
|
||||||
|
let _ = use_event_listener(cx, window(), keypress, move |evt: KeyboardEvent| {
|
||||||
|
set_key(evt.key())
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let (debounce_value, set_debounce_value) = create_signal(cx, "not called");
|
||||||
|
|
||||||
|
let debounced_fn = use_debounce_fn(
|
||||||
|
move || {
|
||||||
|
set_debounce_value("called");
|
||||||
|
},
|
||||||
|
2000.0,
|
||||||
|
);
|
||||||
|
debounced_fn();
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<h1>"Leptos-Use SSR Example"</h1>
|
||||||
|
<button on:click=on_click>"Click Me: " { count }</button>
|
||||||
|
<p>"Locale \"zh-Hans-CN-u-nu-hanidec\": " { zh_count }</p>
|
||||||
|
<p>"Press any key: " { key }</p>
|
||||||
|
<p>"Debounced called: " { debounce_value }</p>
|
||||||
|
}
|
||||||
|
}
|
76
examples/ssr/src/error_template.rs
Normal file
76
examples/ssr/src/error_template.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
use http::status::StatusCode;
|
||||||
|
use leptos::*;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
use leptos_axum::ResponseOptions;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Error)]
|
||||||
|
pub enum AppError {
|
||||||
|
#[error("Not Found")]
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppError {
|
||||||
|
pub fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
AppError::NotFound => StatusCode::NOT_FOUND,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A basic function to display errors served by the error boundaries.
|
||||||
|
// Feel free to do more complicated things here than just displaying the error.
|
||||||
|
#[component]
|
||||||
|
pub fn ErrorTemplate(
|
||||||
|
cx: Scope,
|
||||||
|
#[prop(optional)] outside_errors: Option<Errors>,
|
||||||
|
#[prop(optional)] errors: Option<RwSignal<Errors>>,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let errors = match outside_errors {
|
||||||
|
Some(e) => create_rw_signal(cx, e),
|
||||||
|
None => match errors {
|
||||||
|
Some(e) => e,
|
||||||
|
None => panic!("No Errors found and we expected errors!"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// Get Errors from Signal
|
||||||
|
let errors = errors.get();
|
||||||
|
|
||||||
|
// Downcast lets us take a type that implements `std::error::Error`
|
||||||
|
let errors: Vec<AppError> = errors
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())
|
||||||
|
.collect();
|
||||||
|
println!("Errors: {errors:#?}");
|
||||||
|
|
||||||
|
// Only the response code for the first error is actually sent from the server
|
||||||
|
// this may be customized by the specific application
|
||||||
|
cfg_if! { if #[cfg(feature="ssr")] {
|
||||||
|
let response = use_context::<ResponseOptions>(cx);
|
||||||
|
if let Some(response) = response {
|
||||||
|
response.set_status(errors[0].status_code());
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
view! {cx,
|
||||||
|
<h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1>
|
||||||
|
<For
|
||||||
|
// a function that returns the items we're iterating over; a signal is fine
|
||||||
|
each= move || {errors.clone().into_iter().enumerate()}
|
||||||
|
// a unique key for each item as a reference
|
||||||
|
key=|(index, _error)| *index
|
||||||
|
// renders each item to a view
|
||||||
|
view= move |cx, error| {
|
||||||
|
let error_string = error.1.to_string();
|
||||||
|
let error_code= error.1.status_code();
|
||||||
|
view! {
|
||||||
|
cx,
|
||||||
|
<h2>{error_code.to_string()}</h2>
|
||||||
|
<p>"Error: " {error_string}</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
40
examples/ssr/src/fileserv.rs
Normal file
40
examples/ssr/src/fileserv.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
use axum::{
|
||||||
|
body::{boxed, Body, BoxBody},
|
||||||
|
extract::State,
|
||||||
|
response::IntoResponse,
|
||||||
|
http::{Request, Response, StatusCode, Uri},
|
||||||
|
};
|
||||||
|
use axum::response::Response as AxumResponse;
|
||||||
|
use tower::ServiceExt;
|
||||||
|
use tower_http::services::ServeDir;
|
||||||
|
use leptos::*;
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
|
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
|
||||||
|
let root = options.site_root.clone();
|
||||||
|
let res = get_static_file(uri.clone(), &root).await.unwrap();
|
||||||
|
|
||||||
|
if res.status() == StatusCode::OK {
|
||||||
|
res.into_response()
|
||||||
|
} else {
|
||||||
|
let handler = leptos_axum::render_app_to_stream(options.to_owned(), move |cx| view!{cx, <App/>});
|
||||||
|
handler(req).await.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> {
|
||||||
|
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
|
||||||
|
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
|
||||||
|
// This path is relative to the cargo root
|
||||||
|
match ServeDir::new(root).oneshot(req).await {
|
||||||
|
Ok(res) => Ok(res.map(boxed)),
|
||||||
|
Err(err) => Err((
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Something went wrong: {err}"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
21
examples/ssr/src/lib.rs
Normal file
21
examples/ssr/src/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
pub mod app;
|
||||||
|
pub mod error_template;
|
||||||
|
pub mod fileserv;
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "hydrate")] {
|
||||||
|
use leptos::*;
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
use crate::app::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn hydrate() {
|
||||||
|
// initializes logging using the `log` crate
|
||||||
|
_ = console_log::init_with_level(log::Level::Debug);
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
leptos::mount_to_body(move |cx| {
|
||||||
|
view! { cx, <App/> }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
43
examples/ssr/src/main.rs
Normal file
43
examples/ssr/src/main.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
use axum::{routing::post, Router};
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
|
use start_axum::app::*;
|
||||||
|
use start_axum::fileserv::file_and_error_handler;
|
||||||
|
|
||||||
|
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
|
||||||
|
|
||||||
|
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
||||||
|
// For deployment these variables are:
|
||||||
|
// <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>
|
||||||
|
// Alternately a file can be specified such as Some("Cargo.toml")
|
||||||
|
// The file would need to be included with the executable when moved to deployment
|
||||||
|
let conf = get_configuration(None).await.unwrap();
|
||||||
|
let leptos_options = conf.leptos_options;
|
||||||
|
let addr = leptos_options.site_addr;
|
||||||
|
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
|
||||||
|
|
||||||
|
// 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, |cx| view! { cx, <App/> })
|
||||||
|
.fallback(file_and_error_handler)
|
||||||
|
.with_state(leptos_options);
|
||||||
|
|
||||||
|
// run our app with hyper
|
||||||
|
// `axum::Server` is a re-export of `hyper::Server`
|
||||||
|
log!("listening on http://{}", &addr);
|
||||||
|
axum::Server::bind(&addr)
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
pub fn main() {
|
||||||
|
// no client-side main function
|
||||||
|
// unless we want this to work with e.g., Trunk for a purely client-side app
|
||||||
|
// see lib.rs for hydration function instead
|
||||||
|
}
|
4
examples/ssr/style/main.scss
Normal file
4
examples/ssr/style/main.scss
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
// #![feature(doc_cfg)]
|
// #![feature(doc_cfg)]
|
||||||
//! Collection of essential Leptos utilities inspired by SolidJS USE / VueUse
|
//! Collection of essential Leptos utilities inspired by SolidJS USE / VueUse
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
#[cfg(feature = "docs")]
|
#[cfg(feature = "docs")]
|
||||||
pub mod docs;
|
pub mod docs;
|
||||||
|
@ -10,15 +12,13 @@ pub mod math;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
#[cfg(web_sys_unstable_apis)]
|
cfg_if! { if #[cfg(web_sys_unstable_apis)] {
|
||||||
mod use_element_size;
|
mod use_element_size;
|
||||||
#[cfg(web_sys_unstable_apis)]
|
|
||||||
mod use_resize_observer;
|
mod use_resize_observer;
|
||||||
|
|
||||||
#[cfg(web_sys_unstable_apis)]
|
|
||||||
pub use use_element_size::*;
|
pub use use_element_size::*;
|
||||||
#[cfg(web_sys_unstable_apis)]
|
|
||||||
pub use use_resize_observer::*;
|
pub use use_resize_observer::*;
|
||||||
|
}}
|
||||||
|
|
||||||
mod on_click_outside;
|
mod on_click_outside;
|
||||||
mod use_active_element;
|
mod use_active_element;
|
||||||
|
|
|
@ -45,6 +45,10 @@ static IOS_WORKAROUND: RwLock<bool> = RwLock::new(false);
|
||||||
/// which is **not** supported by IE 11, Edge 18 and below.
|
/// which is **not** supported by IE 11, Edge 18 and below.
|
||||||
/// If you are targeting these browsers, we recommend you to include
|
/// If you are targeting these browsers, we recommend you to include
|
||||||
/// [this code snippet](https://gist.github.com/sibbng/13e83b1dd1b733317ce0130ef07d4efd) on your project.
|
/// [this code snippet](https://gist.github.com/sibbng/13e83b1dd1b733317ce0130ef07d4efd) on your project.
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
pub fn on_click_outside<El, T, F>(cx: Scope, target: El, handler: F) -> impl FnOnce() + Clone
|
pub fn on_click_outside<El, T, F>(cx: Scope, target: El, handler: F) -> impl FnOnce() + Clone
|
||||||
where
|
where
|
||||||
El: Clone,
|
El: Clone,
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
|
||||||
|
|
||||||
use crate::utils::{CloneableFn, CloneableFnWithArg, FilterOptions};
|
use crate::utils::{CloneableFn, CloneableFnWithArg, FilterOptions};
|
||||||
use crate::{
|
use crate::{
|
||||||
filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions,
|
filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions,
|
||||||
ThrottleOptions, WatchOptions, WatchPausableReturn,
|
ThrottleOptions, WatchOptions, WatchPausableReturn,
|
||||||
};
|
};
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use js_sys::Reflect;
|
use js_sys::Reflect;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -144,6 +147,10 @@ const CUSTOM_STORAGE_EVENT_NAME: &str = "leptos-use-storage";
|
||||||
///
|
///
|
||||||
/// You can specify `debounce` or `throttle` options for limiting writes to storage.
|
/// You can specify `debounce` or `throttle` options for limiting writes to storage.
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this falls back to a `create_signal(cx, default)` and an empty remove function.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_local_storage`]
|
/// * [`use_local_storage`]
|
||||||
|
@ -188,6 +195,9 @@ where
|
||||||
|
|
||||||
let (data, set_data) = create_signal(cx, defaults.get_untracked());
|
let (data, set_data) = create_signal(cx, defaults.get_untracked());
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
let remove: Box<dyn CloneableFn> = Box::new(|| {});
|
||||||
|
} else {
|
||||||
let storage = storage_type.into_storage();
|
let storage = storage_type.into_storage();
|
||||||
|
|
||||||
let remove: Box<dyn CloneableFn> = match storage {
|
let remove: Box<dyn CloneableFn> = match storage {
|
||||||
|
@ -374,6 +384,7 @@ where
|
||||||
Box::new(move || {})
|
Box::new(move || {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
(data, set_data, move || remove.clone()())
|
(data, set_data, move || remove.clone()())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::use_event_listener_with_options;
|
use crate::use_event_listener_with_options;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use leptos::ev::{blur, focus};
|
use leptos::ev::{blur, focus};
|
||||||
use leptos::html::{AnyElement, ToHtmlElement};
|
use leptos::html::{AnyElement, ToHtmlElement};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -27,16 +30,24 @@ use web_sys::AddEventListenerOptions;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this returns a `Signal` that always contains the value `None`.
|
||||||
pub fn use_active_element(cx: Scope) -> Signal<Option<HtmlElement<AnyElement>>> {
|
pub fn use_active_element(cx: Scope) -> Signal<Option<HtmlElement<AnyElement>>> {
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
let get_active_element = || { None };
|
||||||
|
} else {
|
||||||
let get_active_element = move || {
|
let get_active_element = move || {
|
||||||
document()
|
document()
|
||||||
.active_element()
|
.active_element()
|
||||||
.map(|el| el.to_leptos_element(cx))
|
.map(|el| el.to_leptos_element(cx))
|
||||||
};
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
let (active_element, set_active_element) = create_signal(cx, get_active_element());
|
let (active_element, set_active_element) = create_signal(cx, get_active_element());
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let mut listener_options = AddEventListenerOptions::new();
|
let mut listener_options = AddEventListenerOptions::new();
|
||||||
listener_options.capture(true);
|
listener_options.capture(true);
|
||||||
|
|
||||||
|
@ -63,6 +74,7 @@ pub fn use_active_element(cx: Scope) -> Signal<Option<HtmlElement<AnyElement>>>
|
||||||
},
|
},
|
||||||
listener_options,
|
listener_options,
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
|
|
||||||
active_element.into()
|
active_element.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,11 @@ use std::hash::Hash;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Since internally this uses [`use_media_query`], which returns always `false` on the server,
|
||||||
|
/// the returned methods also will return `false`.
|
||||||
pub fn use_breakpoints<K: Eq + Hash + Debug + Clone>(
|
pub fn use_breakpoints<K: Eq + Hash + Debug + Clone>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
breakpoints: HashMap<K, u32>,
|
breakpoints: HashMap<K, u32>,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::fmt::{Display, Formatter};
|
||||||
use crate::core::StorageType;
|
use crate::core::StorageType;
|
||||||
use crate::use_preferred_dark;
|
use crate::use_preferred_dark;
|
||||||
use crate::utils::CloneableFnWithArg;
|
use crate::utils::CloneableFnWithArg;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -88,6 +89,10 @@ use wasm_bindgen::JsCast;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this will by default return `ColorMode::Light`. Persistence is disabled, of course.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_dark`]
|
/// * [`use_dark`]
|
||||||
|
@ -247,9 +252,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "storage"))]
|
|
||||||
/// Color modes
|
/// Color modes
|
||||||
#[derive(Clone, Default, PartialEq, Eq, Hash)]
|
#[derive(Clone, Default, PartialEq, Eq, Hash, Debug)]
|
||||||
|
#[cfg_attr(feature = "storage", derive(Serialize, Deserialize))]
|
||||||
pub enum ColorMode {
|
pub enum ColorMode {
|
||||||
#[default]
|
#[default]
|
||||||
Auto,
|
Auto,
|
||||||
|
@ -258,35 +263,7 @@ pub enum ColorMode {
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "storage"))]
|
cfg_if! { if #[cfg(feature = "storage")] {
|
||||||
fn get_store_signal(
|
|
||||||
cx: Scope,
|
|
||||||
initial_value: MaybeSignal<ColorMode>,
|
|
||||||
storage_signal: Option<RwSignal<ColorMode>>,
|
|
||||||
_storage_key: &String,
|
|
||||||
_storage_enabled: bool,
|
|
||||||
_storage: StorageType,
|
|
||||||
_listen_to_storage_changes: bool,
|
|
||||||
) -> (ReadSignal<ColorMode>, WriteSignal<ColorMode>) {
|
|
||||||
if let Some(storage_signal) = storage_signal {
|
|
||||||
storage_signal.split()
|
|
||||||
} else {
|
|
||||||
create_signal(cx, initial_value.get_untracked())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "storage")]
|
|
||||||
/// Color modes
|
|
||||||
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub enum ColorMode {
|
|
||||||
#[default]
|
|
||||||
Auto,
|
|
||||||
Light,
|
|
||||||
Dark,
|
|
||||||
Custom(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "storage")]
|
|
||||||
fn get_store_signal(
|
fn get_store_signal(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
initial_value: MaybeSignal<ColorMode>,
|
initial_value: MaybeSignal<ColorMode>,
|
||||||
|
@ -313,6 +290,23 @@ fn get_store_signal(
|
||||||
create_signal(cx, initial_value.get_untracked())
|
create_signal(cx, initial_value.get_untracked())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fn get_store_signal(
|
||||||
|
cx: Scope,
|
||||||
|
initial_value: MaybeSignal<ColorMode>,
|
||||||
|
storage_signal: Option<RwSignal<ColorMode>>,
|
||||||
|
_storage_key: &String,
|
||||||
|
_storage_enabled: bool,
|
||||||
|
_storage: StorageType,
|
||||||
|
_listen_to_storage_changes: bool,
|
||||||
|
) -> (ReadSignal<ColorMode>, WriteSignal<ColorMode>) {
|
||||||
|
if let Some(storage_signal) = storage_signal {
|
||||||
|
storage_signal.split()
|
||||||
|
} else {
|
||||||
|
create_signal(cx, initial_value.get_untracked())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
impl Display for ColorMode {
|
impl Display for ColorMode {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::core::ElementMaybeSignal;
|
use crate::core::ElementMaybeSignal;
|
||||||
use crate::{use_mutation_observer_with_options, watch, watch_with_options, WatchOptions};
|
use crate::{use_mutation_observer_with_options, watch, watch_with_options, WatchOptions};
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -69,7 +72,10 @@ use wasm_bindgen::{JsCast, JsValue};
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this simply returns `create_signal(cx, options.initial_value)`.
|
||||||
pub fn use_css_var(
|
pub fn use_css_var(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
prop: impl Into<MaybeSignal<String>>,
|
prop: impl Into<MaybeSignal<String>>,
|
||||||
|
@ -98,6 +104,7 @@ where
|
||||||
|
|
||||||
let (variable, set_variable) = create_signal(cx, initial_value.clone());
|
let (variable, set_variable) = create_signal(cx, initial_value.clone());
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let el_signal = (cx, target).into();
|
let el_signal = (cx, target).into();
|
||||||
let prop = prop.into();
|
let prop = prop.into();
|
||||||
|
|
||||||
|
@ -164,6 +171,7 @@ where
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
|
|
||||||
(variable, set_variable)
|
(variable, set_variable)
|
||||||
}
|
}
|
||||||
|
@ -192,6 +200,18 @@ where
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
impl Default for UseCssVarOptions<Option<web_sys::Element>, web_sys::Element> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
target: None,
|
||||||
|
initial_value: "".into(),
|
||||||
|
observe: false,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
impl Default for UseCssVarOptions<web_sys::Element, web_sys::Element> {
|
impl Default for UseCssVarOptions<web_sys::Element, web_sys::Element> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -202,3 +222,4 @@ impl Default for UseCssVarOptions<web_sys::Element, web_sys::Element> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
|
|
|
@ -31,7 +31,6 @@ use leptos::*;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
pub fn use_cycle_list<T, L>(
|
pub fn use_cycle_list<T, L>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
list: L,
|
list: L,
|
||||||
|
|
|
@ -67,6 +67,11 @@ use std::rc::Rc;
|
||||||
///
|
///
|
||||||
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
||||||
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Internally this uses `setTimeout` which is not supported on the server. So usually calling
|
||||||
|
/// a debounced function on the server will simply be ignored.
|
||||||
pub fn use_debounce_fn<F, R>(
|
pub fn use_debounce_fn<F, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>> + 'static,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::use_event_listener;
|
use crate::use_event_listener;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use leptos::ev::visibilitychange;
|
use leptos::ev::visibilitychange;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
|
@ -21,12 +24,24 @@ use leptos::*;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this returns a `Signal` that always contains the value `web_sys::VisibilityState::Hidden`.
|
||||||
pub fn use_document_visibility(cx: Scope) -> Signal<web_sys::VisibilityState> {
|
pub fn use_document_visibility(cx: Scope) -> Signal<web_sys::VisibilityState> {
|
||||||
let (visibility, set_visibility) = create_signal(cx, document().visibility_state());
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
let inital_visibility = web_sys::VisibilityState::Hidden;
|
||||||
|
} else {
|
||||||
|
let inital_visibility = document().visibility_state();
|
||||||
|
}}
|
||||||
|
|
||||||
|
let (visibility, set_visibility) = create_signal(cx, inital_visibility);
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let _ = use_event_listener(cx, document(), visibilitychange, move |_| {
|
let _ = use_event_listener(cx, document(), visibilitychange, move |_| {
|
||||||
set_visibility.set(document().visibility_state());
|
set_visibility.set(document().visibility_state());
|
||||||
});
|
});
|
||||||
|
}}
|
||||||
|
|
||||||
visibility.into()
|
visibility.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,10 @@ use web_sys::AddEventListenerOptions;
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
pub fn use_element_hover<El, T>(cx: Scope, el: El) -> Signal<bool>
|
pub fn use_element_hover<El, T>(cx: Scope, el: El) -> Signal<bool>
|
||||||
where
|
where
|
||||||
El: Clone,
|
El: Clone,
|
||||||
|
|
|
@ -39,6 +39,10 @@ use wasm_bindgen::JsCast;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// - [`use_resize_observer`]
|
/// - [`use_resize_observer`]
|
||||||
|
|
|
@ -31,6 +31,10 @@ use std::marker::PhantomData;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_intersection_observer`]
|
/// * [`use_intersection_observer`]
|
||||||
|
|
|
@ -76,11 +76,9 @@ use wasm_bindgen::JsCast;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Note if your components also run in SSR (Server Side Rendering), you might get errors
|
/// ## Server-Side Rendering
|
||||||
/// because DOM APIs like document and window are not available outside of the browser.
|
///
|
||||||
/// To avoid that you can put the logic inside a
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
/// [`create_effect`](https://docs.rs/leptos/latest/leptos/fn.create_effect.html) hook
|
|
||||||
/// which only runs client side.
|
|
||||||
pub fn use_event_listener<Ev, El, T, F>(
|
pub fn use_event_listener<Ev, El, T, F>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
target: El,
|
target: El,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::watch;
|
use crate::watch;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
@ -53,6 +56,9 @@ use wasm_bindgen::JsCast;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server only the signals work but no favicon will be changed obviously.
|
||||||
pub fn use_favicon(cx: Scope) -> (ReadSignal<Option<String>>, WriteSignal<Option<String>>) {
|
pub fn use_favicon(cx: Scope) -> (ReadSignal<Option<String>>, WriteSignal<Option<String>>) {
|
||||||
use_favicon_with_options(cx, UseFaviconOptions::default())
|
use_favicon_with_options(cx, UseFaviconOptions::default())
|
||||||
}
|
}
|
||||||
|
@ -68,14 +74,15 @@ pub fn use_favicon_with_options(
|
||||||
rel,
|
rel,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let link_selector = format!("link[rel*=\"{rel}\"]");
|
|
||||||
|
|
||||||
let (favicon, set_favicon) = create_signal(cx, new_icon.get_untracked());
|
let (favicon, set_favicon) = create_signal(cx, new_icon.get_untracked());
|
||||||
|
|
||||||
if matches!(&new_icon, MaybeSignal::Dynamic(_)) {
|
if matches!(&new_icon, MaybeSignal::Dynamic(_)) {
|
||||||
create_effect(cx, move |_| set_favicon.set(new_icon.get()));
|
create_effect(cx, move |_| set_favicon.set(new_icon.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
|
let link_selector = format!("link[rel*=\"{rel}\"]");
|
||||||
|
|
||||||
let apply_icon = move |icon: &String| {
|
let apply_icon = move |icon: &String| {
|
||||||
if let Some(head) = document().head() {
|
if let Some(head) = document().head() {
|
||||||
if let Ok(links) = head.query_selector_all(&link_selector) {
|
if let Ok(links) = head.query_selector_all(&link_selector) {
|
||||||
|
@ -101,6 +108,7 @@ pub fn use_favicon_with_options(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
|
|
||||||
(favicon, set_favicon)
|
(favicon, set_favicon)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,10 @@ use wasm_bindgen::prelude::*;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_element_visibility`]
|
/// * [`use_element_visibility`]
|
||||||
|
|
|
@ -28,6 +28,10 @@ use leptos::*;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this function will simply be ignored.
|
||||||
pub fn use_interval<N>(
|
pub fn use_interval<N>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
interval: N,
|
interval: N,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::utils::Pausable;
|
use crate::utils::Pausable;
|
||||||
use crate::watch;
|
use crate::watch;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::leptos_dom::helpers::IntervalHandle;
|
use leptos::leptos_dom::helpers::IntervalHandle;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -32,6 +35,10 @@ use std::time::Duration;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this function will simply be ignored.
|
||||||
pub fn use_interval_fn<CbFn, N>(
|
pub fn use_interval_fn<CbFn, N>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
callback: CbFn,
|
callback: CbFn,
|
||||||
|
@ -86,6 +93,7 @@ where
|
||||||
let interval = interval.into();
|
let interval = interval.into();
|
||||||
|
|
||||||
let resume = move || {
|
let resume = move || {
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let interval_value = interval.get();
|
let interval_value = interval.get();
|
||||||
if interval_value == 0 {
|
if interval_value == 0 {
|
||||||
return;
|
return;
|
||||||
|
@ -101,6 +109,7 @@ where
|
||||||
timer.set(
|
timer.set(
|
||||||
set_interval_with_handle(callback.clone(), Duration::from_millis(interval_value)).ok(),
|
set_interval_with_handle(callback.clone(), Duration::from_millis(interval_value)).ok(),
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
};
|
};
|
||||||
|
|
||||||
if immediate {
|
if immediate {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
|
||||||
|
|
||||||
use crate::utils::js_value_from_to_string;
|
use crate::utils::js_value_from_to_string;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use js_sys::Reflect;
|
use js_sys::Reflect;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -31,7 +34,7 @@ use wasm_bindgen::{JsCast, JsValue};
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ### Using locales
|
/// ## Using locales
|
||||||
///
|
///
|
||||||
/// This example shows some of the variations in localized number formats. In order to get the format
|
/// This example shows some of the variations in localized number formats. In order to get the format
|
||||||
/// of the language used in the user interface of your application, make sure to specify that language
|
/// of the language used in the user interface of your application, make sure to specify that language
|
||||||
|
@ -81,7 +84,7 @@ use wasm_bindgen::{JsCast, JsValue};
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ### Using options
|
/// ## Using options
|
||||||
///
|
///
|
||||||
/// The results can be customized in multiple ways.
|
/// The results can be customized in multiple ways.
|
||||||
///
|
///
|
||||||
|
@ -143,12 +146,20 @@ use wasm_bindgen::{JsCast, JsValue};
|
||||||
///
|
///
|
||||||
/// For an exhaustive list of options see [`UseIntlNumberFormatOptions`](https://docs.rs/leptos_use/latest/leptos_use/struct.UseIntlNumberFormatOptions.html).
|
/// For an exhaustive list of options see [`UseIntlNumberFormatOptions`](https://docs.rs/leptos_use/latest/leptos_use/struct.UseIntlNumberFormatOptions.html).
|
||||||
///
|
///
|
||||||
/// ### Formatting ranges
|
/// ## Formatting ranges
|
||||||
///
|
///
|
||||||
/// Apart from the `format` method, the `format_range` method can be used to format a range of numbers.
|
/// Apart from the `format` method, the `format_range` method can be used to format a range of numbers.
|
||||||
/// Please see [`UseIntlNumberFormatReturn::format_range`](https://docs.rs/leptos_use/latest/leptos_use/struct.UseIntlNumberFormatReturn.html#method.format_range)
|
/// Please see [`UseIntlNumberFormatReturn::format_range`](https://docs.rs/leptos_use/latest/leptos_use/struct.UseIntlNumberFormatReturn.html#method.format_range)
|
||||||
/// for details.
|
/// for details.
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Since `Intl.NumberFormat` is a JavaScript API it is not available on the server. That's why
|
||||||
|
/// it falls back to a simple call to `format!()` on the server.
|
||||||
pub fn use_intl_number_format(options: UseIntlNumberFormatOptions) -> UseIntlNumberFormatReturn {
|
pub fn use_intl_number_format(options: UseIntlNumberFormatOptions) -> UseIntlNumberFormatReturn {
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
UseIntlNumberFormatReturn
|
||||||
|
} else {
|
||||||
let number_format = js_sys::Intl::NumberFormat::new(
|
let number_format = js_sys::Intl::NumberFormat::new(
|
||||||
&js_sys::Array::from_iter(options.locales.iter().map(JsValue::from)),
|
&js_sys::Array::from_iter(options.locales.iter().map(JsValue::from)),
|
||||||
&js_sys::Object::from(options),
|
&js_sys::Object::from(options),
|
||||||
|
@ -157,6 +168,7 @@ pub fn use_intl_number_format(options: UseIntlNumberFormatOptions) -> UseIntlNum
|
||||||
UseIntlNumberFormatReturn {
|
UseIntlNumberFormatReturn {
|
||||||
js_intl_number_format: number_format,
|
js_intl_number_format: number_format,
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
@ -788,21 +800,32 @@ impl From<UseIntlNumberFormatOptions> for js_sys::Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
/// Return type of [`use_intl_number_format`].
|
||||||
|
pub struct UseIntlNumberFormatReturn;
|
||||||
|
} else {
|
||||||
/// Return type of [`use_intl_number_format`].
|
/// Return type of [`use_intl_number_format`].
|
||||||
pub struct UseIntlNumberFormatReturn {
|
pub struct UseIntlNumberFormatReturn {
|
||||||
/// The instance of [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat).
|
/// The instance of [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat).
|
||||||
pub js_intl_number_format: js_sys::Intl::NumberFormat,
|
pub js_intl_number_format: js_sys::Intl::NumberFormat,
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
impl UseIntlNumberFormatReturn {
|
impl UseIntlNumberFormatReturn {
|
||||||
/// Formats a number according to the [locale and formatting options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#parameters) of this `Intl.NumberFormat` object.
|
/// Formats a number according to the [locale and formatting options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#parameters) of this `Intl.NumberFormat` object.
|
||||||
/// See [`use_intl_number_format`] for more information.
|
/// See [`use_intl_number_format`] for more information.
|
||||||
pub fn format<N>(&self, cx: Scope, number: impl Into<MaybeSignal<N>>) -> Signal<String>
|
pub fn format<N>(&self, cx: Scope, number: impl Into<MaybeSignal<N>>) -> Signal<String>
|
||||||
where
|
where
|
||||||
N: Clone + 'static,
|
N: Clone + Display + 'static,
|
||||||
js_sys::Number: From<N>,
|
js_sys::Number: From<N>,
|
||||||
{
|
{
|
||||||
let number = number.into();
|
let number = number.into();
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
Signal::derive(cx, move || {
|
||||||
|
format!("{}", number.get())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
let number_format = self.js_intl_number_format.clone();
|
let number_format = self.js_intl_number_format.clone();
|
||||||
|
|
||||||
Signal::derive(cx, move || {
|
Signal::derive(cx, move || {
|
||||||
|
@ -815,6 +838,7 @@ impl UseIntlNumberFormatReturn {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats a range of numbers according to the locale and formatting options of this `Intl.NumberFormat` object.
|
/// Formats a range of numbers according to the locale and formatting options of this `Intl.NumberFormat` object.
|
||||||
|
@ -870,13 +894,19 @@ impl UseIntlNumberFormatReturn {
|
||||||
end: impl Into<MaybeSignal<NEnd>>,
|
end: impl Into<MaybeSignal<NEnd>>,
|
||||||
) -> Signal<String>
|
) -> Signal<String>
|
||||||
where
|
where
|
||||||
NStart: Clone + 'static,
|
NStart: Clone + Display + 'static,
|
||||||
NEnd: Clone + 'static,
|
NEnd: Clone + Display + 'static,
|
||||||
js_sys::Number: From<NStart>,
|
js_sys::Number: From<NStart>,
|
||||||
js_sys::Number: From<NEnd>,
|
js_sys::Number: From<NEnd>,
|
||||||
{
|
{
|
||||||
let start = start.into();
|
let start = start.into();
|
||||||
let end = end.into();
|
let end = end.into();
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
Signal::derive(cx, move || {
|
||||||
|
format!("{} - {}", start.get(), end.get())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
let number_format = self.js_intl_number_format.clone();
|
let number_format = self.js_intl_number_format.clone();
|
||||||
|
|
||||||
Signal::derive(cx, move || {
|
Signal::derive(cx, move || {
|
||||||
|
@ -894,6 +924,7 @@ impl UseIntlNumberFormatReturn {
|
||||||
|
|
||||||
"".to_string()
|
"".to_string()
|
||||||
})
|
})
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : Allows locale-aware formatting of strings produced by this `Intl.NumberFormat` object.
|
// TODO : Allows locale-aware formatting of strings produced by this `Intl.NumberFormat` object.
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
|
||||||
|
|
||||||
use crate::use_event_listener;
|
use crate::use_event_listener;
|
||||||
use crate::utils::CloneableFnMutWithArg;
|
use crate::utils::CloneableFnMutWithArg;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use leptos::ev::change;
|
use leptos::ev::change;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -28,6 +31,10 @@ use std::rc::Rc;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this functions returns a Signal that is always `false`.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_preferred_dark`]
|
/// * [`use_preferred_dark`]
|
||||||
|
@ -37,6 +44,7 @@ pub fn use_media_query(cx: Scope, query: impl Into<MaybeSignal<String>>) -> Sign
|
||||||
|
|
||||||
let (matches, set_matches) = create_signal(cx, false);
|
let (matches, set_matches) = create_signal(cx, false);
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let media_query: Rc<RefCell<Option<web_sys::MediaQueryList>>> = Rc::new(RefCell::new(None));
|
let media_query: Rc<RefCell<Option<web_sys::MediaQueryList>>> = Rc::new(RefCell::new(None));
|
||||||
let remove_listener: RemoveListener = Rc::new(RefCell::new(None));
|
let remove_listener: RemoveListener = Rc::new(RefCell::new(None));
|
||||||
|
|
||||||
|
@ -87,6 +95,7 @@ pub fn use_media_query(cx: Scope, query: impl Into<MaybeSignal<String>>) -> Sign
|
||||||
create_effect(cx, move |_| update());
|
create_effect(cx, move |_| update());
|
||||||
|
|
||||||
on_cleanup(cx, cleanup);
|
on_cleanup(cx, cleanup);
|
||||||
|
}}
|
||||||
|
|
||||||
matches.into()
|
matches.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::core::{ElementMaybeSignal, Position};
|
use crate::core::{ElementMaybeSignal, Position};
|
||||||
use crate::use_event_listener_with_options;
|
use crate::use_event_listener_with_options;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::ev::{dragover, mousemove, touchend, touchmove, touchstart};
|
use leptos::ev::{dragover, mousemove, touchend, touchmove, touchstart};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -83,6 +86,10 @@ use web_sys::AddEventListenerOptions;
|
||||||
/// view! { cx, <div node_ref=element></div> }
|
/// view! { cx, <div node_ref=element></div> }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this returns simple `Signal`s with the `initial_value`s.
|
||||||
pub fn use_mouse(cx: Scope) -> UseMouseReturn {
|
pub fn use_mouse(cx: Scope) -> UseMouseReturn {
|
||||||
use_mouse_with_options(cx, Default::default())
|
use_mouse_with_options(cx, Default::default())
|
||||||
}
|
}
|
||||||
|
@ -154,6 +161,7 @@ where
|
||||||
|
|
||||||
// TODO : event filters?
|
// TODO : event filters?
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let target = options.target;
|
let target = options.target;
|
||||||
let mut event_listener_options = AddEventListenerOptions::new();
|
let mut event_listener_options = AddEventListenerOptions::new();
|
||||||
event_listener_options.passive(true);
|
event_listener_options.passive(true);
|
||||||
|
@ -197,6 +205,7 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
UseMouseReturn {
|
UseMouseReturn {
|
||||||
x,
|
x,
|
||||||
|
@ -235,6 +244,20 @@ where
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
impl Default for UseMouseOptions<Option<web_sys::Window>, web_sys::Window, UseMouseEventExtractorDefault> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
coord_type: UseMouseCoordType::<UseMouseEventExtractorDefault>::default(),
|
||||||
|
target: None,
|
||||||
|
touch: true,
|
||||||
|
reset_on_touch_ends: false,
|
||||||
|
initial_value: Position { x: 0.0, y: 0.0 },
|
||||||
|
_marker: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
impl Default for UseMouseOptions<web_sys::Window, web_sys::Window, UseMouseEventExtractorDefault> {
|
impl Default for UseMouseOptions<web_sys::Window, web_sys::Window, UseMouseEventExtractorDefault> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -247,6 +270,7 @@ impl Default for UseMouseOptions<web_sys::Window, web_sys::Window, UseMouseEvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
/// Defines how to get the coordinates from the event.
|
/// Defines how to get the coordinates from the event.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -45,6 +45,10 @@ use web_sys::MutationObserverInit;
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
pub fn use_mutation_observer<El, T, F>(
|
pub fn use_mutation_observer<El, T, F>(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
target: El,
|
target: El,
|
||||||
|
|
|
@ -19,6 +19,10 @@ use std::fmt::Display;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this returns a `Signal` that always contains the value `PreferredContrast::NoPreference`.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_media_query`]
|
/// * [`use_media_query`]
|
||||||
|
|
|
@ -18,6 +18,10 @@ use leptos::*;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this functions returns a Signal that is always `false`.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`use_media_query`]
|
/// * [`use_media_query`]
|
||||||
|
|
|
@ -43,6 +43,11 @@ use wasm_bindgen::prelude::*;
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// - [`use_element_size`]
|
/// - [`use_element_size`]
|
||||||
|
|
|
@ -164,6 +164,10 @@ use wasm_bindgen::JsCast;
|
||||||
/// # }
|
/// # }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Please refer to ["Functions with Target Elements"](https://leptos-use.rs/server_side_rendering.html#functions-with-target-elements)
|
||||||
pub fn use_scroll<El, T>(cx: Scope, element: El) -> UseScrollReturn
|
pub fn use_scroll<El, T>(cx: Scope, element: El) -> UseScrollReturn
|
||||||
where
|
where
|
||||||
El: Clone,
|
El: Clone,
|
||||||
|
|
|
@ -63,6 +63,11 @@ pub use crate::utils::ThrottleOptions;
|
||||||
///
|
///
|
||||||
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
||||||
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// Internally this uses `setTimeout` which is not supported on the server. So usually calling
|
||||||
|
/// a throttled function on the server will simply be ignored.
|
||||||
pub fn use_throttle_fn<F, R>(
|
pub fn use_throttle_fn<F, R>(
|
||||||
func: F,
|
func: F,
|
||||||
ms: impl Into<MaybeSignal<f64>> + 'static,
|
ms: impl Into<MaybeSignal<f64>> + 'static,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports, dead_code))]
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ use crate::utils::CloneableFnWithArg;
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos::*;
|
/// # use leptos::*;
|
||||||
/// # use leptos_use::websocket::*;
|
/// # use leptos_use::{use_websocket, UseWebSocketReadyState, UseWebsocketReturn};
|
||||||
/// #
|
/// #
|
||||||
/// # #[component]
|
/// # #[component]
|
||||||
/// # fn Demo(cx: Scope) -> impl IntoView {
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
@ -73,7 +75,10 @@ use crate::utils::CloneableFnWithArg;
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
// #[doc(cfg(feature = "websocket"))]
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server the returned functions amount to noops.
|
||||||
pub fn use_websocket(
|
pub fn use_websocket(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
url: String,
|
url: String,
|
||||||
|
@ -87,7 +92,6 @@ pub fn use_websocket(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use.
|
/// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use.
|
||||||
// #[doc(cfg(feature = "websocket"))]
|
|
||||||
pub fn use_websocket_with_options(
|
pub fn use_websocket_with_options(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
url: String,
|
url: String,
|
||||||
|
@ -103,24 +107,26 @@ pub fn use_websocket_with_options(
|
||||||
let (message_bytes, set_message_bytes) = create_signal(cx, None);
|
let (message_bytes, set_message_bytes) = create_signal(cx, None);
|
||||||
let ws_ref: StoredValue<Option<WebSocket>> = store_value(cx, None);
|
let ws_ref: StoredValue<Option<WebSocket>> = store_value(cx, None);
|
||||||
|
|
||||||
|
let reconnect_limit = options.reconnect_limit.unwrap_or(3);
|
||||||
|
|
||||||
|
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(cx, None);
|
||||||
|
let manual = options.manual;
|
||||||
|
|
||||||
|
let reconnect_times_ref: StoredValue<u64> = store_value(cx, 0);
|
||||||
|
let unmounted_ref = store_value(cx, false);
|
||||||
|
|
||||||
|
let connect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(cx, None);
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let on_open_ref = store_value(cx, options.on_open);
|
let on_open_ref = store_value(cx, options.on_open);
|
||||||
let on_message_ref = store_value(cx, options.on_message);
|
let on_message_ref = store_value(cx, options.on_message);
|
||||||
let on_message_bytes_ref = store_value(cx, options.on_message_bytes);
|
let on_message_bytes_ref = store_value(cx, options.on_message_bytes);
|
||||||
let on_error_ref = store_value(cx, options.on_error);
|
let on_error_ref = store_value(cx, options.on_error);
|
||||||
let on_close_ref = store_value(cx, options.on_close);
|
let on_close_ref = store_value(cx, options.on_close);
|
||||||
|
|
||||||
let reconnect_limit = options.reconnect_limit.unwrap_or(3);
|
|
||||||
let reconnect_interval = options.reconnect_interval.unwrap_or(3 * 1000);
|
let reconnect_interval = options.reconnect_interval.unwrap_or(3 * 1000);
|
||||||
|
|
||||||
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(cx, None);
|
|
||||||
let manual = options.manual;
|
|
||||||
let protocols = options.protocols;
|
let protocols = options.protocols;
|
||||||
|
|
||||||
let reconnect_times_ref: StoredValue<u64> = store_value(cx, 0);
|
|
||||||
let unmounted_ref = store_value(cx, false);
|
|
||||||
|
|
||||||
let connect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(cx, None);
|
|
||||||
|
|
||||||
let reconnect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(cx, None);
|
let reconnect_ref: StoredValue<Option<Rc<dyn Fn()>>> = store_value(cx, None);
|
||||||
reconnect_ref.set_value({
|
reconnect_ref.set_value({
|
||||||
let ws = ws_ref.get_value();
|
let ws = ws_ref.get_value();
|
||||||
|
@ -270,6 +276,7 @@ pub fn use_websocket_with_options(
|
||||||
ws_ref.set_value(Some(web_socket));
|
ws_ref.set_value(Some(web_socket));
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
}}
|
||||||
|
|
||||||
// Send text (String)
|
// Send text (String)
|
||||||
let send = {
|
let send = {
|
||||||
|
@ -283,29 +290,26 @@ pub fn use_websocket_with_options(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send bytes
|
// Send bytes
|
||||||
let send_bytes = {
|
let send_bytes = move |data: Vec<u8>| {
|
||||||
move |data: Vec<u8>| {
|
|
||||||
if ready_state.get() == UseWebSocketReadyState::Open {
|
if ready_state.get() == UseWebSocketReadyState::Open {
|
||||||
if let Some(web_socket) = ws_ref.get_value() {
|
if let Some(web_socket) = ws_ref.get_value() {
|
||||||
let _ = web_socket.send_with_u8_array(&data);
|
let _ = web_socket.send_with_u8_array(&data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Open connection
|
// Open connection
|
||||||
let open = {
|
let open = move || {
|
||||||
move || {
|
|
||||||
reconnect_times_ref.set_value(0);
|
reconnect_times_ref.set_value(0);
|
||||||
if let Some(connect) = connect_ref.get_value() {
|
if let Some(connect) = connect_ref.get_value() {
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close connection
|
// Close connection
|
||||||
let close = {
|
let close = {
|
||||||
reconnect_timer_ref.set_value(None);
|
reconnect_timer_ref.set_value(None);
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
reconnect_times_ref.set_value(reconnect_limit);
|
reconnect_times_ref.set_value(reconnect_limit);
|
||||||
if let Some(web_socket) = ws_ref.get_value() {
|
if let Some(web_socket) = ws_ref.get_value() {
|
||||||
|
@ -319,8 +323,6 @@ pub fn use_websocket_with_options(
|
||||||
if !manual {
|
if !manual {
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|| ()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// clean up (unmount)
|
// clean up (unmount)
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::use_event_listener;
|
use crate::use_event_listener;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use leptos::ev::{blur, focus};
|
use leptos::ev::{blur, focus};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
|
@ -22,11 +25,23 @@ use leptos::*;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this returns a `Signal` that is always `true`.
|
||||||
pub fn use_window_focus(cx: Scope) -> Signal<bool> {
|
pub fn use_window_focus(cx: Scope) -> Signal<bool> {
|
||||||
let (focused, set_focused) = create_signal(cx, document().has_focus().unwrap_or_default());
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
let initial_focus = true;
|
||||||
|
} else {
|
||||||
|
let initial_focus = document().has_focus().unwrap_or_default();
|
||||||
|
}}
|
||||||
|
|
||||||
|
let (focused, set_focused) = create_signal(cx, initial_focus);
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let _ = use_event_listener(cx, window(), blur, move |_| set_focused.set(false));
|
let _ = use_event_listener(cx, window(), blur, move |_| set_focused.set(false));
|
||||||
let _ = use_event_listener(cx, window(), focus, move |_| set_focused.set(true));
|
let _ = use_event_listener(cx, window(), focus, move |_| set_focused.set(true));
|
||||||
|
}}
|
||||||
|
|
||||||
focused.into()
|
focused.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::use_event_listener_with_options;
|
use crate::use_event_listener_with_options;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use leptos::ev::scroll;
|
use leptos::ev::scroll;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use web_sys::AddEventListenerOptions;
|
use web_sys::AddEventListenerOptions;
|
||||||
|
@ -22,10 +25,22 @@ use web_sys::AddEventListenerOptions;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this returns `Signal`s that are always `0.0`.
|
||||||
pub fn use_window_scroll(cx: Scope) -> (Signal<f64>, Signal<f64>) {
|
pub fn use_window_scroll(cx: Scope) -> (Signal<f64>, Signal<f64>) {
|
||||||
let (x, set_x) = create_signal(cx, window().scroll_x().unwrap_or_default());
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
let (y, set_y) = create_signal(cx, window().scroll_y().unwrap_or_default());
|
let initial_x = 0.0;
|
||||||
|
let initial_y = 0.0;
|
||||||
|
} else {
|
||||||
|
let initial_x = window().scroll_x().unwrap_or_default();
|
||||||
|
let initial_y = window().scroll_y().unwrap_or_default();
|
||||||
|
}}
|
||||||
|
let (x, set_x) = create_signal(cx, initial_x);
|
||||||
|
let (y, set_y) = create_signal(cx, initial_y);
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let mut options = AddEventListenerOptions::new();
|
let mut options = AddEventListenerOptions::new();
|
||||||
options.capture(false);
|
options.capture(false);
|
||||||
options.passive(true);
|
options.passive(true);
|
||||||
|
@ -40,6 +55,7 @@ pub fn use_window_scroll(cx: Scope) -> (Signal<f64>, Signal<f64>) {
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
|
|
||||||
(x.into(), y.into())
|
(x.into(), y.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::utils::CloneableFnWithReturn;
|
use crate::utils::CloneableFnWithReturn;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::leptos_dom::helpers::TimeoutHandle;
|
use leptos::leptos_dom::helpers::TimeoutHandle;
|
||||||
use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked};
|
use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked};
|
||||||
|
@ -56,6 +59,7 @@ where
|
||||||
return Rc::clone(&last_return_value);
|
return Rc::clone(&last_return_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
// Create the max_timer. Clears the regular timer on invoke
|
// Create the max_timer. Clears the regular timer on invoke
|
||||||
if let Some(max_duration) = max_duration {
|
if let Some(max_duration) = max_duration {
|
||||||
if max_timer.get().is_none() {
|
if max_timer.get().is_none() {
|
||||||
|
@ -87,6 +91,7 @@ where
|
||||||
)
|
)
|
||||||
.ok(),
|
.ok(),
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
|
|
||||||
Rc::clone(&last_return_value)
|
Rc::clone(&last_return_value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
||||||
|
|
||||||
use crate::utils::CloneableFnWithReturn;
|
use crate::utils::CloneableFnWithReturn;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use js_sys::Date;
|
use js_sys::Date;
|
||||||
use leptos::leptos_dom::helpers::TimeoutHandle;
|
use leptos::leptos_dom::helpers::TimeoutHandle;
|
||||||
|
@ -72,6 +75,7 @@ where
|
||||||
last_exec.set(Date::now());
|
last_exec.set(Date::now());
|
||||||
invoke();
|
invoke();
|
||||||
} else if options.trailing {
|
} else if options.trailing {
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
let last_exec = Rc::clone(&last_exec);
|
let last_exec = Rc::clone(&last_exec);
|
||||||
let is_leading = Rc::clone(&is_leading);
|
let is_leading = Rc::clone(&is_leading);
|
||||||
timer.set(
|
timer.set(
|
||||||
|
@ -86,8 +90,10 @@ where
|
||||||
)
|
)
|
||||||
.ok(),
|
.ok(),
|
||||||
);
|
);
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
||||||
if !options.leading && timer.get().is_none() {
|
if !options.leading && timer.get().is_none() {
|
||||||
let is_leading = Rc::clone(&is_leading);
|
let is_leading = Rc::clone(&is_leading);
|
||||||
timer.set(
|
timer.set(
|
||||||
|
@ -100,6 +106,7 @@ where
|
||||||
.ok(),
|
.ok(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
is_leading.set(false);
|
is_leading.set(false);
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,12 @@ use std::rc::Rc;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this works just fine except if you throttle or debounce in which case the callback
|
||||||
|
/// will never be called except if you set `immediate` to `true` in which case the callback will be
|
||||||
|
/// called exactly once.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`watch_throttled`]
|
/// * [`watch_throttled`]
|
||||||
|
|
|
@ -60,6 +60,12 @@ use leptos::*;
|
||||||
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
||||||
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server the callback
|
||||||
|
/// will never be called except if you set `immediate` to `true` in which case the callback will be
|
||||||
|
/// called exactly once.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`watch`]
|
/// * [`watch`]
|
||||||
|
|
|
@ -44,6 +44,12 @@ use leptos::*;
|
||||||
///
|
///
|
||||||
/// There's also [`watch_pausable_with_options`] which takes the same options as [`watch`].
|
/// There's also [`watch_pausable_with_options`] which takes the same options as [`watch`].
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this works just fine except if you throttle or debounce in which case the callback
|
||||||
|
/// will never be called except if you set `immediate` to `true` in which case the callback will be
|
||||||
|
/// called exactly once.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`watch`]
|
/// * [`watch`]
|
||||||
|
|
|
@ -60,6 +60,12 @@ use leptos::*;
|
||||||
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
|
||||||
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
/// - [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||||
///
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server the callback
|
||||||
|
/// will never be called except if you set `immediate` to `true` in which case the callback will be
|
||||||
|
/// called exactly once.
|
||||||
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`watch`]
|
/// * [`watch`]
|
||||||
|
|
|
@ -76,6 +76,12 @@ use leptos::*;
|
||||||
/// # view! { cx, }
|
/// # view! { cx, }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Server-Side Rendering
|
||||||
|
///
|
||||||
|
/// On the server this works just fine except if you throttle or debounce in which case the callback
|
||||||
|
/// will never be called except if you set `immediate` to `true` in which case the callback will be
|
||||||
|
/// called exactly once.
|
||||||
pub fn whenever<T, DFn, CFn>(cx: Scope, source: DFn, callback: CFn) -> impl Fn() + Clone
|
pub fn whenever<T, DFn, CFn>(cx: Scope, source: DFn, callback: CFn) -> impl Fn() + Clone
|
||||||
where
|
where
|
||||||
DFn: Fn() -> bool + 'static,
|
DFn: Fn() -> bool + 'static,
|
||||||
|
|
Loading…
Add table
Reference in a new issue