mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
fix some issues with animated routing (#889)
This commit is contained in:
parent
2001bd808f
commit
f919127a7e
7 changed files with 82 additions and 43 deletions
|
@ -41,11 +41,11 @@ a[aria-current] {
|
|||
}
|
||||
|
||||
.slideIn {
|
||||
animation: 0.125s slideIn forwards;
|
||||
animation: 0.25s slideIn forwards;
|
||||
}
|
||||
|
||||
.slideOut {
|
||||
animation: 0.125s slideOut forwards;
|
||||
animation: 0.25s slideOut forwards;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
|
@ -67,11 +67,11 @@ a[aria-current] {
|
|||
}
|
||||
|
||||
.slideInBack {
|
||||
animation: 0.125s slideInBack forwards;
|
||||
animation: 0.25s slideInBack forwards;
|
||||
}
|
||||
|
||||
.slideOutBack {
|
||||
animation: 0.125s slideOutBack forwards;
|
||||
animation: 0.25s slideOutBack forwards;
|
||||
}
|
||||
|
||||
@keyframes slideInBack {
|
||||
|
|
|
@ -166,14 +166,25 @@ pub fn AnimatedOutlet(
|
|||
animation_class.to_string()
|
||||
}
|
||||
};
|
||||
let node_ref = create_node_ref::<html::Div>(cx);
|
||||
let animationend = move |ev: AnimationEvent| {
|
||||
ev.stop_propagation();
|
||||
let current = current_animation.get();
|
||||
set_animation_state.update(|current_state| {
|
||||
let (next, _) =
|
||||
animation.next_state(¤t, is_back.get_untracked());
|
||||
*current_state = next;
|
||||
});
|
||||
use wasm_bindgen::JsCast;
|
||||
if let Some(target) = ev.target() {
|
||||
let node_ref = node_ref.get();
|
||||
if node_ref.is_none()
|
||||
|| target
|
||||
.unchecked_ref::<web_sys::Node>()
|
||||
.is_same_node(Some(&*node_ref.unwrap()))
|
||||
{
|
||||
ev.stop_propagation();
|
||||
let current = current_animation.get();
|
||||
set_animation_state.update(|current_state| {
|
||||
let (next, _) =
|
||||
animation.next_state(¤t, is_back.get_untracked());
|
||||
*current_state = next;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
view! { cx,
|
||||
|
|
|
@ -55,6 +55,7 @@ pub(crate) struct RouterContextInner {
|
|||
state: ReadSignal<State>,
|
||||
set_state: WriteSignal<State>,
|
||||
pub(crate) is_back: RwSignal<bool>,
|
||||
pub(crate) path_stack: StoredValue<Vec<String>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RouterContextInner {
|
||||
|
@ -68,6 +69,7 @@ impl std::fmt::Debug for RouterContextInner {
|
|||
.field("referrers", &self.referrers)
|
||||
.field("state", &self.state)
|
||||
.field("set_state", &self.set_state)
|
||||
.field("path_stack", &self.path_stack)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +113,6 @@ impl RouterContext {
|
|||
replace: true,
|
||||
scroll: false,
|
||||
state: State(None),
|
||||
back: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +155,10 @@ impl RouterContext {
|
|||
|
||||
let inner = Rc::new(RouterContextInner {
|
||||
base_path: base_path.into_owned(),
|
||||
path_stack: store_value(
|
||||
cx,
|
||||
vec![location.pathname.get_untracked()],
|
||||
),
|
||||
location,
|
||||
base,
|
||||
history: Box::new(history),
|
||||
|
@ -203,7 +208,6 @@ impl RouterContextInner {
|
|||
self: Rc<Self>,
|
||||
to: &str,
|
||||
options: &NavigateOptions,
|
||||
back: bool,
|
||||
) -> Result<(), NavigationError> {
|
||||
let cx = self.cx;
|
||||
let this = Rc::clone(&self);
|
||||
|
@ -231,7 +235,6 @@ impl RouterContextInner {
|
|||
replace: options.replace,
|
||||
scroll: options.scroll,
|
||||
state: self.state.get(),
|
||||
back,
|
||||
});
|
||||
}
|
||||
let len = self.referrers.borrow().len();
|
||||
|
@ -249,13 +252,17 @@ impl RouterContextInner {
|
|||
let next_state = state.clone();
|
||||
move |state| *state = next_state
|
||||
});
|
||||
|
||||
self.path_stack.update_value(|stack| {
|
||||
stack.push(resolved_to.clone())
|
||||
});
|
||||
|
||||
if referrers.borrow().len() == len {
|
||||
this.navigate_end(LocationChange {
|
||||
value: resolved_to,
|
||||
replace: false,
|
||||
scroll: true,
|
||||
state,
|
||||
back,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -280,6 +287,8 @@ impl RouterContextInner {
|
|||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub(crate) fn handle_anchor_click(self: Rc<Self>, ev: web_sys::Event) {
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
let ev = ev.unchecked_into::<web_sys::MouseEvent>();
|
||||
if ev.default_prevented()
|
||||
|| ev.button() != 0
|
||||
|
@ -343,7 +352,7 @@ impl RouterContextInner {
|
|||
leptos_dom::helpers::get_property(a.unchecked_ref(), "state")
|
||||
.ok()
|
||||
.and_then(|value| {
|
||||
if value == wasm_bindgen::JsValue::UNDEFINED {
|
||||
if value == JsValue::UNDEFINED {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
|
@ -365,7 +374,6 @@ impl RouterContextInner {
|
|||
scroll: !a.has_attribute("noscroll"),
|
||||
state: State(state),
|
||||
},
|
||||
false,
|
||||
) {
|
||||
leptos::error!("{e:#?}");
|
||||
}
|
||||
|
|
|
@ -121,7 +121,10 @@ pub fn AnimatedRoutes(
|
|||
create_signal(cx, AnimationState::Finally);
|
||||
let next_route = router.pathname();
|
||||
|
||||
let is_complete = Rc::new(Cell::new(true));
|
||||
let animation_and_route = create_memo(cx, {
|
||||
let is_complete = Rc::clone(&is_complete);
|
||||
|
||||
move |prev: Option<&(AnimationState, String)>| {
|
||||
let animation_state = animation_state.get();
|
||||
let next_route = next_route.get();
|
||||
|
@ -140,7 +143,7 @@ pub fn AnimatedRoutes(
|
|||
let (next_state, can_advance) = animation
|
||||
.next_state(prev_state, is_back.get_untracked());
|
||||
|
||||
if can_advance {
|
||||
if can_advance || !is_complete.get() {
|
||||
(next_state, next_route)
|
||||
} else {
|
||||
(next_state, prev_route.to_owned())
|
||||
|
@ -158,8 +161,10 @@ pub fn AnimatedRoutes(
|
|||
let route_states = route_states(cx, &router, current_route, &root_equal);
|
||||
|
||||
let root = root_route(cx, base_route, route_states, root_equal);
|
||||
let node_ref = create_node_ref::<html::Div>(cx);
|
||||
|
||||
html::div(cx)
|
||||
.node_ref(node_ref)
|
||||
.attr(
|
||||
"class",
|
||||
(cx, move || {
|
||||
|
@ -171,6 +176,7 @@ pub fn AnimatedRoutes(
|
|||
AnimationState::OutroBack => outro_back.unwrap_or_default(),
|
||||
AnimationState::IntroBack => intro_back.unwrap_or_default(),
|
||||
};
|
||||
is_complete.set(animation_class == finally.unwrap_or_default());
|
||||
if let Some(class) = &class {
|
||||
format!("{} {animation_class}", class.get())
|
||||
} else {
|
||||
|
@ -178,13 +184,21 @@ pub fn AnimatedRoutes(
|
|||
}
|
||||
}),
|
||||
)
|
||||
.on(leptos::ev::animationend, move |_| {
|
||||
let current = current_animation.get();
|
||||
set_animation_state.update(|current_state| {
|
||||
let (next, _) =
|
||||
animation.next_state(¤t, is_back.get_untracked());
|
||||
*current_state = next;
|
||||
})
|
||||
.on(leptos::ev::animationend, move |ev| {
|
||||
use wasm_bindgen::JsCast;
|
||||
if let Some(target) = ev.target() {
|
||||
if target
|
||||
.unchecked_ref::<web_sys::Node>()
|
||||
.is_same_node(Some(&*node_ref.get().unwrap()))
|
||||
{
|
||||
let current = current_animation.get();
|
||||
set_animation_state.update(|current_state| {
|
||||
let (next, _) = animation
|
||||
.next_state(¤t, is_back.get_untracked());
|
||||
*current_state = next;
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.child(move || root.get())
|
||||
.into_view(cx)
|
||||
|
|
|
@ -62,8 +62,6 @@ pub struct LocationChange {
|
|||
pub scroll: bool,
|
||||
/// The [`state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) that will be added during navigation.
|
||||
pub state: State,
|
||||
/// Whether the navigation is a “back” navigation.
|
||||
pub back: bool,
|
||||
}
|
||||
|
||||
impl Default for LocationChange {
|
||||
|
@ -73,7 +71,6 @@ impl Default for LocationChange {
|
|||
replace: true,
|
||||
scroll: true,
|
||||
state: Default::default(),
|
||||
back: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ pub trait History {
|
|||
pub struct BrowserIntegration {}
|
||||
|
||||
impl BrowserIntegration {
|
||||
fn current(back: bool) -> LocationChange {
|
||||
fn current() -> LocationChange {
|
||||
let loc = leptos_dom::helpers::location();
|
||||
LocationChange {
|
||||
value: loc.pathname().unwrap_or_default()
|
||||
|
@ -43,8 +43,7 @@ impl BrowserIntegration {
|
|||
+ &loc.hash().unwrap_or_default(),
|
||||
replace: true,
|
||||
scroll: true,
|
||||
state: State(None), // TODO
|
||||
back,
|
||||
state: State(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,14 +52,28 @@ impl History for BrowserIntegration {
|
|||
fn location(&self, cx: Scope) -> ReadSignal<LocationChange> {
|
||||
use crate::{NavigateOptions, RouterContext};
|
||||
|
||||
let (location, set_location) = create_signal(cx, Self::current(false));
|
||||
let (location, set_location) = create_signal(cx, Self::current());
|
||||
|
||||
leptos::window_event_listener_untyped("popstate", move |_| {
|
||||
let router = use_context::<RouterContext>(cx);
|
||||
if let Some(router) = router {
|
||||
let path_stack = router.inner.path_stack;
|
||||
|
||||
let is_back = router.inner.is_back;
|
||||
let change = Self::current(true);
|
||||
is_back.set(true);
|
||||
let change = Self::current();
|
||||
|
||||
let is_navigating_back = path_stack.with_value(|stack| {
|
||||
stack.len() == 1
|
||||
|| stack.get(stack.len() - 2) == Some(&change.value)
|
||||
});
|
||||
if is_navigating_back {
|
||||
path_stack.update_value(|stack| {
|
||||
stack.pop();
|
||||
});
|
||||
}
|
||||
|
||||
is_back.set(is_navigating_back);
|
||||
|
||||
request_animation_frame(move || {
|
||||
is_back.set(false);
|
||||
});
|
||||
|
@ -72,11 +85,10 @@ impl History for BrowserIntegration {
|
|||
scroll: change.scroll,
|
||||
state: change.state,
|
||||
},
|
||||
true,
|
||||
) {
|
||||
leptos::error!("{e:#?}");
|
||||
}
|
||||
set_location.set(Self::current(true));
|
||||
set_location.set(Self::current());
|
||||
} else {
|
||||
leptos::warn!("RouterContext not found");
|
||||
}
|
||||
|
@ -97,12 +109,10 @@ impl History for BrowserIntegration {
|
|||
)
|
||||
.unwrap_throw();
|
||||
} else {
|
||||
// push the "forward direction" marker
|
||||
let state = &loc.state.to_js_value();
|
||||
history
|
||||
.push_state_with_url(
|
||||
&loc.state.to_js_value(),
|
||||
"",
|
||||
Some(&loc.value),
|
||||
)
|
||||
.push_state_with_url(state, "", Some(&loc.value))
|
||||
.unwrap_throw();
|
||||
}
|
||||
// scroll to el
|
||||
|
@ -172,7 +182,6 @@ impl History for ServerIntegration {
|
|||
replace: false,
|
||||
scroll: true,
|
||||
state: State(None),
|
||||
back: false,
|
||||
},
|
||||
)
|
||||
.0
|
||||
|
|
|
@ -81,7 +81,7 @@ pub fn use_navigate(
|
|||
) -> impl Fn(&str, NavigateOptions) -> Result<(), NavigationError> {
|
||||
let router = use_router(cx);
|
||||
move |to, options| {
|
||||
Rc::clone(&router.inner).navigate_from_route(to, &options, false)
|
||||
Rc::clone(&router.inner).navigate_from_route(to, &options)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue