@jsenv/navi 0.29.449 → 0.29.450
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 +92 -31
- package/dist/dev/jsenv_navi.js.map +3 -3
- package/dist/jsenv_navi.js +92 -31
- package/dist/jsenv_navi.js.map +3 -3
- package/docs/AI_INSTRUCTIONS.md +6 -4
- package/docs/list_refresh.md +10 -4
- package/docs/scroll.md +36 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -72291,6 +72291,9 @@ const createListItems = () => {
|
|
|
72291
72291
|
// what puts the view back once they have (see VirtualFiller).
|
|
72292
72292
|
captureAnchor: () => {},
|
|
72293
72293
|
holdViewStill: () => {},
|
|
72294
|
+
// The list has asked for a window no render has drawn yet: a run holding
|
|
72295
|
+
// the one before has nothing to ask for it (see useRequestMissing).
|
|
72296
|
+
windowOnItsWay: () => false,
|
|
72294
72297
|
horizontal: false,
|
|
72295
72298
|
virtualItemSizeSignal: null,
|
|
72296
72299
|
roomById,
|
|
@@ -74460,6 +74463,7 @@ const useListScrollSync = ({
|
|
|
74460
74463
|
const renderWindowRef = useRef(null);
|
|
74461
74464
|
// A window was asked for that no render has drawn yet (see settleWindow).
|
|
74462
74465
|
const windowAskedRef = useRef(false);
|
|
74466
|
+
listItems.windowOnItsWay = () => windowAskedRef.current;
|
|
74463
74467
|
const windowLeavesItemsOut = () => {
|
|
74464
74468
|
const renderWindow = renderWindowRef.current;
|
|
74465
74469
|
if (!renderWindow) {
|
|
@@ -75931,6 +75935,7 @@ const useListScrollSync = ({
|
|
|
75931
75935
|
}, [scrollerElResolved]);
|
|
75932
75936
|
holdWindow();
|
|
75933
75937
|
// Whatever was asked for until now is what this render draws.
|
|
75938
|
+
const windowWasAsked = windowAskedRef.current;
|
|
75934
75939
|
windowAskedRef.current = false;
|
|
75935
75940
|
const {
|
|
75936
75941
|
start: windowStart,
|
|
@@ -75946,13 +75951,15 @@ const useListScrollSync = ({
|
|
|
75946
75951
|
// column — started anywhere but on a line, every item drawn would sit in the
|
|
75947
75952
|
// column of another. Started on one, an item is in the column its place says
|
|
75948
75953
|
// whatever the window frames. The same object while the numbers hold, so the
|
|
75949
|
-
// runs are not told about a window that did not move
|
|
75954
|
+
// runs are not told about a window that did not move — except after a window
|
|
75955
|
+
// was asked for: a run waiting for it to ask (see useRequestMissing) must be
|
|
75956
|
+
// told, even when the window framed comes back to the same numbers.
|
|
75950
75957
|
const windowDrawnRef = useRef(null);
|
|
75951
75958
|
const drawnStart = windowStart - windowStart % itemsPerLine;
|
|
75952
75959
|
const endPastLine = windowEnd % itemsPerLine;
|
|
75953
75960
|
const drawnEnd = endPastLine === 0 ? windowEnd : windowEnd + itemsPerLine - endPastLine;
|
|
75954
75961
|
let windowDrawn = windowDrawnRef.current;
|
|
75955
|
-
if (!windowDrawn || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
75962
|
+
if (!windowDrawn || windowWasAsked || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
75956
75963
|
windowDrawn = {
|
|
75957
75964
|
start: drawnStart,
|
|
75958
75965
|
end: drawnEnd,
|
|
@@ -78087,6 +78094,15 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
78087
78094
|
* @param {(range: object) => any} [props.itemsAction]
|
|
78088
78095
|
* Where the items come from when the collection is read a slice at a time:
|
|
78089
78096
|
* a resource's range reader (`RESOURCE.GET_RANGE.bindParams(...)`).
|
|
78097
|
+
* @param {number} [props.count]
|
|
78098
|
+
* How many items the collection holds, when the app knows it before the
|
|
78099
|
+
* first answer: a total shown elsewhere, a profile saying how many there are.
|
|
78100
|
+
* The list is then as long as the collection from its first frame: it opens
|
|
78101
|
+
* where it is held, the scrollbar right, and asks for that window. Without
|
|
78102
|
+
* it, the run stands for skeletons up to the item the list opens on and a
|
|
78103
|
+
* page past it, until the first answer says how many there are. Not worth a
|
|
78104
|
+
* request of its own, since the first answer gives it (see docs/scroll.md,
|
|
78105
|
+
* "What a run knows when it opens").
|
|
78090
78106
|
* @param {number} [props.debounce]
|
|
78091
78107
|
* Milliseconds the reader must hold still before the run draws the
|
|
78092
78108
|
* collection it reads: params following a field as the user types
|
|
@@ -78202,6 +78218,7 @@ const ListItemsRun = ({
|
|
|
78202
78218
|
count,
|
|
78203
78219
|
itemsAction,
|
|
78204
78220
|
settling,
|
|
78221
|
+
pageSize: runProps.pageSize,
|
|
78205
78222
|
memoryBudget,
|
|
78206
78223
|
onRequestStateChange,
|
|
78207
78224
|
onCountChange
|
|
@@ -78470,9 +78487,17 @@ const useRunItems = (store, {
|
|
|
78470
78487
|
// A hole with nothing on either side (the scrollbar was thrown into
|
|
78471
78488
|
// territory never visited): grow it both ways around what is on
|
|
78472
78489
|
// screen.
|
|
78473
|
-
|
|
78474
|
-
|
|
78475
|
-
|
|
78490
|
+
askStart = missingStart - Math.floor((itemsPerPage - holeSize) / 2);
|
|
78491
|
+
askEnd = askStart + itemsPerPage - 1;
|
|
78492
|
+
// Cut by an edge of the run (the first page, the last one), the page
|
|
78493
|
+
// takes the room on the other side.
|
|
78494
|
+
if (askStart < runStart) {
|
|
78495
|
+
askEnd += runStart - askStart;
|
|
78496
|
+
askStart = runStart;
|
|
78497
|
+
} else if (askEnd > runEnd - 1) {
|
|
78498
|
+
askStart -= askEnd - (runEnd - 1);
|
|
78499
|
+
askEnd = runEnd - 1;
|
|
78500
|
+
}
|
|
78476
78501
|
}
|
|
78477
78502
|
}
|
|
78478
78503
|
if (askStart < runStart) {
|
|
@@ -78872,6 +78897,18 @@ const ListItemsFailure = ({
|
|
|
78872
78897
|
const ITEM_STORE_MAX_DEFAULT = 1000;
|
|
78873
78898
|
const ITEM_STORE_KEEP_AROUND = 250;
|
|
78874
78899
|
const COUNT_NOT_SAID = {};
|
|
78900
|
+
|
|
78901
|
+
// The index of the item the list is held at, when the hold names one: a number,
|
|
78902
|
+
// or a position remembered with its index (see placeWhereHeld).
|
|
78903
|
+
const readHeldIndex = scrolled => {
|
|
78904
|
+
if (typeof scrolled === "number") {
|
|
78905
|
+
return scrolled;
|
|
78906
|
+
}
|
|
78907
|
+
if (scrolled && typeof scrolled === "object" && typeof scrolled.index === "number") {
|
|
78908
|
+
return scrolled.index;
|
|
78909
|
+
}
|
|
78910
|
+
return null;
|
|
78911
|
+
};
|
|
78875
78912
|
const rangeIsSame = (a, b) => {
|
|
78876
78913
|
if (!a || !b) {
|
|
78877
78914
|
return a === b;
|
|
@@ -78889,6 +78926,7 @@ const useItemStore = ({
|
|
|
78889
78926
|
count,
|
|
78890
78927
|
itemsAction,
|
|
78891
78928
|
settling,
|
|
78929
|
+
pageSize,
|
|
78892
78930
|
memoryBudget,
|
|
78893
78931
|
onRequestStateChange,
|
|
78894
78932
|
onCountChange
|
|
@@ -79082,8 +79120,17 @@ const useItemStore = ({
|
|
|
79082
79120
|
});
|
|
79083
79121
|
// Before the first answer a run does not know how many items it stands for.
|
|
79084
79122
|
// It stands for a page of them: a list that is about to be filled looks
|
|
79085
|
-
// like items on their way, not like an empty list.
|
|
79086
|
-
|
|
79123
|
+
// like items on their way, not like an empty list. A list held on an item
|
|
79124
|
+
// (see placeWhereHeld) is about to be filled there: the run stands for the
|
|
79125
|
+
// items up to that one and a page past it, so the window frames the item
|
|
79126
|
+
// before any answer, and what the run asks for first is that window.
|
|
79127
|
+
const itemsPerPage = pageSize || listItems.pageSize;
|
|
79128
|
+
const countKnown = pages.count ?? count;
|
|
79129
|
+
let itemCount = countKnown;
|
|
79130
|
+
if (itemCount === undefined) {
|
|
79131
|
+
const heldIndex = readHeldIndex(listItems.scrolled);
|
|
79132
|
+
itemCount = heldIndex === null ? itemsPerPage : heldIndex + itemsPerPage;
|
|
79133
|
+
}
|
|
79087
79134
|
// The items drawn are from before while they are read again, and while the
|
|
79088
79135
|
// collection that replaces them waits for its params to hold still (see
|
|
79089
79136
|
// useSettledReader).
|
|
@@ -79102,7 +79149,6 @@ const useItemStore = ({
|
|
|
79102
79149
|
// say: the count the source answered or the caller gave, never the page the
|
|
79103
79150
|
// run stands for before it knows. Said by each run on its first commit, so a
|
|
79104
79151
|
// run drawing another collection takes back what the one before said.
|
|
79105
|
-
const countKnown = pages.count ?? count;
|
|
79106
79152
|
const onCountChangeRef = useRef(onCountChange);
|
|
79107
79153
|
onCountChangeRef.current = onCountChange;
|
|
79108
79154
|
const countSaidRef = useRef(COUNT_NOT_SAID);
|
|
@@ -79150,6 +79196,8 @@ const useItemStore = ({
|
|
|
79150
79196
|
// A list taken off the screen while it was asking leaves nothing ringing
|
|
79151
79197
|
// behind it: whoever is drawing "looking for items" has to stop.
|
|
79152
79198
|
const mountedRef = useRef(false);
|
|
79199
|
+
// Which render's ask is the one to send (see useRequestMissing).
|
|
79200
|
+
const askTurnRef = useRef(0);
|
|
79153
79201
|
useLayoutEffect(() => {
|
|
79154
79202
|
mountedRef.current = true;
|
|
79155
79203
|
return () => {
|
|
@@ -79213,11 +79261,7 @@ const useItemStore = ({
|
|
|
79213
79261
|
// what it hands over (see rankOf). The one thing read from the list
|
|
79214
79262
|
// itself is where it is being held, which is a list item.
|
|
79215
79263
|
const rankOfIndex = itemIndex => itemIndex - runStart;
|
|
79216
|
-
|
|
79217
|
-
// how many items there are, so it asks for the items the list would open
|
|
79218
|
-
// on — counting back from the end when that is where it opens, the way
|
|
79219
|
-
// an HTTP range does.
|
|
79220
|
-
const budget = listItems.pageSize;
|
|
79264
|
+
const budget = itemsPerPage;
|
|
79221
79265
|
let start = missingStart;
|
|
79222
79266
|
let end = missingEnd;
|
|
79223
79267
|
let around;
|
|
@@ -79287,26 +79331,26 @@ const useItemStore = ({
|
|
|
79287
79331
|
around = firstStale.id;
|
|
79288
79332
|
}
|
|
79289
79333
|
} else if (pages.count === undefined) {
|
|
79334
|
+
// The very first ask is what the window misses, like any other: the
|
|
79335
|
+
// run stands for the items up to where the list opens (see
|
|
79336
|
+
// itemCount), so the window frames that place already.
|
|
79290
79337
|
const scrolled = listItems.scrolled;
|
|
79291
|
-
if (scrolled === "end") {
|
|
79292
|
-
//
|
|
79293
|
-
//
|
|
79294
|
-
// there are.
|
|
79338
|
+
if (scrolled === "end" && countKnown === undefined) {
|
|
79339
|
+
// Except the end, which no window can frame before the count is
|
|
79340
|
+
// known: counted back from, the way an HTTP range does.
|
|
79295
79341
|
start = -budget;
|
|
79296
79342
|
end = -1;
|
|
79297
|
-
} else if (
|
|
79298
|
-
//
|
|
79299
|
-
//
|
|
79300
|
-
|
|
79301
|
-
|
|
79302
|
-
|
|
79303
|
-
|
|
79304
|
-
|
|
79343
|
+
} else if (start === -1) {
|
|
79344
|
+
// The window does not reach the run yet: its first page is asked
|
|
79345
|
+
// for all the same, which says how many items it stands for.
|
|
79346
|
+
start = 0;
|
|
79347
|
+
end = budget - 1;
|
|
79348
|
+
}
|
|
79349
|
+
if (scrolled && scrolled.id !== undefined) {
|
|
79350
|
+
// Asked for by name too, since a place can have changed hands since
|
|
79351
|
+
// it was written down: the answer says where it really landed (see
|
|
79352
|
+
// the page's own `start`).
|
|
79305
79353
|
around = scrolled.id;
|
|
79306
|
-
} else {
|
|
79307
|
-
const first = typeof scrolled === "number" ? rankOfIndex(scrolled) - Math.floor(budget / 2) : 0;
|
|
79308
|
-
start = first < 0 ? 0 : first;
|
|
79309
|
-
end = start + budget - 1;
|
|
79310
79354
|
}
|
|
79311
79355
|
}
|
|
79312
79356
|
const ask = () => {
|
|
@@ -79320,6 +79364,12 @@ const useItemStore = ({
|
|
|
79320
79364
|
const debugAsk = outcome => {
|
|
79321
79365
|
debugScroll(`ask ${start}-${end}: ${outcome}`, `(revalidating=${revalidating} holdPending=${listItems.holdPending} count=${pages.count})`);
|
|
79322
79366
|
};
|
|
79367
|
+
if (listItems.windowOnItsWay()) {
|
|
79368
|
+
// The list moved its window after this render drew the one it had:
|
|
79369
|
+
// the run draws the new one in the list's next pass, and asks then.
|
|
79370
|
+
debugAsk("the list is moving its window");
|
|
79371
|
+
return;
|
|
79372
|
+
}
|
|
79323
79373
|
if (start === -1) {
|
|
79324
79374
|
// Nothing missing and nothing to revalidate: the run has what it
|
|
79325
79375
|
// draws.
|
|
@@ -79474,9 +79524,20 @@ const useItemStore = ({
|
|
|
79474
79524
|
done(result);
|
|
79475
79525
|
}
|
|
79476
79526
|
};
|
|
79527
|
+
// Asked once the commit is over rather than from the effect: the list
|
|
79528
|
+
// frames its window after its runs, in that same commit — sized on the
|
|
79529
|
+
// screen, aimed at the item it is held on — and what the run asks for is
|
|
79530
|
+
// the window the list settles on. A render coming meanwhile asks instead.
|
|
79477
79531
|
useLayoutEffect(() => {
|
|
79478
|
-
|
|
79479
|
-
|
|
79532
|
+
askTurnRef.current++;
|
|
79533
|
+
const turn = askTurnRef.current;
|
|
79534
|
+
queueMicrotask(() => {
|
|
79535
|
+
if (turn !== askTurnRef.current || !mountedRef.current) {
|
|
79536
|
+
return;
|
|
79537
|
+
}
|
|
79538
|
+
ask();
|
|
79539
|
+
publishRequestState();
|
|
79540
|
+
});
|
|
79480
79541
|
});
|
|
79481
79542
|
}
|
|
79482
79543
|
};
|