@jsenv/navi 0.29.432 → 0.29.433
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/jsenv_navi.js +85 -49
- package/dist/dev/jsenv_navi.js.map +6 -6
- package/dist/jsenv_navi.js +85 -49
- package/dist/jsenv_navi.js.map +6 -6
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -11109,6 +11109,11 @@ const [publishRouteRender, observeRouteRender] = createPubSub();
|
|
|
11109
11109
|
const STORAGE_KEY = "navi_scroll_positions";
|
|
11110
11110
|
|
|
11111
11111
|
const positionByUrl = new Map();
|
|
11112
|
+
// Where the document stands, as its scroll events last said, whoever it
|
|
11113
|
+
// belongs to: what a url shown without the document moving is given (see
|
|
11114
|
+
// stayInPlace). Null until the restoration is installed — nothing is recorded
|
|
11115
|
+
// before.
|
|
11116
|
+
let documentOffset = null;
|
|
11112
11117
|
// url -> (scroller name -> position). The position is whatever the scroller
|
|
11113
11118
|
// handed in: what it can put itself back on, in its own terms.
|
|
11114
11119
|
const scrollerPositionsByUrl = new Map();
|
|
@@ -11225,30 +11230,25 @@ const installScrollRestoration = () => {
|
|
|
11225
11230
|
}
|
|
11226
11231
|
window.history.scrollRestoration = "manual";
|
|
11227
11232
|
loadStore();
|
|
11233
|
+
documentOffset = { x: window.scrollX, y: window.scrollY };
|
|
11228
11234
|
// Read as it happens rather than when leaving: a traverse changes the url
|
|
11229
11235
|
// before anything here is told, so a position read then would be read for
|
|
11230
11236
|
// the wrong page.
|
|
11231
11237
|
window.addEventListener(
|
|
11232
11238
|
"scroll",
|
|
11233
11239
|
() => {
|
|
11240
|
+
documentOffset = { x: window.scrollX, y: window.scrollY };
|
|
11234
11241
|
if (suspendCount) {
|
|
11235
11242
|
return;
|
|
11236
11243
|
}
|
|
11237
|
-
positionByUrl.set(window.location.href,
|
|
11238
|
-
x: window.scrollX,
|
|
11239
|
-
y: window.scrollY,
|
|
11240
|
-
});
|
|
11244
|
+
positionByUrl.set(window.location.href, documentOffset);
|
|
11241
11245
|
},
|
|
11242
11246
|
{ passive: true },
|
|
11243
11247
|
);
|
|
11244
11248
|
const positionOnLoad = positionByUrl.get(window.location.href);
|
|
11245
11249
|
if (!positionOnLoad) {
|
|
11246
|
-
//
|
|
11247
|
-
|
|
11248
|
-
positionByUrl.set(window.location.href, {
|
|
11249
|
-
x: window.scrollX,
|
|
11250
|
-
y: window.scrollY,
|
|
11251
|
-
});
|
|
11250
|
+
// The first page, where the browser landed it.
|
|
11251
|
+
stayInPlace(window.location.href);
|
|
11252
11252
|
return;
|
|
11253
11253
|
}
|
|
11254
11254
|
// What a reload asks for, now that the browser has been told not to do it.
|
|
@@ -11339,6 +11339,7 @@ const restoreScrollPositionOnReturn = (url) => {
|
|
|
11339
11339
|
// app that scrolls an element of its own scrolls it itself.
|
|
11340
11340
|
const startAtTop = (url, { from } = {}) => {
|
|
11341
11341
|
if (!isArrival(url, { from })) {
|
|
11342
|
+
stayInPlace(url);
|
|
11342
11343
|
return;
|
|
11343
11344
|
}
|
|
11344
11345
|
arrivalCount++;
|
|
@@ -11360,6 +11361,23 @@ const isArrival = (url, { from }) => {
|
|
|
11360
11361
|
return true;
|
|
11361
11362
|
};
|
|
11362
11363
|
|
|
11364
|
+
// A url the document is shown under without moving: a replace, a push that
|
|
11365
|
+
// keeps the page (see startAtTop), the first page loaded. No scroll event says
|
|
11366
|
+
// where it stands, so it is written down here, or a return to it would find
|
|
11367
|
+
// nothing and keep the offset of the page being left. The offset the scroll
|
|
11368
|
+
// events last told, not read again: a read here would lay the page out in the
|
|
11369
|
+
// middle of a routing, once per address a settling param writes.
|
|
11370
|
+
//
|
|
11371
|
+
// Deaf with the recording: a row of tabs replaces the url as it travels, and
|
|
11372
|
+
// the offset belongs to nobody until the row says where its tab lands (see
|
|
11373
|
+
// arriveAtScrollPosition).
|
|
11374
|
+
const stayInPlace = (url) => {
|
|
11375
|
+
if (documentOffset === null || suspendCount) {
|
|
11376
|
+
return;
|
|
11377
|
+
}
|
|
11378
|
+
positionByUrl.set(new URL(url, window.location.href).href, documentOffset);
|
|
11379
|
+
};
|
|
11380
|
+
|
|
11363
11381
|
// The same arrival, for the page's own scrollers. The document is scrolled to
|
|
11364
11382
|
// its top once the page is there; a list opens where it decides to in its
|
|
11365
11383
|
// first render, so what it must not find is dropped before the routing
|
|
@@ -11835,14 +11853,18 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
11835
11853
|
// also waits for the page it returns to: an offset is clamped to whatever
|
|
11836
11854
|
// page the document holds when it is written.
|
|
11837
11855
|
//
|
|
11838
|
-
// A replace
|
|
11839
|
-
// settling, a state written — and moving the reader
|
|
11840
|
-
// them out of a page they never left.
|
|
11841
|
-
//
|
|
11842
|
-
//
|
|
11843
|
-
//
|
|
11856
|
+
// A replace leaves the document where it is: it is the same place said
|
|
11857
|
+
// differently — a param settling, a state written — and moving the reader
|
|
11858
|
+
// for it would throw them out of a page they never left. Where they stay
|
|
11859
|
+
// is written down for the new address (see stayInPlace). A push that keeps
|
|
11860
|
+
// the pathname is read the same way, by startAtTop itself. The one replace
|
|
11861
|
+
// that IS an arrival is a row of tabs travelling, and the row says so for
|
|
11862
|
+
// itself (see route_travel.jsx) — from inside the routing above, which is
|
|
11863
|
+
// why the replace is written down after it.
|
|
11844
11864
|
if (navigationType === "push") {
|
|
11845
11865
|
whenRenderingResumes(() => startAtTop(url, { from: urlLeft }));
|
|
11866
|
+
} else if (navigationType === "replace") {
|
|
11867
|
+
stayInPlace(url);
|
|
11846
11868
|
} else if (navigationType === "traverse") {
|
|
11847
11869
|
restoreScrollPositionOnReturn(url);
|
|
11848
11870
|
}
|
|
@@ -70336,32 +70358,43 @@ document.addEventListener(
|
|
|
70336
70358
|
);
|
|
70337
70359
|
|
|
70338
70360
|
/*
|
|
70339
|
-
* Whether a
|
|
70340
|
-
*
|
|
70341
|
-
*
|
|
70342
|
-
*
|
|
70343
|
-
*
|
|
70344
|
-
*
|
|
70345
|
-
*
|
|
70346
|
-
*
|
|
70347
|
-
* from
|
|
70348
|
-
*
|
|
70349
|
-
*
|
|
70350
|
-
*
|
|
70351
|
-
*
|
|
70352
|
-
*
|
|
70353
|
-
*
|
|
70354
|
-
*
|
|
70355
|
-
*
|
|
70356
|
-
*
|
|
70357
|
-
*
|
|
70358
|
-
*
|
|
70361
|
+
* Whether writing a scroll position now would cost the user the scroll in
|
|
70362
|
+
* progress. A write is an instant scroll, and the browser abandons for it any
|
|
70363
|
+
* scroll it is playing on its own, short of where that one was going: a key
|
|
70364
|
+
* (Home, PageDown, Space), a smooth `scrollTo` or `scrollIntoView`, the status
|
|
70365
|
+
* bar tap on iOS, the fling a finger leaves behind once it lifts. A scroll
|
|
70366
|
+
* following a hand is not abandoned: the wheel's next turn carries on, and on
|
|
70367
|
+
* most engines a finger keeps dragging from wherever the write left it.
|
|
70368
|
+
*
|
|
70369
|
+
* Not on iOS. Its finger places the content from where the drag began, so a
|
|
70370
|
+
* write under it is undone at the finger's next move — or, when the finger
|
|
70371
|
+
* lifts first, lands after the lift and stops the fling dead. And a fling
|
|
70372
|
+
* stops on any write during its momentum, as the status bar's scroll to the
|
|
70373
|
+
* top does (Safari 26.5 on the simulator: flings of 1700px stopped 130 to 340px
|
|
70374
|
+
* after the lift, 3 in 3; a write 50ms before the lift stopped 2 flings in 3).
|
|
70375
|
+
* No finger there is a hand a write could hold to. Chrome flings on through a
|
|
70376
|
+
* write, and loses nothing by not getting one.
|
|
70377
|
+
*
|
|
70378
|
+
* A scroll is followed from its first "scroll" event to its "scrollend", and
|
|
70379
|
+
* is a hand's while a finger is down, or when a wheel turned around the time it
|
|
70380
|
+
* started or since. A finger is down from its touchstart to its touchend,
|
|
70381
|
+
* heard on what it touched: a list removes the items scrolled out of its window
|
|
70382
|
+
* while the finger is still on one of them, and the touchend of a removed
|
|
70383
|
+
* element never reaches the document.
|
|
70384
|
+
*
|
|
70385
|
+
* In a browser without "scrollend", no write is said to interrupt anything: a
|
|
70386
|
+
* scroll would never be seen ending.
|
|
70359
70387
|
*/
|
|
70360
70388
|
|
|
70361
70389
|
// The scroll a wheel turn causes starts in the frame the turn is read, or the
|
|
70362
70390
|
// next one. A turn further from it moved nothing (a wheel at an edge) and is no
|
|
70363
70391
|
// hand on a scroll that came from elsewhere.
|
|
70364
70392
|
const WHEEL_REACH = 100;
|
|
70393
|
+
// iOS's touch handling, the one engine whose finger drops writes (see above).
|
|
70394
|
+
const FINGER_KEEPS_WRITES = !window.CSS.supports(
|
|
70395
|
+
"-webkit-touch-callout",
|
|
70396
|
+
"none",
|
|
70397
|
+
);
|
|
70365
70398
|
|
|
70366
70399
|
const flightMap = new WeakMap();
|
|
70367
70400
|
let fingersDown = 0;
|
|
@@ -70371,13 +70404,13 @@ let wheelTimeStamp = -Infinity;
|
|
|
70371
70404
|
* @param {Element} scrollerEl
|
|
70372
70405
|
* @returns {boolean}
|
|
70373
70406
|
*/
|
|
70374
|
-
const
|
|
70407
|
+
const wouldInterruptScroll = (scrollerEl) => {
|
|
70375
70408
|
const flight = flightMap.get(scrollerEl);
|
|
70376
70409
|
if (!flight) {
|
|
70377
70410
|
return false;
|
|
70378
70411
|
}
|
|
70379
|
-
if (
|
|
70380
|
-
return
|
|
70412
|
+
if (fingersDown > 0) {
|
|
70413
|
+
return !FINGER_KEEPS_WRITES;
|
|
70381
70414
|
}
|
|
70382
70415
|
if (wheelTimeStamp > flight.timeStamp - WHEEL_REACH) {
|
|
70383
70416
|
return false;
|
|
@@ -70407,10 +70440,7 @@ if ("onscrollend" in window) {
|
|
|
70407
70440
|
if (flightMap.has(scroller)) {
|
|
70408
70441
|
return;
|
|
70409
70442
|
}
|
|
70410
|
-
flightMap.set(scroller, {
|
|
70411
|
-
timeStamp: e.timeStamp,
|
|
70412
|
-
fingerDown: fingersDown > 0,
|
|
70413
|
-
});
|
|
70443
|
+
flightMap.set(scroller, { timeStamp: e.timeStamp });
|
|
70414
70444
|
},
|
|
70415
70445
|
{ capture: true, passive: true },
|
|
70416
70446
|
);
|
|
@@ -74097,6 +74127,13 @@ const useListScrollSync = ({
|
|
|
74097
74127
|
if (delta > -0.5 && delta < 0.5) {
|
|
74098
74128
|
return;
|
|
74099
74129
|
}
|
|
74130
|
+
if (wouldInterruptScroll(scrollerEl)) {
|
|
74131
|
+
// As the anchoring's write would (see holdAnchorStill). The document's
|
|
74132
|
+
// box is the scroller of a list scrolling the page, and it resizes with
|
|
74133
|
+
// every item drawn taller than its filler: while the page scrolls, that
|
|
74134
|
+
// is at every window move.
|
|
74135
|
+
return;
|
|
74136
|
+
}
|
|
74100
74137
|
anchorRef.current = null;
|
|
74101
74138
|
if (horizontal) {
|
|
74102
74139
|
scrollerEl.scrollLeft += delta;
|
|
@@ -74194,13 +74231,12 @@ const useListScrollSync = ({
|
|
|
74194
74231
|
return;
|
|
74195
74232
|
}
|
|
74196
74233
|
anchorRef.current = null;
|
|
74197
|
-
if (
|
|
74234
|
+
if (wouldInterruptScroll(scrollerEl)) {
|
|
74198
74235
|
// Nobody is looking at items standing still: they are passing by, on
|
|
74199
|
-
// their way somewhere the browser
|
|
74200
|
-
//
|
|
74201
|
-
//
|
|
74202
|
-
//
|
|
74203
|
-
// of it moves, and the glide lands.
|
|
74236
|
+
// their way somewhere the browser takes them (a key, a smooth scrollTo,
|
|
74237
|
+
// the status bar, a fling). The write would stop that scroll short of
|
|
74238
|
+
// where it was going. Left alone, the items drawn above push the rest by
|
|
74239
|
+
// `drift` while all of it moves, and the scroll lands.
|
|
74204
74240
|
return;
|
|
74205
74241
|
}
|
|
74206
74242
|
scrolledByListRef.current = true;
|