mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 09:02:37 -05:00
Compare commits
1 Commits
context-do
...
component-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4360a73392 |
@@ -18,7 +18,6 @@ members = [
|
||||
"router",
|
||||
|
||||
# examples
|
||||
"examples/cloudflare-worker",
|
||||
"examples/counter",
|
||||
"examples/counter-isomorphic",
|
||||
"examples/counters",
|
||||
|
||||
11
README.md
11
README.md
@@ -54,17 +54,6 @@ Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained re
|
||||
- **Fine-grained reactivity**: The entire framework is build from reactive primitives. This allows for extremely performant code with minimal overhead: when a reactive signal’s value changes, it can update a single text node, toggle a single class, or remove an element from the DOM without any other code running. (_So, no virtual DOM!_)
|
||||
- **Declarative**: Tell Leptos how you want the page to look, and let the framework tell the browser how to do it.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The best way to get started with a Leptos project right now is to use the [`cargo-leptos`](https://github.com/akesson/cargo-leptos) build tool and our [starter template](https://github.com/leptos-rs/start).
|
||||
|
||||
```bash
|
||||
cargo install cargo-leptos
|
||||
cargo leptos new --git https://github.com/leptos-rs/start
|
||||
cd [your project name]
|
||||
cargo leptos watch
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
Here are some resources for learning more about Leptos:
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
tab_width = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
10
examples/cloudflare-worker/.gitignore
vendored
10
examples/cloudflare-worker/.gitignore
vendored
@@ -1,10 +0,0 @@
|
||||
.DS_Store
|
||||
/node_modules
|
||||
|
||||
**/*.rs.bk
|
||||
wasm-pack.log
|
||||
|
||||
build/
|
||||
/target
|
||||
/dist
|
||||
pkg
|
||||
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "counter-worker"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1"
|
||||
worker = { version = "0.0.12", optional = true }
|
||||
serde_json = "1"
|
||||
leptos = { path = "../../leptos", default-features = false }
|
||||
console_error_panic_hook = "0.1"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["ssr"]
|
||||
ssr = ["leptos/ssr", "dep:worker"]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
@@ -1,19 +0,0 @@
|
||||
This example shows the basics of how you’d render a Leptos component to HTML in a Cloudflare Worker and then hydrate it on the client side.
|
||||
|
||||
First, compile the client-side WebAssembly with
|
||||
```sh
|
||||
wasm-pack build --target=web --no-default-features --features=hydrate
|
||||
```
|
||||
|
||||
Then run the Worker:
|
||||
|
||||
```sh
|
||||
# run your Worker in an ideal development workflow (with a local server, file watcher & more)
|
||||
$ npm run dev
|
||||
|
||||
# deploy your Worker globally to the Cloudflare network (update your wrangler.toml file for configuration)
|
||||
$ npm run deploy
|
||||
```
|
||||
|
||||
## Important Note
|
||||
It's possible the URL for some of the JS necessary for hydration will change between `wasm-pack` builds. Obviously this is not great, but this is a proof of concept more than anything. If there's trouble loading JS, check the URL at `lib.rs:72` against the filenames in `pkg` and adjust `lib.rs` to match the correct path.
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"deploy": "wrangler publish",
|
||||
"dev": "wrangler dev --local"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
use cfg_if::cfg_if;
|
||||
use leptos::*;
|
||||
use worker::ResponseBody;
|
||||
|
||||
const PKG_PATH: &str = "/pkg/counter_worker";
|
||||
|
||||
#[component]
|
||||
pub fn Counter(cx: Scope) -> Element {
|
||||
let (value, set_value) = create_signal(cx, 0);
|
||||
|
||||
view! { cx,
|
||||
<div>
|
||||
<button on:click=move |_| set_value(0)>"Clear"</button>
|
||||
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
|
||||
<span>"Value: " {move || value().to_string()} "!"</span>
|
||||
<button on:click=move |_| set_value.update(|value| *value += 1)>"+1"</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
// Load client-side Wasm and hydrate rendered app
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "hydrate")] {
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn hydrate() {
|
||||
console_error_panic_hook::set_once();
|
||||
leptos::hydrate(body().unwrap(), move |cx| {
|
||||
view! { cx, <Counter/> }
|
||||
});
|
||||
}
|
||||
} else {
|
||||
use worker::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn log_request(req: &Request) {
|
||||
console_log!(
|
||||
"{} - [{}], located at: {:?}, within: {}",
|
||||
Date::now().to_string(),
|
||||
req.path(),
|
||||
req.cf().coordinates().unwrap_or_default(),
|
||||
req.cf().region().unwrap_or_else(|| "unknown region".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[event(fetch)]
|
||||
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
|
||||
log_request(&req);
|
||||
|
||||
// Optionally, use the Router to handle matching endpoints, use ":name" placeholders, or "*name"
|
||||
// catch-alls to match on specific patterns. Alternatively, use `Router::with_data(D)` to
|
||||
// provide arbitrary data that will be accessible in each route via the `ctx.data()` method.
|
||||
let router = Router::new();
|
||||
|
||||
// Add as many routes as your Worker needs! Each route will get a `Request` for handling HTTP
|
||||
// functionality and a `RouteContext` which you can use to and get route parameters and
|
||||
// Environment bindings like KV Stores, Durable Objects, Secrets, and Variables.
|
||||
router
|
||||
.get("/", |_, _| Response::from_html(render_html_page(&render_to_string(|cx| view! { cx, <Counter/> }))))
|
||||
// serve JS to load Wasm
|
||||
// this section is kind of a mess; ideally it could point to static files rather than inlining them and serving like this
|
||||
.get(&format!("{PKG_PATH}.js"), |_, _| {
|
||||
let mut headers = Headers::new();
|
||||
headers.set("Content-Type", "text/javascript")?;
|
||||
let body = ResponseBody::Body(include_str!("../pkg/counter_worker.js").as_bytes().to_vec());
|
||||
Ok(
|
||||
Response::from_body(body)?
|
||||
.with_headers(headers)
|
||||
)
|
||||
})
|
||||
.get("/pkg/snippets/leptos_dom-68e8edfe5e6c8b92/inline0.js", |_, _| {
|
||||
let mut headers = Headers::new();
|
||||
headers.set("Content-Type", "text/javascript")?;
|
||||
let body = ResponseBody::Body(include_str!("../pkg/snippets/leptos_dom-68e8edfe5e6c8b92/inline0.js").as_bytes().to_vec());
|
||||
Ok(
|
||||
Response::from_body(body)?
|
||||
.with_headers(headers)
|
||||
)
|
||||
})
|
||||
.get(&format!("{PKG_PATH}_bg.wasm"), |_, _| {
|
||||
let mut headers = Headers::new();
|
||||
headers.set("Content-Type", "application/wasm")?;
|
||||
let body = ResponseBody::Body(include_bytes!("../pkg/counter_worker_bg.wasm").to_vec());
|
||||
Ok(
|
||||
Response::from_body(body)?
|
||||
.with_headers(headers)
|
||||
)
|
||||
})
|
||||
.get("/worker-version", |_, ctx| {
|
||||
let version = ctx.var("WORKERS_RS_VERSION")?.to_string();
|
||||
Response::ok(version)
|
||||
})
|
||||
.run(req, env)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_html_page(body: &str) -> String {
|
||||
format!(
|
||||
r#"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="modulepreload" href="{PKG_PATH}.js">
|
||||
<link rel="preload" href="{PKG_PATH}_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<script type="module">import init, {{ hydrate }} from '{PKG_PATH}.js'; init('{PKG_PATH}_bg.wasm').then(hydrate);</script>
|
||||
</head>
|
||||
<body>
|
||||
{body}
|
||||
</body>
|
||||
</html>"#
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
name = "" # todo
|
||||
main = "build/worker/shim.mjs"
|
||||
compatibility_date = "2022-01-20"
|
||||
|
||||
[vars]
|
||||
WORKERS_RS_VERSION = "0.0.11"
|
||||
|
||||
[build]
|
||||
command = "cargo install -q worker-build --version 0.0.7 && worker-build --release"
|
||||
@@ -9,11 +9,11 @@ use leptos::*;
|
||||
/// - **step** [`i32`] - The change that should be applied on each step.
|
||||
#[component]
|
||||
pub fn SimpleCounter(cx: Scope, initial_value: i32, step: i32) -> web_sys::Element {
|
||||
let (value, set_value) = create_signal(cx, 0);
|
||||
let (value, set_value) = create_signal(cx, initial_value);
|
||||
|
||||
view! { cx,
|
||||
<div>
|
||||
<button on:click=move |_| set_value(0)>"Clear"</button>
|
||||
<button on:click=move |_| set_value(initial_value)>"Clear"</button>
|
||||
<button on:click=move |_| set_value.update(|value| *value -= step)>"-1"</button>
|
||||
<span>"Value: " {move || value().to_string()} "!"</span>
|
||||
<button on:click=move |_| set_value.update(|value| *value += step)>"+1"</button>
|
||||
|
||||
@@ -4,5 +4,5 @@ use leptos::*;
|
||||
pub fn main() {
|
||||
_ = console_log::init_with_level(log::Level::Debug);
|
||||
console_error_panic_hook::set_once();
|
||||
mount_to_body(|cx| view! { cx, <SimpleCounter initial_value=3 step=1/> })
|
||||
mount_to_body(|cx| view! { cx, <SimpleCounter initial_value=0 step=1/> })
|
||||
}
|
||||
|
||||
10
examples/tailwind/.gitignore
vendored
10
examples/tailwind/.gitignore
vendored
@@ -1,10 +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
|
||||
@@ -1,92 +0,0 @@
|
||||
[workspace]
|
||||
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
[dependencies]
|
||||
leptos = { git = "https://github.com/gbj/leptos", default-features = false, features = [
|
||||
"serde",
|
||||
] }
|
||||
leptos_meta = { git = "https://github.com/gbj/leptos", default-features = false }
|
||||
leptos_router = { git = "https://github.com/gbj/leptos", default-features = false }
|
||||
|
||||
gloo-net = { version = "0.2", features = ["http"] }
|
||||
log = "0.4"
|
||||
cfg-if = "1.0"
|
||||
|
||||
# dependecies for client (enable when csr or hydrate set)
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
console_log = { version = "0.2", optional = true }
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
||||
|
||||
# dependecies for server (enable when ssr set)
|
||||
actix-files = { version = "0.6", optional = true }
|
||||
actix-web = { version = "4", features = ["macros"], optional = true }
|
||||
futures = { version = "0.3", optional = true }
|
||||
simple_logger = { version = "4.0", optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
reqwest = { version = "0.11", features = ["json"], optional = true }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
opt-level = 'z'
|
||||
|
||||
[features]
|
||||
leptos_autoreload = []
|
||||
default = ["csr"]
|
||||
hydrate = [
|
||||
"leptos/hydrate",
|
||||
"leptos_meta/hydrate",
|
||||
"leptos_router/hydrate",
|
||||
"dep:wasm-bindgen",
|
||||
"dep:console_log",
|
||||
"dep:console_error_panic_hook",
|
||||
]
|
||||
csr = [
|
||||
"leptos/csr",
|
||||
"leptos_meta/csr",
|
||||
"leptos_router/csr",
|
||||
"dep:wasm-bindgen",
|
||||
"dep:console_log",
|
||||
"dep:console_error_panic_hook",
|
||||
]
|
||||
ssr = [
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:reqwest",
|
||||
"dep:actix-web",
|
||||
"dep:actix-files",
|
||||
"dep:futures",
|
||||
"dep:simple_logger",
|
||||
"dep:serde_json",
|
||||
]
|
||||
|
||||
[package.metadata.leptos]
|
||||
# Path, relative to root, to generat rust code to
|
||||
gen_file = "src/server/generated.rs"
|
||||
# Path to the source index.html file
|
||||
index_file = "index.html"
|
||||
# [Optional] Files in the asset_dir will be copied to the target/site directory
|
||||
assets_dir = "assets"
|
||||
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
|
||||
end2end_test_cmd = "npx playwright test"
|
||||
# On which port to serve the client side rendered site (when using --csr option)
|
||||
csr_port = 3000
|
||||
# The port to use for automatic reload monitoring
|
||||
reload_port = 3001
|
||||
|
||||
[package.metadata.leptos.style]
|
||||
# This points to the TailwindCSS output file
|
||||
file = "style/output.css"
|
||||
# A https://browsersl.ist query
|
||||
browserquery = "defaults"
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 henrik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,74 +0,0 @@
|
||||
# Leptos Starter Template
|
||||
|
||||
This is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/gbj/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
|
||||
|
||||
If you don't have `cargo-leptos` installed you can install it with
|
||||
|
||||
`cargo install --locked cargo-leptos`
|
||||
|
||||
Then run
|
||||
|
||||
`npx tailwindcss -i ./input.css -o ./style/output.scss --watch`
|
||||
|
||||
and
|
||||
|
||||
`cargo leptos watch`
|
||||
|
||||
in this directory.
|
||||
|
||||
You can begin editing your app at `src/app/mod.rs`.
|
||||
|
||||
## Installing Tailwind
|
||||
|
||||
You can install Tailwind using `npm`:
|
||||
|
||||
```bash
|
||||
npm install -D tailwindcss
|
||||
```
|
||||
|
||||
If you'd rather not use `npm`, you can install the Tailwind binary [here](https://github.com/tailwindlabs/tailwindcss/releases).
|
||||
|
||||
## Setting up with VS Code and Additional Tools
|
||||
|
||||
If you're using VS Code, add the following to your `settings.json`
|
||||
|
||||
```json
|
||||
"emmet.includeLanguages": {
|
||||
"rust": "html",
|
||||
"*.rs": "html"
|
||||
},
|
||||
"tailwindCSS.includeLanguages": {
|
||||
"rust": "html",
|
||||
"*.rs": "html"
|
||||
},
|
||||
"files.associations": {
|
||||
"*.rs": "rust"
|
||||
},
|
||||
"editor.quickSuggestions": {
|
||||
"other": "on",
|
||||
"comments": "on",
|
||||
"strings": true
|
||||
},
|
||||
"css.validate": false,
|
||||
```
|
||||
|
||||
|
||||
Install [Tailwind CSS Intellisense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss).
|
||||
|
||||
Install "VS Browser" extension, a browser at the right window.
|
||||
Allow vscode Ports forward: 3000, 3001.
|
||||
|
||||
|
||||
## Notes about Tooling
|
||||
|
||||
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||
|
||||
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||
2. `rustup default nightly` - setup nightly as default, or you can use rust-toolchain file later on
|
||||
3. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||
4. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||
5. `npm install -g sass` - install `dart-sass` (should be optional in future
|
||||
|
||||
## Attribution
|
||||
|
||||
Many thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/gbj/leptos/discussions/125).
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB |
74
examples/tailwind/end2end/package-lock.json
generated
74
examples/tailwind/end2end/package-lock.json
generated
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"name": "end2end",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "end2end",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
|
||||
"integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.28.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
|
||||
"integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/test": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
|
||||
"integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.28.0"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||
"dev": true
|
||||
},
|
||||
"playwright-core": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
|
||||
"integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "end2end",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.28.0"
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||
import { devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: "./tests",
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000,
|
||||
},
|
||||
/* 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.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
// baseURL: 'http://localhost:3000',
|
||||
|
||||
/* 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: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// port: 3000,
|
||||
// },
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,9 +0,0 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("homepage has title and links to intro page", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
|
||||
await expect(page).toHaveTitle("Cargo Leptos");
|
||||
|
||||
await expect(page.locator("h1")).toHaveText("Hi from your Leptos WASM!");
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Cargo Leptos</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!-- INJECT HEAD -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- INJECT BODY -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,26 +0,0 @@
|
||||
use leptos::*;
|
||||
use leptos_meta::*;
|
||||
|
||||
#[component]
|
||||
pub fn App(cx: Scope) -> Element {
|
||||
provide_context(cx, MetaContext::default());
|
||||
let (count, set_count) = create_signal(cx, 0);
|
||||
|
||||
view! {
|
||||
cx,
|
||||
<main class="my-0 mx-auto max-w-3xl text-center">
|
||||
<h2 class="p-6 text-4xl">"Welcome to Leptos with Tailwind"</h2>
|
||||
<p class="px-10 pb-10 text-left">"Tailwind will scan your Rust files for Tailwind class names and compile them into a CSS file."</p>
|
||||
<button
|
||||
class="bg-sky-600 hover:bg-sky-700 px-5 py-3 text-white rounded-lg"
|
||||
on:click=move |_| set_count.update(|count| *count += 1)
|
||||
>
|
||||
{move || if count() == 0 {
|
||||
"Click me!".to_string()
|
||||
} else {
|
||||
count().to_string()
|
||||
}}
|
||||
</button>
|
||||
</main>
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
mod app;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "hydrate")] {
|
||||
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main() {
|
||||
use app::*;
|
||||
use leptos::*;
|
||||
|
||||
_ = console_log::init_with_level(log::Level::Debug);
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
log!("hydrate mode - hydrating");
|
||||
|
||||
leptos::hydrate(body().unwrap(), move |cx| {
|
||||
view! { cx, <App/> }
|
||||
});
|
||||
}
|
||||
}
|
||||
else if #[cfg(feature = "csr")] {
|
||||
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main() {
|
||||
use app::*;
|
||||
use leptos::*;
|
||||
_ = console_log::init_with_level(log::Level::Debug);
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
log!("csr mode - mounting to body");
|
||||
|
||||
mount_to_body(|cx| {
|
||||
view! { cx, <App /> }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
mod app;
|
||||
#[cfg(feature = "ssr")]
|
||||
mod server;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "ssr")] {
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
server::run().await
|
||||
}
|
||||
}
|
||||
else {
|
||||
pub fn main() {}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
//! THIS FILE IS AUTOGENERATED, DO NOT MODIFY
|
||||
//! When building, `cargo-leptos` generates this file based on
|
||||
//! the `index.html` file specified in the Config.toml
|
||||
//!
|
||||
//! This file can be commited to version control. It only
|
||||
//! changes when the configuration changes
|
||||
|
||||
#[cfg(feature = "leptos_autoreload")]
|
||||
/// index.html content up to `<!-- INJECT HEAD -->` plus `cargo leptos` injected css and js content.
|
||||
pub const HTML_START: &str = r##"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Cargo Leptos</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script type="module">import init from '/pkg/app.js';init('/pkg/app.wasm');</script>
|
||||
<link rel="preload" href="/pkg/app.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<link rel="stylesheet" href="/pkg/app.css">
|
||||
<link rel="modulepreload" href="/pkg/app.js">
|
||||
<script crossorigin="">(function () {
|
||||
var ws = new WebSocket('ws://127.0.0.1:3001/autoreload');
|
||||
ws.onmessage = (ev) => {
|
||||
console.log(`Reload message: `);
|
||||
if (ev.data === 'reload') window.location.reload();
|
||||
};
|
||||
ws.onclose = () => console.warn('Autoreload stopped. Manual reload necessary.');
|
||||
})()
|
||||
</script>"##;
|
||||
|
||||
#[cfg(not(feature = "leptos_autoreload"))]
|
||||
/// index.html content up to `<!-- INJECT HEAD -->` plus `cargo leptos` injected css and js content.
|
||||
pub const HTML_START: &str = r##"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Cargo Leptos</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script type="module">import init from '/pkg/app.js';init('/pkg/app.wasm');</script>
|
||||
<link rel="preload" href="/pkg/app.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<link rel="stylesheet" href="/pkg/app.css">
|
||||
<link rel="modulepreload" href="/pkg/app.js">"##;
|
||||
|
||||
/// index.html content from `<!-- INJECT HEAD -->` up to `<!-- INJECT BODY -->`
|
||||
pub const HTML_MIDDLE: &str = r##" </head>
|
||||
<body>"##;
|
||||
|
||||
/// index.html content from `<!-- INJECT BODY -->` until the end
|
||||
pub const HTML_END: &str = r##" </body>
|
||||
</html>"##;
|
||||
@@ -1,92 +0,0 @@
|
||||
mod generated;
|
||||
|
||||
use crate::app::*;
|
||||
use actix_files::Files;
|
||||
use actix_web::*;
|
||||
use futures::StreamExt;
|
||||
use generated::{HTML_END, HTML_MIDDLE, HTML_START};
|
||||
use leptos::*;
|
||||
use leptos_meta::*;
|
||||
use leptos_router::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct ActixIntegration {
|
||||
path: ReadSignal<String>,
|
||||
}
|
||||
|
||||
impl History for ActixIntegration {
|
||||
fn location(&self, cx: leptos::Scope) -> ReadSignal<LocationChange> {
|
||||
create_signal(
|
||||
cx,
|
||||
LocationChange {
|
||||
value: self.path.get(),
|
||||
replace: false,
|
||||
scroll: true,
|
||||
state: State(None),
|
||||
},
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
fn navigate(&self, _loc: &LocationChange) {}
|
||||
}
|
||||
|
||||
// match every path — our router will handle actual dispatch
|
||||
#[get("{tail:.*}")]
|
||||
async fn render_app(req: HttpRequest) -> impl Responder {
|
||||
let path = req.path();
|
||||
|
||||
let query = req.query_string();
|
||||
let path = if query.is_empty() {
|
||||
"http://leptos".to_string() + path
|
||||
} else {
|
||||
"http://leptos".to_string() + path + "?" + query
|
||||
};
|
||||
|
||||
let app = move |cx| {
|
||||
let integration = ActixIntegration {
|
||||
path: create_signal(cx, path.clone()).0,
|
||||
};
|
||||
provide_context(cx, RouterIntegrationContext(std::rc::Rc::new(integration)));
|
||||
|
||||
view! { cx, <App /> }
|
||||
};
|
||||
|
||||
HttpResponse::Ok().content_type("text/html").streaming(
|
||||
futures::stream::once(async { HTML_START.to_string() })
|
||||
.chain(render_to_stream(move |cx| {
|
||||
use_context::<MetaContext>(cx)
|
||||
.map(|meta| meta.dehydrate())
|
||||
.unwrap_or_default()
|
||||
}))
|
||||
.chain(futures::stream::once(async { HTML_MIDDLE.to_string() }))
|
||||
.chain(render_to_stream(move |cx| app(cx).to_string()))
|
||||
.chain(futures::stream::once(async { HTML_END.to_string() }))
|
||||
.map(|html| Ok(web::Bytes::from(html)) as Result<web::Bytes>),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn run() -> std::io::Result<()> {
|
||||
let host = std::env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||
let port = std::env::var("PORT")
|
||||
.unwrap_or_else(|_| "3000".to_string())
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
|
||||
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
|
||||
|
||||
log::info!("serving at {host}:{port}");
|
||||
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.service(
|
||||
web::scope("/pkg")
|
||||
.service(Files::new("", "target/site/pkg"))
|
||||
.wrap(middleware::Compress::default()),
|
||||
)
|
||||
.service(render_app)
|
||||
})
|
||||
.bind((host, port))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
/** Imports your Tailwind output */
|
||||
@import './output.scss';
|
||||
@@ -1,583 +0,0 @@
|
||||
/*
|
||||
! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com
|
||||
*/
|
||||
|
||||
/*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
border-width: 0;
|
||||
/* 2 */
|
||||
border-style: solid;
|
||||
/* 2 */
|
||||
border-color: #e5e7eb;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
::before,
|
||||
::after {
|
||||
--tw-content: '';
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.5;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
-moz-tab-size: 4;
|
||||
/* 3 */
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
/* 3 */
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
/* 4 */
|
||||
font-feature-settings: normal;
|
||||
/* 5 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove the margin in all browsers.
|
||||
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Ensure horizontal rules are visible by default.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 2 */
|
||||
border-top-width: 1px;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0;
|
||||
/* 1 */
|
||||
border-color: inherit;
|
||||
/* 2 */
|
||||
border-collapse: collapse;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Change the font styles in all browsers.
|
||||
2. Remove the margin in Firefox and Safari.
|
||||
3. Remove default padding in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
/* 1 */
|
||||
font-size: 100%;
|
||||
/* 1 */
|
||||
font-weight: inherit;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 1 */
|
||||
margin: 0;
|
||||
/* 2 */
|
||||
padding: 0;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inheritance of text transform in Edge and Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Remove default button styles.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
background-color: transparent;
|
||||
/* 2 */
|
||||
background-image: none;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the odd appearance in Chrome and Safari.
|
||||
2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield;
|
||||
/* 1 */
|
||||
outline-offset: -2px;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
font: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||
*/
|
||||
|
||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default cursor for buttons.
|
||||
*/
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block;
|
||||
/* 1 */
|
||||
vertical-align: middle;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Make elements with the HTML hidden attribute stay hidden by default */
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
*, ::before, ::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
.my-0 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.max-w-3xl {
|
||||
max-width: 48rem;
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.bg-sky-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(2 132 199 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.px-10 {
|
||||
padding-left: 2.5rem;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.px-5 {
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.pb-10 {
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-4xl {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-sky-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(3 105 161 / var(--tw-bg-opacity));
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: {
|
||||
files: ["*.html", "./src/**/*.rs"],
|
||||
},
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
@@ -201,9 +201,7 @@ pub fn render_app_to_stream(
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="modulepreload" href="{pkg_path}.js">
|
||||
<link rel="preload" href="{pkg_path}.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init('{pkg_path}.wasm').then(hydrate);</script>
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init().then(hydrate);</script>
|
||||
{leptos_autoreload}
|
||||
"#
|
||||
);
|
||||
|
||||
@@ -228,9 +228,7 @@ pub fn render_app_to_stream(
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="modulepreload" href="{pkg_path}.js">
|
||||
<link rel="preload" href="{pkg_path}.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init('{pkg_path}.wasm').then(hydrate);</script>
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init().then(hydrate);</script>
|
||||
{leptos_autoreload}
|
||||
"#
|
||||
);
|
||||
|
||||
@@ -35,10 +35,10 @@ impl RenderOptions {
|
||||
let options = format!(
|
||||
r#"// This file is auto-generated. Changing it will have no effect on leptos. Change these by changing RenderOptions and rerunning
|
||||
RenderOptions {{
|
||||
pkg-path "{}"
|
||||
pkg_path "{}"
|
||||
environment "{:?}"
|
||||
socket-address "{:?}"
|
||||
reload-port {:?}
|
||||
socket_address "{:?}"
|
||||
reload_port {:?}
|
||||
}}
|
||||
"#,
|
||||
self.pkg_path, self.environment, self.socket_address, self.reload_port
|
||||
|
||||
@@ -33,7 +33,6 @@ cfg-if = "1.0.0"
|
||||
rmp-serde = "1.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
leptos = "0.0"
|
||||
tokio-test = "0.4"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -13,37 +13,35 @@ use crate::{runtime::with_runtime, Scope};
|
||||
/// arguments to a function or properties of a component.
|
||||
///
|
||||
/// ```
|
||||
/// use leptos::*;
|
||||
///
|
||||
/// // define a newtype we'll provide as context
|
||||
/// // contexts are stored by their types, so it can be useful to create
|
||||
/// // a new type to avoid confusion with other `WriteSignal<i32>`s we may have
|
||||
/// // all types to be shared via context should implement `Clone`
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// struct ValueSetter(WriteSignal<i32>);
|
||||
///
|
||||
/// #[component]
|
||||
/// pub fn Provider(cx: Scope) -> Element {
|
||||
/// let (value, set_value) = create_signal(cx, 0);
|
||||
///
|
||||
/// // the newtype pattern isn't *necessary* here but is a good practice
|
||||
/// // it avoids confusion with other possible future `WriteSignal<bool>` contexts
|
||||
/// // and makes it easier to refer to it in ButtonD
|
||||
/// provide_context(cx, ValueSetter(set_value));
|
||||
///
|
||||
/// // because <Consumer/> is nested inside <Provider/>,
|
||||
/// // it has access to the provided context
|
||||
/// view! { cx, <div><Consumer/></div> }
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// pub fn Consumer(cx: Scope) -> Element {
|
||||
/// // consume the provided context of type `ValueSetter` using `use_context`
|
||||
/// // this traverses up the tree of `Scope`s and gets the nearest provided `ValueSetter`
|
||||
/// let set_value = use_context::<ValueSetter>(cx).unwrap().0;
|
||||
///
|
||||
/// todo!()
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
///
|
||||
/// // Note: this example doesn’t use Leptos’s DOM model or component structure,
|
||||
/// // so it ends up being a little silly.
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct SharedData {
|
||||
/// name: (ReadSignal<String>, WriteSignal<String>)
|
||||
/// }
|
||||
///
|
||||
/// let my_context_obj = SharedData { name: create_signal(cx, "Greg".to_string()) };
|
||||
/// provide_context(cx, my_context_obj);
|
||||
///
|
||||
/// // we can access it in this Scope
|
||||
/// let shared_data = use_context::<SharedData>(cx).unwrap();
|
||||
/// let (name, set_name) = shared_data.name;
|
||||
///
|
||||
/// // we can access it somewhere in a lower scope
|
||||
/// cx.child_scope(|cx| {
|
||||
/// let shared_data_lower_in_tree = use_context::<SharedData>(cx).unwrap();
|
||||
/// let (name, set_name) = shared_data.name;
|
||||
/// set_name("Bob".to_string());
|
||||
/// });
|
||||
///
|
||||
/// // the change made in a lower scope updated the signal in the parent scope
|
||||
/// assert_eq!(name(), "Bob");
|
||||
///
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn provide_context<T>(cx: Scope, value: T)
|
||||
where
|
||||
@@ -67,37 +65,35 @@ where
|
||||
/// arguments to a function or properties of a component.
|
||||
///
|
||||
/// ```
|
||||
/// use leptos::*;
|
||||
///
|
||||
/// // define a newtype we'll provide as context
|
||||
/// // contexts are stored by their types, so it can be useful to create
|
||||
/// // a new type to avoid confusion with other `WriteSignal<i32>`s we may have
|
||||
/// // all types to be shared via context should implement `Clone`
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// struct ValueSetter(WriteSignal<i32>);
|
||||
///
|
||||
/// #[component]
|
||||
/// pub fn Provider(cx: Scope) -> Element {
|
||||
/// let (value, set_value) = create_signal(cx, 0);
|
||||
///
|
||||
/// // the newtype pattern isn't *necessary* here but is a good practice
|
||||
/// // it avoids confusion with other possible future `WriteSignal<bool>` contexts
|
||||
/// // and makes it easier to refer to it in ButtonD
|
||||
/// provide_context(cx, ValueSetter(set_value));
|
||||
///
|
||||
/// // because <Consumer/> is nested inside <Provider/>,
|
||||
/// // it has access to the provided context
|
||||
/// view! { cx, <div><Consumer/></div> }
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// pub fn Consumer(cx: Scope) -> Element {
|
||||
/// // consume the provided context of type `ValueSetter` using `use_context`
|
||||
/// // this traverses up the tree of `Scope`s and gets the nearest provided `ValueSetter`
|
||||
/// let set_value = use_context::<ValueSetter>(cx).unwrap().0;
|
||||
///
|
||||
/// todo!()
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
///
|
||||
/// // Note: this example doesn’t use Leptos’s DOM model or component structure,
|
||||
/// // so it ends up being a little silly.
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct SharedData {
|
||||
/// name: (ReadSignal<String>, WriteSignal<String>)
|
||||
/// }
|
||||
///
|
||||
/// let my_context_obj = SharedData { name: create_signal(cx, "Greg".to_string()) };
|
||||
/// provide_context(cx, my_context_obj);
|
||||
///
|
||||
/// // we can access it in this Scope
|
||||
/// let shared_data = use_context::<SharedData>(cx).unwrap();
|
||||
/// let (name, set_name) = shared_data.name;
|
||||
///
|
||||
/// // we can access it somewhere in a lower scope
|
||||
/// cx.child_scope(|cx| {
|
||||
/// let shared_data_lower_in_tree = use_context::<SharedData>(cx).unwrap();
|
||||
/// let (name, set_name) = shared_data.name;
|
||||
/// set_name("Bob".to_string());
|
||||
/// });
|
||||
///
|
||||
/// // the change made in a lower scope updated the signal in the parent scope
|
||||
/// assert_eq!(name(), "Bob");
|
||||
///
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn use_context<T>(cx: Scope) -> Option<T>
|
||||
where
|
||||
|
||||
@@ -123,11 +123,6 @@ pub trait UntrackedSettableSignal<T> {
|
||||
/// Runs the provided closure with a mutable reference to the current
|
||||
/// value without notifying dependents.
|
||||
fn update_untracked(&self, f: impl FnOnce(&mut T));
|
||||
|
||||
/// Runs the provided closure with a mutable reference to the current
|
||||
/// value without notifying dependents and returns
|
||||
/// the value the closure returned.
|
||||
fn update_returning_untracked<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -312,10 +312,6 @@ where
|
||||
fn update_untracked(&self, f: impl FnOnce(&mut T)) {
|
||||
self.id.update_with_no_effect(self.runtime, f);
|
||||
}
|
||||
|
||||
fn update_returning_untracked<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
|
||||
self.id.update_with_no_effect(self.runtime, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WriteSignal<T>
|
||||
@@ -343,31 +339,6 @@ where
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn update(&self, f: impl FnOnce(&mut T)) {
|
||||
self.id.update(self.runtime, f);
|
||||
}
|
||||
|
||||
/// Applies a function to the current value to mutate it in place
|
||||
/// and notifies subscribers that the signal has changed.
|
||||
/// Forwards the return value of the closure if the closure was called
|
||||
///
|
||||
/// **Note:** `update()` does not auto-memoize, i.e., it will notify subscribers
|
||||
/// even if the value has not actually changed.
|
||||
/// ```
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let (count, set_count) = create_signal(cx, 0);
|
||||
///
|
||||
/// // notifies subscribers
|
||||
/// let value = set_count.update_returning(|n| { *n = 1; *n * 10 });
|
||||
/// assert_eq!(value, Some(10));
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// let value = set_count.update_returning(|n| { *n += 1; *n * 10 });
|
||||
/// assert_eq!(value, Some(20));
|
||||
/// assert_eq!(count(), 2);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn update_returning<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
|
||||
self.id.update(self.runtime, f)
|
||||
}
|
||||
|
||||
@@ -391,7 +362,7 @@ where
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn set(&self, new_value: T) {
|
||||
self.id.update(self.runtime, |n| *n = new_value);
|
||||
self.id.update(self.runtime, |n| *n = new_value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,14 +497,10 @@ impl<T> UntrackedGettableSignal<T> for RwSignal<T> {
|
||||
impl<T> UntrackedSettableSignal<T> for RwSignal<T> {
|
||||
fn set_untracked(&self, new_value: T) {
|
||||
self.id
|
||||
.update_with_no_effect(self.runtime, |v| *v = new_value);
|
||||
.update_with_no_effect(self.runtime, |v| *v = new_value)
|
||||
}
|
||||
|
||||
fn update_untracked(&self, f: impl FnOnce(&mut T)) {
|
||||
self.id.update_with_no_effect(self.runtime, f);
|
||||
}
|
||||
|
||||
fn update_returning_untracked<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
|
||||
self.id.update_with_no_effect(self.runtime, f)
|
||||
}
|
||||
}
|
||||
@@ -604,29 +571,6 @@ where
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn update(&self, f: impl FnOnce(&mut T)) {
|
||||
self.id.update(self.runtime, f);
|
||||
}
|
||||
|
||||
/// Applies a function to the current value to mutate it in place
|
||||
/// and notifies subscribers that the signal has changed.
|
||||
/// Forwards the return value of the closure if the closure was called
|
||||
///
|
||||
/// ```
|
||||
/// # use leptos_reactive::*;
|
||||
/// # create_scope(create_runtime(), |cx| {
|
||||
/// let count = create_rw_signal(cx, 0);
|
||||
///
|
||||
/// // notifies subscribers
|
||||
/// let value = count.update_returning(|n| { *n = 1; *n * 10 });
|
||||
/// assert_eq!(value, Some(10));
|
||||
/// assert_eq!(count(), 1);
|
||||
///
|
||||
/// let value = count.update_returning(|n| { *n += 1; *n * 10 });
|
||||
/// assert_eq!(value, Some(20));
|
||||
/// assert_eq!(count(), 2);
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn update_returning<U>(&self, f: impl FnOnce(&mut T) -> U) -> Option<U> {
|
||||
self.id.update(self.runtime, f)
|
||||
}
|
||||
|
||||
@@ -645,7 +589,7 @@ where
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
pub fn set(&self, value: T) {
|
||||
self.id.update(self.runtime, |n| *n = value);
|
||||
self.id.update(self.runtime, |n| *n = value)
|
||||
}
|
||||
|
||||
/// Returns a read-only handle to the signal.
|
||||
@@ -853,7 +797,7 @@ impl SignalId {
|
||||
with_runtime(runtime, |runtime| self.try_with(runtime, f).unwrap())
|
||||
}
|
||||
|
||||
fn update_value<T, U>(&self, runtime: RuntimeId, f: impl FnOnce(&mut T) -> U) -> Option<U>
|
||||
fn update_value<T>(&self, runtime: RuntimeId, f: impl FnOnce(&mut T)) -> bool
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -865,29 +809,26 @@ impl SignalId {
|
||||
if let Some(value) = value {
|
||||
let mut value = value.borrow_mut();
|
||||
if let Some(value) = value.downcast_mut::<T>() {
|
||||
Some(f(value))
|
||||
f(value);
|
||||
true
|
||||
} else {
|
||||
debug_warn!(
|
||||
"[Signal::update] failed when downcasting to Signal<{}>",
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
None
|
||||
false
|
||||
}
|
||||
} else {
|
||||
debug_warn!(
|
||||
"[Signal::update] You’re trying to update a Signal<{}> that has already been disposed of. This is probably either a logic error in a component that creates and disposes of scopes, or a Resource resolving after its scope has been dropped without having been cleaned up.",
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
None
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn update<T, U>(
|
||||
&self,
|
||||
runtime_id: RuntimeId,
|
||||
f: impl FnOnce(&mut T) -> U,
|
||||
) -> Option<U>
|
||||
pub(crate) fn update<T>(&self, runtime_id: RuntimeId, f: impl FnOnce(&mut T))
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -896,7 +837,7 @@ impl SignalId {
|
||||
let updated = self.update_value(runtime_id, f);
|
||||
|
||||
// notify subscribers
|
||||
if updated.is_some() {
|
||||
if updated {
|
||||
let subs = {
|
||||
let subs = runtime.signal_subscribers.borrow();
|
||||
let subs = subs.get(*self);
|
||||
@@ -913,20 +854,15 @@ impl SignalId {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
updated
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn update_with_no_effect<T, U>(
|
||||
&self,
|
||||
runtime: RuntimeId,
|
||||
f: impl FnOnce(&mut T) -> U,
|
||||
) -> Option<U>
|
||||
pub(crate) fn update_with_no_effect<T>(&self, runtime: RuntimeId, f: impl FnOnce(&mut T))
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
// update the value
|
||||
self.update_value(runtime, f)
|
||||
self.update_value(runtime, f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@ impl SuspenseContext {
|
||||
/// Notifies the suspense context that a new resource is now pending.
|
||||
pub fn increment(&self) {
|
||||
let setter = self.set_pending_resources;
|
||||
queue_microtask(move || {
|
||||
setter.update(|n| *n += 1);
|
||||
});
|
||||
queue_microtask(move || setter.update(|n| *n += 1));
|
||||
}
|
||||
|
||||
/// Notifies the suspense context that a resource has resolved.
|
||||
@@ -49,7 +47,7 @@ impl SuspenseContext {
|
||||
if *n > 0 {
|
||||
*n -= 1
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -72,13 +72,8 @@ where
|
||||
/// An HTML [`a`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
|
||||
/// progressively enhanced to use client-side routing.
|
||||
///
|
||||
/// Client-side routing also works with ordinary HTML `<a>` tags, but `<A>` does two additional things:
|
||||
/// 1) Correctly resolves relative nested routes. Relative routing with ordinary `<a>` tags can be tricky.
|
||||
/// For example, if you have a route like `/post/:id`, `<A href="1">` will generate the correct relative
|
||||
/// route, but `<a href="1">` likely will not (depending on where it appears in your view.)
|
||||
/// 2) Sets the `aria-current` attribute if this link is the active link (i.e., it’s a link to the page you’re on).
|
||||
/// This is helpful for accessibility and for styling. For example, maybe you want to set the link a
|
||||
/// different color if it’s a link to the page you’re currently on.
|
||||
/// Note that client-side routing also works with ordinary HTML `<a>` tags, although
|
||||
/// the `<A/>` component automatically resolves nested relative routes correctly.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn A<C, H>(cx: Scope, props: AProps<C, H>) -> Element
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user