lib.rs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. use dioxus_core::prelude::{provide_context, provide_root_context};
  2. use std::{rc::Rc, sync::Arc};
  3. mod memory;
  4. pub use memory::*;
  5. /// Get the history provider for the current platform if the platform doesn't implement a history functionality.
  6. pub fn history() -> Rc<dyn History> {
  7. match dioxus_core::prelude::try_consume_context::<Rc<dyn History>>() {
  8. Some(history) => history,
  9. None => {
  10. tracing::error!("Unable to find a history provider in the renderer. Make sure your renderer supports the Router. Falling back to the in-memory history provider.");
  11. provide_root_context(Rc::new(MemoryHistory::default()))
  12. }
  13. }
  14. }
  15. /// Provide a history context to the current component.
  16. pub fn provide_history_context(history: Rc<dyn History>) {
  17. provide_context(history);
  18. }
  19. pub trait History {
  20. /// Get the path of the current URL.
  21. ///
  22. /// **Must start** with `/`. **Must _not_ contain** the prefix.
  23. ///
  24. /// ```rust
  25. /// # use dioxus::prelude::*;
  26. /// # #[component]
  27. /// # fn Index() -> Element { VNode::empty() }
  28. /// # #[component]
  29. /// # fn OtherPage() -> Element { VNode::empty() }
  30. /// #[derive(Clone, Routable, Debug, PartialEq)]
  31. /// enum Route {
  32. /// #[route("/")]
  33. /// Index {},
  34. /// #[route("/some-other-page")]
  35. /// OtherPage {},
  36. /// }
  37. /// let mut history = dioxus::history::MemoryHistory::default();
  38. /// assert_eq!(history.current_route(), "/");
  39. ///
  40. /// history.push(Route::OtherPage {}.to_string());
  41. /// assert_eq!(history.current_route(), "/some-other-page");
  42. /// ```
  43. #[must_use]
  44. fn current_route(&self) -> String;
  45. /// Get the current path prefix of the URL.
  46. ///
  47. /// Not all [`HistoryProvider`]s need a prefix feature. It is meant for environments where a
  48. /// dioxus-router-core-routed application is not running on `/`. The [`HistoryProvider`] is responsible
  49. /// for removing the prefix from the dioxus-router-core-internal path, and also for adding it back in
  50. /// during navigation. This functions value is only used for creating `href`s (e.g. for SSR or
  51. /// display (but not navigation) in a web app).
  52. fn current_prefix(&self) -> Option<String> {
  53. None
  54. }
  55. /// Check whether there is a previous page to navigate back to.
  56. ///
  57. /// If a [`HistoryProvider`] cannot know this, it should return [`true`].
  58. ///
  59. /// ```rust
  60. /// # use dioxus::prelude::*;
  61. /// # #[component]
  62. /// # fn Index() -> Element { VNode::empty() }
  63. /// # fn Other() -> Element { VNode::empty() }
  64. /// #[derive(Clone, Routable, Debug, PartialEq)]
  65. /// enum Route {
  66. /// #[route("/")]
  67. /// Index {},
  68. /// #[route("/other")]
  69. /// Other {},
  70. /// }
  71. /// let mut history = dioxus::history::MemoryHistory::default();
  72. /// assert_eq!(history.can_go_back(), false);
  73. ///
  74. /// history.push(Route::Other {}.to_string());
  75. /// assert_eq!(history.can_go_back(), true);
  76. /// ```
  77. #[must_use]
  78. fn can_go_back(&self) -> bool {
  79. true
  80. }
  81. /// Go back to a previous page.
  82. ///
  83. /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method
  84. /// might be called, even if `can_go_back` returns [`false`].
  85. ///
  86. /// ```rust
  87. /// # use dioxus::prelude::*;
  88. /// # #[component]
  89. /// # fn Index() -> Element { VNode::empty() }
  90. /// # #[component]
  91. /// # fn OtherPage() -> Element { VNode::empty() }
  92. /// #[derive(Clone, Routable, Debug, PartialEq)]
  93. /// enum Route {
  94. /// #[route("/")]
  95. /// Index {},
  96. /// #[route("/some-other-page")]
  97. /// OtherPage {},
  98. /// }
  99. /// let mut history = dioxus::history::MemoryHistory::default();
  100. /// assert_eq!(history.current_route(), "/");
  101. ///
  102. /// history.go_back();
  103. /// assert_eq!(history.current_route(), "/");
  104. ///
  105. /// history.push(Route::OtherPage {}.to_string());
  106. /// assert_eq!(history.current_route(), "/some-other-page");
  107. ///
  108. /// history.go_back();
  109. /// assert_eq!(history.current_route(), "/");
  110. /// ```
  111. fn go_back(&self);
  112. /// Check whether there is a future page to navigate forward to.
  113. ///
  114. /// If a [`HistoryProvider`] cannot know this, it should return [`true`].
  115. ///
  116. /// ```rust
  117. /// # use dioxus::prelude::*;
  118. /// # #[component]
  119. /// # fn Index() -> Element { VNode::empty() }
  120. /// # #[component]
  121. /// # fn OtherPage() -> Element { VNode::empty() }
  122. /// #[derive(Clone, Routable, Debug, PartialEq)]
  123. /// enum Route {
  124. /// #[route("/")]
  125. /// Index {},
  126. /// #[route("/some-other-page")]
  127. /// OtherPage {},
  128. /// }
  129. /// let mut history = dioxus::history::MemoryHistory::default();
  130. /// assert_eq!(history.can_go_forward(), false);
  131. ///
  132. /// history.push(Route::OtherPage {}.to_string());
  133. /// assert_eq!(history.can_go_forward(), false);
  134. ///
  135. /// history.go_back();
  136. /// assert_eq!(history.can_go_forward(), true);
  137. /// ```
  138. #[must_use]
  139. fn can_go_forward(&self) -> bool {
  140. true
  141. }
  142. /// Go forward to a future page.
  143. ///
  144. /// If a [`HistoryProvider`] cannot go to a previous page, it should do nothing. This method
  145. /// might be called, even if `can_go_forward` returns [`false`].
  146. ///
  147. /// ```rust
  148. /// # use dioxus::prelude::*;
  149. /// # #[component]
  150. /// # fn Index() -> Element { VNode::empty() }
  151. /// # #[component]
  152. /// # fn OtherPage() -> Element { VNode::empty() }
  153. /// #[derive(Clone, Routable, Debug, PartialEq)]
  154. /// enum Route {
  155. /// #[route("/")]
  156. /// Index {},
  157. /// #[route("/some-other-page")]
  158. /// OtherPage {},
  159. /// }
  160. /// let mut history = dioxus::history::MemoryHistory::default();
  161. /// history.push(Route::OtherPage {}.to_string());
  162. /// assert_eq!(history.current_route(), Route::OtherPage {}.to_string());
  163. ///
  164. /// history.go_back();
  165. /// assert_eq!(history.current_route(), Route::Index {}.to_string());
  166. ///
  167. /// history.go_forward();
  168. /// assert_eq!(history.current_route(), Route::OtherPage {}.to_string());
  169. /// ```
  170. fn go_forward(&self);
  171. /// Go to another page.
  172. ///
  173. /// This should do three things:
  174. /// 1. Merge the current URL with the `path` parameter (which may also include a query part).
  175. /// 2. Remove the previous URL to the navigation history.
  176. /// 3. Clear the navigation future.
  177. ///
  178. /// ```rust
  179. /// # use dioxus::prelude::*;
  180. /// # #[component]
  181. /// # fn Index() -> Element { VNode::empty() }
  182. /// # #[component]
  183. /// # fn OtherPage() -> Element { VNode::empty() }
  184. /// #[derive(Clone, Routable, Debug, PartialEq)]
  185. /// enum Route {
  186. /// #[route("/")]
  187. /// Index {},
  188. /// #[route("/some-other-page")]
  189. /// OtherPage {},
  190. /// }
  191. /// let mut history = dioxus::history::MemoryHistory::default();
  192. /// assert_eq!(history.current_route(), Route::Index {}.to_string());
  193. ///
  194. /// history.push(Route::OtherPage {}.to_string());
  195. /// assert_eq!(history.current_route(), Route::OtherPage {}.to_string());
  196. /// assert!(history.can_go_back());
  197. /// ```
  198. fn push(&self, route: String);
  199. /// Replace the current page with another one.
  200. ///
  201. /// This should merge the current URL with the `path` parameter (which may also include a query
  202. /// part). In contrast to the `push` function, the navigation history and future should stay
  203. /// untouched.
  204. ///
  205. /// ```rust
  206. /// # use dioxus::prelude::*;
  207. /// # #[component]
  208. /// # fn Index() -> Element { VNode::empty() }
  209. /// # #[component]
  210. /// # fn OtherPage() -> Element { VNode::empty() }
  211. /// #[derive(Clone, Routable, Debug, PartialEq)]
  212. /// enum Route {
  213. /// #[route("/")]
  214. /// Index {},
  215. /// #[route("/some-other-page")]
  216. /// OtherPage {},
  217. /// }
  218. /// let mut history = dioxus::history::MemoryHistory::default();
  219. /// assert_eq!(history.current_route(), Route::Index {}.to_string());
  220. ///
  221. /// history.replace(Route::OtherPage {}.to_string());
  222. /// assert_eq!(history.current_route(), Route::OtherPage {}.to_string());
  223. /// assert!(!history.can_go_back());
  224. /// ```
  225. fn replace(&self, path: String);
  226. /// Navigate to an external URL.
  227. ///
  228. /// This should navigate to an external URL, which isn't controlled by the router. If a
  229. /// [`HistoryProvider`] cannot do that, it should return [`false`], otherwise [`true`].
  230. ///
  231. /// Returning [`false`] will cause the router to handle the external navigation failure.
  232. #[allow(unused_variables)]
  233. fn external(&self, url: String) -> bool {
  234. false
  235. }
  236. /// Provide the [`HistoryProvider`] with an update callback.
  237. ///
  238. /// Some [`HistoryProvider`]s may receive URL updates from outside the router. When such
  239. /// updates are received, they should call `callback`, which will cause the router to update.
  240. #[allow(unused_variables)]
  241. fn updater(&self, callback: Arc<dyn Fn() + Send + Sync>) {}
  242. /// Whether the router should include the legacy prevent default attribute instead of the new
  243. /// prevent default method. This should only be used by liveview.
  244. fn include_prevent_default(&self) -> bool {
  245. false
  246. }
  247. }