1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
use crate::contexts::router::RoutingCallback;
use crate::history::HistoryProvider;
use crate::prelude::*;
use crate::routable::Routable;
use dioxus_lib::prelude::*;
use std::sync::Arc;
/// Global configuration options for the router.
///
/// This implements [`Default`] and follows the builder pattern, so you can use it like this:
/// ```rust,no_run
/// # use dioxus_router::prelude::*;
/// # use dioxus::prelude::*;
/// # #[component]
/// # fn Index() -> Element {
/// # None
/// # }
/// #[derive(Clone, Routable)]
/// enum Route {
/// #[route("/")]
/// Index {},
/// }
/// let cfg = RouterConfig::default().history(WebHistory::<Route>::default());
/// ```
pub struct RouterConfig<R: Routable> {
pub(crate) failure_external_navigation: fn() -> Element,
pub(crate) history: Option<Box<dyn AnyHistoryProvider>>,
pub(crate) on_update: Option<RoutingCallback<R>>,
pub(crate) initial_route: Option<R>,
}
impl<R: Routable + Clone> Default for RouterConfig<R>
where
<R as std::str::FromStr>::Err: std::fmt::Display,
{
fn default() -> Self {
Self {
failure_external_navigation: FailureExternalNavigation,
history: None,
on_update: None,
initial_route: None,
}
}
}
#[cfg(not(feature = "serde"))]
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 AnyHistoryProvider> {
#[allow(unused)]
let initial_route = self.initial_route.clone().unwrap_or("/".parse().unwrap_or_else(|err|
panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err)
));
self.history
.take()
.unwrap_or_else(|| default_history(initial_route))
}
}
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
/// updated.
///
/// If the callback returns a [`NavigationTarget`] the router will replace the current location
/// with it. If no navigation failure was triggered, the router will then updated dependent
/// components and hooks.
///
/// The callback is called no more than once per rerouting. It will not be called if a
/// navigation failure occurs.
///
/// Defaults to [`None`].
pub fn on_update(
self,
callback: impl Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>> + 'static,
) -> Self {
Self {
on_update: Some(Arc::new(callback)),
..self
}
}
/// The [`HistoryProvider`] the router should use.
///
/// Defaults to a different history provider depending on the target platform.
pub fn history(self, history: impl HistoryProvider<R> + 'static) -> Self {
Self {
history: Some(Box::new(AnyHistoryProviderImplWrapper::new(history))),
..self
}
}
/// The initial route the router should use if no history provider is set.
pub fn initial_route(self, route: R) -> Self {
Self {
initial_route: Some(route),
..self
}
}
/// A component to render when an external navigation fails.
///
/// Defaults to a router-internal component called [`FailureExternalNavigation`]
pub fn failure_external_navigation(self, component: fn() -> Element) -> Self {
Self {
failure_external_navigation: component,
..self
}
}
}
/// Get the default history provider for the current platform.
#[allow(unreachable_code, unused)]
fn default_history<R: Routable + Clone>(initial_route: R) -> Box<dyn AnyHistoryProvider>
where
<R as std::str::FromStr>::Err: std::fmt::Display,
{
// If we're on the web and have wasm, use the web history provider
#[cfg(all(target_arch = "wasm32", feature = "web"))]
return Box::new(AnyHistoryProviderImplWrapper::new(
WebHistory::<R>::default(),
));
// If we're using fullstack and server side rendering, use the memory history provider
#[cfg(all(feature = "fullstack", feature = "ssr"))]
return Box::new(AnyHistoryProviderImplWrapper::new(
MemoryHistory::<R>::with_initial_path(
dioxus_fullstack::prelude::server_context()
.request_parts_blocking()
.uri
.to_string()
.parse()
.unwrap_or_else(|err| {
tracing::error!("Failed to parse uri: {}", err);
"/".parse().unwrap_or_else(|err| {
panic!("Failed to parse uri: {}", err);
})
}),
),
));
// If liveview is enabled, use the liveview history provider
#[cfg(feature = "liveview")]
return Box::new(AnyHistoryProviderImplWrapper::new(
LiveviewHistory::new_with_initial_path(initial_route),
));
// If none of the above, use the memory history provider, which is a decent enough fallback
// Eventually we want to integrate with the mobile history provider, and other platform providers
Box::new(AnyHistoryProviderImplWrapper::new(
MemoryHistory::with_initial_path(initial_route),
))
}