mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
ported Routes
This commit is contained in:
parent
610e38a967
commit
8adf9108c5
1 changed files with 159 additions and 161 deletions
|
@ -16,159 +16,150 @@ use crate::{
|
|||
RouteContext, RouterContext,
|
||||
};
|
||||
|
||||
/// Props for the [Routes] component, which contains route definitions and manages routing.
|
||||
#[derive(TypedBuilder)]
|
||||
pub struct RoutesProps {
|
||||
#[builder(default, setter(strip_option))]
|
||||
base: Option<String>,
|
||||
children: Box<dyn Fn(Scope) -> Fragment>,
|
||||
}
|
||||
|
||||
/// Contains route definitions and manages the actual routing process.
|
||||
///
|
||||
/// You should locate the `<Routes/>` component wherever on the page you want the routes to appear.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Routes(cx: Scope, props: RoutesProps) -> impl IntoView {
|
||||
Component::new("Routes", move |cx| {
|
||||
let router = use_context::<RouterContext>(cx).unwrap_or_else(|| {
|
||||
log::warn!("<Routes/> component should be nested within a <Router/>.");
|
||||
panic!()
|
||||
});
|
||||
|
||||
let mut branches = Vec::new();
|
||||
let children = (props.children)(cx)
|
||||
.as_children()
|
||||
.iter()
|
||||
.filter_map(|child| child.as_transparent().and_then(|t| t.downcast_ref::<RouteDefinition>()))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
create_branches(
|
||||
&children,
|
||||
&props.base.unwrap_or_default(),
|
||||
&mut Vec::new(),
|
||||
&mut branches,
|
||||
);
|
||||
|
||||
// whenever path changes, update matches
|
||||
let matches = create_memo(cx, {
|
||||
let router = router.clone();
|
||||
move |_| get_route_matches(branches.clone(), router.pathname().get())
|
||||
});
|
||||
|
||||
// Rebuild the list of nested routes conservatively, and show the root route here
|
||||
let disposers = RefCell::new(Vec::<ScopeDisposer>::new());
|
||||
|
||||
// iterate over the new matches, reusing old routes when they are the same
|
||||
// and replacing them with new routes when they differ
|
||||
let next: Rc<RefCell<Vec<RouteContext>>> = Default::default();
|
||||
|
||||
let root_equal = Rc::new(Cell::new(true));
|
||||
|
||||
let route_states: Memo<RouterState> = create_memo(cx, {
|
||||
let root_equal = root_equal.clone();
|
||||
move |prev: Option<&RouterState>| {
|
||||
root_equal.set(true);
|
||||
next.borrow_mut().clear();
|
||||
|
||||
let next_matches = matches.get();
|
||||
let prev_matches = prev.as_ref().map(|p| &p.matches);
|
||||
let prev_routes = prev.as_ref().map(|p| &p.routes);
|
||||
|
||||
// are the new route matches the same as the previous route matches so far?
|
||||
let mut equal = prev_matches
|
||||
.map(|prev_matches| next_matches.len() == prev_matches.len())
|
||||
.unwrap_or(false);
|
||||
|
||||
for i in 0..next_matches.len() {
|
||||
let next = next.clone();
|
||||
let prev_match = prev_matches.and_then(|p| p.get(i));
|
||||
let next_match = next_matches.get(i).unwrap();
|
||||
|
||||
match (prev_routes, prev_match) {
|
||||
(Some(prev), Some(prev_match))
|
||||
if next_match.route.key == prev_match.route.key =>
|
||||
{
|
||||
let prev_one = { prev.borrow()[i].clone() };
|
||||
if i >= next.borrow().len() {
|
||||
next.borrow_mut().push(prev_one);
|
||||
} else {
|
||||
*(next.borrow_mut().index_mut(i)) = prev_one;
|
||||
}
|
||||
#[component]
|
||||
pub fn Routes(
|
||||
cx: Scope,
|
||||
#[prop(optional)] base: Option<String>,
|
||||
children: Box<dyn Fn(Scope) -> Fragment>,
|
||||
) -> impl IntoView {
|
||||
let router = use_context::<RouterContext>(cx).unwrap_or_else(|| {
|
||||
log::warn!("<Routes/> component should be nested within a <Router/>.");
|
||||
panic!()
|
||||
});
|
||||
|
||||
let mut branches = Vec::new();
|
||||
let children = children(cx)
|
||||
.as_children()
|
||||
.iter()
|
||||
.filter_map(|child| {
|
||||
child
|
||||
.as_transparent()
|
||||
.and_then(|t| t.downcast_ref::<RouteDefinition>())
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
create_branches(
|
||||
&children,
|
||||
&base.unwrap_or_default(),
|
||||
&mut Vec::new(),
|
||||
&mut branches,
|
||||
);
|
||||
|
||||
// whenever path changes, update matches
|
||||
let matches = create_memo(cx, {
|
||||
let router = router.clone();
|
||||
move |_| get_route_matches(branches.clone(), router.pathname().get())
|
||||
});
|
||||
|
||||
// Rebuild the list of nested routes conservatively, and show the root route here
|
||||
let disposers = RefCell::new(Vec::<ScopeDisposer>::new());
|
||||
|
||||
// iterate over the new matches, reusing old routes when they are the same
|
||||
// and replacing them with new routes when they differ
|
||||
let next: Rc<RefCell<Vec<RouteContext>>> = Default::default();
|
||||
|
||||
let root_equal = Rc::new(Cell::new(true));
|
||||
|
||||
let route_states: Memo<RouterState> = create_memo(cx, {
|
||||
let root_equal = root_equal.clone();
|
||||
move |prev: Option<&RouterState>| {
|
||||
root_equal.set(true);
|
||||
next.borrow_mut().clear();
|
||||
|
||||
let next_matches = matches.get();
|
||||
let prev_matches = prev.as_ref().map(|p| &p.matches);
|
||||
let prev_routes = prev.as_ref().map(|p| &p.routes);
|
||||
|
||||
// are the new route matches the same as the previous route matches so far?
|
||||
let mut equal = prev_matches
|
||||
.map(|prev_matches| next_matches.len() == prev_matches.len())
|
||||
.unwrap_or(false);
|
||||
|
||||
for i in 0..next_matches.len() {
|
||||
let next = next.clone();
|
||||
let prev_match = prev_matches.and_then(|p| p.get(i));
|
||||
let next_match = next_matches.get(i).unwrap();
|
||||
|
||||
match (prev_routes, prev_match) {
|
||||
(Some(prev), Some(prev_match))
|
||||
if next_match.route.key == prev_match.route.key =>
|
||||
{
|
||||
let prev_one = { prev.borrow()[i].clone() };
|
||||
if i >= next.borrow().len() {
|
||||
next.borrow_mut().push(prev_one);
|
||||
} else {
|
||||
*(next.borrow_mut().index_mut(i)) = prev_one;
|
||||
}
|
||||
_ => {
|
||||
equal = false;
|
||||
if i == 0 {
|
||||
root_equal.set(false);
|
||||
}
|
||||
|
||||
let disposer = cx.child_scope({
|
||||
}
|
||||
_ => {
|
||||
equal = false;
|
||||
if i == 0 {
|
||||
root_equal.set(false);
|
||||
}
|
||||
|
||||
let disposer = cx.child_scope({
|
||||
let next = next.clone();
|
||||
let router = Rc::clone(&router.inner);
|
||||
move |cx| {
|
||||
let next = next.clone();
|
||||
let router = Rc::clone(&router.inner);
|
||||
move |cx| {
|
||||
let next = next.clone();
|
||||
let next_ctx = RouteContext::new(
|
||||
cx,
|
||||
&RouterContext { inner: router },
|
||||
{
|
||||
let next = next.clone();
|
||||
move || {
|
||||
if let Some(route_states) =
|
||||
use_context::<Memo<RouterState>>(cx)
|
||||
{
|
||||
route_states.with(|route_states| {
|
||||
let routes = route_states.routes.borrow();
|
||||
routes.get(i + 1).cloned()
|
||||
})
|
||||
} else {
|
||||
next.borrow().get(i + 1).cloned()
|
||||
}
|
||||
let next_ctx = RouteContext::new(
|
||||
cx,
|
||||
&RouterContext { inner: router },
|
||||
{
|
||||
let next = next.clone();
|
||||
move || {
|
||||
if let Some(route_states) =
|
||||
use_context::<Memo<RouterState>>(cx)
|
||||
{
|
||||
route_states.with(|route_states| {
|
||||
let routes = route_states.routes.borrow();
|
||||
routes.get(i + 1).cloned()
|
||||
})
|
||||
} else {
|
||||
next.borrow().get(i + 1).cloned()
|
||||
}
|
||||
},
|
||||
move || matches.with(|m| m.get(i).cloned()),
|
||||
);
|
||||
|
||||
if let Some(next_ctx) = next_ctx {
|
||||
if next.borrow().len() > i + 1 {
|
||||
next.borrow_mut()[i] = next_ctx;
|
||||
} else {
|
||||
next.borrow_mut().push(next_ctx);
|
||||
}
|
||||
},
|
||||
move || matches.with(|m| m.get(i).cloned()),
|
||||
);
|
||||
|
||||
if let Some(next_ctx) = next_ctx {
|
||||
if next.borrow().len() > i + 1 {
|
||||
next.borrow_mut()[i] = next_ctx;
|
||||
} else {
|
||||
next.borrow_mut().push(next_ctx);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if disposers.borrow().len() > i + 1 {
|
||||
let mut disposers = disposers.borrow_mut();
|
||||
let old_route_disposer = std::mem::replace(&mut disposers[i], disposer);
|
||||
old_route_disposer.dispose();
|
||||
} else {
|
||||
disposers.borrow_mut().push(disposer);
|
||||
}
|
||||
});
|
||||
|
||||
if disposers.borrow().len() > i + 1 {
|
||||
let mut disposers = disposers.borrow_mut();
|
||||
let old_route_disposer = std::mem::replace(&mut disposers[i], disposer);
|
||||
old_route_disposer.dispose();
|
||||
} else {
|
||||
disposers.borrow_mut().push(disposer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if disposers.borrow().len() > next_matches.len() {
|
||||
let surplus_disposers = disposers.borrow_mut().split_off(next_matches.len() + 1);
|
||||
for disposer in surplus_disposers {
|
||||
disposer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if disposers.borrow().len() > next_matches.len() {
|
||||
let surplus_disposers = disposers.borrow_mut().split_off(next_matches.len() + 1);
|
||||
for disposer in surplus_disposers {
|
||||
disposer.dispose();
|
||||
}
|
||||
|
||||
if let Some(prev) = &prev {
|
||||
if equal {
|
||||
RouterState {
|
||||
matches: next_matches.to_vec(),
|
||||
routes: prev_routes.cloned().unwrap_or_default(),
|
||||
root: prev.root.clone(),
|
||||
}
|
||||
} else {
|
||||
let root = next.borrow().get(0).cloned();
|
||||
RouterState {
|
||||
matches: next_matches.to_vec(),
|
||||
routes: Rc::new(RefCell::new(next.borrow().to_vec())),
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(prev) = &prev {
|
||||
if equal {
|
||||
RouterState {
|
||||
matches: next_matches.to_vec(),
|
||||
routes: prev_routes.cloned().unwrap_or_default(),
|
||||
root: prev.root.clone(),
|
||||
}
|
||||
} else {
|
||||
let root = next.borrow().get(0).cloned();
|
||||
|
@ -178,29 +169,36 @@ pub fn Routes(cx: Scope, props: RoutesProps) -> impl IntoView {
|
|||
root,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let root = next.borrow().get(0).cloned();
|
||||
RouterState {
|
||||
matches: next_matches.to_vec(),
|
||||
routes: Rc::new(RefCell::new(next.borrow().to_vec())),
|
||||
root,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// show the root route
|
||||
let root = create_memo(cx, move |prev| {
|
||||
provide_context(cx, route_states);
|
||||
route_states.with(|state| {
|
||||
let root = state.routes.borrow();
|
||||
let root = root.get(0);
|
||||
if let Some(route) = root {
|
||||
provide_context(cx, route.clone());
|
||||
}
|
||||
|
||||
if prev.is_none() || !root_equal.get() {
|
||||
root.as_ref().map(|route| route.outlet().into_view(cx))
|
||||
} else {
|
||||
prev.cloned().unwrap()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
move || root.get()
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// show the root route
|
||||
let root = create_memo(cx, move |prev| {
|
||||
provide_context(cx, route_states);
|
||||
route_states.with(|state| {
|
||||
let root = state.routes.borrow();
|
||||
let root = root.get(0);
|
||||
if let Some(route) = root {
|
||||
provide_context(cx, route.clone());
|
||||
}
|
||||
|
||||
if prev.is_none() || !root_equal.get() {
|
||||
root.as_ref().map(|route| route.outlet().into_view(cx))
|
||||
} else {
|
||||
prev.cloned().unwrap()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
move || root.get()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
Loading…
Reference in a new issue