mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 13:43:01 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a7344ca50 | ||
|
|
921bb616b4 | ||
|
|
5bb7122b93 | ||
|
|
ec2aa3e7a4 | ||
|
|
43959a96c7 | ||
|
|
7925fc4245 | ||
|
|
2bd1ad0f11 | ||
|
|
dd730aa4ac |
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -16,10 +16,10 @@ Please copy and paste the Leptos dependencies and features from your `Cargo.toml
|
||||
|
||||
For example:
|
||||
```toml
|
||||
leptos = { version = "0.3", features = ["serde"] }
|
||||
leptos = { version = "0.3", default-features = false, features = ["serde"] }
|
||||
leptos_axum = { version = "0.3", optional = true }
|
||||
leptos_meta = { version = "0.3"}
|
||||
leptos_router = { version = "0.3"}
|
||||
leptos_meta = { version = "0.3", default-features = false }
|
||||
leptos_router = { version = "0.3", default-features = false }
|
||||
```
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
30
Cargo.toml
30
Cargo.toml
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
resolver="2"
|
||||
members = [
|
||||
# core
|
||||
"leptos",
|
||||
@@ -26,22 +26,22 @@ members = [
|
||||
exclude = ["benchmarks", "examples"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
leptos = { path = "./leptos", version = "0.4.0" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.4.0" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.4.0" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.4.0" }
|
||||
leptos_reactive = { path = "./leptos_reactive", version = "0.4.0" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.4.0" }
|
||||
server_fn = { path = "./server_fn", version = "0.4.0" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.4.0" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.4.0" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.4.0" }
|
||||
leptos_router = { path = "./router", version = "0.4.0" }
|
||||
leptos_meta = { path = "./meta", version = "0.4.0" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.4.0" }
|
||||
leptos = { path = "./leptos", default-features = false, version = "0.3.0" }
|
||||
leptos_dom = { path = "./leptos_dom", default-features = false, version = "0.3.0" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.3.0" }
|
||||
leptos_macro = { path = "./leptos_macro", default-features = false, version = "0.3.0" }
|
||||
leptos_reactive = { path = "./leptos_reactive", default-features = false, version = "0.3.0" }
|
||||
leptos_server = { path = "./leptos_server", default-features = false, version = "0.3.0" }
|
||||
server_fn = { path = "./server_fn", default-features = false, version = "0.3.0" }
|
||||
server_fn_macro = { path = "./server_fn_macro", default-features = false, version = "0.3.0" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", default-features = false, version = "0.3.0" }
|
||||
leptos_config = { path = "./leptos_config", default-features = false, version = "0.3.0" }
|
||||
leptos_router = { path = "./router", version = "0.3.0" }
|
||||
leptos_meta = { path = "./meta", default-features = false, version = "0.3.0" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.3.0" }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
10
README.md
10
README.md
@@ -68,7 +68,7 @@ Here are some resources for learning more about Leptos:
|
||||
|
||||
## `nightly` Note
|
||||
|
||||
Most of the examples assume you’re using `nightly` version of Rust and the `nightly` feature of Leptos. To use `nightly` Rust, you can either set your toolchain globally or on per-project basis.
|
||||
Most of the examples assume you’re using `nightly` version of Rust. For this, you can either set your toolchain globally or on per-project basis.
|
||||
|
||||
To set `nightly` as a default toolchain for all projects (and add the ability to compile Rust to WebAssembly, if you haven’t already):
|
||||
|
||||
@@ -86,9 +86,13 @@ channel = "nightly"
|
||||
targets = ["wasm32-unknown-unknown"]
|
||||
```
|
||||
|
||||
The `nightly` feature enables the function call syntax for accessing and setting signals, as opposed to `.get()` and `.set()`. This leads to a consistent mental model in which accessing a reactive value of any kind (a signal, memo, or derived signal) is always represented as a function call. This is only possible with nightly Rust and the `nightly` feature.
|
||||
If you’re on `stable`, note the following:
|
||||
|
||||
> Note: The `nightly` feature is present on the main branch version right now, but not in 0.3.x. For 0.3.x, nightly is the default and `stable` has a special feature.
|
||||
1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.2", features = ["stable"] }`
|
||||
2. `nightly` enables the function call syntax for accessing and setting signals. If you’re using `stable`,
|
||||
you’ll just call `.get()`, `.set()`, or `.update()` manually. Check out the
|
||||
[`counters_stable` example](https://github.com/leptos-rs/leptos/blob/main/examples/counters_stable/src/main.rs)
|
||||
for examples of the correct API.
|
||||
|
||||
## `cargo-leptos`
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
l021 = { package = "leptos", version = "0.2.1" }
|
||||
leptos = { path = "../leptos", features = ["ssr"] }
|
||||
leptos = { path = "../leptos", default-features = false, features = ["ssr"] }
|
||||
sycamore = { version = "0.8", features = ["ssr"] }
|
||||
yew = { git = "https://github.com/yewstack/yew", features = ["ssr"] }
|
||||
tokio-test = "0.4"
|
||||
|
||||
@@ -34,7 +34,7 @@ category = "Cleanup"
|
||||
script = '''
|
||||
for pw_dir in $(find . -name playwright.config.ts | xargs dirname)
|
||||
do
|
||||
rm -rf $pw_dir/playwright-report pw_dir/playwright pw_dir/test-results
|
||||
rm -rf $pw_dir/playwright-report
|
||||
done
|
||||
'''
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
extend = [{ path = "../cargo-make/common.toml" }]
|
||||
|
||||
[tasks.ci]
|
||||
alias = "verify-flow"
|
||||
|
||||
[tasks.verify-flow]
|
||||
description = "Provides pre and post hooks for verify"
|
||||
dependencies = ["pre-verify", "verify", "post-verify"]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
extend = [{ path = "../cargo-make/playwright.toml" }]
|
||||
|
||||
[tasks.test-e2e]
|
||||
dependencies = ["setup-node", "test-playwright-autostart"]
|
||||
|
||||
[tasks.clean-all]
|
||||
dependencies = ["clean-cargo", "clean-node_modules", "clean-playwright"]
|
||||
@@ -1,119 +0,0 @@
|
||||
[tasks.clean-playwright]
|
||||
description = "Delete playwright directories"
|
||||
category = "Cleanup"
|
||||
script = '''
|
||||
for pw_dir in $(find . -name playwright.config.ts | xargs dirname)
|
||||
do
|
||||
rm -rf $pw_dir/playwright-report pw_dir/playwright pw_dir/test-results
|
||||
done
|
||||
'''
|
||||
|
||||
[tasks.test-playwright-autostart]
|
||||
description = "Run playwright test with server autostart"
|
||||
category = "Test"
|
||||
command = "npm"
|
||||
args = ["run", "e2e:auto-start"]
|
||||
|
||||
[tasks.test-playwright]
|
||||
description = "Run playwright test"
|
||||
category = "Test"
|
||||
script = '''
|
||||
BOLD="\e[1m"
|
||||
GREEN="\e[0;32m"
|
||||
RED="\e[0;31m"
|
||||
RESET="\e[0m"
|
||||
|
||||
project_dir=$CARGO_MAKE_WORKING_DIRECTORY
|
||||
|
||||
# Discover commands
|
||||
if command -v pnpm; then
|
||||
PLAYWRIGHT_CMD=pnpm
|
||||
elif command -v npm; then
|
||||
PLAYWRIGHT_CMD=npx
|
||||
else
|
||||
echo "${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run playwright command
|
||||
for pw_path in $(find . -name playwright.config.ts)
|
||||
do
|
||||
pw_dir=$(dirname $pw_path)
|
||||
cd $pw_dir
|
||||
${PLAYWRIGHT_CMD} playwright test
|
||||
cd $project_dir
|
||||
done
|
||||
'''
|
||||
|
||||
[tasks.test-playwright-ui]
|
||||
description = "Run playwright test --ui"
|
||||
category = "Test"
|
||||
script = '''
|
||||
BOLD="\e[1m"
|
||||
GREEN="\e[0;32m"
|
||||
RED="\e[0;31m"
|
||||
RESET="\e[0m"
|
||||
|
||||
project_dir=$CARGO_MAKE_WORKING_DIRECTORY
|
||||
|
||||
# Discover commands
|
||||
if command -v pnpm; then
|
||||
PLAYWRIGHT_CMD=pnpm
|
||||
elif command -v npm; then
|
||||
PLAYWRIGHT_CMD=npx
|
||||
else
|
||||
echo "${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run playwright command
|
||||
for pw_path in $(find . -name playwright.config.ts)
|
||||
do
|
||||
pw_dir=$(dirname $pw_path)
|
||||
cd $pw_dir
|
||||
${PLAYWRIGHT_CMD} playwright test --ui
|
||||
cd $project_dir
|
||||
done
|
||||
'''
|
||||
|
||||
[tasks.test-playwright-report]
|
||||
description = "Run playwright show-report"
|
||||
category = "Test"
|
||||
script = '''
|
||||
BOLD="\e[1m"
|
||||
GREEN="\e[0;32m"
|
||||
RED="\e[0;31m"
|
||||
RESET="\e[0m"
|
||||
|
||||
project_dir=$CARGO_MAKE_WORKING_DIRECTORY
|
||||
|
||||
# Discover commands
|
||||
if command -v pnpm; then
|
||||
PLAYWRIGHT_CMD=pnpm
|
||||
elif command -v npm; then
|
||||
PLAYWRIGHT_CMD=npx
|
||||
else
|
||||
echo "${RED}${BOLD}ERROR${RESET} - pnpm or npm is required by this task"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run playwright command
|
||||
for pw_path in $(find . -name playwright.config.ts)
|
||||
do
|
||||
pw_dir=$(dirname $pw_path)
|
||||
cd $pw_dir
|
||||
${PLAYWRIGHT_CMD} playwright show-report
|
||||
cd $project_dir
|
||||
done
|
||||
'''
|
||||
|
||||
# ALIASES
|
||||
|
||||
[tasks.pw]
|
||||
dependencies = ["test-playwright"]
|
||||
|
||||
[tasks.pw-ui]
|
||||
dependencies = ["test-playwright-ui"]
|
||||
|
||||
[tasks.pw-report]
|
||||
dependencies = ["test-playwright-report"]
|
||||
@@ -1,22 +0,0 @@
|
||||
[tasks.build]
|
||||
command = "trunk"
|
||||
args = ["build"]
|
||||
|
||||
[tasks.clean-trunk]
|
||||
command = "trunk"
|
||||
args = ["clean"]
|
||||
|
||||
[tasks.start-trunk]
|
||||
command = "trunk"
|
||||
args = ["serve", "--open"]
|
||||
|
||||
[tasks.stop-trunk]
|
||||
script = '''
|
||||
pkill -f "cargo-make"
|
||||
pkill -f "trunk"
|
||||
'''
|
||||
|
||||
# ALIASES
|
||||
|
||||
[tasks.dev]
|
||||
dependencies = ["start-trunk"]
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -19,17 +19,19 @@ console_error_panic_hook = "0.1"
|
||||
futures = "0.3"
|
||||
cfg-if = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4"
|
||||
gloo-net = { git = "https://github.com/rustwasm/gloo" }
|
||||
wasm-bindgen = "=0.2.86"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
default = []
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
@@ -39,10 +41,10 @@ ssr = [
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
]
|
||||
nightly = ["leptos/nightly", "leptos_router/nightly"]
|
||||
stable = ["leptos/stable", "leptos_router/stable"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["actix-files", "actix-web", "leptos_actix", "nightly"]
|
||||
denylist = ["actix-files", "actix-web", "leptos_actix", "stable"]
|
||||
skip_feature_sets = [["ssr", "hydrate"]]
|
||||
|
||||
[package.metadata.leptos]
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos = { path = "../../leptos", features = ["stable"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
20
examples/counters_stable/.gitignore
vendored
20
examples/counters_stable/.gitignore
vendored
@@ -1,20 +0,0 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# Support playwright testing
|
||||
node_modules/
|
||||
test-results/
|
||||
end2end/playwright-report/
|
||||
playwright/.cache/
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Support trunk
|
||||
dist
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr"] }
|
||||
leptos = { path = "../../leptos", features = ["stable"] }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
extend = [
|
||||
{ path = "../cargo-make/main.toml" },
|
||||
{ path = "../cargo-make/trunk_server.toml" },
|
||||
{ path = "../cargo-make/playwright-test.toml" },
|
||||
]
|
||||
extend = [{ path = "../cargo-make/main.toml" }]
|
||||
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
|
||||
4
examples/counters_stable/e2e/.gitignore
vendored
4
examples/counters_stable/e2e/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
83
examples/counters_stable/e2e/package-lock.json
generated
83
examples/counters_stable/e2e/package-lock.json
generated
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"name": "grip",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "grip",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.35.1"
|
||||
}
|
||||
},
|
||||
"node_modules/.pnpm/@playwright+test@1.33.0": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/.pnpm/@types+node@20.2.1/node_modules/@types/node": {
|
||||
"version": "20.2.1",
|
||||
"extraneous": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/.pnpm/playwright-core@1.33.0/node_modules/playwright-core": {
|
||||
"version": "1.33.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz",
|
||||
"integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.35.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
|
||||
"integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.35.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz",
|
||||
"integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"private": "true",
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.35.1"
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !process.env.DEV,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.DEV ? 0 : 10,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.DEV ? 1 : 1,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [["html", { open: "never" }], ["list"]],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: "http://127.0.0.1:8080",
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: "firefox",
|
||||
// use: { ...devices["Desktop Firefox"] },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: "webkit",
|
||||
// use: { ...devices["Desktop Safari"] },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: "cd ../ && trunk serve",
|
||||
// url: "http://127.0.0.1:8080",
|
||||
// reuseExistingServer: false, //!process.env.CI,
|
||||
// },
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./counters_page";
|
||||
|
||||
test.describe("Add 1000 Counters", () => {
|
||||
test("should increment the total count by 1K", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
|
||||
await Promise.all([
|
||||
await ui.goto(),
|
||||
await ui.addOneThousandCountersButton.waitFor(),
|
||||
]);
|
||||
|
||||
await ui.addOneThousandCounters();
|
||||
await ui.addOneThousandCounters();
|
||||
await ui.addOneThousandCounters();
|
||||
|
||||
await expect(ui.total).toHaveText("0");
|
||||
await expect(ui.counters).toHaveText("3000");
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./counters_page";
|
||||
|
||||
test.describe("Add Counter", () => {
|
||||
test("should increment the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
|
||||
await expect(ui.total).toHaveText("0");
|
||||
await expect(ui.counters).toHaveText("3");
|
||||
});
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./counters_page";
|
||||
|
||||
test.describe("Clear Counters", () => {
|
||||
test("should reset the counts", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.clearCounters();
|
||||
|
||||
await expect(ui.total).toHaveText("0");
|
||||
await expect(ui.counters).toHaveText("0");
|
||||
});
|
||||
});
|
||||
@@ -1,75 +0,0 @@
|
||||
import { expect, Locator, Page } from "@playwright/test";
|
||||
|
||||
export class CountersPage {
|
||||
readonly page: Page;
|
||||
readonly addCounterButton: Locator;
|
||||
readonly addOneThousandCountersButton: Locator;
|
||||
readonly clearCountersButton: Locator;
|
||||
readonly decrementCountButton: Locator;
|
||||
readonly incrementCountButton: Locator;
|
||||
|
||||
readonly total: Locator;
|
||||
readonly counters: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
|
||||
this.addCounterButton = page.locator("button", { hasText: "Add Counter" });
|
||||
|
||||
this.addOneThousandCountersButton = page.locator("button", {
|
||||
hasText: "Add 1000 Counters",
|
||||
});
|
||||
|
||||
this.clearCountersButton = page.locator("button", {
|
||||
hasText: "Clear Counters",
|
||||
});
|
||||
|
||||
this.decrementCountButton = page.locator("button", {
|
||||
hasText: "-1",
|
||||
});
|
||||
|
||||
this.incrementCountButton = page.locator("button", {
|
||||
hasText: "+1",
|
||||
});
|
||||
|
||||
this.total = page.getByTestId("total");
|
||||
|
||||
this.counters = page.getByTestId("counters");
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto("/");
|
||||
}
|
||||
|
||||
async addCounter() {
|
||||
await Promise.all([
|
||||
this.addCounterButton.waitFor(),
|
||||
this.addCounterButton.click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async addOneThousandCounters() {
|
||||
this.addOneThousandCountersButton.click();
|
||||
}
|
||||
|
||||
async decrementCount() {
|
||||
await Promise.all([
|
||||
this.decrementCountButton.waitFor(),
|
||||
this.decrementCountButton.click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async incrementCount() {
|
||||
await Promise.all([
|
||||
this.incrementCountButton.waitFor(),
|
||||
this.incrementCountButton.click(),
|
||||
]);
|
||||
}
|
||||
|
||||
async clearCounters() {
|
||||
await Promise.all([
|
||||
this.clearCountersButton.waitFor(),
|
||||
this.clearCountersButton.click(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./counters_page";
|
||||
|
||||
test.describe("Decrement Count", () => {
|
||||
test("should decrement the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.decrementCount();
|
||||
await ui.decrementCount();
|
||||
await ui.decrementCount();
|
||||
|
||||
await expect(ui.total).toHaveText("-3");
|
||||
await expect(ui.counters).toHaveText("1");
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./counters_page";
|
||||
|
||||
test.describe("Increment Count", () => {
|
||||
test("should increment the total count", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
await ui.addCounter();
|
||||
|
||||
await ui.incrementCount();
|
||||
await ui.incrementCount();
|
||||
await ui.incrementCount();
|
||||
|
||||
await expect(ui.total).toHaveText("3");
|
||||
await expect(ui.counters).toHaveText("1");
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { CountersPage } from "./counters_page";
|
||||
|
||||
test.describe("View Counters", () => {
|
||||
test("should_see_the_title", async ({ page }) => {
|
||||
const ui = new CountersPage(page);
|
||||
await ui.goto();
|
||||
|
||||
await expect(page).toHaveTitle("Counters (Stable)");
|
||||
});
|
||||
|
||||
test("should see the initial counts", async ({ page }) => {
|
||||
const counters = new CountersPage(page);
|
||||
await counters.goto();
|
||||
|
||||
await expect(counters.total).toHaveText("0");
|
||||
await expect(counters.counters).toHaveText("0");
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Counters (Stable)</title>
|
||||
<link data-trunk rel="rust" data-wasm-opt="z" data-weak-refs/>
|
||||
</head>
|
||||
<body></body>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"private": "true",
|
||||
"scripts": {
|
||||
"start-server": "trunk serve",
|
||||
"e2e": "cargo make test-playwright",
|
||||
"e2e:auto-start": "start-server-and-test start-server http://127.0.0.1:8080 e2e"
|
||||
},
|
||||
"devDependencies": {
|
||||
"start-server-and-test": "^1.15.4"
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ pub fn Counters(cx: Scope) -> impl IntoView {
|
||||
</button>
|
||||
<p>
|
||||
"Total: "
|
||||
<span id="total" data-testid="total">{move ||
|
||||
<span>{move ||
|
||||
counters.get()
|
||||
.iter()
|
||||
.map(|(_, (count, _))| count.get())
|
||||
@@ -64,7 +64,7 @@ pub fn Counters(cx: Scope) -> impl IntoView {
|
||||
.to_string()
|
||||
}</span>
|
||||
" from "
|
||||
<span id="counters" data-testid="counters">{move || counters.with(|counters| counters.len()).to_string()}</span>
|
||||
<span>{move || counters.with(|counters| counters.len()).to_string()}</span>
|
||||
" counters."
|
||||
</p>
|
||||
<ul>
|
||||
@@ -99,13 +99,13 @@ fn Counter(
|
||||
|
||||
view! { cx,
|
||||
<li>
|
||||
<button id="decrement_count" on:click=move |_| set_value.update(move |value| *value -= 1)>"-1"</button>
|
||||
<button on:click=move |_| set_value.update(move |value| *value -= 1)>"-1"</button>
|
||||
<input type="text"
|
||||
prop:value={move || value.get().to_string()}
|
||||
on:input=input
|
||||
/>
|
||||
<span>{move || value.get().to_string()}</span>
|
||||
<button id="increment_count" on:click=move |_| set_value.update(move |value| *value += 1)>"+1"</button>
|
||||
<button on:click=move |_| set_value.update(move |value| *value += 1)>"+1"</button>
|
||||
<button on:click=move |_| set_counters.update(move |counters| counters.retain(|(counter_id, _)| counter_id != &id))>"x"</button>
|
||||
</li>
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -10,10 +10,12 @@ crate-type = ["cdylib", "rlib"]
|
||||
console_log = "1.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
cfg-if = "1.0.0"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta" }
|
||||
leptos_router = { path = "../../router" }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4.17"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
simple_logger = "4.0.0"
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
reqwasm = "0.5"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
log = "0.4"
|
||||
|
||||
@@ -4,5 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
gtk = { version = "0.5.0", package = "gtk4" }
|
||||
@@ -16,10 +16,12 @@ actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1"
|
||||
cfg-if = "1"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_actix = { path = "../../integrations/actix", default-features = false, optional = true }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
gloo-net = { version = "0.2", features = ["http"] }
|
||||
|
||||
@@ -14,10 +14,12 @@ lto = true
|
||||
console_log = "1.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
cfg-if = "1.0.0"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.0.0"
|
||||
serde = { version = "1.0.148", features = ["derive"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly", "template_macro"] }
|
||||
leptos = { path = "../../leptos", features=["template_macro"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
# used in rand, but we need to enable js feature
|
||||
|
||||
@@ -11,10 +11,12 @@ axum = { version = "0.6.18", optional = true }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1"
|
||||
cfg-if = "1"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4"
|
||||
tokio = { version = "1.28.1", optional = true }
|
||||
|
||||
@@ -6,6 +6,6 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[patch.crates-io]
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_router = { path = "../../router" }
|
||||
api-boundary = { path = "api-boundary" }
|
||||
|
||||
@@ -7,9 +7,8 @@ publish = false
|
||||
[dependencies]
|
||||
api-boundary = "*"
|
||||
|
||||
leptos = { path = "../../../leptos", features = ["csr"] }
|
||||
leptos_meta = { path = "../../../meta", features = ["csr"] }
|
||||
leptos_router = { path = "../../../router", features = ["csr"] }
|
||||
leptos = { version = "0.2.0-alpha2", features = ["stable"] }
|
||||
leptos_router = { version = "0.2.0-alpha2", features = ["stable", "csr"] }
|
||||
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn App(cx: Scope) -> impl IntoView {
|
||||
// Button B: pass a closure
|
||||
<ButtonB on_click=move |_| set_right.update(|value| *value = !*value)/>
|
||||
|
||||
// Button C: use a regular event listener
|
||||
// Button B: use a regular event listener
|
||||
// setting an event listener on a component like this applies it
|
||||
// to each of the top-level elements the component returns
|
||||
<ButtonC on:click=move |_| set_italics.update(|value| *value = !*value)/>
|
||||
|
||||
@@ -10,12 +10,12 @@ lto = true
|
||||
[dependencies]
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_router = { path = "../../router", features = ["csr"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
futures = "0.3"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
leptos_meta = { path = "../../meta", features = ["csr", "nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["csr"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
|
||||
@@ -13,10 +13,12 @@ rand = { version = "0.8.5", features = ["min_const_gen"], optional = true }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.25"
|
||||
cfg-if = "1.0.0"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.0.0"
|
||||
serde = { version = "1.0.148", features = ["derive"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -13,10 +13,12 @@ console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
cfg-if = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_actix = { path = "../../integrations/actix", default-features = false, optional = true }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
|
||||
@@ -11,10 +11,12 @@ console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
cfg-if = "1"
|
||||
lazy_static = "1"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
|
||||
@@ -10,10 +10,12 @@ crate-type = ["cdylib", "rlib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
gloo-net = { version = "0.2", features = ["http"] }
|
||||
log = "0.4"
|
||||
cfg-if = "1.0"
|
||||
|
||||
@@ -4,9 +4,12 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos_meta = { path = "../../meta", features = ["csr", "nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["csr", "nightly"] }
|
||||
leptos = { version = "0.2", features = [
|
||||
"serde",
|
||||
"csr",
|
||||
] }
|
||||
leptos_meta = { version = "0.2", features = ["csr"] }
|
||||
leptos_router = { version = "0.2", features = ["csr"] }
|
||||
log = "0.4"
|
||||
gloo-net = { version = "0.2", features = ["http"] }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -16,10 +16,12 @@ console_error_panic_hook = "0.1.7"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
futures = "0.3.25"
|
||||
cfg-if = "1.0.0"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_actix = { path = "../../integrations/actix", optional = true }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.0.0"
|
||||
gloo = { git = "https://github.com/rustwasm/gloo" }
|
||||
@@ -30,6 +32,7 @@ sqlx = { version = "0.6.2", features = [
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["ssr"]
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = [
|
||||
"dep:actix-files",
|
||||
|
||||
@@ -11,10 +11,12 @@ console_log = "1.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.25"
|
||||
cfg-if = "1.0.0"
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos_axum = { path = "../../integrations/axum", optional = true }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.0.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@@ -31,6 +33,8 @@ thiserror = "1.0.38"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["csr"]
|
||||
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
|
||||
@@ -11,11 +11,13 @@ console_log = "1.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.25"
|
||||
cfg-if = "1.0.0"
|
||||
leptos = { path = "../../leptos" }
|
||||
leptos_viz = { path = "../../integrations/viz", optional = true }
|
||||
leptos_meta = { path = "../../meta", features = ["nightly"] }
|
||||
leptos_router = { path = "../../router", features = ["nightly"] }
|
||||
leptos_reactive = { path = "../../leptos_reactive", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_viz = { path = "../../integrations/viz", default-features = false, optional = true }
|
||||
leptos_meta = { path = "../../meta", default-features = false }
|
||||
leptos_router = { path = "../../router", default-features = false }
|
||||
leptos_reactive = { path = "../../leptos_reactive", default-features = false }
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.0.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
@@ -30,6 +32,7 @@ thiserror = "1.0.38"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["csr"]
|
||||
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = [
|
||||
|
||||
@@ -8,7 +8,7 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["nightly"] }
|
||||
leptos = { path = "../../leptos", default-features = false }
|
||||
log = "0.4"
|
||||
console_log = "1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -13,19 +13,19 @@ cfg-if = "1"
|
||||
leptos_dom = { workspace = true }
|
||||
leptos_macro = { workspace = true }
|
||||
leptos_reactive = { workspace = true }
|
||||
leptos_server = { workspace = true}
|
||||
leptos_server = { workspace = true, default-features = false }
|
||||
leptos_config = { workspace = true }
|
||||
tracing = "0.1"
|
||||
typed-builder = "0.14"
|
||||
server_fn = { workspace = true}
|
||||
server_fn = { workspace = true, default-features = false }
|
||||
web-sys = { version = "0.3.63", optional = true }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
leptos = { path = "."}
|
||||
leptos = { path = ".", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
default = ["csr", "serde"]
|
||||
template_macro = ["leptos_dom/web", "web-sys", "wasm-bindgen"]
|
||||
csr = [
|
||||
"leptos_dom/web",
|
||||
@@ -47,11 +47,11 @@ ssr = [
|
||||
"leptos_reactive/ssr",
|
||||
"leptos_server/ssr",
|
||||
]
|
||||
nightly = [
|
||||
"leptos_dom/nightly",
|
||||
"leptos_macro/nightly",
|
||||
"leptos_reactive/nightly",
|
||||
"leptos_server/nightly",
|
||||
stable = [
|
||||
"leptos_dom/stable",
|
||||
"leptos_macro/stable",
|
||||
"leptos_reactive/stable",
|
||||
"leptos_server/stable",
|
||||
]
|
||||
serde = ["leptos_reactive/serde"]
|
||||
serde-lite = ["leptos_reactive/serde-lite"]
|
||||
@@ -60,7 +60,7 @@ rkyv = ["leptos_reactive/rkyv"]
|
||||
tracing = ["leptos_macro/tracing"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["nightly", "tracing", "template_macro", "rustls", "default-tls", "web-sys", "wasm-bindgen"]
|
||||
denylist = ["stable", "tracing", "template_macro", "rustls", "default-tls", "web-sys", "wasm-bindgen"]
|
||||
skip_feature_sets = [
|
||||
[
|
||||
"csr",
|
||||
|
||||
@@ -16,14 +16,14 @@ use leptos_reactive::{
|
||||
/// # use leptos_dom::*; use leptos::*;
|
||||
/// # run_scope(create_runtime(), |cx| {
|
||||
/// let (value, set_value) = create_signal(cx, Ok(0));
|
||||
/// let on_input = move |ev| set_value.set(event_target_value(&ev).parse::<i32>());
|
||||
/// let on_input = move |ev| set_value(event_target_value(&ev).parse::<i32>());
|
||||
///
|
||||
/// view! { cx,
|
||||
/// <input type="text" on:input=on_input/>
|
||||
/// <ErrorBoundary
|
||||
/// fallback=move |_, _| view! { cx, <p class="error">"Enter a valid number."</p>}
|
||||
/// >
|
||||
/// <p>"Value is: " {move || value.get()}</p>
|
||||
/// <p>"Value is: " {value}</p>
|
||||
/// </ErrorBoundary>
|
||||
/// }
|
||||
/// # });
|
||||
|
||||
@@ -26,7 +26,7 @@ use std::hash::Hash;
|
||||
/// <div>
|
||||
/// <For
|
||||
/// // a function that returns the items we're iterating over; a signal is fine
|
||||
/// each=move || counters.get()
|
||||
/// each=counters
|
||||
/// // a unique key for each item
|
||||
/// key=|counter| counter.id
|
||||
/// // renders each item to a view
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
//! Join us on our [Discord Channel](https://discord.gg/v38Eef6sWG) to see what the community is building.
|
||||
//! Explore our [Examples](https://github.com/leptos-rs/leptos/tree/main/examples) to see Leptos in action.
|
||||
//!
|
||||
//! # `nightly` Note
|
||||
//! Most of the examples assume you’re using `nightly` Rust. If you’re on stable, note the following:
|
||||
//! 1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.0", features = ["stable"] }`
|
||||
//! 2. `nightly` enables the function call syntax for accessing and setting signals. If you’re using `stable`,
|
||||
//! you’ll just call `.get()`, `.set()`, or `.update()` manually. Check out the
|
||||
//! [`counters_stable` example](https://github.com/leptos-rs/leptos/blob/main/examples/counters_stable/src/main.rs)
|
||||
//! for examples of the correct API.
|
||||
//!
|
||||
//! # Learning by Example
|
||||
//!
|
||||
//! If you want to see what Leptos is capable of, check out
|
||||
@@ -76,10 +84,12 @@
|
||||
//! - **Server Functions**: the [server](crate::leptos_server) macro, [create_action], and [create_server_action]
|
||||
//!
|
||||
//! # Feature Flags
|
||||
//! - `nightly`: On `nightly` Rust, enables the function-call syntax for signal getters and setters.
|
||||
//! - `csr` Client-side rendering: Generate DOM nodes in the browser
|
||||
//! - `csr` (*Default*) Client-side rendering: Generate DOM nodes in the browser
|
||||
//! - `ssr` Server-side rendering: Generate an HTML string (typically on the server)
|
||||
//! - `hydrate` Hydration: use this to add interactivity to an SSRed Leptos app
|
||||
//! - `stable` By default, Leptos requires `nightly` Rust, which is what allows the ergonomics
|
||||
//! of calling signals as functions. If you need to use `stable`, you will need to call `.get()`
|
||||
//! and `.set()` manually.
|
||||
//! - `serde` (*Default*) In SSR/hydrate mode, uses [serde](https://docs.rs/serde/latest/serde/) to serialize resources and send them
|
||||
//! from the server to the client.
|
||||
//! - `serde-lite` In SSR/hydrate mode, uses [serde-lite](https://docs.rs/serde-lite/latest/serde_lite/) to serialize resources and send them
|
||||
@@ -112,7 +122,7 @@
|
||||
//! <div>
|
||||
//! <button on:click=clear>"Clear"</button>
|
||||
//! <button on:click=decrement>"-1"</button>
|
||||
//! <span>"Value: " {move || value.get().to_string()} "!"</span>
|
||||
//! <span>"Value: " {move || value().to_string()} "!"</span>
|
||||
//! <button on:click=increment>"+1"</button>
|
||||
//! </div>
|
||||
//! }
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::{cell::RefCell, rc::Rc};
|
||||
///
|
||||
/// view! { cx,
|
||||
/// <Show
|
||||
/// when=move || value.get() < 5
|
||||
/// when=move || value() < 5
|
||||
/// fallback=|cx| view! { cx, "Big number!" }
|
||||
/// >
|
||||
/// "Small number!"
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::rc::Rc;
|
||||
///
|
||||
/// let (cat_count, set_cat_count) = create_signal::<u32>(cx, 1);
|
||||
///
|
||||
/// let cats = create_resource(cx, move || cat_count.get(), |count| fetch_cats(count));
|
||||
/// let cats = create_resource(cx, cat_count, |count| fetch_cats(count));
|
||||
///
|
||||
/// view! { cx,
|
||||
/// <div>
|
||||
|
||||
@@ -32,8 +32,7 @@ use std::{
|
||||
/// let (cat_count, set_cat_count) = create_signal::<u32>(cx, 1);
|
||||
/// let (pending, set_pending) = create_signal(cx, false);
|
||||
///
|
||||
/// let cats =
|
||||
/// create_resource(cx, move || cat_count.get(), |count| fetch_cats(count));
|
||||
/// let cats = create_resource(cx, cat_count, |count| fetch_cats(count));
|
||||
///
|
||||
/// view! { cx,
|
||||
/// <div>
|
||||
@@ -65,7 +64,7 @@ use std::{
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
tracing::instrument(level = "info", skip_all)
|
||||
)]
|
||||
#[component(transparent)]
|
||||
#[component]
|
||||
pub fn Transition<F, E>(
|
||||
cx: Scope,
|
||||
/// Will be displayed while resources are pending.
|
||||
|
||||
@@ -12,9 +12,9 @@ actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
cfg-if = "1"
|
||||
leptos = { path = "../../..", features = ["serde"] }
|
||||
leptos = { path = "../../..", default-features = false, features = ["serde"] }
|
||||
leptos_actix = { path = "../../../../integrations/actix", optional = true }
|
||||
leptos_router = { path = "../../../../router"}
|
||||
leptos_router = { path = "../../../../router", default-features = false }
|
||||
log = "0.4"
|
||||
simple_logger = "4"
|
||||
wasm-bindgen = "0.2.87"
|
||||
|
||||
@@ -159,8 +159,8 @@ features = [
|
||||
default = []
|
||||
web = ["leptos_reactive/csr"]
|
||||
ssr = ["leptos_reactive/ssr"]
|
||||
nightly = ["leptos_reactive/nightly"]
|
||||
stable = ["leptos_reactive/stable"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["nightly"]
|
||||
denylist = ["stable"]
|
||||
skip_feature_sets = [["web", "ssr"]]
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../../leptos", features = ["nightly"] }
|
||||
leptos = { path = "../../../leptos", default-features = false }
|
||||
actix-web = { version = "4", optional = true }
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
gloo = { version = "0.8", features = ["futures"] }
|
||||
leptos = { path = "../../../leptos", features = ["nightly", "csr", "tracing"] }
|
||||
leptos = { path = "../../../leptos", features = ["tracing"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber-wasm = "0.1"
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos", features = ["csr", "nightly"] }
|
||||
leptos = { path = "../../leptos" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![deny(missing_docs)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![cfg_attr(feature = "nightly", feature(fn_traits))]
|
||||
#![cfg_attr(feature = "nightly", feature(unboxed_closures))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(fn_traits))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(unboxed_closures))]
|
||||
|
||||
//! The DOM implementation for `leptos`.
|
||||
|
||||
@@ -1125,7 +1125,7 @@ viewable_primitive![
|
||||
];
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "nightly")] {
|
||||
if #[cfg(not(feature = "stable"))] {
|
||||
viewable_primitive! {
|
||||
std::backtrace::Backtrace
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ impl<T: ElementDescriptor> Clone for NodeRef<T> {
|
||||
impl<T: ElementDescriptor + 'static> Copy for NodeRef<T> {}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "nightly")] {
|
||||
if #[cfg(not(feature = "stable"))] {
|
||||
impl<T: Clone + ElementDescriptor + 'static> FnOnce<()> for NodeRef<T> {
|
||||
type Output = Option<HtmlElement<T>>;
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ default = ["ssr"]
|
||||
csr = []
|
||||
hydrate = []
|
||||
ssr = []
|
||||
nightly = ["server_fn_macro/nightly"]
|
||||
stable = ["server_fn_macro/stable"]
|
||||
tracing = []
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["nightly", "tracing"]
|
||||
denylist = ["stable", "tracing"]
|
||||
skip_feature_sets = [["csr", "hydrate"], ["hydrate", "csr"], ["hydrate", "ssr"]]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg_attr(feature = "nightly", feature(proc_macro_span))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(proc_macro_span))]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
#[macro_use]
|
||||
@@ -94,7 +94,7 @@ mod template;
|
||||
/// Attributes can take a wide variety of primitive types that can be converted to strings. They can also
|
||||
/// take an `Option`, in which case `Some` sets the attribute and `None` removes the attribute.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// ```rust
|
||||
/// # use leptos::*;
|
||||
/// # run_scope(create_runtime(), |cx| {
|
||||
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
|
||||
@@ -102,11 +102,11 @@ mod template;
|
||||
///
|
||||
/// view! {
|
||||
/// cx,
|
||||
/// // ❌ not like this: `count.get()` returns an `i32`, not a function
|
||||
/// <p>{count.get()}</p>
|
||||
/// // ❌ not like this: `count()` returns an `i32`, not a function
|
||||
/// <p>{count()}</p>
|
||||
/// // ✅ this is good: Leptos sees the function and knows it's a dynamic value
|
||||
/// <p>{move || count.get()}</p>
|
||||
/// // 🔥 with the `nightly` feature, `count` is a function, so `count` itself can be passed directly into the view
|
||||
/// // 🔥 `count` is itself a function, so you can pass it directly (unless you're on `stable`)
|
||||
/// <p>{count}</p>
|
||||
/// }
|
||||
/// # ;
|
||||
@@ -147,9 +147,9 @@ mod template;
|
||||
/// <input
|
||||
/// type="text"
|
||||
/// name="user_name"
|
||||
/// value={move || name.get()} // this only sets the default value!
|
||||
/// prop:value={move || name.get()} // here's how you update values. Sorry, I didn’t invent the DOM.
|
||||
/// on:click=move |ev| set_name.set(event_target_value(&ev)) // `event_target_value` is a useful little Leptos helper
|
||||
/// value={name} // this only sets the default value!
|
||||
/// prop:value={name} // here's how you update values. Sorry, I didn’t invent the DOM.
|
||||
/// on:click=move |ev| set_name(event_target_value(&ev)) // `event_target_value` is a useful little Leptos helper
|
||||
/// />
|
||||
/// }
|
||||
/// # ;
|
||||
@@ -163,7 +163,7 @@ mod template;
|
||||
/// # run_scope(create_runtime(), |cx| {
|
||||
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// view! { cx, <div class:hidden-div={move || count.get() < 3}>"Now you see me, now you don’t."</div> }
|
||||
/// view! { cx, <div class:hidden-div={move || count() < 3}>"Now you see me, now you don’t."</div> }
|
||||
/// # ;
|
||||
/// # }
|
||||
/// # });
|
||||
@@ -176,7 +176,7 @@ mod template;
|
||||
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// // `hidden-div-25` is invalid at the moment
|
||||
/// view! { cx, <div class:hidden-div-25={move || count.get() < 3}>"Now you see me, now you don’t."</div> }
|
||||
/// view! { cx, <div class:hidden-div-25={move || count() < 3}>"Now you see me, now you don’t."</div> }
|
||||
/// # ;
|
||||
/// # }
|
||||
/// # });
|
||||
@@ -191,7 +191,7 @@ mod template;
|
||||
/// // this allows you to use CSS frameworks that include complex class names
|
||||
/// view! { cx,
|
||||
/// <div
|
||||
/// class=("is-[this_-_really]-necessary-42", move || count.get() < 3)
|
||||
/// class=("is-[this_-_really]-necessary-42", move || count() < 3)
|
||||
/// >
|
||||
/// "Now you see me, now you don’t."
|
||||
/// </div>
|
||||
@@ -211,9 +211,9 @@ mod template;
|
||||
/// view! { cx,
|
||||
/// <div
|
||||
/// style="position: absolute"
|
||||
/// style:left=move || format!("{}px", x.get())
|
||||
/// style:top=move || format!("{}px", y.get())
|
||||
/// style=("background-color", move || format!("rgb({}, {}, 100)", x.get(), y.get()))
|
||||
/// style:left=move || format!("{}px", x())
|
||||
/// style:top=move || format!("{}px", y())
|
||||
/// style=("background-color", move || format!("rgb({}, {}, 100)", x(), y()))
|
||||
/// >
|
||||
/// "Moves when coordinates change"
|
||||
/// </div>
|
||||
@@ -285,7 +285,7 @@ mod template;
|
||||
///
|
||||
/// // create event handlers for our buttons
|
||||
/// // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures
|
||||
/// let clear = move |_ev| set_value.set(0);
|
||||
/// let clear = move |_ev| set_value(0);
|
||||
/// let decrement = move |_ev| set_value.update(|value| *value -= 1);
|
||||
/// let increment = move |_ev| set_value.update(|value| *value += 1);
|
||||
///
|
||||
@@ -295,7 +295,7 @@ mod template;
|
||||
/// <div>
|
||||
/// <button on:click=clear>"Clear"</button>
|
||||
/// <button on:click=decrement>"-1"</button>
|
||||
/// <span>"Value: " {move || value.get().to_string()} "!"</span>
|
||||
/// <span>"Value: " {move || value().to_string()} "!"</span>
|
||||
/// <button on:click=increment>"+1"</button>
|
||||
/// </div>
|
||||
/// }
|
||||
@@ -381,7 +381,7 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
||||
|
||||
fn normalized_call_site(site: proc_macro::Span) -> Option<String> {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(debug_assertions, feature = "nightly"))] {
|
||||
if #[cfg(all(debug_assertions, not(feature = "stable")))] {
|
||||
Some(leptos_hot_reload::span_to_stable_id(
|
||||
site.source_file().path(),
|
||||
site.into()
|
||||
@@ -467,7 +467,7 @@ pub fn template(tokens: TokenStream) -> TokenStream {
|
||||
/// // return the user interface, which will be automatically updated
|
||||
/// // when signal values change
|
||||
/// view! { cx,
|
||||
/// <p>"Your name is " {name} " and you are " {move || age.get()} " years old."</p>
|
||||
/// <p>"Your name is " {name} " and you are " {age} " years old."</p>
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
|
||||
@@ -581,7 +581,7 @@ fn attribute_to_tokens_ssr<'a>(
|
||||
|| name.strip_prefix("style:").is_some()
|
||||
{
|
||||
// ignore props for SSR
|
||||
// ignore classes and styles: we'll handle these separately
|
||||
// ignore classes and sdtyles: we'll handle these separately
|
||||
} else if name == "inner_html" {
|
||||
return attr.value();
|
||||
} else {
|
||||
@@ -606,9 +606,7 @@ fn attribute_to_tokens_ssr<'a>(
|
||||
if let Some(value) = value_to_string(value) {
|
||||
template.push_str(&name);
|
||||
template.push_str("=\"");
|
||||
template.push_str(&html_escape::encode_quoted_attribute(
|
||||
&value,
|
||||
));
|
||||
template.push_str(&value);
|
||||
template.push('"');
|
||||
} else {
|
||||
template.push_str("{}");
|
||||
@@ -731,9 +729,7 @@ fn set_class_attribute_ssr(
|
||||
{
|
||||
template.push_str(" class=\"");
|
||||
|
||||
template.push_str(&html_escape::encode_quoted_attribute(
|
||||
&static_class_attr,
|
||||
));
|
||||
template.push_str(&static_class_attr);
|
||||
|
||||
for (_span, value) in dyn_class_attr {
|
||||
if let Some(value) = value {
|
||||
|
||||
@@ -68,14 +68,14 @@ hydrate = [
|
||||
"dep:web-sys",
|
||||
]
|
||||
ssr = ["dep:tokio"]
|
||||
nightly = []
|
||||
stable = []
|
||||
serde = []
|
||||
serde-lite = ["dep:serde-lite"]
|
||||
miniserde = ["dep:miniserde"]
|
||||
rkyv = ["dep:rkyv", "dep:bytecheck"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["nightly"]
|
||||
denylist = ["stable"]
|
||||
skip_feature_sets = [
|
||||
[
|
||||
"csr",
|
||||
|
||||
@@ -29,20 +29,20 @@ use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
|
||||
/// // ✅ use effects to interact between reactive state and the outside world
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // immediately prints "Value: 0" and subscribes to `a`
|
||||
/// log::debug!("Value: {}", a.get());
|
||||
/// log::debug!("Value: {}", a());
|
||||
/// });
|
||||
///
|
||||
/// set_a.set(1);
|
||||
/// set_a(1);
|
||||
/// // ✅ because it's subscribed to `a`, the effect reruns and prints "Value: 1"
|
||||
///
|
||||
/// // ❌ don't use effects to synchronize state within the reactive system
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // this technically works but can cause unnecessary re-renders
|
||||
/// // and easily lead to problems like infinite loops
|
||||
/// set_b.set(a.get() + 1);
|
||||
/// set_b(a() + 1);
|
||||
/// });
|
||||
/// # if !cfg!(feature = "ssr") {
|
||||
/// # assert_eq!(b.get(), 2);
|
||||
/// # assert_eq!(b(), 2);
|
||||
/// # }
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
@@ -88,19 +88,19 @@ where
|
||||
/// // ✅ use effects to interact between reactive state and the outside world
|
||||
/// create_isomorphic_effect(cx, move |_| {
|
||||
/// // immediately prints "Value: 0" and subscribes to `a`
|
||||
/// log::debug!("Value: {}", a.get());
|
||||
/// log::debug!("Value: {}", a());
|
||||
/// });
|
||||
///
|
||||
/// set_a.set(1);
|
||||
/// set_a(1);
|
||||
/// // ✅ because it's subscribed to `a`, the effect reruns and prints "Value: 1"
|
||||
///
|
||||
/// // ❌ don't use effects to synchronize state within the reactive system
|
||||
/// create_isomorphic_effect(cx, move |_| {
|
||||
/// // this technically works but can cause unnecessary re-renders
|
||||
/// // and easily lead to problems like infinite loops
|
||||
/// set_b.set(a.get() + 1);
|
||||
/// set_b(a() + 1);
|
||||
/// });
|
||||
/// # assert_eq!(b.get(), 2);
|
||||
/// # assert_eq!(b(), 2);
|
||||
/// # }).dispose();
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature="ssr"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(feature = "nightly", feature(fn_traits))]
|
||||
#![cfg_attr(feature = "nightly", feature(unboxed_closures))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_name_of_val))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(fn_traits))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(unboxed_closures))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(type_name_of_val))]
|
||||
|
||||
//! The reactive system for the [Leptos](https://docs.rs/leptos/latest/leptos/) Web framework.
|
||||
//!
|
||||
@@ -44,28 +44,25 @@
|
||||
//! let (count, set_count) = create_signal(cx, 0);
|
||||
//!
|
||||
//! // calling the getter gets the value
|
||||
//! // can be `count()` on nightly
|
||||
//! assert_eq!(count.get(), 0);
|
||||
//! assert_eq!(count(), 0);
|
||||
//! // calling the setter sets the value
|
||||
//! // can be `set_count(1)` on nightly
|
||||
//! set_count.set(1);
|
||||
//! set_count(1);
|
||||
//! // or we can mutate it in place with update()
|
||||
//! set_count.update(|n| *n += 1);
|
||||
//!
|
||||
//! // a derived signal: a plain closure that relies on the signal
|
||||
//! // the closure will run whenever we *access* double_count()
|
||||
//! let double_count = move || count.get() * 2;
|
||||
//! let double_count = move || count() * 2;
|
||||
//! assert_eq!(double_count(), 4);
|
||||
//!
|
||||
//! // a memo: subscribes to the signal
|
||||
//! // the closure will run only when count changes
|
||||
//! let memoized_triple_count = create_memo(cx, move |_| count.get() * 3);
|
||||
//! // can be `memoized_triple_count()` on nightly
|
||||
//! assert_eq!(memoized_triple_count.get(), 6);
|
||||
//! let memoized_triple_count = create_memo(cx, move |_| count() * 3);
|
||||
//! assert_eq!(memoized_triple_count(), 6);
|
||||
//!
|
||||
//! // this effect will run whenever `count` changes
|
||||
//! // this effect will run whenever count() changes
|
||||
//! create_effect(cx, move |_| {
|
||||
//! println!("Count = {}", count.get());
|
||||
//! println!("Count = {}", count());
|
||||
//! });
|
||||
//! });
|
||||
//! ```
|
||||
|
||||
@@ -40,12 +40,12 @@ use std::{any::Any, cell::RefCell, fmt, marker::PhantomData, rc::Rc};
|
||||
/// let (value, set_value) = create_signal(cx, 0);
|
||||
///
|
||||
/// // 🆗 we could create a derived signal with a simple function
|
||||
/// let double_value = move || value.get() * 2;
|
||||
/// set_value.set(2);
|
||||
/// let double_value = move || value() * 2;
|
||||
/// set_value(2);
|
||||
/// assert_eq!(double_value(), 4);
|
||||
///
|
||||
/// // but imagine the computation is really expensive
|
||||
/// let expensive = move || really_expensive_computation(value.get()); // lazy: doesn't run until called
|
||||
/// let expensive = move || really_expensive_computation(value()); // lazy: doesn't run until called
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // 🆗 run #1: calls `really_expensive_computation` the first time
|
||||
/// log::debug!("expensive = {}", expensive());
|
||||
@@ -58,15 +58,14 @@ use std::{any::Any, cell::RefCell, fmt, marker::PhantomData, rc::Rc};
|
||||
///
|
||||
/// // instead, we create a memo
|
||||
/// // 🆗 run #1: the calculation runs once immediately
|
||||
/// let memoized = create_memo(cx, move |_| really_expensive_computation(value.get()));
|
||||
/// let memoized = create_memo(cx, move |_| really_expensive_computation(value()));
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // 🆗 reads the current value of the memo
|
||||
/// // can be `memoized()` on nightly
|
||||
/// log::debug!("memoized = {}", memoized.get());
|
||||
/// // 🆗 reads the current value of the memo
|
||||
/// log::debug!("memoized = {}", memoized());
|
||||
/// });
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // ✅ reads the current value **without re-running the calculation**
|
||||
/// let value = memoized.get();
|
||||
/// let value = memoized();
|
||||
/// // do something else...
|
||||
/// });
|
||||
/// # }).dispose();
|
||||
@@ -132,12 +131,12 @@ where
|
||||
/// let (value, set_value) = create_signal(cx, 0);
|
||||
///
|
||||
/// // 🆗 we could create a derived signal with a simple function
|
||||
/// let double_value = move || value.get() * 2;
|
||||
/// set_value.set(2);
|
||||
/// let double_value = move || value() * 2;
|
||||
/// set_value(2);
|
||||
/// assert_eq!(double_value(), 4);
|
||||
///
|
||||
/// // but imagine the computation is really expensive
|
||||
/// let expensive = move || really_expensive_computation(value.get()); // lazy: doesn't run until called
|
||||
/// let expensive = move || really_expensive_computation(value()); // lazy: doesn't run until called
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // 🆗 run #1: calls `really_expensive_computation` the first time
|
||||
/// log::debug!("expensive = {}", expensive());
|
||||
@@ -150,15 +149,14 @@ where
|
||||
///
|
||||
/// // instead, we create a memo
|
||||
/// // 🆗 run #1: the calculation runs once immediately
|
||||
/// let memoized = create_memo(cx, move |_| really_expensive_computation(value.get()));
|
||||
/// let memoized = create_memo(cx, move |_| really_expensive_computation(value()));
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // 🆗 reads the current value of the memo
|
||||
/// log::debug!("memoized = {}", memoized.get());
|
||||
/// log::debug!("memoized = {}", memoized());
|
||||
/// });
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // ✅ reads the current value **without re-running the calculation**
|
||||
/// // can be `memoized()` on nightly
|
||||
/// let value = memoized.get();
|
||||
/// let value = memoized();
|
||||
/// // do something else...
|
||||
/// });
|
||||
/// # }).dispose();
|
||||
@@ -329,14 +327,13 @@ impl<T> SignalWithUntracked<T> for Memo<T> {
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
/// let double_count = create_memo(cx, move |_| count.get() * 2);
|
||||
/// let double_count = create_memo(cx, move |_| count() * 2);
|
||||
///
|
||||
/// assert_eq!(double_count.get(), 0);
|
||||
/// set_count.set(1);
|
||||
/// set_count(1);
|
||||
///
|
||||
/// // can be `double_count()` on nightly
|
||||
/// // assert_eq!(double_count(), 2);
|
||||
/// assert_eq!(double_count.get(), 2);
|
||||
/// // double_count() is shorthand for double_count.get()
|
||||
/// assert_eq!(double_count(), 2);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
/// ```
|
||||
|
||||
@@ -51,7 +51,7 @@ use std::{
|
||||
/// # // `csr`, `hydrate`, and `ssr` all have issues here
|
||||
/// # // because we're not running in a browser or in Tokio. Let's just ignore it.
|
||||
/// # if false {
|
||||
/// let cats = create_resource(cx, move || how_many_cats.get(), fetch_cat_picture_urls);
|
||||
/// let cats = create_resource(cx, how_many_cats, fetch_cat_picture_urls);
|
||||
///
|
||||
/// // when we read the signal, it contains either
|
||||
/// // 1) None (if the Future isn't ready yet) or
|
||||
@@ -59,7 +59,7 @@ use std::{
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["1".to_string()]));
|
||||
///
|
||||
/// // when the signal's value changes, the `Resource` will generate and run a new `Future`
|
||||
/// set_how_many_cats.set(2);
|
||||
/// set_how_many_cats(2);
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["2".to_string()]));
|
||||
/// # }
|
||||
/// # }).dispose();
|
||||
@@ -696,7 +696,7 @@ impl<S, T> SignalSet<T> for Resource<S, T> {
|
||||
/// # // `csr`, `hydrate`, and `ssr` all have issues here
|
||||
/// # // because we're not running in a browser or in Tokio. Let's just ignore it.
|
||||
/// # if false {
|
||||
/// let cats = create_resource(cx, move || how_many_cats.get(), fetch_cat_picture_urls);
|
||||
/// let cats = create_resource(cx, how_many_cats, fetch_cat_picture_urls);
|
||||
///
|
||||
/// // when we read the signal, it contains either
|
||||
/// // 1) None (if the Future isn't ready yet) or
|
||||
@@ -704,7 +704,7 @@ impl<S, T> SignalSet<T> for Resource<S, T> {
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["1".to_string()]));
|
||||
///
|
||||
/// // when the signal's value changes, the `Resource` will generate and run a new `Future`
|
||||
/// set_how_many_cats.set(2);
|
||||
/// set_how_many_cats(2);
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["2".to_string()]));
|
||||
/// # }
|
||||
/// # }).dispose();
|
||||
|
||||
@@ -189,17 +189,17 @@ impl Scope {
|
||||
/// let (b, set_b) = create_signal(cx, 0);
|
||||
/// let c = create_memo(cx, move |_| {
|
||||
/// // this memo will *only* update when `a` changes
|
||||
/// a.get() + cx.untrack(move || b.get())
|
||||
/// a() + cx.untrack(move || b())
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(c.get(), 0);
|
||||
/// set_a.set(1);
|
||||
/// assert_eq!(c.get(), 1);
|
||||
/// set_b.set(1);
|
||||
/// assert_eq!(c(), 0);
|
||||
/// set_a(1);
|
||||
/// assert_eq!(c(), 1);
|
||||
/// set_b(1);
|
||||
/// // hasn't updated, because we untracked before reading b
|
||||
/// assert_eq!(c.get(), 1);
|
||||
/// set_a.set(2);
|
||||
/// assert_eq!(c.get(), 3);
|
||||
/// assert_eq!(c(), 1);
|
||||
/// set_a(2);
|
||||
/// assert_eq!(c(), 3);
|
||||
///
|
||||
/// # });
|
||||
/// ```
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::{cell::RefCell, collections::HashMap, hash::Hash, rc::Rc};
|
||||
/// # use std::cell::RefCell;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (a, set_a) = create_signal(cx, 0);
|
||||
/// let is_selected = create_selector(cx, move || a.get());
|
||||
/// let is_selected = create_selector(cx, a);
|
||||
/// let total_notifications = Rc::new(RefCell::new(0));
|
||||
/// let not = Rc::clone(&total_notifications);
|
||||
/// create_isomorphic_effect(cx, {
|
||||
@@ -33,13 +33,13 @@ use std::{cell::RefCell, collections::HashMap, hash::Hash, rc::Rc};
|
||||
///
|
||||
/// assert_eq!(is_selected(5), false);
|
||||
/// assert_eq!(*total_notifications.borrow(), 0);
|
||||
/// set_a.set(5);
|
||||
/// set_a(5);
|
||||
/// assert_eq!(is_selected(5), true);
|
||||
/// assert_eq!(*total_notifications.borrow(), 1);
|
||||
/// set_a.set(5);
|
||||
/// set_a(5);
|
||||
/// assert_eq!(is_selected(5), true);
|
||||
/// assert_eq!(*total_notifications.borrow(), 1);
|
||||
/// set_a.set(4);
|
||||
/// set_a(4);
|
||||
/// assert_eq!(is_selected(5), false);
|
||||
/// # })
|
||||
/// # .dispose()
|
||||
|
||||
@@ -23,7 +23,7 @@ use thiserror::Error;
|
||||
macro_rules! impl_get_fn_traits {
|
||||
($($ty:ident $(($method_name:ident))?),*) => {
|
||||
$(
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<T: Clone> FnOnce<()> for $ty<T> {
|
||||
type Output = T;
|
||||
|
||||
@@ -33,7 +33,7 @@ macro_rules! impl_get_fn_traits {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<T: Clone> FnMut<()> for $ty<T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||
@@ -41,7 +41,7 @@ macro_rules! impl_get_fn_traits {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<T: Clone> Fn<()> for $ty<T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||
@@ -61,7 +61,7 @@ macro_rules! impl_get_fn_traits {
|
||||
macro_rules! impl_set_fn_traits {
|
||||
($($ty:ident $($method_name:ident)?),*) => {
|
||||
$(
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<T> FnOnce<(T,)> for $ty<T> {
|
||||
type Output = ();
|
||||
|
||||
@@ -71,7 +71,7 @@ macro_rules! impl_set_fn_traits {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<T> FnMut<(T,)> for $ty<T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, args: (T,)) -> Self::Output {
|
||||
@@ -79,7 +79,7 @@ macro_rules! impl_set_fn_traits {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<T> Fn<(T,)> for $ty<T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, args: (T,)) -> Self::Output {
|
||||
@@ -290,26 +290,24 @@ pub trait SignalDispose {
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
///
|
||||
/// // ✅ calling the getter clones and returns the value
|
||||
/// // this can be `count()` on nightly
|
||||
/// assert_eq!(count.get(), 0);
|
||||
/// assert_eq!(count(), 0);
|
||||
///
|
||||
/// // ✅ calling the setter sets the value
|
||||
/// // this can be `set_count(1)` on nightly
|
||||
/// set_count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // ❌ you could call the getter within the setter
|
||||
/// // set_count.set(count.get() + 1);
|
||||
/// // set_count(count() + 1);
|
||||
///
|
||||
/// // ✅ however it's more efficient to use .update() and mutate the value in place
|
||||
/// set_count.update(|count: &mut i32| *count += 1);
|
||||
/// assert_eq!(count.get(), 2);
|
||||
/// assert_eq!(count(), 2);
|
||||
///
|
||||
/// // ✅ you can create "derived signals" with a Fn() -> T interface
|
||||
/// let double_count = move || count.get() * 2; // signals are `Copy` so you can `move` them anywhere
|
||||
/// set_count.set(0);
|
||||
/// // ✅ you can create "derived signals" with the same Fn() -> T interface
|
||||
/// let double_count = move || count() * 2; // signals are `Copy` so you can `move` them anywhere
|
||||
/// set_count(0);
|
||||
/// assert_eq!(double_count(), 0);
|
||||
/// set_count.set(1);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(double_count(), 2);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
@@ -449,24 +447,24 @@ pub fn create_signal_from_stream<T>(
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
///
|
||||
/// // ✅ calling the getter clones and returns the value
|
||||
/// assert_eq!(count.get(), 0);
|
||||
/// assert_eq!(count(), 0);
|
||||
///
|
||||
/// // ✅ calling the setter sets the value
|
||||
/// set_count.set(1); // `set_count(1)` on nightly
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // ❌ you could call the getter within the setter
|
||||
/// // set_count.set(count.get() + 1);
|
||||
/// // set_count(count() + 1);
|
||||
///
|
||||
/// // ✅ however it's more efficient to use .update() and mutate the value in place
|
||||
/// set_count.update(|count: &mut i32| *count += 1);
|
||||
/// assert_eq!(count.get(), 2);
|
||||
/// assert_eq!(count(), 2);
|
||||
///
|
||||
/// // ✅ you can create "derived signals" with the same Fn() -> T interface
|
||||
/// let double_count = move || count.get() * 2; // signals are `Copy` so you can `move` them anywhere
|
||||
/// set_count.set(0);
|
||||
/// let double_count = move || count() * 2; // signals are `Copy` so you can `move` them anywhere
|
||||
/// set_count(0);
|
||||
/// assert_eq!(double_count(), 0);
|
||||
/// set_count.set(1);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(double_count(), 2);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
@@ -587,13 +585,13 @@ impl<T> SignalWithUntracked<T> for ReadSignal<T> {
|
||||
/// let (name, set_name) = create_signal(cx, "Alice".to_string());
|
||||
///
|
||||
/// // ❌ unnecessarily clones the string
|
||||
/// let first_char = move || name.get().chars().next().unwrap();
|
||||
/// let first_char = move || name().chars().next().unwrap();
|
||||
/// assert_eq!(first_char(), 'A');
|
||||
///
|
||||
/// // ✅ gets the first char without cloning the `String`
|
||||
/// let first_char = move || name.with(|n| n.chars().next().unwrap());
|
||||
/// assert_eq!(first_char(), 'A');
|
||||
/// set_name.set("Bob".to_string());
|
||||
/// set_name("Bob".to_string());
|
||||
/// assert_eq!(first_char(), 'B');
|
||||
/// # });
|
||||
/// ```
|
||||
@@ -664,8 +662,8 @@ impl<T> SignalWith<T> for ReadSignal<T> {
|
||||
///
|
||||
/// assert_eq!(count.get(), 0);
|
||||
///
|
||||
/// // count() is shorthand for count.get() on `nightly`
|
||||
/// // assert_eq!(count.get(), 0);
|
||||
/// // count() is shorthand for count.get()
|
||||
/// assert_eq!(count(), 0);
|
||||
/// # });
|
||||
/// ```
|
||||
impl<T: Clone> SignalGet<T> for ReadSignal<T> {
|
||||
@@ -851,16 +849,15 @@ impl<T> Hash for ReadSignal<T> {
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
///
|
||||
/// // ✅ calling the setter sets the value
|
||||
/// // `set_count(1)` on nightly
|
||||
/// set_count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // ❌ you could call the getter within the setter
|
||||
/// // set_count.set(count.get() + 1);
|
||||
/// // set_count(count() + 1);
|
||||
///
|
||||
/// // ✅ however it's more efficient to use .update() and mutate the value in place
|
||||
/// set_count.update(|count: &mut i32| *count += 1);
|
||||
/// assert_eq!(count.get(), 2);
|
||||
/// assert_eq!(count(), 2);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
/// ```
|
||||
@@ -955,13 +952,13 @@ impl<T> SignalUpdateUntracked<T> for WriteSignal<T> {
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
///
|
||||
/// // notifies subscribers
|
||||
/// set_count.update(|n| *n = 1); // it's easier just to call set_count.set(1), though!
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// set_count.update(|n| *n = 1); // it's easier just to call set_count(1), though!
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // you can include arbitrary logic in this update function
|
||||
/// // also notifies subscribers, even though the value hasn't changed
|
||||
/// set_count.update(|n| if *n > 3 { *n += 1 });
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
impl<T> SignalUpdate<T> for WriteSignal<T> {
|
||||
@@ -1015,13 +1012,13 @@ impl<T> SignalUpdate<T> for WriteSignal<T> {
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
///
|
||||
/// // notifies subscribers
|
||||
/// set_count.update(|n| *n = 1); // it's easier just to call set_count.set(1), though!
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// set_count.update(|n| *n = 1); // it's easier just to call set_count(1), though!
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // you can include arbitrary logic in this update function
|
||||
/// // also notifies subscribers, even though the value hasn't changed
|
||||
/// set_count.update(|n| if *n > 3 { *n += 1 });
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
impl<T> SignalSet<T> for WriteSignal<T> {
|
||||
@@ -1116,14 +1113,14 @@ impl<T> Hash for WriteSignal<T> {
|
||||
///
|
||||
/// // ✅ set the value
|
||||
/// count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // ❌ you can call the getter within the setter
|
||||
/// // count.set(count.get() + 1);
|
||||
///
|
||||
/// // ✅ however, it's more efficient to use .update() and mutate the value in place
|
||||
/// count.update(|count: &mut i32| *count += 1);
|
||||
/// assert_eq!(count.get(), 2);
|
||||
/// assert_eq!(count(), 2);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
/// ```
|
||||
@@ -1176,14 +1173,14 @@ pub fn create_rw_signal<T>(cx: Scope, value: T) -> RwSignal<T> {
|
||||
///
|
||||
/// // ✅ set the value
|
||||
/// count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // ❌ you can call the getter within the setter
|
||||
/// // count.set(count.get() + 1);
|
||||
///
|
||||
/// // ✅ however, it's more efficient to use .update() and mutate the value in place
|
||||
/// count.update(|count: &mut i32| *count += 1);
|
||||
/// assert_eq!(count.get(), 2);
|
||||
/// assert_eq!(count(), 2);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
/// ```
|
||||
@@ -1416,7 +1413,7 @@ impl<T> SignalUpdateUntracked<T> for RwSignal<T> {
|
||||
/// let name = create_rw_signal(cx, "Alice".to_string());
|
||||
///
|
||||
/// // ❌ unnecessarily clones the string
|
||||
/// let first_char = move || name.get().chars().next().unwrap();
|
||||
/// let first_char = move || name().chars().next().unwrap();
|
||||
/// assert_eq!(first_char(), 'A');
|
||||
///
|
||||
/// // ✅ gets the first char without cloning the `String`
|
||||
@@ -1494,8 +1491,8 @@ impl<T> SignalWith<T> for RwSignal<T> {
|
||||
///
|
||||
/// assert_eq!(count.get(), 0);
|
||||
///
|
||||
/// // count() is shorthand for count.get() on `nightly`
|
||||
/// // assert_eq!(count(), 0);
|
||||
/// // count() is shorthand for count.get()
|
||||
/// assert_eq!(count(), 0);
|
||||
/// # }).dispose();
|
||||
/// #
|
||||
/// ```
|
||||
@@ -1566,8 +1563,8 @@ impl<T: Clone> SignalGet<T> for RwSignal<T> {
|
||||
/// let count = create_rw_signal(cx, 0);
|
||||
///
|
||||
/// // notifies subscribers
|
||||
/// count.update(|n| *n = 1); // it's easier just to call set_count.set(1), though!
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// count.update(|n| *n = 1); // it's easier just to call set_count(1), though!
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// // you can include arbitrary logic in this update function
|
||||
/// // also notifies subscribers, even though the value hasn't changed
|
||||
@@ -1576,7 +1573,7 @@ impl<T: Clone> SignalGet<T> for RwSignal<T> {
|
||||
/// *n += 1
|
||||
/// }
|
||||
/// });
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
impl<T> SignalUpdate<T> for RwSignal<T> {
|
||||
@@ -1629,9 +1626,9 @@ impl<T> SignalUpdate<T> for RwSignal<T> {
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let count = create_rw_signal(cx, 0);
|
||||
///
|
||||
/// assert_eq!(count.get(), 0);
|
||||
/// assert_eq!(count(), 0);
|
||||
/// count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
impl<T> SignalSet<T> for RwSignal<T> {
|
||||
@@ -1709,11 +1706,11 @@ impl<T> RwSignal<T> {
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let count = create_rw_signal(cx, 0);
|
||||
/// let read_count = count.read_only();
|
||||
/// assert_eq!(count.get(), 0);
|
||||
/// assert_eq!(read_count.get(), 0);
|
||||
/// assert_eq!(count(), 0);
|
||||
/// assert_eq!(read_count(), 0);
|
||||
/// count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(read_count.get(), 1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// assert_eq!(read_count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
@@ -1749,9 +1746,9 @@ impl<T> RwSignal<T> {
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let count = create_rw_signal(cx, 0);
|
||||
/// let set_count = count.write_only();
|
||||
/// assert_eq!(count.get(), 0);
|
||||
/// set_count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(count(), 0);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
@@ -1784,11 +1781,11 @@ impl<T> RwSignal<T> {
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let count = create_rw_signal(cx, 0);
|
||||
/// let (get_count, set_count) = count.split();
|
||||
/// assert_eq!(count.get(), 0);
|
||||
/// assert_eq!(get_count.get(), 0);
|
||||
/// set_count.set(1);
|
||||
/// assert_eq!(count.get(), 1);
|
||||
/// assert_eq!(get_count.get(), 1);
|
||||
/// assert_eq!(count(), 0);
|
||||
/// assert_eq!(get_count(), 0);
|
||||
/// set_count(1);
|
||||
/// assert_eq!(count(), 1);
|
||||
/// assert_eq!(get_count(), 1);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
|
||||
@@ -45,14 +45,14 @@ where
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let double_count = Signal::derive(cx, move || count.get() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count.get() * 2);
|
||||
/// let double_count = Signal::derive(cx, move || count() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count() * 2);
|
||||
///
|
||||
/// // this function takes any kind of wrapped signal
|
||||
/// fn above_3(arg: &Signal<i32>) -> bool {
|
||||
/// // ✅ calling the signal clones and returns the value
|
||||
/// // can be `arg() > 3` on nightly
|
||||
/// arg.get() > 3
|
||||
/// // it is a shorthand for arg.get()
|
||||
/// arg() > 3
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(above_3(&count.into()), false);
|
||||
@@ -210,7 +210,7 @@ impl<T> SignalWithUntracked<T> for Signal<T> {
|
||||
/// // this function takes any kind of wrapped signal
|
||||
/// fn current_len_inefficient(arg: Signal<String>) -> usize {
|
||||
/// // ❌ unnecessarily clones the string
|
||||
/// arg.get().len()
|
||||
/// arg().len()
|
||||
/// }
|
||||
///
|
||||
/// fn current_len(arg: &Signal<String>) -> usize {
|
||||
@@ -222,9 +222,9 @@ impl<T> SignalWithUntracked<T> for Signal<T> {
|
||||
/// assert_eq!(current_len(&name_upper), 5);
|
||||
/// assert_eq!(current_len(&memoized_lower.into()), 5);
|
||||
///
|
||||
/// assert_eq!(name.get(), "Alice");
|
||||
/// assert_eq!(name_upper.get(), "ALICE");
|
||||
/// assert_eq!(memoized_lower.get(), "alice");
|
||||
/// assert_eq!(name(), "Alice");
|
||||
/// assert_eq!(name_upper(), "ALICE");
|
||||
/// assert_eq!(memoized_lower(), "alice");
|
||||
/// # });
|
||||
/// ```
|
||||
impl<T> SignalWith<T> for Signal<T> {
|
||||
@@ -276,8 +276,8 @@ impl<T> SignalWith<T> for Signal<T> {
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let double_count = Signal::derive(cx, move || count.get() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count.get() * 2);
|
||||
/// let double_count = Signal::derive(cx, move || count() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count() * 2);
|
||||
///
|
||||
/// // this function takes any kind of wrapped signal
|
||||
/// fn above_3(arg: &Signal<i32>) -> bool {
|
||||
@@ -342,7 +342,7 @@ where
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let double_count = Signal::derive(cx, move || count.get() * 2);
|
||||
/// let double_count = Signal::derive(cx, move || count() * 2);
|
||||
///
|
||||
/// // this function takes any kind of wrapped signal
|
||||
/// fn above_3(arg: &Signal<i32>) -> bool {
|
||||
@@ -499,15 +499,15 @@ impl<T> Eq for SignalTypes<T> where T: PartialEq {}
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let double_count = MaybeSignal::derive(cx, move || count.get() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count.get() * 2);
|
||||
/// let double_count = MaybeSignal::derive(cx, move || count() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count() * 2);
|
||||
/// let static_value = 5;
|
||||
///
|
||||
/// // this function takes either a reactive or non-reactive value
|
||||
/// fn above_3(arg: &MaybeSignal<i32>) -> bool {
|
||||
/// // ✅ calling the signal clones and returns the value
|
||||
/// // it is a shorthand for arg.get()
|
||||
/// arg.get() > 3
|
||||
/// arg() > 3
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(above_3(&static_value.into()), true);
|
||||
@@ -550,8 +550,8 @@ impl<T: Default> Default for MaybeSignal<T> {
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let double_count = MaybeSignal::derive(cx, move || count.get() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count.get() * 2);
|
||||
/// let double_count = MaybeSignal::derive(cx, move || count() * 2);
|
||||
/// let memoized_double_count = create_memo(cx, move |_| count() * 2);
|
||||
/// let static_value: MaybeSignal<i32> = 5.into();
|
||||
///
|
||||
/// // this function takes any kind of wrapped signal
|
||||
@@ -596,7 +596,7 @@ impl<T: Clone> SignalGet<T> for MaybeSignal<T> {
|
||||
/// // this function takes any kind of wrapped signal
|
||||
/// fn current_len_inefficient(arg: &MaybeSignal<String>) -> usize {
|
||||
/// // ❌ unnecessarily clones the string
|
||||
/// arg.get().len()
|
||||
/// arg().len()
|
||||
/// }
|
||||
///
|
||||
/// fn current_len(arg: &MaybeSignal<String>) -> usize {
|
||||
@@ -609,10 +609,10 @@ impl<T: Clone> SignalGet<T> for MaybeSignal<T> {
|
||||
/// assert_eq!(current_len(&memoized_lower.into()), 5);
|
||||
/// assert_eq!(current_len(&static_value), 3);
|
||||
///
|
||||
/// assert_eq!(name.get(), "Alice");
|
||||
/// assert_eq!(name_upper.get(), "ALICE");
|
||||
/// assert_eq!(memoized_lower.get(), "alice");
|
||||
/// assert_eq!(static_value.get(), "Bob");
|
||||
/// assert_eq!(name(), "Alice");
|
||||
/// assert_eq!(name_upper(), "ALICE");
|
||||
/// assert_eq!(memoized_lower(), "alice");
|
||||
/// assert_eq!(static_value(), "Bob");
|
||||
/// # });
|
||||
/// ```
|
||||
impl<T> SignalWith<T> for MaybeSignal<T> {
|
||||
@@ -709,7 +709,7 @@ where
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let double_count = Signal::derive(cx, move || count.get() * 2);
|
||||
/// let double_count = Signal::derive(cx, move || count() * 2);
|
||||
///
|
||||
/// // this function takes any kind of wrapped signal
|
||||
/// fn above_3(arg: &MaybeSignal<i32>) -> bool {
|
||||
|
||||
@@ -36,19 +36,19 @@ where
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let set_double_input = SignalSetter::map(cx, move |n| set_count.set(n * 2));
|
||||
/// let set_double_input = SignalSetter::map(cx, move |n| set_count(n * 2));
|
||||
///
|
||||
/// // this function takes any kind of signal setter
|
||||
/// fn set_to_4(setter: &SignalSetter<i32>) {
|
||||
/// // ✅ calling the signal sets the value
|
||||
/// // can be `setter(4)` on nightly
|
||||
/// setter.set(4);
|
||||
/// // it is a shorthand for arg.set()
|
||||
/// setter(4);
|
||||
/// }
|
||||
///
|
||||
/// set_to_4(&set_count.into());
|
||||
/// assert_eq!(count.get(), 4);
|
||||
/// assert_eq!(count(), 4);
|
||||
/// set_to_4(&set_double_input);
|
||||
/// assert_eq!(count.get(), 8);
|
||||
/// assert_eq!(count(), 8);
|
||||
/// # });
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -121,19 +121,19 @@ where
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let set_double_count = SignalSetter::map(cx, move |n| set_count.set(n * 2));
|
||||
/// let set_double_count = SignalSetter::map(cx, move |n| set_count(n * 2));
|
||||
///
|
||||
/// // this function takes any kind of signal setter
|
||||
/// fn set_to_4(setter: &SignalSetter<i32>) {
|
||||
/// // ✅ calling the signal sets the value
|
||||
/// // can be `setter(4)` on nightly
|
||||
/// setter.set(4)
|
||||
/// // it is a shorthand for arg.set()
|
||||
/// setter(4)
|
||||
/// }
|
||||
///
|
||||
/// set_to_4(&set_count.into());
|
||||
/// assert_eq!(count.get(), 4);
|
||||
/// assert_eq!(count(), 4);
|
||||
/// set_to_4(&set_double_count);
|
||||
/// assert_eq!(count.get(), 8);
|
||||
/// assert_eq!(count(), 8);
|
||||
/// # });
|
||||
/// ```
|
||||
#[track_caller]
|
||||
@@ -164,19 +164,19 @@ where
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 2);
|
||||
/// let set_double_count = SignalSetter::map(cx, move |n| set_count.set(n * 2));
|
||||
/// let set_double_count = SignalSetter::map(cx, move |n| set_count(n * 2));
|
||||
///
|
||||
/// // this function takes any kind of signal setter
|
||||
/// fn set_to_4(setter: &SignalSetter<i32>) {
|
||||
/// // ✅ calling the signal sets the value
|
||||
/// // can be `setter(4)` on nightly
|
||||
/// setter.set(4);
|
||||
/// // it is a shorthand for arg.set()
|
||||
/// setter(4);
|
||||
/// }
|
||||
///
|
||||
/// set_to_4(&set_count.into());
|
||||
/// assert_eq!(count.get(), 4);
|
||||
/// assert_eq!(count(), 4);
|
||||
/// set_to_4(&set_double_count);
|
||||
/// assert_eq!(count.get(), 8);
|
||||
/// assert_eq!(count(), 8);
|
||||
/// # });
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
|
||||
@@ -56,17 +56,17 @@ use crate::{
|
||||
///
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // note: in the browser, use leptos::log! instead
|
||||
/// println!("name is {}", name.get());
|
||||
/// println!("name is {}", name());
|
||||
/// });
|
||||
/// create_effect(cx, move |_| {
|
||||
/// println!("count is {}", count.get());
|
||||
/// println!("count is {}", count());
|
||||
/// });
|
||||
///
|
||||
/// // setting count only causes count to log, not name
|
||||
/// set_count.set(42);
|
||||
/// set_count(42);
|
||||
///
|
||||
/// // setting name only causes name to log, not count
|
||||
/// set_name.set("Bob".into());
|
||||
/// set_name("Bob".into());
|
||||
/// ```
|
||||
pub fn create_slice<T, O, S>(
|
||||
cx: Scope,
|
||||
|
||||
@@ -87,8 +87,8 @@ impl<T> StoredValue<T> {
|
||||
///
|
||||
/// // calling .get_value() clones and returns the value
|
||||
/// assert_eq!(data.get_value().value, "a");
|
||||
/// // can be `data().value` on nightly
|
||||
/// // assert_eq!(data().value, "a");
|
||||
/// // there's a short-hand getter form
|
||||
/// assert_eq!(data().value, "a");
|
||||
/// # });
|
||||
/// ```
|
||||
#[track_caller]
|
||||
|
||||
@@ -77,8 +77,7 @@ impl Trigger {
|
||||
/// let o = output.clone();
|
||||
/// let e = external_data.clone();
|
||||
/// create_effect(cx, move |_| {
|
||||
/// // can be `rerun_on_data()` on nightly
|
||||
/// rerun_on_data.track();
|
||||
/// rerun_on_data(); // or rerun_on_data.track();
|
||||
/// write!(o.borrow_mut(), "{}", *e.borrow());
|
||||
/// *e.borrow_mut() += 1;
|
||||
/// });
|
||||
@@ -226,7 +225,7 @@ impl SignalSet<()> for Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl FnOnce<()> for Trigger {
|
||||
type Output = ();
|
||||
|
||||
@@ -236,7 +235,7 @@ impl FnOnce<()> for Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl FnMut<()> for Trigger {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||
@@ -244,7 +243,7 @@ impl FnMut<()> for Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl Fn<()> for Trigger {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#[cfg(not(feature = "stable"))]
|
||||
use leptos_reactive::{
|
||||
create_isomorphic_effect, create_memo, create_runtime, create_rw_signal,
|
||||
create_scope, create_signal, SignalGet, SignalSet,
|
||||
create_scope, create_signal, SignalSet,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn effect_runs() {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
@@ -16,27 +18,28 @@ fn effect_runs() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let b = b.clone();
|
||||
move |_| {
|
||||
let formatted = format!("Value is {}", a.get());
|
||||
let formatted = format!("Value is {}", a());
|
||||
*b.borrow_mut() = formatted;
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(b.borrow().as_str(), "Value is -1");
|
||||
|
||||
set_a.set(1);
|
||||
set_a(1);
|
||||
|
||||
assert_eq!(b.borrow().as_str(), "Value is 1");
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn effect_tracks_memo() {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let (a, set_a) = create_signal(cx, -1);
|
||||
let b = create_memo(cx, move |_| format!("Value is {}", a.get()));
|
||||
let b = create_memo(cx, move |_| format!("Value is {}", a()));
|
||||
|
||||
// simulate an arbitrary side effect
|
||||
let c = Rc::new(RefCell::new(String::new()));
|
||||
@@ -44,21 +47,22 @@ fn effect_tracks_memo() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let c = c.clone();
|
||||
move |_| {
|
||||
*c.borrow_mut() = b.get();
|
||||
*c.borrow_mut() = b();
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(b.get().as_str(), "Value is -1");
|
||||
assert_eq!(b().as_str(), "Value is -1");
|
||||
assert_eq!(c.borrow().as_str(), "Value is -1");
|
||||
|
||||
set_a.set(1);
|
||||
set_a(1);
|
||||
|
||||
assert_eq!(b.get().as_str(), "Value is 1");
|
||||
assert_eq!(b().as_str(), "Value is 1");
|
||||
assert_eq!(c.borrow().as_str(), "Value is 1");
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn untrack_mutes_effect() {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
@@ -72,23 +76,23 @@ fn untrack_mutes_effect() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let b = b.clone();
|
||||
move |_| {
|
||||
let formatted =
|
||||
format!("Value is {}", cx.untrack(move || a.get()));
|
||||
let formatted = format!("Value is {}", cx.untrack(a));
|
||||
*b.borrow_mut() = formatted;
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(a.get(), -1);
|
||||
assert_eq!(a(), -1);
|
||||
assert_eq!(b.borrow().as_str(), "Value is -1");
|
||||
|
||||
set_a.set(1);
|
||||
set_a(1);
|
||||
|
||||
assert_eq!(a.get(), 1);
|
||||
assert_eq!(a(), 1);
|
||||
assert_eq!(b.borrow().as_str(), "Value is -1");
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn batching_actually_batches() {
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
@@ -103,8 +107,8 @@ fn batching_actually_batches() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let count = count.clone();
|
||||
move |_| {
|
||||
_ = first_name.get();
|
||||
_ = last_name.get();
|
||||
_ = first_name();
|
||||
_ = last_name();
|
||||
|
||||
count.set(count.get() + 1);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use leptos_reactive::*;
|
||||
#[cfg(not(feature = "stable"))]
|
||||
use leptos_reactive::{
|
||||
create_memo, create_runtime, create_scope, create_signal,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn basic_memo() {
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let a = create_memo(cx, |_| 5);
|
||||
assert_eq!(a.get(), 5);
|
||||
assert_eq!(a(), 5);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn signal_with_untracked() {
|
||||
use leptos_reactive::SignalWithUntracked;
|
||||
@@ -21,6 +26,7 @@ fn signal_with_untracked() {
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn signal_get_untracked() {
|
||||
use leptos_reactive::SignalGetUntracked;
|
||||
@@ -33,42 +39,45 @@ fn signal_get_untracked() {
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn memo_with_computed_value() {
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
let (b, set_b) = create_signal(cx, 0);
|
||||
let c = create_memo(cx, move |_| a.get() + b.get());
|
||||
assert_eq!(c.get(), 0);
|
||||
set_a.set(5);
|
||||
assert_eq!(c.get(), 5);
|
||||
set_b.set(1);
|
||||
assert_eq!(c.get(), 6);
|
||||
let c = create_memo(cx, move |_| a() + b());
|
||||
assert_eq!(c(), 0);
|
||||
set_a(5);
|
||||
assert_eq!(c(), 5);
|
||||
set_b(1);
|
||||
assert_eq!(c(), 6);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn nested_memos() {
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let (a, set_a) = create_signal(cx, 0); // 1
|
||||
let (b, set_b) = create_signal(cx, 0); // 2
|
||||
let c = create_memo(cx, move |_| a.get() + b.get()); // 3
|
||||
let d = create_memo(cx, move |_| c.get() * 2); // 4
|
||||
let e = create_memo(cx, move |_| d.get() + 1); // 5
|
||||
assert_eq!(d.get(), 0);
|
||||
set_a.set(5);
|
||||
assert_eq!(e.get(), 11);
|
||||
assert_eq!(d.get(), 10);
|
||||
assert_eq!(c.get(), 5);
|
||||
set_b.set(1);
|
||||
assert_eq!(e.get(), 13);
|
||||
assert_eq!(d.get(), 12);
|
||||
assert_eq!(c.get(), 6);
|
||||
let c = create_memo(cx, move |_| a() + b()); // 3
|
||||
let d = create_memo(cx, move |_| c() * 2); // 4
|
||||
let e = create_memo(cx, move |_| d() + 1); // 5
|
||||
assert_eq!(d(), 0);
|
||||
set_a(5);
|
||||
assert_eq!(e(), 11);
|
||||
assert_eq!(d(), 10);
|
||||
assert_eq!(c(), 5);
|
||||
set_b(1);
|
||||
assert_eq!(e(), 13);
|
||||
assert_eq!(d(), 12);
|
||||
assert_eq!(c(), 6);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn memo_runs_only_when_inputs_change() {
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
@@ -86,7 +95,7 @@ fn memo_runs_only_when_inputs_change() {
|
||||
let call_count = call_count.clone();
|
||||
move |_| {
|
||||
call_count.set(call_count.get() + 1);
|
||||
a.get() + b.get() + c.get()
|
||||
a() + b() + c()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -94,23 +103,24 @@ fn memo_runs_only_when_inputs_change() {
|
||||
assert_eq!(call_count.get(), 0);
|
||||
|
||||
// here we access the value a bunch of times
|
||||
assert_eq!(c.get(), 0);
|
||||
assert_eq!(c.get(), 0);
|
||||
assert_eq!(c.get(), 0);
|
||||
assert_eq!(c.get(), 0);
|
||||
assert_eq!(c.get(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
assert_eq!(c(), 0);
|
||||
|
||||
// we've still only called the memo calculation once
|
||||
assert_eq!(call_count.get(), 1);
|
||||
|
||||
// and we only call it again when an input changes
|
||||
set_a.set(1);
|
||||
assert_eq!(c.get(), 1);
|
||||
set_a(1);
|
||||
assert_eq!(c(), 1);
|
||||
assert_eq!(call_count.get(), 2);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn diamond_problem() {
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
@@ -118,10 +128,10 @@ fn diamond_problem() {
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let (name, set_name) = create_signal(cx, "Greg Johnston".to_string());
|
||||
let first = create_memo(cx, move |_| {
|
||||
name.get().split_whitespace().next().unwrap().to_string()
|
||||
name().split_whitespace().next().unwrap().to_string()
|
||||
});
|
||||
let last = create_memo(cx, move |_| {
|
||||
name.get().split_whitespace().nth(1).unwrap().to_string()
|
||||
name().split_whitespace().nth(1).unwrap().to_string()
|
||||
});
|
||||
|
||||
let combined_count = Rc::new(Cell::new(0));
|
||||
@@ -129,17 +139,17 @@ fn diamond_problem() {
|
||||
let combined_count = Rc::clone(&combined_count);
|
||||
move |_| {
|
||||
combined_count.set(combined_count.get() + 1);
|
||||
format!("{} {}", first.get(), last.get())
|
||||
format!("{} {}", first(), last())
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(first.get(), "Greg");
|
||||
assert_eq!(last.get(), "Johnston");
|
||||
assert_eq!(first(), "Greg");
|
||||
assert_eq!(last(), "Johnston");
|
||||
|
||||
set_name.set("Will Smith".to_string());
|
||||
assert_eq!(first.get(), "Will");
|
||||
assert_eq!(last.get(), "Smith");
|
||||
assert_eq!(combined.get(), "Will Smith");
|
||||
set_name("Will Smith".to_string());
|
||||
assert_eq!(first(), "Will");
|
||||
assert_eq!(last(), "Smith");
|
||||
assert_eq!(combined(), "Will Smith");
|
||||
// should not have run the memo logic twice, even
|
||||
// though both paths have been updated
|
||||
assert_eq!(combined_count.get(), 1);
|
||||
@@ -147,6 +157,7 @@ fn diamond_problem() {
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn dynamic_dependencies() {
|
||||
use leptos_reactive::create_isomorphic_effect;
|
||||
@@ -157,10 +168,10 @@ fn dynamic_dependencies() {
|
||||
let (last, set_last) = create_signal(cx, "Johnston");
|
||||
let (use_last, set_use_last) = create_signal(cx, true);
|
||||
let name = create_memo(cx, move |_| {
|
||||
if use_last.get() {
|
||||
format!("{} {}", first.get(), last.get())
|
||||
if use_last() {
|
||||
format!("{} {}", first(), last())
|
||||
} else {
|
||||
first.get().to_string()
|
||||
first().to_string()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -169,37 +180,37 @@ fn dynamic_dependencies() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let combined_count = Rc::clone(&combined_count);
|
||||
move |_| {
|
||||
_ = name.get();
|
||||
_ = name();
|
||||
combined_count.set(combined_count.get() + 1);
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(combined_count.get(), 1);
|
||||
|
||||
set_first.set("Bob");
|
||||
assert_eq!(name.get(), "Bob Johnston");
|
||||
set_first("Bob");
|
||||
assert_eq!(name(), "Bob Johnston");
|
||||
|
||||
assert_eq!(combined_count.get(), 2);
|
||||
|
||||
set_last.set("Thompson");
|
||||
set_last("Thompson");
|
||||
|
||||
assert_eq!(combined_count.get(), 3);
|
||||
|
||||
set_use_last.set(false);
|
||||
set_use_last(false);
|
||||
|
||||
assert_eq!(name.get(), "Bob");
|
||||
assert_eq!(name(), "Bob");
|
||||
assert_eq!(combined_count.get(), 4);
|
||||
|
||||
assert_eq!(combined_count.get(), 4);
|
||||
set_last.set("Jones");
|
||||
set_last("Jones");
|
||||
assert_eq!(combined_count.get(), 4);
|
||||
set_last.set("Smith");
|
||||
set_last("Smith");
|
||||
assert_eq!(combined_count.get(), 4);
|
||||
set_last.set("Stevens");
|
||||
set_last("Stevens");
|
||||
assert_eq!(combined_count.get(), 4);
|
||||
|
||||
set_use_last.set(true);
|
||||
assert_eq!(name.get(), "Bob Stevens");
|
||||
set_use_last(true);
|
||||
assert_eq!(name(), "Bob Stevens");
|
||||
assert_eq!(combined_count.get(), 5);
|
||||
})
|
||||
.dispose()
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
use leptos_reactive::*;
|
||||
#[cfg(not(feature = "stable"))]
|
||||
use leptos_reactive::{create_runtime, create_scope, create_signal};
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn basic_signal() {
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
assert_eq!(a.get(), 0);
|
||||
set_a.set(5);
|
||||
assert_eq!(a.get(), 5);
|
||||
assert_eq!(a(), 0);
|
||||
set_a(5);
|
||||
assert_eq!(a(), 5);
|
||||
})
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn derived_signals() {
|
||||
create_scope(create_runtime(), |cx| {
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
let (b, set_b) = create_signal(cx, 0);
|
||||
let c = move || a.get() + b.get();
|
||||
let c = move || a() + b();
|
||||
assert_eq!(c(), 0);
|
||||
set_a.set(5);
|
||||
set_a(5);
|
||||
assert_eq!(c(), 5);
|
||||
set_b.set(1);
|
||||
set_b(1);
|
||||
assert_eq!(c(), 6);
|
||||
})
|
||||
.dispose()
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#[cfg(not(feature = "stable"))]
|
||||
use leptos_reactive::{
|
||||
create_isomorphic_effect, create_runtime, create_scope, create_signal,
|
||||
signal_prelude::*, SignalGetUntracked, SignalSetUntracked,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn untracked_set_doesnt_trigger_effect() {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
@@ -16,7 +18,7 @@ fn untracked_set_doesnt_trigger_effect() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let b = b.clone();
|
||||
move |_| {
|
||||
let formatted = format!("Value is {}", a.get());
|
||||
let formatted = format!("Value is {}", a());
|
||||
*b.borrow_mut() = formatted;
|
||||
}
|
||||
});
|
||||
@@ -34,6 +36,7 @@ fn untracked_set_doesnt_trigger_effect() {
|
||||
.dispose()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
#[test]
|
||||
fn untracked_get_doesnt_trigger_effect() {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
@@ -48,11 +51,8 @@ fn untracked_get_doesnt_trigger_effect() {
|
||||
create_isomorphic_effect(cx, {
|
||||
let b = b.clone();
|
||||
move |_| {
|
||||
let formatted = format!(
|
||||
"Values are {} and {}",
|
||||
a.get(),
|
||||
a2.get_untracked()
|
||||
);
|
||||
let formatted =
|
||||
format!("Values are {} and {}", a(), a2.get_untracked());
|
||||
*b.borrow_mut() = formatted;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ readme = "../README.md"
|
||||
|
||||
[dependencies]
|
||||
leptos_reactive = { workspace = true }
|
||||
server_fn = { workspace = true}
|
||||
server_fn = { workspace = true, default-features = false }
|
||||
lazy_static = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
@@ -27,7 +27,7 @@ default-tls = ["server_fn/default-tls"]
|
||||
hydrate = ["leptos_reactive/hydrate"]
|
||||
rustls = ["server_fn/rustls"]
|
||||
ssr = ["leptos_reactive/ssr", "server_fn/ssr"]
|
||||
nightly = ["leptos_reactive/nightly", "server_fn/nightly"]
|
||||
stable = ["leptos_reactive/stable", "server_fn/stable"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["nightly"]
|
||||
denylist = ["stable"]
|
||||
|
||||
@@ -35,24 +35,24 @@ use std::{cell::Cell, future::Future, pin::Pin, rc::Rc};
|
||||
/// let version = save_data.version();
|
||||
///
|
||||
/// // before we do anything
|
||||
/// assert_eq!(input.get(), None); // no argument yet
|
||||
/// assert_eq!(pending.get(), false); // isn't pending a response
|
||||
/// assert_eq!(result_of_call.get(), None); // there's no "last value"
|
||||
/// assert_eq!(version.get(), 0);
|
||||
/// assert_eq!(input(), None); // no argument yet
|
||||
/// assert_eq!(pending(), false); // isn't pending a response
|
||||
/// assert_eq!(result_of_call(), None); // there's no "last value"
|
||||
/// assert_eq!(version(), 0);
|
||||
/// # if false {
|
||||
/// // dispatch the action
|
||||
/// save_data.dispatch("My todo".to_string());
|
||||
///
|
||||
/// // when we're making the call
|
||||
/// // assert_eq!(input.get(), Some("My todo".to_string()));
|
||||
/// // assert_eq!(pending.get(), true); // is pending
|
||||
/// // assert_eq!(result_of_call.get(), None); // has not yet gotten a response
|
||||
/// // assert_eq!(input(), Some("My todo".to_string()));
|
||||
/// // assert_eq!(pending(), true); // is pending
|
||||
/// // assert_eq!(result_of_call(), None); // has not yet gotten a response
|
||||
///
|
||||
/// // after call has resolved
|
||||
/// assert_eq!(input.get(), None); // input clears out after resolved
|
||||
/// assert_eq!(pending.get(), false); // no longer pending
|
||||
/// assert_eq!(result_of_call.get(), Some(42));
|
||||
/// assert_eq!(version.get(), 1);
|
||||
/// assert_eq!(input(), None); // input clears out after resolved
|
||||
/// assert_eq!(pending(), false); // no longer pending
|
||||
/// assert_eq!(result_of_call(), Some(42));
|
||||
/// assert_eq!(version(), 1);
|
||||
/// # }
|
||||
/// # });
|
||||
/// ```
|
||||
@@ -287,24 +287,24 @@ where
|
||||
/// let version = save_data.version();
|
||||
///
|
||||
/// // before we do anything
|
||||
/// assert_eq!(input.get(), None); // no argument yet
|
||||
/// assert_eq!(pending.get(), false); // isn't pending a response
|
||||
/// assert_eq!(result_of_call.get(), None); // there's no "last value"
|
||||
/// assert_eq!(version.get(), 0);
|
||||
/// assert_eq!(input(), None); // no argument yet
|
||||
/// assert_eq!(pending(), false); // isn't pending a response
|
||||
/// assert_eq!(result_of_call(), None); // there's no "last value"
|
||||
/// assert_eq!(version(), 0);
|
||||
/// # if false {
|
||||
/// // dispatch the action
|
||||
/// save_data.dispatch("My todo".to_string());
|
||||
///
|
||||
/// // when we're making the call
|
||||
/// // assert_eq!(input.get(), Some("My todo".to_string()));
|
||||
/// // assert_eq!(pending.get(), true); // is pending
|
||||
/// // assert_eq!(result_of_call.get(), None); // has not yet gotten a response
|
||||
/// // assert_eq!(input(), Some("My todo".to_string()));
|
||||
/// // assert_eq!(pending(), true); // is pending
|
||||
/// // assert_eq!(result_of_call(), None); // has not yet gotten a response
|
||||
///
|
||||
/// // after call has resolved
|
||||
/// assert_eq!(input.get(), None); // input clears out after resolved
|
||||
/// assert_eq!(pending.get(), false); // no longer pending
|
||||
/// assert_eq!(result_of_call.get(), Some(42));
|
||||
/// assert_eq!(version.get(), 1);
|
||||
/// assert_eq!(input(), None); // input clears out after resolved
|
||||
/// assert_eq!(pending(), false); // no longer pending
|
||||
/// assert_eq!(result_of_call(), Some(42));
|
||||
/// assert_eq!(version(), 1);
|
||||
/// # }
|
||||
/// # });
|
||||
/// ```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leptos_meta"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
@@ -23,8 +23,8 @@ default = []
|
||||
csr = ["leptos/csr", "leptos/tracing"]
|
||||
hydrate = ["leptos/hydrate", "leptos/tracing"]
|
||||
ssr = ["leptos/ssr", "leptos/tracing"]
|
||||
nightly = ["leptos/nightly", "leptos/tracing"]
|
||||
stable = ["leptos/stable", "leptos/tracing"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["nightly"]
|
||||
denylist = ["stable"]
|
||||
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
use cfg_if::cfg_if;
|
||||
use leptos::*;
|
||||
#[cfg(feature = "ssr")]
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
/// Contains the current metadata for the document's `<body>`.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct BodyContext {
|
||||
#[cfg(feature = "ssr")]
|
||||
class: Rc<RefCell<Option<TextProp>>>,
|
||||
#[cfg(feature = "ssr")]
|
||||
attributes: Rc<RefCell<Option<MaybeSignal<AdditionalAttributes>>>>,
|
||||
}
|
||||
|
||||
impl BodyContext {
|
||||
/// Converts the `<body>` metadata into an HTML string.
|
||||
#[cfg(any(feature = "ssr", doc))]
|
||||
pub fn as_string(&self) -> Option<String> {
|
||||
let class = self.class.borrow().as_ref().map(|val| {
|
||||
format!(
|
||||
"class=\"{}\"",
|
||||
leptos::leptos_dom::ssr::escape_attr(&val.get())
|
||||
)
|
||||
});
|
||||
let class = self
|
||||
.class
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|val| format!("class=\"{}\"", val.get()));
|
||||
let attributes = self.attributes.borrow().as_ref().map(|val| {
|
||||
val.with(|val| {
|
||||
val.into_iter()
|
||||
.map(|(n, v)| {
|
||||
format!(
|
||||
"{}=\"{}\"",
|
||||
n,
|
||||
leptos::leptos_dom::ssr::escape_attr(&v.get())
|
||||
)
|
||||
})
|
||||
.map(|(n, v)| format!("{}=\"{}\"", n, v.get()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
})
|
||||
@@ -68,7 +57,7 @@ impl std::fmt::Debug for BodyContext {
|
||||
/// provide_meta_context(cx);
|
||||
/// let (prefers_dark, set_prefers_dark) = create_signal(cx, false);
|
||||
/// let body_class = move || {
|
||||
/// if prefers_dark.get() {
|
||||
/// if prefers_dark() {
|
||||
/// "dark".to_string()
|
||||
/// } else {
|
||||
/// "light".to_string()
|
||||
@@ -92,6 +81,9 @@ pub fn Body(
|
||||
#[prop(optional, into)]
|
||||
attributes: Option<MaybeSignal<AdditionalAttributes>>,
|
||||
) -> impl IntoView {
|
||||
#[cfg(debug_assertions)]
|
||||
crate::feature_warning();
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
|
||||
let el = document().body().expect("there to be a <body> element");
|
||||
@@ -118,17 +110,10 @@ pub fn Body(
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if #[cfg(feature = "ssr")] {
|
||||
} else {
|
||||
let meta = crate::use_head(cx);
|
||||
*meta.body.class.borrow_mut() = class;
|
||||
*meta.body.attributes.borrow_mut() = attributes;
|
||||
} else {
|
||||
_ = cx;
|
||||
_ = class;
|
||||
_ = attributes;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
crate::feature_warning();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,38 @@
|
||||
use cfg_if::cfg_if;
|
||||
use leptos::*;
|
||||
#[cfg(feature = "ssr")]
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
/// Contains the current metadata for the document's `<html>`.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct HtmlContext {
|
||||
#[cfg(feature = "ssr")]
|
||||
lang: Rc<RefCell<Option<TextProp>>>,
|
||||
#[cfg(feature = "ssr")]
|
||||
dir: Rc<RefCell<Option<TextProp>>>,
|
||||
#[cfg(feature = "ssr")]
|
||||
class: Rc<RefCell<Option<TextProp>>>,
|
||||
#[cfg(feature = "ssr")]
|
||||
attributes: Rc<RefCell<Option<MaybeSignal<AdditionalAttributes>>>>,
|
||||
}
|
||||
|
||||
impl HtmlContext {
|
||||
/// Converts the `<html>` metadata into an HTML string.
|
||||
#[cfg(any(feature = "ssr", doc))]
|
||||
pub fn as_string(&self) -> Option<String> {
|
||||
let lang = self.lang.borrow().as_ref().map(|val| {
|
||||
format!(
|
||||
"lang=\"{}\"",
|
||||
leptos::leptos_dom::ssr::escape_attr(&val.get())
|
||||
)
|
||||
});
|
||||
let dir = self.dir.borrow().as_ref().map(|val| {
|
||||
format!(
|
||||
"dir=\"{}\"",
|
||||
leptos::leptos_dom::ssr::escape_attr(&val.get())
|
||||
)
|
||||
});
|
||||
let class = self.class.borrow().as_ref().map(|val| {
|
||||
format!(
|
||||
"class=\"{}\"",
|
||||
leptos::leptos_dom::ssr::escape_attr(&val.get())
|
||||
)
|
||||
});
|
||||
let lang = self
|
||||
.lang
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|val| format!("lang=\"{}\"", val.get()));
|
||||
let dir = self
|
||||
.dir
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|val| format!("dir=\"{}\"", val.get()));
|
||||
let class = self
|
||||
.class
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|val| format!("class=\"{}\"", val.get()));
|
||||
let attributes = self.attributes.borrow().as_ref().map(|val| {
|
||||
val.with(|val| {
|
||||
val.into_iter()
|
||||
.map(|(n, v)| {
|
||||
format!(
|
||||
"{}=\"{}\"",
|
||||
n,
|
||||
leptos::leptos_dom::ssr::escape_attr(&v.get())
|
||||
)
|
||||
})
|
||||
.map(|(n, v)| format!("{}=\"{}\"", n, v.get()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
})
|
||||
@@ -111,6 +96,9 @@ pub fn Html(
|
||||
#[prop(optional, into)]
|
||||
attributes: Option<MaybeSignal<AdditionalAttributes>>,
|
||||
) -> impl IntoView {
|
||||
#[cfg(debug_assertions)]
|
||||
crate::feature_warning();
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
|
||||
let el = document().document_element().expect("there to be a <html> element");
|
||||
@@ -151,20 +139,12 @@ pub fn Html(
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if #[cfg(feature = "ssr")] {
|
||||
} else {
|
||||
let meta = crate::use_head(cx);
|
||||
*meta.html.lang.borrow_mut() = lang;
|
||||
*meta.html.dir.borrow_mut() = dir;
|
||||
*meta.html.class.borrow_mut() = class;
|
||||
*meta.html.attributes.borrow_mut() = attributes;
|
||||
} else {
|
||||
_ = cx;
|
||||
_ = lang;
|
||||
_ = dir;
|
||||
_ = class;
|
||||
_ = attributes;
|
||||
#[cfg(debug_assertions)]
|
||||
crate::feature_warning();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
//! view! { cx,
|
||||
//! <Title
|
||||
//! // reactively sets document.title when `name` changes
|
||||
//! text=move || name.get()
|
||||
//! text=name
|
||||
//! // applies the `formatter` function to the `text` value
|
||||
//! formatter=|text| format!("“{text}” is your name")
|
||||
//! />
|
||||
//! <main>
|
||||
//! <input
|
||||
//! prop:value=move || name.get()
|
||||
//! on:input=move |ev| set_name.set(event_target_value(&ev))
|
||||
//! prop:value=name
|
||||
//! on:input=move |ev| set_name(event_target_value(&ev))
|
||||
//! />
|
||||
//! </main>
|
||||
//! }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leptos_router"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
@@ -60,9 +60,9 @@ default = []
|
||||
csr = ["leptos/csr"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = ["leptos/ssr", "dep:cached", "dep:lru", "dep:url", "dep:regex"]
|
||||
nightly = ["leptos/nightly"]
|
||||
stable = ["leptos/stable"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
# No need to test optional dependencies as they are enabled by the ssr feature
|
||||
denylist = ["url", "regex", "nightly"]
|
||||
denylist = ["url", "regex", "stable"]
|
||||
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
|
||||
|
||||
@@ -140,7 +140,7 @@ where
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "nightly")] {
|
||||
if #[cfg(not(feature = "stable"))] {
|
||||
auto trait NotOption {}
|
||||
impl<T> !NotOption for Option<T> {}
|
||||
|
||||
|
||||
@@ -183,9 +183,9 @@
|
||||
//! **Important Note:** You must enable one of `csr`, `hydrate`, or `ssr` to tell Leptos
|
||||
//! which mode your app is operating in.
|
||||
|
||||
#![cfg_attr(feature = "nightly", feature(auto_traits))]
|
||||
#![cfg_attr(feature = "nightly", feature(negative_impls))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_name_of_val))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(auto_traits))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(negative_impls))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(type_name_of_val))]
|
||||
|
||||
mod animation;
|
||||
mod components;
|
||||
|
||||
@@ -28,7 +28,7 @@ gloo-net = "0.2"
|
||||
js-sys = "0.3"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
reqwest = { version = "0.11"}
|
||||
reqwest = { version = "0.11", default-features = false }
|
||||
once_cell = "1"
|
||||
|
||||
[features]
|
||||
@@ -36,4 +36,4 @@ default = ["default-tls"]
|
||||
default-tls = ["reqwest/default-tls"]
|
||||
rustls = ["reqwest/rustls-tls"]
|
||||
ssr = ["inventory"]
|
||||
nightly = ["server_fn_macro_default/nightly"]
|
||||
stable = ["server_fn_macro_default/stable"]
|
||||
|
||||
@@ -19,4 +19,4 @@ server_fn = { version = "0.2" }
|
||||
serde = "1"
|
||||
|
||||
[features]
|
||||
nightly = ["server_fn_macro/nightly"]
|
||||
stable = ["server_fn_macro/stable"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg_attr(feature = "nightly", feature(proc_macro_span))]
|
||||
#![cfg_attr(not(feature = "stable"), feature(proc_macro_span))]
|
||||
//! This crate contains the default implementation of the #[macro@crate::server] macro without a context from the server. See the [server_fn_macro] crate for more information.
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user