mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Merge pull request #196 from gbj/cleanup
Clean up issues relating to `0.1.0` merge
This commit is contained in:
commit
26e90d1959
24 changed files with 269 additions and 237 deletions
47
README.md
47
README.md
|
@ -63,9 +63,20 @@ Here are some resources for learning more about Leptos:
|
||||||
- [Common Bugs](https://github.com/gbj/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
|
- [Common Bugs](https://github.com/gbj/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
|
||||||
- Leptos Guide (in progress)
|
- Leptos Guide (in progress)
|
||||||
|
|
||||||
|
|
||||||
## `nightly` Note
|
## `nightly` Note
|
||||||
|
|
||||||
Most of the examples assume you’re using `nightly` Rust. If you’re on stable, note the following:
|
Most of the examples assume you’re using `nightly` Rust.
|
||||||
|
|
||||||
|
To set up your Rust toolchain using `nightly` (and add the ability to compile Rust to WebAssembly, if you haven’t already)
|
||||||
|
|
||||||
|
```
|
||||||
|
rustup toolchain install nightly
|
||||||
|
rustup default nightly
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
If you’re on `stable`, note the following:
|
||||||
|
|
||||||
1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.1.0-alpha", features = ["stable"] }`
|
1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.1.0-alpha", features = ["stable"] }`
|
||||||
2. `nightly` enables the function call syntax for accessing and setting signals. If you’re using `stable`,
|
2. `nightly` enables the function call syntax for accessing and setting signals. If you’re using `stable`,
|
||||||
|
@ -73,6 +84,17 @@ Most of the examples assume you’re using `nightly` Rust. If you’re on stable
|
||||||
[`counters-stable` example](https://github.com/gbj/leptos/blob/main/examples/counters-stable/src/main.rs)
|
[`counters-stable` example](https://github.com/gbj/leptos/blob/main/examples/counters-stable/src/main.rs)
|
||||||
for examples of the correct API.
|
for examples of the correct API.
|
||||||
|
|
||||||
|
## `cargo-leptos`
|
||||||
|
|
||||||
|
[`cargo-leptos`](https://github.com/akesson/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` 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
|
||||||
|
```
|
||||||
|
|
||||||
## FAQs
|
## FAQs
|
||||||
|
|
||||||
### Can I use this for native GUI?
|
### Can I use this for native GUI?
|
||||||
|
@ -106,17 +128,16 @@ There are some practical differences that make a significant difference:
|
||||||
- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use `create_rw_signal` to give a unified read/write signal.)_
|
- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use `create_rw_signal` to give a unified read/write signal.)_
|
||||||
- **Signals are functions:** In Leptos, you can call a signal to access it rather than calling a specific method (so, `count()` instead of `count.get()`) This creates a more consistent mental model: accessing a reactive value is always a matter of calling a function. For example:
|
- **Signals are functions:** In Leptos, you can call a signal to access it rather than calling a specific method (so, `count()` instead of `count.get()`) This creates a more consistent mental model: accessing a reactive value is always a matter of calling a function. For example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let (count, set_count) = create_signal(cx, 0); // a signal
|
let (count, set_count) = create_signal(cx, 0); // a signal
|
||||||
let double_count = move || count() * 2; // a derived signal
|
let double_count = move || count() * 2; // a derived signal
|
||||||
let memoized_count = create_memo(cx, move |_| count() * 3); // a memo
|
let memoized_count = create_memo(cx, move |_| count() * 3); // a memo
|
||||||
// all are accessed by calling them
|
// all are accessed by calling them
|
||||||
assert_eq!(count(), 0);
|
assert_eq!(count(), 0);
|
||||||
assert_eq!(double_count(), 0);
|
assert_eq!(double_count(), 0);
|
||||||
assert_eq!(memoized_count(), 0);
|
assert_eq!(memoized_count(), 0);
|
||||||
|
// this function can accept any of those signals
|
||||||
// this function can accept any of those signals
|
fn do_work_on_signal(my_signal: impl Fn() -> i32) { ... }
|
||||||
fn do_work_on_signal(my_signal: impl Fn() -> i32) { ... }
|
```
|
||||||
```
|
|
||||||
|
|
||||||
- **Signals and scopes are `'static`:** Both Leptos and Sycamore ease the pain of moving signals in closures (in particular, event listeners) by making them `Copy`, to avoid the `{ let count = count.clone(); move |_| ... }` that's very familiar in Rust UI code. Sycamore does this by using bump allocation to tie the lifetimes of its signals to its scopes: since references are `Copy`, `&'a Signal<T>` can be moved into a closure. Leptos does this by using arena allocation and passing around indices: types like `ReadSignal<T>`, `WriteSignal<T>`, and `Memo<T>` are actually wrapper for indices into an arena. This means that both scopes and signals are both `Copy` and `'static` in Leptos, which means that they can be moved easily into closures without adding lifetime complexity.
|
- **Signals and scopes are `'static`:** Both Leptos and Sycamore ease the pain of moving signals in closures (in particular, event listeners) by making them `Copy`, to avoid the `{ let count = count.clone(); move |_| ... }` that's very familiar in Rust UI code. Sycamore does this by using bump allocation to tie the lifetimes of its signals to its scopes: since references are `Copy`, `&'a Signal<T>` can be moved into a closure. Leptos does this by using arena allocation and passing around indices: types like `ReadSignal<T>`, `WriteSignal<T>`, and `Memo<T>` are actually wrapper for indices into an arena. This means that both scopes and signals are both `Copy` and `'static` in Leptos, which means that they can be moved easily into closures without adding lifetime complexity.
|
||||||
|
|
|
@ -10,7 +10,7 @@ This document is intended as a running list of common issues, with example code
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let (a, set_a) = create_signal(cx, 0);
|
let (a, set_a) = create_signal(cx, 0);
|
||||||
let (b, set_a) = create_signal(cx, false);
|
let (b, set_b) = create_signal(cx, false);
|
||||||
|
|
||||||
create_effect(cx, move |_| {
|
create_effect(cx, move |_| {
|
||||||
if a() > 5 {
|
if a() > 5 {
|
||||||
|
|
|
@ -38,6 +38,7 @@ ssr = [
|
||||||
"leptos_meta/ssr",
|
"leptos_meta/ssr",
|
||||||
"leptos_router/ssr",
|
"leptos_router/ssr",
|
||||||
]
|
]
|
||||||
|
stable = ["leptos/stable", "leptos_router/stable"]
|
||||||
|
|
||||||
[package.metadata.cargo-all-features]
|
[package.metadata.cargo-all-features]
|
||||||
denylist = ["actix-files", "actix-web", "leptos_actix"]
|
denylist = ["actix-files", "actix-web", "leptos_actix"]
|
||||||
|
|
|
@ -22,42 +22,45 @@ pub fn Story(cx: Scope) -> impl IntoView {
|
||||||
view! { cx,
|
view! { cx,
|
||||||
<>
|
<>
|
||||||
<Meta name="description" content=meta_description/>
|
<Meta name="description" content=meta_description/>
|
||||||
{move || story.read().map(|story| match story {
|
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||||
None => view! { cx, <div class="item-view">"Error loading this story."</div> },
|
{move || story.read().map(|story| match story {
|
||||||
Some(story) => view! { cx,
|
None => view! { cx, <div class="item-view">"Error loading this story."</div> },
|
||||||
<div class="item-view">
|
Some(story) => view! { cx,
|
||||||
<div class="item-view-header">
|
<div class="item-view">
|
||||||
<a href=story.url target="_blank">
|
<div class="item-view-header">
|
||||||
<h1>{story.title}</h1>
|
<a href=story.url target="_blank">
|
||||||
</a>
|
<h1>{story.title}</h1>
|
||||||
<span class="host">
|
</a>
|
||||||
"("{story.domain}")"
|
<span class="host">
|
||||||
</span>
|
"("{story.domain}")"
|
||||||
{story.user.map(|user| view! { cx, <p class="meta">
|
</span>
|
||||||
{story.points}
|
{story.user.map(|user| view! { cx, <p class="meta">
|
||||||
" points | by "
|
{story.points}
|
||||||
<A href=format!("/users/{}", user)>{user.clone()}</A>
|
" points | by "
|
||||||
{format!(" {}", story.time_ago)}
|
<A href=format!("/users/{}", user)>{user.clone()}</A>
|
||||||
</p>})}
|
{format!(" {}", story.time_ago)}
|
||||||
|
</p>})}
|
||||||
|
</div>
|
||||||
|
<div class="item-view-comments">
|
||||||
|
<p class="item-view-comments-header">
|
||||||
|
{if story.comments_count.unwrap_or_default() > 0 {
|
||||||
|
format!("{} comments", story.comments_count.unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
"No comments yet.".into()
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<ul class="comment-children">
|
||||||
|
<For
|
||||||
|
each=move || story.comments.clone().unwrap_or_default()
|
||||||
|
key=|comment| comment.id
|
||||||
|
view=move |comment| view! { cx, <Comment comment /> }
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-view-comments">
|
}})
|
||||||
<p class="item-view-comments-header">
|
}
|
||||||
{if story.comments_count.unwrap_or_default() > 0 {
|
</Suspense>
|
||||||
format!("{} comments", story.comments_count.unwrap_or_default())
|
|
||||||
} else {
|
|
||||||
"No comments yet.".into()
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<ul class="comment-children">
|
|
||||||
<For
|
|
||||||
each=move || story.comments.clone().unwrap_or_default()
|
|
||||||
key=|comment| comment.id
|
|
||||||
view=move |comment| view! { cx, <Comment comment /> }
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}})}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,28 +18,30 @@ pub fn User(cx: Scope) -> impl IntoView {
|
||||||
);
|
);
|
||||||
view! { cx,
|
view! { cx,
|
||||||
<div class="user-view">
|
<div class="user-view">
|
||||||
{move || user.read().map(|user| match user {
|
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||||
None => view! { cx, <h1>"User not found."</h1> }.into_any(),
|
{move || user.read().map(|user| match user {
|
||||||
Some(user) => view! { cx,
|
None => view! { cx, <h1>"User not found."</h1> }.into_any(),
|
||||||
<div>
|
Some(user) => view! { cx,
|
||||||
<h1>"User: " {&user.id}</h1>
|
<div>
|
||||||
<ul class="meta">
|
<h1>"User: " {&user.id}</h1>
|
||||||
<li>
|
<ul class="meta">
|
||||||
<span class="label">"Created: "</span> {user.created}
|
<li>
|
||||||
</li>
|
<span class="label">"Created: "</span> {user.created}
|
||||||
<li>
|
</li>
|
||||||
<span class="label">"Karma: "</span> {user.karma}
|
<li>
|
||||||
</li>
|
<span class="label">"Karma: "</span> {user.karma}
|
||||||
{user.about.as_ref().map(|about| view! { cx, <li inner_html=about class="about"></li> })}
|
</li>
|
||||||
</ul>
|
{user.about.as_ref().map(|about| view! { cx, <li inner_html=about class="about"></li> })}
|
||||||
<p class="links">
|
</ul>
|
||||||
<a href=format!("https://news.ycombinator.com/submitted?id={}", user.id)>"submissions"</a>
|
<p class="links">
|
||||||
" | "
|
<a href=format!("https://news.ycombinator.com/submitted?id={}", user.id)>"submissions"</a>
|
||||||
<a href=format!("https://news.ycombinator.com/threads?id={}", user.id)>"comments"</a>
|
" | "
|
||||||
</p>
|
<a href=format!("https://news.ycombinator.com/threads?id={}", user.id)>"comments"</a>
|
||||||
</div>
|
</p>
|
||||||
}.into_any()
|
</div>
|
||||||
})}
|
}.into_any()
|
||||||
|
})}
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
|
||||||
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
||||||
output-name = "leptos_hackernews"
|
output-name = "leptos_hackernews"
|
||||||
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
|
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
|
||||||
site-root = "target/site"
|
site-root = "/pkg"
|
||||||
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
|
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
|
||||||
# Defaults to pkg
|
# Defaults to pkg
|
||||||
site-pkg-dir = "pkg"
|
site-pkg-dir = "pkg"
|
||||||
|
|
|
@ -22,42 +22,45 @@ pub fn Story(cx: Scope) -> impl IntoView {
|
||||||
view! { cx,
|
view! { cx,
|
||||||
<>
|
<>
|
||||||
<Meta name="description" content=meta_description/>
|
<Meta name="description" content=meta_description/>
|
||||||
{move || story.read().map(|story| match story {
|
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||||
None => view! { cx, <div class="item-view">"Error loading this story."</div> },
|
{move || story.read().map(|story| match story {
|
||||||
Some(story) => view! { cx,
|
None => view! { cx, <div class="item-view">"Error loading this story."</div> },
|
||||||
<div class="item-view">
|
Some(story) => view! { cx,
|
||||||
<div class="item-view-header">
|
<div class="item-view">
|
||||||
<a href=story.url target="_blank">
|
<div class="item-view-header">
|
||||||
<h1>{story.title}</h1>
|
<a href=story.url target="_blank">
|
||||||
</a>
|
<h1>{story.title}</h1>
|
||||||
<span class="host">
|
</a>
|
||||||
"("{story.domain}")"
|
<span class="host">
|
||||||
</span>
|
"("{story.domain}")"
|
||||||
{story.user.map(|user| view! { cx, <p class="meta">
|
</span>
|
||||||
{story.points}
|
{story.user.map(|user| view! { cx, <p class="meta">
|
||||||
" points | by "
|
{story.points}
|
||||||
<A href=format!("/users/{}", user)>{user.clone()}</A>
|
" points | by "
|
||||||
{format!(" {}", story.time_ago)}
|
<A href=format!("/users/{}", user)>{user.clone()}</A>
|
||||||
</p>})}
|
{format!(" {}", story.time_ago)}
|
||||||
|
</p>})}
|
||||||
|
</div>
|
||||||
|
<div class="item-view-comments">
|
||||||
|
<p class="item-view-comments-header">
|
||||||
|
{if story.comments_count.unwrap_or_default() > 0 {
|
||||||
|
format!("{} comments", story.comments_count.unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
"No comments yet.".into()
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<ul class="comment-children">
|
||||||
|
<For
|
||||||
|
each=move || story.comments.clone().unwrap_or_default()
|
||||||
|
key=|comment| comment.id
|
||||||
|
view=move |comment| view! { cx, <Comment comment /> }
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-view-comments">
|
}})
|
||||||
<p class="item-view-comments-header">
|
}
|
||||||
{if story.comments_count.unwrap_or_default() > 0 {
|
</Suspense>
|
||||||
format!("{} comments", story.comments_count.unwrap_or_default())
|
|
||||||
} else {
|
|
||||||
"No comments yet.".into()
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<ul class="comment-children">
|
|
||||||
<For
|
|
||||||
each=move || story.comments.clone().unwrap_or_default()
|
|
||||||
key=|comment| comment.id
|
|
||||||
view=move |comment| view! { cx, <Comment comment /> }
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}})}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,28 +18,30 @@ pub fn User(cx: Scope) -> impl IntoView {
|
||||||
);
|
);
|
||||||
view! { cx,
|
view! { cx,
|
||||||
<div class="user-view">
|
<div class="user-view">
|
||||||
{move || user.read().map(|user| match user {
|
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||||
None => view! { cx, <h1>"User not found."</h1> }.into_any(),
|
{move || user.read().map(|user| match user {
|
||||||
Some(user) => view! { cx,
|
None => view! { cx, <h1>"User not found."</h1> }.into_any(),
|
||||||
<div>
|
Some(user) => view! { cx,
|
||||||
<h1>"User: " {&user.id}</h1>
|
<div>
|
||||||
<ul class="meta">
|
<h1>"User: " {&user.id}</h1>
|
||||||
<li>
|
<ul class="meta">
|
||||||
<span class="label">"Created: "</span> {user.created}
|
<li>
|
||||||
</li>
|
<span class="label">"Created: "</span> {user.created}
|
||||||
<li>
|
</li>
|
||||||
<span class="label">"Karma: "</span> {user.karma}
|
<li>
|
||||||
</li>
|
<span class="label">"Karma: "</span> {user.karma}
|
||||||
{user.about.as_ref().map(|about| view! { cx, <li inner_html=about class="about"></li> })}
|
</li>
|
||||||
</ul>
|
{user.about.as_ref().map(|about| view! { cx, <li inner_html=about class="about"></li> })}
|
||||||
<p class="links">
|
</ul>
|
||||||
<a href=format!("https://news.ycombinator.com/submitted?id={}", user.id)>"submissions"</a>
|
<p class="links">
|
||||||
" | "
|
<a href=format!("https://news.ycombinator.com/submitted?id={}", user.id)>"submissions"</a>
|
||||||
<a href=format!("https://news.ycombinator.com/threads?id={}", user.id)>"comments"</a>
|
" | "
|
||||||
</p>
|
<a href=format!("https://news.ycombinator.com/threads?id={}", user.id)>"comments"</a>
|
||||||
</div>
|
</p>
|
||||||
}.into_any()
|
</div>
|
||||||
})}
|
}.into_any()
|
||||||
|
})}
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ pub fn Settings(cx: Scope) -> impl IntoView {
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>"Name"</legend>
|
<legend>"Name"</legend>
|
||||||
<input type="text" name="first_name" placeholder="First"/>
|
<input type="text" name="first_name" placeholder="First"/>
|
||||||
<input type="text" name="first_name" placeholder="Last"/>
|
<input type="text" name="last_name" placeholder="Last"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<pre>"This page is just a placeholder."</pre>
|
<pre>"This page is just a placeholder."</pre>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -14,9 +14,9 @@ cfg_if! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_server_functions() {
|
pub fn register_server_functions() {
|
||||||
GetTodos::register();
|
_ = GetTodos::register();
|
||||||
AddTodo::register();
|
_ = AddTodo::register();
|
||||||
DeleteTodo::register();
|
_ = DeleteTodo::register();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||||
|
@ -151,7 +151,7 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
||||||
</label>
|
</label>
|
||||||
<input type="submit" value="Add"/>
|
<input type="submit" value="Add"/>
|
||||||
</MultiActionForm>
|
</MultiActionForm>
|
||||||
<Suspense fallback=move || view! {cx, <p>"Loading..."</p> }>
|
<Transition fallback=move || view! {cx, <p>"Loading..."</p> }>
|
||||||
{
|
{
|
||||||
let delete_todo = delete_todo.clone();
|
let delete_todo = delete_todo.clone();
|
||||||
move || {
|
move || {
|
||||||
|
@ -221,7 +221,7 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</Suspense>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ cfg_if! {
|
||||||
use actix_files::{Files};
|
use actix_files::{Files};
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
use crate::todo::*;
|
use crate::todo::*;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
#[get("/style.css")]
|
#[get("/style.css")]
|
||||||
async fn css() -> impl Responder {
|
async fn css() -> impl Responder {
|
||||||
|
|
|
@ -13,9 +13,9 @@ cfg_if! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_server_functions() {
|
pub fn register_server_functions() {
|
||||||
GetTodos::register();
|
_ = GetTodos::register();
|
||||||
AddTodo::register();
|
_ = AddTodo::register();
|
||||||
DeleteTodo::register();
|
_ = DeleteTodo::register();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||||
|
@ -135,7 +135,7 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
||||||
</label>
|
</label>
|
||||||
<input type="submit" value="Add"/>
|
<input type="submit" value="Add"/>
|
||||||
</MultiActionForm>
|
</MultiActionForm>
|
||||||
<Suspense fallback=move || view! {cx, <p>"Loading..."</p> }>
|
<Transition fallback=move || view! {cx, <p>"Loading..."</p> }>
|
||||||
{
|
{
|
||||||
let delete_todo = delete_todo.clone();
|
let delete_todo = delete_todo.clone();
|
||||||
move || {
|
move || {
|
||||||
|
@ -205,7 +205,7 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</Suspense>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,8 +187,10 @@ where
|
||||||
// If it was, `DynChild` no longer "owns" that child, and
|
// If it was, `DynChild` no longer "owns" that child, and
|
||||||
// is therefore no longer sound to unmount it from the DOM
|
// is therefore no longer sound to unmount it from the DOM
|
||||||
// or to reuse it in the case of a text node
|
// or to reuse it in the case of a text node
|
||||||
let was_child_moved =
|
|
||||||
child.get_closing_node().next_sibling().as_ref() != Some(&closing);
|
// FIXME this was breaking DynChild updates on text nodes, at least...
|
||||||
|
let was_child_moved = false;
|
||||||
|
//child.get_closing_node().next_sibling().as_ref() != Some(&closing);
|
||||||
|
|
||||||
// If the previous child was a text node, we would like to
|
// If the previous child was a text node, we would like to
|
||||||
// make use of it again if our current child is also a text
|
// make use of it again if our current child is also a text
|
||||||
|
|
|
@ -49,7 +49,7 @@ use std::fmt::Debug;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn create_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
|
pub fn create_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
|
||||||
where
|
where
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(not(feature = "ssr"))] {
|
if #[cfg(not(feature = "ssr"))] {
|
||||||
|
@ -90,7 +90,7 @@ where
|
||||||
/// # }).dispose();
|
/// # }).dispose();
|
||||||
pub fn create_isomorphic_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
|
pub fn create_isomorphic_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
|
||||||
where
|
where
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
let e = cx.runtime.create_effect(f);
|
let e = cx.runtime.create_effect(f);
|
||||||
cx.with_scope_property(|prop| prop.push(ScopeProperty::Effect(e)))
|
cx.with_scope_property(|prop| prop.push(ScopeProperty::Effect(e)))
|
||||||
|
@ -99,7 +99,7 @@ where
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn create_render_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
|
pub fn create_render_effect<T>(cx: Scope, f: impl Fn(Option<T>) -> T + 'static)
|
||||||
where
|
where
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
create_effect(cx, f);
|
create_effect(cx, f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> FnOnce<()> for Memo<T>
|
impl<T> FnOnce<()> for Memo<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> FnMut<()> for Memo<T>
|
impl<T> FnMut<()> for Memo<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||||
self.get()
|
self.get()
|
||||||
|
@ -237,7 +237,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> Fn<()> for Memo<T>
|
impl<T> Fn<()> for Memo<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||||
self.get()
|
self.get()
|
||||||
|
|
|
@ -66,7 +66,7 @@ pub fn create_resource<S, T, Fu>(
|
||||||
) -> Resource<S, T>
|
) -> Resource<S, T>
|
||||||
where
|
where
|
||||||
S: PartialEq + Debug + Clone + 'static,
|
S: PartialEq + Debug + Clone + 'static,
|
||||||
T: Debug + Serializable + 'static,
|
T: Serializable + 'static,
|
||||||
Fu: Future<Output = T> + 'static,
|
Fu: Future<Output = T> + 'static,
|
||||||
{
|
{
|
||||||
// can't check this on the server without running the future
|
// can't check this on the server without running the future
|
||||||
|
@ -91,7 +91,7 @@ pub fn create_resource_with_initial_value<S, T, Fu>(
|
||||||
) -> Resource<S, T>
|
) -> Resource<S, T>
|
||||||
where
|
where
|
||||||
S: PartialEq + Debug + Clone + 'static,
|
S: PartialEq + Debug + Clone + 'static,
|
||||||
T: Debug + Serializable + 'static,
|
T: Serializable + 'static,
|
||||||
Fu: Future<Output = T> + 'static,
|
Fu: Future<Output = T> + 'static,
|
||||||
{
|
{
|
||||||
let resolved = initial_value.is_some();
|
let resolved = initial_value.is_some();
|
||||||
|
@ -173,7 +173,7 @@ pub fn create_local_resource<S, T, Fu>(
|
||||||
) -> Resource<S, T>
|
) -> Resource<S, T>
|
||||||
where
|
where
|
||||||
S: PartialEq + Debug + Clone + 'static,
|
S: PartialEq + Debug + Clone + 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
Fu: Future<Output = T> + 'static,
|
Fu: Future<Output = T> + 'static,
|
||||||
{
|
{
|
||||||
let initial_value = None;
|
let initial_value = None;
|
||||||
|
@ -195,7 +195,7 @@ pub fn create_local_resource_with_initial_value<S, T, Fu>(
|
||||||
) -> Resource<S, T>
|
) -> Resource<S, T>
|
||||||
where
|
where
|
||||||
S: PartialEq + Debug + Clone + 'static,
|
S: PartialEq + Debug + Clone + 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
Fu: Future<Output = T> + 'static,
|
Fu: Future<Output = T> + 'static,
|
||||||
{
|
{
|
||||||
let resolved = initial_value.is_some();
|
let resolved = initial_value.is_some();
|
||||||
|
@ -244,7 +244,7 @@ where
|
||||||
fn load_resource<S, T>(_cx: Scope, _id: ResourceId, r: Rc<ResourceState<S, T>>)
|
fn load_resource<S, T>(_cx: Scope, _id: ResourceId, r: Rc<ResourceState<S, T>>)
|
||||||
where
|
where
|
||||||
S: PartialEq + Debug + Clone + 'static,
|
S: PartialEq + Debug + Clone + 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
r.load(false)
|
r.load(false)
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ where
|
||||||
fn load_resource<S, T>(cx: Scope, id: ResourceId, r: Rc<ResourceState<S, T>>)
|
fn load_resource<S, T>(cx: Scope, id: ResourceId, r: Rc<ResourceState<S, T>>)
|
||||||
where
|
where
|
||||||
S: PartialEq + Debug + Clone + 'static,
|
S: PartialEq + Debug + Clone + 'static,
|
||||||
T: Debug + Serializable + 'static,
|
T: Serializable + 'static,
|
||||||
{
|
{
|
||||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||||
|
|
||||||
|
@ -267,18 +267,8 @@ where
|
||||||
|
|
||||||
let res = T::from_json(&data).expect_throw("could not deserialize Resource JSON");
|
let res = T::from_json(&data).expect_throw("could not deserialize Resource JSON");
|
||||||
|
|
||||||
// if we're under Suspense, the HTML has already streamed in so we can just set it
|
r.set_value.update(|n| *n = Some(res));
|
||||||
// if not under Suspense, there will be a hydration mismatch, so let's wait a tick
|
r.set_loading.update(|n| *n = false);
|
||||||
if use_context::<SuspenseContext>(cx).is_some() {
|
|
||||||
r.set_value.update(|n| *n = Some(res));
|
|
||||||
r.set_loading.update(|n| *n = false);
|
|
||||||
} else {
|
|
||||||
let r = Rc::clone(&r);
|
|
||||||
spawn_local(async move {
|
|
||||||
r.set_value.update(|n| *n = Some(res));
|
|
||||||
r.set_loading.update(|n| *n = false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// for reactivity
|
// for reactivity
|
||||||
r.source.subscribe();
|
r.source.subscribe();
|
||||||
|
@ -326,8 +316,8 @@ where
|
||||||
|
|
||||||
impl<S, T> Resource<S, T>
|
impl<S, T> Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
/// Clones and returns the current value of the resource ([Option::None] if the
|
/// Clones and returns the current value of the resource ([Option::None] if the
|
||||||
/// resource is still pending). Also subscribes the running effect to this
|
/// resource is still pending). Also subscribes the running effect to this
|
||||||
|
@ -433,8 +423,8 @@ where
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Resource<S, T>
|
pub struct Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + 'static,
|
S: 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
runtime: RuntimeId,
|
runtime: RuntimeId,
|
||||||
pub(crate) id: ResourceId,
|
pub(crate) id: ResourceId,
|
||||||
|
@ -450,8 +440,8 @@ slotmap::new_key_type! {
|
||||||
|
|
||||||
impl<S, T> Clone for Resource<S, T>
|
impl<S, T> Clone for Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + Clone + 'static,
|
T: Clone + 'static,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -465,16 +455,16 @@ where
|
||||||
|
|
||||||
impl<S, T> Copy for Resource<S, T>
|
impl<S, T> Copy for Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + Clone + 'static,
|
T: Clone + 'static,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<S, T> FnOnce<()> for Resource<S, T>
|
impl<S, T> FnOnce<()> for Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + Clone + 'static,
|
T: Clone + 'static,
|
||||||
{
|
{
|
||||||
type Output = Option<T>;
|
type Output = Option<T>;
|
||||||
|
|
||||||
|
@ -486,8 +476,8 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<S, T> FnMut<()> for Resource<S, T>
|
impl<S, T> FnMut<()> for Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + Clone + 'static,
|
T: Clone + 'static,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||||
self.read()
|
self.read()
|
||||||
|
@ -497,8 +487,8 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<S, T> Fn<()> for Resource<S, T>
|
impl<S, T> Fn<()> for Resource<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + Clone + 'static,
|
T: Clone + 'static,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||||
self.read()
|
self.read()
|
||||||
|
@ -509,7 +499,7 @@ where
|
||||||
pub(crate) struct ResourceState<S, T>
|
pub(crate) struct ResourceState<S, T>
|
||||||
where
|
where
|
||||||
S: 'static,
|
S: 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
value: ReadSignal<Option<T>>,
|
value: ReadSignal<Option<T>>,
|
||||||
|
@ -526,8 +516,8 @@ where
|
||||||
|
|
||||||
impl<S, T> ResourceState<S, T>
|
impl<S, T> ResourceState<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
pub fn read(&self) -> Option<T>
|
pub fn read(&self) -> Option<T>
|
||||||
where
|
where
|
||||||
|
@ -666,8 +656,8 @@ pub(crate) trait SerializableResource {
|
||||||
|
|
||||||
impl<S, T> SerializableResource for ResourceState<S, T>
|
impl<S, T> SerializableResource for ResourceState<S, T>
|
||||||
where
|
where
|
||||||
S: Debug + Clone,
|
S: Clone,
|
||||||
T: Debug + Serializable,
|
T: Serializable,
|
||||||
{
|
{
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
|
@ -686,11 +676,7 @@ pub(crate) trait UnserializableResource {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> UnserializableResource for ResourceState<S, T>
|
impl<S, T> UnserializableResource for ResourceState<S, T> {
|
||||||
where
|
|
||||||
S: Debug,
|
|
||||||
T: Debug,
|
|
||||||
{
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,8 +234,8 @@ impl Runtime {
|
||||||
state: Rc<ResourceState<S, T>>,
|
state: Rc<ResourceState<S, T>>,
|
||||||
) -> ResourceId
|
) -> ResourceId
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
self.resources
|
self.resources
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -247,8 +247,8 @@ impl Runtime {
|
||||||
state: Rc<ResourceState<S, T>>,
|
state: Rc<ResourceState<S, T>>,
|
||||||
) -> ResourceId
|
) -> ResourceId
|
||||||
where
|
where
|
||||||
S: Debug + Clone + 'static,
|
S: Clone + 'static,
|
||||||
T: Debug + Serializable + 'static,
|
T: Serializable + 'static,
|
||||||
{
|
{
|
||||||
self.resources
|
self.resources
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -261,8 +261,8 @@ impl Runtime {
|
||||||
f: impl FnOnce(&ResourceState<S, T>) -> U,
|
f: impl FnOnce(&ResourceState<S, T>) -> U,
|
||||||
) -> U
|
) -> U
|
||||||
where
|
where
|
||||||
S: Debug + 'static,
|
S: 'static,
|
||||||
T: Debug + 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
let resources = self.resources.borrow();
|
let resources = self.resources.borrow();
|
||||||
let res = resources.get(id);
|
let res = resources.get(id);
|
||||||
|
|
|
@ -228,7 +228,7 @@ impl<T> Copy for ReadSignal<T> {}
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> FnOnce<()> for ReadSignal<T>
|
impl<T> FnOnce<()> for ReadSignal<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> FnMut<()> for ReadSignal<T>
|
impl<T> FnMut<()> for ReadSignal<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||||
self.get()
|
self.get()
|
||||||
|
@ -250,7 +250,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> Fn<()> for ReadSignal<T>
|
impl<T> Fn<()> for ReadSignal<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||||
self.get()
|
self.get()
|
||||||
|
@ -734,7 +734,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> FnOnce<()> for RwSignal<T>
|
impl<T> FnOnce<()> for RwSignal<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
|
@ -746,7 +746,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> FnMut<()> for RwSignal<T>
|
impl<T> FnMut<()> for RwSignal<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||||
self.get()
|
self.get()
|
||||||
|
@ -756,7 +756,7 @@ where
|
||||||
#[cfg(not(feature = "stable"))]
|
#[cfg(not(feature = "stable"))]
|
||||||
impl<T> Fn<()> for RwSignal<T>
|
impl<T> Fn<()> for RwSignal<T>
|
||||||
where
|
where
|
||||||
T: Debug + Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||||
self.get()
|
self.get()
|
||||||
|
|
|
@ -9,8 +9,8 @@ description = "Tools to set HTML metadata in the Leptos web framework."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
leptos = { path = "../leptos", version = "0.1.0-alpha", default-features = false }
|
leptos = { path = "../leptos", default-features = false }
|
||||||
tracing = "0.1"
|
typed-builder = "0.11"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
@ -18,6 +18,10 @@ features = ["HtmlLinkElement", "HtmlMetaElement", "HtmlTitleElement"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["csr"]
|
default = ["csr"]
|
||||||
csr = ["leptos/csr", "leptos/tracing"]
|
csr = ["leptos/csr"]
|
||||||
hydrate = ["leptos/hydrate", "leptos/tracing"]
|
hydrate = ["leptos/hydrate"]
|
||||||
ssr = ["leptos/ssr", "leptos/tracing"]
|
ssr = ["leptos/ssr"]
|
||||||
|
stable = ["leptos/stable"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-all-features]
|
||||||
|
denylist = ["stable"]
|
||||||
|
|
|
@ -57,7 +57,8 @@ default = ["csr"]
|
||||||
csr = ["leptos/csr"]
|
csr = ["leptos/csr"]
|
||||||
hydrate = ["leptos/hydrate"]
|
hydrate = ["leptos/hydrate"]
|
||||||
ssr = ["leptos/ssr", "dep:url", "dep:regex"]
|
ssr = ["leptos/ssr", "dep:url", "dep:regex"]
|
||||||
|
stable = ["leptos/stable"]
|
||||||
|
|
||||||
[package.metadata.cargo-all-features]
|
[package.metadata.cargo-all-features]
|
||||||
# No need to test optional dependencies as they are enabled by the ssr feature
|
# No need to test optional dependencies as they are enabled by the ssr feature
|
||||||
denylist = ["url", "regex"]
|
denylist = ["url", "regex", "stable"]
|
||||||
|
|
|
@ -64,7 +64,6 @@ impl std::fmt::Debug for RouterContextInner {
|
||||||
f.debug_struct("RouterContextInner")
|
f.debug_struct("RouterContextInner")
|
||||||
.field("location", &self.location)
|
.field("location", &self.location)
|
||||||
.field("base", &self.base)
|
.field("base", &self.base)
|
||||||
.field("history", &std::any::type_name_of_val(&self.history))
|
|
||||||
.field("cx", &self.cx)
|
.field("cx", &self.cx)
|
||||||
.field("reference", &self.reference)
|
.field("reference", &self.reference)
|
||||||
.field("set_reference", &self.set_reference)
|
.field("set_reference", &self.set_reference)
|
||||||
|
@ -99,13 +98,15 @@ impl RouterContext {
|
||||||
let base = base.unwrap_or_default();
|
let base = base.unwrap_or_default();
|
||||||
let base_path = resolve_path("", base, None);
|
let base_path = resolve_path("", base, None);
|
||||||
|
|
||||||
if let Some(base_path) = &base_path && source.with(|s| s.value.is_empty()) {
|
if let Some(base_path) = &base_path {
|
||||||
history.navigate(&LocationChange {
|
if source.with(|s| s.value.is_empty()) {
|
||||||
value: base_path.to_string(),
|
history.navigate(&LocationChange {
|
||||||
replace: true,
|
value: base_path.to_string(),
|
||||||
scroll: false,
|
replace: true,
|
||||||
state: State(None)
|
scroll: false,
|
||||||
});
|
state: State(None)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the current URL
|
// the current URL
|
||||||
|
|
|
@ -126,17 +126,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto trait NotOption {}
|
cfg_if::cfg_if! {
|
||||||
impl<T> !NotOption for Option<T> {}
|
if #[cfg(not(feature = "stable"))] {
|
||||||
|
auto trait NotOption {}
|
||||||
|
impl<T> !NotOption for Option<T> {}
|
||||||
|
|
||||||
impl<T> IntoParam for T
|
impl<T> IntoParam for T
|
||||||
where
|
where
|
||||||
T: FromStr + NotOption,
|
T: FromStr + NotOption,
|
||||||
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError> {
|
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError> {
|
||||||
let value = value.ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;
|
let value = value.ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;
|
||||||
Self::from_str(value).map_err(|e| ParamsError::Params(Rc::new(e)))
|
Self::from_str(value).map_err(|e| ParamsError::Params(Rc::new(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,10 +136,9 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![feature(auto_traits)]
|
#![cfg_attr(not(feature = "stable"), feature(auto_traits))]
|
||||||
#![feature(let_chains)]
|
#![cfg_attr(not(feature = "stable"), feature(negative_impls))]
|
||||||
#![feature(negative_impls)]
|
#![cfg_attr(not(feature = "stable"), feature(type_name_of_val))]
|
||||||
#![feature(type_name_of_val)]
|
|
||||||
|
|
||||||
mod components;
|
mod components;
|
||||||
mod history;
|
mod history;
|
||||||
|
|
|
@ -80,13 +80,15 @@ impl Matcher {
|
||||||
path.push_str(loc_segment);
|
path.push_str(loc_segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(splat) = &self.splat && !splat.is_empty() {
|
if let Some(splat) = &self.splat {
|
||||||
let value = if len_diff > 0 {
|
if !splat.is_empty() {
|
||||||
loc_segments[self.len..].join("/")
|
let value = if len_diff > 0 {
|
||||||
} else {
|
loc_segments[self.len..].join("/")
|
||||||
"".into()
|
} else {
|
||||||
};
|
"".into()
|
||||||
params.insert(splat.into(), value);
|
};
|
||||||
|
params.insert(splat.into(), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(PathMatch { path, params })
|
Some(PathMatch { path, params })
|
||||||
|
|
Loading…
Reference in a new issue