mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-27 16:54:41 -05:00
Compare commits
72 Commits
custom-vie
...
2182
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1632efa09 | ||
|
|
d475b213d7 | ||
|
|
b932bd5e04 | ||
|
|
a0638b786c | ||
|
|
1033133d3a | ||
|
|
ba40560ad7 | ||
|
|
78dc8b4410 | ||
|
|
c6bf525270 | ||
|
|
fcdfd617f5 | ||
|
|
d9c8d4ff66 | ||
|
|
621f112f4c | ||
|
|
cfe2341dec | ||
|
|
5ed2cc9596 | ||
|
|
47b07b0143 | ||
|
|
0793d56648 | ||
|
|
2dde9738b2 | ||
|
|
1b276e1e02 | ||
|
|
9771619b0d | ||
|
|
382a36406c | ||
|
|
a898d3f2f7 | ||
|
|
951f4a73ee | ||
|
|
2a26a648ba | ||
|
|
66d7cb5d12 | ||
|
|
aa49ad760b | ||
|
|
3469e9335c | ||
|
|
62408d9202 | ||
|
|
db1c15f4e4 | ||
|
|
3f751551a8 | ||
|
|
2470036f57 | ||
|
|
e01dfbf497 | ||
|
|
49c1661f92 | ||
|
|
b9dfd9a5ae | ||
|
|
17a150b3bf | ||
|
|
c860f524ad | ||
|
|
20af4928b2 | ||
|
|
7b62ad44d2 | ||
|
|
5657abc07d | ||
|
|
9fc351ceac | ||
|
|
893d47f1c5 | ||
|
|
3c2a2304e4 | ||
|
|
3de4b7b982 | ||
|
|
36957cb569 | ||
|
|
27dbadb7d2 | ||
|
|
84590b98ed | ||
|
|
0222182286 | ||
|
|
7ed4d08dab | ||
|
|
4fa2e58551 | ||
|
|
5fe58369f5 | ||
|
|
10860ebb1e | ||
|
|
887eb99cf6 | ||
|
|
9f99571b28 | ||
|
|
b3e2040ec9 | ||
|
|
693861434c | ||
|
|
d1248d3faf | ||
|
|
f1fae63064 | ||
|
|
f7053ac960 | ||
|
|
8670365594 | ||
|
|
e0cc6fd7b9 | ||
|
|
84457b45ff | ||
|
|
8c959d3e24 | ||
|
|
288da232ac | ||
|
|
ef52e8620b | ||
|
|
a8c5ce2722 | ||
|
|
a40ba9a39e | ||
|
|
83a110fb85 | ||
|
|
5deedf721e | ||
|
|
0ae67cf122 | ||
|
|
97e0222061 | ||
|
|
a7aedee4fd | ||
|
|
f180941b7b | ||
|
|
83cb3cb764 | ||
|
|
f174688974 |
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "cargo"
|
||||
directories:
|
||||
- "/"
|
||||
- "/examples/*"
|
||||
- "/benchmarks"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
2
.github/workflows/ci-semver.yml
vendored
2
.github/workflows/ci-semver.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
test:
|
||||
needs: [get-leptos-changed]
|
||||
if: needs.get-leptos-changed.outputs.leptos_changed == 'true' && github.event.pull_request.labels[0].name != 'breaking'
|
||||
if: github.event.pull_request.labels[0].name == 'semver' # needs.get-leptos-changed.outputs.leptos_changed == 'true' && github.event.pull_request.labels[0].name != 'breaking'
|
||||
name: Run semver check (nightly-2024-08-01)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
- name: Get example project directories that changed
|
||||
id: changed-dirs
|
||||
uses: tj-actions/changed-files@v41
|
||||
uses: tj-actions/changed-files@v44
|
||||
with:
|
||||
dir_names: true
|
||||
dir_names_max_depth: "2"
|
||||
|
||||
2
.github/workflows/get-example-changed.yml
vendored
2
.github/workflows/get-example-changed.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Get example files that changed
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v43
|
||||
uses: tj-actions/changed-files@v44
|
||||
with:
|
||||
files: |
|
||||
examples/**
|
||||
|
||||
2
.github/workflows/get-leptos-changed.yml
vendored
2
.github/workflows/get-leptos-changed.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Get source files that changed
|
||||
id: changed-source
|
||||
uses: tj-actions/changed-files@v43
|
||||
uses: tj-actions/changed-files@v44
|
||||
with:
|
||||
files: |
|
||||
any_error/**
|
||||
|
||||
2
.github/workflows/run-cargo-make-task.yml
vendored
2
.github/workflows/run-cargo-make-task.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
|
||||
57
Cargo.toml
57
Cargo.toml
@@ -37,38 +37,39 @@ members = [
|
||||
"router_macro",
|
||||
"any_error",
|
||||
]
|
||||
exclude = ["benchmarks", "examples"]
|
||||
exclude = ["benchmarks", "examples", "projects"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.7.0-beta"
|
||||
rust-version = "1.75"
|
||||
version = "0.7.0-beta2"
|
||||
edition = "2021"
|
||||
rust-version = "1.76"
|
||||
|
||||
[workspace.dependencies]
|
||||
throw_error = { path = "./any_error/", version = "0.2.0-beta" }
|
||||
any_spawner = { path = "./any_spawner/", version = "0.1" }
|
||||
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1" }
|
||||
either_of = { path = "./either_of/", version = "0.1" }
|
||||
hydration_context = { path = "./hydration_context", version = "0.2.0-beta" }
|
||||
leptos = { path = "./leptos", version = "0.7.0-beta" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.7.0-beta" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.7.0-beta" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0-beta" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0-beta" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.7.0-beta" }
|
||||
leptos_router = { path = "./router", version = "0.7.0-beta" }
|
||||
leptos_router_macro = { path = "./router_macro", version = "0.7.0-beta" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.7.0-beta" }
|
||||
leptos_meta = { path = "./meta", version = "0.7.0-beta" }
|
||||
next_tuple = { path = "./next_tuple", version = "0.1.0-beta" }
|
||||
oco_ref = { path = "./oco", version = "0.2" }
|
||||
or_poisoned = { path = "./or_poisoned", version = "0.1" }
|
||||
reactive_graph = { path = "./reactive_graph", version = "0.1.0-beta" }
|
||||
reactive_stores = { path = "./reactive_stores", version = "0.1.0-beta" }
|
||||
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0-beta" }
|
||||
server_fn = { path = "./server_fn", version = "0.7.0-beta" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0-beta" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0-beta" }
|
||||
tachys = { path = "./tachys", version = "0.1.0-beta" }
|
||||
throw_error = { path = "./any_error/", version = "0.2.0-beta2" }
|
||||
any_spawner = { path = "./any_spawner/", version = "0.1.0" }
|
||||
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1.0" }
|
||||
either_of = { path = "./either_of/", version = "0.1.0" }
|
||||
hydration_context = { path = "./hydration_context", version = "0.2.0-beta2" }
|
||||
leptos = { path = "./leptos", version = "0.7.0-beta2" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.7.0-beta2" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.7.0-beta2" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0-beta2" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0-beta2" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.7.0-beta2" }
|
||||
leptos_router = { path = "./router", version = "0.7.0-beta2" }
|
||||
leptos_router_macro = { path = "./router_macro", version = "0.7.0-beta2" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.7.0-beta2" }
|
||||
leptos_meta = { path = "./meta", version = "0.7.0-beta2" }
|
||||
next_tuple = { path = "./next_tuple", version = "0.1.0-beta2" }
|
||||
oco_ref = { path = "./oco", version = "0.2.0" }
|
||||
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
|
||||
reactive_graph = { path = "./reactive_graph", version = "0.1.0-beta2" }
|
||||
reactive_stores = { path = "./reactive_stores", version = "0.1.0-beta2" }
|
||||
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0-beta2" }
|
||||
server_fn = { path = "./server_fn", version = "0.7.0-beta2" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0-beta2" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0-beta2" }
|
||||
tachys = { path = "./tachys", version = "0.1.0-beta2" }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
You can find a list of useful libraries and example projects at [`awesome-leptos`](https://github.com/leptos-rs/awesome-leptos).
|
||||
|
||||
# The `main` branch is currently undergoing major changes in preparation for the [0.7](https://github.com/leptos-rs/leptos/milestone/4) release. For a stable version, please use the [v0.6.13 tag](https://github.com/leptos-rs/leptos/tree/v0.6.13)
|
||||
|
||||
# Leptos
|
||||
|
||||
```rust
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "throw_error"
|
||||
edition = "2021"
|
||||
version = "0.2.0-beta"
|
||||
version = "0.2.0-beta2"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for wrapping, throwing, and catching errors."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pin-project-lite = "0.2"
|
||||
pin-project-lite = "0.2.14"
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[package]
|
||||
name = "any_spawner"
|
||||
edition = "2021"
|
||||
version = "0.1.1"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Spawn asynchronous tasks in an executor-independent way."
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
glib = { version = "0.19", optional = true }
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", optional = true, default-features = false, features = [
|
||||
"rt",
|
||||
futures = "0.3.30"
|
||||
glib = { version = "0.20.0", optional = true }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.39", optional = true, default-features = false, features = [
|
||||
"rt",
|
||||
] }
|
||||
tracing = { version = "0.1", optional = true }
|
||||
wasm-bindgen-futures = { version = "0.4", optional = true }
|
||||
tracing = { version = "0.1.40", optional = true }
|
||||
wasm-bindgen-futures = { version = "0.4.42", optional = true }
|
||||
|
||||
[features]
|
||||
tracing = ["dep:tracing"]
|
||||
|
||||
@@ -6,31 +6,31 @@ rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
l0410 = { package = "leptos", version = "0.4.10", features = [
|
||||
"nightly",
|
||||
"ssr",
|
||||
"nightly",
|
||||
"ssr",
|
||||
] }
|
||||
leptos = { path = "../leptos", features = ["ssr", "nightly"] }
|
||||
leptos_reactive = { path = "../leptos_reactive", features = ["ssr", "nightly"] }
|
||||
tachydom = { git = "https://github.com/gbj/tachys", features = [
|
||||
"nightly",
|
||||
"leptos",
|
||||
"nightly",
|
||||
"leptos",
|
||||
] }
|
||||
tachy_maccy = { git = "https://github.com/gbj/tachys", features = ["nightly"] }
|
||||
sycamore = { version = "0.8", features = ["ssr"] }
|
||||
yew = { version = "0.20", features = ["ssr"] }
|
||||
tokio-test = "0.4"
|
||||
miniserde = "0.1"
|
||||
gloo = "0.8"
|
||||
uuid = { version = "1", features = ["serde", "v4", "wasm-bindgen"] }
|
||||
wasm-bindgen = "0.2"
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
serde = { version = "1", features = ["derive", "rc"] }
|
||||
serde_json = "1"
|
||||
tera = "1"
|
||||
sycamore = { version = "0.8.0", features = ["ssr"] }
|
||||
yew = { version = "0.20.0", features = ["ssr"] }
|
||||
tokio-test = "0.4.0"
|
||||
miniserde = "0.1.0"
|
||||
gloo = "0.8.0"
|
||||
uuid = { version = "1.0", features = ["serde", "v4", "wasm-bindgen"] }
|
||||
wasm-bindgen = "0.2.0"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4.0"
|
||||
strum = "0.24.0"
|
||||
strum_macros = "0.24.0"
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_json = "1.0"
|
||||
tera = "1.0"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
version = "0.3.0"
|
||||
features = ["Window", "Document", "HtmlElement", "HtmlInputElement"]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
[package]
|
||||
name = "const_str_slice_concat"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
@@ -8,5 +7,6 @@ readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for const concatenation of string slices."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
[package]
|
||||
name = "either_of"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
@@ -8,6 +7,7 @@ readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for working with enumerated types that contain one of 2..n other types."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pin-project-lite = "0.2"
|
||||
pin-project-lite = "0.2.14"
|
||||
|
||||
@@ -7,19 +7,18 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
cfg-if = "1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
cfg-if = "1.0"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
wasm-bindgen = "0.2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
|
||||
@@ -1,68 +1,9 @@
|
||||
<picture>
|
||||
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
|
||||
</picture>
|
||||
# Action Form Error Handling Example
|
||||
|
||||
# Leptos Starter Template
|
||||
## Getting Started
|
||||
|
||||
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
|
||||
See the [Examples README](../README.md) for setup and run instructions.
|
||||
|
||||
## Creating your template repo
|
||||
## Quick Start
|
||||
|
||||
If you don't have `cargo-leptos` installed you can install it with
|
||||
|
||||
`cargo install cargo-leptos`
|
||||
|
||||
Then run
|
||||
|
||||
`cargo leptos new --git leptos-rs/start`
|
||||
|
||||
to generate a new project template (you will be prompted to enter a project name).
|
||||
|
||||
`cd {projectname}`
|
||||
|
||||
to go to your newly created project.
|
||||
|
||||
Of course, you should explore around the project structure, but the best place to start with your application code is in `src/app.rs`.
|
||||
|
||||
## Running your project
|
||||
|
||||
`cargo leptos watch`
|
||||
By default, you can access your local project at `http://localhost:3000`
|
||||
|
||||
## 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)
|
||||
|
||||
## 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
|
||||
leptos_start
|
||||
site/
|
||||
```
|
||||
Set the following environment variables (updating for your project as needed):
|
||||
```sh
|
||||
export LEPTOS_OUTPUT_NAME="leptos_start"
|
||||
export LEPTOS_SITE_ROOT="site"
|
||||
export LEPTOS_SITE_PKG_DIR="pkg"
|
||||
export LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
||||
export LEPTOS_RELOAD_PORT="3001"
|
||||
```
|
||||
Finally, run the server binary.
|
||||
|
||||
## Notes about CSR and Trunk:
|
||||
Although it is not recommended, you can also run your project without server integration using the feature `csr` and `trunk serve`:
|
||||
|
||||
`trunk serve --open --features csr`
|
||||
|
||||
This may be useful for integrating external tools which require a static site, e.g. `tauri`.
|
||||
Execute `cargo leptos watch` to run this example.
|
||||
|
||||
@@ -52,23 +52,10 @@ async fn main() -> std::io::Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "ssr", feature = "csr")))]
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub fn main() {
|
||||
// no client-side main function
|
||||
// unless we want this to work with e.g., Trunk for pure client-side testing
|
||||
// see lib.rs for hydration function instead
|
||||
// see optional feature `csr` instead
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "ssr"), feature = "csr"))]
|
||||
pub fn main() {
|
||||
// a client-side main function is required for using `trunk serve`
|
||||
// prefer using `cargo leptos serve` instead
|
||||
// to run: `trunk serve --open --features csr`
|
||||
use action_form_error_handling::app::*;
|
||||
use leptos::prelude::*;
|
||||
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
mount_to_body(App);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
gloo-timers = { version = "0.3.0", features = ["futures"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -11,23 +11,23 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
broadcaster = "1"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
lazy_static = "1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
broadcaster = "1.0"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
lazy_static = "1.5"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
once_cell = "1.18"
|
||||
gloo-net = { version = "0.6" }
|
||||
wasm-bindgen = "0.2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
simple_logger = "4.3"
|
||||
tracing = { version = "0.1", optional = true }
|
||||
log = "0.4.22"
|
||||
once_cell = "1.19"
|
||||
gloo-net = { version = "0.6.0" }
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
simple_logger = "5.0"
|
||||
tracing = { version = "0.1.40", optional = true }
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -13,6 +13,6 @@ leptos_router = { path = "../../router", features = [] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -14,10 +14,10 @@ console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.34"
|
||||
pretty_assertions = "1.3.0"
|
||||
rstest = "0.17.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
pretty_assertions = "1.4"
|
||||
rstest = "0.22.0"
|
||||
|
||||
[dev-dependencies.web-sys]
|
||||
features = ["HtmlElement", "XPathResult"]
|
||||
version = "0.3.61"
|
||||
version = "0.3.70"
|
||||
|
||||
@@ -4,10 +4,10 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -5,12 +5,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
log = "0.4.22"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
web-sys = { version = "0.3", features = ["Clipboard", "Navigator"] }
|
||||
web-sys = { version = "0.3.70", features = ["Clipboard", "Navigator"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["NodeList"] }
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = { version = "0.3.70", features = ["NodeList"] }
|
||||
@@ -24,11 +24,7 @@ pub fn copy_to_clipboard(el: Element, content: &str) {
|
||||
evt.prevent_default();
|
||||
evt.stop_propagation();
|
||||
|
||||
let _ = window()
|
||||
.navigator()
|
||||
.clipboard()
|
||||
.expect("navigator.clipboard to be available")
|
||||
.write_text(&content);
|
||||
let _ = window().navigator().clipboard().write_text(&content);
|
||||
|
||||
el.set_inner_html(&format!("Copied \"{}\"", &content));
|
||||
});
|
||||
@@ -53,7 +49,6 @@ impl From<()> for Amount {
|
||||
}
|
||||
}
|
||||
|
||||
// .into() will automatically be called on the parameter
|
||||
pub fn add_dot(el: Element, amount: Amount) {
|
||||
use leptos::wasm_bindgen::JsCast;
|
||||
let el = el.unchecked_into::<web_sys::HtmlElement>();
|
||||
@@ -82,12 +77,17 @@ pub fn App() -> impl IntoView {
|
||||
let data = "Hello World!";
|
||||
|
||||
view! {
|
||||
<a href="#" use:copy_to_clipboard=data>"Copy \"" {data} "\" to clipboard"</a>
|
||||
<a href="#" use:copy_to_clipboard=data>
|
||||
"Copy \""
|
||||
{data}
|
||||
"\" to clipboard"
|
||||
</a>
|
||||
// automatically applies the directive to every root element in `SomeComponent`
|
||||
<SomeComponent use:highlight />
|
||||
<SomeComponent use:highlight/>
|
||||
// no value will default to `().into()`
|
||||
<button use:add_dot>"Add a dot"</button>
|
||||
// `5.into()` automatically called
|
||||
<button use:add_dot=5>"Add 5 dots"</button>
|
||||
// can manually call `.into()` to convert to the correct type
|
||||
// (automatically calling `.into()` prevents using generics in directive functions)
|
||||
<button use:add_dot=5.into()>"Add 5 dots"</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -7,19 +7,19 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1" }
|
||||
thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
|
||||
@@ -9,16 +9,16 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "tracing"] }
|
||||
reqwasm = "0.5"
|
||||
gloo-timers = { version = "0.3", features = ["futures"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
thiserror = "1"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber-wasm = "0.1"
|
||||
reqwasm = "0.5.0"
|
||||
gloo-timers = { version = "0.3.0", features = ["futures"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
log = "0.4.22"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
thiserror = "1.0"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
tracing-subscriber-wasm = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
|
||||
@@ -4,15 +4,15 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
throw_error = { path = "../../any_error/" }
|
||||
any_spawner = { path = "../../any_spawner/" }
|
||||
|
||||
# these are used to build the integration
|
||||
gtk = { version = "0.9.0", package = "gtk4" }
|
||||
next_tuple = { path = "../../next_tuple/" }
|
||||
gtk = { version = "0.8.0", package = "gtk4", optional = true }
|
||||
paste = "1.0.14"
|
||||
paste = "1.0"
|
||||
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
|
||||
[features]
|
||||
gtk = ["dep:gtk", "any_spawner/glib"]
|
||||
wasm = ["any_spawner/wasm-bindgen", "dep:console_error_panic_hook"]
|
||||
# we want to support using glib for the reactive runtime event loop
|
||||
any_spawner = { path = "../../any_spawner/", features = ["glib"] }
|
||||
# yes, we want effects to run: this is a "frontend," not a backend
|
||||
reactive_graph = { path = "../../reactive_graph", features = ["effects"] }
|
||||
|
||||
@@ -56,11 +56,12 @@ impl Mountable<LeptosGtk> for Element {
|
||||
.insert_before(&parent.0, marker.as_ref().map(|m| &m.0));
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable<LeptosGtk>,
|
||||
) -> bool {
|
||||
child.mount(parent, Some(self.as_ref()));
|
||||
true
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
if let Some(parent) = self.0.parent() {
|
||||
child.mount(&Element(parent), Some(self));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +80,8 @@ impl Mountable<LeptosGtk> for Text {
|
||||
.insert_before(&parent.0, marker.as_ref().map(|m| &m.0));
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable<LeptosGtk>,
|
||||
) -> bool {
|
||||
child.mount(parent, Some(self.as_ref()));
|
||||
true
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
self.0.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,16 +330,12 @@ where
|
||||
parent: &<LeptosGtk as Renderer>::Element,
|
||||
marker: Option<&<LeptosGtk as Renderer>::Node>,
|
||||
) {
|
||||
println!("mounting {}", std::any::type_name::<Widg>());
|
||||
self.children.mount(&self.widget, None);
|
||||
LeptosGtk::insert_node(parent, &self.widget, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable<LeptosGtk>,
|
||||
) -> bool {
|
||||
child.mount(parent, Some(self.widget.as_ref()));
|
||||
true
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
self.widget.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
#[cfg(feature = "gtk")]
|
||||
use gtk::{
|
||||
glib::Value, prelude::*, Application, ApplicationWindow, Orientation,
|
||||
Widget,
|
||||
};
|
||||
#[cfg(feature = "wasm")]
|
||||
use leptos::tachys::{dom::body, html::element, html::event as ev};
|
||||
use leptos::{
|
||||
logging,
|
||||
prelude::*,
|
||||
reactive_graph::{effect::Effect, owner::Owner, signal::RwSignal},
|
||||
Executor, For, ForProps,
|
||||
};
|
||||
#[cfg(feature = "gtk")]
|
||||
use leptos_gtk::{Element, LGtkWidget, LeptosGtk};
|
||||
use any_spawner::Executor;
|
||||
use gtk::{prelude::*, Application, ApplicationWindow, Orientation};
|
||||
use leptos::prelude::*;
|
||||
use leptos_gtk::LeptosGtk;
|
||||
use std::{mem, thread, time::Duration};
|
||||
#[cfg(feature = "gtk")]
|
||||
mod leptos_gtk;
|
||||
|
||||
const APP_ID: &str = "dev.leptos.Counter";
|
||||
@@ -22,59 +10,39 @@ const APP_ID: &str = "dev.leptos.Counter";
|
||||
// Basic GTK app setup from https://gtk-rs.org/gtk4-rs/stable/latest/book/hello_world.html
|
||||
fn main() {
|
||||
// use the glib event loop to power the reactive system
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
_ = Executor::init_glib();
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
_ = Executor::init_glib();
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
|
||||
app.connect_startup(|_| load_css());
|
||||
app.connect_startup(|_| load_css());
|
||||
|
||||
app.connect_activate(|app| {
|
||||
// Connect to "activate" signal of `app`
|
||||
let owner = Owner::new();
|
||||
let view = owner.with(ui);
|
||||
let (root, state) = leptos_gtk::root(view);
|
||||
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("TachyGTK")
|
||||
.child(&root)
|
||||
.build();
|
||||
// Present window
|
||||
window.present();
|
||||
mem::forget((owner, state));
|
||||
});
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
console_error_panic_hook::set_once();
|
||||
_ = Executor::init_wasm_bindgen();
|
||||
app.connect_activate(|app| {
|
||||
// Connect to "activate" signal of `app`
|
||||
let owner = Owner::new();
|
||||
let view = owner.with(ui);
|
||||
let mut state = view.build();
|
||||
state.mount(&body().into(), None);
|
||||
let (root, state) = leptos_gtk::root(view);
|
||||
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("TachyGTK")
|
||||
.child(&root)
|
||||
.build();
|
||||
// Present window
|
||||
window.present();
|
||||
mem::forget((owner, state));
|
||||
}
|
||||
});
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
#[cfg(feature = "gtk")]
|
||||
type Rndr = LeptosGtk;
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
type Rndr = Dom;
|
||||
|
||||
fn ui() -> impl Render<Rndr> {
|
||||
fn ui() -> impl Render<LeptosGtk> {
|
||||
let value = RwSignal::new(0);
|
||||
let rows = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
|
||||
Effect::new(move |_| {
|
||||
logging::log!("value = {}", value.get());
|
||||
println!("value = {}", value.get());
|
||||
});
|
||||
|
||||
// just an example of multithreaded reactivity
|
||||
#[cfg(feature = "gtk")]
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
value.update(|n| *n += 1);
|
||||
@@ -82,7 +50,10 @@ fn ui() -> impl Render<Rndr> {
|
||||
|
||||
vstack((
|
||||
hstack((
|
||||
button("-1", move || value.update(|n| *n -= 1)),
|
||||
button("-1", move || {
|
||||
println!("clicked -1");
|
||||
value.update(|n| *n -= 1);
|
||||
}),
|
||||
move || value.get().to_string(),
|
||||
button("+1", move || value.update(|n| *n += 1)),
|
||||
)),
|
||||
@@ -91,75 +62,36 @@ fn ui() -> impl Render<Rndr> {
|
||||
items.swap(1, 3);
|
||||
})
|
||||
}),
|
||||
hstack(For(ForProps::builder()
|
||||
.each(move || rows.get())
|
||||
.key(|k| *k)
|
||||
.children(|v| v)
|
||||
.build())),
|
||||
hstack(rows),
|
||||
))
|
||||
}
|
||||
|
||||
fn button(
|
||||
label: impl Render<Rndr>,
|
||||
label: impl Render<LeptosGtk>,
|
||||
callback: impl Fn() + Send + Sync + 'static,
|
||||
) -> impl Render<Rndr> {
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
leptos_gtk::button()
|
||||
.child(label)
|
||||
.connect("clicked", move |_| {
|
||||
callback();
|
||||
None
|
||||
})
|
||||
}
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
element::button()
|
||||
.on(ev::click, move |_| callback())
|
||||
.child(label)
|
||||
}
|
||||
) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::button()
|
||||
.child(label)
|
||||
.connect("clicked", move |_| {
|
||||
callback();
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn vstack(children: impl Render<Rndr>) -> impl Render<Rndr> {
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
element::div()
|
||||
.style(("display", "flex"))
|
||||
.style(("flex-direction", "column"))
|
||||
.style(("align-items", "center"))
|
||||
.style(("justify-content", "center"))
|
||||
.style(("margin", "1rem"))
|
||||
.child(children)
|
||||
}
|
||||
fn vstack(children: impl Render<LeptosGtk>) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
|
||||
fn hstack(children: impl Render<Rndr>) -> impl Render<Rndr> {
|
||||
#[cfg(feature = "gtk")]
|
||||
{
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
#[cfg(all(feature = "wasm", not(feature = "gtk")))]
|
||||
{
|
||||
element::div()
|
||||
.style(("display", "flex"))
|
||||
.style(("align-items", "center"))
|
||||
.style(("justify-content", "center"))
|
||||
.style(("margin", "1rem"))
|
||||
.child(children)
|
||||
}
|
||||
fn hstack(children: impl Render<LeptosGtk>) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
|
||||
#[cfg(feature = "gtk")]
|
||||
fn load_css() {
|
||||
use gtk::{gdk::Display, CssProvider};
|
||||
|
||||
|
||||
@@ -13,23 +13,24 @@ panic = "abort"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
gloo-net = { version = "0.6", features = ["http"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = { version = "0.3.70", features = ["AbortController", "AbortSignal"] }
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[features]
|
||||
default = ["csr"]
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = ["dep:actix-files", "dep:actix-web", "dep:leptos_actix", "leptos/ssr"]
|
||||
|
||||
@@ -11,22 +11,22 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
gloo-net = { version = "0.6", features = ["http"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0", optional = true }
|
||||
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2"
|
||||
tracing = "0.1.40"
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1", optional = true }
|
||||
web-sys = { version = "0.3.70", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -11,36 +11,35 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos", features = ["experimental-islands"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
gloo-net = { version = "0.6", features = ["http"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
axum = { version = "0.7", optional = true, features = ["http2"] }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = [
|
||||
tracing = "0.1.40"
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
axum = { version = "0.7.5", optional = true, features = ["http2"] }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = [
|
||||
"fs",
|
||||
"compression-gzip",
|
||||
"compression-br",
|
||||
], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0", optional = true }
|
||||
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2"
|
||||
lazy_static = "1.4.0"
|
||||
rust-embed = { version = "8", features = [
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1", optional = true }
|
||||
web-sys = { version = "0.3.70", features = ["AbortController", "AbortSignal"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
lazy_static = "1.5"
|
||||
rust-embed = { version = "8.5", features = [
|
||||
"axum",
|
||||
"mime_guess",
|
||||
"tokio",
|
||||
], optional = true }
|
||||
mime_guess = { version = "2.0.4", optional = true }
|
||||
mime_guess = { version = "2.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
|
||||
@@ -11,30 +11,30 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
log = "0.4"
|
||||
log = "0.4.22"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
leptos_server = { path = "../../leptos_server", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing = "0.1"
|
||||
gloo-net = { version = "0.6", features = ["http"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
axum = { version = "0.7", default-features = false, optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
http = { version = "1.0", optional = true }
|
||||
web-sys = { version = "0.3", features = [
|
||||
tracing = "0.1.40"
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
axum = { version = "0.7.5", default-features = false, optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
http = { version = "1.1", optional = true }
|
||||
web-sys = { version = "0.3.70", features = [
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Request",
|
||||
"Response",
|
||||
] }
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = { version = "0.4.37", features = [
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-futures = { version = "0.4.42", features = [
|
||||
"futures-core-03-stream",
|
||||
], optional = true }
|
||||
axum-js-fetch = { git = "https://github.com/seanaye/axum-js-fetch", optional = true }
|
||||
|
||||
@@ -7,32 +7,32 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
] }
|
||||
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
]
|
||||
|
||||
[profile.wasm-release]
|
||||
|
||||
@@ -7,33 +7,35 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
"tracing",
|
||||
"experimental-islands",
|
||||
] }
|
||||
leptos_router = { path = "../../router" }
|
||||
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
|
||||
leptos_axum = { path = "../../integrations/axum", features = ["islands-router"], optional = true }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
leptos_axum = { path = "../../integrations/axum", features = [
|
||||
"islands-router",
|
||||
], optional = true }
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
]
|
||||
|
||||
[profile.wasm-release]
|
||||
|
||||
@@ -8,13 +8,13 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] } # for actual benchmarking, add `nightly` and `event-delegation` features
|
||||
leptos = { path = "../../leptos", features = ["csr"] } # for actual benchmarking, add `nightly` and `delegation` features
|
||||
# used in rand, but we need to enable js feature
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3"
|
||||
|
||||
@@ -9,7 +9,7 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
web-sys = "0.3"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -5,12 +5,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
log = "0.4.22"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
wasm-bindgen = "0.2.93"
|
||||
web-sys = "0.3.70"
|
||||
@@ -10,16 +10,16 @@ codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
console_log = "1"
|
||||
console_log = "1.0"
|
||||
leptos = { path = "../../leptos", features = ["csr", "tracing"] }
|
||||
leptos_router = { path = "../../router" } #, features = ["tracing"] }
|
||||
leptos_router = { path = "../../router" } #, features = ["tracing"] }
|
||||
leptos_router_macro = { path = "../../router_macro" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
futures = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
futures = "0.3.30"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
tracing-subscriber = "0.3.18"
|
||||
tracing-subscriber-wasm = "0.1.0"
|
||||
tracing = "0.1.40"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
|
||||
@@ -72,6 +72,7 @@ pub fn ContactRoutes() -> impl MatchNestedRoutes<Dom> + Clone {
|
||||
<Route path=path!("/:id") view=Contact/>
|
||||
</ParentRoute>
|
||||
}
|
||||
.into_inner()
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
@@ -7,44 +7,52 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
server_fn = { path = "../../server_fn", features = ["serde-lite", "rkyv", "multipart"] }
|
||||
log = "0.4"
|
||||
simple_logger = "4.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs", "tracing", "trace"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
server_fn = { path = "../../server_fn", features = [
|
||||
"serde-lite",
|
||||
"rkyv",
|
||||
"multipart",
|
||||
] }
|
||||
log = "0.4.22"
|
||||
simple_logger = "5.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = [
|
||||
"fs",
|
||||
"tracing",
|
||||
"trace",
|
||||
], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde_toml = "0.0.1"
|
||||
toml = "0.8.8"
|
||||
web-sys = { version = "0.3.67", features = ["FileList", "File"] }
|
||||
strum = { version = "0.25.0", features = ["strum_macros", "derive"] }
|
||||
notify = { version = "6.1.1", optional = true }
|
||||
pin-project-lite = "0.2.13"
|
||||
dashmap = { version = "5.5.3", optional = true }
|
||||
once_cell = { version = "1.19.0", optional = true }
|
||||
async-broadcast = { version = "0.6.0", optional = true }
|
||||
toml = "0.8.19"
|
||||
web-sys = { version = "0.3.70", features = ["FileList", "File"] }
|
||||
strum = { version = "0.26.3", features = ["strum_macros", "derive"] }
|
||||
notify = { version = "6.1", optional = true }
|
||||
pin-project-lite = "0.2.14"
|
||||
dashmap = { version = "6.0", optional = true }
|
||||
once_cell = { version = "1.19", optional = true }
|
||||
async-broadcast = { version = "0.7.1", optional = true }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:notify",
|
||||
"dep:dashmap",
|
||||
"dep:once_cell",
|
||||
"dep:async-broadcast",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:notify",
|
||||
"dep:dashmap",
|
||||
"dep:once_cell",
|
||||
"dep:async-broadcast",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -9,6 +9,6 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -7,20 +7,20 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos"}
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
lazy_static = "1.5"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = ["time"] }
|
||||
wasm-bindgen = "0.2"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.39", features = ["time"] }
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
|
||||
@@ -7,37 +7,39 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos", features = ["hydration" ] } #"nightly", "hydration"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1.0"
|
||||
lazy_static = "1.5"
|
||||
leptos = { path = "../../leptos", features = [
|
||||
"hydration",
|
||||
] } #"nightly", "hydration"] }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
"time",
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
"time",
|
||||
], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"dep:leptos_axum",
|
||||
"leptos_router/ssr",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"dep:leptos_axum",
|
||||
"leptos_router/ssr",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<AutoReload options=options.clone() />
|
||||
<AutoReload options=options.clone()/>
|
||||
<HydrationScripts options/>
|
||||
<MetaTags/>
|
||||
</head>
|
||||
@@ -64,15 +64,24 @@ pub fn App() -> impl IntoView {
|
||||
<a href="/admin">"Admin"</a>
|
||||
<Transition>
|
||||
<ActionForm action=toggle_admin>
|
||||
<input type="hidden" name="is_admin"
|
||||
value=move || (!is_admin.get().and_then(|n| n.ok()).unwrap_or_default()).to_string()
|
||||
<input
|
||||
type="hidden"
|
||||
name="is_admin"
|
||||
value=move || {
|
||||
(!is_admin.get().and_then(|n| n.ok()).unwrap_or_default())
|
||||
.to_string()
|
||||
}
|
||||
/>
|
||||
|
||||
<button>
|
||||
{move || if is_admin.get().and_then(Result::ok).unwrap_or_default() {
|
||||
"Log Out"
|
||||
} else {
|
||||
"Log In"
|
||||
{move || {
|
||||
if is_admin.get().and_then(Result::ok).unwrap_or_default() {
|
||||
"Log Out"
|
||||
} else {
|
||||
"Log In"
|
||||
}
|
||||
}}
|
||||
|
||||
</button>
|
||||
</ActionForm>
|
||||
</Transition>
|
||||
@@ -139,9 +148,15 @@ fn HomePage() -> impl IntoView {
|
||||
<li>
|
||||
<a href=format!("/post/{}", post.id)>{post.title.clone()}</a>
|
||||
"|"
|
||||
<a href=format!("/post_in_order/{}", post.id)>{post.title.clone()} "(in order)"</a>
|
||||
<a href=format!(
|
||||
"/post_in_order/{}",
|
||||
post.id,
|
||||
)>{post.title.clone()} "(in order)"</a>
|
||||
"|"
|
||||
<a href=format!("/post_partially_blocked/{}", post.id)>{post.title} "(partially blocked)"</a>
|
||||
<a href=format!(
|
||||
"/post_partially_blocked/{}",
|
||||
post.id,
|
||||
)>{post.title} "(partially blocked)"</a>
|
||||
</li>
|
||||
</For>
|
||||
</ul>
|
||||
@@ -204,12 +219,11 @@ fn Post() -> impl IntoView {
|
||||
Ok(comments) => Ok(view! {
|
||||
<h1>"Comments"</h1>
|
||||
<ul>
|
||||
{comments.into_iter()
|
||||
.map(|comment| view! {
|
||||
<li>{comment}</li>
|
||||
})
|
||||
.collect_view()
|
||||
}
|
||||
{comments
|
||||
.into_iter()
|
||||
.map(|comment| view! { <li>{comment}</li> })
|
||||
.collect_view()}
|
||||
|
||||
</ul>
|
||||
}),
|
||||
_ => Err(PostError::ServerError),
|
||||
@@ -237,17 +251,13 @@ fn Post() -> impl IntoView {
|
||||
}
|
||||
}>{post_view}</ErrorBoundary>
|
||||
</Suspense>
|
||||
<Suspense fallback=move || view! { <p>"Loading comments..."</p> }>
|
||||
{comments_view}
|
||||
</Suspense>
|
||||
<Suspense fallback=move || view! { <p>"Loading comments..."</p> }>{comments_view}</Suspense>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Admin() -> impl IntoView {
|
||||
view! {
|
||||
<p>"You can only see this page if you're logged in."</p>
|
||||
}
|
||||
view! { <p>"You can only see this page if you're logged in."</p> }
|
||||
}
|
||||
|
||||
// Dummy API
|
||||
|
||||
@@ -15,6 +15,6 @@ reactive_stores_macro = { path = "../../reactive_stores_macro" }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
web-sys = "0.3"
|
||||
wasm-bindgen = "0.2.93"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
web-sys = "0.3.70"
|
||||
|
||||
@@ -7,26 +7,26 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
log = "0.4"
|
||||
wasm-bindgen = "0.2"
|
||||
serde = "1.0.159"
|
||||
tokio = { version = "1.29", features = ["time", "rt"], optional = true }
|
||||
log = "0.4.22"
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde = "1.0"
|
||||
tokio = { version = "1.39", features = ["time", "rt"], optional = true }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:leptos_actix",
|
||||
"leptos/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:tokio",
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:leptos_actix",
|
||||
"leptos/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:tokio",
|
||||
]
|
||||
|
||||
[package.metadata.leptos]
|
||||
|
||||
@@ -4,14 +4,14 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.72"
|
||||
async-trait = "0.1.72"
|
||||
cucumber = "0.19.1"
|
||||
fantoccini = "0.19.3"
|
||||
pretty_assertions = "1.4.0"
|
||||
serde_json = "1.0.104"
|
||||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.4.0"
|
||||
anyhow = "1.0"
|
||||
async-trait = "0.1.81"
|
||||
cucumber = "0.21.1"
|
||||
fantoccini = "0.21.1"
|
||||
pretty_assertions = "1.4"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.39", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.5"
|
||||
|
||||
[[test]]
|
||||
name = "app_suite"
|
||||
|
||||
@@ -8,8 +8,8 @@ This example demonstrates e2e testing with Rust using executable requirements.
|
||||
|---|---|---|
|
||||
| [Cucumber](https://github.com/cucumber-rs/cucumber/tree/main) | Test Runner | Run [Gherkin](https://cucumber.io/docs/gherkin/reference/) specifications as Rust tests |
|
||||
| [Fantoccini](https://github.com/jonhoo/fantoccini/tree/main) | Browser Client | Interact with web pages through WebDriver |
|
||||
| [Cargo Leptos ](https://github.com/leptos-rs/cargo-leptos) | Build Tool | Compile example and start the server and end-2-end tests |
|
||||
| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome
|
||||
| [Cargo Leptos](https://github.com/leptos-rs/cargo-leptos) | Build Tool | Compile example and start the server and end-2-end tests |
|
||||
| [chromedriver](https://chromedriver.chromium.org/downloads) | WebDriver | Provide WebDriver for Chrome |
|
||||
|
||||
## Testing Organization
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
|
||||
# dependencies for browser (enable when hydrate set)
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
wasm-bindgen = { version = "0.2.93", optional = true }
|
||||
|
||||
# dependencies for server (enable when ssr set)
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", features = ["macros"], optional = true }
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", features = ["macros"], optional = true }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate", "dep:wasm-bindgen", "dep:console_error_panic_hook"]
|
||||
|
||||
@@ -7,32 +7,35 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.7", optional = true }
|
||||
console_error_panic_hook = "0.1"
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router" }
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"], optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
tokio = { version = "1.39", features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
], optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
wasm-bindgen = "0.2.93"
|
||||
thiserror = "1.0"
|
||||
tracing = { version = "0.1", optional = true }
|
||||
http = "1.0"
|
||||
tracing = { version = "0.1.40", optional = true }
|
||||
http = "1.1"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tokio",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:leptos_axum",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:tracing",
|
||||
"dep:axum",
|
||||
"dep:tokio",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:leptos_axum",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:tracing",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -7,5 +7,5 @@ edition = "2021"
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
gloo-net = { version = "0.6", features = ["http"] }
|
||||
console_error_panic_hook = { version = "0.1" }
|
||||
gloo-net = { version = "0.6.0", features = ["http"] }
|
||||
console_error_panic_hook = { version = "0.1.7" }
|
||||
|
||||
@@ -8,15 +8,15 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_log = "1.0"
|
||||
log = "0.4.22"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
version = "0.3.70"
|
||||
features = ["Window"]
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
|
||||
@@ -7,36 +7,36 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.6.2", optional = true }
|
||||
actix-web = { version = "4.2.1", optional = true, features = ["macros"] }
|
||||
anyhow = "1.0.68"
|
||||
broadcaster = "1.0.0"
|
||||
console_log = "1.0.0"
|
||||
actix-files = { version = "0.6.6", optional = true }
|
||||
actix-web = { version = "4.8", optional = true, features = ["macros"] }
|
||||
anyhow = "1.0"
|
||||
broadcaster = "1.0"
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
futures = "0.3.25"
|
||||
leptos = { path = "../../leptos"}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
futures = "0.3.30"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.0.0"
|
||||
log = "0.4.22"
|
||||
simple_logger = "5.0"
|
||||
gloo = { git = "https://github.com/rustwasm/gloo" }
|
||||
sqlx = { version = "0.6.2", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
sqlx = { version = "0.8.0", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
], optional = true }
|
||||
wasm-bindgen = "0.2"
|
||||
tokio = { version = "1", features = ["rt", "time"], optional = true }
|
||||
wasm-bindgen = "0.2.93"
|
||||
tokio = { version = "1.39", features = ["rt", "time"], optional = true }
|
||||
server_fn = { path = "../../server_fn", features = ["cbor"] }
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:sqlx",
|
||||
"leptos/ssr",
|
||||
"leptos_actix",
|
||||
"dep:tokio",
|
||||
"dep:actix-files",
|
||||
"dep:actix-web",
|
||||
"dep:sqlx",
|
||||
"leptos/ssr",
|
||||
"leptos_actix",
|
||||
"dep:tokio",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -4,14 +4,14 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.72"
|
||||
async-trait = "0.1.72"
|
||||
cucumber = "0.19.1"
|
||||
fantoccini = "0.19.3"
|
||||
pretty_assertions = "1.4.0"
|
||||
serde_json = "1.0.104"
|
||||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.4.0"
|
||||
anyhow = "1.0"
|
||||
async-trait = "0.1.81"
|
||||
cucumber = "0.21.1"
|
||||
fantoccini = "0.21.1"
|
||||
pretty_assertions = "1.4"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.39", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.5"
|
||||
|
||||
[[test]]
|
||||
name = "app_suite"
|
||||
|
||||
@@ -8,35 +8,35 @@ crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_log = "1.0"
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
http = "1.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
leptos = { path = "../../leptos", features = ["tracing"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
log = "0.4"
|
||||
simple_logger = "4.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
sqlx = { version = "0.7", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
log = "0.4.22"
|
||||
simple_logger = "5.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
sqlx = { version = "0.8.0", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
], optional = true }
|
||||
thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"dep:sqlx",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"dep:sqlx",
|
||||
"leptos/ssr",
|
||||
"dep:leptos_axum",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -4,14 +4,14 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.72"
|
||||
async-trait = "0.1.72"
|
||||
cucumber = "0.19.1"
|
||||
fantoccini = "0.19.3"
|
||||
pretty_assertions = "1.4.0"
|
||||
serde_json = "1.0.104"
|
||||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.4.0"
|
||||
anyhow = "1.0"
|
||||
async-trait = "0.1.81"
|
||||
cucumber = "0.21.1"
|
||||
fantoccini = "0.21.1"
|
||||
pretty_assertions = "1.4"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.39", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.5"
|
||||
|
||||
[[test]]
|
||||
name = "app_suite"
|
||||
|
||||
@@ -7,39 +7,39 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.30"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta"}
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
leptos_integration_utils = { path = "../../integrations/utils", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
axum = { version = "0.7", optional = true }
|
||||
tower = { version = "0.4", optional = true }
|
||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
http = { version = "1.0" }
|
||||
sqlx = { version = "0.7", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
axum = { version = "0.7.5", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.5.2", features = ["fs"], optional = true }
|
||||
tokio = { version = "1.39", features = ["full"], optional = true }
|
||||
http = { version = "1.1" }
|
||||
sqlx = { version = "0.8.0", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
], optional = true }
|
||||
thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[features]
|
||||
csr = ["leptos/csr"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"dep:sqlx",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:leptos_integration_utils",
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:tokio",
|
||||
"dep:sqlx",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:leptos_axum",
|
||||
"dep:leptos_integration_utils",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -4,14 +4,14 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.72"
|
||||
async-trait = "0.1.72"
|
||||
cucumber = "0.19.1"
|
||||
fantoccini = "0.19.3"
|
||||
pretty_assertions = "1.4.0"
|
||||
serde_json = "1.0.104"
|
||||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.4.0"
|
||||
anyhow = "1.0"
|
||||
async-trait = "0.1.81"
|
||||
cucumber = "0.21.1"
|
||||
fantoccini = "0.21.1"
|
||||
pretty_assertions = "1.4"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.39", features = ["macros", "rt-multi-thread", "time"] }
|
||||
url = "2.5"
|
||||
|
||||
[[test]]
|
||||
name = "app_suite"
|
||||
|
||||
@@ -8,19 +8,15 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
uuid = { version = "1", features = ["v4", "js", "serde"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
web-sys = { version = "0.3.60", features = ["Storage"] }
|
||||
uuid = { version = "1.10", features = ["v4", "js", "serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
web-sys = { version = "0.3.70", features = ["Storage"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
|
||||
[features]
|
||||
default = ["csr"]
|
||||
csr = ["leptos/csr"]
|
||||
wasm-bindgen-test = "0.3.42"
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[package]
|
||||
name = "hydration_context"
|
||||
edition = "2021"
|
||||
version = "0.2.0-beta"
|
||||
version = "0.2.0-beta2"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities for sharing data between web servers and client-side web applications."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
throw_error = { workspace = true }
|
||||
or_poisoned = { workspace = true }
|
||||
futures = "0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
js-sys = { version = "0.3", optional = true }
|
||||
once_cell = "1.19.0"
|
||||
futures = "0.3.30"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen = { version = "0.2.93", optional = true }
|
||||
js-sys = { version = "0.3.69", optional = true }
|
||||
once_cell = "1.19"
|
||||
pin-project-lite = "0.2.14"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -12,42 +12,48 @@ use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(thread_local)]
|
||||
static __RESOLVED_RESOURCES: Array;
|
||||
|
||||
#[wasm_bindgen(thread_local)]
|
||||
static __SERIALIZED_ERRORS: Array;
|
||||
|
||||
#[wasm_bindgen(thread_local)]
|
||||
static __INCOMPLETE_CHUNKS: Array;
|
||||
}
|
||||
|
||||
fn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {
|
||||
__SERIALIZED_ERRORS
|
||||
.iter()
|
||||
.flat_map(|value| {
|
||||
value.dyn_ref::<Array>().map(|value| {
|
||||
let error_boundary_id = value.get(0).as_f64().unwrap() as usize;
|
||||
let error_id = value.get(1).as_f64().unwrap() as usize;
|
||||
let value = value
|
||||
.get(2)
|
||||
.as_string()
|
||||
.expect("Expected a [number, string] tuple");
|
||||
(
|
||||
SerializedDataId(error_boundary_id),
|
||||
ErrorId::from(error_id),
|
||||
Error::from(SerializedError(value)),
|
||||
)
|
||||
__SERIALIZED_ERRORS.with(|s| {
|
||||
s.iter()
|
||||
.flat_map(|value| {
|
||||
value.dyn_ref::<Array>().map(|value| {
|
||||
let error_boundary_id =
|
||||
value.get(0).as_f64().unwrap() as usize;
|
||||
let error_id = value.get(1).as_f64().unwrap() as usize;
|
||||
let value = value
|
||||
.get(2)
|
||||
.as_string()
|
||||
.expect("Expected a [number, string] tuple");
|
||||
(
|
||||
SerializedDataId(error_boundary_id),
|
||||
ErrorId::from(error_id),
|
||||
Error::from(SerializedError(value)),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn incomplete_chunks() -> Vec<SerializedDataId> {
|
||||
__INCOMPLETE_CHUNKS
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let id = value.as_f64().unwrap() as usize;
|
||||
SerializedDataId(id)
|
||||
})
|
||||
.collect()
|
||||
__INCOMPLETE_CHUNKS.with(|i| {
|
||||
i.iter()
|
||||
.map(|value| {
|
||||
let id = value.as_f64().unwrap() as usize;
|
||||
SerializedDataId(id)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// An error that has been serialized across the network boundary.
|
||||
@@ -118,7 +124,7 @@ impl SharedContext for HydrateSharedContext {
|
||||
fn write_async(&self, _id: SerializedDataId, _fut: PinnedFuture<String>) {}
|
||||
|
||||
fn read_data(&self, id: &SerializedDataId) -> Option<String> {
|
||||
__RESOLVED_RESOURCES.get(id.0 as u32).as_string()
|
||||
__RESOLVED_RESOURCES.with(|r| r.get(id.0 as u32).as_string())
|
||||
}
|
||||
|
||||
fn await_data(&self, _id: &SerializedDataId) -> Option<String> {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
[package]
|
||||
name = "leptos_actix"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Actix integrations for the Leptos web framework."
|
||||
version = { workspace = true }
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
actix-http = "3"
|
||||
actix-web = "4"
|
||||
futures = "0.3"
|
||||
actix-http = "3.8"
|
||||
actix-web = "4.8"
|
||||
futures = "0.3.30"
|
||||
any_spawner = { workspace = true, features = ["tokio"] }
|
||||
hydration_context = { workspace = true }
|
||||
leptos = { workspace = true, features = ["nonce", "ssr"] }
|
||||
@@ -20,10 +20,10 @@ leptos_macro = { workspace = true, features = ["actix"] }
|
||||
leptos_meta = { workspace = true }
|
||||
leptos_router = { workspace = true, features = ["ssr"] }
|
||||
server_fn = { workspace = true, features = ["actix"] }
|
||||
serde_json = "1"
|
||||
parking_lot = "0.12.1"
|
||||
tracing = "0.1.37"
|
||||
tokio = { version = "1", features = ["rt", "fs"] }
|
||||
serde_json = "1.0"
|
||||
parking_lot = "0.12.3"
|
||||
tracing = "0.1.40"
|
||||
tokio = { version = "1.39", features = ["rt", "fs"] }
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
||||
@@ -182,6 +182,20 @@ impl ExtendResponse for ActixResponse {
|
||||
|
||||
/// Provides an easy way to redirect the user from within a server function.
|
||||
///
|
||||
/// Calling `redirect` in a server function will redirect the browser in three
|
||||
/// situations:
|
||||
/// 1. A server function that is calling in a [blocking
|
||||
/// resource](leptos::server::Resource::new_blocking).
|
||||
/// 2. A server function that is called from WASM running in the client (e.g., a dispatched action
|
||||
/// or a spawned `Future`).
|
||||
/// 3. A `<form>` submitted to the server function endpoint using default browser APIs (often due
|
||||
/// to using [`ActionForm`](leptos::form::ActionForm) without JS/WASM present.)
|
||||
///
|
||||
/// Using it with a non-blocking [`Resource`](leptos::server::Resource) will not work if you are using streaming rendering,
|
||||
/// as the response's headers will already have been sent by the time the server function calls `redirect()`.
|
||||
///
|
||||
/// ### Implementation
|
||||
///
|
||||
/// This sets the `Location` header to the URL given.
|
||||
///
|
||||
/// If the route or server function in which this is called is being accessed
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
[package]
|
||||
name = "leptos_axum"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Axum integrations for the Leptos web framework."
|
||||
version = { workspace = true }
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
any_spawner = { workspace = true, features = ["tokio"] }
|
||||
hydration_context = { workspace = true }
|
||||
axum = { version = "0.7", default-features = false, features = [
|
||||
"matched-path",
|
||||
axum = { version = "0.7.5", default-features = false, features = [
|
||||
"matched-path",
|
||||
] }
|
||||
futures = "0.3"
|
||||
http = "1"
|
||||
http-body-util = "0.1"
|
||||
futures = "0.3.30"
|
||||
http = "1.1"
|
||||
http-body-util = "0.1.2"
|
||||
leptos = { workspace = true, features = ["nonce", "ssr"] }
|
||||
server_fn = { workspace = true, features = ["axum-no-default"] }
|
||||
leptos_macro = { workspace = true, features = ["axum"] }
|
||||
leptos_meta = { workspace = true, features = ["ssr"] }
|
||||
leptos_router = { workspace = true, features = ["ssr"] }
|
||||
leptos_integration_utils = { workspace = true }
|
||||
parking_lot = "0.12"
|
||||
serde_json = "1"
|
||||
tokio = { version = "1", default-features = false }
|
||||
tower = "0.4"
|
||||
tower-http = "0.5"
|
||||
tracing = "0.1"
|
||||
parking_lot = "0.12.3"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.39", default-features = false }
|
||||
tower = "0.4.13"
|
||||
tower-http = "0.5.2"
|
||||
tracing = "0.1.40"
|
||||
|
||||
[dev-dependencies]
|
||||
axum = "0.7"
|
||||
tokio = { version = "1", features = ["net", "rt-multi-thread"] }
|
||||
axum = "0.7.5"
|
||||
tokio = { version = "1.39", features = ["net", "rt-multi-thread"] }
|
||||
|
||||
[features]
|
||||
wasm = []
|
||||
|
||||
@@ -178,9 +178,33 @@ impl ExtendResponse for AxumResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides an easy way to redirect the user from within a server function. Mimicking the Remix `redirect()`,
|
||||
/// it sets a StatusCode of 302 and a LOCATION header with the provided value.
|
||||
/// If looking to redirect from the client, `leptos_router::use_navigate()` should be used instead
|
||||
/// Provides an easy way to redirect the user from within a server function.
|
||||
///
|
||||
/// Calling `redirect` in a server function will redirect the browser in three
|
||||
/// situations:
|
||||
/// 1. A server function that is calling in a [blocking
|
||||
/// resource](leptos::server::Resource::new_blocking).
|
||||
/// 2. A server function that is called from WASM running in the client (e.g., a dispatched action
|
||||
/// or a spawned `Future`).
|
||||
/// 3. A `<form>` submitted to the server function endpoint using default browser APIs (often due
|
||||
/// to using [`ActionForm`](leptos::form::ActionForm) without JS/WASM present.)
|
||||
///
|
||||
/// Using it with a non-blocking [`Resource`](leptos::server::Resource) will not work if you are using streaming rendering,
|
||||
/// as the response's headers will already have been sent by the time the server function calls `redirect()`.
|
||||
///
|
||||
/// ### Implementation
|
||||
///
|
||||
/// This sets the `Location` header to the URL given.
|
||||
///
|
||||
/// If the route or server function in which this is called is being accessed
|
||||
/// by an ordinary `GET` request or an HTML `<form>` without any enhancement, it also sets a
|
||||
/// status code of `302` for a temporary redirect. (This is determined by whether the `Accept`
|
||||
/// header contains `text/html` as it does for an ordinary navigation.)
|
||||
///
|
||||
/// Otherwise, it sets a custom header that indicates to the client that it should redirect,
|
||||
/// without actually setting the status code. This means that the client will not follow the
|
||||
/// redirect, and can therefore return the value of the server function and then handle
|
||||
/// the redirect with client-side routing.
|
||||
pub fn redirect(path: &str) {
|
||||
if let (Some(req), Some(res)) =
|
||||
(use_context::<Parts>(), use_context::<ResponseOptions>())
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[package]
|
||||
name = "leptos_integration_utils"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utilities to help build server integrations for the Leptos web framework."
|
||||
version = { workspace = true }
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
futures = "0.3.30"
|
||||
hydration_context = { workspace = true }
|
||||
leptos = { workspace = true, features = ["nonce"] }
|
||||
leptos_meta = { workspace = true, features = ["ssr"] }
|
||||
leptos_router = { workspace = true, features = ["ssr"] }
|
||||
leptos_config = { workspace = true }
|
||||
reactive_graph = { workspace = true, features = ["sandboxed-arenas"] }
|
||||
tracing = "0.1.37"
|
||||
tracing = "0.1.40"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
||||
@@ -1,57 +1,62 @@
|
||||
[package]
|
||||
name = "leptos"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces."
|
||||
readme = "../README.md"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
throw_error = { workspace = true }
|
||||
any_spawner = { workspace = true, features = ["wasm-bindgen"] }
|
||||
base64 = { version = "0.22", optional = true }
|
||||
cfg-if = "1"
|
||||
base64 = { version = "0.22.1", optional = true }
|
||||
cfg-if = "1.0"
|
||||
hydration_context = { workspace = true }
|
||||
either_of = { workspace = true }
|
||||
leptos_dom = { workspace = true }
|
||||
leptos_hot_reload = { workspace = true }
|
||||
leptos_macro = { workspace = true }
|
||||
leptos_server = { workspace = true, features = ["tachys"] }
|
||||
leptos_config = { workspace = true }
|
||||
leptos-spin-macro = { version = "0.1", optional = true }
|
||||
leptos-spin-macro = { version = "0.2.0", optional = true }
|
||||
oco_ref = { workspace = true }
|
||||
or_poisoned = { workspace = true }
|
||||
paste = "1"
|
||||
rand = { version = "0.8", optional = true }
|
||||
paste = "1.0"
|
||||
rand = { version = "0.8.5", optional = true }
|
||||
reactive_graph = { workspace = true, features = ["serde"] }
|
||||
rustc-hash = "1"
|
||||
rustc-hash = "2.0"
|
||||
tachys = { workspace = true, features = ["reactive_graph", "oco"] }
|
||||
thiserror = "1"
|
||||
tracing = "0.1"
|
||||
typed-builder = "0.18"
|
||||
typed-builder-macro = "0.18"
|
||||
serde = "1"
|
||||
serde_json = { version = "1", optional = true }
|
||||
thiserror = "1.0"
|
||||
tracing = "0.1.40"
|
||||
typed-builder = "0.19.1"
|
||||
typed-builder-macro = "0.19.1"
|
||||
serde = "1.0"
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
server_fn = { workspace = true, features = [
|
||||
"form-redirects",
|
||||
"browser",
|
||||
"url",
|
||||
] }
|
||||
web-sys = { version = "0.3.63", features = [
|
||||
web-sys = { version = "0.3.70", features = [
|
||||
"ShadowRoot",
|
||||
"ShadowRootInit",
|
||||
"ShadowRootMode",
|
||||
] }
|
||||
wasm-bindgen = "0.2"
|
||||
serde_qs = "0.12.0"
|
||||
slotmap = "1.0.7"
|
||||
wasm-bindgen = "0.2.93"
|
||||
serde_qs = "0.13.0"
|
||||
slotmap = "1.0"
|
||||
futures = "0.3.30"
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[features]
|
||||
hydration = ["reactive_graph/hydration", "leptos_server/hydration", "hydration_context/browser"]
|
||||
hydration = [
|
||||
"reactive_graph/hydration",
|
||||
"leptos_server/hydration",
|
||||
"hydration_context/browser",
|
||||
]
|
||||
csr = ["leptos_macro/csr", "reactive_graph/effects"]
|
||||
hydrate = [
|
||||
"leptos_macro/hydrate",
|
||||
@@ -76,13 +81,8 @@ tracing = [
|
||||
] #, "leptos_macro/tracing", "leptos_dom/tracing"]
|
||||
nonce = ["base64", "rand"]
|
||||
spin = ["leptos-spin-macro"]
|
||||
experimental-islands = [
|
||||
"leptos_macro/experimental-islands",
|
||||
"dep:serde_json",
|
||||
]
|
||||
trace-component-props = [
|
||||
"leptos_macro/trace-component-props",
|
||||
]
|
||||
experimental-islands = ["leptos_macro/experimental-islands", "dep:serde_json"]
|
||||
trace-component-props = ["leptos_macro/trace-component-props"]
|
||||
delegation = ["tachys/delegation"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
//! ) -> impl IntoView {
|
||||
//! view! {
|
||||
//! <div>
|
||||
//! {render_number.call(1)}
|
||||
//! {render_number.run(1)}
|
||||
//! // callbacks can be called multiple times
|
||||
//! {render_number.call(42)}
|
||||
//! {render_number.run(42)}
|
||||
//! </div>
|
||||
//! }
|
||||
//! }
|
||||
@@ -31,28 +31,28 @@
|
||||
//! *Notes*:
|
||||
//! - The `render_number` prop can receive any type that implements `Fn(i32) -> String`.
|
||||
//! - Callbacks are most useful when you want optional generic props.
|
||||
//! - All callbacks implement the [`Callable`] trait, and can be invoked with `my_callback.call(input)`. On nightly, you can even do `my_callback(input)`
|
||||
//! - All callbacks implement the [`Callable`] trait, and can be invoked with `my_callback.run(input)`.
|
||||
//! - The callback types implement [`Copy`], so they can easily be moved into and out of other closures, just like signals.
|
||||
//!
|
||||
//! # Types
|
||||
//! This modules implements 2 callback types:
|
||||
//! - [`Callback`]
|
||||
//! - [`SyncCallback`]
|
||||
//! - [`UnsyncCallback`]
|
||||
//!
|
||||
//! Use `SyncCallback` when you want the function to be `Sync` and `Send`.
|
||||
//! Use `SyncCallback` if the function is not `Sync` and `Send`.
|
||||
|
||||
use reactive_graph::owner::StoredValue;
|
||||
use reactive_graph::owner::{LocalStorage, StoredValue};
|
||||
use std::{fmt, rc::Rc, sync::Arc};
|
||||
|
||||
/// A wrapper trait for calling callbacks.
|
||||
pub trait Callable<In: 'static, Out: 'static = ()> {
|
||||
/// calls the callback with the specified argument.
|
||||
fn call(&self, input: In) -> Out;
|
||||
fn run(&self, input: In) -> Out;
|
||||
}
|
||||
|
||||
/// A callback type that is not required to be `Send + Sync`.
|
||||
pub struct UnsyncCallback<In: 'static, Out: 'static = ()>(
|
||||
Rc<dyn Fn(In) -> Out>,
|
||||
StoredValue<Rc<dyn Fn(In) -> Out>, LocalStorage>,
|
||||
);
|
||||
|
||||
impl<In> fmt::Debug for UnsyncCallback<In> {
|
||||
@@ -61,9 +61,11 @@ impl<In> fmt::Debug for UnsyncCallback<In> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<In, Out> Copy for UnsyncCallback<In, Out> {}
|
||||
|
||||
impl<In, Out> Clone for UnsyncCallback<In, Out> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Rc::clone(&self.0))
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,74 +75,24 @@ impl<In, Out> UnsyncCallback<In, Out> {
|
||||
where
|
||||
F: Fn(In) -> Out + 'static,
|
||||
{
|
||||
Self(Rc::new(f))
|
||||
Self(StoredValue::new_local(Rc::new(f)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<In: 'static, Out: 'static> Callable<In, Out> for UnsyncCallback<In, Out> {
|
||||
fn call(&self, input: In) -> Out {
|
||||
(self.0)(input)
|
||||
fn run(&self, input: In) -> Out {
|
||||
self.0.with_value(|fun| fun(input))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_fn {
|
||||
($ty:ident) => {
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
impl<F, In, T, Out> From<F> for $ty<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + Send + Sync + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
In: Send + Sync + 'static,
|
||||
{
|
||||
fn from(f: F) -> Self {
|
||||
Self::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
|
||||
paste::paste! {
|
||||
#[cfg(feature = "nightly")]
|
||||
auto trait [<NotRaw $ty>] {}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<A, B> ![<NotRaw $ty>] for $ty<A, B> {}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<F, In, T, Out> From<F> for $ty<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + Send + Sync + [<NotRaw $ty>] + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
In: Send + Sync + 'static
|
||||
{
|
||||
fn from(f: F) -> Self {
|
||||
Self::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_fn!(UnsyncCallback);
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> FnOnce<(In,)> for UnsyncCallback<In, Out> {
|
||||
type Output = Out;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (In,)) -> Self::Output {
|
||||
Callable::call(&self, args.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> FnMut<(In,)> for UnsyncCallback<In, Out> {
|
||||
extern "rust-call" fn call_mut(&mut self, args: (In,)) -> Self::Output {
|
||||
Callable::call(&*self, args.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> Fn<(In,)> for UnsyncCallback<In, Out> {
|
||||
extern "rust-call" fn call(&self, args: (In,)) -> Self::Output {
|
||||
Callable::call(self, args.0)
|
||||
impl<F, In, T, Out> From<F> for UnsyncCallback<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
In: 'static,
|
||||
{
|
||||
fn from(f: F) -> Self {
|
||||
Self::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +108,7 @@ impl<In, Out> Fn<(In,)> for UnsyncCallback<In, Out> {
|
||||
/// ) -> impl IntoView {
|
||||
/// view! {
|
||||
/// <div>
|
||||
/// {render_number.call(42)}
|
||||
/// {render_number.run(42)}
|
||||
/// </div>
|
||||
/// }
|
||||
/// }
|
||||
@@ -181,7 +133,7 @@ impl<In, Out> fmt::Debug for Callback<In, Out> {
|
||||
}
|
||||
|
||||
impl<In, Out> Callable<In, Out> for Callback<In, Out> {
|
||||
fn call(&self, input: In) -> Out {
|
||||
fn run(&self, input: In) -> Out {
|
||||
self.0
|
||||
.try_with_value(|f| f(input))
|
||||
.expect("called a callback that has been disposed")
|
||||
@@ -196,6 +148,17 @@ impl<In, Out> Clone for Callback<In, Out> {
|
||||
|
||||
impl<In, Out> Copy for Callback<In, Out> {}
|
||||
|
||||
impl<F, In, T, Out> From<F> for Callback<In, Out>
|
||||
where
|
||||
F: Fn(In) -> T + Send + Sync + 'static,
|
||||
T: Into<Out> + 'static,
|
||||
In: Send + Sync + 'static,
|
||||
{
|
||||
fn from(f: F) -> Self {
|
||||
Self::new(move |x| f(x).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<In: 'static, Out: 'static> Callback<In, Out> {
|
||||
/// Creates a new callback from the given function.
|
||||
pub fn new<F>(fun: F) -> Self
|
||||
@@ -206,43 +169,6 @@ impl<In: 'static, Out: 'static> Callback<In, Out> {
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_fn!(Callback);
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> FnOnce<(In,)> for Callback<In, Out>
|
||||
where
|
||||
In: Send + Sync + 'static,
|
||||
Out: 'static,
|
||||
{
|
||||
type Output = Out;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (In,)) -> Self::Output {
|
||||
Callable::call(&self, args.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> FnMut<(In,)> for Callback<In, Out>
|
||||
where
|
||||
In: Send + Sync + 'static,
|
||||
Out: 'static,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, args: (In,)) -> Self::Output {
|
||||
Callable::call(&*self, args.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<In, Out> Fn<(In,)> for Callback<In, Out>
|
||||
where
|
||||
In: Send + Sync + 'static,
|
||||
Out: 'static,
|
||||
{
|
||||
extern "rust-call" fn call(&self, args: (In,)) -> Self::Output {
|
||||
Callable::call(self, args.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::callback::{Callback, UnsyncCallback};
|
||||
@@ -263,7 +189,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback_from() {
|
||||
fn runback_from() {
|
||||
let _callback: Callback<(), String> = (|()| "test").into();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::into_view::IntoView;
|
||||
use leptos_macro::component;
|
||||
use reactive_graph::owner::Owner;
|
||||
use reactive_graph::{
|
||||
owner::Owner,
|
||||
signal::{ArcRwSignal, ReadSignal},
|
||||
traits::Set,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
use tachys::{reactive_graph::OwnedView, view::keyed::keyed};
|
||||
|
||||
@@ -68,10 +72,88 @@ where
|
||||
// a) the reactive owner for each row will not be cleared when the whole list updates
|
||||
// b) context provided in each row will not wipe out the others
|
||||
let parent = Owner::current().expect("no reactive owner");
|
||||
let children = move |child| {
|
||||
let children = move |_, child| {
|
||||
let owner = parent.with(Owner::new);
|
||||
let view = owner.with(|| children(child));
|
||||
OwnedView::new_with_owner(view, owner)
|
||||
(|_| {}, OwnedView::new_with_owner(view, owner))
|
||||
};
|
||||
move || keyed(each(), key.clone(), children.clone())
|
||||
}
|
||||
|
||||
/// Iterates over children and displays them, keyed by the `key` function given.
|
||||
///
|
||||
/// Compared with For, it has an additional index parameter, which can be used to obtain the current index in real time.
|
||||
///
|
||||
/// This is much more efficient than naively iterating over nodes with `.iter().map(|n| view! { ... })...`,
|
||||
/// as it avoids re-creating DOM nodes that are not being changed.
|
||||
///
|
||||
/// ```
|
||||
/// # use leptos::prelude::*;
|
||||
///
|
||||
/// #[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
/// struct Counter {
|
||||
/// id: usize,
|
||||
/// count: RwSignal<i32>
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn Counters() -> impl IntoView {
|
||||
/// let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);
|
||||
///
|
||||
/// view! {
|
||||
/// <div>
|
||||
/// <ForEnumerate
|
||||
/// // a function that returns the items we're iterating over; a signal is fine
|
||||
/// each=move || counters.get()
|
||||
/// // a unique key for each item
|
||||
/// key=|counter| counter.id
|
||||
/// // renders each item to a view
|
||||
/// children={move |index: ReadSignal<usize>, counter: Counter| {
|
||||
/// view! {
|
||||
/// <button>{move || index.get()} ". Value: " {move || counter.count.get()}</button>
|
||||
/// }
|
||||
/// }}
|
||||
/// />
|
||||
/// </div>
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
|
||||
#[component]
|
||||
pub fn ForEnumerate<IF, I, T, EF, N, KF, K>(
|
||||
/// Items over which the component should iterate.
|
||||
each: IF,
|
||||
/// A key function that will be applied to each item.
|
||||
key: KF,
|
||||
/// A function that takes the index and the item, and returns the view that will be displayed for each item.
|
||||
children: EF,
|
||||
) -> impl IntoView
|
||||
where
|
||||
IF: Fn() -> I + Send + 'static,
|
||||
I: IntoIterator<Item = T> + Send + 'static,
|
||||
EF: Fn(ReadSignal<usize>, T) -> N + Send + Clone + 'static,
|
||||
N: IntoView + 'static,
|
||||
KF: Fn(&T) -> K + Send + Clone + 'static,
|
||||
K: Eq + Hash + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
// this takes the owner of the For itself
|
||||
// this will end up with N + 1 children
|
||||
// 1) the effect for the `move || keyed(...)` updates
|
||||
// 2) an owner for each child
|
||||
//
|
||||
// this means
|
||||
// a) the reactive owner for each row will not be cleared when the whole list updates
|
||||
// b) context provided in each row will not wipe out the others
|
||||
let parent = Owner::current().expect("no reactive owner");
|
||||
let children = move |index, child| {
|
||||
let owner = parent.with(Owner::new);
|
||||
let (index, set_index) = ArcRwSignal::new(index).split();
|
||||
let view = owner.with(|| children(index.into(), child));
|
||||
(
|
||||
move |index| set_index.set(index),
|
||||
OwnedView::new_with_owner(view, owner),
|
||||
)
|
||||
};
|
||||
move || keyed(each(), key.clone(), children.clone())
|
||||
}
|
||||
@@ -86,7 +168,7 @@ mod tests {
|
||||
fn creates_list() {
|
||||
Owner::new().with(|| {
|
||||
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
let list: HtmlElement<_, _, _, Dom> = view! {
|
||||
let list: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<ol>
|
||||
<For each=move || values.get() key=|i| *i let:i>
|
||||
<li>{i}</li>
|
||||
@@ -100,4 +182,37 @@ mod tests {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_list_enumerate() {
|
||||
Owner::new().with(|| {
|
||||
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
let list: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<ol>
|
||||
<ForEnumerate each=move || values.get() key=|i| *i let(index, i)>
|
||||
<li>{move || index.get()}"-"{i}</li>
|
||||
</ForEnumerate>
|
||||
</ol>
|
||||
};
|
||||
assert_eq!(
|
||||
list.to_html(),
|
||||
"<ol><li>0<!>-<!>1</li><li>1<!>-<!>2</li><li>2<!>-<!>3</li><li>3\
|
||||
<!>-<!>4</li><li>4<!>-<!>5</li><!></ol>"
|
||||
);
|
||||
|
||||
let list: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<ol>
|
||||
<ForEnumerate each=move || values.get() key=|i| *i let(index, i)>
|
||||
<li>{move || index.get()}"-"{i}</li>
|
||||
</ForEnumerate>
|
||||
</ol>
|
||||
};
|
||||
values.set(vec![5, 4, 1, 2, 3]);
|
||||
assert_eq!(
|
||||
list.to_html(),
|
||||
"<ol><li>0<!>-<!>5</li><li>1<!>-<!>4</li><li>2<!>-<!>1</li><li>3\
|
||||
<!>-<!>2</li><li>4<!>-<!>3</li><!></ol>"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,13 @@ pub fn AutoReload(
|
||||
leptos_config::ReloadWSProtocol::WSS => "'wss://'",
|
||||
};
|
||||
|
||||
let script = include_str!("reload_script.js");
|
||||
view! { <script nonce=nonce>{format!("{script}({reload_port:?}, {protocol})")}</script> }
|
||||
let script = format!(
|
||||
"(function (reload_port, protocol) {{ {} {} }})({reload_port:?}, \
|
||||
{protocol})",
|
||||
leptos_hot_reload::HOT_RELOAD_JS,
|
||||
include_str!("reload_script.js")
|
||||
);
|
||||
view! { <script nonce=nonce>{script}</script> }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
(function (reload_port, protocol) {
|
||||
let host = window.location.hostname;
|
||||
let ws = new WebSocket(`${protocol}${host}:${reload_port}/live_reload`);
|
||||
ws.onmessage = (ev) => {
|
||||
@@ -20,4 +19,3 @@ ws.onmessage = (ev) => {
|
||||
}
|
||||
};
|
||||
ws.onclose = () => console.warn('Live-reload stopped. Manual reload necessary.');
|
||||
})
|
||||
|
||||
@@ -1,18 +1,50 @@
|
||||
use std::borrow::Cow;
|
||||
use tachys::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::dom::Dom,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
view::{add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml},
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
|
||||
ToTemplate,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct View<T>(T)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct View<T>
|
||||
where
|
||||
T: Sized;
|
||||
T: Sized,
|
||||
{
|
||||
inner: T,
|
||||
#[cfg(debug_assertions)]
|
||||
view_marker: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
impl<T> View<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
#[cfg(debug_assertions)]
|
||||
view_marker: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
self.inner
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_view_marker(
|
||||
#[allow(unused_mut)] // used in debug
|
||||
mut self,
|
||||
#[allow(unused_variables)] // used in debug
|
||||
view_marker: impl Into<Cow<'static, str>>,
|
||||
) -> Self {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
self.view_marker = Some(view_marker.into());
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,33 +60,37 @@ where
|
||||
T: Sized + Render<Dom> + RenderHtml<Dom> + Send, //+ AddAnyAttr<Dom>,
|
||||
{
|
||||
fn into_view(self) -> View<Self> {
|
||||
View(self)
|
||||
View {
|
||||
inner: self,
|
||||
#[cfg(debug_assertions)]
|
||||
view_marker: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoView> Render<Dom> for View<T> {
|
||||
impl<T: Render<Rndr>, Rndr: Renderer> Render<Rndr> for View<T> {
|
||||
type State = T::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.0.build()
|
||||
self.inner.build()
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.0.rebuild(state)
|
||||
self.inner.rebuild(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoView> RenderHtml<Dom> for View<T> {
|
||||
impl<T: RenderHtml<Rndr>, Rndr: Renderer> RenderHtml<Rndr> for View<T> {
|
||||
type AsyncOutput = T::AsyncOutput;
|
||||
|
||||
const MIN_LENGTH: usize = <T as RenderHtml<Dom>>::MIN_LENGTH;
|
||||
const MIN_LENGTH: usize = <T as RenderHtml<Rndr>>::MIN_LENGTH;
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
self.0.resolve().await
|
||||
self.inner.resolve().await
|
||||
}
|
||||
|
||||
fn dry_resolve(&mut self) {
|
||||
self.0.dry_resolve();
|
||||
self.inner.dry_resolve();
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
@@ -64,8 +100,20 @@ impl<T: IntoView> RenderHtml<Dom> for View<T> {
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
self.0
|
||||
#[cfg(debug_assertions)]
|
||||
let vm = self.view_marker.to_owned();
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(vm) = vm.as_ref() {
|
||||
buf.push_str(&format!("<!--hot-reload|{vm}|open-->"));
|
||||
}
|
||||
|
||||
self.inner
|
||||
.to_html_with_buf(buf, position, escape, mark_branches);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(vm) = vm.as_ref() {
|
||||
buf.push_str(&format!("<!--hot-reload|{vm}|close-->"));
|
||||
}
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
@@ -77,35 +125,70 @@ impl<T: IntoView> RenderHtml<Dom> for View<T> {
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.to_html_async_with_buf::<OUT_OF_ORDER>(
|
||||
#[cfg(debug_assertions)]
|
||||
let vm = self.view_marker.to_owned();
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(vm) = vm.as_ref() {
|
||||
buf.push_sync(&format!("<!--hot-reload|{vm}|open-->"));
|
||||
}
|
||||
|
||||
self.inner.to_html_async_with_buf::<OUT_OF_ORDER>(
|
||||
buf,
|
||||
position,
|
||||
escape,
|
||||
mark_branches,
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(vm) = vm.as_ref() {
|
||||
buf.push_sync(&format!("<!--hot-reload|{vm}|close-->"));
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Dom>,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.0.hydrate::<FROM_SERVER>(cursor, position)
|
||||
self.inner.hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoView> AddAnyAttr<Dom> for View<T> {
|
||||
type Output<SomeNewAttr: Attribute<Dom>> =
|
||||
<T as AddAnyAttr<Dom>>::Output<SomeNewAttr>;
|
||||
impl<T: ToTemplate> ToTemplate for View<T> {
|
||||
fn to_template(
|
||||
buf: &mut String,
|
||||
class: &mut String,
|
||||
style: &mut String,
|
||||
inner_html: &mut String,
|
||||
position: &mut Position,
|
||||
) {
|
||||
T::to_template(buf, class, style, inner_html, position);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute<Dom>>(
|
||||
impl<T: AddAnyAttr<Rndr>, Rndr> AddAnyAttr<Rndr> for View<T>
|
||||
where
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = View<T::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml<Dom>,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
self.0.add_any_attr(attr)
|
||||
let View {
|
||||
inner,
|
||||
#[cfg(debug_assertions)]
|
||||
view_marker,
|
||||
} = self;
|
||||
View {
|
||||
inner: inner.add_any_attr(attr),
|
||||
#[cfg(debug_assertions)]
|
||||
view_marker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,8 +142,6 @@
|
||||
|
||||
#![cfg_attr(feature = "nightly", feature(fn_traits))]
|
||||
#![cfg_attr(feature = "nightly", feature(unboxed_closures))]
|
||||
#![cfg_attr(feature = "nightly", feature(auto_traits))]
|
||||
#![cfg_attr(feature = "nightly", feature(negative_impls))]
|
||||
|
||||
extern crate self as leptos;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::{logging, IntoView};
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::logging;
|
||||
use crate::IntoView;
|
||||
use any_spawner::Executor;
|
||||
use reactive_graph::owner::Owner;
|
||||
#[cfg(debug_assertions)]
|
||||
|
||||
@@ -442,3 +442,101 @@ where
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper that prevents [`Suspense`] from waiting for any resource reads that happen inside
|
||||
/// `Unsuspend`.
|
||||
pub struct Unsuspend<T>(Box<dyn FnOnce() -> T + Send>);
|
||||
|
||||
impl<T> Unsuspend<T> {
|
||||
/// Wraps the given function, such that it is not called until all resources are ready.
|
||||
pub fn new(fun: impl FnOnce() -> T + Send + 'static) -> Self {
|
||||
Self(Box::new(fun))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> Render<Rndr> for Unsuspend<T>
|
||||
where
|
||||
T: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = T::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
(self.0)().build()
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
(self.0)().rebuild(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> AddAnyAttr<Rndr> for Unsuspend<T>
|
||||
where
|
||||
T: AddAnyAttr<Rndr> + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> =
|
||||
Unsuspend<T::Output<SomeNewAttr::CloneableOwned>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let attr = attr.into_cloneable_owned();
|
||||
Unsuspend::new(move || (self.0)().add_any_attr(attr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> RenderHtml<Rndr> for Unsuspend<T>
|
||||
where
|
||||
T: RenderHtml<Rndr> + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = T::MIN_LENGTH;
|
||||
|
||||
fn dry_resolve(&mut self) {}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
(self.0)().to_html_with_buf(buf, position, escape, mark_branches);
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
(self.0)().to_html_async_with_buf::<OUT_OF_ORDER>(
|
||||
buf,
|
||||
position,
|
||||
escape,
|
||||
mark_branches,
|
||||
);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(self.0)().hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ fn simple_ssr_test() {
|
||||
use leptos::prelude::*;
|
||||
|
||||
let (value, set_value) = signal(0);
|
||||
let rendered: HtmlElement<_, _, _, Dom> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div>
|
||||
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
|
||||
<span>"Value: " {move || value.get().to_string()} "!"</span>
|
||||
@@ -36,7 +36,7 @@ fn ssr_test_with_components() {
|
||||
}
|
||||
}
|
||||
|
||||
let rendered: HtmlElement<_, _, _, Dom> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div class="counters">
|
||||
<Counter initial_value=1/>
|
||||
<Counter initial_value=2/>
|
||||
@@ -66,7 +66,7 @@ fn ssr_test_with_snake_case_components() {
|
||||
</div>
|
||||
}
|
||||
}
|
||||
let rendered: HtmlElement<_, _, _, Dom> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div class="counters">
|
||||
<SnakeCaseCounter initial_value=1/>
|
||||
<SnakeCaseCounter initial_value=2/>
|
||||
@@ -86,7 +86,7 @@ fn test_classes() {
|
||||
use leptos::prelude::*;
|
||||
|
||||
let (value, _set_value) = signal(5);
|
||||
let rendered: HtmlElement<_, _, _, Dom> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div
|
||||
class="my big"
|
||||
class:a=move || { value.get() > 10 }
|
||||
@@ -104,7 +104,7 @@ fn ssr_with_styles() {
|
||||
|
||||
let (_, set_value) = signal(0);
|
||||
let styles = "myclass";
|
||||
let rendered: HtmlElement<_, _, _, Dom> = view! { class=styles,
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! { class=styles,
|
||||
<div>
|
||||
<button class="btn" on:click=move |_| set_value.update(|value| *value -= 1)>
|
||||
"-1"
|
||||
@@ -124,7 +124,7 @@ fn ssr_option() {
|
||||
use leptos::prelude::*;
|
||||
|
||||
let (_, _) = signal(0);
|
||||
let rendered: HtmlElement<_, _, _, Dom> = view! { <option></option> };
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! { <option></option> };
|
||||
|
||||
assert_eq!(rendered.to_html(), "<option></option>");
|
||||
}
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
[package]
|
||||
name = "leptos_config"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Configuration for the Leptos web framework."
|
||||
readme = "../README.md"
|
||||
version = { workspace = true }
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
config = { version = "0.14", default-features = false, features = ["toml", "convert-case"] }
|
||||
regex = "1.7.0"
|
||||
serde = { version = "1.0.151", features = ["derive"] }
|
||||
thiserror = "1.0.38"
|
||||
typed-builder = "0.18"
|
||||
config = { version = "0.14.0", default-features = false, features = [
|
||||
"toml",
|
||||
"convert-case",
|
||||
] }
|
||||
regex = "1.10"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
typed-builder = "0.19.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["rt", "macros"] }
|
||||
tempfile = "3"
|
||||
tokio = { version = "1.39", features = ["rt", "macros"] }
|
||||
tempfile = "3.12"
|
||||
temp-env = { version = "0.3.6", features = ["async_closure"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
[package]
|
||||
name = "leptos_dom"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "DOM operations for the Leptos web framework."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tachys = { workspace = true }
|
||||
reactive_graph = { workspace = true }
|
||||
or_poisoned = { workspace = true }
|
||||
js-sys = "0.3"
|
||||
send_wrapper = "0.6"
|
||||
tracing = "0.1"
|
||||
wasm-bindgen = "0.2"
|
||||
js-sys = "0.3.69"
|
||||
send_wrapper = "0.6.0"
|
||||
tracing = "0.1.40"
|
||||
wasm-bindgen = "0.2.93"
|
||||
|
||||
[dev-dependencies]
|
||||
leptos = { path = "../leptos" }
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
version = "0.3.70"
|
||||
features = ["Location"]
|
||||
|
||||
[features]
|
||||
|
||||
@@ -9,11 +9,11 @@ crate-type = ["cdylib", "rlib"]
|
||||
[dependencies]
|
||||
leptos = { path = "../../../leptos", features = ["nightly"] }
|
||||
actix-web = { version = "4", optional = true }
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
gloo = { version = "0.8", optional = true }
|
||||
actix-files = { version = "0.6.0", optional = true }
|
||||
wasm-bindgen = { version = "0.2.0", optional = true }
|
||||
gloo = { version = "0.11.0", optional = true }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3"
|
||||
futures = "0.3.0"
|
||||
|
||||
[features]
|
||||
default = ["ssr"]
|
||||
|
||||
@@ -4,11 +4,11 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
gloo = { version = "0.8", features = ["futures"] }
|
||||
console_error_panic_hook = "0.1.0"
|
||||
gloo = { version = "0.11.0", features = ["futures"] }
|
||||
leptos = { path = "../../../leptos", features = ["nightly", "csr", "tracing"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber-wasm = "0.1"
|
||||
tracing = "0.1.0"
|
||||
tracing-subscriber = "0.3.0"
|
||||
tracing-subscriber-wasm = "0.1.0"
|
||||
|
||||
[workspace]
|
||||
|
||||
@@ -5,10 +5,9 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_log = "1.0"
|
||||
log = "0.4.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
[package]
|
||||
name = "leptos_hot_reload"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Utility types used for dev mode and hot-reloading for the Leptos web framework."
|
||||
readme = "../README.md"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
syn = { version = "2", features = [
|
||||
"full",
|
||||
"parsing",
|
||||
"extra-traits",
|
||||
"visit",
|
||||
"printing",
|
||||
anyhow = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
syn = { version = "2.0", features = [
|
||||
"full",
|
||||
"parsing",
|
||||
"extra-traits",
|
||||
"visit",
|
||||
"printing",
|
||||
] }
|
||||
quote = "1"
|
||||
quote = "1.0"
|
||||
rstml = "0.11.2"
|
||||
proc-macro2 = { version = "1", features = ["span-locations", "nightly"] }
|
||||
parking_lot = "0.12"
|
||||
walkdir = "2"
|
||||
camino = "1.1.3"
|
||||
indexmap = "2"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations", "nightly"] }
|
||||
parking_lot = "0.12.3"
|
||||
walkdir = "2.5"
|
||||
camino = "1.1"
|
||||
indexmap = "2.3"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
console.log("[HOT RELOADING] Connected to server.");
|
||||
console.log("[HOT RELOADING] Connected to server.\n\nNote: `cargo-leptos watch --hot-reload` only works with the `nightly` feature enabled on Leptos.");
|
||||
function patch(json) {
|
||||
try {
|
||||
const views = JSON.parse(json);
|
||||
for (const [id, patches] of views) {
|
||||
console.log("[HOT RELOAD]", id, patches);
|
||||
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT),
|
||||
open = `leptos-view|${id}|open`,
|
||||
close = `leptos-view|${id}|close`;
|
||||
open = `hot-reload|${id}|open`,
|
||||
close = `hot-reload|${id}|close`;
|
||||
let start, end;
|
||||
const instances = [];
|
||||
while (walker.nextNode()) {
|
||||
@@ -259,11 +259,11 @@ function patch(json) {
|
||||
node: walker.currentNode,
|
||||
});
|
||||
} else if (walker.currentNode.nodeType == Node.COMMENT_NODE) {
|
||||
if (walker.currentNode.textContent.trim().startsWith("leptos-view")) {
|
||||
if (walker.currentNode.textContent.trim().startsWith("hot-reload")) {
|
||||
if (walker.currentNode.textContent.trim().endsWith("-children|open")) {
|
||||
const startingName = walker.currentNode.textContent.trim();
|
||||
const componentName = startingName.replace("-children|open").replace("leptos-view|");
|
||||
const endingName = `leptos-view|${componentName}-children|close`;
|
||||
const componentName = startingName.replace("-children|open").replace("hot-reload|");
|
||||
const endingName = `hot-reload|${componentName}-children|close`;
|
||||
let start = walker.currentNode;
|
||||
let depth = 1;
|
||||
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
[package]
|
||||
name = "leptos_macro"
|
||||
version = "0.7.0-beta"
|
||||
edition = "2021"
|
||||
version = "0.7.0-beta2"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "view macro for the Leptos web framework."
|
||||
readme = "../README.md"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
attribute-derive = { version = "0.9", features = ["syn-full"]}
|
||||
cfg-if = "1"
|
||||
html-escape = "0.2"
|
||||
itertools = "0.12"
|
||||
prettyplease = "0.2.4"
|
||||
proc-macro-error = { version = "1", default-features = false }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
attribute-derive = { version = "0.9.2", features = ["syn-full"] }
|
||||
cfg-if = "1.0"
|
||||
html-escape = "0.2.13"
|
||||
itertools = "0.13.0"
|
||||
prettyplease = "0.2.20"
|
||||
proc-macro-error = { version = "1.0", default-features = false }
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
rstml = "0.11.2"
|
||||
leptos_hot_reload = { workspace = true }
|
||||
server_fn_macro = { workspace = true }
|
||||
convert_case = "0.6.0"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
tracing = "0.1.37"
|
||||
uuid = { version = "1.10", features = ["v4"] }
|
||||
tracing = "0.1.40"
|
||||
|
||||
[dev-dependencies]
|
||||
log = "0.4"
|
||||
typed-builder = "0.18"
|
||||
trybuild = "1"
|
||||
log = "0.4.22"
|
||||
typed-builder = "0.19.1"
|
||||
trybuild = "1.0"
|
||||
leptos = { path = "../leptos" }
|
||||
server_fn = { path = "../server_fn", features = ["cbor"] }
|
||||
insta = "1.29"
|
||||
serde = "1"
|
||||
insta = "1.39"
|
||||
serde = "1.0"
|
||||
|
||||
[features]
|
||||
csr = []
|
||||
|
||||
@@ -19,6 +19,7 @@ use syn::{
|
||||
pub struct Model {
|
||||
is_island: bool,
|
||||
docs: Docs,
|
||||
unknown_attrs: UnknownAttrs,
|
||||
vis: Visibility,
|
||||
name: Ident,
|
||||
props: Vec<Prop>,
|
||||
@@ -32,6 +33,7 @@ impl Parse for Model {
|
||||
convert_impl_trait_to_generic(&mut item.sig);
|
||||
|
||||
let docs = Docs::new(&item.attrs);
|
||||
let unknown_attrs = UnknownAttrs::new(&item.attrs);
|
||||
|
||||
let props = item
|
||||
.sig
|
||||
@@ -61,6 +63,7 @@ impl Parse for Model {
|
||||
Ok(Self {
|
||||
is_island: false,
|
||||
docs,
|
||||
unknown_attrs,
|
||||
vis: item.vis.clone(),
|
||||
name: convert_from_snake_case(&item.sig.ident),
|
||||
props,
|
||||
@@ -100,6 +103,7 @@ impl ToTokens for Model {
|
||||
let Self {
|
||||
is_island,
|
||||
docs,
|
||||
unknown_attrs,
|
||||
vis,
|
||||
name,
|
||||
props,
|
||||
@@ -457,6 +461,7 @@ impl ToTokens for Model {
|
||||
}
|
||||
} */
|
||||
|
||||
#unknown_attrs
|
||||
#docs_and_prop_docs
|
||||
#[allow(non_snake_case, clippy::too_many_arguments)]
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
@@ -496,7 +501,10 @@ pub struct DummyModel {
|
||||
|
||||
impl Parse for DummyModel {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
let mut attrs = input.call(Attribute::parse_outer)?;
|
||||
// Drop unknown attributes like #[deprecated]
|
||||
drain_filter(&mut attrs, |attr| !attr.path().is_ident("doc"));
|
||||
|
||||
let vis: Visibility = input.parse()?;
|
||||
let sig: Signature = input.parse()?;
|
||||
|
||||
@@ -745,6 +753,37 @@ impl Docs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnknownAttrs(Vec<(TokenStream, Span)>);
|
||||
|
||||
impl UnknownAttrs {
|
||||
pub fn new(attrs: &[Attribute]) -> Self {
|
||||
let attrs = attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if attr.path().is_ident("doc") {
|
||||
if let Meta::NameValue(_) = &attr.meta {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some((attr.into_token_stream(), attr.span()))
|
||||
})
|
||||
.collect_vec();
|
||||
Self(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for UnknownAttrs {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let s = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|(attr, span)| quote_spanned!(*span=> #attr))
|
||||
.collect::<TokenStream>();
|
||||
tokens.append_all(s);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, FromAttr)]
|
||||
#[attribute(ident = prop)]
|
||||
struct PropOpt {
|
||||
|
||||
@@ -13,12 +13,14 @@ use component::DummyModel;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenTree};
|
||||
use quote::{quote, ToTokens};
|
||||
use std::str::FromStr;
|
||||
use syn::{parse_macro_input, spanned::Spanned, token::Pub, Visibility};
|
||||
|
||||
mod params;
|
||||
mod view;
|
||||
use crate::component::unmodified_fn_name_from_fn_name;
|
||||
mod component;
|
||||
mod memo;
|
||||
mod slice;
|
||||
mod slot;
|
||||
|
||||
@@ -302,7 +304,11 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
||||
let parser = rstml::Parser::new(config);
|
||||
let (nodes, errors) = parser.parse_recoverable(tokens).split_vec();
|
||||
let errors = errors.into_iter().map(|e| e.emit_as_expr_tokens());
|
||||
let nodes_output = view::render_view(&nodes, global_class.as_ref(), None);
|
||||
let nodes_output = view::render_view(
|
||||
&nodes,
|
||||
global_class.as_ref(),
|
||||
normalized_call_site(proc_macro::Span::call_site()),
|
||||
);
|
||||
quote! {
|
||||
{
|
||||
#(#errors;)*
|
||||
@@ -312,6 +318,45 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
fn normalized_call_site(site: proc_macro::Span) -> Option<String> {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(debug_assertions, feature = "nightly"))] {
|
||||
Some(leptos_hot_reload::span_to_stable_id(
|
||||
site.source_file().path(),
|
||||
site.start().line()
|
||||
))
|
||||
} else {
|
||||
_ = site;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This behaves like the [`view`] macro, but loads the view from an external file instead of
|
||||
/// parsing it inline.
|
||||
///
|
||||
/// This is designed to allow editing views in a separate file, if this improves a user's workflow.
|
||||
///
|
||||
/// The file is loaded and parsed during proc-macro execution, and its path is resolved relative to
|
||||
/// the crate root rather than relative to the file from which it is called.
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro]
|
||||
pub fn include_view(tokens: TokenStream) -> TokenStream {
|
||||
let file_name = syn::parse::<syn::LitStr>(tokens).unwrap_or_else(|_| {
|
||||
abort!(
|
||||
Span::call_site(),
|
||||
"the only supported argument is a string literal"
|
||||
);
|
||||
});
|
||||
let file =
|
||||
std::fs::read_to_string(file_name.value()).unwrap_or_else(|_| {
|
||||
abort!(Span::call_site(), "could not open file");
|
||||
});
|
||||
let tokens = proc_macro2::TokenStream::from_str(&file)
|
||||
.unwrap_or_else(|e| abort!(Span::call_site(), e));
|
||||
view(tokens.into())
|
||||
}
|
||||
|
||||
/// Annotates a function so that it can be used with your template as a Leptos `<Component/>`.
|
||||
///
|
||||
/// The `#[component]` macro allows you to annotate plain Rust functions as components
|
||||
@@ -857,3 +902,35 @@ pub fn params_derive(
|
||||
pub fn slice(input: TokenStream) -> TokenStream {
|
||||
slice::slice_impl(input)
|
||||
}
|
||||
|
||||
/// Generates a `memo` into a struct with a default getter.
|
||||
///
|
||||
/// Can be used to access deeply nested fields within a global state object.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use leptos::prelude::*;
|
||||
/// # use leptos_macro::memo;
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// pub struct Outer {
|
||||
/// count: i32,
|
||||
/// inner: Inner,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// pub struct Inner {
|
||||
/// inner_count: i32,
|
||||
/// inner_name: String,
|
||||
/// }
|
||||
///
|
||||
/// let outer_signal = RwSignal::new(Outer::default());
|
||||
///
|
||||
/// let count = memo!(outer_signal.count);
|
||||
///
|
||||
/// let inner_count = memo!(outer_signal.inner.inner_count);
|
||||
/// let inner_name = memo!(outer_signal.inner.inner_name);
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn memo(input: TokenStream) -> TokenStream {
|
||||
memo::memo_impl(input)
|
||||
}
|
||||
|
||||
56
leptos_macro/src/memo.rs
Normal file
56
leptos_macro/src/memo.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input,
|
||||
punctuated::Punctuated,
|
||||
Token,
|
||||
};
|
||||
|
||||
struct MemoMacroInput {
|
||||
root: syn::Ident,
|
||||
path: Punctuated<syn::Member, Token![.]>,
|
||||
}
|
||||
|
||||
impl Parse for MemoMacroInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let root: syn::Ident = input.parse()?;
|
||||
input.parse::<Token![.]>()?;
|
||||
// do not accept trailing punctuation
|
||||
let path: Punctuated<syn::Member, Token![.]> =
|
||||
Punctuated::parse_separated_nonempty(input)?;
|
||||
|
||||
if path.is_empty() {
|
||||
return Err(input.error("expected identifier"));
|
||||
}
|
||||
|
||||
if !input.is_empty() {
|
||||
return Err(input.error("unexpected token"));
|
||||
}
|
||||
|
||||
Ok(Self { root, path })
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MemoMacroInput {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let root = &self.root;
|
||||
let path = &self.path;
|
||||
|
||||
tokens.extend(quote! {
|
||||
::leptos::reactive_graph::computed::Memo::new(
|
||||
move |_| {
|
||||
use ::leptos::reactive_graph::traits::With;
|
||||
#root.with(|st: _| st.#path.clone())
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memo_impl(tokens: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(tokens as MemoMacroInput);
|
||||
input.into_token_stream().into()
|
||||
}
|
||||
@@ -78,10 +78,8 @@ pub(crate) fn component_to_tokens(
|
||||
})
|
||||
.unwrap_or_else(|| quote! { #name });
|
||||
|
||||
let value = quote_spanned!(value.span()=> { #value });
|
||||
|
||||
quote_spanned! {attr.span()=>
|
||||
.#name(#[allow(unused_braces)] #value)
|
||||
quote! {
|
||||
.#name(#[allow(unused_braces)] { #value })
|
||||
}
|
||||
});
|
||||
|
||||
@@ -129,8 +127,6 @@ pub(crate) fn component_to_tokens(
|
||||
if idx == spread_marker {
|
||||
return None;
|
||||
}
|
||||
use rstml::node::NodeBlock;
|
||||
use syn::{Expr, ExprRange, RangeLimits, Stmt};
|
||||
|
||||
if let NodeAttribute::Block(block) = attr {
|
||||
let dotted = if let NodeBlock::ValidBlock(block) = block {
|
||||
@@ -264,30 +260,18 @@ pub(crate) fn component_to_tokens(
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let name_ref = quote_spanned! {name.span()=>
|
||||
&#name
|
||||
};
|
||||
|
||||
let build = quote_spanned! {name.span()=>
|
||||
.build()
|
||||
};
|
||||
|
||||
let component_props_builder = quote_spanned! {name.span()=>
|
||||
::leptos::component::component_props_builder(#name_ref #generics)
|
||||
};
|
||||
|
||||
#[allow(unused_mut)] // used in debug
|
||||
let mut component = quote_spanned! {node.span()=>
|
||||
let mut component = quote! {
|
||||
{
|
||||
#[allow(unreachable_code)]
|
||||
::leptos::component::component_view(
|
||||
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||
#name_ref,
|
||||
#component_props_builder
|
||||
&#name,
|
||||
::leptos::component::component_props_builder(&#name #generics)
|
||||
#(#props)*
|
||||
#(#slots)*
|
||||
#children
|
||||
#build
|
||||
.build()
|
||||
)
|
||||
#spreads
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
mod component_builder;
|
||||
mod slot_helper;
|
||||
|
||||
use self::{
|
||||
component_builder::component_to_tokens,
|
||||
slot_helper::{get_slot, slot_to_tokens},
|
||||
@@ -14,8 +13,11 @@ use rstml::node::{
|
||||
KeyedAttribute, Node, NodeAttribute, NodeBlock, NodeElement, NodeName,
|
||||
NodeNameFragment,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use syn::{spanned::Spanned, Expr, ExprRange, Lit, LitStr, RangeLimits, Stmt};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use syn::{
|
||||
spanned::Spanned, Expr, Expr::Tuple, ExprLit, ExprRange, Lit, LitStr,
|
||||
RangeLimits, Stmt,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum TagType {
|
||||
@@ -30,28 +32,60 @@ pub fn render_view(
|
||||
global_class: Option<&TokenTree>,
|
||||
view_marker: Option<String>,
|
||||
) -> Option<TokenStream> {
|
||||
match nodes.len() {
|
||||
let (base, should_add_view) = match nodes.len() {
|
||||
0 => {
|
||||
let span = Span::call_site();
|
||||
Some(quote_spanned! {
|
||||
span => ()
|
||||
})
|
||||
(
|
||||
Some(quote_spanned! {
|
||||
span => ()
|
||||
}),
|
||||
false,
|
||||
)
|
||||
}
|
||||
1 => node_to_tokens(
|
||||
&nodes[0],
|
||||
TagType::Unknown,
|
||||
None,
|
||||
global_class,
|
||||
view_marker.as_deref(),
|
||||
1 => (
|
||||
node_to_tokens(
|
||||
&nodes[0],
|
||||
TagType::Unknown,
|
||||
None,
|
||||
global_class,
|
||||
view_marker.as_deref(),
|
||||
),
|
||||
// only add View wrapper and view marker to a regular HTML
|
||||
// element or component, not to a <{..} /> attribute list
|
||||
match &nodes[0] {
|
||||
Node::Element(node) => !is_spread_marker(node),
|
||||
_ => false,
|
||||
},
|
||||
),
|
||||
_ => fragment_to_tokens(
|
||||
nodes,
|
||||
TagType::Unknown,
|
||||
None,
|
||||
global_class,
|
||||
view_marker.as_deref(),
|
||||
_ => (
|
||||
fragment_to_tokens(
|
||||
nodes,
|
||||
TagType::Unknown,
|
||||
None,
|
||||
global_class,
|
||||
view_marker.as_deref(),
|
||||
),
|
||||
true,
|
||||
),
|
||||
}
|
||||
};
|
||||
base.map(|view| {
|
||||
if !should_add_view {
|
||||
view
|
||||
} else if let Some(vm) = view_marker {
|
||||
quote! {
|
||||
::leptos::prelude::View::new(
|
||||
#view
|
||||
)
|
||||
.with_view_marker(#vm)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
::leptos::prelude::View::new(
|
||||
#view
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn element_children_to_tokens(
|
||||
@@ -208,6 +242,26 @@ pub(crate) fn element_to_tokens(
|
||||
global_class: Option<&TokenTree>,
|
||||
view_marker: Option<&str>,
|
||||
) -> Option<TokenStream> {
|
||||
// check for duplicate attribute names and emit an error for all subsequent ones
|
||||
let mut names = HashSet::new();
|
||||
for attr in node.attributes() {
|
||||
if let NodeAttribute::Attribute(attr) = attr {
|
||||
let mut name = attr.key.to_string();
|
||||
if let Some(tuple_name) = tuple_name(&name, attr) {
|
||||
name.push(':');
|
||||
name.push_str(&tuple_name);
|
||||
}
|
||||
if names.contains(&name) {
|
||||
proc_macro_error::emit_error!(
|
||||
attr.span(),
|
||||
format!("This element already has a `{name}` attribute.")
|
||||
);
|
||||
} else {
|
||||
names.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let name = node.name();
|
||||
if is_component_node(node) {
|
||||
if let Some(slot) = get_slot(node) {
|
||||
@@ -520,7 +574,7 @@ pub(crate) fn attribute_absolute(
|
||||
} else if id == "use" {
|
||||
let key = &parts[1];
|
||||
let param = if let Some(value) = node.value() {
|
||||
quote!(::std::convert::Into::into(#value))
|
||||
quote!(#value)
|
||||
} else {
|
||||
quote_spanned!(node.key.span()=> ().into())
|
||||
};
|
||||
@@ -641,20 +695,14 @@ pub(crate) fn event_type_and_handler(
|
||||
let event_type = if is_custom {
|
||||
event_type
|
||||
} else if let Some(ev_name) = event_name_ident {
|
||||
let span = ev_name.span();
|
||||
quote_spanned! {
|
||||
span => #ev_name
|
||||
}
|
||||
quote! { #ev_name }
|
||||
} else {
|
||||
event_type
|
||||
};
|
||||
|
||||
let event_type = if is_force_undelegated {
|
||||
let undelegated = if let Some(undelegated) = undelegated_ident {
|
||||
let span = undelegated.span();
|
||||
quote_spanned! {
|
||||
span => #undelegated
|
||||
}
|
||||
quote! { #undelegated }
|
||||
} else {
|
||||
quote! { undelegated }
|
||||
};
|
||||
@@ -1071,10 +1119,30 @@ pub(crate) fn directive_call_from_attribute_node(
|
||||
let handler = syn::Ident::new(directive_name, attr.key.span());
|
||||
|
||||
let param = if let Some(value) = attr.value() {
|
||||
quote!(::std::convert::Into::into(#value))
|
||||
quote!(#value)
|
||||
} else {
|
||||
quote_spanned!(attr.key.span()=> ().into())
|
||||
};
|
||||
|
||||
quote! { .directive(#handler, #[allow(clippy::useless_conversion)] #param) }
|
||||
}
|
||||
|
||||
fn tuple_name(name: &str, node: &KeyedAttribute) -> Option<String> {
|
||||
if name == "style" || name == "class" {
|
||||
if let Some(Tuple(tuple)) = node.value() {
|
||||
{
|
||||
if tuple.elems.len() == 2 {
|
||||
let style_name = &tuple.elems[0];
|
||||
if let Expr::Lit(ExprLit {
|
||||
lit: Lit::Str(s), ..
|
||||
}) = style_name
|
||||
{
|
||||
return Some(s.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -59,10 +59,8 @@ pub(crate) fn slot_to_tokens(
|
||||
})
|
||||
.unwrap_or_else(|| quote! { #name });
|
||||
|
||||
let value = quote_spanned!(value.span()=> { #value });
|
||||
|
||||
quote_spanned! {attr.span()=>
|
||||
.#name(#[allow(unused_braces)] #value)
|
||||
quote! {
|
||||
.#name(#[allow(unused_braces)] { #value })
|
||||
}
|
||||
});
|
||||
|
||||
@@ -136,8 +134,9 @@ pub(crate) fn slot_to_tokens(
|
||||
items_to_bind.iter().map(|ident| quote! { #ident, });
|
||||
|
||||
let clonables = items_to_clone.iter().map(|ident| {
|
||||
let ident_ref = quote_spanned!(ident.span()=> &#ident);
|
||||
quote! { let #ident = ::core::clone::Clone::clone(#ident_ref); }
|
||||
quote_spanned! {ident.span()=>
|
||||
let #ident = ::core::clone::Clone::clone(&#ident);
|
||||
}
|
||||
});
|
||||
|
||||
if bindables.len() > 0 {
|
||||
@@ -169,7 +168,7 @@ pub(crate) fn slot_to_tokens(
|
||||
.span();
|
||||
let slot = Ident::new(&slot, span);
|
||||
let value = if values.len() > 1 {
|
||||
quote_spanned! {span=>
|
||||
quote! {
|
||||
::std::vec![
|
||||
#(#values)*
|
||||
]
|
||||
|
||||
33
leptos_macro/tests/memo.rs
Normal file
33
leptos_macro/tests/memo.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use leptos::prelude::RwSignal;
|
||||
use leptos_macro::memo;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OuterState {
|
||||
count: i32,
|
||||
inner: InnerState,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub struct InnerState {
|
||||
inner_count: i32,
|
||||
inner_tuple: InnerTuple,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub struct InnerTuple(String);
|
||||
|
||||
#[test]
|
||||
fn green() {
|
||||
let outer_signal = RwSignal::new(OuterState::default());
|
||||
|
||||
let _ = memo!(outer_signal.count);
|
||||
|
||||
let _ = memo!(outer_signal.inner.inner_count);
|
||||
let _ = memo!(outer_signal.inner.inner_tuple.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn red() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/memo/red.rs")
|
||||
}
|
||||
26
leptos_macro/tests/memo/red.rs
Normal file
26
leptos_macro/tests/memo/red.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use leptos::prelude::RwSignal;
|
||||
use leptos_macro::memo;
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub struct OuterState {
|
||||
count: i32,
|
||||
inner: InnerState,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub struct InnerState {
|
||||
inner_count: i32,
|
||||
inner_name: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let outer_signal = RwSignal::new(OuterState::default());
|
||||
|
||||
let _ = memo!();
|
||||
|
||||
let _ = memo!(outer_signal);
|
||||
|
||||
let _ = memo!(outer_signal.);
|
||||
|
||||
let _ = memo!(outer_signal.inner.);
|
||||
}
|
||||
31
leptos_macro/tests/memo/red.stderr
Normal file
31
leptos_macro/tests/memo/red.stderr
Normal file
@@ -0,0 +1,31 @@
|
||||
error: unexpected end of input, expected identifier
|
||||
--> tests/memo/red.rs:19:13
|
||||
|
|
||||
19 | let _ = memo!();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: expected `.`
|
||||
--> tests/memo/red.rs:21:13
|
||||
|
|
||||
21 | let _ = memo!(outer_signal);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unexpected end of input, expected identifier or integer
|
||||
--> tests/memo/red.rs:23:13
|
||||
|
|
||||
23 | let _ = memo!(outer_signal.);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: unexpected end of input, expected identifier or integer
|
||||
--> tests/memo/red.rs:25:13
|
||||
|
|
||||
25 | let _ = memo!(outer_signal.inner.);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `memo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -1,31 +1,31 @@
|
||||
[package]
|
||||
name = "leptos_server"
|
||||
version = { workspace = true }
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "RPC for the Leptos web framework."
|
||||
readme = "../README.md"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22"
|
||||
codee = { version = "0.1", features = ["json_serde"] }
|
||||
base64 = "0.22.1"
|
||||
codee = { version = "0.1.2", features = ["json_serde"] }
|
||||
hydration_context = { workspace = true }
|
||||
reactive_graph = { workspace = true, features = ["hydration"] }
|
||||
server_fn = { workspace = true }
|
||||
tracing = { version = "0.1", optional = true }
|
||||
futures = "0.3"
|
||||
tracing = { version = "0.1.40", optional = true }
|
||||
futures = "0.3.30"
|
||||
|
||||
any_spawner = { workspace = true }
|
||||
tachys = { workspace = true, optional = true, features = ["reactive_graph"] }
|
||||
|
||||
# serialization formats
|
||||
serde = { version = "1"}
|
||||
js-sys = { version = "0.3", optional = true }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
serde_json = { version = "1"}
|
||||
serde = { version = "1.0" }
|
||||
js-sys = { version = "0.3.69", optional = true }
|
||||
wasm-bindgen = { version = "0.2.93", optional = true }
|
||||
serde_json = { version = "1.0" }
|
||||
|
||||
[features]
|
||||
ssr = []
|
||||
|
||||
@@ -22,11 +22,13 @@ use reactive_graph::{
|
||||
graph::{Source, ToAnySubscriber},
|
||||
owner::Owner,
|
||||
prelude::*,
|
||||
signal::{ArcRwSignal, RwSignal},
|
||||
};
|
||||
use std::{future::IntoFuture, ops::Deref};
|
||||
|
||||
pub struct ArcResource<T, Ser = JsonSerdeCodec> {
|
||||
ser: PhantomData<Ser>,
|
||||
refetch: ArcRwSignal<usize>,
|
||||
data: ArcAsyncDerived<T>,
|
||||
}
|
||||
|
||||
@@ -34,6 +36,7 @@ impl<T, Ser> Clone for ArcResource<T, Ser> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ser: self.ser,
|
||||
refetch: self.refetch.clone(),
|
||||
data: self.data.clone(),
|
||||
}
|
||||
}
|
||||
@@ -77,13 +80,19 @@ where
|
||||
let initial = Self::initial_value(&id);
|
||||
let is_ready = initial.is_some();
|
||||
|
||||
let refetch = ArcRwSignal::new(0);
|
||||
let source = ArcMemo::new(move |_| source());
|
||||
let fun = {
|
||||
let source = source.clone();
|
||||
move || fetcher(source.get())
|
||||
let refetch = refetch.clone();
|
||||
move || {
|
||||
refetch.track();
|
||||
fetcher(source.get())
|
||||
}
|
||||
};
|
||||
|
||||
let data = ArcAsyncDerived::new_with_initial(initial, fun);
|
||||
let data =
|
||||
ArcAsyncDerived::new_with_initial_without_spawning(initial, fun);
|
||||
if is_ready {
|
||||
source.with(|_| ());
|
||||
source.add_subscriber(data.to_any_subscriber());
|
||||
@@ -116,9 +125,15 @@ where
|
||||
ArcResource {
|
||||
ser: PhantomData,
|
||||
data,
|
||||
refetch,
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-runs the async function with the current source data.
|
||||
pub fn refetch(&self) {
|
||||
*self.refetch.write() += 1;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(unused)]
|
||||
fn initial_value(id: &SerializedDataId) -> Option<T> {
|
||||
@@ -401,6 +416,7 @@ where
|
||||
{
|
||||
ser: PhantomData<Ser>,
|
||||
data: AsyncDerived<T>,
|
||||
refetch: RwSignal<usize>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static, Ser> Copy for Resource<T, Ser> {}
|
||||
@@ -650,13 +666,19 @@ where
|
||||
T: Send + Sync + 'static,
|
||||
Fut: Future<Output = T> + Send + 'static,
|
||||
{
|
||||
let ArcResource { data, .. }: ArcResource<T, Ser> =
|
||||
let ArcResource { data, refetch, .. }: ArcResource<T, Ser> =
|
||||
ArcResource::new_with_options(source, fetcher, blocking);
|
||||
Resource {
|
||||
ser: PhantomData,
|
||||
data: data.into(),
|
||||
refetch: refetch.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-runs the async function with the current source data.
|
||||
pub fn refetch(&self) {
|
||||
self.refetch.try_update(|n| *n += 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Ser> IntoFuture for Resource<T, Ser>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
[package]
|
||||
name = "leptos_meta"
|
||||
version = "0.7.0-beta"
|
||||
edition = "2021"
|
||||
version = "0.7.0-beta3"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "Tools to set HTML metadata in the Leptos web framework."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { workspace = true }
|
||||
once_cell = "1"
|
||||
once_cell = "1.19"
|
||||
or_poisoned = { workspace = true }
|
||||
indexmap = "2"
|
||||
indexmap = "2.3"
|
||||
send_wrapper = "0.6.0"
|
||||
tracing = "0.1"
|
||||
wasm-bindgen = "0.2"
|
||||
tracing = "0.1.40"
|
||||
wasm-bindgen = "0.2.93"
|
||||
futures = "0.3.30"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
version = "0.3.70"
|
||||
features = ["HtmlLinkElement", "HtmlMetaElement", "HtmlTitleElement"]
|
||||
|
||||
[features]
|
||||
|
||||
@@ -364,6 +364,15 @@ where
|
||||
state: <HtmlElement<E, At, Ch, Dom> as Render<Dom>>::State,
|
||||
}
|
||||
|
||||
impl<E, At, Ch> Drop for RegisteredMetaTagState<E, At, Ch>
|
||||
where
|
||||
HtmlElement<E, At, Ch, Dom>: Render<Dom>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.state.unmount();
|
||||
}
|
||||
}
|
||||
|
||||
fn document_head() -> HtmlHeadElement {
|
||||
let document = document();
|
||||
document.head().unwrap_or_else(|| {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "next_tuple"
|
||||
version = "0.1.0-beta"
|
||||
edition = "2021"
|
||||
version = "0.1.0-beta2"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "A trait to build and extend tuples."
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "oco_ref"
|
||||
edition = "2021"
|
||||
version = "0.2.0"
|
||||
authors = ["Danik Vitek", "Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/leptos-rs/leptos"
|
||||
description = "A smart pointer for storing immutable values with relatively-cheap cloning. (Like a `Cow` meets an `Rc`!)"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = "1"
|
||||
thiserror = "1"
|
||||
serde = "1.0"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
serde_json = "1.0"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user