123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- use crate::ParsedRoute;
- use crate::{cfg::RouterCfg, RouteEvent, RouterCore};
- use dioxus_core as dioxus;
- use dioxus_core::prelude::*;
- use dioxus_core_macro::*;
- use dioxus_html as dioxus_elements;
- use futures_util::stream::StreamExt;
- use std::sync::Arc;
- /// The props for the [`Router`](fn.Router.html) component.
- #[derive(Props)]
- pub struct RouterProps<'a> {
- /// The routes and elements that should be rendered when the path matches.
- ///
- /// If elements are not contained within Routes, the will be rendered
- /// regardless of the path.
- pub children: Element<'a>,
- /// The URL to point at
- ///
- /// This will be used to trim any latent segments from the URL when your app is
- /// not deployed to the root of the domain.
- pub base_url: Option<&'a str>,
- /// Hook into the router when the route is changed.
- ///
- /// This lets you easily implement redirects
- #[props(default)]
- pub onchange: EventHandler<'a, Arc<RouterCore>>,
- /// Set the active class of all Link components contained in this router.
- ///
- /// This is useful if you don't want to repeat the same `active_class` prop value in every Link.
- /// By default set to `"active"`.
- pub active_class: Option<&'a str>,
- }
- /// A component that conditionally renders children based on the current location of the app.
- ///
- /// Uses BrowserRouter in the browser and HashRouter everywhere else.
- ///
- /// Will fallback to HashRouter is BrowserRouter is not available, or through configuration.
- #[allow(non_snake_case)]
- pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
- let svc = cx.use_hook(|_| {
- let (tx, mut rx) = futures_channel::mpsc::unbounded::<RouteEvent>();
- let svc = RouterCore::new(
- tx,
- RouterCfg {
- base_url: cx.props.base_url.map(|s| s.to_string()),
- active_class: cx.props.active_class.map(|s| s.to_string()),
- },
- );
- cx.spawn({
- let svc = svc.clone();
- let regen_route = cx.schedule_update_any();
- let router_id = cx.scope_id();
- async move {
- while let Some(msg) = rx.next().await {
- match msg {
- RouteEvent::Push {
- route,
- serialized_state,
- title,
- } => {
- let new_route = Arc::new(ParsedRoute {
- url: svc.current_location().url.join(&route).ok().unwrap(),
- title,
- serialized_state,
- });
- svc.history.push(&new_route);
- svc.stack.borrow_mut().push(new_route);
- }
- RouteEvent::Replace {
- route,
- title,
- serialized_state,
- } => {
- let new_route = Arc::new(ParsedRoute {
- url: svc.current_location().url.join(&route).ok().unwrap(),
- title,
- serialized_state,
- });
- svc.history.replace(&new_route);
- *svc.stack.borrow_mut().last_mut().unwrap() = new_route;
- }
- RouteEvent::Pop => {
- let mut stack = svc.stack.borrow_mut();
- if stack.len() == 1 {
- continue;
- }
- stack.pop();
- }
- }
- svc.route_found.set(None);
- regen_route(router_id);
- for listener in svc.onchange_listeners.borrow().iter() {
- regen_route(*listener);
- }
- for route in svc.ordering.borrow().iter().rev() {
- regen_route(*route);
- }
- }
- }
- });
- cx.provide_context(svc)
- });
- // next time we run the rout_found will be filled
- if svc.route_found.get().is_none() {
- cx.props.onchange.call(svc.clone());
- }
- cx.render(rsx!(&cx.props.children))
- }
|