@jsenv/navi 0.29.442 → 0.29.443
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 +293 -85
- package/dist/dev/jsenv_navi.js.map +4 -4
- package/dist/jsenv_navi.js +283 -85
- package/dist/jsenv_navi.js.map +4 -4
- package/docs/AI_INSTRUCTIONS.md +5 -4
- package/docs/mobile_touch.md +19 -6
- package/docs/scroll.md +39 -9
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -71419,15 +71419,16 @@ document.addEventListener(
|
|
|
71419
71419
|
);
|
|
71420
71420
|
|
|
71421
71421
|
/*
|
|
71422
|
-
*
|
|
71423
|
-
* progress. A write is an instant scroll, and
|
|
71424
|
-
* scroll it is playing on its own, short of
|
|
71425
|
-
* (Home, PageDown, Space), a smooth `scrollTo`
|
|
71426
|
-
* bar tap on iOS. A scroll following a hand is
|
|
71427
|
-
* turn carries on, and on most engines a
|
|
71428
|
-
* the write left it, and the fling it
|
|
71429
|
-
*
|
|
71430
|
-
* every move of a list's
|
|
71422
|
+
* Moves a scroll by what the content moved under the user, unless the write
|
|
71423
|
+
* would cost them the scroll in progress. A write is an instant scroll, and
|
|
71424
|
+
* the browser abandons for it any scroll it is playing on its own, short of
|
|
71425
|
+
* where that one was going: a key (Home, PageDown, Space), a smooth `scrollTo`
|
|
71426
|
+
* or `scrollIntoView`, the status bar tap on iOS. A scroll following a hand is
|
|
71427
|
+
* not abandoned: the wheel's next turn carries on, and on most engines a
|
|
71428
|
+
* finger keeps dragging from wherever the write left it, and the fling it
|
|
71429
|
+
* leaves behind once it lifts carries on from there too (Chrome under
|
|
71430
|
+
* DevTools' touch emulation: flings written to at every move of a list's
|
|
71431
|
+
* window went as far as flings never written to).
|
|
71431
71432
|
*
|
|
71432
71433
|
* A fling must not be taken for a scroll the browser plays on its own. A list
|
|
71433
71434
|
* then leaves uncorrected, for the whole fling, the room its fillers lose above
|
|
@@ -71454,6 +71455,18 @@ document.addEventListener(
|
|
|
71454
71455
|
* while the finger is still on one of them, and the touchend of a removed
|
|
71455
71456
|
* element never reaches the document.
|
|
71456
71457
|
*
|
|
71458
|
+
* Chrome ends a scroll at every write, a finger's fling included: "scrollend"
|
|
71459
|
+
* a frame after the write, and the fling goes on a frame later as a scroll of
|
|
71460
|
+
* its own, as far as it was going (a bare page written to every 120ms of a
|
|
71461
|
+
* fling: 9 "scrollend" for 8 writes, the fling as long). Taken for a new
|
|
71462
|
+
* scroll, it is nobody's once it starts far enough from the finger's last
|
|
71463
|
+
* move, and the list's corrections are refused for the rest of the fling: in
|
|
71464
|
+
* wematch, cards drawn above the screen pushing it down at every window slide.
|
|
71465
|
+
* So a scroll starting right after the "scrollend" of a scroll written to here
|
|
71466
|
+
* goes on as that scroll: same start, same hand. Only a written one: a scroll
|
|
71467
|
+
* starting right after one that ended on its own is a new one, a key or a
|
|
71468
|
+
* smooth `scrollTo` must not pass for the fling before it.
|
|
71469
|
+
*
|
|
71457
71470
|
* In a browser without "scrollend", no write is said to interrupt anything: a
|
|
71458
71471
|
* scroll would never be seen ending.
|
|
71459
71472
|
*/
|
|
@@ -71463,8 +71476,8 @@ document.addEventListener(
|
|
|
71463
71476
|
// hand on a scroll that came from elsewhere.
|
|
71464
71477
|
const WHEEL_REACH = 100;
|
|
71465
71478
|
// The fling Chrome plays as a scroll of its own starts a frame or two after the
|
|
71466
|
-
// drag
|
|
71467
|
-
//
|
|
71479
|
+
// "scrollend" of the drag, or of a write (DevTools' touch emulation, 4× CPU:
|
|
71480
|
+
// 12ms after the drag's, 24 to 40ms after a write's in wematch).
|
|
71468
71481
|
const FLING_REACH = 150;
|
|
71469
71482
|
// iOS's touch handling, the one engine whose finger drops writes (see above).
|
|
71470
71483
|
const FINGER_KEEPS_WRITES = !window.CSS.supports(
|
|
@@ -71473,14 +71486,41 @@ const FINGER_KEEPS_WRITES = !window.CSS.supports(
|
|
|
71473
71486
|
);
|
|
71474
71487
|
|
|
71475
71488
|
const flightMap = new WeakMap();
|
|
71489
|
+
// Scrollers written to since their last "scrollend": that "scrollend" may be
|
|
71490
|
+
// the write's.
|
|
71491
|
+
const writtenScrollerSet = new WeakSet();
|
|
71492
|
+
// The flight of a written scroller, from its "scrollend" until a scroll goes on
|
|
71493
|
+
// with it.
|
|
71494
|
+
const writtenFlightEndMap = new WeakMap();
|
|
71476
71495
|
let fingersDown = 0;
|
|
71477
71496
|
let wheelTimeStamp = -Infinity;
|
|
71478
71497
|
let fingerScrollTimeStamp = -Infinity;
|
|
71479
71498
|
|
|
71480
71499
|
/**
|
|
71500
|
+
* Answers whether it wrote.
|
|
71501
|
+
*
|
|
71481
71502
|
* @param {Element} scrollerEl
|
|
71503
|
+
* @param {number} delta
|
|
71504
|
+
* @param {{ horizontal?: boolean }} [options]
|
|
71482
71505
|
* @returns {boolean}
|
|
71483
71506
|
*/
|
|
71507
|
+
const scrollByUnlessInterrupting = (
|
|
71508
|
+
scrollerEl,
|
|
71509
|
+
delta,
|
|
71510
|
+
{ horizontal } = {},
|
|
71511
|
+
) => {
|
|
71512
|
+
if (wouldInterruptScroll(scrollerEl)) {
|
|
71513
|
+
return false;
|
|
71514
|
+
}
|
|
71515
|
+
writtenScrollerSet.add(scrollerEl);
|
|
71516
|
+
if (horizontal) {
|
|
71517
|
+
scrollerEl.scrollLeft += delta;
|
|
71518
|
+
} else {
|
|
71519
|
+
scrollerEl.scrollTop += delta;
|
|
71520
|
+
}
|
|
71521
|
+
return true;
|
|
71522
|
+
};
|
|
71523
|
+
|
|
71484
71524
|
const wouldInterruptScroll = (scrollerEl) => {
|
|
71485
71525
|
const flight = flightMap.get(scrollerEl);
|
|
71486
71526
|
if (!flight) {
|
|
@@ -71523,6 +71563,15 @@ if ("onscrollend" in window) {
|
|
|
71523
71563
|
if (flightMap.has(scroller)) {
|
|
71524
71564
|
return;
|
|
71525
71565
|
}
|
|
71566
|
+
const writtenFlightEnd = writtenFlightEndMap.get(scroller);
|
|
71567
|
+
writtenFlightEndMap.delete(scroller);
|
|
71568
|
+
if (
|
|
71569
|
+
writtenFlightEnd &&
|
|
71570
|
+
e.timeStamp - writtenFlightEnd.timeStamp < FLING_REACH
|
|
71571
|
+
) {
|
|
71572
|
+
flightMap.set(scroller, writtenFlightEnd.flight);
|
|
71573
|
+
return;
|
|
71574
|
+
}
|
|
71526
71575
|
flightMap.set(scroller, { timeStamp: e.timeStamp });
|
|
71527
71576
|
},
|
|
71528
71577
|
{ capture: true, passive: true },
|
|
@@ -71530,7 +71579,13 @@ if ("onscrollend" in window) {
|
|
|
71530
71579
|
document.addEventListener(
|
|
71531
71580
|
"scrollend",
|
|
71532
71581
|
(e) => {
|
|
71533
|
-
|
|
71582
|
+
const scroller = asScroller(e.target);
|
|
71583
|
+
const flight = flightMap.get(scroller);
|
|
71584
|
+
flightMap.delete(scroller);
|
|
71585
|
+
if (flight && writtenScrollerSet.has(scroller)) {
|
|
71586
|
+
writtenFlightEndMap.set(scroller, { flight, timeStamp: e.timeStamp });
|
|
71587
|
+
}
|
|
71588
|
+
writtenScrollerSet.delete(scroller);
|
|
71534
71589
|
},
|
|
71535
71590
|
{ capture: true, passive: true },
|
|
71536
71591
|
);
|
|
@@ -72252,13 +72307,24 @@ const createListItems = () => {
|
|
|
72252
72307
|
}
|
|
72253
72308
|
return null;
|
|
72254
72309
|
},
|
|
72255
|
-
// The room the item at that index took when it was
|
|
72256
|
-
//
|
|
72310
|
+
// The room the item at that index takes: the room it took when it was
|
|
72311
|
+
// last drawn, else the room its run is given for it (see List.Items'
|
|
72312
|
+
// itemSize), else undefined.
|
|
72257
72313
|
roomAt: (index) => {
|
|
72258
72314
|
for (const locator of locatorByOwner.values()) {
|
|
72259
|
-
const
|
|
72260
|
-
if (
|
|
72261
|
-
return
|
|
72315
|
+
const room = locator.roomAt(index);
|
|
72316
|
+
if (room !== undefined) {
|
|
72317
|
+
return room;
|
|
72318
|
+
}
|
|
72319
|
+
}
|
|
72320
|
+
return undefined;
|
|
72321
|
+
},
|
|
72322
|
+
// The room the run of that index is given for it, whatever was measured.
|
|
72323
|
+
givenRoomAt: (index) => {
|
|
72324
|
+
for (const locator of locatorByOwner.values()) {
|
|
72325
|
+
const room = locator.givenRoomAt(index);
|
|
72326
|
+
if (room !== undefined) {
|
|
72327
|
+
return room;
|
|
72262
72328
|
}
|
|
72263
72329
|
}
|
|
72264
72330
|
return undefined;
|
|
@@ -73747,6 +73813,23 @@ const css$x = /* css */`@property --list-group-label-height {
|
|
|
73747
73813
|
&[data-hidden-while-empty]:not(:has([navi-list-item-real])) {
|
|
73748
73814
|
display: none;
|
|
73749
73815
|
}
|
|
73816
|
+
|
|
73817
|
+
&[data-continued] {
|
|
73818
|
+
margin-top: calc(-1 * var(--x-list-gap, 0px));
|
|
73819
|
+
display: grid;
|
|
73820
|
+
|
|
73821
|
+
& > .navi_list_item_group_label, & > .navi_list_item_group_list {
|
|
73822
|
+
grid-area: 1 / 1;
|
|
73823
|
+
}
|
|
73824
|
+
|
|
73825
|
+
& > .navi_list_item_group_label {
|
|
73826
|
+
align-self: start;
|
|
73827
|
+
}
|
|
73828
|
+
}
|
|
73829
|
+
|
|
73830
|
+
&[data-continued="hole"] > .navi_list_item_group_label {
|
|
73831
|
+
visibility: hidden;
|
|
73832
|
+
}
|
|
73750
73833
|
}
|
|
73751
73834
|
|
|
73752
73835
|
.navi_list_container[data-horizontal] > .navi_list_scroll_container > .navi_list > .navi_list_item_group {
|
|
@@ -74528,6 +74611,7 @@ const useListScrollSync = ({
|
|
|
74528
74611
|
// that changed since is measured again when drawn again, and the anchoring
|
|
74529
74612
|
// holds the view across the difference. Items laid side by side in a grid
|
|
74530
74613
|
// share a line, which the fillers hold at the one size.
|
|
74614
|
+
useRef(false);
|
|
74531
74615
|
const rememberItemRooms = () => {
|
|
74532
74616
|
if (!ref.current || itemsPerLineRef.current !== 1) {
|
|
74533
74617
|
return;
|
|
@@ -74536,7 +74620,12 @@ const useListScrollSync = ({
|
|
|
74536
74620
|
if (!listEl) {
|
|
74537
74621
|
return;
|
|
74538
74622
|
}
|
|
74539
|
-
readItemRooms(listEl, horizontal,
|
|
74623
|
+
readItemRooms(listEl, horizontal, (item, room, complete) => {
|
|
74624
|
+
listItems.roomById.set(item.id, room);
|
|
74625
|
+
{
|
|
74626
|
+
return;
|
|
74627
|
+
}
|
|
74628
|
+
});
|
|
74540
74629
|
};
|
|
74541
74630
|
const updateRenderWindow = (newStart, newEnd, reason) => {
|
|
74542
74631
|
const {
|
|
@@ -75294,18 +75383,14 @@ const useListScrollSync = ({
|
|
|
75294
75383
|
if (delta > -0.5 && delta < 0.5) {
|
|
75295
75384
|
return;
|
|
75296
75385
|
}
|
|
75297
|
-
|
|
75298
|
-
|
|
75299
|
-
|
|
75300
|
-
|
|
75301
|
-
|
|
75302
|
-
|
|
75303
|
-
}
|
|
75304
|
-
|
|
75305
|
-
if (horizontal) {
|
|
75306
|
-
scrollerEl.scrollLeft += delta;
|
|
75307
|
-
} else {
|
|
75308
|
-
scrollerEl.scrollTop += delta;
|
|
75386
|
+
// Not into a scroll it would cut short, as the anchoring's write (see
|
|
75387
|
+
// holdAnchorStill). The document's box is the scroller of a list
|
|
75388
|
+
// scrolling the page, and it resizes with every item drawn taller than
|
|
75389
|
+
// its filler: while the page scrolls, that is at every window move.
|
|
75390
|
+
if (scrollByUnlessInterrupting(scrollerEl, delta, {
|
|
75391
|
+
horizontal
|
|
75392
|
+
})) {
|
|
75393
|
+
anchorRef.current = null;
|
|
75309
75394
|
}
|
|
75310
75395
|
};
|
|
75311
75396
|
const observer = new ResizeObserver(entries => {
|
|
@@ -75398,19 +75483,15 @@ const useListScrollSync = ({
|
|
|
75398
75483
|
return;
|
|
75399
75484
|
}
|
|
75400
75485
|
anchorRef.current = null;
|
|
75401
|
-
|
|
75402
|
-
|
|
75403
|
-
|
|
75404
|
-
|
|
75405
|
-
|
|
75406
|
-
|
|
75407
|
-
|
|
75408
|
-
}
|
|
75409
|
-
|
|
75410
|
-
if (horizontal) {
|
|
75411
|
-
scrollerEl.scrollLeft += drift;
|
|
75412
|
-
} else {
|
|
75413
|
-
scrollerEl.scrollTop += drift;
|
|
75486
|
+
// Not written into a scroll nobody is looking at: its items are passing
|
|
75487
|
+
// by, on their way somewhere the browser takes them (a key, a smooth
|
|
75488
|
+
// scrollTo, the status bar, a fling on iOS). The write would stop that
|
|
75489
|
+
// scroll short of where it was going. Left alone, the items drawn above
|
|
75490
|
+
// push the rest by `drift` while all of it moves, and the scroll lands.
|
|
75491
|
+
if (scrollByUnlessInterrupting(scrollerEl, drift, {
|
|
75492
|
+
horizontal
|
|
75493
|
+
})) {
|
|
75494
|
+
scrolledByListRef.current = true;
|
|
75414
75495
|
}
|
|
75415
75496
|
};
|
|
75416
75497
|
useLayoutEffect(holdAnchorStill);
|
|
@@ -76451,18 +76532,17 @@ const readWindowGeometry = (scrollerEl, listEl, horizontal) => {
|
|
|
76451
76532
|
};
|
|
76452
76533
|
};
|
|
76453
76534
|
// The room each item drawn takes along the scroll axis (see
|
|
76454
|
-
// rememberItemRooms):
|
|
76455
|
-
// what stands between
|
|
76456
|
-
// opens — is counted
|
|
76457
|
-
//
|
|
76458
|
-
|
|
76535
|
+
// rememberItemRooms): from where the item before it ends to where it ends, so
|
|
76536
|
+
// that what stands between the two — a separator, the label of the group it
|
|
76537
|
+
// opens — is counted with the item it comes with. A group the window cuts
|
|
76538
|
+
// draws its label without room (see the data-continued rule above): that
|
|
76539
|
+
// label's room is held by the item that opens the group, in the filler. The
|
|
76540
|
+
// first item drawn is measured from the end of what stands before it in the
|
|
76541
|
+
// list (a filler, a header); a stand-in is not measured, it takes the room it
|
|
76542
|
+
// is given and not the room of its item, but an item after it ends where it
|
|
76543
|
+
// ends. `complete` says whether the room was read up to an end before it.
|
|
76544
|
+
const readItemRooms = (listEl, horizontal, onRoom) => {
|
|
76459
76545
|
let previous = null;
|
|
76460
|
-
const keepRoom = (item, next) => {
|
|
76461
|
-
if (item.id === null) {
|
|
76462
|
-
return;
|
|
76463
|
-
}
|
|
76464
|
-
roomById.set(item.id, next && next.index === item.index + 1 ? next.from - item.from : item.to - item.from);
|
|
76465
|
-
};
|
|
76466
76546
|
for (const itemEl of listEl.querySelectorAll(`[${LIST_ITEM_INDEX_ATTRIBUTE}]`)) {
|
|
76467
76547
|
if (!isOwnItem(itemEl, listEl)) {
|
|
76468
76548
|
continue;
|
|
@@ -76477,14 +76557,41 @@ const readItemRooms = (listEl, horizontal, roomById) => {
|
|
|
76477
76557
|
from: horizontal ? rect.left : rect.top,
|
|
76478
76558
|
to: horizontal ? rect.right : rect.bottom
|
|
76479
76559
|
};
|
|
76480
|
-
if (
|
|
76481
|
-
|
|
76560
|
+
if (item.id !== null) {
|
|
76561
|
+
const endBefore = previous && previous.index === item.index - 1 ? previous.to : readEndBefore(itemEl, listEl, horizontal);
|
|
76562
|
+
if (endBefore === null) {
|
|
76563
|
+
onRoom(item, item.to - item.from, false);
|
|
76564
|
+
} else {
|
|
76565
|
+
onRoom(item, item.to - endBefore, true);
|
|
76566
|
+
}
|
|
76482
76567
|
}
|
|
76483
76568
|
previous = item;
|
|
76484
76569
|
}
|
|
76485
|
-
|
|
76486
|
-
|
|
76570
|
+
};
|
|
76571
|
+
// Where what stands before an item in the list ends: the element before the
|
|
76572
|
+
// item, or before its group when it is the first of it — or the start of the
|
|
76573
|
+
// list's content when nothing does. Null when the element before is not laid
|
|
76574
|
+
// out.
|
|
76575
|
+
const readEndBefore = (itemEl, listEl, horizontal) => {
|
|
76576
|
+
let slotEl = itemEl;
|
|
76577
|
+
const groupListEl = itemEl.parentElement;
|
|
76578
|
+
if (groupListEl && groupListEl.classList.contains("navi_list_item_group_list") && groupListEl.firstElementChild === itemEl) {
|
|
76579
|
+
slotEl = groupListEl.closest(".navi_list_item_group") || itemEl;
|
|
76580
|
+
}
|
|
76581
|
+
const beforeEl = slotEl.previousElementSibling;
|
|
76582
|
+
if (beforeEl) {
|
|
76583
|
+
const rect = beforeEl.getBoundingClientRect();
|
|
76584
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
76585
|
+
return null;
|
|
76586
|
+
}
|
|
76587
|
+
return horizontal ? rect.right : rect.bottom;
|
|
76487
76588
|
}
|
|
76589
|
+
if (slotEl.parentElement !== listEl) {
|
|
76590
|
+
return null;
|
|
76591
|
+
}
|
|
76592
|
+
const listRect = listEl.getBoundingClientRect();
|
|
76593
|
+
const listStyle = window.getComputedStyle(listEl);
|
|
76594
|
+
return horizontal ? listRect.left + parseFloat(listStyle.borderLeftWidth) + parseFloat(listStyle.paddingLeft) : listRect.top + parseFloat(listStyle.borderTopWidth) + parseFloat(listStyle.paddingTop);
|
|
76488
76595
|
};
|
|
76489
76596
|
// The first item the screen shows from `position` on, and the item after the
|
|
76490
76597
|
// last one it shows up to `position` (exclusive, like the window's end). An
|
|
@@ -77034,6 +77141,8 @@ const UnorderedList = ({
|
|
|
77034
77141
|
grid: trackColumns ? true : undefined,
|
|
77035
77142
|
gridTemplateColumns: trackColumns,
|
|
77036
77143
|
...rest,
|
|
77144
|
+
// The gap a group the window cuts takes back (see data-continued).
|
|
77145
|
+
style: withListGap(rest.style, spacing),
|
|
77037
77146
|
spacing: spacing,
|
|
77038
77147
|
baseClassName: "navi_list",
|
|
77039
77148
|
children: [!suppressFallback && searchFallbackShown && jsx(SearchFallback, {
|
|
@@ -77066,6 +77175,21 @@ const UnorderedList = ({
|
|
|
77066
77175
|
})]
|
|
77067
77176
|
});
|
|
77068
77177
|
};
|
|
77178
|
+
// The list's spacing as a variable, next to the style it is given (an object
|
|
77179
|
+
// or a css string, as Box takes either).
|
|
77180
|
+
const withListGap = (style, spacing) => {
|
|
77181
|
+
if (spacing === undefined) {
|
|
77182
|
+
return style;
|
|
77183
|
+
}
|
|
77184
|
+
const gap = stringifySpacingStyle(spacing, "gap");
|
|
77185
|
+
if (typeof style === "string") {
|
|
77186
|
+
return `${style};--x-list-gap:${gap}`;
|
|
77187
|
+
}
|
|
77188
|
+
return {
|
|
77189
|
+
...style,
|
|
77190
|
+
"--x-list-gap": gap
|
|
77191
|
+
};
|
|
77192
|
+
};
|
|
77069
77193
|
|
|
77070
77194
|
// The "no match" message. Whether it shows is decided by ListUI (see its
|
|
77071
77195
|
// searchFallbackShown): a search left nothing to display, and the caller did
|
|
@@ -77103,19 +77227,20 @@ const Fallback = ({
|
|
|
77103
77227
|
// every item it draws must not be redrawn — every item of it — because the size
|
|
77104
77228
|
// settled after the first commit.
|
|
77105
77229
|
// It holds lines: the items side by side on one take its room together, and a
|
|
77106
|
-
// last line left short takes it all the same. An item
|
|
77107
|
-
//
|
|
77230
|
+
// last line left short takes it all the same. An item whose room is known takes
|
|
77231
|
+
// that room instead: the one it took when the list drew it before (see
|
|
77232
|
+
// rememberItemRooms), or the one its run is given (see List.Items' itemSize).
|
|
77108
77233
|
const VirtualFiller = ({
|
|
77109
77234
|
edge,
|
|
77110
77235
|
itemCount,
|
|
77111
77236
|
itemsPerLine,
|
|
77112
|
-
|
|
77113
|
-
|
|
77237
|
+
itemsKnown,
|
|
77238
|
+
roomKnown,
|
|
77114
77239
|
findChunks
|
|
77115
77240
|
}) => {
|
|
77116
77241
|
const listItems = useContext(ListItemsContext);
|
|
77117
77242
|
const lineSize = listItems.virtualItemSizeSignal.value;
|
|
77118
|
-
const sizeToFill = Math.ceil(itemCount / itemsPerLine) * lineSize -
|
|
77243
|
+
const sizeToFill = Math.ceil(itemCount / itemsPerLine) * lineSize - itemsKnown * lineSize / itemsPerLine + roomKnown;
|
|
77119
77244
|
// A filler resizing moves what stands below it — the items on screen, when it
|
|
77120
77245
|
// holds the room of items above them — and it resizes in a commit of its own
|
|
77121
77246
|
// when the item size settles after the list has rendered: the list puts its
|
|
@@ -77829,6 +77954,7 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
77829
77954
|
* pageSize?: number,
|
|
77830
77955
|
* memoryBudget?: number,
|
|
77831
77956
|
* renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
|
|
77957
|
+
* itemSize?: (index: number) => number,
|
|
77832
77958
|
* renderError?: (failure: {error: any, retry: () => void, start: number, end: number}) => import("ignore:preact").ComponentChildren,
|
|
77833
77959
|
* onRequestStateChange?: (state: {busy: boolean, refreshing: boolean, range: {start: number, end: number}|null}) => void,
|
|
77834
77960
|
* }>}
|
|
@@ -77861,6 +77987,10 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
77861
77987
|
* `<List.Group>` whose label (`renderGroupLabel`) stays on screen for as long
|
|
77862
77988
|
* as one of them is. The groups are found in the data as it arrives, which is
|
|
77863
77989
|
* the only way a list that discovers its items page by page can have any.
|
|
77990
|
+
* Style the label, not the group element: a group the render window cuts is
|
|
77991
|
+
* laid out by the list, its label over its first items, so that the label
|
|
77992
|
+
* and the gap before it — held in the room of the item opening the group —
|
|
77993
|
+
* take no room twice.
|
|
77864
77994
|
* @param {(item: any, index: number) => any} [props.renderGroupLabel]
|
|
77865
77995
|
* The label of the group an item opens, given that item.
|
|
77866
77996
|
* @param {(item: any, index: number) => object} [props.groupLabelProps]
|
|
@@ -77879,9 +78009,26 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
77879
78009
|
* further out. `Infinity` keeps every item the run ever received; `0` keeps
|
|
77880
78010
|
* only the ones around the window.
|
|
77881
78011
|
* @param {false|(index: number) => any} [props.renderSkeleton]
|
|
77882
|
-
* What to draw for an item the run does not hold
|
|
78012
|
+
* What to draw for an item the run does not hold, at the room that item will
|
|
78013
|
+
* take: a screen of skeletons alone has no item for the list to hold still,
|
|
78014
|
+
* and when the page lands, what is below a skeleton of the wrong size moves
|
|
78015
|
+
* by the difference (see docs/scroll.md). Defaults to List's own
|
|
77883
78016
|
* `renderSkeleton`, then to a bare `<List.Item skeleton>`; `false` leaves the
|
|
77884
78017
|
* item empty (its room is still held, or the list would jump as it loads).
|
|
78018
|
+
* @param {(index: number) => number} [props.itemSize]
|
|
78019
|
+
* The room the item at an index takes along the scroll axis, in px, known
|
|
78020
|
+
* before the item arrives: from where the item before it ends to where it
|
|
78021
|
+
* ends — what stands between the two is its own (the gap, a separator, the
|
|
78022
|
+
* label of the group it opens; the first item of the list has no gap before
|
|
78023
|
+
* it). The fillers then hold every item the list has not drawn at its own
|
|
78024
|
+
* room instead of List's `virtualItemSize`, and an item drawn above the
|
|
78025
|
+
* screen takes exactly the room held for it: nothing on screen moves. It is
|
|
78026
|
+
* the only way the screen holds still during a fling on iOS, where writing
|
|
78027
|
+
* the scroll to correct it would stop the fling. The same index
|
|
78028
|
+
* `renderSkeleton` is given — the two draw on the same knowledge. A room
|
|
78029
|
+
* measured on an item drawn wins over it, and in development one that
|
|
78030
|
+
* differs from it is reported. Items side by side in a grid (`columns`)
|
|
78031
|
+
* share a line, which keeps `virtualItemSize`.
|
|
77885
78032
|
* @param {(failure: {error: any, retry: () => void, start: number, end: number}) => any} [props.renderError]
|
|
77886
78033
|
* What to draw where items were asked for and never came: given the `error`,
|
|
77887
78034
|
* a `retry` to call, and the `start`/`end` of the range that failed — the
|
|
@@ -77954,6 +78101,7 @@ const useRunItems = (store, {
|
|
|
77954
78101
|
renderItem,
|
|
77955
78102
|
findText,
|
|
77956
78103
|
pageSize,
|
|
78104
|
+
itemSize,
|
|
77957
78105
|
groupBy,
|
|
77958
78106
|
renderGroupLabel,
|
|
77959
78107
|
groupLabelProps,
|
|
@@ -78037,6 +78185,22 @@ const useRunItems = (store, {
|
|
|
78037
78185
|
const item = getItemAt(itemIndex);
|
|
78038
78186
|
return item === undefined ? undefined : idOf(item, itemIndex);
|
|
78039
78187
|
};
|
|
78188
|
+
// The room the caller says an item takes (`itemSize`), drawn or not.
|
|
78189
|
+
const givenRoomOf = itemIndex => {
|
|
78190
|
+
if (!itemSize || itemsPerLine !== 1 || itemIndex < runStart || itemIndex >= runEnd) {
|
|
78191
|
+
return undefined;
|
|
78192
|
+
}
|
|
78193
|
+
const room = itemSize(itemIndex);
|
|
78194
|
+
return room > 0 ? room : undefined;
|
|
78195
|
+
};
|
|
78196
|
+
// The room an item took when the list last drew it (see rememberItemRooms),
|
|
78197
|
+
// else the room the caller gives it, else undefined: a filler holds an item
|
|
78198
|
+
// it knows nothing about at the item size.
|
|
78199
|
+
const roomOf = itemIndex => {
|
|
78200
|
+
const id = idAt(itemIndex);
|
|
78201
|
+
const remembered = id === undefined ? undefined : listItems.roomById.get(id);
|
|
78202
|
+
return remembered === undefined ? givenRoomOf(itemIndex) : remembered;
|
|
78203
|
+
};
|
|
78040
78204
|
listItems.setItemLocator(ownerId, {
|
|
78041
78205
|
indexOf: id => {
|
|
78042
78206
|
let found = null;
|
|
@@ -78048,32 +78212,27 @@ const useRunItems = (store, {
|
|
|
78048
78212
|
});
|
|
78049
78213
|
return found;
|
|
78050
78214
|
},
|
|
78051
|
-
|
|
78215
|
+
roomAt: roomOf,
|
|
78216
|
+
givenRoomAt: givenRoomOf
|
|
78052
78217
|
});
|
|
78053
|
-
|
|
78054
|
-
|
|
78055
|
-
const roomOf = itemIndex => {
|
|
78056
|
-
const id = idAt(itemIndex);
|
|
78057
|
-
return id === undefined ? undefined : listItems.roomById.get(id);
|
|
78058
|
-
};
|
|
78059
|
-
const readRemembered = (from, to) => {
|
|
78060
|
-
const remembered = {
|
|
78218
|
+
const readKnownRooms = (from, to) => {
|
|
78219
|
+
const known = {
|
|
78061
78220
|
room: 0,
|
|
78062
78221
|
count: 0
|
|
78063
78222
|
};
|
|
78064
|
-
if (listItems.roomById.size === 0) {
|
|
78065
|
-
return
|
|
78223
|
+
if (listItems.roomById.size === 0 && !itemSize) {
|
|
78224
|
+
return known;
|
|
78066
78225
|
}
|
|
78067
78226
|
let itemIndex = from;
|
|
78068
78227
|
while (itemIndex < to) {
|
|
78069
78228
|
const room = roomOf(itemIndex);
|
|
78070
78229
|
if (room !== undefined) {
|
|
78071
|
-
|
|
78072
|
-
|
|
78230
|
+
known.room += room;
|
|
78231
|
+
known.count++;
|
|
78073
78232
|
}
|
|
78074
78233
|
itemIndex++;
|
|
78075
78234
|
}
|
|
78076
|
-
return
|
|
78235
|
+
return known;
|
|
78077
78236
|
};
|
|
78078
78237
|
useLayoutEffect(() => {
|
|
78079
78238
|
return () => {
|
|
@@ -78194,6 +78353,7 @@ const useRunItems = (store, {
|
|
|
78194
78353
|
nodes.push(jsx(ListItemGroup, {
|
|
78195
78354
|
label: group.label,
|
|
78196
78355
|
labelProps: group.labelProps,
|
|
78356
|
+
continued: group.continued,
|
|
78197
78357
|
children: group.children
|
|
78198
78358
|
}, groupCount === 1 ? `${ownerId}_group_${group.key}` : `${ownerId}_group_${group.key}_${groupCount}`));
|
|
78199
78359
|
group = null;
|
|
@@ -78231,10 +78391,38 @@ const useRunItems = (store, {
|
|
|
78231
78391
|
}
|
|
78232
78392
|
return groupBy(item, itemIndex);
|
|
78233
78393
|
};
|
|
78394
|
+
// Where a group opened at that item began, when not there (see
|
|
78395
|
+
// data-continued): "above" the window, or in the "hole" of items not held
|
|
78396
|
+
// drawn right before it. Items not held belong to the group they lead to as
|
|
78397
|
+
// often as not — a page lands every hundred items, a group starts a few
|
|
78398
|
+
// times in a thread — so a group the window starts in is taken to go on
|
|
78399
|
+
// above it unless the item before, held, says otherwise.
|
|
78400
|
+
// Along y only, never built along x: there a group stands its label over
|
|
78401
|
+
// its items, so the label takes no room on the axis (unless it is wider than
|
|
78402
|
+
// the items drawn) and only the gap would be taken back — `margin-left`,
|
|
78403
|
+
// without the overlay of the data-continued rule. Nothing measured it yet.
|
|
78404
|
+
let afterHole = false;
|
|
78405
|
+
const whereGroupContinues = (groupKey, itemIndex) => {
|
|
78406
|
+
if (listItems.horizontal) {
|
|
78407
|
+
return undefined;
|
|
78408
|
+
}
|
|
78409
|
+
if (afterHole) {
|
|
78410
|
+
return "hole";
|
|
78411
|
+
}
|
|
78412
|
+
if (itemIndex !== windowFrom || windowFrom <= runStart) {
|
|
78413
|
+
return undefined;
|
|
78414
|
+
}
|
|
78415
|
+
const itemBefore = getItemAt(windowFrom - 1);
|
|
78416
|
+
if (itemBefore === undefined || groupBy(itemBefore, windowFrom - 1) === groupKey) {
|
|
78417
|
+
return "above";
|
|
78418
|
+
}
|
|
78419
|
+
return undefined;
|
|
78420
|
+
};
|
|
78234
78421
|
const pushItem = (itemNode, item, itemIndex, groupKey) => {
|
|
78235
78422
|
if (groupKey === undefined) {
|
|
78236
78423
|
closeGroup();
|
|
78237
78424
|
nodes.push(itemNode);
|
|
78425
|
+
afterHole = item === undefined;
|
|
78238
78426
|
return;
|
|
78239
78427
|
}
|
|
78240
78428
|
if (!group || group.key !== groupKey) {
|
|
@@ -78243,9 +78431,11 @@ const useRunItems = (store, {
|
|
|
78243
78431
|
key: groupKey,
|
|
78244
78432
|
label: renderGroupLabel ? renderGroupLabel(item, itemIndex) : groupKey,
|
|
78245
78433
|
labelProps: groupLabelProps ? groupLabelProps(item, itemIndex) : undefined,
|
|
78434
|
+
continued: whereGroupContinues(groupKey, itemIndex),
|
|
78246
78435
|
children: []
|
|
78247
78436
|
};
|
|
78248
78437
|
}
|
|
78438
|
+
afterHole = false;
|
|
78249
78439
|
group.children.push(itemNode);
|
|
78250
78440
|
};
|
|
78251
78441
|
// The text of the items a filler stands for, cut in chunks aligned on the
|
|
@@ -78288,13 +78478,13 @@ const useRunItems = (store, {
|
|
|
78288
78478
|
// one run, and what sits before or after it (a header, items given one by
|
|
78289
78479
|
// one) is not virtualized at all.
|
|
78290
78480
|
if (windowFrom > runStart) {
|
|
78291
|
-
const
|
|
78481
|
+
const known = readKnownRooms(runStart, windowFrom);
|
|
78292
78482
|
nodes.push(jsx(VirtualFiller, {
|
|
78293
78483
|
edge: "before",
|
|
78294
78484
|
itemCount: windowFrom - runStart,
|
|
78295
78485
|
itemsPerLine: itemsPerLine,
|
|
78296
|
-
|
|
78297
|
-
|
|
78486
|
+
itemsKnown: known.count,
|
|
78487
|
+
roomKnown: known.room,
|
|
78298
78488
|
findChunks: getFindChunks(runStart, windowFrom)
|
|
78299
78489
|
}, "navi-list-filler-before"));
|
|
78300
78490
|
}
|
|
@@ -78321,6 +78511,7 @@ const useRunItems = (store, {
|
|
|
78321
78511
|
retry: store.retry
|
|
78322
78512
|
})
|
|
78323
78513
|
}, `${ownerId}_failure_${failureFrom}`));
|
|
78514
|
+
afterHole = false;
|
|
78324
78515
|
itemIndex = failureTo + 1;
|
|
78325
78516
|
continue;
|
|
78326
78517
|
}
|
|
@@ -78396,13 +78587,13 @@ const useRunItems = (store, {
|
|
|
78396
78587
|
}
|
|
78397
78588
|
closeGroup();
|
|
78398
78589
|
if (runEnd > windowTo) {
|
|
78399
|
-
const
|
|
78590
|
+
const known = readKnownRooms(windowTo, runEnd);
|
|
78400
78591
|
nodes.push(jsx(VirtualFiller, {
|
|
78401
78592
|
edge: "after",
|
|
78402
78593
|
itemCount: runEnd - windowTo,
|
|
78403
78594
|
itemsPerLine: itemsPerLine,
|
|
78404
|
-
|
|
78405
|
-
|
|
78595
|
+
itemsKnown: known.count,
|
|
78596
|
+
roomKnown: known.room,
|
|
78406
78597
|
findChunks: getFindChunks(windowTo, runEnd)
|
|
78407
78598
|
}, "navi-list-filler-after"));
|
|
78408
78599
|
}
|
|
@@ -79047,12 +79238,17 @@ const useItemStore = ({
|
|
|
79047
79238
|
* hiddenWhileEmpty — the group leaves the flow (`display: none`) while it
|
|
79048
79239
|
* holds no real item — a search that emptied it, items not
|
|
79049
79240
|
* arrived yet
|
|
79241
|
+
* continued — set by a run of items, not by callers: the group began
|
|
79242
|
+
* before the first item drawn ("above" the render window,
|
|
79243
|
+
* or in a "hole" of items not held), and what stands
|
|
79244
|
+
* before that item takes no room (see data-continued)
|
|
79050
79245
|
* ...rest — forwarded to the outer <li role="presentation">
|
|
79051
79246
|
*/
|
|
79052
79247
|
const ListItemGroup = ({
|
|
79053
79248
|
label,
|
|
79054
79249
|
labelProps,
|
|
79055
79250
|
hiddenWhileEmpty,
|
|
79251
|
+
continued,
|
|
79056
79252
|
children,
|
|
79057
79253
|
...rest
|
|
79058
79254
|
}) => {
|
|
@@ -79095,6 +79291,7 @@ const ListItemGroup = ({
|
|
|
79095
79291
|
baseClassName: "navi_list_item_group",
|
|
79096
79292
|
role: "presentation",
|
|
79097
79293
|
"data-hidden-while-empty": hiddenWhileEmpty ? "" : undefined,
|
|
79294
|
+
"data-continued": continued,
|
|
79098
79295
|
children: [jsx("span", {
|
|
79099
79296
|
...labelRest,
|
|
79100
79297
|
ref: labelRef,
|
|
@@ -79341,7 +79538,8 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
79341
79538
|
* the fillers hold for it outside the render window, what a scroll position
|
|
79342
79539
|
* inside them is read with, and the least an item on its way takes (see
|
|
79343
79540
|
* `renderSkeleton`). An item the list has drawn is held at the room it took
|
|
79344
|
-
* then
|
|
79541
|
+
* then, and an item whose run says its room (`<List.Items itemSize>`) at
|
|
79542
|
+
* that room. Left out, it is the average of the items measured so far — once when
|
|
79345
79543
|
* the list mounts, again when a popup around it opens, and after each commit
|
|
79346
79544
|
* while items are held off screen. Given, it is the worst case: the size
|
|
79347
79545
|
* every item has, or, in a list whose items differ, the smallest an item can
|