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
This commit is contained in:
Sabin Regmi authored and GitHub committed 2025-03-04 12:34:22 -06:00
1 parent f4c59e1188
commit 6a0ae17d56
3 files changed
+62 -32

No files matched your search

+3 -4
View File
@@ -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> {} }
+58 -28
View File
@@ -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>,
}
impl<R> Clone for OutletContext<R> {
fn clone(&self) -> Self {
OutletContext {
current_level: self.current_level,
_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,
_marker: std::marker::PhantomData,
})
})
/// 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> OutletContext<R> {
/// Creates a new outlet context starting at level 1
pub fn new() -> Self {
Self {
current_level: 0,
_marker: std::marker::PhantomData,
}
}
/// 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
}
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
View File
@@ -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::*;