Browse Source

Expose Outlet Context (#3788)

* Expose Outlet Context

* feat(router): expose OutletContext and use_outlet_context for improved accessibility

* Hide OutletContext properties

* Starts from 0 ?

* Clippy fix with Default
Sabin Regmi 3 months ago
parent
commit
6a0ae17d56

+ 3 - 4
packages/router/src/components/router.rs

@@ -47,11 +47,10 @@ where
 
     use_hook(|| {
         provide_router_context(RouterContext::new(props.config.call(())));
+    });
 
-        provide_context(OutletContext::<R> {
-            current_level: 0,
-            _marker: std::marker::PhantomData,
-        });
+    use_hook(|| {
+        provide_context(OutletContext::<R>::new());
     });
 
     rsx! { Outlet::<R> {} }

+ 53 - 23
packages/router/src/contexts/outlet.rs

@@ -2,43 +2,50 @@ use dioxus_lib::prelude::*;
 
 use crate::{routable::Routable, utils::use_router_internal::use_router_internal};
 
-pub(crate) struct OutletContext<R> {
-    pub current_level: usize,
-    pub _marker: std::marker::PhantomData<R>,
+/// A context that manages nested routing levels for outlet components.
+///
+/// The outlet context keeps track of the current nesting level of routes and helps
+/// manage the hierarchical structure of nested routes in the application.
+///
+/// # Type Parameters
+///
+/// * `R` - The routable type that implements the routing logic
+#[derive(Clone, Default)]
+pub struct OutletContext<R> {
+    current_level: usize,
+    _marker: std::marker::PhantomData<R>,
 }
 
-impl<R> Clone for OutletContext<R> {
-    fn clone(&self) -> Self {
-        OutletContext {
-            current_level: self.current_level,
+impl<R> OutletContext<R> {
+    /// Creates a new outlet context starting at level 1
+    pub fn new() -> Self {
+        Self {
+            current_level: 0,
             _marker: std::marker::PhantomData,
         }
     }
-}
 
-pub(crate) fn use_outlet_context<R: 'static>() -> OutletContext<R> {
-    use_hook(|| {
-        try_consume_context().unwrap_or(OutletContext::<R> {
-            current_level: 1,
+    /// Creates a new outlet context for the next nesting level
+    pub fn next(&self) -> Self {
+        Self {
+            current_level: self.current_level + 1,
             _marker: std::marker::PhantomData,
-        })
-    })
-}
+        }
+    }
+
+    /// Returns the current nesting level of this outlet
+    pub fn level(&self) -> usize {
+        self.current_level
+    }
 
-impl<R> OutletContext<R> {
     pub(crate) fn render() -> Element
     where
         R: Routable + Clone,
     {
         let router = use_router_internal().expect("Outlet must be inside of a router");
         let outlet: OutletContext<R> = use_outlet_context();
-        let current_level = outlet.current_level;
-        provide_context({
-            OutletContext::<R> {
-                current_level: current_level + 1,
-                _marker: std::marker::PhantomData,
-            }
-        });
+        let current_level = outlet.level();
+        provide_context(outlet.next());
 
         if let Some(error) = router.render_error() {
             return if current_level == 0 {
@@ -51,3 +58,26 @@ impl<R> OutletContext<R> {
         router.current::<R>().render(current_level)
     }
 }
+
+/// Returns the current outlet context from the component hierarchy.
+///
+/// This hook retrieves the outlet context from the current component scope. If no context is found,
+/// it creates a new context with a default level of 1.
+///
+/// # Type Parameters
+///
+/// * `R` - The routable type that implements the routing logic
+///
+/// # Returns
+///
+/// Returns an [`OutletContext<R>`] containing the current nesting level information.
+///
+/// # Examples
+///
+/// ```rust
+/// let outlet_ctx = use_outlet_context::<MyRouter>();
+/// println!("Current nesting level: {}", outlet_ctx.level());
+/// ```
+pub fn use_outlet_context<R: Clone + 'static>() -> OutletContext<R> {
+    use_hook(|| try_consume_context().unwrap_or_else(OutletContext::new))
+}

+ 1 - 0
packages/router/src/lib.rs

@@ -35,6 +35,7 @@ pub mod components {
 mod contexts {
     pub(crate) mod navigator;
     pub(crate) mod outlet;
+    pub use outlet::{use_outlet_context, OutletContext};
     pub(crate) mod router;
     pub use navigator::*;
     pub(crate) use router::*;