@jsenv/navi 0.29.442 → 0.29.444
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 +350 -101
- package/dist/dev/jsenv_navi.js.map +9 -9
- package/dist/jsenv_navi.js +328 -100
- package/dist/jsenv_navi.js.map +9 -9
- package/docs/AI_INSTRUCTIONS.md +11 -8
- package/docs/actions.md +6 -2
- package/docs/control_value.md +28 -8
- package/docs/mobile_touch.md +19 -6
- package/docs/scroll.md +39 -9
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -6502,6 +6502,10 @@ const CONTROL_PROP_SET = new Set([
|
|
|
6502
6502
|
"charGuard",
|
|
6503
6503
|
"maxLengthGuard",
|
|
6504
6504
|
|
|
6505
|
+
// A checkbox that is a yes/no: `true` checked, `false` unchecked, instead of
|
|
6506
|
+
// HTML's value-or-nothing (see resolveEmptyUIState).
|
|
6507
|
+
"boolean",
|
|
6508
|
+
|
|
6505
6509
|
// This control answers for itself: it registers into no group, no form and no
|
|
6506
6510
|
// picker around it (see useUIStateController). It belongs here rather than
|
|
6507
6511
|
// being deleted off `props` where it is read: props is the vnode's own object
|
|
@@ -43198,6 +43202,14 @@ const useUIStateController = (
|
|
|
43198
43202
|
return ownUIState || parentUIState;
|
|
43199
43203
|
})
|
|
43200
43204
|
: ownUIStateSignal;
|
|
43205
|
+
// What the control is worth, which its action is bound to: what it
|
|
43206
|
+
// holds, or its own empty when it holds nothing. Apart from
|
|
43207
|
+
// uiStateSignal because unchecked is `undefined` there (the DOM reads any
|
|
43208
|
+
// other value as checked), while a yes/no unchecked is worth `false`.
|
|
43209
|
+
const emptyUIStateSignal = signal(emptyUIState);
|
|
43210
|
+
const valueSignal = computed(() =>
|
|
43211
|
+
uiStateOrEmpty(uiStateSignal.value, emptyUIStateSignal.value),
|
|
43212
|
+
);
|
|
43201
43213
|
|
|
43202
43214
|
// The two-way half of a bound `signal` prop: setting it re-renders and
|
|
43203
43215
|
// re-syncs via state_prop_change, but with the same value → guarded as a
|
|
@@ -43246,6 +43258,7 @@ const useUIStateController = (
|
|
|
43246
43258
|
// travel command ended up carrying a picker's selection: see
|
|
43247
43259
|
// resolveCommandValue in commands.js.
|
|
43248
43260
|
ownUIStateSignal,
|
|
43261
|
+
valueSignal,
|
|
43249
43262
|
value: controlInfo.value,
|
|
43250
43263
|
// The suggestion this control started on — what tells a field showing
|
|
43251
43264
|
// its default from one carrying an answer (see isUIStateHeld).
|
|
@@ -43309,8 +43322,12 @@ const useUIStateController = (
|
|
|
43309
43322
|
}
|
|
43310
43323
|
s.uiActionInternal?.(currentUIState, e);
|
|
43311
43324
|
if (s.uiAction) {
|
|
43312
|
-
|
|
43313
|
-
|
|
43325
|
+
const value = uiStateOrEmpty(
|
|
43326
|
+
currentUIState,
|
|
43327
|
+
controller.emptyUIState,
|
|
43328
|
+
);
|
|
43329
|
+
debugUIState(`calling uiAction for ${controlType}`, value);
|
|
43330
|
+
s.uiAction(value, e);
|
|
43314
43331
|
}
|
|
43315
43332
|
if (skipCommand) ; else {
|
|
43316
43333
|
const command = controller.controlHostProps.command;
|
|
@@ -43814,6 +43831,7 @@ const useUIStateController = (
|
|
|
43814
43831
|
return {
|
|
43815
43832
|
controller,
|
|
43816
43833
|
parentUiStateSignalHolder,
|
|
43834
|
+
emptyUIStateSignal,
|
|
43817
43835
|
...liveValues(),
|
|
43818
43836
|
};
|
|
43819
43837
|
},
|
|
@@ -43935,6 +43953,7 @@ const useUIStateController = (
|
|
|
43935
43953
|
);
|
|
43936
43954
|
scope.parentUiStateSignalHolder.value =
|
|
43937
43955
|
parentUIStateController?.uiStateSignal ?? null;
|
|
43956
|
+
scope.emptyUIStateSignal.value = emptyUIState;
|
|
43938
43957
|
|
|
43939
43958
|
const { controller } = scope;
|
|
43940
43959
|
const controllerRef = controller.ref;
|
|
@@ -44145,14 +44164,11 @@ const keptAndNamedChildUIStates = (children, fallbackState, kept) => {
|
|
|
44145
44164
|
const readNamedChildUIStates = (children, { warnNameless }) => {
|
|
44146
44165
|
const values = {};
|
|
44147
44166
|
for (const child of children) {
|
|
44148
|
-
const { name
|
|
44167
|
+
const { name } = child;
|
|
44149
44168
|
// A control holding nothing writes its own empty, not a hole: the key is in
|
|
44150
44169
|
// the object either way, and what is read from it keeps the shape the
|
|
44151
44170
|
// reader was promised (see resolveEmptyUIState).
|
|
44152
|
-
const uiState =
|
|
44153
|
-
child.uiState === undefined && emptyUIState !== undefined
|
|
44154
|
-
? emptyUIState
|
|
44155
|
-
: child.uiState;
|
|
44171
|
+
const uiState = uiStateOrEmpty(child.uiState, child.emptyUIState);
|
|
44156
44172
|
if (!name) {
|
|
44157
44173
|
if (isNamelessGrouping(child, uiState)) {
|
|
44158
44174
|
Object.assign(
|
|
@@ -45499,11 +45515,10 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
45499
45515
|
// whose last item was unselected holds [], the same thing clearing it
|
|
45500
45516
|
// leaves — not a value that changes type on its owner the moment it
|
|
45501
45517
|
// empties.
|
|
45502
|
-
const
|
|
45503
|
-
|
|
45504
|
-
|
|
45505
|
-
|
|
45506
|
-
: child.uiState;
|
|
45518
|
+
const uiStateToAdopt = uiStateOrEmpty(
|
|
45519
|
+
child.uiState,
|
|
45520
|
+
s.realUIStateController.emptyUIState,
|
|
45521
|
+
);
|
|
45507
45522
|
s.realUIStateController.setUIState(uiStateToAdopt, propagateUpEvent);
|
|
45508
45523
|
updatingRef.current = false;
|
|
45509
45524
|
},
|
|
@@ -45821,8 +45836,9 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
45821
45836
|
if (controlType === "input" && props.type === "checkbox") {
|
|
45822
45837
|
// A checkbox is a member of a set, the way HTML has it: checked it sends
|
|
45823
45838
|
// its value ("on" by default), unchecked it sends nothing at all. Only one
|
|
45824
|
-
//
|
|
45825
|
-
|
|
45839
|
+
// declared `boolean` is a yes/no, and a yes/no nobody said yes to is
|
|
45840
|
+
// `false`.
|
|
45841
|
+
return props.boolean ? false : undefined;
|
|
45826
45842
|
}
|
|
45827
45843
|
const stateShape = props["navi-state-shape"];
|
|
45828
45844
|
if (stateShape === "array") {
|
|
@@ -45834,6 +45850,15 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
45834
45850
|
return undefined;
|
|
45835
45851
|
};
|
|
45836
45852
|
|
|
45853
|
+
// What a control holding `uiState` is worth: its own empty when it holds
|
|
45854
|
+
// nothing, so the reader gets the shape it was promised.
|
|
45855
|
+
const uiStateOrEmpty = (uiState, emptyUIState) => {
|
|
45856
|
+
if (uiState === undefined && emptyUIState !== undefined) {
|
|
45857
|
+
return emptyUIState;
|
|
45858
|
+
}
|
|
45859
|
+
return uiState;
|
|
45860
|
+
};
|
|
45861
|
+
|
|
45837
45862
|
// What a cleared control shows: its own empty, kept in the shape it was holding.
|
|
45838
45863
|
const resolveClearedUIState = (controller) => {
|
|
45839
45864
|
const { controlType, props } = controller;
|
|
@@ -46069,7 +46094,7 @@ const useControlProps = (props, {
|
|
|
46069
46094
|
// of a form would re-render at each change of any of its fields.
|
|
46070
46095
|
// eslint-disable-next-line no-unused-expressions
|
|
46071
46096
|
uiStateController.ownUIStateSignal.value;
|
|
46072
|
-
const boundAction = useAction(props.action, uiStateController.
|
|
46097
|
+
const boundAction = useAction(props.action, uiStateController.valueSignal);
|
|
46073
46098
|
const [controlRootProps, controlHostProps] = useInteractiveProps(props, {
|
|
46074
46099
|
uiStateController,
|
|
46075
46100
|
boundAction,
|
|
@@ -58190,7 +58215,23 @@ const css$M = /* css */`@layer navi {
|
|
|
58190
58215
|
const InputCheckbox = props => {
|
|
58191
58216
|
const defaultRef = useRef();
|
|
58192
58217
|
props.ref = props.ref || defaultRef;
|
|
58193
|
-
|
|
58218
|
+
const valueWarnedRef = useRef(false);
|
|
58219
|
+
if (!valueWarnedRef.current) {
|
|
58220
|
+
// `true` is what `boolean` writes into `value` below, and Preact hands the
|
|
58221
|
+
// same props object back on a re-render the component triggers itself.
|
|
58222
|
+
if (props.boolean && props.value !== undefined && props.value !== true) {
|
|
58223
|
+
valueWarnedRef.current = true;
|
|
58224
|
+
console.warn(`[checkbox] "boolean" is true or false, its "value" is ignored: remove value={${JSON.stringify(props.value)}}.`);
|
|
58225
|
+
} else if (!props.boolean && props.value === true) {
|
|
58226
|
+
valueWarnedRef.current = true;
|
|
58227
|
+
console.warn(`[checkbox] value={true} is a value like any other: unchecked is undefined. For a yes/no (true or false), use the "boolean" prop.`);
|
|
58228
|
+
}
|
|
58229
|
+
}
|
|
58230
|
+
if (props.boolean) {
|
|
58231
|
+
props.value = true;
|
|
58232
|
+
} else if (props.value === undefined) {
|
|
58233
|
+
props.value = "on";
|
|
58234
|
+
}
|
|
58194
58235
|
if (props.headless) {
|
|
58195
58236
|
return jsx(InputCheckboxHeadless, {
|
|
58196
58237
|
...props,
|
|
@@ -72814,15 +72855,16 @@ document.addEventListener(
|
|
|
72814
72855
|
);
|
|
72815
72856
|
|
|
72816
72857
|
/*
|
|
72817
|
-
*
|
|
72818
|
-
* progress. A write is an instant scroll, and
|
|
72819
|
-
* scroll it is playing on its own, short of
|
|
72820
|
-
* (Home, PageDown, Space), a smooth `scrollTo`
|
|
72821
|
-
* bar tap on iOS. A scroll following a hand is
|
|
72822
|
-
* turn carries on, and on most engines a
|
|
72823
|
-
* the write left it, and the fling it
|
|
72824
|
-
*
|
|
72825
|
-
* every move of a list's
|
|
72858
|
+
* Moves a scroll by what the content moved under the user, unless the write
|
|
72859
|
+
* would cost them the scroll in progress. A write is an instant scroll, and
|
|
72860
|
+
* the browser abandons for it any scroll it is playing on its own, short of
|
|
72861
|
+
* where that one was going: a key (Home, PageDown, Space), a smooth `scrollTo`
|
|
72862
|
+
* or `scrollIntoView`, the status bar tap on iOS. A scroll following a hand is
|
|
72863
|
+
* not abandoned: the wheel's next turn carries on, and on most engines a
|
|
72864
|
+
* finger keeps dragging from wherever the write left it, and the fling it
|
|
72865
|
+
* leaves behind once it lifts carries on from there too (Chrome under
|
|
72866
|
+
* DevTools' touch emulation: flings written to at every move of a list's
|
|
72867
|
+
* window went as far as flings never written to).
|
|
72826
72868
|
*
|
|
72827
72869
|
* A fling must not be taken for a scroll the browser plays on its own. A list
|
|
72828
72870
|
* then leaves uncorrected, for the whole fling, the room its fillers lose above
|
|
@@ -72849,6 +72891,18 @@ document.addEventListener(
|
|
|
72849
72891
|
* while the finger is still on one of them, and the touchend of a removed
|
|
72850
72892
|
* element never reaches the document.
|
|
72851
72893
|
*
|
|
72894
|
+
* Chrome ends a scroll at every write, a finger's fling included: "scrollend"
|
|
72895
|
+
* a frame after the write, and the fling goes on a frame later as a scroll of
|
|
72896
|
+
* its own, as far as it was going (a bare page written to every 120ms of a
|
|
72897
|
+
* fling: 9 "scrollend" for 8 writes, the fling as long). Taken for a new
|
|
72898
|
+
* scroll, it is nobody's once it starts far enough from the finger's last
|
|
72899
|
+
* move, and the list's corrections are refused for the rest of the fling: in
|
|
72900
|
+
* wematch, cards drawn above the screen pushing it down at every window slide.
|
|
72901
|
+
* So a scroll starting right after the "scrollend" of a scroll written to here
|
|
72902
|
+
* goes on as that scroll: same start, same hand. Only a written one: a scroll
|
|
72903
|
+
* starting right after one that ended on its own is a new one, a key or a
|
|
72904
|
+
* smooth `scrollTo` must not pass for the fling before it.
|
|
72905
|
+
*
|
|
72852
72906
|
* In a browser without "scrollend", no write is said to interrupt anything: a
|
|
72853
72907
|
* scroll would never be seen ending.
|
|
72854
72908
|
*/
|
|
@@ -72858,8 +72912,8 @@ document.addEventListener(
|
|
|
72858
72912
|
// hand on a scroll that came from elsewhere.
|
|
72859
72913
|
const WHEEL_REACH = 100;
|
|
72860
72914
|
// The fling Chrome plays as a scroll of its own starts a frame or two after the
|
|
72861
|
-
// drag
|
|
72862
|
-
//
|
|
72915
|
+
// "scrollend" of the drag, or of a write (DevTools' touch emulation, 4× CPU:
|
|
72916
|
+
// 12ms after the drag's, 24 to 40ms after a write's in wematch).
|
|
72863
72917
|
const FLING_REACH = 150;
|
|
72864
72918
|
// iOS's touch handling, the one engine whose finger drops writes (see above).
|
|
72865
72919
|
const FINGER_KEEPS_WRITES = !window.CSS.supports(
|
|
@@ -72868,14 +72922,41 @@ const FINGER_KEEPS_WRITES = !window.CSS.supports(
|
|
|
72868
72922
|
);
|
|
72869
72923
|
|
|
72870
72924
|
const flightMap = new WeakMap();
|
|
72925
|
+
// Scrollers written to since their last "scrollend": that "scrollend" may be
|
|
72926
|
+
// the write's.
|
|
72927
|
+
const writtenScrollerSet = new WeakSet();
|
|
72928
|
+
// The flight of a written scroller, from its "scrollend" until a scroll goes on
|
|
72929
|
+
// with it.
|
|
72930
|
+
const writtenFlightEndMap = new WeakMap();
|
|
72871
72931
|
let fingersDown = 0;
|
|
72872
72932
|
let wheelTimeStamp = -Infinity;
|
|
72873
72933
|
let fingerScrollTimeStamp = -Infinity;
|
|
72874
72934
|
|
|
72875
72935
|
/**
|
|
72936
|
+
* Answers whether it wrote.
|
|
72937
|
+
*
|
|
72876
72938
|
* @param {Element} scrollerEl
|
|
72939
|
+
* @param {number} delta
|
|
72940
|
+
* @param {{ horizontal?: boolean }} [options]
|
|
72877
72941
|
* @returns {boolean}
|
|
72878
72942
|
*/
|
|
72943
|
+
const scrollByUnlessInterrupting = (
|
|
72944
|
+
scrollerEl,
|
|
72945
|
+
delta,
|
|
72946
|
+
{ horizontal } = {},
|
|
72947
|
+
) => {
|
|
72948
|
+
if (wouldInterruptScroll(scrollerEl)) {
|
|
72949
|
+
return false;
|
|
72950
|
+
}
|
|
72951
|
+
writtenScrollerSet.add(scrollerEl);
|
|
72952
|
+
if (horizontal) {
|
|
72953
|
+
scrollerEl.scrollLeft += delta;
|
|
72954
|
+
} else {
|
|
72955
|
+
scrollerEl.scrollTop += delta;
|
|
72956
|
+
}
|
|
72957
|
+
return true;
|
|
72958
|
+
};
|
|
72959
|
+
|
|
72879
72960
|
const wouldInterruptScroll = (scrollerEl) => {
|
|
72880
72961
|
const flight = flightMap.get(scrollerEl);
|
|
72881
72962
|
if (!flight) {
|
|
@@ -72918,6 +72999,15 @@ if ("onscrollend" in window) {
|
|
|
72918
72999
|
if (flightMap.has(scroller)) {
|
|
72919
73000
|
return;
|
|
72920
73001
|
}
|
|
73002
|
+
const writtenFlightEnd = writtenFlightEndMap.get(scroller);
|
|
73003
|
+
writtenFlightEndMap.delete(scroller);
|
|
73004
|
+
if (
|
|
73005
|
+
writtenFlightEnd &&
|
|
73006
|
+
e.timeStamp - writtenFlightEnd.timeStamp < FLING_REACH
|
|
73007
|
+
) {
|
|
73008
|
+
flightMap.set(scroller, writtenFlightEnd.flight);
|
|
73009
|
+
return;
|
|
73010
|
+
}
|
|
72921
73011
|
flightMap.set(scroller, { timeStamp: e.timeStamp });
|
|
72922
73012
|
},
|
|
72923
73013
|
{ capture: true, passive: true },
|
|
@@ -72925,7 +73015,13 @@ if ("onscrollend" in window) {
|
|
|
72925
73015
|
document.addEventListener(
|
|
72926
73016
|
"scrollend",
|
|
72927
73017
|
(e) => {
|
|
72928
|
-
|
|
73018
|
+
const scroller = asScroller(e.target);
|
|
73019
|
+
const flight = flightMap.get(scroller);
|
|
73020
|
+
flightMap.delete(scroller);
|
|
73021
|
+
if (flight && writtenScrollerSet.has(scroller)) {
|
|
73022
|
+
writtenFlightEndMap.set(scroller, { flight, timeStamp: e.timeStamp });
|
|
73023
|
+
}
|
|
73024
|
+
writtenScrollerSet.delete(scroller);
|
|
72929
73025
|
},
|
|
72930
73026
|
{ capture: true, passive: true },
|
|
72931
73027
|
);
|
|
@@ -73647,13 +73743,24 @@ const createListItems = () => {
|
|
|
73647
73743
|
}
|
|
73648
73744
|
return null;
|
|
73649
73745
|
},
|
|
73650
|
-
// The room the item at that index took when it was
|
|
73651
|
-
//
|
|
73746
|
+
// The room the item at that index takes: the room it took when it was
|
|
73747
|
+
// last drawn, else the room its run is given for it (see List.Items'
|
|
73748
|
+
// itemSize), else undefined.
|
|
73652
73749
|
roomAt: (index) => {
|
|
73653
73750
|
for (const locator of locatorByOwner.values()) {
|
|
73654
|
-
const
|
|
73655
|
-
if (
|
|
73656
|
-
return
|
|
73751
|
+
const room = locator.roomAt(index);
|
|
73752
|
+
if (room !== undefined) {
|
|
73753
|
+
return room;
|
|
73754
|
+
}
|
|
73755
|
+
}
|
|
73756
|
+
return undefined;
|
|
73757
|
+
},
|
|
73758
|
+
// The room the run of that index is given for it, whatever was measured.
|
|
73759
|
+
givenRoomAt: (index) => {
|
|
73760
|
+
for (const locator of locatorByOwner.values()) {
|
|
73761
|
+
const room = locator.givenRoomAt(index);
|
|
73762
|
+
if (room !== undefined) {
|
|
73763
|
+
return room;
|
|
73657
73764
|
}
|
|
73658
73765
|
}
|
|
73659
73766
|
return undefined;
|
|
@@ -75142,6 +75249,23 @@ const css$x = /* css */`@property --list-group-label-height {
|
|
|
75142
75249
|
&[data-hidden-while-empty]:not(:has([navi-list-item-real])) {
|
|
75143
75250
|
display: none;
|
|
75144
75251
|
}
|
|
75252
|
+
|
|
75253
|
+
&[data-continued] {
|
|
75254
|
+
margin-top: calc(-1 * var(--x-list-gap, 0px));
|
|
75255
|
+
display: grid;
|
|
75256
|
+
|
|
75257
|
+
& > .navi_list_item_group_label, & > .navi_list_item_group_list {
|
|
75258
|
+
grid-area: 1 / 1;
|
|
75259
|
+
}
|
|
75260
|
+
|
|
75261
|
+
& > .navi_list_item_group_label {
|
|
75262
|
+
align-self: start;
|
|
75263
|
+
}
|
|
75264
|
+
}
|
|
75265
|
+
|
|
75266
|
+
&[data-continued="hole"] > .navi_list_item_group_label {
|
|
75267
|
+
visibility: hidden;
|
|
75268
|
+
}
|
|
75145
75269
|
}
|
|
75146
75270
|
|
|
75147
75271
|
.navi_list_container[data-horizontal] > .navi_list_scroll_container > .navi_list > .navi_list_item_group {
|
|
@@ -75962,6 +76086,7 @@ const useListScrollSync = ({
|
|
|
75962
76086
|
// that changed since is measured again when drawn again, and the anchoring
|
|
75963
76087
|
// holds the view across the difference. Items laid side by side in a grid
|
|
75964
76088
|
// share a line, which the fillers hold at the one size.
|
|
76089
|
+
const givenRoomWarnedRef = useRef(false);
|
|
75965
76090
|
const rememberItemRooms = () => {
|
|
75966
76091
|
if (!ref.current || itemsPerLineRef.current !== 1) {
|
|
75967
76092
|
return;
|
|
@@ -75970,7 +76095,22 @@ const useListScrollSync = ({
|
|
|
75970
76095
|
if (!listEl) {
|
|
75971
76096
|
return;
|
|
75972
76097
|
}
|
|
75973
|
-
readItemRooms(listEl, horizontal,
|
|
76098
|
+
readItemRooms(listEl, horizontal, (item, room, complete) => {
|
|
76099
|
+
listItems.roomById.set(item.id, room);
|
|
76100
|
+
if (!complete || givenRoomWarnedRef.current) {
|
|
76101
|
+
return;
|
|
76102
|
+
}
|
|
76103
|
+
const givenRoom = listItems.givenRoomAt(item.index);
|
|
76104
|
+
if (givenRoom === undefined) {
|
|
76105
|
+
return;
|
|
76106
|
+
}
|
|
76107
|
+
const difference = room - givenRoom;
|
|
76108
|
+
if (difference > -0.5 && difference < 0.5) {
|
|
76109
|
+
return;
|
|
76110
|
+
}
|
|
76111
|
+
givenRoomWarnedRef.current = true;
|
|
76112
|
+
console.warn(`List.Items itemSize gives ${givenRoom}px to the item at index ${item.index}, which took ${Math.round(room * 10) / 10}px from the end of the item before it to its own end. The items not drawn are held at the room itemSize gives, so the screen moves by the difference when they are drawn above it — and nothing corrects it during a fling on iOS.`);
|
|
76113
|
+
});
|
|
75974
76114
|
};
|
|
75975
76115
|
const updateRenderWindow = (newStart, newEnd, reason) => {
|
|
75976
76116
|
const {
|
|
@@ -76732,18 +76872,14 @@ const useListScrollSync = ({
|
|
|
76732
76872
|
if (delta > -0.5 && delta < 0.5) {
|
|
76733
76873
|
return;
|
|
76734
76874
|
}
|
|
76735
|
-
|
|
76736
|
-
|
|
76737
|
-
|
|
76738
|
-
|
|
76739
|
-
|
|
76740
|
-
|
|
76741
|
-
}
|
|
76742
|
-
|
|
76743
|
-
if (horizontal) {
|
|
76744
|
-
scrollerEl.scrollLeft += delta;
|
|
76745
|
-
} else {
|
|
76746
|
-
scrollerEl.scrollTop += delta;
|
|
76875
|
+
// Not into a scroll it would cut short, as the anchoring's write (see
|
|
76876
|
+
// holdAnchorStill). The document's box is the scroller of a list
|
|
76877
|
+
// scrolling the page, and it resizes with every item drawn taller than
|
|
76878
|
+
// its filler: while the page scrolls, that is at every window move.
|
|
76879
|
+
if (scrollByUnlessInterrupting(scrollerEl, delta, {
|
|
76880
|
+
horizontal
|
|
76881
|
+
})) {
|
|
76882
|
+
anchorRef.current = null;
|
|
76747
76883
|
}
|
|
76748
76884
|
};
|
|
76749
76885
|
const observer = new ResizeObserver(entries => {
|
|
@@ -76836,19 +76972,15 @@ const useListScrollSync = ({
|
|
|
76836
76972
|
return;
|
|
76837
76973
|
}
|
|
76838
76974
|
anchorRef.current = null;
|
|
76839
|
-
|
|
76840
|
-
|
|
76841
|
-
|
|
76842
|
-
|
|
76843
|
-
|
|
76844
|
-
|
|
76845
|
-
|
|
76846
|
-
}
|
|
76847
|
-
|
|
76848
|
-
if (horizontal) {
|
|
76849
|
-
scrollerEl.scrollLeft += drift;
|
|
76850
|
-
} else {
|
|
76851
|
-
scrollerEl.scrollTop += drift;
|
|
76975
|
+
// Not written into a scroll nobody is looking at: its items are passing
|
|
76976
|
+
// by, on their way somewhere the browser takes them (a key, a smooth
|
|
76977
|
+
// scrollTo, the status bar, a fling on iOS). The write would stop that
|
|
76978
|
+
// scroll short of where it was going. Left alone, the items drawn above
|
|
76979
|
+
// push the rest by `drift` while all of it moves, and the scroll lands.
|
|
76980
|
+
if (scrollByUnlessInterrupting(scrollerEl, drift, {
|
|
76981
|
+
horizontal
|
|
76982
|
+
})) {
|
|
76983
|
+
scrolledByListRef.current = true;
|
|
76852
76984
|
}
|
|
76853
76985
|
};
|
|
76854
76986
|
useLayoutEffect(holdAnchorStill);
|
|
@@ -77983,18 +78115,17 @@ const readWindowGeometry = (scrollerEl, listEl, horizontal) => {
|
|
|
77983
78115
|
};
|
|
77984
78116
|
};
|
|
77985
78117
|
// The room each item drawn takes along the scroll axis (see
|
|
77986
|
-
// rememberItemRooms):
|
|
77987
|
-
// what stands between
|
|
77988
|
-
// opens — is counted
|
|
77989
|
-
//
|
|
77990
|
-
|
|
78118
|
+
// rememberItemRooms): from where the item before it ends to where it ends, so
|
|
78119
|
+
// that what stands between the two — a separator, the label of the group it
|
|
78120
|
+
// opens — is counted with the item it comes with. A group the window cuts
|
|
78121
|
+
// draws its label without room (see the data-continued rule above): that
|
|
78122
|
+
// label's room is held by the item that opens the group, in the filler. The
|
|
78123
|
+
// first item drawn is measured from the end of what stands before it in the
|
|
78124
|
+
// list (a filler, a header); a stand-in is not measured, it takes the room it
|
|
78125
|
+
// is given and not the room of its item, but an item after it ends where it
|
|
78126
|
+
// ends. `complete` says whether the room was read up to an end before it.
|
|
78127
|
+
const readItemRooms = (listEl, horizontal, onRoom) => {
|
|
77991
78128
|
let previous = null;
|
|
77992
|
-
const keepRoom = (item, next) => {
|
|
77993
|
-
if (item.id === null) {
|
|
77994
|
-
return;
|
|
77995
|
-
}
|
|
77996
|
-
roomById.set(item.id, next && next.index === item.index + 1 ? next.from - item.from : item.to - item.from);
|
|
77997
|
-
};
|
|
77998
78129
|
for (const itemEl of listEl.querySelectorAll(`[${LIST_ITEM_INDEX_ATTRIBUTE}]`)) {
|
|
77999
78130
|
if (!isOwnItem(itemEl, listEl)) {
|
|
78000
78131
|
continue;
|
|
@@ -78009,14 +78140,41 @@ const readItemRooms = (listEl, horizontal, roomById) => {
|
|
|
78009
78140
|
from: horizontal ? rect.left : rect.top,
|
|
78010
78141
|
to: horizontal ? rect.right : rect.bottom
|
|
78011
78142
|
};
|
|
78012
|
-
if (
|
|
78013
|
-
|
|
78143
|
+
if (item.id !== null) {
|
|
78144
|
+
const endBefore = previous && previous.index === item.index - 1 ? previous.to : readEndBefore(itemEl, listEl, horizontal);
|
|
78145
|
+
if (endBefore === null) {
|
|
78146
|
+
onRoom(item, item.to - item.from, false);
|
|
78147
|
+
} else {
|
|
78148
|
+
onRoom(item, item.to - endBefore, true);
|
|
78149
|
+
}
|
|
78014
78150
|
}
|
|
78015
78151
|
previous = item;
|
|
78016
78152
|
}
|
|
78017
|
-
|
|
78018
|
-
|
|
78153
|
+
};
|
|
78154
|
+
// Where what stands before an item in the list ends: the element before the
|
|
78155
|
+
// item, or before its group when it is the first of it — or the start of the
|
|
78156
|
+
// list's content when nothing does. Null when the element before is not laid
|
|
78157
|
+
// out.
|
|
78158
|
+
const readEndBefore = (itemEl, listEl, horizontal) => {
|
|
78159
|
+
let slotEl = itemEl;
|
|
78160
|
+
const groupListEl = itemEl.parentElement;
|
|
78161
|
+
if (groupListEl && groupListEl.classList.contains("navi_list_item_group_list") && groupListEl.firstElementChild === itemEl) {
|
|
78162
|
+
slotEl = groupListEl.closest(".navi_list_item_group") || itemEl;
|
|
78163
|
+
}
|
|
78164
|
+
const beforeEl = slotEl.previousElementSibling;
|
|
78165
|
+
if (beforeEl) {
|
|
78166
|
+
const rect = beforeEl.getBoundingClientRect();
|
|
78167
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
78168
|
+
return null;
|
|
78169
|
+
}
|
|
78170
|
+
return horizontal ? rect.right : rect.bottom;
|
|
78171
|
+
}
|
|
78172
|
+
if (slotEl.parentElement !== listEl) {
|
|
78173
|
+
return null;
|
|
78019
78174
|
}
|
|
78175
|
+
const listRect = listEl.getBoundingClientRect();
|
|
78176
|
+
const listStyle = window.getComputedStyle(listEl);
|
|
78177
|
+
return horizontal ? listRect.left + parseFloat(listStyle.borderLeftWidth) + parseFloat(listStyle.paddingLeft) : listRect.top + parseFloat(listStyle.borderTopWidth) + parseFloat(listStyle.paddingTop);
|
|
78020
78178
|
};
|
|
78021
78179
|
// The first item the screen shows from `position` on, and the item after the
|
|
78022
78180
|
// last one it shows up to `position` (exclusive, like the window's end). An
|
|
@@ -78566,6 +78724,8 @@ const UnorderedList = ({
|
|
|
78566
78724
|
grid: trackColumns ? true : undefined,
|
|
78567
78725
|
gridTemplateColumns: trackColumns,
|
|
78568
78726
|
...rest,
|
|
78727
|
+
// The gap a group the window cuts takes back (see data-continued).
|
|
78728
|
+
style: withListGap(rest.style, spacing),
|
|
78569
78729
|
spacing: spacing,
|
|
78570
78730
|
baseClassName: "navi_list",
|
|
78571
78731
|
children: [!suppressFallback && searchFallbackShown && jsx(SearchFallback, {
|
|
@@ -78598,6 +78758,21 @@ const UnorderedList = ({
|
|
|
78598
78758
|
})]
|
|
78599
78759
|
});
|
|
78600
78760
|
};
|
|
78761
|
+
// The list's spacing as a variable, next to the style it is given (an object
|
|
78762
|
+
// or a css string, as Box takes either).
|
|
78763
|
+
const withListGap = (style, spacing) => {
|
|
78764
|
+
if (spacing === undefined) {
|
|
78765
|
+
return style;
|
|
78766
|
+
}
|
|
78767
|
+
const gap = stringifySpacingStyle(spacing, "gap");
|
|
78768
|
+
if (typeof style === "string") {
|
|
78769
|
+
return `${style};--x-list-gap:${gap}`;
|
|
78770
|
+
}
|
|
78771
|
+
return {
|
|
78772
|
+
...style,
|
|
78773
|
+
"--x-list-gap": gap
|
|
78774
|
+
};
|
|
78775
|
+
};
|
|
78601
78776
|
|
|
78602
78777
|
// The "no match" message. Whether it shows is decided by ListUI (see its
|
|
78603
78778
|
// searchFallbackShown): a search left nothing to display, and the caller did
|
|
@@ -78635,19 +78810,20 @@ const Fallback = ({
|
|
|
78635
78810
|
// every item it draws must not be redrawn — every item of it — because the size
|
|
78636
78811
|
// settled after the first commit.
|
|
78637
78812
|
// It holds lines: the items side by side on one take its room together, and a
|
|
78638
|
-
// last line left short takes it all the same. An item
|
|
78639
|
-
//
|
|
78813
|
+
// last line left short takes it all the same. An item whose room is known takes
|
|
78814
|
+
// that room instead: the one it took when the list drew it before (see
|
|
78815
|
+
// rememberItemRooms), or the one its run is given (see List.Items' itemSize).
|
|
78640
78816
|
const VirtualFiller = ({
|
|
78641
78817
|
edge,
|
|
78642
78818
|
itemCount,
|
|
78643
78819
|
itemsPerLine,
|
|
78644
|
-
|
|
78645
|
-
|
|
78820
|
+
itemsKnown,
|
|
78821
|
+
roomKnown,
|
|
78646
78822
|
findChunks
|
|
78647
78823
|
}) => {
|
|
78648
78824
|
const listItems = useContext(ListItemsContext);
|
|
78649
78825
|
const lineSize = listItems.virtualItemSizeSignal.value;
|
|
78650
|
-
const sizeToFill = Math.ceil(itemCount / itemsPerLine) * lineSize -
|
|
78826
|
+
const sizeToFill = Math.ceil(itemCount / itemsPerLine) * lineSize - itemsKnown * lineSize / itemsPerLine + roomKnown;
|
|
78651
78827
|
// A filler resizing moves what stands below it — the items on screen, when it
|
|
78652
78828
|
// holds the room of items above them — and it resizes in a commit of its own
|
|
78653
78829
|
// when the item size settles after the list has rendered: the list puts its
|
|
@@ -79364,6 +79540,7 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
79364
79540
|
* pageSize?: number,
|
|
79365
79541
|
* memoryBudget?: number,
|
|
79366
79542
|
* renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
|
|
79543
|
+
* itemSize?: (index: number) => number,
|
|
79367
79544
|
* renderError?: (failure: {error: any, retry: () => void, start: number, end: number}) => import("ignore:preact").ComponentChildren,
|
|
79368
79545
|
* onRequestStateChange?: (state: {busy: boolean, refreshing: boolean, range: {start: number, end: number}|null}) => void,
|
|
79369
79546
|
* }>}
|
|
@@ -79396,6 +79573,10 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
79396
79573
|
* `<List.Group>` whose label (`renderGroupLabel`) stays on screen for as long
|
|
79397
79574
|
* as one of them is. The groups are found in the data as it arrives, which is
|
|
79398
79575
|
* the only way a list that discovers its items page by page can have any.
|
|
79576
|
+
* Style the label, not the group element: a group the render window cuts is
|
|
79577
|
+
* laid out by the list, its label over its first items, so that the label
|
|
79578
|
+
* and the gap before it — held in the room of the item opening the group —
|
|
79579
|
+
* take no room twice.
|
|
79399
79580
|
* @param {(item: any, index: number) => any} [props.renderGroupLabel]
|
|
79400
79581
|
* The label of the group an item opens, given that item.
|
|
79401
79582
|
* @param {(item: any, index: number) => object} [props.groupLabelProps]
|
|
@@ -79414,9 +79595,26 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
79414
79595
|
* further out. `Infinity` keeps every item the run ever received; `0` keeps
|
|
79415
79596
|
* only the ones around the window.
|
|
79416
79597
|
* @param {false|(index: number) => any} [props.renderSkeleton]
|
|
79417
|
-
* What to draw for an item the run does not hold
|
|
79598
|
+
* What to draw for an item the run does not hold, at the room that item will
|
|
79599
|
+
* take: a screen of skeletons alone has no item for the list to hold still,
|
|
79600
|
+
* and when the page lands, what is below a skeleton of the wrong size moves
|
|
79601
|
+
* by the difference (see docs/scroll.md). Defaults to List's own
|
|
79418
79602
|
* `renderSkeleton`, then to a bare `<List.Item skeleton>`; `false` leaves the
|
|
79419
79603
|
* item empty (its room is still held, or the list would jump as it loads).
|
|
79604
|
+
* @param {(index: number) => number} [props.itemSize]
|
|
79605
|
+
* The room the item at an index takes along the scroll axis, in px, known
|
|
79606
|
+
* before the item arrives: from where the item before it ends to where it
|
|
79607
|
+
* ends — what stands between the two is its own (the gap, a separator, the
|
|
79608
|
+
* label of the group it opens; the first item of the list has no gap before
|
|
79609
|
+
* it). The fillers then hold every item the list has not drawn at its own
|
|
79610
|
+
* room instead of List's `virtualItemSize`, and an item drawn above the
|
|
79611
|
+
* screen takes exactly the room held for it: nothing on screen moves. It is
|
|
79612
|
+
* the only way the screen holds still during a fling on iOS, where writing
|
|
79613
|
+
* the scroll to correct it would stop the fling. The same index
|
|
79614
|
+
* `renderSkeleton` is given — the two draw on the same knowledge. A room
|
|
79615
|
+
* measured on an item drawn wins over it, and in development one that
|
|
79616
|
+
* differs from it is reported. Items side by side in a grid (`columns`)
|
|
79617
|
+
* share a line, which keeps `virtualItemSize`.
|
|
79420
79618
|
* @param {(failure: {error: any, retry: () => void, start: number, end: number}) => any} [props.renderError]
|
|
79421
79619
|
* What to draw where items were asked for and never came: given the `error`,
|
|
79422
79620
|
* a `retry` to call, and the `start`/`end` of the range that failed — the
|
|
@@ -79489,6 +79687,7 @@ const useRunItems = (store, {
|
|
|
79489
79687
|
renderItem,
|
|
79490
79688
|
findText,
|
|
79491
79689
|
pageSize,
|
|
79690
|
+
itemSize,
|
|
79492
79691
|
groupBy,
|
|
79493
79692
|
renderGroupLabel,
|
|
79494
79693
|
groupLabelProps,
|
|
@@ -79572,6 +79771,22 @@ const useRunItems = (store, {
|
|
|
79572
79771
|
const item = getItemAt(itemIndex);
|
|
79573
79772
|
return item === undefined ? undefined : idOf(item, itemIndex);
|
|
79574
79773
|
};
|
|
79774
|
+
// The room the caller says an item takes (`itemSize`), drawn or not.
|
|
79775
|
+
const givenRoomOf = itemIndex => {
|
|
79776
|
+
if (!itemSize || itemsPerLine !== 1 || itemIndex < runStart || itemIndex >= runEnd) {
|
|
79777
|
+
return undefined;
|
|
79778
|
+
}
|
|
79779
|
+
const room = itemSize(itemIndex);
|
|
79780
|
+
return room > 0 ? room : undefined;
|
|
79781
|
+
};
|
|
79782
|
+
// The room an item took when the list last drew it (see rememberItemRooms),
|
|
79783
|
+
// else the room the caller gives it, else undefined: a filler holds an item
|
|
79784
|
+
// it knows nothing about at the item size.
|
|
79785
|
+
const roomOf = itemIndex => {
|
|
79786
|
+
const id = idAt(itemIndex);
|
|
79787
|
+
const remembered = id === undefined ? undefined : listItems.roomById.get(id);
|
|
79788
|
+
return remembered === undefined ? givenRoomOf(itemIndex) : remembered;
|
|
79789
|
+
};
|
|
79575
79790
|
listItems.setItemLocator(ownerId, {
|
|
79576
79791
|
indexOf: id => {
|
|
79577
79792
|
let found = null;
|
|
@@ -79583,32 +79798,27 @@ const useRunItems = (store, {
|
|
|
79583
79798
|
});
|
|
79584
79799
|
return found;
|
|
79585
79800
|
},
|
|
79586
|
-
|
|
79801
|
+
roomAt: roomOf,
|
|
79802
|
+
givenRoomAt: givenRoomOf
|
|
79587
79803
|
});
|
|
79588
|
-
|
|
79589
|
-
|
|
79590
|
-
const roomOf = itemIndex => {
|
|
79591
|
-
const id = idAt(itemIndex);
|
|
79592
|
-
return id === undefined ? undefined : listItems.roomById.get(id);
|
|
79593
|
-
};
|
|
79594
|
-
const readRemembered = (from, to) => {
|
|
79595
|
-
const remembered = {
|
|
79804
|
+
const readKnownRooms = (from, to) => {
|
|
79805
|
+
const known = {
|
|
79596
79806
|
room: 0,
|
|
79597
79807
|
count: 0
|
|
79598
79808
|
};
|
|
79599
|
-
if (listItems.roomById.size === 0) {
|
|
79600
|
-
return
|
|
79809
|
+
if (listItems.roomById.size === 0 && !itemSize) {
|
|
79810
|
+
return known;
|
|
79601
79811
|
}
|
|
79602
79812
|
let itemIndex = from;
|
|
79603
79813
|
while (itemIndex < to) {
|
|
79604
79814
|
const room = roomOf(itemIndex);
|
|
79605
79815
|
if (room !== undefined) {
|
|
79606
|
-
|
|
79607
|
-
|
|
79816
|
+
known.room += room;
|
|
79817
|
+
known.count++;
|
|
79608
79818
|
}
|
|
79609
79819
|
itemIndex++;
|
|
79610
79820
|
}
|
|
79611
|
-
return
|
|
79821
|
+
return known;
|
|
79612
79822
|
};
|
|
79613
79823
|
useLayoutEffect(() => {
|
|
79614
79824
|
return () => {
|
|
@@ -79729,6 +79939,7 @@ const useRunItems = (store, {
|
|
|
79729
79939
|
nodes.push(jsx(ListItemGroup, {
|
|
79730
79940
|
label: group.label,
|
|
79731
79941
|
labelProps: group.labelProps,
|
|
79942
|
+
continued: group.continued,
|
|
79732
79943
|
children: group.children
|
|
79733
79944
|
}, groupCount === 1 ? `${ownerId}_group_${group.key}` : `${ownerId}_group_${group.key}_${groupCount}`));
|
|
79734
79945
|
group = null;
|
|
@@ -79766,10 +79977,38 @@ const useRunItems = (store, {
|
|
|
79766
79977
|
}
|
|
79767
79978
|
return groupBy(item, itemIndex);
|
|
79768
79979
|
};
|
|
79980
|
+
// Where a group opened at that item began, when not there (see
|
|
79981
|
+
// data-continued): "above" the window, or in the "hole" of items not held
|
|
79982
|
+
// drawn right before it. Items not held belong to the group they lead to as
|
|
79983
|
+
// often as not — a page lands every hundred items, a group starts a few
|
|
79984
|
+
// times in a thread — so a group the window starts in is taken to go on
|
|
79985
|
+
// above it unless the item before, held, says otherwise.
|
|
79986
|
+
// Along y only, never built along x: there a group stands its label over
|
|
79987
|
+
// its items, so the label takes no room on the axis (unless it is wider than
|
|
79988
|
+
// the items drawn) and only the gap would be taken back — `margin-left`,
|
|
79989
|
+
// without the overlay of the data-continued rule. Nothing measured it yet.
|
|
79990
|
+
let afterHole = false;
|
|
79991
|
+
const whereGroupContinues = (groupKey, itemIndex) => {
|
|
79992
|
+
if (listItems.horizontal) {
|
|
79993
|
+
return undefined;
|
|
79994
|
+
}
|
|
79995
|
+
if (afterHole) {
|
|
79996
|
+
return "hole";
|
|
79997
|
+
}
|
|
79998
|
+
if (itemIndex !== windowFrom || windowFrom <= runStart) {
|
|
79999
|
+
return undefined;
|
|
80000
|
+
}
|
|
80001
|
+
const itemBefore = getItemAt(windowFrom - 1);
|
|
80002
|
+
if (itemBefore === undefined || groupBy(itemBefore, windowFrom - 1) === groupKey) {
|
|
80003
|
+
return "above";
|
|
80004
|
+
}
|
|
80005
|
+
return undefined;
|
|
80006
|
+
};
|
|
79769
80007
|
const pushItem = (itemNode, item, itemIndex, groupKey) => {
|
|
79770
80008
|
if (groupKey === undefined) {
|
|
79771
80009
|
closeGroup();
|
|
79772
80010
|
nodes.push(itemNode);
|
|
80011
|
+
afterHole = item === undefined;
|
|
79773
80012
|
return;
|
|
79774
80013
|
}
|
|
79775
80014
|
if (!group || group.key !== groupKey) {
|
|
@@ -79778,9 +80017,11 @@ const useRunItems = (store, {
|
|
|
79778
80017
|
key: groupKey,
|
|
79779
80018
|
label: renderGroupLabel ? renderGroupLabel(item, itemIndex) : groupKey,
|
|
79780
80019
|
labelProps: groupLabelProps ? groupLabelProps(item, itemIndex) : undefined,
|
|
80020
|
+
continued: whereGroupContinues(groupKey, itemIndex),
|
|
79781
80021
|
children: []
|
|
79782
80022
|
};
|
|
79783
80023
|
}
|
|
80024
|
+
afterHole = false;
|
|
79784
80025
|
group.children.push(itemNode);
|
|
79785
80026
|
};
|
|
79786
80027
|
// The text of the items a filler stands for, cut in chunks aligned on the
|
|
@@ -79823,13 +80064,13 @@ const useRunItems = (store, {
|
|
|
79823
80064
|
// one run, and what sits before or after it (a header, items given one by
|
|
79824
80065
|
// one) is not virtualized at all.
|
|
79825
80066
|
if (windowFrom > runStart) {
|
|
79826
|
-
const
|
|
80067
|
+
const known = readKnownRooms(runStart, windowFrom);
|
|
79827
80068
|
nodes.push(jsx(VirtualFiller, {
|
|
79828
80069
|
edge: "before",
|
|
79829
80070
|
itemCount: windowFrom - runStart,
|
|
79830
80071
|
itemsPerLine: itemsPerLine,
|
|
79831
|
-
|
|
79832
|
-
|
|
80072
|
+
itemsKnown: known.count,
|
|
80073
|
+
roomKnown: known.room,
|
|
79833
80074
|
findChunks: getFindChunks(runStart, windowFrom)
|
|
79834
80075
|
}, "navi-list-filler-before"));
|
|
79835
80076
|
}
|
|
@@ -79856,6 +80097,7 @@ const useRunItems = (store, {
|
|
|
79856
80097
|
retry: store.retry
|
|
79857
80098
|
})
|
|
79858
80099
|
}, `${ownerId}_failure_${failureFrom}`));
|
|
80100
|
+
afterHole = false;
|
|
79859
80101
|
itemIndex = failureTo + 1;
|
|
79860
80102
|
continue;
|
|
79861
80103
|
}
|
|
@@ -79931,13 +80173,13 @@ const useRunItems = (store, {
|
|
|
79931
80173
|
}
|
|
79932
80174
|
closeGroup();
|
|
79933
80175
|
if (runEnd > windowTo) {
|
|
79934
|
-
const
|
|
80176
|
+
const known = readKnownRooms(windowTo, runEnd);
|
|
79935
80177
|
nodes.push(jsx(VirtualFiller, {
|
|
79936
80178
|
edge: "after",
|
|
79937
80179
|
itemCount: runEnd - windowTo,
|
|
79938
80180
|
itemsPerLine: itemsPerLine,
|
|
79939
|
-
|
|
79940
|
-
|
|
80181
|
+
itemsKnown: known.count,
|
|
80182
|
+
roomKnown: known.room,
|
|
79941
80183
|
findChunks: getFindChunks(windowTo, runEnd)
|
|
79942
80184
|
}, "navi-list-filler-after"));
|
|
79943
80185
|
}
|
|
@@ -80591,12 +80833,17 @@ const useItemStore = ({
|
|
|
80591
80833
|
* hiddenWhileEmpty — the group leaves the flow (`display: none`) while it
|
|
80592
80834
|
* holds no real item — a search that emptied it, items not
|
|
80593
80835
|
* arrived yet
|
|
80836
|
+
* continued — set by a run of items, not by callers: the group began
|
|
80837
|
+
* before the first item drawn ("above" the render window,
|
|
80838
|
+
* or in a "hole" of items not held), and what stands
|
|
80839
|
+
* before that item takes no room (see data-continued)
|
|
80594
80840
|
* ...rest — forwarded to the outer <li role="presentation">
|
|
80595
80841
|
*/
|
|
80596
80842
|
const ListItemGroup = ({
|
|
80597
80843
|
label,
|
|
80598
80844
|
labelProps,
|
|
80599
80845
|
hiddenWhileEmpty,
|
|
80846
|
+
continued,
|
|
80600
80847
|
children,
|
|
80601
80848
|
...rest
|
|
80602
80849
|
}) => {
|
|
@@ -80639,6 +80886,7 @@ const ListItemGroup = ({
|
|
|
80639
80886
|
baseClassName: "navi_list_item_group",
|
|
80640
80887
|
role: "presentation",
|
|
80641
80888
|
"data-hidden-while-empty": hiddenWhileEmpty ? "" : undefined,
|
|
80889
|
+
"data-continued": continued,
|
|
80642
80890
|
children: [jsx("span", {
|
|
80643
80891
|
...labelRest,
|
|
80644
80892
|
ref: labelRef,
|
|
@@ -80885,7 +81133,8 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
80885
81133
|
* the fillers hold for it outside the render window, what a scroll position
|
|
80886
81134
|
* inside them is read with, and the least an item on its way takes (see
|
|
80887
81135
|
* `renderSkeleton`). An item the list has drawn is held at the room it took
|
|
80888
|
-
* then
|
|
81136
|
+
* then, and an item whose run says its room (`<List.Items itemSize>`) at
|
|
81137
|
+
* that room. Left out, it is the average of the items measured so far — once when
|
|
80889
81138
|
* the list mounts, again when a popup around it opens, and after each commit
|
|
80890
81139
|
* while items are held off screen. Given, it is the worst case: the size
|
|
80891
81140
|
* every item has, or, in a list whose items differ, the smallest an item can
|