dioxus_router/contexts/outlet.rs
1use dioxus_core::{provide_context, try_consume_context, use_hook, Element, VNode};
2
3use crate::{routable::Routable, utils::use_router_internal::use_router_internal};
4
5/// A context that manages nested routing levels for outlet components.
6///
7/// The outlet context keeps track of the current nesting level of routes and helps
8/// manage the hierarchical structure of nested routes in the application.
9///
10/// # Type Parameters
11///
12/// * `R` - The routable type that implements the routing logic
13#[derive(Clone, Default)]
14pub struct OutletContext<R> {
15 current_level: usize,
16 _marker: std::marker::PhantomData<R>,
17}
18
19impl<R> OutletContext<R> {
20 /// Creates a new outlet context starting at level 0
21 pub fn new() -> Self {
22 Self {
23 current_level: 0,
24 _marker: std::marker::PhantomData,
25 }
26 }
27
28 /// Creates a new outlet context for the next nesting level
29 pub fn next(&self) -> Self {
30 Self {
31 current_level: self.current_level + 1,
32 _marker: std::marker::PhantomData,
33 }
34 }
35
36 /// Returns the current nesting level of this outlet
37 pub fn level(&self) -> usize {
38 self.current_level
39 }
40
41 pub(crate) fn render() -> Element
42 where
43 R: Routable + Clone,
44 {
45 let router = use_router_internal().expect("Outlet must be inside of a router");
46 let outlet: OutletContext<R> = use_outlet_context();
47 let current_level = outlet.level();
48 provide_context(outlet.next());
49
50 if let Some(error) = router.render_error() {
51 return if current_level == 0 {
52 error
53 } else {
54 VNode::empty()
55 };
56 }
57
58 router.current::<R>().render(current_level)
59 }
60}
61
62/// Returns the current outlet context from the component hierarchy.
63///
64/// This hook retrieves the outlet context from the current component scope. If no context is found,
65/// it creates a new context with a default level of 0.
66///
67/// # Type Parameters
68///
69/// * `R` - The routable type that implements the routing logic
70///
71/// # Returns
72///
73/// Returns an [`OutletContext<R>`] containing the current nesting level information.
74///
75/// # Examples
76///
77/// ```rust, no_run
78/// # use dioxus::prelude::*;
79/// # use dioxus_router::use_outlet_context;
80///
81/// # #[derive(Routable,Clone,PartialEq)]
82/// # enum MyRouter {
83/// # #[route("/")]
84/// # MyView
85/// # }
86///
87/// # #[component]
88/// # fn MyView() -> Element {
89/// # rsx!{ div { "My Text" } }
90/// # }
91///
92/// let outlet_ctx = use_outlet_context::<MyRouter>();
93/// println!("Current nesting level: {}", outlet_ctx.level());
94/// ```
95pub fn use_outlet_context<R: Clone + 'static>() -> OutletContext<R> {
96 use_hook(|| try_consume_context().unwrap_or_else(OutletContext::new))
97}