freya_components/
activable_route.rs

1use dioxus::prelude::*;
2use dioxus_router::{
3    hooks::use_route,
4    prelude::Routable,
5};
6use freya_hooks::ActivableRouteContext;
7
8/// Sometimes you might want to know if a route is selected so you can style a specific UI element in a different way,
9/// like a button with a different color.
10/// To avoid cluttering your components with router-specific code you might instead want to wrap your component in an `ActivableRoute`
11/// and inside your component call `use_activable_route`.
12///
13/// This way, your component and all its desdendants will just know whether a route is activated or not, but not which one.
14///
15/// ```rs
16/// Link {
17///     to: Route::Home, // Direction route
18///     ActivableRoute {
19///         route: Route::Home, // Activation route
20///         SidebarItem {
21///             // `SidebarItem` will now appear "activated" when the route is `Route::Home`
22///             // `ActivableRoute` is letting it know whether `Route::Home` is enabled
23///             // or not, without the need to add router-specific logic in `SidebarItem`.
24///             label {
25///                 "Go to Hey ! 👋"
26///             }
27///         },
28///     }
29/// }
30/// ```
31#[allow(non_snake_case)]
32#[component]
33pub fn ActivableRoute<T: Clone + PartialEq + Routable + 'static>(
34    children: Element,
35    route: T,
36    #[props(default = Vec::new())] routes: Vec<T>,
37    #[props(default = false)] exact: bool,
38) -> Element {
39    let current_route = use_route::<T>();
40
41    let is_descendent_route_active = current_route.is_child_of(&route);
42    let is_descendent_routes_active = routes.iter().any(|route| current_route.is_child_of(route));
43    let is_descendent_active =
44        !exact && (is_descendent_route_active || is_descendent_routes_active);
45
46    let is_exact_active = current_route == route || routes.contains(&current_route);
47
48    let is_active = is_descendent_active || is_exact_active;
49
50    let mut ctx = use_context_provider::<ActivableRouteContext>(|| {
51        ActivableRouteContext(Signal::new(is_active))
52    });
53
54    if *ctx.0.peek() != is_active {
55        *ctx.0.write() = is_active;
56    }
57
58    rsx!({ children })
59}