fix some issues with animated routing (#889)

This commit is contained in:
Greg Johnston 2023-04-21 15:33:14 -04:00 committed by GitHub
parent 2001bd808f
commit f919127a7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 43 deletions

View file

@ -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 {

View file

@ -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(&current, 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(&current, is_back.get_untracked());
*current_state = next;
});
}
}
};
view! { cx,

View file

@ -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:#?}");
}

View file

@ -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(&current, 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(&current, is_back.get_untracked());
*current_state = next;
})
}
}
})
.child(move || root.get())
.into_view(cx)

View file

@ -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,
}
}
}

View file

@ -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

View file

@ -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)
}
}