Skip to content

Commit 87e6136

Browse files
committed
work
1 parent 6ca5a21 commit 87e6136

4 files changed

Lines changed: 43 additions & 13 deletions

File tree

packages/frontend/navi/docs/navigation.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -527,10 +527,14 @@ the two, never both: see [route_transitions.md](./route_transitions.md).
527527

528528
Four cases, and they are not a policy to configure but four different facts:
529529

530-
- **Going somewhere new** (a `<Link>`, anything that pushes) lands at the top.
531-
It is an arrival: the offset one had elsewhere means nothing here, and left
532-
alone the new entry would be born holding the previous page's offset — which
533-
the browser would then hand back as if it were this page's own.
530+
- **Going somewhere new** (a `<Link>`, anything that pushes to another path)
531+
lands at the top. It is an arrival: the offset one had elsewhere means nothing
532+
here, and left alone the new entry would be born holding the previous page's
533+
offset — which the browser would then hand back as if it were this page's own.
534+
A push that keeps the path is not one: it stacks an entry over the page the
535+
reader is scrolled in — a [layer](#a-layer-over-the-screen-what-its-address-may-say)
536+
opening, a [state whose values are places](#a-state-whose-values-are-places-history-push)
537+
written — and moves nothing, like a replace.
534538
- **Going back or forward** (the browser's buttons, `navBack()`,
535539
`history.back()`) lands where that page was left. navi keeps the position and
536540
puts it back once the page is really rendered, which is what the browser

packages/frontend/navi/src/nav/browser_integration/scroll_restoration.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,26 @@ export const restoreScrollPosition = (url) => {
151151
// the one before it — and that borrowed offset is what is then remembered FOR
152152
// it, and handed back on the way forward.
153153
//
154+
// Arrived at, which a push does not always mean. A push that keeps the pathname
155+
// stacks an entry over the document the reader is scrolled in — a layer opened
156+
// over the screen, a param whose values are places one came from (see
157+
// navigation.md) — and the page under the new address is the one they never
158+
// left. The path names the page; the search says what is drawn on or over it.
159+
// So `from`, the url being left, tells an arrival from the same place said
160+
// differently, and only the first is moved.
161+
//
154162
// The document, because the document is the scrollport in the common case. An
155163
// app that scrolls an element of its own scrolls it itself.
156-
export const startAtTop = (url) => {
164+
export const startAtTop = (url, { from } = {}) => {
165+
const urlObject = new URL(url, window.location.href);
157166
// A fragment names where to land, and the browser is the one that finds it.
158-
if (new URL(url, window.location.href).hash) {
167+
if (urlObject.hash) {
168+
return;
169+
}
170+
if (
171+
from !== undefined &&
172+
new URL(from, window.location.href).pathname === urlObject.pathname
173+
) {
159174
return;
160175
}
161176
window.scrollTo({ top: 0, left: 0, behavior: "instant" });

packages/frontend/navi/src/nav/browser_integration/via_history.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ export const setupBrowserIntegrationViaHistory = ({
180180
};
181181

182182
const applyRoutingTask = (url, options) => {
183-
const isSameUrl = url === window.location.href;
183+
// Read before the history is written: the url the reader is being taken
184+
// away from, which is what decides where a push lands (see startAtTop).
185+
const urlLeft = window.location.href;
186+
const isSameUrl = url === urlLeft;
184187
const {
185188
reason,
186189
navigationType, // "load", "reload", "replace", "push", "traverse"
@@ -260,11 +263,12 @@ export const setupBrowserIntegrationViaHistory = ({
260263
//
261264
// A replace gets neither: it is the same place said differently — a param
262265
// settling, a state written — and moving the reader for it would throw
263-
// them out of a page they never left. The one replace that IS an arrival
264-
// is a row of tabs travelling, and the row says so for itself (see
266+
// them out of a page they never left. A push that keeps the pathname is
267+
// read the same way, by startAtTop itself. The one replace that IS an
268+
// arrival is a row of tabs travelling, and the row says so for itself (see
265269
// route_travel.jsx).
266270
if (navigationType === "push") {
267-
whenRenderingResumes(() => startAtTop(url));
271+
whenRenderingResumes(() => startAtTop(url, { from: urlLeft }));
268272
} else if (navigationType === "traverse") {
269273
whenRenderingResumes(() => restoreScrollPosition(url));
270274
}

packages/frontend/navi/src/nav/browser_integration/via_navigation.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ export const setupBrowserIntegrationViaNavigation = ({
141141
// Aborting: each run aborts the previous one, and the navigate event's own
142142
// signal (superseded navigation, stop button) aborts the current one.
143143
let abortController = null;
144-
const runRouting = (url, { reason, navigationType, state, abortEvent }) => {
144+
const runRouting = (
145+
url,
146+
{ reason, navigationType, state, abortEvent, urlLeft },
147+
) => {
145148
const redirectionUrl = resolveRouteRedirection(url);
146149
if (redirectionUrl) {
147150
redirectAway(redirectionUrl);
@@ -205,7 +208,7 @@ export const setupBrowserIntegrationViaNavigation = ({
205208
// Same rule, same timing as via_history.js's own, and the same reading of
206209
// a replace: only the row that travels knows one of its own is an arrival.
207210
if (navigationType === "push") {
208-
whenRenderingResumes(() => startAtTop(url));
211+
whenRenderingResumes(() => startAtTop(url, { from: urlLeft }));
209212
} else if (navigationType === "traverse") {
210213
whenRenderingResumes(() => restoreScrollPosition(url));
211214
}
@@ -340,7 +343,10 @@ export const setupBrowserIntegrationViaNavigation = ({
340343
event.sourceElement || (event.info ? event.info.element : undefined),
341344
routeTransition: event.info ? event.info.routeTransition : undefined,
342345
});
343-
const isSameUrl = url === window.location.href;
346+
// The url the reader is being taken away from, read here because the
347+
// commit — and with it the document url — happens before the handler runs.
348+
const urlLeft = window.location.href;
349+
const isSameUrl = url === urlLeft;
344350
event.intercept({
345351
// The browser would scroll at commit time — before the picture of the
346352
// page being left is taken. The shared scroll machinery does it at the
@@ -366,6 +372,7 @@ export const setupBrowserIntegrationViaNavigation = ({
366372
navigationType,
367373
state,
368374
abortEvent: event.signal,
375+
urlLeft,
369376
});
370377
// The handler's promise IS the navigation for the browser (its
371378
// loading UI follows it) — but a routing that fails is displayed by

0 commit comments

Comments
 (0)