it almost lives?

This commit is contained in:
Evan Almloff 2023-07-25 18:14:48 -07:00
parent d637ef187c
commit 5f0dd3af3e
43 changed files with 506 additions and 244 deletions

View file

@ -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::<Route> {
// 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> {
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> {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
// if the current location doesn't match any of the above routes, redirect to "/home"

View file

@ -28,7 +28,7 @@ Ao contrário de outros roteadores no ecossistema Rust, nosso roteador é constr
```rust, no_run
rsx!{
Router {
Router::<Route> {
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> {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
Route { to: "", NotFound {} }
@ -55,7 +55,7 @@ rsx!{
```rust, no_run
rsx!{
Router {
Router::<Route> {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
Redirect { from: "", to: "/home" }

View file

@ -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::<Route> {
onchange: move |_| log::info!("Route changed!"),
ul {
Link { to: "/", li { "Go home!" } }

View file

@ -17,7 +17,7 @@ enum Route {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
// ANCHOR_END: app

View file

@ -35,7 +35,7 @@ enum Route {
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
@ -48,7 +48,7 @@ fn NavBar(cx: Scope) -> Element {
li { Link { to: Route::BlogList {}, "Blog" } }
}
}
Outlet {}
Outlet::<Route> {}
}
}
@ -64,7 +64,7 @@ fn Home(cx: Scope) -> Element {
fn Blog(cx: Scope) -> Element {
render! {
h1 { "Blog" }
Outlet {}
Outlet::<Route> {}
}
}
// ANCHOR_END: blog

View file

@ -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"
}
}

View file

@ -19,7 +19,7 @@ enum Route {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
// ANCHOR_END: app

View file

@ -39,7 +39,7 @@ enum Route {
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
@ -52,7 +52,7 @@ fn NavBar(cx: Scope) -> Element {
li { Link { to: Route::BlogList {}, "Blog" } }
}
}
Outlet {}
Outlet::<Route> {}
}
}
@ -67,7 +67,7 @@ fn Home(cx: Scope) -> Element {
fn Blog(cx: Scope) -> Element {
render! {
h1 { "Blog" }
Outlet {}
Outlet::<Route> {}
}
}

View file

@ -12,7 +12,7 @@ enum Route {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {
Router::<Route> {
config: || RouterConfig::default().history(WebHistory::default())
}
}

View file

@ -32,7 +32,7 @@ fn NavBar(cx: Scope) -> Element {
}
}
}
Outlet {}
Outlet::<Route> {}
}
}
// ANCHOR_END: nav
@ -41,7 +41,7 @@ fn NavBar(cx: Scope) -> Element {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
// ANCHOR_END: app

View file

@ -14,7 +14,7 @@ enum Route {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}

View file

@ -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::<Route> {}
}
}
// ANCHOR_END: nav
@ -35,7 +35,7 @@ fn NavBar(cx: Scope) -> Element {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
// ANCHOR_END: app

View file

@ -16,7 +16,7 @@ fn Wrapper(cx: Scope) -> Element {
render! {
header { "header" }
// The index route will be rendered here
Outlet { }
Outlet::<Route> { }
footer { "footer" }
}
}
@ -31,7 +31,7 @@ fn Index(cx: Scope) -> Element {
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}

View file

@ -18,7 +18,7 @@ enum Route {
#[inline_props]
fn App(cx: Scope) -> Element {
render! {
Router {
Router::<Route> {
config: || RouterConfig::default().history(WebHistory::default())
}
}

View file

@ -27,7 +27,7 @@ fn Index(cx: Scope) -> Element {
fn app(cx: Scope) -> Element {
render! {
Router {
Router::<Route> {
config: || RouterConfig::default().on_update(|state|{
(state.current() == Route::Index {}).then_some(
NavigationTarget::Internal(Route::Home {})

View file

@ -46,7 +46,7 @@ fn App(cx: Scope) -> Element {
h1 { "Dioxus CRM Example" }
Router {}
Router::<Route> {}
}
}

View file

@ -18,7 +18,7 @@ fn main() {
fn app(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
@ -40,7 +40,7 @@ enum Route {
fn Footer(cx: Scope) -> Element {
render! {
div {
Outlet { }
Outlet::<Route> { }
p {
"----"

View file

@ -23,7 +23,7 @@ fn app(cx: Scope) -> Element {
}
}
div {
Router {}
Router::<Route> {}
}
))
}
@ -46,7 +46,7 @@ fn Header(cx: Scope) -> Element {
li { Link { to: Route::Home {}, "home" } }
li { Link { to: Route::Settings {}, "settings" } }
}
Outlet {}
Outlet::<Route> {}
}
}

View file

@ -39,7 +39,7 @@ enum Route {
fn App(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
@ -52,7 +52,7 @@ fn NavBar(cx: Scope) -> Element {
li { Link { to: Route::BlogList {}, "Blog" } }
}
}
Outlet {}
Outlet::<Route> {}
}
}
@ -67,7 +67,7 @@ fn Home(cx: Scope) -> Element {
fn Blog(cx: Scope) -> Element {
render! {
h1 { "Blog" }
Outlet {}
Outlet::<Route> {}
}
}

View file

@ -14,7 +14,7 @@ fn main() {
fn app(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
@ -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::<Route> {}
}
}

View file

@ -16,7 +16,7 @@ where
let cfg = *cx.props;
render! {
dioxus_router::prelude::GenericRouter::<R> {
dioxus_router::prelude::Router::<R> {
config: move || {
RouterConfig::default()
.failure_external_navigation(cfg.failure_external_navigation)
@ -54,7 +54,7 @@ where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
dioxus_router::prelude::FailureExternalNavigation::<R>
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::<R>,
failure_external_navigation: dioxus_router::prelude::FailureExternalNavigation,
scroll_restoration: true,
phantom: std::marker::PhantomData,
}

View file

@ -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_router::prelude::GenericRouterProps<#name>>) -> 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<Redirect>,
routes: Vec<Route>,
@ -267,7 +236,6 @@ struct RouteEnum {
impl RouteEnum {
fn parse(data: syn::ItemEnum) -> syn::Result<Self> {
let name = &data.ident;
let vis = &data.vis;
let mut site_map = Vec::new();
let mut site_map_stack: Vec<Vec<SiteMapSegment>> = Vec::new();
@ -457,7 +425,6 @@ impl RouteEnum {
}
let myself = Self {
vis: vis.clone(),
name: name.clone(),
routes,
redirects,

View file

@ -51,7 +51,7 @@ enum Route {
fn App(cx: Scope) -> Element {
render! {
Router { }
Router::<Route> { }
}
}
@ -70,7 +70,7 @@ fn Index(cx: Scope) -> Element {
fn Blog(cx: Scope) -> Element {
render! {
h1 { "Blog" }
Outlet { }
Outlet::<Route> { }
}
}

View file

@ -193,7 +193,7 @@ enum Route {
fn RenderPath(cx: Scope, path: Route) -> Element {
let path = path.clone();
render! {
Router {
Router::<Route> {
config: || RouterConfig::default().history(MemoryHistory::with_initial_path(path))
}
}

View file

@ -14,7 +14,7 @@ fn main() {
fn root(cx: Scope) -> Element {
render! {
Router {}
Router::<Route> {}
}
}
@ -27,7 +27,7 @@ fn UserFrame(cx: Scope, user_id: usize) -> Element {
div {
background_color: "rgba(0,0,0,50%)",
"children:"
Outlet {}
Outlet::<Route> {}
}
}
}
@ -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::<Route>::External("https://www.google.com".to_string())); },
"google link"
}
p { "Site Map" }

View file

@ -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<R: Routable + Clone>(cx: Scope) -> Element {
let router = use_generic_router::<R>(cx);
pub fn FailureExternalNavigation(cx: Scope) -> Element {
let router = use_router(cx);
render! {
h1 { "External Navigation Failure!" }

View file

@ -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::<Route> {}
/// }
/// }
///
@ -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::<R>(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::<Route> {}
/// }
/// }
///
@ -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::<R>(cx) {
let router = match use_router_internal(cx) {
Some(r) => r,
#[allow(unreachable_code)]
None => {

View file

@ -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<dyn AnyDisplay>),
}
impl<R: Routable> From<R> for IntoRoutable {
fn from(value: R) -> Self {
IntoRoutable::Route(Box::new(value))
}
}
impl<R: Routable> From<NavigationTarget<R>> for IntoRoutable {
fn from(value: NavigationTarget<R>) -> Self {
match value {
NavigationTarget::Internal(route) => IntoRoutable::Route(Box::new(route)),
NavigationTarget::External(url) => IntoRoutable::FromStr(url),
}
}
}
impl From<String> 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<R>,
pub to: IntoRoutable,
}
impl<R: Routable> 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<R: Routable> 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<R: Routable> Debug for GenericLinkProps<'_, R> {
///
/// fn App(cx: Scope) -> Element {
/// render! {
/// Router {}
/// Router::<Route> {}
/// }
/// }
///
@ -122,8 +163,8 @@ impl<R: Routable> 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::<R>(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<Box<dyn Any>> = 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<Box<dyn Any>> = 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 {

View file

@ -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::<Route> {} // The content of child routes will be rendered here
/// }
/// }
///
@ -57,7 +57,7 @@ use dioxus::prelude::*;
///
/// # fn App(cx: Scope) -> Element {
/// # render! {
/// # Router {
/// # Router::<Route> {
/// # 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), "<h1>App</h1><p>Child</p>");
/// ```
pub fn GenericOutlet<R: Routable + Clone>(cx: Scope) -> Element {
pub fn Outlet<R: Routable + Clone>(cx: Scope) -> Element {
OutletContext::<R>::render(cx)
}

View file

@ -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<R: Routable> {
#[allow(clippy::type_complexity)]
config: RefCell<Option<Box<dyn FnOnce() -> RouterConfig<R>>>>,
@ -43,9 +39,9 @@ impl<R: Routable, F: FnOnce() -> RouterConfig<R> + 'static> From<F> for RouterCo
}
#[cfg(feature = "serde")]
/// The props for [`GenericRouter`].
/// The props for [`Router`].
#[derive(Props)]
pub struct GenericRouterProps<R: Routable>
pub struct RouterProps<R: Routable>
where
<R as FromStr>::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<R: Routable>
pub struct RouterProps<R: Routable>
where
<R as FromStr>::Err: std::fmt::Display,
{
@ -66,7 +62,7 @@ where
}
#[cfg(not(feature = "serde"))]
impl<R: Routable> Default for GenericRouterProps<R>
impl<R: Routable> Default for RouterProps<R>
where
<R as FromStr>::Err: std::fmt::Display,
{
@ -78,7 +74,7 @@ where
}
#[cfg(feature = "serde")]
impl<R: Routable> Default for GenericRouterProps<R>
impl<R: Routable> Default for RouterProps<R>
where
<R as FromStr>::Err: std::fmt::Display,
R: serde::Serialize + serde::de::DeserializeOwned,
@ -91,7 +87,7 @@ where
}
#[cfg(not(feature = "serde"))]
impl<R: Routable> PartialEq for GenericRouterProps<R>
impl<R: Routable> PartialEq for RouterProps<R>
where
<R as FromStr>::Err: std::fmt::Display,
{
@ -102,7 +98,7 @@ where
}
#[cfg(feature = "serde")]
impl<R: Routable> PartialEq for GenericRouterProps<R>
impl<R: Routable> PartialEq for RouterProps<R>
where
<R as FromStr>::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<R: Routable + Clone>(cx: Scope<GenericRouterProps<R>>) -> Element
pub fn Router<R: Routable + Clone>(cx: Scope<RouterProps<R>>) -> Element
where
<R as FromStr>::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::<R> {}
Outlet::<R> {}
}
}
#[cfg(feature = "serde")]
/// A component that renders the current route.
pub fn GenericRouter<R: Routable + Clone>(cx: Scope<GenericRouterProps<R>>) -> Element
pub fn Router<R: Routable + Clone>(cx: Scope<RouterProps<R>>) -> Element
where
<R as FromStr>::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::<R> {}
Outlet::<R> {}
}
}

View file

@ -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<R: Routable>(pub(crate) GenericRouterContext<R>);
pub struct GenericNavigator(pub(crate) RouterContext);
impl<R: Routable> GenericNavigator<R> {
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<R: Routable> GenericNavigator<R> {
/// Push a new location.
///
/// The previous location will be available to go back to.
pub fn push(
pub fn push<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
@ -44,7 +44,7 @@ impl<R: Routable> GenericNavigator<R> {
/// Replace the current location.
///
/// The previous location will **not** be available to go back to.
pub fn replace(
pub fn replace<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {

View file

@ -31,7 +31,7 @@ impl<R> OutletContext<R> {
where
R: Routable + Clone,
{
let router = use_router_internal::<R>(cx)
let router = use_router_internal(cx)
.as_ref()
.expect("Outlet must be inside of a router");
let outlet: &OutletContext<R> = use_outlet_context(cx);
@ -51,6 +51,6 @@ impl<R> OutletContext<R> {
}
}
router.current().render(cx, current_level)
router.current::<R>().render(cx, current_level)
}
}

View file

@ -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<R> =
Arc<dyn Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>>>;
pub(crate) type RoutingCallback<R> = Arc<dyn Fn(LinkContext<R>) -> Option<NavigationTarget<R>>>;
struct MutableRouterState<R>
where
R: Routable,
{
struct MutableRouterState {
/// The current prefix.
prefix: Option<String>,
history: Box<dyn HistoryProvider<R>>,
history: Box<dyn AnyHistoryProvider>,
unresolved_error: Option<ExternalNavigationFailure>,
}
/// A collection of router data that manages all routing functionality.
pub struct GenericRouterContext<R>
where
R: Routable,
{
state: Arc<RwLock<MutableRouterState<R>>>,
#[derive(Clone)]
pub struct RouterContext {
state: Arc<RwLock<MutableRouterState>>,
subscribers: Arc<RwLock<HashSet<ScopeId>>>,
subscriber_update: Arc<dyn Fn(ScopeId)>,
routing_callback: Option<RoutingCallback<R>>,
routing_callback: Option<Arc<dyn Fn(RouterContext) -> Option<NavigationTarget<Box<dyn Any>>>>>,
failure_external_navigation: fn(Scope) -> Element,
}
impl<R: Routable> Clone for GenericRouterContext<R> {
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<R> GenericRouterContext<R>
where
R: Routable,
{
pub(crate) fn new(
impl RouterContext {
pub(crate) fn new<R: Routable + 'static>(
mut cfg: RouterConfig<R>,
mark_dirty: Arc<dyn Fn(ScopeId) + Sync + Send>,
) -> 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<dyn Any>)
}
NavigationTarget::External(s) => NavigationTarget::External(s),
})
})
as Arc<dyn Fn(RouterContext) -> Option<NavigationTarget<Box<dyn Any>>>>
}),
failure_external_navigation: cfg.failure_external_navigation,
};
@ -100,6 +94,11 @@ where
myself
}
pub(crate) fn route_from_str(&self, route: &str) -> Result<Box<dyn Any>, 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<NavigationTarget<R>>,
target: NavigationTarget<Box<dyn Any>>,
) -> Option<ExternalNavigationFailure> {
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<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
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<R: Routable>(
&self,
target: impl Into<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
@ -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<R: Routable>(&self) -> R {
self.state
.read()
.unwrap()
.history
.current_route()
.downcast::<R>()
.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<MutableRouterState<R>> {
fn state_mut(&self) -> RwLockWriteGuard<MutableRouterState> {
self.state.write().unwrap()
}
@ -254,3 +281,87 @@ where
None
}
}
pub struct LinkContext<R> {
inner: RouterContext,
_marker: std::marker::PhantomData<R>,
}
impl<R> LinkContext<R>
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<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
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<NavigationTarget<R>>,
) -> Option<ExternalNavigationFailure> {
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<String> {
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()
}
}

View file

@ -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<R: Routable> {
#[allow(unused_variables)]
fn updater(&mut self, callback: Arc<dyn Fn() + Send + Sync>) {}
}
/// 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<T: Any + Clone>(self: Box<dyn AnyDisplay>) -> Option<T> {
self.as_any().downcast_ref::<T>().cloned()
}
}
impl<T: Display + Any> AnyDisplay for T {
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) trait AnyHistoryProvider {
#[must_use]
fn parse_route(&self, route: &str) -> Result<Box<dyn Any>, String>;
#[must_use]
fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool;
#[must_use]
fn current_route(&self) -> Box<dyn AnyDisplay>;
#[must_use]
fn current_prefix(&self) -> Option<String> {
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<dyn Any>);
fn replace(&mut self, path: Box<dyn Any>);
#[allow(unused_variables)]
fn external(&mut self, url: String) -> bool {
false
}
#[allow(unused_variables)]
fn updater(&mut self, callback: Arc<dyn Fn() + Send + Sync>) {}
}
pub(crate) struct AnyHistoryProviderImplWrapper<R, H> {
inner: H,
_marker: std::marker::PhantomData<R>,
}
impl<R, H> AnyHistoryProviderImplWrapper<R, H> {
pub fn new(inner: H) -> Self {
Self {
inner,
_marker: std::marker::PhantomData,
}
}
}
impl<R, H: Default> Default for AnyHistoryProviderImplWrapper<R, H> {
fn default() -> Self {
Self::new(H::default())
}
}
impl<R, H> AnyHistoryProvider for AnyHistoryProviderImplWrapper<R, H>
where
R: Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
H: HistoryProvider<R>,
{
fn parse_route(&self, route: &str) -> Result<Box<dyn Any>, String> {
R::from_str(route)
.map_err(|err| err.to_string())
.map(|route| Box::new(route) as Box<dyn Any>)
}
fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool {
type_id == &std::any::TypeId::of::<R>()
}
fn current_route(&self) -> Box<dyn AnyDisplay> {
let route = self.inner.current_route();
println!("current_route {route}");
Box::new(route)
}
fn current_prefix(&self) -> Option<String> {
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<dyn Any>) {
self.inner.push(*route.downcast().unwrap())
}
fn replace(&mut self, path: Box<dyn Any>) {
self.inner.replace(*path.downcast().unwrap())
}
fn external(&mut self, url: String) -> bool {
self.inner.external(url)
}
fn updater(&mut self, callback: Arc<dyn Fn() + Send + Sync>) {
self.inner.updater(callback)
}
}

View file

@ -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::<Route> {}
/// }
/// }
///
@ -50,10 +47,10 @@ use crate::{
/// # let mut vdom = VirtualDom::new(App);
/// # let _ = vdom.rebuild();
/// ```
pub fn use_generic_navigator<R: Routable + Clone>(cx: &ScopeState) -> &GenericNavigator<R> {
pub fn use_navigator(cx: &ScopeState) -> &GenericNavigator {
&*cx.use_hook(|| {
let router = cx
.consume_context::<GenericRouterContext<R>>()
.consume_context::<RouterContext>()
.expect("Must be called in a descendant of a Router component");
GenericNavigator(router)

View file

@ -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::<Route> {}
/// }
/// }
///
@ -45,7 +45,7 @@ use crate::utils::use_router_internal::use_router_internal;
/// # let _ = vdom.rebuild();
/// # assert_eq!(dioxus_ssr::render(&vdom), "<h1>App</h1><h2>Current Path</h2><p>/</p>")
/// ```
pub fn use_generic_route<R: Routable + Clone>(cx: &ScopeState) -> Option<R> {
pub fn use_route<R: Routable + Clone>(cx: &ScopeState) -> Option<R> {
match use_router_internal(cx) {
Some(r) => Some(r.current()),
None => {

View file

@ -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<R: Routable + Clone>(cx: &ScopeState) -> &GenericRouterContext<R> {
pub fn use_router(cx: &ScopeState) -> &RouterContext {
use_router_internal(cx)
.as_ref()
.expect("use_route must have access to a router")

View file

@ -95,7 +95,7 @@ where
{
let path = path.clone();
render! {
GenericRouter::<R> {
Link::<R> {
config: || RouterConfig::default().history(MemoryHistory::with_initial_path(path))
}
}

View file

@ -11,7 +11,7 @@ use crate::routable::Routable;
/// A target for the router to navigate to.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum NavigationTarget<R: Routable> {
pub enum NavigationTarget<R> {
/// An internal path that the router can navigate to by itself.
///
/// ```rust

View file

@ -26,7 +26,7 @@ use crate::prelude::*;
/// ```
pub struct RouterConfig<R: Routable> {
pub(crate) failure_external_navigation: fn(Scope) -> Element,
pub(crate) history: Option<Box<dyn HistoryProvider<R>>>,
pub(crate) history: Option<Box<dyn AnyHistoryProvider>>,
pub(crate) on_update: Option<RoutingCallback<R>>,
}
@ -69,7 +69,7 @@ where
{
fn default() -> Self {
Self {
failure_external_navigation: FailureExternalNavigation::<R>,
failure_external_navigation: FailureExternalNavigation,
history: None,
on_update: None,
}
@ -81,18 +81,22 @@ impl<R: Routable + Clone> RouterConfig<R>
where
<R as std::str::FromStr>::Err: std::fmt::Display,
{
pub(crate) fn take_history(&mut self) -> Box<dyn HistoryProvider<R>> {
pub(crate) fn take_history(&mut self) -> Box<dyn AnyHistoryProvider> {
self.history.take().unwrap_or_else(|| {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
let history = Box::<WebHistory<R>>::default();
let history = Box::<AnyHistoryProviderImplWrapper<R, WebHistory<R>>>::default();
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
let history = Box::<MemoryHistory<R>>::default();
let history = Box::<AnyHistoryProviderImplWrapper<R, MemoryHistory<R>>>::default();
history
})
}
}
impl<R: Routable> RouterConfig<R> {
impl<R> RouterConfig<R>
where
R: Routable,
<R as std::str::FromStr>::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<R: Routable> RouterConfig<R> {
/// Defaults to [`None`].
pub fn on_update(
self,
callback: impl Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>> + 'static,
callback: impl Fn(LinkContext<R>) -> Option<NavigationTarget<R>> + 'static,
) -> Self {
Self {
on_update: Some(Arc::new(callback)),
@ -121,7 +125,7 @@ impl<R: Routable> RouterConfig<R> {
/// Defaults to a default [`MemoryHistory`].
pub fn history(self, history: impl HistoryProvider<R> + 'static) -> Self {
Self {
history: Some(Box::new(history)),
history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))),
..self
}
}

View file

@ -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<R: Routable>(
cx: &ScopeState,
) -> &Option<GenericRouterContext<R>> {
pub(crate) fn use_router_internal(cx: &ScopeState) -> &Option<RouterContext> {
let inner = cx.use_hook(|| {
let router = cx.consume_context::<GenericRouterContext<R>>()?;
let router = cx.consume_context::<RouterContext>()?;
let id = cx.scope_id();
router.subscribe(id);
@ -24,12 +22,12 @@ pub(crate) fn use_router_internal<R: Routable>(
cx.use_hook(|| inner.as_ref().map(|s| s.router.clone()))
}
struct Subscription<R: Routable> {
router: GenericRouterContext<R>,
struct Subscription {
router: RouterContext,
id: ScopeId,
}
impl<R: Routable> Drop for Subscription<R> {
impl Drop for Subscription {
fn drop(&mut self) {
self.router.unsubscribe(self.id);
}

View file

@ -33,7 +33,7 @@ where
{
render! {
h1 { "App" }
GenericRouter::<R> {
Router::<R> {
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"
}

View file

@ -37,7 +37,7 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
fn App(cx: Scope<AppProps>) -> Element {
render! {
h1 { "App" }
Router {
Router::<Route> {
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<String>) -> VirtualDom {
fn Fixed(cx: Scope) -> Element {
render! {
h2 { "Fixed" }
Outlet { }
Outlet::<Route> { }
}
}
@ -79,7 +79,7 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
fn Parameter(cx: Scope, id: u8) -> Element {
render! {
h2 { "Parameter {id}" }
Outlet { }
Outlet::<Route> { }
}
}