router_cfg.rs 6.5 KB

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