diff --git a/docs/guide/src/en/interactivity/router.md b/docs/guide/src/en/interactivity/router.md index c5f8c7c36..b4f20eb78 100644 --- a/docs/guide/src/en/interactivity/router.md +++ b/docs/guide/src/en/interactivity/router.md @@ -26,7 +26,7 @@ Unlike other routers in the Rust ecosystem, our router is built declaratively. T ```rust, no_run rsx!{ // All of our routes will be rendered inside this Router component - Router { + Router:: { // if the current location is "/home", render the Home component Route { to: "/home", Home {} } // if the current location is "/blog", render the Blog component @@ -43,7 +43,7 @@ We can fix this one of two ways: ```rust, no_run rsx!{ - Router { + Router:: { Route { to: "/home", Home {} } Route { to: "/blog", Blog {} } // if the current location doesn't match any of the above routes, render the NotFound component @@ -56,7 +56,7 @@ rsx!{ ```rust, no_run rsx!{ - Router { + Router:: { Route { to: "/home", Home {} } Route { to: "/blog", Blog {} } // if the current location doesn't match any of the above routes, redirect to "/home" diff --git a/docs/guide/src/pt-br/interactivity/router.md b/docs/guide/src/pt-br/interactivity/router.md index 398546c0c..282508df9 100644 --- a/docs/guide/src/pt-br/interactivity/router.md +++ b/docs/guide/src/pt-br/interactivity/router.md @@ -28,7 +28,7 @@ Ao contrário de outros roteadores no ecossistema Rust, nosso roteador é constr ```rust, no_run rsx!{ - Router { + Router:: { Route { to: "/home", Home {} } Route { to: "/blog", Blog {} } } @@ -43,7 +43,7 @@ Podemos corrigir isso de duas maneiras: ```rust, no_run rsx!{ - Router { + Router:: { Route { to: "/home", Home {} } Route { to: "/blog", Blog {} } Route { to: "", NotFound {} } @@ -55,7 +55,7 @@ rsx!{ ```rust, no_run rsx!{ - Router { + Router:: { Route { to: "/home", Home {} } Route { to: "/blog", Blog {} } Redirect { from: "", to: "/home" } diff --git a/docs/posts/release-0-2-0.md b/docs/posts/release-0-2-0.md index 7901ee8b0..82d87a730 100644 --- a/docs/posts/release-0-2-0.md +++ b/docs/posts/release-0-2-0.md @@ -72,7 +72,7 @@ Apps with routers are _really_ simple now. It's easy to compose the "Router", a ```rust, no_run fn app(cx: Scope) -> Element { cx.render(rsx! { - Router { + Router:: { onchange: move |_| log::info!("Route changed!"), ul { Link { to: "/", li { "Go home!" } } diff --git a/docs/router/examples/catch_all.rs b/docs/router/examples/catch_all.rs index 38dfa11f1..0e989b7a6 100644 --- a/docs/router/examples/catch_all.rs +++ b/docs/router/examples/catch_all.rs @@ -17,7 +17,7 @@ enum Route { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } // ANCHOR_END: app diff --git a/docs/router/examples/dynamic_route.rs b/docs/router/examples/dynamic_route.rs index 0f0ace6a9..f8a103e7c 100644 --- a/docs/router/examples/dynamic_route.rs +++ b/docs/router/examples/dynamic_route.rs @@ -35,7 +35,7 @@ enum Route { fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } @@ -48,7 +48,7 @@ fn NavBar(cx: Scope) -> Element { li { Link { to: Route::BlogList {}, "Blog" } } } } - Outlet {} + Outlet:: {} } } @@ -64,7 +64,7 @@ fn Home(cx: Scope) -> Element { fn Blog(cx: Scope) -> Element { render! { h1 { "Blog" } - Outlet {} + Outlet:: {} } } // ANCHOR_END: blog diff --git a/docs/router/examples/external_link.rs b/docs/router/examples/external_link.rs index a36f41f76..ed855e2d3 100644 --- a/docs/router/examples/external_link.rs +++ b/docs/router/examples/external_link.rs @@ -20,7 +20,7 @@ fn main() {} fn GoToDioxus(cx: Scope) -> Element { render! { Link { - to: NavigationTarget::External("https://dioxuslabs.com".into()), + to: "https://dioxuslabs.com", "ExternalTarget target" } } diff --git a/docs/router/examples/first_route.rs b/docs/router/examples/first_route.rs index d13bef93d..5afa513f9 100644 --- a/docs/router/examples/first_route.rs +++ b/docs/router/examples/first_route.rs @@ -19,7 +19,7 @@ enum Route { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } // ANCHOR_END: app diff --git a/docs/router/examples/full_example.rs b/docs/router/examples/full_example.rs index 1a561292e..11465c38f 100644 --- a/docs/router/examples/full_example.rs +++ b/docs/router/examples/full_example.rs @@ -39,7 +39,7 @@ enum Route { fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } @@ -52,7 +52,7 @@ fn NavBar(cx: Scope) -> Element { li { Link { to: Route::BlogList {}, "Blog" } } } } - Outlet {} + Outlet:: {} } } @@ -67,7 +67,7 @@ fn Home(cx: Scope) -> Element { fn Blog(cx: Scope) -> Element { render! { h1 { "Blog" } - Outlet {} + Outlet:: {} } } diff --git a/docs/router/examples/history_provider.rs b/docs/router/examples/history_provider.rs index 090019ad0..57ec27732 100644 --- a/docs/router/examples/history_provider.rs +++ b/docs/router/examples/history_provider.rs @@ -12,7 +12,7 @@ enum Route { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router { + Router:: { config: || RouterConfig::default().history(WebHistory::default()) } } diff --git a/docs/router/examples/links.rs b/docs/router/examples/links.rs index cf9c41930..356e9c0a7 100644 --- a/docs/router/examples/links.rs +++ b/docs/router/examples/links.rs @@ -32,7 +32,7 @@ fn NavBar(cx: Scope) -> Element { } } } - Outlet {} + Outlet:: {} } } // ANCHOR_END: nav @@ -41,7 +41,7 @@ fn NavBar(cx: Scope) -> Element { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } // ANCHOR_END: app diff --git a/docs/router/examples/navigator.rs b/docs/router/examples/navigator.rs index 4c6249366..9f32b7c12 100644 --- a/docs/router/examples/navigator.rs +++ b/docs/router/examples/navigator.rs @@ -14,7 +14,7 @@ enum Route { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } diff --git a/docs/router/examples/nested_routes.rs b/docs/router/examples/nested_routes.rs index 7462d8e74..63e080016 100644 --- a/docs/router/examples/nested_routes.rs +++ b/docs/router/examples/nested_routes.rs @@ -26,7 +26,7 @@ fn NavBar(cx: Scope) -> Element { } } // The Outlet component will render child routes (In this case just the Home component) inside the Outlet component - Outlet {} + Outlet:: {} } } // ANCHOR_END: nav @@ -35,7 +35,7 @@ fn NavBar(cx: Scope) -> Element { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } // ANCHOR_END: app diff --git a/docs/router/examples/outlet.rs b/docs/router/examples/outlet.rs index b28a61f66..5c7ee8dbf 100644 --- a/docs/router/examples/outlet.rs +++ b/docs/router/examples/outlet.rs @@ -16,7 +16,7 @@ fn Wrapper(cx: Scope) -> Element { render! { header { "header" } // The index route will be rendered here - Outlet { } + Outlet:: { } footer { "footer" } } } @@ -31,7 +31,7 @@ fn Index(cx: Scope) -> Element { fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } diff --git a/docs/router/examples/router_cfg.rs b/docs/router/examples/router_cfg.rs index 9b29af75c..5fd0402af 100644 --- a/docs/router/examples/router_cfg.rs +++ b/docs/router/examples/router_cfg.rs @@ -18,7 +18,7 @@ enum Route { #[inline_props] fn App(cx: Scope) -> Element { render! { - Router { + Router:: { config: || RouterConfig::default().history(WebHistory::default()) } } diff --git a/docs/router/examples/routing_update.rs b/docs/router/examples/routing_update.rs index 35097dbaf..a7f83d633 100644 --- a/docs/router/examples/routing_update.rs +++ b/docs/router/examples/routing_update.rs @@ -27,7 +27,7 @@ fn Index(cx: Scope) -> Element { fn app(cx: Scope) -> Element { render! { - Router { + Router:: { config: || RouterConfig::default().on_update(|state|{ (state.current() == Route::Index {}).then_some( NavigationTarget::Internal(Route::Home {}) diff --git a/examples/crm.rs b/examples/crm.rs index 5c89448bc..f3da8ab7a 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -46,7 +46,7 @@ fn App(cx: Scope) -> Element { h1 { "Dioxus CRM Example" } - Router {} + Router:: {} } } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 7163ab160..b8e3d6425 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -18,7 +18,7 @@ fn main() { fn app(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } @@ -40,7 +40,7 @@ enum Route { fn Footer(cx: Scope) -> Element { render! { div { - Outlet { } + Outlet:: { } p { "----" diff --git a/examples/link.rs b/examples/link.rs index 01bd38a5b..c66b1abd1 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -23,7 +23,7 @@ fn app(cx: Scope) -> Element { } } div { - Router {} + Router:: {} } )) } @@ -46,7 +46,7 @@ fn Header(cx: Scope) -> Element { li { Link { to: Route::Home {}, "home" } } li { Link { to: Route::Settings {}, "settings" } } } - Outlet {} + Outlet:: {} } } diff --git a/examples/router.rs b/examples/router.rs index 1a561292e..11465c38f 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -39,7 +39,7 @@ enum Route { fn App(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } @@ -52,7 +52,7 @@ fn NavBar(cx: Scope) -> Element { li { Link { to: Route::BlogList {}, "Blog" } } } } - Outlet {} + Outlet:: {} } } @@ -67,7 +67,7 @@ fn Home(cx: Scope) -> Element { fn Blog(cx: Scope) -> Element { render! { h1 { "Blog" } - Outlet {} + Outlet:: {} } } diff --git a/examples/simple_desktop.rs b/examples/simple_desktop.rs index 08d402e39..374b58a4e 100644 --- a/examples/simple_desktop.rs +++ b/examples/simple_desktop.rs @@ -14,7 +14,7 @@ fn main() { fn app(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } @@ -47,7 +47,7 @@ fn NavBar(cx: Scope) -> Element { li { Link { to: Route::BlogPost { post: "bill".into() }, "bills' blog" } } li { Link { to: Route::BlogPost { post: "james".into() }, "james amazing' blog" } } } - Outlet {} + Outlet:: {} } } diff --git a/packages/fullstack/src/router.rs b/packages/fullstack/src/router.rs index 97bcdf5d1..8eb7ef536 100644 --- a/packages/fullstack/src/router.rs +++ b/packages/fullstack/src/router.rs @@ -16,7 +16,7 @@ where let cfg = *cx.props; render! { - dioxus_router::prelude::GenericRouter:: { + dioxus_router::prelude::Router:: { config: move || { RouterConfig::default() .failure_external_navigation(cfg.failure_external_navigation) @@ -54,7 +54,7 @@ where R: dioxus_router::prelude::Routable, ::Err: std::fmt::Display, { - dioxus_router::prelude::FailureExternalNavigation:: + dioxus_router::prelude::FailureExternalNavigation } /// The configeration for the router @@ -96,7 +96,7 @@ where { fn default() -> Self { Self { - failure_external_navigation: dioxus_router::prelude::FailureExternalNavigation::, + failure_external_navigation: dioxus_router::prelude::FailureExternalNavigation, scroll_restoration: true, phantom: std::marker::PhantomData, } diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index 708aa6891..771c9640f 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -211,38 +211,8 @@ pub fn routable(input: TokenStream) -> TokenStream { let parse_impl = route_enum.parse_impl(); let display_impl = route_enum.impl_display(); let routable_impl = route_enum.routable_impl(); - let name = &route_enum.name; - let vis = &route_enum.vis; quote! { - #vis fn Outlet(cx: dioxus::prelude::Scope) -> dioxus::prelude::Element { - dioxus_router::prelude::GenericOutlet::<#name>(cx) - } - - #vis fn Router(cx: dioxus::prelude::Scope>) -> dioxus::prelude::Element { - dioxus_router::prelude::GenericRouter(cx) - } - - #vis fn Link<'a>(cx: dioxus::prelude::Scope<'a, dioxus_router::prelude::GenericLinkProps<'a, #name>>) -> dioxus::prelude::Element<'a> { - dioxus_router::prelude::GenericLink(cx) - } - - #vis fn GoBackButton<'a>(cx: dioxus::prelude::Scope<'a, dioxus_router::prelude::GenericHistoryButtonProps<'a>>) -> dioxus::prelude::Element<'a> { - dioxus_router::prelude::GenericGoBackButton::<#name>(cx) - } - - #vis fn GoForwardButton<'a>(cx: dioxus::prelude::Scope<'a, dioxus_router::prelude::GenericHistoryButtonProps<'a>>) -> dioxus::prelude::Element<'a> { - dioxus_router::prelude::GenericGoForwardButton::<#name>(cx) - } - - #vis fn use_route(cx: &dioxus::prelude::ScopeState) -> Option<#name> { - dioxus_router::prelude::use_generic_route(cx) - } - - #vis fn use_navigator(cx: &dioxus::prelude::ScopeState) -> &dioxus_router::prelude::GenericNavigator<#name> { - dioxus_router::prelude::use_generic_navigator(cx) - } - #error_type #display_impl @@ -255,7 +225,6 @@ pub fn routable(input: TokenStream) -> TokenStream { } struct RouteEnum { - vis: syn::Visibility, name: Ident, redirects: Vec, routes: Vec, @@ -267,7 +236,6 @@ struct RouteEnum { impl RouteEnum { fn parse(data: syn::ItemEnum) -> syn::Result { let name = &data.ident; - let vis = &data.vis; let mut site_map = Vec::new(); let mut site_map_stack: Vec> = Vec::new(); @@ -457,7 +425,6 @@ impl RouteEnum { } let myself = Self { - vis: vis.clone(), name: name.clone(), routes, redirects, diff --git a/packages/router/README.md b/packages/router/README.md index 88f0fde5b..db6cfdf4f 100644 --- a/packages/router/README.md +++ b/packages/router/README.md @@ -51,7 +51,7 @@ enum Route { fn App(cx: Scope) -> Element { render! { - Router { } + Router:: { } } } @@ -70,7 +70,7 @@ fn Index(cx: Scope) -> Element { fn Blog(cx: Scope) -> Element { render! { h1 { "Blog" } - Outlet { } + Outlet:: { } } } diff --git a/packages/router/benches/incremental.rs b/packages/router/benches/incremental.rs index 0694e3636..0c2697a80 100644 --- a/packages/router/benches/incremental.rs +++ b/packages/router/benches/incremental.rs @@ -193,7 +193,7 @@ enum Route { fn RenderPath(cx: Scope, path: Route) -> Element { let path = path.clone(); render! { - Router { + Router:: { config: || RouterConfig::default().history(MemoryHistory::with_initial_path(path)) } } diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index acb5bffce..ac408bb72 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -14,7 +14,7 @@ fn main() { fn root(cx: Scope) -> Element { render! { - Router {} + Router:: {} } } @@ -27,7 +27,7 @@ fn UserFrame(cx: Scope, user_id: usize) -> Element { div { background_color: "rgba(0,0,0,50%)", "children:" - Outlet {} + Outlet:: {} } } } @@ -90,7 +90,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element { "hello world link" } button { - onclick: move |_| { navigator.push(NavigationTarget::External("https://www.google.com".to_string())); }, + onclick: move |_| { navigator.push(NavigationTarget::::External("https://www.google.com".to_string())); }, "google link" } p { "Site Map" } diff --git a/packages/router/src/components/default_errors.rs b/packages/router/src/components/default_errors.rs index 16a680803..704c6b972 100644 --- a/packages/router/src/components/default_errors.rs +++ b/packages/router/src/components/default_errors.rs @@ -1,10 +1,10 @@ -use crate::{hooks::use_generic_router, routable::Routable}; +use crate::hooks::use_router; use dioxus::prelude::*; /// The default component to render when an external navigation fails. #[allow(non_snake_case)] -pub fn FailureExternalNavigation(cx: Scope) -> Element { - let router = use_generic_router::(cx); +pub fn FailureExternalNavigation(cx: Scope) -> Element { + let router = use_router(cx); render! { h1 { "External Navigation Failure!" } diff --git a/packages/router/src/components/history_buttons.rs b/packages/router/src/components/history_buttons.rs index a09bc66b6..6bd23d860 100644 --- a/packages/router/src/components/history_buttons.rs +++ b/packages/router/src/components/history_buttons.rs @@ -1,23 +1,23 @@ use dioxus::prelude::*; use log::error; -use crate::{prelude::*, utils::use_router_internal::use_router_internal}; +use crate::utils::use_router_internal::use_router_internal; -/// The properties for a [`GenericGoBackButton`] or a [`GenericGoForwardButton`]. +/// The properties for a [`GoBackButton`] or a [`GoForwardButton`]. #[derive(Debug, Props)] -pub struct GenericHistoryButtonProps<'a> { +pub struct HistoryButtonProps<'a> { /// The children to render within the generated HTML button tag. pub children: Element<'a>, } /// A button to go back through the navigation history. Similar to a browsers back button. /// -/// Only works as descendant of a [`GenericRouter`] component, otherwise it will be inactive. +/// Only works as descendant of a [`Link`] component, otherwise it will be inactive. /// /// The button will disable itself if it is known that no prior history is available. /// /// # Panic -/// - When the [`GenericGoBackButton`] is not nested within a [`GenericRouter`] component +/// - When the [`GoBackButton`] is not nested within a [`Link`] component /// hook, but only in debug builds. /// /// # Example @@ -32,7 +32,7 @@ pub struct GenericHistoryButtonProps<'a> { /// /// fn App(cx: Scope) -> Element { /// render! { -/// Router {} +/// Router:: {} /// } /// } /// @@ -53,13 +53,11 @@ pub struct GenericHistoryButtonProps<'a> { /// # ); /// ``` #[allow(non_snake_case)] -pub fn GenericGoBackButton<'a, R: Routable>( - cx: Scope<'a, GenericHistoryButtonProps<'a>>, -) -> Element { - let GenericHistoryButtonProps { children } = cx.props; +pub fn GoBackButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element { + let HistoryButtonProps { children } = cx.props; // hook up to router - let router = match use_router_internal::(cx) { + let router = match use_router_internal(cx) { Some(r) => r, #[allow(unreachable_code)] None => { @@ -85,12 +83,12 @@ pub fn GenericGoBackButton<'a, R: Routable>( /// A button to go forward through the navigation history. Similar to a browsers forward button. /// -/// Only works as descendant of a [`GenericRouter`] component, otherwise it will be inactive. +/// Only works as descendant of a [`Link`] component, otherwise it will be inactive. /// /// The button will disable itself if it is known that no later history is available. /// /// # Panic -/// - When the [`GenericGoForwardButton`] is not nested within a [`GenericRouter`] component +/// - When the [`GoForwardButton`] is not nested within a [`Link`] component /// hook, but only in debug builds. /// /// # Example @@ -105,7 +103,7 @@ pub fn GenericGoBackButton<'a, R: Routable>( /// /// fn App(cx: Scope) -> Element { /// render! { -/// Router {} +/// Router:: {} /// } /// } /// @@ -126,13 +124,11 @@ pub fn GenericGoBackButton<'a, R: Routable>( /// # ); /// ``` #[allow(non_snake_case)] -pub fn GenericGoForwardButton<'a, R: Routable>( - cx: Scope<'a, GenericHistoryButtonProps<'a>>, -) -> Element { - let GenericHistoryButtonProps { children } = cx.props; +pub fn GoForwardButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element { + let HistoryButtonProps { children } = cx.props; // hook up to router - let router = match use_router_internal::(cx) { + let router = match use_router_internal(cx) { Some(r) => r, #[allow(unreachable_code)] None => { diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index 00d5ee551..efed7fb5f 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -1,15 +1,57 @@ +use std::any::Any; use std::fmt::Debug; use dioxus::prelude::*; use log::error; use crate::navigation::NavigationTarget; -use crate::prelude::*; +use crate::prelude::{AnyDisplay, Routable}; use crate::utils::use_router_internal::use_router_internal; -/// The properties for a [`GenericLink`]. +/// Something that can be converted into a [`NavigationTarget`]. +pub enum IntoRoutable { + /// A raw string target. + FromStr(String), + /// A internal target. + Route(Box), +} + +impl From for IntoRoutable { + fn from(value: R) -> Self { + IntoRoutable::Route(Box::new(value)) + } +} + +impl From> for IntoRoutable { + fn from(value: NavigationTarget) -> Self { + match value { + NavigationTarget::Internal(route) => IntoRoutable::Route(Box::new(route)), + NavigationTarget::External(url) => IntoRoutable::FromStr(url), + } + } +} + +impl From for IntoRoutable { + fn from(value: String) -> Self { + IntoRoutable::FromStr(value) + } +} + +impl From<&String> for IntoRoutable { + fn from(value: &String) -> Self { + IntoRoutable::FromStr(value.to_string()) + } +} + +impl From<&str> for IntoRoutable { + fn from(value: &str) -> Self { + IntoRoutable::FromStr(value.to_string()) + } +} + +/// The properties for a [`Link`]. #[derive(Props)] -pub struct GenericLinkProps<'a, R: Routable> { +pub struct LinkProps<'a> { /// A class to apply to the generate HTML anchor tag if the `target` route is active. pub active_class: Option<&'a str>, /// The children to render within the generated HTML anchor tag. @@ -23,7 +65,7 @@ pub struct GenericLinkProps<'a, R: Routable> { pub id: Option<&'a str>, /// When [`true`], the `target` route will be opened in a new tab. /// - /// This does not change whether the [`GenericLink`] is active or not. + /// This does not change whether the [`Link`] is active or not. #[props(default)] pub new_tab: bool, /// The onclick event handler. @@ -42,10 +84,10 @@ pub struct GenericLinkProps<'a, R: Routable> { pub rel: Option<&'a str>, /// The navigation target. Roughly equivalent to the href attribute of an HTML anchor tag. #[props(into)] - pub to: NavigationTarget, + pub to: IntoRoutable, } -impl Debug for GenericLinkProps<'_, R> { +impl Debug for LinkProps<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("LinkProps") .field("active_class", &self.active_class) @@ -56,27 +98,26 @@ impl Debug for GenericLinkProps<'_, R> { .field("onclick", &self.onclick.as_ref().map(|_| "onclick is set")) .field("onclick_only", &self.onclick_only) .field("rel", &self.rel) - .field("to", &self.to.to_string()) .finish() } } /// A link to navigate to another route. /// -/// Only works as descendant of a [`GenericRouter`] component, otherwise it will be inactive. +/// Only works as descendant of a [`Router`] component, otherwise it will be inactive. /// -/// Unlike a regular HTML anchor, a [`GenericLink`] allows the router to handle the navigation and doesn't +/// Unlike a regular HTML anchor, a [`Link`] allows the router to handle the navigation and doesn't /// cause the browser to load a new page. /// -/// However, in the background a [`GenericLink`] still generates an anchor, which you can use for styling +/// However, in the background a [`Link`] still generates an anchor, which you can use for styling /// as normal. /// /// # External targets -/// When the [`GenericLink`]s target is an [`NavigationTarget::External`] target, that is used as the `href` directly. This -/// means that a [`GenericLink`] can always navigate to an [`NavigationTarget::External`] target, even if the [`HistoryProvider`] does not support it. +/// When the [`Link`]s target is an [`NavigationTarget::External`] target, that is used as the `href` directly. This +/// means that a [`Link`] can always navigate to an [`NavigationTarget::External`] target, even if the [`HistoryProvider`] does not support it. /// /// # Panic -/// - When the [`GenericLink`] is not nested within a [`GenericRouter`], but +/// - When the [`Link`] is not nested within a [`Router`], but /// only in debug builds. /// /// # Example @@ -92,7 +133,7 @@ impl Debug for GenericLinkProps<'_, R> { /// /// fn App(cx: Scope) -> Element { /// render! { -/// Router {} +/// Router:: {} /// } /// } /// @@ -122,8 +163,8 @@ impl Debug for GenericLinkProps<'_, R> { /// # ); /// ``` #[allow(non_snake_case)] -pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R>>) -> Element { - let GenericLinkProps { +pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element { + let LinkProps { active_class, children, class, @@ -133,10 +174,11 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R onclick_only, rel, to, + .. } = cx.props; // hook up to router - let router = match use_router_internal::(cx) { + let router = match use_router_internal(cx) { Some(r) => r, #[allow(unreachable_code)] None => { @@ -148,9 +190,15 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R } }; - let current_route = router.current(); - let current_url = current_route.to_string(); - let href = to.to_string(); + let current_url = router.current_route_string(); + let href = match to { + IntoRoutable::FromStr(url) => url.to_string(), + IntoRoutable::Route(route) => route.to_string(), + }; + let parsed_route: NavigationTarget> = match router.route_from_str(&href) { + Ok(route) => NavigationTarget::Internal(route.into()), + Err(err) => NavigationTarget::External(err), + }; let ac = active_class .and_then(|active_class| (href == current_url).then(|| format!(" {active_class}"))) .unwrap_or_default(); @@ -159,7 +207,7 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R let class = format!("{}{ac}", class.unwrap_or_default()); let tag_target = new_tab.then_some("_blank").unwrap_or_default(); - let is_external = matches!(to, NavigationTarget::External(_)); + let is_external = matches!(parsed_route, NavigationTarget::External(_)); let is_router_nav = !is_external && !new_tab; let prevent_default = is_router_nav.then_some("onclick").unwrap_or_default(); let rel = rel @@ -169,7 +217,15 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R let do_default = onclick.is_none() || !onclick_only; let action = move |event| { if do_default && is_router_nav { - router.push(to.clone()); + let href = match to { + IntoRoutable::FromStr(url) => url.to_string(), + IntoRoutable::Route(route) => route.to_string(), + }; + let parsed_route: NavigationTarget> = match router.route_from_str(&href) { + Ok(route) => NavigationTarget::Internal(route.into()), + Err(err) => NavigationTarget::External(err), + }; + router.push_any(parsed_route); } if let Some(handler) = onclick { diff --git a/packages/router/src/components/outlet.rs b/packages/router/src/components/outlet.rs index da312402e..133a23543 100644 --- a/packages/router/src/components/outlet.rs +++ b/packages/router/src/components/outlet.rs @@ -3,13 +3,13 @@ use dioxus::prelude::*; /// An outlet for the current content. /// -/// Only works as descendant of a [`GenericRouter`] component, otherwise it will be inactive. +/// Only works as descendant of a [`Link`] component, otherwise it will be inactive. /// -/// The [`GenericOutlet`] is aware of how many [`GenericOutlet`]s it is nested within. It will render the content +/// The [`Outlet`] is aware of how many [`Outlet`]s it is nested within. It will render the content /// of the active route that is __exactly as deep__. /// /// # Panic -/// - When the [`GenericOutlet`] is not nested a [`GenericRouter`] component, +/// - When the [`Outlet`] is not nested a [`Link`] component, /// but only in debug builds. /// /// # Example @@ -42,7 +42,7 @@ use dioxus::prelude::*; /// fn Wrapper(cx: Scope) -> Element { /// render! { /// h1 { "App" } -/// Outlet {} // The content of child routes will be rendered here +/// Outlet:: {} // The content of child routes will be rendered here /// } /// } /// @@ -57,7 +57,7 @@ use dioxus::prelude::*; /// /// # fn App(cx: Scope) -> Element { /// # render! { -/// # Router { +/// # Router:: { /// # config: || RouterConfig::default().history(MemoryHistory::with_initial_path(Route::Child {})) /// # } /// # } @@ -67,6 +67,6 @@ use dioxus::prelude::*; /// # let _ = vdom.rebuild(); /// # assert_eq!(dioxus_ssr::render(&vdom), "

App

Child

"); /// ``` -pub fn GenericOutlet(cx: Scope) -> Element { +pub fn Outlet(cx: Scope) -> Element { OutletContext::::render(cx) } diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index 7c82cf19b..dae4e3e66 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -1,13 +1,9 @@ use dioxus::prelude::*; use std::{cell::RefCell, str::FromStr}; -use crate::{ - prelude::{GenericOutlet, GenericRouterContext}, - routable::Routable, - router_cfg::RouterConfig, -}; +use crate::{prelude::Outlet, routable::Routable, router_cfg::RouterConfig}; -/// The config for [`GenericRouter`]. +/// The config for [`Router`]. pub struct RouterConfigFactory { #[allow(clippy::type_complexity)] config: RefCell RouterConfig>>>, @@ -43,9 +39,9 @@ impl RouterConfig + 'static> From for RouterCo } #[cfg(feature = "serde")] -/// The props for [`GenericRouter`]. +/// The props for [`Router`]. #[derive(Props)] -pub struct GenericRouterProps +pub struct RouterProps where ::Err: std::fmt::Display, R: serde::Serialize + serde::de::DeserializeOwned, @@ -55,9 +51,9 @@ where } #[cfg(not(feature = "serde"))] -/// The props for [`GenericRouter`]. +/// The props for [`Router`]. #[derive(Props)] -pub struct GenericRouterProps +pub struct RouterProps where ::Err: std::fmt::Display, { @@ -66,7 +62,7 @@ where } #[cfg(not(feature = "serde"))] -impl Default for GenericRouterProps +impl Default for RouterProps where ::Err: std::fmt::Display, { @@ -78,7 +74,7 @@ where } #[cfg(feature = "serde")] -impl Default for GenericRouterProps +impl Default for RouterProps where ::Err: std::fmt::Display, R: serde::Serialize + serde::de::DeserializeOwned, @@ -91,7 +87,7 @@ where } #[cfg(not(feature = "serde"))] -impl PartialEq for GenericRouterProps +impl PartialEq for RouterProps where ::Err: std::fmt::Display, { @@ -102,7 +98,7 @@ where } #[cfg(feature = "serde")] -impl PartialEq for GenericRouterProps +impl PartialEq for RouterProps where ::Err: std::fmt::Display, R: serde::Serialize + serde::de::DeserializeOwned, @@ -115,14 +111,14 @@ where #[cfg(not(feature = "serde"))] /// A component that renders the current route. -pub fn GenericRouter(cx: Scope>) -> Element +pub fn Router(cx: Scope>) -> Element where ::Err: std::fmt::Display, { - use crate::prelude::outlet::OutletContext; + use crate::prelude::{outlet::OutletContext, RouterContext}; use_context_provider(cx, || { - GenericRouterContext::new( + RouterContext::new( (cx.props .config .config @@ -137,19 +133,19 @@ where }); render! { - GenericOutlet:: {} + Outlet:: {} } } #[cfg(feature = "serde")] /// A component that renders the current route. -pub fn GenericRouter(cx: Scope>) -> Element +pub fn Router(cx: Scope>) -> Element where ::Err: std::fmt::Display, R: serde::Serialize + serde::de::DeserializeOwned, { use_context_provider(cx, || { - GenericRouterContext::new( + RouterContext::new( (cx.props .config .config @@ -164,6 +160,6 @@ where }); render! { - GenericOutlet:: {} + Outlet:: {} } } diff --git a/packages/router/src/contexts/navigator.rs b/packages/router/src/contexts/navigator.rs index 57538dc58..d8cf6aa57 100644 --- a/packages/router/src/contexts/navigator.rs +++ b/packages/router/src/contexts/navigator.rs @@ -1,10 +1,10 @@ -use crate::prelude::{ExternalNavigationFailure, GenericRouterContext, NavigationTarget, Routable}; +use crate::prelude::{ExternalNavigationFailure, NavigationTarget, Routable, RouterContext}; /// A view into the navigation state of a router. #[derive(Clone)] -pub struct GenericNavigator(pub(crate) GenericRouterContext); +pub struct GenericNavigator(pub(crate) RouterContext); -impl GenericNavigator { +impl GenericNavigator { /// Check whether there is a previous page to navigate back to. #[must_use] pub fn can_go_back(&self) -> bool { @@ -34,7 +34,7 @@ impl GenericNavigator { /// Push a new location. /// /// The previous location will be available to go back to. - pub fn push( + pub fn push( &self, target: impl Into>, ) -> Option { @@ -44,7 +44,7 @@ impl GenericNavigator { /// Replace the current location. /// /// The previous location will **not** be available to go back to. - pub fn replace( + pub fn replace( &self, target: impl Into>, ) -> Option { diff --git a/packages/router/src/contexts/outlet.rs b/packages/router/src/contexts/outlet.rs index 6284e25f8..d4a070351 100644 --- a/packages/router/src/contexts/outlet.rs +++ b/packages/router/src/contexts/outlet.rs @@ -31,7 +31,7 @@ impl OutletContext { where R: Routable + Clone, { - let router = use_router_internal::(cx) + let router = use_router_internal(cx) .as_ref() .expect("Outlet must be inside of a router"); let outlet: &OutletContext = use_outlet_context(cx); @@ -51,6 +51,6 @@ impl OutletContext { } } - router.current().render(cx, current_level) + router.current::().render(cx, current_level) } } diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index a64b64b4c..136e9e3b7 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -1,4 +1,5 @@ use std::{ + any::Any, collections::HashSet, sync::{Arc, RwLock, RwLockWriteGuard}, }; @@ -6,7 +7,7 @@ use std::{ use dioxus::prelude::*; use crate::{ - history::HistoryProvider, navigation::NavigationTarget, routable::Routable, + navigation::NavigationTarget, prelude::AnyHistoryProvider, routable::Routable, router_cfg::RouterConfig, }; @@ -15,52 +16,31 @@ use crate::{ pub struct ExternalNavigationFailure(String); /// A function the router will call after every routing update. -pub(crate) type RoutingCallback = - Arc) -> Option>>; +pub(crate) type RoutingCallback = Arc) -> Option>>; -struct MutableRouterState -where - R: Routable, -{ +struct MutableRouterState { /// The current prefix. prefix: Option, - history: Box>, + history: Box, unresolved_error: Option, } /// A collection of router data that manages all routing functionality. -pub struct GenericRouterContext -where - R: Routable, -{ - state: Arc>>, +#[derive(Clone)] +pub struct RouterContext { + state: Arc>, subscribers: Arc>>, subscriber_update: Arc, - routing_callback: Option>, + routing_callback: Option Option>>>>, failure_external_navigation: fn(Scope) -> Element, } -impl Clone for GenericRouterContext { - fn clone(&self) -> Self { - Self { - state: self.state.clone(), - subscribers: self.subscribers.clone(), - subscriber_update: self.subscriber_update.clone(), - routing_callback: self.routing_callback.clone(), - failure_external_navigation: self.failure_external_navigation, - } - } -} - -impl GenericRouterContext -where - R: Routable, -{ - pub(crate) fn new( +impl RouterContext { + pub(crate) fn new( mut cfg: RouterConfig, mark_dirty: Arc, ) -> Self @@ -82,7 +62,21 @@ where subscribers: subscribers.clone(), subscriber_update, - routing_callback: cfg.on_update, + routing_callback: cfg.on_update.map(|update| { + Arc::new(move |ctx| { + let ctx = LinkContext { + inner: ctx, + _marker: std::marker::PhantomData, + }; + update(ctx).map(|t| match t { + NavigationTarget::Internal(r) => { + NavigationTarget::Internal(Box::new(r) as Box) + } + NavigationTarget::External(s) => NavigationTarget::External(s), + }) + }) + as Arc Option>>> + }), failure_external_navigation: cfg.failure_external_navigation, }; @@ -100,6 +94,11 @@ where myself } + pub(crate) fn route_from_str(&self, route: &str) -> Result, String> { + let state = self.state.read().unwrap(); + state.history.parse_route(route) + } + /// Check whether there is a previous page to navigate back to. #[must_use] pub fn can_go_back(&self) -> bool { @@ -134,14 +133,10 @@ where self.change_route(); } - /// Push a new location. - /// - /// The previous location will be available to go back to. - pub fn push( + pub(crate) fn push_any( &self, - target: impl Into>, + target: NavigationTarget>, ) -> Option { - let target = target.into(); match target { NavigationTarget::Internal(p) => { let mut state = self.state_mut(); @@ -153,10 +148,29 @@ where self.change_route() } + /// Push a new location. + /// + /// The previous location will be available to go back to. + pub fn push( + &self, + target: impl Into>, + ) -> Option { + let target = target.into(); + match target { + NavigationTarget::Internal(p) => { + let mut state = self.state_mut(); + state.history.push(Box::new(p)) + } + NavigationTarget::External(e) => return self.external(e), + } + + self.change_route() + } + /// Replace the current location. /// /// The previous location will **not** be available to go back to. - pub fn replace( + pub fn replace( &self, target: impl Into>, ) -> Option { @@ -165,7 +179,7 @@ where { let mut state = self.state_mut(); match target { - NavigationTarget::Internal(p) => state.history.replace(p), + NavigationTarget::Internal(p) => state.history.replace(Box::new(p)), NavigationTarget::External(e) => return self.external(e), } } @@ -174,11 +188,24 @@ where } /// The route that is currently active. - pub fn current(&self) -> R - where - R: Clone, - { - self.state.read().unwrap().history.current_route() + pub fn current(&self) -> R { + self.state + .read() + .unwrap() + .history + .current_route() + .downcast::() + .unwrap() + } + + /// The route that is currently active. + pub fn current_route_string(&self) -> String { + self.state + .read() + .unwrap() + .history + .current_route() + .to_string() } /// The prefix that is currently active. @@ -201,7 +228,7 @@ where } } - fn state_mut(&self) -> RwLockWriteGuard> { + fn state_mut(&self) -> RwLockWriteGuard { self.state.write().unwrap() } @@ -254,3 +281,87 @@ where None } } + +pub struct LinkContext { + inner: RouterContext, + _marker: std::marker::PhantomData, +} + +impl LinkContext +where + R: Routable, +{ + /// Check whether there is a previous page to navigate back to. + #[must_use] + pub fn can_go_back(&self) -> bool { + self.inner.can_go_back() + } + + /// Check whether there is a future page to navigate forward to. + #[must_use] + pub fn can_go_forward(&self) -> bool { + self.inner.can_go_forward() + } + + /// Go back to the previous location. + /// + /// Will fail silently if there is no previous location to go to. + pub fn go_back(&self) { + self.inner.go_back(); + } + + /// Go back to the next location. + /// + /// Will fail silently if there is no next location to go to. + pub fn go_forward(&self) { + self.inner.go_forward(); + } + + /// Push a new location. + /// + /// The previous location will be available to go back to. + pub fn push( + &self, + target: impl Into>, + ) -> Option { + self.inner.push(target) + } + + /// Replace the current location. + /// + /// The previous location will **not** be available to go back to. + pub fn replace( + &self, + target: impl Into>, + ) -> Option { + self.inner.replace(target) + } + + /// The route that is currently active. + pub fn current(&self) -> R + where + R: Clone, + { + self.inner.current() + } + + /// The prefix that is currently active. + pub fn prefix(&self) -> Option { + self.inner.prefix() + } + + /// Manually subscribe to the current route + pub fn subscribe(&self, id: ScopeId) { + self.inner.subscribe(id) + } + + /// Manually unsubscribe from the current route + pub fn unsubscribe(&self, id: ScopeId) { + self.inner.unsubscribe(id) + } + + /// Clear any unresolved errors + pub fn clear_error(&self) { + self.inner.clear_error() + } +} diff --git a/packages/router/src/history/mod.rs b/packages/router/src/history/mod.rs index 21c7b9e92..e666e8550 100644 --- a/packages/router/src/history/mod.rs +++ b/packages/router/src/history/mod.rs @@ -10,7 +10,7 @@ //! 1) [`MemoryHistory`] for desktop/mobile/ssr platforms //! 2) [`WebHistory`] for web platforms -use std::sync::Arc; +use std::{any::Any, fmt::Display, sync::Arc}; mod memory; pub use memory::*; @@ -277,3 +277,143 @@ pub trait HistoryProvider { #[allow(unused_variables)] fn updater(&mut self, callback: Arc) {} } + +/// Something that can be displayed and is also an [`Any`] +pub trait AnyDisplay: Display + Any { + /// Get a reference to the inner [`Any`] object. + fn as_any(&self) -> &dyn Any; +} + +impl dyn AnyDisplay { + /// Try to downcast the inner [`Any`] object to a concrete type. + pub fn downcast(self: Box) -> Option { + self.as_any().downcast_ref::().cloned() + } +} + +impl AnyDisplay for T { + fn as_any(&self) -> &dyn Any { + self + } +} + +pub(crate) trait AnyHistoryProvider { + #[must_use] + fn parse_route(&self, route: &str) -> Result, String>; + + #[must_use] + fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool; + + #[must_use] + fn current_route(&self) -> Box; + + #[must_use] + fn current_prefix(&self) -> Option { + None + } + + #[must_use] + fn can_go_back(&self) -> bool { + true + } + + fn go_back(&mut self); + + #[must_use] + fn can_go_forward(&self) -> bool { + true + } + + fn go_forward(&mut self); + + fn push(&mut self, route: Box); + + fn replace(&mut self, path: Box); + + #[allow(unused_variables)] + fn external(&mut self, url: String) -> bool { + false + } + + #[allow(unused_variables)] + fn updater(&mut self, callback: Arc) {} +} + +pub(crate) struct AnyHistoryProviderImplWrapper { + inner: H, + _marker: std::marker::PhantomData, +} + +impl AnyHistoryProviderImplWrapper { + pub fn new(inner: H) -> Self { + Self { + inner, + _marker: std::marker::PhantomData, + } + } +} + +impl Default for AnyHistoryProviderImplWrapper { + fn default() -> Self { + Self::new(H::default()) + } +} + +impl AnyHistoryProvider for AnyHistoryProviderImplWrapper +where + R: Routable, + ::Err: std::fmt::Display, + H: HistoryProvider, +{ + fn parse_route(&self, route: &str) -> Result, String> { + R::from_str(route) + .map_err(|err| err.to_string()) + .map(|route| Box::new(route) as Box) + } + + fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool { + type_id == &std::any::TypeId::of::() + } + + fn current_route(&self) -> Box { + let route = self.inner.current_route(); + println!("current_route {route}"); + Box::new(route) + } + + fn current_prefix(&self) -> Option { + self.inner.current_prefix() + } + + fn can_go_back(&self) -> bool { + self.inner.can_go_back() + } + + fn go_back(&mut self) { + self.inner.go_back() + } + + fn can_go_forward(&self) -> bool { + self.inner.can_go_forward() + } + + fn go_forward(&mut self) { + self.inner.go_forward() + } + + fn push(&mut self, route: Box) { + self.inner.push(*route.downcast().unwrap()) + } + + fn replace(&mut self, path: Box) { + self.inner.replace(*path.downcast().unwrap()) + } + + fn external(&mut self, url: String) -> bool { + self.inner.external(url) + } + + fn updater(&mut self, callback: Arc) { + self.inner.updater(callback) + } +} diff --git a/packages/router/src/hooks/use_navigator.rs b/packages/router/src/hooks/use_navigator.rs index 5cd0a0e83..aab64fdc2 100644 --- a/packages/router/src/hooks/use_navigator.rs +++ b/packages/router/src/hooks/use_navigator.rs @@ -1,9 +1,6 @@ use dioxus::prelude::ScopeState; -use crate::{ - prelude::{GenericNavigator, GenericRouterContext}, - routable::Routable, -}; +use crate::prelude::{GenericNavigator, RouterContext}; /// A hook that provides access to the navigator to change the router history. Unlike [`use_router`], this hook will not cause a rerender when the current route changes /// @@ -22,7 +19,7 @@ use crate::{ /// /// fn App(cx: Scope) -> Element { /// render! { -/// Router {} +/// Router:: {} /// } /// } /// @@ -50,10 +47,10 @@ use crate::{ /// # let mut vdom = VirtualDom::new(App); /// # let _ = vdom.rebuild(); /// ``` -pub fn use_generic_navigator(cx: &ScopeState) -> &GenericNavigator { +pub fn use_navigator(cx: &ScopeState) -> &GenericNavigator { &*cx.use_hook(|| { let router = cx - .consume_context::>() + .consume_context::() .expect("Must be called in a descendant of a Router component"); GenericNavigator(router) diff --git a/packages/router/src/hooks/use_route.rs b/packages/router/src/hooks/use_route.rs index a0ba34ce8..e6aa906d5 100644 --- a/packages/router/src/hooks/use_route.rs +++ b/packages/router/src/hooks/use_route.rs @@ -8,11 +8,11 @@ use crate::utils::use_router_internal::use_router_internal; /// > The Routable macro will define a version of this hook with an explicit type. /// /// # Return values -/// - None, when not called inside a [`GenericRouter`] component. +/// - None, when not called inside a [`Link`] component. /// - Otherwise the current route. /// /// # Panic -/// - When the calling component is not nested within a [`GenericRouter`] component durring a debug build. +/// - When the calling component is not nested within a [`Link`] component durring a debug build. /// /// # Example /// ```rust @@ -28,7 +28,7 @@ use crate::utils::use_router_internal::use_router_internal; /// fn App(cx: Scope) -> Element { /// render! { /// h1 { "App" } -/// Router {} +/// Router:: {} /// } /// } /// @@ -45,7 +45,7 @@ use crate::utils::use_router_internal::use_router_internal; /// # let _ = vdom.rebuild(); /// # assert_eq!(dioxus_ssr::render(&vdom), "

App

Current Path

/

") /// ``` -pub fn use_generic_route(cx: &ScopeState) -> Option { +pub fn use_route(cx: &ScopeState) -> Option { match use_router_internal(cx) { Some(r) => Some(r.current()), None => { diff --git a/packages/router/src/hooks/use_router.rs b/packages/router/src/hooks/use_router.rs index b9485577a..1b7556665 100644 --- a/packages/router/src/hooks/use_router.rs +++ b/packages/router/src/hooks/use_router.rs @@ -1,12 +1,9 @@ use dioxus::prelude::ScopeState; -use crate::{ - prelude::GenericRouterContext, routable::Routable, - utils::use_router_internal::use_router_internal, -}; +use crate::{prelude::RouterContext, utils::use_router_internal::use_router_internal}; /// A hook that provides access to information about the router. -pub fn use_generic_router(cx: &ScopeState) -> &GenericRouterContext { +pub fn use_router(cx: &ScopeState) -> &RouterContext { use_router_internal(cx) .as_ref() .expect("use_route must have access to a router") diff --git a/packages/router/src/incremental.rs b/packages/router/src/incremental.rs index 2299be614..b89a2fc09 100644 --- a/packages/router/src/incremental.rs +++ b/packages/router/src/incremental.rs @@ -95,7 +95,7 @@ where { let path = path.clone(); render! { - GenericRouter:: { + Link:: { config: || RouterConfig::default().history(MemoryHistory::with_initial_path(path)) } } diff --git a/packages/router/src/navigation.rs b/packages/router/src/navigation.rs index 71ca9a4ab..c0a85fe67 100644 --- a/packages/router/src/navigation.rs +++ b/packages/router/src/navigation.rs @@ -11,7 +11,7 @@ use crate::routable::Routable; /// A target for the router to navigate to. #[derive(Clone, PartialEq, Eq, Debug)] -pub enum NavigationTarget { +pub enum NavigationTarget { /// An internal path that the router can navigate to by itself. /// /// ```rust diff --git a/packages/router/src/router_cfg.rs b/packages/router/src/router_cfg.rs index 31eeeb4d5..23731810c 100644 --- a/packages/router/src/router_cfg.rs +++ b/packages/router/src/router_cfg.rs @@ -26,7 +26,7 @@ use crate::prelude::*; /// ``` pub struct RouterConfig { pub(crate) failure_external_navigation: fn(Scope) -> Element, - pub(crate) history: Option>>, + pub(crate) history: Option>, pub(crate) on_update: Option>, } @@ -69,7 +69,7 @@ where { fn default() -> Self { Self { - failure_external_navigation: FailureExternalNavigation::, + failure_external_navigation: FailureExternalNavigation, history: None, on_update: None, } @@ -81,18 +81,22 @@ impl RouterConfig where ::Err: std::fmt::Display, { - pub(crate) fn take_history(&mut self) -> Box> { + pub(crate) fn take_history(&mut self) -> Box { self.history.take().unwrap_or_else(|| { #[cfg(all(target_arch = "wasm32", feature = "web"))] - let history = Box::>::default(); + let history = Box::>>::default(); #[cfg(not(all(target_arch = "wasm32", feature = "web")))] - let history = Box::>::default(); + let history = Box::>>::default(); history }) } } -impl RouterConfig { +impl RouterConfig +where + R: Routable, + ::Err: std::fmt::Display, +{ /// A function to be called whenever the routing is updated. /// /// The callback is invoked after the routing is updated, but before components and hooks are @@ -108,7 +112,7 @@ impl RouterConfig { /// Defaults to [`None`]. pub fn on_update( self, - callback: impl Fn(GenericRouterContext) -> Option> + 'static, + callback: impl Fn(LinkContext) -> Option> + 'static, ) -> Self { Self { on_update: Some(Arc::new(callback)), @@ -121,7 +125,7 @@ impl RouterConfig { /// Defaults to a default [`MemoryHistory`]. pub fn history(self, history: impl HistoryProvider + 'static) -> Self { Self { - history: Some(Box::new(history)), + history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))), ..self } } diff --git a/packages/router/src/utils/use_router_internal.rs b/packages/router/src/utils/use_router_internal.rs index 898ef539f..9ac33f5fb 100644 --- a/packages/router/src/utils/use_router_internal.rs +++ b/packages/router/src/utils/use_router_internal.rs @@ -1,6 +1,6 @@ use dioxus::prelude::{ScopeId, ScopeState}; -use crate::{contexts::router::GenericRouterContext, prelude::*}; +use crate::prelude::*; /// A private hook to subscribe to the router. /// @@ -8,13 +8,11 @@ use crate::{contexts::router::GenericRouterContext, prelude::*}; /// single component, but not recommended. Multiple subscriptions will be discarded. /// /// # Return values -/// - [`None`], when the current component isn't a descendant of a [`GenericRouter`] component. +/// - [`None`], when the current component isn't a descendant of a [`Link`] component. /// - Otherwise [`Some`]. -pub(crate) fn use_router_internal( - cx: &ScopeState, -) -> &Option> { +pub(crate) fn use_router_internal(cx: &ScopeState) -> &Option { let inner = cx.use_hook(|| { - let router = cx.consume_context::>()?; + let router = cx.consume_context::()?; let id = cx.scope_id(); router.subscribe(id); @@ -24,12 +22,12 @@ pub(crate) fn use_router_internal( cx.use_hook(|| inner.as_ref().map(|s| s.router.clone())) } -struct Subscription { - router: GenericRouterContext, +struct Subscription { + router: RouterContext, id: ScopeId, } -impl Drop for Subscription { +impl Drop for Subscription { fn drop(&mut self) { self.router.unsubscribe(self.id); } diff --git a/packages/router/tests/via_ssr/link.rs b/packages/router/tests/via_ssr/link.rs index 764a9f699..3fe899128 100644 --- a/packages/router/tests/via_ssr/link.rs +++ b/packages/router/tests/via_ssr/link.rs @@ -33,7 +33,7 @@ where { render! { h1 { "App" } - GenericRouter:: { + Router:: { config: || RouterConfig::default().history(MemoryHistory::default()) } } @@ -97,7 +97,7 @@ fn href_external() { fn Root(cx: Scope) -> Element { render! { Link { - to: NavigationTarget::External("https://dioxuslabs.com/".into()), + to: "https://dioxuslabs.com/", "Link" } } @@ -318,7 +318,7 @@ fn with_new_tab_external() { fn Root(cx: Scope) -> Element { render! { Link { - to: NavigationTarget::External("https://dioxuslabs.com/".into()), + to: "https://dioxuslabs.com/", new_tab: true, "Link" } diff --git a/packages/router/tests/via_ssr/outlet.rs b/packages/router/tests/via_ssr/outlet.rs index ba8eb8873..d48b355d0 100644 --- a/packages/router/tests/via_ssr/outlet.rs +++ b/packages/router/tests/via_ssr/outlet.rs @@ -37,7 +37,7 @@ fn prepare(path: impl Into) -> VirtualDom { fn App(cx: Scope) -> Element { render! { h1 { "App" } - Router { + Router:: { config: { let path = cx.props.path.parse().unwrap(); move || RouterConfig::default().history(MemoryHistory::with_initial_path(path)) @@ -57,7 +57,7 @@ fn prepare(path: impl Into) -> VirtualDom { fn Fixed(cx: Scope) -> Element { render! { h2 { "Fixed" } - Outlet { } + Outlet:: { } } } @@ -79,7 +79,7 @@ fn prepare(path: impl Into) -> VirtualDom { fn Parameter(cx: Scope, id: u8) -> Element { render! { h2 { "Parameter {id}" } - Outlet { } + Outlet:: { } } }