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
use dioxus::prelude::*;
use dioxus_router::{
    hooks::use_route,
    prelude::Routable,
};
use freya_hooks::ActivableRouteContext;
/// Sometimes you might want to know if a route is selected so you can style a specific UI element in a different way,
/// like a button with a different color.
/// To avoid cluttering your components with router-specific code you might instead want to wrap your component in an `ActivableRoute`
/// and inside your component call `use_activable_route`.
///
/// This way, your component and all its desdendants will just know whether a route is activated or not, but not which one.
///
/// ```rs
/// Link {
///     to: Route::Home, // Direction route
///     ActivableRoute {
///         route: Route::Home, // Activation route
///         SidebarItem {
///             // `SidebarItem` will now appear "activated" when the route is `Route::Home`
///             // `ActivableRoute` is letting it know whether `Route::Home` is enabled
///             // or not, without the need to add router-specific logic in `SidebarItem`.
///             label {
///                 "Go to Hey ! 👋"
///             }
///         },
///     }
/// }
/// ```
#[allow(non_snake_case)]
#[component]
pub fn ActivableRoute<T: Clone + PartialEq + Routable + 'static>(
    children: Element,
    route: T,
    #[props(default = Vec::new())] routes: Vec<T>,
    #[props(default = false)] exact: bool,
) -> Element {
    let current_route = use_route::<T>();
    let is_descendent_route_active = current_route.is_child_of(&route);
    let is_descendent_routes_active = routes.iter().any(|route| current_route.is_child_of(route));
    let is_descendent_active =
        !exact && (is_descendent_route_active || is_descendent_routes_active);
    let is_exact_active = current_route == route || routes.contains(¤t_route);
    let is_active = is_descendent_active || is_exact_active;
    let mut ctx = use_context_provider::<ActivableRouteContext>(|| {
        ActivableRouteContext(Signal::new(is_active))
    });
    if *ctx.0.peek() != is_active {
        *ctx.0.write() = is_active;
    }
    rsx!({ children })
}