diff --git a/packages/router-core/src/outlet.rs b/packages/router-core/src/outlet.rs index f2dc99e1c..2008fdefc 100644 --- a/packages/router-core/src/outlet.rs +++ b/packages/router-core/src/outlet.rs @@ -20,18 +20,18 @@ impl OutletData { /// # use dioxus_router_core::{Name, OutletData}; /// let mut d = OutletData::default(); /// let (m, a, n); - /// (m, d) = d.next(None); - /// (a, d) = d.next(None); - /// (n, d) = d.next(Some(Name::of::())); + /// (m, d) = d.next(&None); + /// (a, d) = d.next(&None); + /// (n, d) = d.next(&Some(Name::of::())); /// /// assert_eq!(m, 0); /// assert_eq!(a, 1); /// assert_eq!(n, 0); /// ``` - pub fn next(&self, name: Option) -> (usize, Self) { + pub fn next(&self, name: &Option) -> (usize, Self) { let mut next = self.clone(); - let depth = next.depth(name.clone()).map(|d| d + 1).unwrap_or(0); + let depth = next.depth(name).map(|d| d + 1).unwrap_or(0); next.set_depth(name, depth); @@ -43,17 +43,17 @@ impl OutletData { /// ```rust /// # use dioxus_router_core::OutletData; /// let mut d = OutletData::default(); - /// let b = d.depth(None); - /// d.set_depth(None, 18); - /// let a = d.depth(None); + /// let b = d.depth(&None); + /// d.set_depth(&None, 18); + /// let a = d.depth(&None); /// /// assert_eq!(b, None); /// assert_eq!(a, Some(18)); /// ``` - pub fn depth(&self, name: Option) -> Option { + pub fn depth(&self, name: &Option) -> Option { match name { None => self.main, - Some(n) => self.named.get(&n).copied(), + Some(n) => self.named.get(n).copied(), } } @@ -62,17 +62,17 @@ impl OutletData { /// ```rust /// # use dioxus_router_core::OutletData; /// let mut d = OutletData::default(); - /// let b = d.depth(None); - /// d.set_depth(None, 18); - /// let a = d.depth(None); + /// let b = d.depth(&None); + /// d.set_depth(&None, 18); + /// let a = d.depth(&None); /// /// assert_eq!(b, None); /// assert_eq!(a, Some(18)); /// ``` - pub fn set_depth(&mut self, name: Option, depth: usize) { + pub fn set_depth(&mut self, name: &Option, depth: usize) { match name { None => self.main = Some(depth), - Some(n) => _ = self.named.insert(n, depth), + Some(n) => _ = self.named.insert(n.clone(), depth), } } } @@ -107,18 +107,18 @@ mod tests { fn depth() { let td = test_data(); - assert_eq!(td.depth(None), Some(18)); - assert_eq!(td.depth(Some(Name::of::())), Some(0)); + assert_eq!(td.depth(&None), Some(18)); + assert_eq!(td.depth(&Some(Name::of::())), Some(0)); - assert_eq!(td.depth(Some(Name::of::())), Some(8)); - assert_eq!(td.depth(Some(Name::of::())), Some(16)); - assert_eq!(td.depth(Some(Name::of::())), Some(32)); - assert_eq!(td.depth(Some(Name::of::())), Some(64)); + assert_eq!(td.depth(&Some(Name::of::())), Some(8)); + assert_eq!(td.depth(&Some(Name::of::())), Some(16)); + assert_eq!(td.depth(&Some(Name::of::())), Some(32)); + assert_eq!(td.depth(&Some(Name::of::())), Some(64)); - assert_eq!(td.depth(Some(Name::of::())), None); - assert_eq!(td.depth(Some(Name::of::())), None); - assert_eq!(td.depth(Some(Name::of::())), None); - assert_eq!(td.depth(Some(Name::of::())), None); + assert_eq!(td.depth(&Some(Name::of::())), None); + assert_eq!(td.depth(&Some(Name::of::())), None); + assert_eq!(td.depth(&Some(Name::of::())), None); + assert_eq!(td.depth(&Some(Name::of::())), None); } #[test] @@ -126,21 +126,21 @@ mod tests { let mut td = test_data(); // set - td.set_depth(None, 0); - td.set_depth(Some(Name::of::()), 1); + td.set_depth(&None, 0); + td.set_depth(&Some(Name::of::()), 1); - td.set_depth(Some(Name::of::()), 2); - td.set_depth(Some(Name::of::()), 4); - td.set_depth(Some(Name::of::()), 8); - td.set_depth(Some(Name::of::()), 16); + td.set_depth(&Some(Name::of::()), 2); + td.set_depth(&Some(Name::of::()), 4); + td.set_depth(&Some(Name::of::()), 8); + td.set_depth(&Some(Name::of::()), 16); - td.set_depth(Some(Name::of::()), 32); - td.set_depth(Some(Name::of::()), 64); - td.set_depth(Some(Name::of::()), 128); - td.set_depth(Some(Name::of::()), 256); + td.set_depth(&Some(Name::of::()), 32); + td.set_depth(&Some(Name::of::()), 64); + td.set_depth(&Some(Name::of::()), 128); + td.set_depth(&Some(Name::of::()), 256); // check - assert_eq!(td.depth(None), Some(0)); + assert_eq!(td.depth(&None), Some(0)); assert_eq!(*td.named.get(&Name::of::()).unwrap(), 1); assert_eq!(*td.named.get(&Name::of::()).unwrap(), 2); @@ -158,43 +158,43 @@ mod tests { fn next() { let td = test_data(); - let (current, next) = td.next(None); + let (current, next) = td.next(&None); assert_eq!(current, 19); - assert_eq!(next.depth(None), Some(19)); + assert_eq!(next.depth(&None), Some(19)); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 1); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 1); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 9); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 9); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 17); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 17); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 33); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 33); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 65); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 65); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 0); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 0); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 0); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 0); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 0); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 0); - let (current, next) = td.next(Some(Name::of::())); + let (current, next) = td.next(&Some(Name::of::())); assert_eq!(current, 0); assert_eq!(*next.named.get(&Name::of::()).unwrap(), 0); } diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 064947908..c2f6c190d 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -16,6 +16,7 @@ dioxus = { path="../dioxus" } dioxus-router-core = { path = "../router-core"} futures-channel = "0.3.25" futures-util = "0.3.25" +log = "0.4.17" # for wasm diff --git a/packages/router/src/components/outlet.rs b/packages/router/src/components/outlet.rs new file mode 100644 index 000000000..be7ebd183 --- /dev/null +++ b/packages/router/src/components/outlet.rs @@ -0,0 +1,57 @@ +use dioxus::prelude::*; +use dioxus_router_core::{Name, OutletData}; +use log::error; + +use crate::utils::use_router_internal::use_router_internal; + +#[derive(Debug, Eq, PartialEq, Props)] +pub struct OutletProps { + pub depth: Option, + pub name: Option, +} + +#[allow(non_snake_case)] +pub fn Outlet(cx: Scope) -> Element { + let OutletProps { depth, name } = cx.props; + + // hook up to router + let router = match use_router_internal(&cx) { + Some(r) => r, + None => { + error!("`Outlet` must have access to a parent router, will be inactive"); + #[cfg(debug_assertions)] + panic!("`Outlet` must have access to a parent router"); + #[cfg(not(debug_assertions))] + return None; + } + }; + let state = loop { + if let Some(state) = router.state.try_read() { + break state; + } + }; + + // do depth calculation and propagation + let depth = cx.use_hook(|| { + let mut context = cx.consume_context::().unwrap_or_default(); + let depth = depth + .or_else(|| context.depth(name)) + .map(|d| d + 1) + .unwrap_or_default(); + context.set_depth(name, depth); + cx.provide_context(context); + depth + }); + + // get content + let X: Component = match name { + None => state.content.get(*depth), + Some(n) => state.named_content.get(n).and_then(|n| n.get(*depth)), + }? + .0 + .clone(); + + render! { + X { } + } +} diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 8cbf51900..a6dc8b866 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -1,4 +1,9 @@ -pub(crate) mod contexts { +pub mod components { + mod outlet; + pub use outlet::*; +} + +mod contexts { pub(crate) mod router; } @@ -10,3 +15,7 @@ pub mod hooks { pub mod prelude { pub use dioxus_router_core::prelude::*; } + +mod utils { + pub(crate) mod use_router_internal; +} diff --git a/packages/router/src/utils/use_router_internal.rs b/packages/router/src/utils/use_router_internal.rs new file mode 100644 index 000000000..2e6ec829b --- /dev/null +++ b/packages/router/src/utils/use_router_internal.rs @@ -0,0 +1,30 @@ +use std::sync::Arc; + +use dioxus::prelude::ScopeState; +use dioxus_router_core::RouterMessage; + +use crate::contexts::router::RouterContext; + +/// A private hook to subscribe to the router. +/// +/// Used to reduce redundancy within other components/hooks. Safe to call multiple times for a +/// single component, but not recommended. Multiple subscriptions will be discarded. +/// +/// # Return values +/// - [`None`], when the current component isn't a descendant of a [`use_router`] component. +/// - Otherwise [`Some`]. +/// +/// [`use_router`]: crate::hooks::use_router +pub(crate) fn use_router_internal<'a>(cx: &'a ScopeState) -> &'a mut Option { + let id = cx.use_hook(|| Arc::new(cx.scope_id())); + + cx.use_hook(|| { + let router = cx.consume_context::()?; + + let _ = router + .sender + .unbounded_send(RouterMessage::Subscribe(id.clone())); + + Some(router) + }) +}