Merge branch 'master' into intigrate-collect-assets
This commit is contained in:
1 parent
ffb5de91d1
commit
f976e08f71
379 files changed
+1962
-13517
No files matched your search
@@ -14,10 +14,10 @@ anyhow = "1.0.66"
|
||||
dioxus = { workspace = true }
|
||||
dioxus-router-macro = { workspace = true }
|
||||
gloo = { version = "0.8.0", optional = true }
|
||||
log = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
serde_urlencoded = { version = "0.7.1", optional = true }
|
||||
urlencoding = "2.1.3"
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
url = "2.3.1"
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
[discord-url]: https://discord.gg/XgGxMSkvUM
|
||||
|
||||
[Website](https://dioxuslabs.com) |
|
||||
[Guides](https://dioxuslabs.com/docs/0.3/router/) |
|
||||
[Guides](https://dioxuslabs.com/learn/0.4/router/) |
|
||||
[API Docs](https://docs.rs/dioxus-router/latest/dioxus_router) |
|
||||
[Chat](https://discord.gg/XgGxMSkvUM)
|
||||
|
||||
@@ -26,8 +26,6 @@ interface similar to React Router, but takes advantage of types for more
|
||||
expressiveness.
|
||||
|
||||
```rust, no_run
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::prelude::*;
|
||||
use std::str::FromStr;
|
||||
@@ -48,14 +46,14 @@ enum Route {
|
||||
Index {},
|
||||
}
|
||||
|
||||
|
||||
#[component]
|
||||
fn App(cx: Scope) -> Element {
|
||||
render! {
|
||||
Router::<Route> { }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Index(cx: Scope) -> Element {
|
||||
render! {
|
||||
h1 { "Index" }
|
||||
@@ -66,7 +64,7 @@ fn Index(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Blog(cx: Scope) -> Element {
|
||||
render! {
|
||||
h1 { "Blog" }
|
||||
@@ -74,7 +72,7 @@ fn Blog(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn BlogList(cx: Scope) -> Element {
|
||||
render! {
|
||||
h2 { "List of blog posts" }
|
||||
@@ -89,7 +87,7 @@ fn BlogList(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn BlogPost(cx: Scope, blog_id: usize) -> Element {
|
||||
render! {
|
||||
h2 { "Blog Post" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![allow(unused, non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -134,7 +134,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Blog(cx: Scope) -> Element {
|
||||
render! {
|
||||
div {
|
||||
@@ -143,7 +143,7 @@ fn Blog(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Post(cx: Scope, id: usize) -> Element {
|
||||
render! {
|
||||
for _ in 0..*id {
|
||||
@@ -154,7 +154,7 @@ fn Post(cx: Scope, id: usize) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn PostHome(cx: Scope) -> Element {
|
||||
render! {
|
||||
div {
|
||||
@@ -163,7 +163,7 @@ fn PostHome(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Home(cx: Scope) -> Element {
|
||||
render! {
|
||||
div {
|
||||
@@ -189,7 +189,7 @@ enum Route {
|
||||
Home {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn RenderPath(cx: Scope, path: Route) -> Element {
|
||||
let path = path.clone();
|
||||
render! {
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::prelude::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
dioxus_desktop::launch(root);
|
||||
dioxus_desktop::launch(Root);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
dioxus_web::launch(root);
|
||||
}
|
||||
|
||||
fn root(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Router::<Route> {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn UserFrame(cx: Scope, user_id: usize) -> Element {
|
||||
render! {
|
||||
pre {
|
||||
@@ -32,7 +31,7 @@ fn UserFrame(cx: Scope, user_id: usize) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Route1(cx: Scope, user_id: usize, dynamic: usize, query: String, extra: String) -> Element {
|
||||
render! {
|
||||
pre {
|
||||
@@ -50,7 +49,7 @@ fn Route1(cx: Scope, user_id: usize, dynamic: usize, query: String, extra: Strin
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Route2(cx: Scope, user_id: usize) -> Element {
|
||||
render! {
|
||||
pre {
|
||||
@@ -65,7 +64,7 @@ fn Route2(cx: Scope, user_id: usize) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Route3(cx: Scope, dynamic: String) -> Element {
|
||||
let navigator = use_navigator(cx);
|
||||
let current_route = use_route(cx)?;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
@@ -54,7 +52,7 @@ async fn main() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Blog(cx: Scope) -> Element {
|
||||
render! {
|
||||
div {
|
||||
@@ -63,7 +61,7 @@ fn Blog(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Post(cx: Scope, id: usize) -> Element {
|
||||
render! {
|
||||
div {
|
||||
@@ -72,7 +70,7 @@ fn Post(cx: Scope, id: usize) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn PostHome(cx: Scope) -> Element {
|
||||
render! {
|
||||
div {
|
||||
@@ -81,7 +79,7 @@ fn PostHome(cx: Scope) -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Home(cx: Scope) -> Element {
|
||||
render! {
|
||||
div {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use dioxus::prelude::*;
|
||||
use log::error;
|
||||
use tracing::error;
|
||||
|
||||
use crate::utils::use_router_internal::use_router_internal;
|
||||
|
||||
@@ -30,13 +30,14 @@ pub struct HistoryButtonProps<'a> {
|
||||
/// Index {},
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// Router::<Route> {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Index(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// GoBackButton {
|
||||
@@ -52,7 +53,7 @@ pub struct HistoryButtonProps<'a> {
|
||||
/// # r#"<button disabled="true" dioxus-prevent-default="onclick">go back</button>"#
|
||||
/// # );
|
||||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
#[component]
|
||||
pub fn GoBackButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element {
|
||||
let HistoryButtonProps { children } = cx.props;
|
||||
|
||||
@@ -101,13 +102,14 @@ pub fn GoBackButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element {
|
||||
/// Index {},
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// Router::<Route> {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Index(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// GoForwardButton {
|
||||
@@ -123,7 +125,7 @@ pub fn GoBackButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element {
|
||||
/// # r#"<button disabled="true" dioxus-prevent-default="onclick">go forward</button>"#
|
||||
/// # );
|
||||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
#[component]
|
||||
pub fn GoForwardButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element {
|
||||
let HistoryButtonProps { children } = cx.props;
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use log::error;
|
||||
use tracing::error;
|
||||
|
||||
use crate::navigation::NavigationTarget;
|
||||
use crate::prelude::Routable;
|
||||
@@ -133,13 +135,14 @@ impl Debug for LinkProps<'_> {
|
||||
/// Index {},
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// Router::<Route> {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Index(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// render! {
|
||||
|
||||
@@ -29,7 +29,7 @@ use dioxus::prelude::*;
|
||||
/// Index {},
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Index(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// div {
|
||||
@@ -38,7 +38,7 @@ use dioxus::prelude::*;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Wrapper(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// h1 { "App" }
|
||||
@@ -46,7 +46,7 @@ use dioxus::prelude::*;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Child(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// p {
|
||||
@@ -55,6 +55,7 @@ use dioxus::prelude::*;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # #[component]
|
||||
/// # fn App(cx: Scope) -> Element {
|
||||
/// # render! {
|
||||
/// # Router::<Route> {
|
||||
|
||||
@@ -20,9 +20,9 @@ where
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
|
||||
@@ -48,9 +48,9 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
@@ -86,17 +86,20 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # fn Other(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
/// #[route("/")]
|
||||
/// Index {},
|
||||
/// #[route("/other")]
|
||||
/// Other {},
|
||||
/// }
|
||||
/// let mut history = MemoryHistory::<Route>::default();
|
||||
/// assert_eq!(history.can_go_back(), false);
|
||||
///
|
||||
/// history.push(Route::Index {});
|
||||
/// history.push(Route::Other {});
|
||||
/// assert_eq!(history.can_go_back(), true);
|
||||
/// ```
|
||||
#[must_use]
|
||||
@@ -112,9 +115,9 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
@@ -144,9 +147,9 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
@@ -177,9 +180,9 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
@@ -210,9 +213,9 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
@@ -239,9 +242,9 @@ pub trait HistoryProvider<R: Routable> {
|
||||
/// ```rust
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element { todo!() }
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn OtherPage(cx: Scope) -> Element { todo!() }
|
||||
/// #[derive(Clone, Routable, Debug, PartialEq)]
|
||||
/// enum Route {
|
||||
|
||||
@@ -139,7 +139,7 @@ impl<R: Routable> WebHistory<R> {
|
||||
);
|
||||
|
||||
let current_route = myself.current_route();
|
||||
log::trace!("initial route: {:?}", current_route);
|
||||
tracing::trace!("initial route: {:?}", current_route);
|
||||
let current_url = current_route.to_string();
|
||||
let state = myself.create_state(current_route);
|
||||
let _ = replace_state_with_url(&myself.history, &state, Some(¤t_url));
|
||||
@@ -195,14 +195,10 @@ where
|
||||
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||
{
|
||||
fn route_from_location(&self) -> R {
|
||||
R::from_str(
|
||||
&self
|
||||
.window
|
||||
.location()
|
||||
.pathname()
|
||||
.unwrap_or_else(|_| String::from("/")),
|
||||
)
|
||||
.unwrap_or_else(|err| panic!("{}", err))
|
||||
let location = self.window.location();
|
||||
let path = location.pathname().unwrap_or_else(|_| "/".into())
|
||||
+ &location.search().unwrap_or("".into());
|
||||
R::from_str(&path).unwrap_or_else(|err| panic!("{}", err))
|
||||
}
|
||||
|
||||
fn full_path(&self, state: &R) -> String {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use gloo::{events::EventListener, render::AnimationFrame, utils::window};
|
||||
use log::error;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use tracing::error;
|
||||
use url::Url;
|
||||
use web_sys::{History, ScrollRestoration, Window};
|
||||
|
||||
|
||||
@@ -17,13 +17,14 @@ use crate::prelude::{Navigator, RouterContext};
|
||||
/// Dynamic { id: usize },
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// Router::<Route> {}
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Index(cx: Scope) -> Element {
|
||||
/// let navigator = use_navigator(&cx);
|
||||
///
|
||||
@@ -35,7 +36,7 @@ use crate::prelude::{Navigator, RouterContext};
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Dynamic(cx: Scope, id: usize) -> Element {
|
||||
/// render! {
|
||||
/// p {
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::utils::use_router_internal::use_router_internal;
|
||||
/// - Otherwise the current route.
|
||||
///
|
||||
/// # Panic
|
||||
/// - When the calling component is not nested within a [`Link`] component durring a debug build.
|
||||
/// - When the calling component is not nested within a [`Link`] component during a debug build.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
@@ -25,6 +25,7 @@ use crate::utils::use_router_internal::use_router_internal;
|
||||
/// Index {},
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// h1 { "App" }
|
||||
@@ -32,9 +33,9 @@ use crate::utils::use_router_internal::use_router_internal;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Index(cx: Scope) -> Element {
|
||||
/// let path = use_route(&cx).unwrap();
|
||||
/// let path: Route = use_route(&cx).unwrap();
|
||||
/// render! {
|
||||
/// h2 { "Current Path" }
|
||||
/// p { "{path}" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Exentsions to the incremental renderer to support pre-caching static routes.
|
||||
//! Extensions to the incremental renderer to support pre-caching static routes.
|
||||
use core::pin::Pin;
|
||||
use std::future::Future;
|
||||
use std::str::FromStr;
|
||||
@@ -59,8 +59,8 @@ where
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("@ route: {}", full_path);
|
||||
log::error!("Error pre-caching static route: {}", e);
|
||||
tracing::info!("@ route: {}", full_path);
|
||||
tracing::error!("Error pre-caching static route: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ where
|
||||
<Rt as FromStr>::Err: std::fmt::Display,
|
||||
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||
{
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn RenderPath<R>(cx: Scope, path: R) -> Element
|
||||
where
|
||||
R: Routable,
|
||||
|
||||
@@ -82,3 +82,8 @@ pub mod prelude {
|
||||
mod utils {
|
||||
pub(crate) mod use_router_internal;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod exports {
|
||||
pub use urlencoding;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ pub enum NavigationTarget<R> {
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus_router::navigation::NavigationTarget;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element {
|
||||
/// # todo!()
|
||||
/// # }
|
||||
@@ -38,7 +38,7 @@ pub enum NavigationTarget<R> {
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus_router::navigation::NavigationTarget;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element {
|
||||
/// # todo!()
|
||||
/// # }
|
||||
|
||||
+126
-71
@@ -3,16 +3,18 @@
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use std::iter::FlatMap;
|
||||
use std::slice::Iter;
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
/// An error that occurs when parsing a route
|
||||
/// An error that occurs when parsing a route.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RouteParseError<E: std::fmt::Display> {
|
||||
/// The attempted routes that failed to match
|
||||
pub struct RouteParseError<E: Display> {
|
||||
/// The attempted routes that failed to match.
|
||||
pub attempted_routes: Vec<E>,
|
||||
}
|
||||
|
||||
impl<E: std::fmt::Display> std::fmt::Display for RouteParseError<E> {
|
||||
impl<E: Display> Display for RouteParseError<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Route did not match:\nAttempted Matches:\n")?;
|
||||
for (i, route) in self.attempted_routes.iter().enumerate() {
|
||||
@@ -26,63 +28,96 @@ impl<E: std::fmt::Display> std::fmt::Display for RouteParseError<E> {
|
||||
///
|
||||
/// This trait needs to be implemented if you want to turn a query string into a struct.
|
||||
///
|
||||
/// A working example can be found in the `examples` folder in the root package under `query_segments_demo`
|
||||
/// A working example can be found in the `examples` folder in the root package under `query_segments_demo`.
|
||||
pub trait FromQuery {
|
||||
/// Create an instance of `Self` from a query string
|
||||
/// Create an instance of `Self` from a query string.
|
||||
fn from_query(query: &str) -> Self;
|
||||
}
|
||||
|
||||
impl<T: for<'a> From<&'a str>> FromQuery for T {
|
||||
fn from_query(query: &str) -> Self {
|
||||
T::from(query)
|
||||
T::from(&*urlencoding::decode(query).expect("Failed to decode url encoding"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be created from a route segment
|
||||
/// Something that can be created from a route segment.
|
||||
pub trait FromRouteSegment: Sized {
|
||||
/// The error that can occur when parsing a route segment
|
||||
/// The error that can occur when parsing a route segment.
|
||||
type Err;
|
||||
|
||||
/// Create an instance of `Self` from a route segment
|
||||
/// Create an instance of `Self` from a route segment.
|
||||
fn from_route_segment(route: &str) -> Result<Self, Self::Err>;
|
||||
}
|
||||
|
||||
impl<T: FromStr> FromRouteSegment for T
|
||||
where
|
||||
<T as FromStr>::Err: std::fmt::Display,
|
||||
<T as FromStr>::Err: Display,
|
||||
{
|
||||
type Err = <T as FromStr>::Err;
|
||||
|
||||
fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
|
||||
T::from_str(route)
|
||||
match urlencoding::decode(route) {
|
||||
Ok(segment) => T::from_str(&segment),
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to decode url encoding: {}", err);
|
||||
T::from_str(route)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be converted to route segments
|
||||
pub trait ToRouteSegments {
|
||||
/// Display the route segments
|
||||
fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
|
||||
#[test]
|
||||
fn full_circle() {
|
||||
let route = "testing 1234 hello world";
|
||||
assert_eq!(String::from_route_segment(route).unwrap(), route);
|
||||
}
|
||||
|
||||
impl<I, T: std::fmt::Display> ToRouteSegments for I
|
||||
/// Something that can be converted to route segments.
|
||||
pub trait ToRouteSegments {
|
||||
/// Display the route segments.
|
||||
fn display_route_segments(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
|
||||
}
|
||||
|
||||
impl<I, T: Display> ToRouteSegments for I
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn display_route_segments(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for segment in self {
|
||||
write!(f, "/")?;
|
||||
write!(f, "{}", segment)?;
|
||||
let segment = segment.to_string();
|
||||
match urlencoding::decode(&segment) {
|
||||
Ok(segment) => write!(f, "{}", segment)?,
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to decode url encoding: {}", err);
|
||||
write!(f, "{}", segment)?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be created from route segments
|
||||
#[test]
|
||||
fn to_route_segments() {
|
||||
struct DisplaysRoute;
|
||||
|
||||
impl Display for DisplaysRoute {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let segments = vec!["hello", "world"];
|
||||
segments.display_route_segments(f)
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(DisplaysRoute.to_string(), "/hello/world");
|
||||
}
|
||||
|
||||
/// Something that can be created from route segments.
|
||||
pub trait FromRouteSegments: Sized {
|
||||
/// The error that can occur when parsing route segments
|
||||
/// The error that can occur when parsing route segments.
|
||||
type Err;
|
||||
|
||||
/// Create an instance of `Self` from route segments
|
||||
/// Create an instance of `Self` from route segments.
|
||||
fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
|
||||
}
|
||||
|
||||
@@ -97,29 +132,54 @@ impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
|
||||
}
|
||||
}
|
||||
|
||||
/// A flattened version of [`Routable::SITE_MAP`].
|
||||
/// This essentially represents a `Vec<Vec<SegmentType>>`, which you can collect it into.
|
||||
type SiteMapFlattened<'a> = FlatMap<
|
||||
Iter<'a, SiteMapSegment>,
|
||||
Vec<Vec<SegmentType>>,
|
||||
fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
|
||||
>;
|
||||
|
||||
fn seg_strs_to_route<T>(segs_maybe: &Option<Vec<&str>>) -> Option<T>
|
||||
where
|
||||
T: Routable,
|
||||
{
|
||||
if let Some(str) = seg_strs_to_str(segs_maybe) {
|
||||
T::from_str(&str).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn seg_strs_to_str(segs_maybe: &Option<Vec<&str>>) -> Option<String> {
|
||||
segs_maybe
|
||||
.as_ref()
|
||||
.map(|segs| String::from('/') + &segs.join("/"))
|
||||
}
|
||||
|
||||
/// Something that can be:
|
||||
/// 1. Converted from a route
|
||||
/// 2. Converted to a route
|
||||
/// 3. Rendered as a component
|
||||
/// 1. Converted from a route.
|
||||
/// 2. Converted to a route.
|
||||
/// 3. Rendered as a component.
|
||||
///
|
||||
/// This trait can be derived using the `#[derive(Routable)]` macro
|
||||
pub trait Routable: std::fmt::Display + std::str::FromStr + Clone + 'static {
|
||||
/// The error that can occur when parsing a route
|
||||
/// This trait can be derived using the `#[derive(Routable)]` macro.
|
||||
pub trait Routable: FromStr + Display + Clone + 'static {
|
||||
/// The error that can occur when parsing a route.
|
||||
const SITE_MAP: &'static [SiteMapSegment];
|
||||
|
||||
/// Render the route at the given level
|
||||
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a>;
|
||||
|
||||
/// Checks if this route is a child of the given route
|
||||
/// Checks if this route is a child of the given route.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use dioxus_router::prelude::*;
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Home(cx: Scope) -> Element { todo!() }
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn About(cx: Scope) -> Element { todo!() }
|
||||
///
|
||||
/// #[derive(Routable, Clone, PartialEq, Debug)]
|
||||
@@ -152,23 +212,23 @@ pub trait Routable: std::fmt::Display + std::str::FromStr + Clone + 'static {
|
||||
true
|
||||
}
|
||||
|
||||
/// Get the parent route of this route
|
||||
/// Get the parent route of this route.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use dioxus_router::prelude::*;
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Home(cx: Scope) -> Element { todo!() }
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn About(cx: Scope) -> Element { todo!() }
|
||||
///
|
||||
/// #[derive(Routable, Clone, PartialEq, Debug)]
|
||||
/// enum Route {
|
||||
/// #[route("/")]
|
||||
/// #[route("/home")]
|
||||
/// Home {},
|
||||
/// #[route("/about")]
|
||||
/// #[route("/home/about")]
|
||||
/// About {},
|
||||
/// }
|
||||
///
|
||||
@@ -192,72 +252,67 @@ pub trait Routable: std::fmt::Display + std::str::FromStr + Clone + 'static {
|
||||
Self::from_str(&new_route).ok()
|
||||
}
|
||||
|
||||
/// Gets a list of all static routes
|
||||
/// Returns a flattened version of [`Self::SITE_MAP`].
|
||||
fn flatten_site_map<'a>() -> SiteMapFlattened<'a> {
|
||||
Self::SITE_MAP.iter().flat_map(SiteMapSegment::flatten)
|
||||
}
|
||||
|
||||
/// Gets a list of all the static routes.
|
||||
/// Example static route: `#[route("/static/route")]`
|
||||
fn static_routes() -> Vec<Self> {
|
||||
Self::SITE_MAP
|
||||
.iter()
|
||||
.flat_map(|segment| segment.flatten())
|
||||
Self::flatten_site_map()
|
||||
.filter_map(|route| {
|
||||
if route
|
||||
let route_if_static = &route
|
||||
.iter()
|
||||
.all(|segment| matches!(segment, SegmentType::Static(_)))
|
||||
{
|
||||
Self::from_str(
|
||||
&route
|
||||
.iter()
|
||||
.map(|segment| match segment {
|
||||
SegmentType::Static(s) => s.to_string(),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("/"),
|
||||
)
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.map(|segment| match segment {
|
||||
SegmentType::Static(s) => Some(*s),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Option<Vec<_>>>();
|
||||
|
||||
seg_strs_to_route(route_if_static)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
trait RoutableFactory {
|
||||
type Err: std::fmt::Display;
|
||||
type Err: Display;
|
||||
type Routable: Routable + FromStr<Err = Self::Err>;
|
||||
}
|
||||
|
||||
impl<R: Routable + FromStr> RoutableFactory for R
|
||||
where
|
||||
<R as FromStr>::Err: std::fmt::Display,
|
||||
<R as FromStr>::Err: Display,
|
||||
{
|
||||
type Err = <R as FromStr>::Err;
|
||||
type Routable = R;
|
||||
}
|
||||
|
||||
trait RouteRenderable: std::fmt::Display + 'static {
|
||||
trait RouteRenderable: Display + 'static {
|
||||
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a>;
|
||||
}
|
||||
|
||||
impl<R: Routable> RouteRenderable for R
|
||||
where
|
||||
<R as FromStr>::Err: std::fmt::Display,
|
||||
<R as FromStr>::Err: Display,
|
||||
{
|
||||
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a> {
|
||||
self.render(cx, level)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type erased map of the site structurens
|
||||
/// A type erased map of the site structure.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SiteMapSegment {
|
||||
/// The type of the route segment
|
||||
/// The type of the route segment.
|
||||
pub segment_type: SegmentType,
|
||||
/// The children of the route segment
|
||||
/// The children of the route segment.
|
||||
pub children: &'static [SiteMapSegment],
|
||||
}
|
||||
|
||||
impl SiteMapSegment {
|
||||
/// Take a map of the site structure and flatten it into a vector of routes
|
||||
/// Take a map of the site structure and flatten it into a vector of routes.
|
||||
pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
|
||||
let mut routes = Vec::new();
|
||||
self.flatten_inner(&mut routes, Vec::new());
|
||||
@@ -277,16 +332,16 @@ impl SiteMapSegment {
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of a route segment
|
||||
/// The type of a route segment.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SegmentType {
|
||||
/// A static route segment
|
||||
/// A static route segment.
|
||||
Static(&'static str),
|
||||
/// A dynamic route segment
|
||||
/// A dynamic route segment.
|
||||
Dynamic(&'static str),
|
||||
/// A catch all route segment
|
||||
/// A catch all route segment.
|
||||
CatchAll(&'static str),
|
||||
/// A child router
|
||||
/// A child router.
|
||||
Child,
|
||||
}
|
||||
|
||||
@@ -296,7 +351,7 @@ impl Display for SegmentType {
|
||||
SegmentType::Static(s) => write!(f, "/{}", s),
|
||||
SegmentType::Child => Ok(()),
|
||||
SegmentType::Dynamic(s) => write!(f, "/:{}", s),
|
||||
SegmentType::CatchAll(s) => write!(f, "/:...{}", s),
|
||||
SegmentType::CatchAll(s) => write!(f, "/:..{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ use crate::prelude::*;
|
||||
/// ```rust,no_run
|
||||
/// # use dioxus_router::prelude::*;
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # #[inline_props]
|
||||
/// # #[component]
|
||||
/// # fn Index(cx: Scope) -> Element {
|
||||
/// # todo!()
|
||||
/// # }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::prelude::*;
|
||||
use std::str::FromStr;
|
||||
@@ -27,6 +26,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App<R: Routable>(cx: Scope<AppProps<R>>) -> Element
|
||||
where
|
||||
<R as FromStr>::Err: std::fmt::Display,
|
||||
@@ -50,12 +50,12 @@ fn href_internal() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -88,12 +88,12 @@ fn href_external() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -126,12 +126,12 @@ fn with_class() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -163,7 +163,7 @@ fn with_active_class_active() {
|
||||
Root {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -198,12 +198,12 @@ fn with_active_class_inactive() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -238,12 +238,12 @@ fn with_id() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -277,12 +277,12 @@ fn with_new_tab() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -314,7 +314,7 @@ fn with_new_tab_external() {
|
||||
Root {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
@@ -348,12 +348,12 @@ fn with_rel() {
|
||||
Test {},
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Test(cx: Scope) -> Element {
|
||||
#[component]
|
||||
fn Test(_cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Root(cx: Scope) -> Element {
|
||||
render! {
|
||||
Link {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![allow(non_snake_case, unused)]
|
||||
#![allow(unused)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_router::prelude::*;
|
||||
@@ -34,6 +34,7 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App(cx: Scope<AppProps>) -> Element {
|
||||
render! {
|
||||
h1 { "App" }
|
||||
@@ -46,14 +47,14 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn RootIndex(cx: Scope) -> Element {
|
||||
render! {
|
||||
h2 { "Root Index" }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Fixed(cx: Scope) -> Element {
|
||||
render! {
|
||||
h2 { "Fixed" }
|
||||
@@ -61,21 +62,21 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn FixedIndex(cx: Scope) -> Element {
|
||||
render! {
|
||||
h3 { "Fixed - Index" }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn FixedFixed(cx: Scope) -> Element {
|
||||
render! {
|
||||
h3 { "Fixed - Fixed"}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn Parameter(cx: Scope, id: u8) -> Element {
|
||||
render! {
|
||||
h2 { "Parameter {id}" }
|
||||
@@ -83,14 +84,14 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn ParameterIndex(cx: Scope, id: u8) -> Element {
|
||||
render! {
|
||||
h3 { "Parameter - Index" }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[component]
|
||||
fn ParameterFixed(cx: Scope, id: u8) -> Element {
|
||||
render! {
|
||||
h3 { "Parameter - Fixed" }
|
||||
|
||||
Reference in new issue
Block a user