@jsenv/navi 0.29.432 → 0.29.434
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 +198 -76
- package/dist/dev/jsenv_navi.js.map +8 -8
- package/dist/jsenv_navi.js +198 -76
- package/dist/jsenv_navi.js.map +8 -8
- package/docs/AI_INSTRUCTIONS.md +4 -3
- package/docs/route_transitions.md +4 -0
- package/docs/scroll.md +38 -5
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -11514,9 +11514,16 @@ const [publishRouteRender, observeRouteRender] = createPubSub();
|
|
|
11514
11514
|
* (recallScrollerPosition). What a push means for them is what it means for
|
|
11515
11515
|
* the document — an arrival opens at the top (see startAtTop): the page
|
|
11516
11516
|
* arrived at has its named positions dropped before its lists render, so a
|
|
11517
|
-
* list recalls only on the way back. A scroller that
|
|
11518
|
-
* stays
|
|
11519
|
-
*
|
|
11517
|
+
* list recalls only on the way back. A scroller that outlives a navigation —
|
|
11518
|
+
* its page stays under a new address, or it lives outside the page that
|
|
11519
|
+
* changed — is shown under the new address too, and is carried to it (see
|
|
11520
|
+
* carryScrollersOnceRendered). A scroller that goes while its page stays — a
|
|
11521
|
+
* popup closing over the same address — has nothing to come back to, and says
|
|
11522
|
+
* so as it leaves (forgetScrollerUnlessPageLeft).
|
|
11523
|
+
*
|
|
11524
|
+
* Every position is kept under the address the routing shows, which can be
|
|
11525
|
+
* ahead of the browser's: a replace waiting for its state to settle has moved
|
|
11526
|
+
* the page and not yet `window.location` (see readShownUrl).
|
|
11520
11527
|
*
|
|
11521
11528
|
* What is NOT covered, and cannot be from here: a page whose height depends on
|
|
11522
11529
|
* something still loading. Its content is not there at the moment it is put
|
|
@@ -11537,12 +11544,28 @@ const [publishRouteRender, observeRouteRender] = createPubSub();
|
|
|
11537
11544
|
const STORAGE_KEY = "navi_scroll_positions";
|
|
11538
11545
|
|
|
11539
11546
|
const positionByUrl = new Map();
|
|
11547
|
+
// Where the document stands, as its scroll events last said, whoever it
|
|
11548
|
+
// belongs to: what a url shown without the document moving is given (see
|
|
11549
|
+
// stayInPlace). Null until the restoration is installed — nothing is recorded
|
|
11550
|
+
// before.
|
|
11551
|
+
let documentOffset = null;
|
|
11540
11552
|
// url -> (scroller name -> position). The position is whatever the scroller
|
|
11541
11553
|
// handed in: what it can put itself back on, in its own terms.
|
|
11542
11554
|
const scrollerPositionsByUrl = new Map();
|
|
11543
|
-
//
|
|
11544
|
-
// a page that stays from one
|
|
11545
|
-
|
|
11555
|
+
// What each named scroller still mounted last said: its position and the url
|
|
11556
|
+
// it said it under. What tells a scroller leaving a page that stays from one
|
|
11557
|
+
// leaving with its page, and what one outliving a navigation is carried with.
|
|
11558
|
+
const saidByScrollerName = new Map();
|
|
11559
|
+
|
|
11560
|
+
// The address the page is shown under, as the routing knows it (see
|
|
11561
|
+
// installScrollRestoration): `window.location` lags behind a replace waiting
|
|
11562
|
+
// for its state to settle (replaceAddressWhenSettled in either integration),
|
|
11563
|
+
// and a scroll keyed by it meanwhile is written for the address being left.
|
|
11564
|
+
// Resolved, as an address handed to navTo can be kept as it was given.
|
|
11565
|
+
let readRoutingAddress = () => window.location.href;
|
|
11566
|
+
const readShownUrl = () => {
|
|
11567
|
+
return new URL(readRoutingAddress(), window.location.href).href;
|
|
11568
|
+
};
|
|
11546
11569
|
|
|
11547
11570
|
// Read once, the first time anyone needs the positions: the document's
|
|
11548
11571
|
// restoration is installed by the routing, and a list remembering itself may
|
|
@@ -11643,40 +11666,38 @@ const holdDocumentScroll = () => {
|
|
|
11643
11666
|
};
|
|
11644
11667
|
|
|
11645
11668
|
let installed = false;
|
|
11646
|
-
const installScrollRestoration = () => {
|
|
11669
|
+
const installScrollRestoration = ({ readAddress }) => {
|
|
11647
11670
|
if (installed) {
|
|
11648
11671
|
return;
|
|
11649
11672
|
}
|
|
11650
11673
|
installed = true;
|
|
11674
|
+
readRoutingAddress = readAddress;
|
|
11651
11675
|
if (!("scrollRestoration" in window.history)) {
|
|
11652
11676
|
return;
|
|
11653
11677
|
}
|
|
11654
11678
|
window.history.scrollRestoration = "manual";
|
|
11655
11679
|
loadStore();
|
|
11680
|
+
documentOffset = { x: window.scrollX, y: window.scrollY };
|
|
11656
11681
|
// Read as it happens rather than when leaving: a traverse changes the url
|
|
11657
11682
|
// before anything here is told, so a position read then would be read for
|
|
11658
11683
|
// the wrong page.
|
|
11659
11684
|
window.addEventListener(
|
|
11660
11685
|
"scroll",
|
|
11661
11686
|
() => {
|
|
11687
|
+
documentOffset = { x: window.scrollX, y: window.scrollY };
|
|
11662
11688
|
if (suspendCount) {
|
|
11663
11689
|
return;
|
|
11664
11690
|
}
|
|
11665
|
-
positionByUrl.set(
|
|
11666
|
-
x: window.scrollX,
|
|
11667
|
-
y: window.scrollY,
|
|
11668
|
-
});
|
|
11691
|
+
positionByUrl.set(readShownUrl(), documentOffset);
|
|
11669
11692
|
},
|
|
11670
11693
|
{ passive: true },
|
|
11671
11694
|
);
|
|
11672
|
-
const
|
|
11695
|
+
const urlOnLoad = readShownUrl();
|
|
11696
|
+
const positionOnLoad = positionByUrl.get(urlOnLoad);
|
|
11673
11697
|
if (!positionOnLoad) {
|
|
11674
|
-
//
|
|
11675
|
-
//
|
|
11676
|
-
positionByUrl.set(
|
|
11677
|
-
x: window.scrollX,
|
|
11678
|
-
y: window.scrollY,
|
|
11679
|
-
});
|
|
11698
|
+
// The first page, where the browser landed it: no scroll event says so
|
|
11699
|
+
// (see stayInPlace).
|
|
11700
|
+
positionByUrl.set(urlOnLoad, documentOffset);
|
|
11680
11701
|
return;
|
|
11681
11702
|
}
|
|
11682
11703
|
// What a reload asks for, now that the browser has been told not to do it.
|
|
@@ -11729,7 +11750,9 @@ const restoreScrollPosition = (url) => {
|
|
|
11729
11750
|
// clamp is a scroll recorded under the url already returned to.
|
|
11730
11751
|
const restoreScrollPositionOnReturn = (url) => {
|
|
11731
11752
|
const arrival = ++arrivalCount;
|
|
11732
|
-
const
|
|
11753
|
+
const href = new URL(url, window.location.href).href;
|
|
11754
|
+
carryScrollersOnceRendered(href);
|
|
11755
|
+
const position = positionByUrl.get(href);
|
|
11733
11756
|
if (!position) {
|
|
11734
11757
|
return;
|
|
11735
11758
|
}
|
|
@@ -11767,14 +11790,17 @@ const restoreScrollPositionOnReturn = (url) => {
|
|
|
11767
11790
|
// app that scrolls an element of its own scrolls it itself.
|
|
11768
11791
|
const startAtTop = (url, { from } = {}) => {
|
|
11769
11792
|
if (!isArrival(url, { from })) {
|
|
11793
|
+
stayInPlace(url);
|
|
11770
11794
|
return;
|
|
11771
11795
|
}
|
|
11772
11796
|
arrivalCount++;
|
|
11797
|
+
const href = new URL(url, window.location.href).href;
|
|
11798
|
+
carryScrollersOnceRendered(href);
|
|
11773
11799
|
// Written down as well as done: a document already at its top fires no
|
|
11774
11800
|
// scroll event, and a page left without ever being scrolled would have no
|
|
11775
11801
|
// position to come back to — the return would keep the offset of the page
|
|
11776
11802
|
// being left.
|
|
11777
|
-
positionByUrl.set(
|
|
11803
|
+
positionByUrl.set(href, { x: 0, y: 0 });
|
|
11778
11804
|
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
11779
11805
|
};
|
|
11780
11806
|
const isArrival = (url, { from }) => {
|
|
@@ -11788,6 +11814,53 @@ const isArrival = (url, { from }) => {
|
|
|
11788
11814
|
return true;
|
|
11789
11815
|
};
|
|
11790
11816
|
|
|
11817
|
+
// A url the page is shown under without changing: a replace, a push that
|
|
11818
|
+
// keeps the page (see startAtTop). Nothing scrolls, so nothing says where the
|
|
11819
|
+
// document stands under it — written down here, or a return to it would find
|
|
11820
|
+
// nothing and keep the offset of the page being left.
|
|
11821
|
+
//
|
|
11822
|
+
// The offset as its scroll events last told it, not read again: a read here
|
|
11823
|
+
// would lay the page out in the middle of a routing, once per address a
|
|
11824
|
+
// settling param writes. Deaf with the recording: a row of tabs replaces the
|
|
11825
|
+
// url as it travels, and the offset belongs to nobody until the row says where
|
|
11826
|
+
// its tab lands (see arriveAtScrollPosition).
|
|
11827
|
+
const stayInPlace = (url) => {
|
|
11828
|
+
const href = new URL(url, window.location.href).href;
|
|
11829
|
+
carryScrollersOnceRendered(href);
|
|
11830
|
+
if (documentOffset !== null && !suspendCount) {
|
|
11831
|
+
positionByUrl.set(href, documentOffset);
|
|
11832
|
+
}
|
|
11833
|
+
};
|
|
11834
|
+
|
|
11835
|
+
// A scroller that outlives a navigation — its page stays under the new url, or
|
|
11836
|
+
// it lives outside the page that changed (a sidebar list kept by every page) —
|
|
11837
|
+
// is shown under that url as well, and says nothing there unless it moves: a
|
|
11838
|
+
// return to it would find no position and open the list at its top. So it is
|
|
11839
|
+
// carried, with the position it last said.
|
|
11840
|
+
//
|
|
11841
|
+
// Told apart once the navigation has rendered: until then a list about to be
|
|
11842
|
+
// swapped out is as mounted as one that stays, and carried, its position is
|
|
11843
|
+
// what a list of the same name arriving would read. By then one that left has
|
|
11844
|
+
// said so (forgetScrollerUnlessPageLeft), and one that arrived has read its
|
|
11845
|
+
// own and speaks under this url. One still mounted on its way out (a page kept
|
|
11846
|
+
// hidden while its route transition plays) is carried, and forgets the copy as
|
|
11847
|
+
// it unmounts under this url.
|
|
11848
|
+
const carryScrollersOnceRendered = (url) => {
|
|
11849
|
+
whenPageRendered(() => {
|
|
11850
|
+
if (readShownUrl() !== url) {
|
|
11851
|
+
// Another navigation came first: what is mounted is shown under its url,
|
|
11852
|
+
// and its own carry says so.
|
|
11853
|
+
return;
|
|
11854
|
+
}
|
|
11855
|
+
for (const [name, said] of saidByScrollerName) {
|
|
11856
|
+
if (said.url === url) {
|
|
11857
|
+
continue;
|
|
11858
|
+
}
|
|
11859
|
+
writeScrollerPosition(url, name, said.position);
|
|
11860
|
+
}
|
|
11861
|
+
});
|
|
11862
|
+
};
|
|
11863
|
+
|
|
11791
11864
|
// The same arrival, for the page's own scrollers. The document is scrolled to
|
|
11792
11865
|
// its top once the page is there; a list opens where it decides to in its
|
|
11793
11866
|
// first render, so what it must not find is dropped before the routing
|
|
@@ -11828,19 +11901,21 @@ const scrollTo = ({ x, y }) => {
|
|
|
11828
11901
|
|
|
11829
11902
|
const rememberScrollerPosition = (name, position) => {
|
|
11830
11903
|
loadStore();
|
|
11831
|
-
|
|
11904
|
+
writeScrollerPosition(readShownUrl(), name, position);
|
|
11905
|
+
};
|
|
11906
|
+
const writeScrollerPosition = (url, name, position) => {
|
|
11832
11907
|
let positionByName = scrollerPositionsByUrl.get(url);
|
|
11833
11908
|
if (!positionByName) {
|
|
11834
11909
|
positionByName = new Map();
|
|
11835
11910
|
scrollerPositionsByUrl.set(url, positionByName);
|
|
11836
11911
|
}
|
|
11837
11912
|
positionByName.set(name, position);
|
|
11838
|
-
|
|
11913
|
+
saidByScrollerName.set(name, { url, position });
|
|
11839
11914
|
};
|
|
11840
11915
|
|
|
11841
11916
|
const recallScrollerPosition = (name) => {
|
|
11842
11917
|
loadStore();
|
|
11843
|
-
const positionByName = scrollerPositionsByUrl.get(
|
|
11918
|
+
const positionByName = scrollerPositionsByUrl.get(readShownUrl());
|
|
11844
11919
|
if (!positionByName) {
|
|
11845
11920
|
return undefined;
|
|
11846
11921
|
}
|
|
@@ -11850,17 +11925,18 @@ const recallScrollerPosition = (name) => {
|
|
|
11850
11925
|
// Said by a scroller as it unmounts. Its page is being left when the url is
|
|
11851
11926
|
// already another one — the history is written before the page it names is
|
|
11852
11927
|
// taken down — and then its position is kept for the way back. The url still
|
|
11853
|
-
// being the one it
|
|
11854
|
-
// goes (a popup closing, a section folding): there is no coming back to
|
|
11855
|
-
// place that was not left, and a position kept would greet the next mount
|
|
11928
|
+
// being the one it was last kept under means the page stays and the scroller
|
|
11929
|
+
// alone goes (a popup closing, a section folding): there is no coming back to
|
|
11930
|
+
// a place that was not left, and a position kept would greet the next mount
|
|
11856
11931
|
// under this address as a return.
|
|
11857
11932
|
const forgetScrollerUnlessPageLeft = (name) => {
|
|
11858
|
-
const
|
|
11859
|
-
if (
|
|
11933
|
+
const said = saidByScrollerName.get(name);
|
|
11934
|
+
if (said === undefined) {
|
|
11860
11935
|
return;
|
|
11861
11936
|
}
|
|
11862
|
-
|
|
11863
|
-
|
|
11937
|
+
saidByScrollerName.delete(name);
|
|
11938
|
+
const { url } = said;
|
|
11939
|
+
if (url !== readShownUrl()) {
|
|
11864
11940
|
return;
|
|
11865
11941
|
}
|
|
11866
11942
|
const positionByName = scrollerPositionsByUrl.get(url);
|
|
@@ -12263,14 +12339,18 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
12263
12339
|
// also waits for the page it returns to: an offset is clamped to whatever
|
|
12264
12340
|
// page the document holds when it is written.
|
|
12265
12341
|
//
|
|
12266
|
-
// A replace
|
|
12267
|
-
// settling, a state written — and moving the reader
|
|
12268
|
-
// them out of a page they never left.
|
|
12269
|
-
//
|
|
12270
|
-
//
|
|
12271
|
-
//
|
|
12342
|
+
// A replace leaves the document where it is: it is the same place said
|
|
12343
|
+
// differently — a param settling, a state written — and moving the reader
|
|
12344
|
+
// for it would throw them out of a page they never left. Where they stay
|
|
12345
|
+
// is written down for the new address (see stayInPlace). A push that keeps
|
|
12346
|
+
// the pathname is read the same way, by startAtTop itself. The one replace
|
|
12347
|
+
// that IS an arrival is a row of tabs travelling, and the row says so for
|
|
12348
|
+
// itself (see route_travel.jsx) — from inside the routing above, which is
|
|
12349
|
+
// why the replace is written down after it.
|
|
12272
12350
|
if (navigationType === "push") {
|
|
12273
12351
|
whenRenderingResumes(() => startAtTop(url, { from: urlLeft }));
|
|
12352
|
+
} else if (navigationType === "replace") {
|
|
12353
|
+
stayInPlace(url);
|
|
12274
12354
|
} else if (navigationType === "traverse") {
|
|
12275
12355
|
restoreScrollPositionOnReturn(url);
|
|
12276
12356
|
}
|
|
@@ -12384,7 +12464,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
12384
12464
|
// The browser's own scroll restoration is taken over here rather than left
|
|
12385
12465
|
// to whoever navigates: it is a decision about the document, and the entry
|
|
12386
12466
|
// being left must be recorded from the first pixel scrolled.
|
|
12387
|
-
installScrollRestoration();
|
|
12467
|
+
installScrollRestoration({ readAddress });
|
|
12388
12468
|
|
|
12389
12469
|
// The document is leaving, or may be killed in the background: what the
|
|
12390
12470
|
// entry says about it has to be what the document says (see
|
|
@@ -33418,6 +33498,20 @@ const toHex = ([r, g, b]) =>
|
|
|
33418
33498
|
// sure to be understood in, whatever getComputedStyle answered with.
|
|
33419
33499
|
let colorContext = null;
|
|
33420
33500
|
const parseColor = (color) => {
|
|
33501
|
+
const [r, g, b, a] = drawColor(color);
|
|
33502
|
+
if (a === 0 || a === 255) {
|
|
33503
|
+
return [r, g, b, a / 255];
|
|
33504
|
+
}
|
|
33505
|
+
// The canvas keeps a pixel premultiplied by its alpha, in bytes: the
|
|
33506
|
+
// channels of a translucent colour come back rounded at that alpha —
|
|
33507
|
+
// rgba(12, 23, 44, 0.45) reads 11, 22, 44. Made opaque, they read whole.
|
|
33508
|
+
const [rOpaque, gOpaque, bOpaque] = drawColor(
|
|
33509
|
+
`color(from ${color} srgb r g b / 1)`,
|
|
33510
|
+
);
|
|
33511
|
+
return [rOpaque, gOpaque, bOpaque, a / 255];
|
|
33512
|
+
};
|
|
33513
|
+
|
|
33514
|
+
const drawColor = (color) => {
|
|
33421
33515
|
if (!colorContext) {
|
|
33422
33516
|
const canvas = document.createElement("canvas");
|
|
33423
33517
|
canvas.width = 1;
|
|
@@ -33430,8 +33524,7 @@ const parseColor = (color) => {
|
|
|
33430
33524
|
colorContext.fillStyle = "transparent";
|
|
33431
33525
|
colorContext.fillStyle = color;
|
|
33432
33526
|
colorContext.fillRect(0, 0, 1, 1);
|
|
33433
|
-
|
|
33434
|
-
return [r, g, b, a / 255];
|
|
33527
|
+
return colorContext.getImageData(0, 0, 1, 1).data;
|
|
33435
33528
|
};
|
|
33436
33529
|
|
|
33437
33530
|
// The title belongs to the last Head to arrive, whatever order they leave in:
|
|
@@ -49640,6 +49733,15 @@ const applySpacingOnTextChildren = (children, spacing, defaultSpace) => {
|
|
|
49640
49733
|
}
|
|
49641
49734
|
return childrenWithGap;
|
|
49642
49735
|
};
|
|
49736
|
+
const isSpacingWidth = spacing => {
|
|
49737
|
+
if (typeof spacing === "number") {
|
|
49738
|
+
return true;
|
|
49739
|
+
}
|
|
49740
|
+
if (typeof spacing !== "string") {
|
|
49741
|
+
return false;
|
|
49742
|
+
}
|
|
49743
|
+
return isSizeSpacingKey(spacing) || hasCSSSizeUnit(spacing) || spacing.startsWith("var(");
|
|
49744
|
+
};
|
|
49643
49745
|
const outsideTextFlowSet = new Set();
|
|
49644
49746
|
const markAsOutsideTextFlow = jsxElement => {
|
|
49645
49747
|
outsideTextFlowSet.add(jsxElement);
|
|
@@ -49907,7 +50009,11 @@ const TextUI = props => {
|
|
|
49907
50009
|
}
|
|
49908
50010
|
const shouldPreserveSpacing = rest.as === "pre" || rest.flex || rest.grid;
|
|
49909
50011
|
if (shouldPreserveSpacing) {
|
|
49910
|
-
|
|
50012
|
+
// Between flex/grid items spacing is a gap, so only a width carries over: a
|
|
50013
|
+
// separator (the default space, a text, an element) or "pre" has no gap.
|
|
50014
|
+
if (isSpacingWidth(spacing)) {
|
|
50015
|
+
boxProps.spacing = spacing;
|
|
50016
|
+
}
|
|
49911
50017
|
} else {
|
|
49912
50018
|
children = applySpacingOnTextChildren(children, resolvedSpacing, defaultSpace);
|
|
49913
50019
|
}
|
|
@@ -71723,32 +71829,43 @@ document.addEventListener(
|
|
|
71723
71829
|
);
|
|
71724
71830
|
|
|
71725
71831
|
/*
|
|
71726
|
-
* Whether a
|
|
71727
|
-
*
|
|
71728
|
-
*
|
|
71729
|
-
*
|
|
71730
|
-
*
|
|
71731
|
-
*
|
|
71732
|
-
*
|
|
71733
|
-
*
|
|
71734
|
-
* from
|
|
71735
|
-
*
|
|
71736
|
-
*
|
|
71737
|
-
*
|
|
71738
|
-
*
|
|
71739
|
-
*
|
|
71740
|
-
*
|
|
71741
|
-
*
|
|
71742
|
-
*
|
|
71743
|
-
*
|
|
71744
|
-
*
|
|
71745
|
-
*
|
|
71832
|
+
* Whether writing a scroll position now would cost the user the scroll in
|
|
71833
|
+
* progress. A write is an instant scroll, and the browser abandons for it any
|
|
71834
|
+
* scroll it is playing on its own, short of where that one was going: a key
|
|
71835
|
+
* (Home, PageDown, Space), a smooth `scrollTo` or `scrollIntoView`, the status
|
|
71836
|
+
* bar tap on iOS, the fling a finger leaves behind once it lifts. A scroll
|
|
71837
|
+
* following a hand is not abandoned: the wheel's next turn carries on, and on
|
|
71838
|
+
* most engines a finger keeps dragging from wherever the write left it.
|
|
71839
|
+
*
|
|
71840
|
+
* Not on iOS. Its finger places the content from where the drag began, so a
|
|
71841
|
+
* write under it is undone at the finger's next move — or, when the finger
|
|
71842
|
+
* lifts first, lands after the lift and stops the fling dead. And a fling
|
|
71843
|
+
* stops on any write during its momentum, as the status bar's scroll to the
|
|
71844
|
+
* top does (Safari 26.5 on the simulator: flings of 1700px stopped 130 to 340px
|
|
71845
|
+
* after the lift, 3 in 3; a write 50ms before the lift stopped 2 flings in 3).
|
|
71846
|
+
* No finger there is a hand a write could hold to. Chrome flings on through a
|
|
71847
|
+
* write, and loses nothing by not getting one.
|
|
71848
|
+
*
|
|
71849
|
+
* A scroll is followed from its first "scroll" event to its "scrollend", and
|
|
71850
|
+
* is a hand's while a finger is down, or when a wheel turned around the time it
|
|
71851
|
+
* started or since. A finger is down from its touchstart to its touchend,
|
|
71852
|
+
* heard on what it touched: a list removes the items scrolled out of its window
|
|
71853
|
+
* while the finger is still on one of them, and the touchend of a removed
|
|
71854
|
+
* element never reaches the document.
|
|
71855
|
+
*
|
|
71856
|
+
* In a browser without "scrollend", no write is said to interrupt anything: a
|
|
71857
|
+
* scroll would never be seen ending.
|
|
71746
71858
|
*/
|
|
71747
71859
|
|
|
71748
71860
|
// The scroll a wheel turn causes starts in the frame the turn is read, or the
|
|
71749
71861
|
// next one. A turn further from it moved nothing (a wheel at an edge) and is no
|
|
71750
71862
|
// hand on a scroll that came from elsewhere.
|
|
71751
71863
|
const WHEEL_REACH = 100;
|
|
71864
|
+
// iOS's touch handling, the one engine whose finger drops writes (see above).
|
|
71865
|
+
const FINGER_KEEPS_WRITES = !window.CSS.supports(
|
|
71866
|
+
"-webkit-touch-callout",
|
|
71867
|
+
"none",
|
|
71868
|
+
);
|
|
71752
71869
|
|
|
71753
71870
|
const flightMap = new WeakMap();
|
|
71754
71871
|
let fingersDown = 0;
|
|
@@ -71758,13 +71875,13 @@ let wheelTimeStamp = -Infinity;
|
|
|
71758
71875
|
* @param {Element} scrollerEl
|
|
71759
71876
|
* @returns {boolean}
|
|
71760
71877
|
*/
|
|
71761
|
-
const
|
|
71878
|
+
const wouldInterruptScroll = (scrollerEl) => {
|
|
71762
71879
|
const flight = flightMap.get(scrollerEl);
|
|
71763
71880
|
if (!flight) {
|
|
71764
71881
|
return false;
|
|
71765
71882
|
}
|
|
71766
|
-
if (
|
|
71767
|
-
return
|
|
71883
|
+
if (fingersDown > 0) {
|
|
71884
|
+
return !FINGER_KEEPS_WRITES;
|
|
71768
71885
|
}
|
|
71769
71886
|
if (wheelTimeStamp > flight.timeStamp - WHEEL_REACH) {
|
|
71770
71887
|
return false;
|
|
@@ -71794,10 +71911,7 @@ if ("onscrollend" in window) {
|
|
|
71794
71911
|
if (flightMap.has(scroller)) {
|
|
71795
71912
|
return;
|
|
71796
71913
|
}
|
|
71797
|
-
flightMap.set(scroller, {
|
|
71798
|
-
timeStamp: e.timeStamp,
|
|
71799
|
-
fingerDown: fingersDown > 0,
|
|
71800
|
-
});
|
|
71914
|
+
flightMap.set(scroller, { timeStamp: e.timeStamp });
|
|
71801
71915
|
},
|
|
71802
71916
|
{ capture: true, passive: true },
|
|
71803
71917
|
);
|
|
@@ -75487,6 +75601,13 @@ const useListScrollSync = ({
|
|
|
75487
75601
|
if (delta > -0.5 && delta < 0.5) {
|
|
75488
75602
|
return;
|
|
75489
75603
|
}
|
|
75604
|
+
if (wouldInterruptScroll(scrollerEl)) {
|
|
75605
|
+
// As the anchoring's write would (see holdAnchorStill). The document's
|
|
75606
|
+
// box is the scroller of a list scrolling the page, and it resizes with
|
|
75607
|
+
// every item drawn taller than its filler: while the page scrolls, that
|
|
75608
|
+
// is at every window move.
|
|
75609
|
+
return;
|
|
75610
|
+
}
|
|
75490
75611
|
anchorRef.current = null;
|
|
75491
75612
|
if (horizontal) {
|
|
75492
75613
|
scrollerEl.scrollLeft += delta;
|
|
@@ -75584,13 +75705,12 @@ const useListScrollSync = ({
|
|
|
75584
75705
|
return;
|
|
75585
75706
|
}
|
|
75586
75707
|
anchorRef.current = null;
|
|
75587
|
-
if (
|
|
75708
|
+
if (wouldInterruptScroll(scrollerEl)) {
|
|
75588
75709
|
// Nobody is looking at items standing still: they are passing by, on
|
|
75589
|
-
// their way somewhere the browser
|
|
75590
|
-
//
|
|
75591
|
-
//
|
|
75592
|
-
//
|
|
75593
|
-
// of it moves, and the glide lands.
|
|
75710
|
+
// their way somewhere the browser takes them (a key, a smooth scrollTo,
|
|
75711
|
+
// the status bar, a fling). The write would stop that scroll short of
|
|
75712
|
+
// where it was going. Left alone, the items drawn above push the rest by
|
|
75713
|
+
// `drift` while all of it moves, and the scroll lands.
|
|
75594
75714
|
return;
|
|
75595
75715
|
}
|
|
75596
75716
|
scrolledByListRef.current = true;
|
|
@@ -79546,9 +79666,11 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
79546
79666
|
* does, and for a list that scrolls itself the page's own restoration cannot
|
|
79547
79667
|
* see. The position is kept under the list's `id` and the page's url, for
|
|
79548
79668
|
* the session (a reload comes back too); a list without an `id` of its own
|
|
79549
|
-
* has nothing to be remembered by
|
|
79550
|
-
* `
|
|
79551
|
-
*
|
|
79669
|
+
* has nothing to be remembered by, and two lists mounted at once under one
|
|
79670
|
+
* `id` — a popup over the page, the page a route transition is leaving —
|
|
79671
|
+
* overwrite each other (see docs/scroll.md). A fresh arrival at the page
|
|
79672
|
+
* opens at `defaultScrolled` either way, and so does a list the caller holds
|
|
79673
|
+
* through `scrolled`.
|
|
79552
79674
|
* @param {boolean} [props.deselectable]
|
|
79553
79675
|
* A single-select list allowed to hold nothing: the selected item, pressed
|
|
79554
79676
|
* again, lets go. Without it the list is a radio group — a choice, once
|