@jsenv/navi 0.29.449 → 0.29.451
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 +103 -32
- package/dist/dev/jsenv_navi.js.map +4 -4
- package/dist/jsenv_navi.js +103 -32
- package/dist/jsenv_navi.js.map +4 -4
- 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
|
@@ -42520,6 +42520,7 @@ const useUIStateController = (
|
|
|
42520
42520
|
if (
|
|
42521
42521
|
e.type === "facade_propagate_up" ||
|
|
42522
42522
|
e.type === "cancel_rollback" ||
|
|
42523
|
+
e.type === "navi_reset_ui_state" ||
|
|
42523
42524
|
e.type === "auto_fix"
|
|
42524
42525
|
) {
|
|
42525
42526
|
// Exception: when the facade propagates a child state change up to the
|
|
@@ -42529,7 +42530,9 @@ const useUIStateController = (
|
|
|
42529
42530
|
// the Form's uiAction fires on every value change.
|
|
42530
42531
|
// A cancel takes the same road back: the Form was told what the
|
|
42531
42532
|
// popup was picking, so it has to be told the picker went back to
|
|
42532
|
-
// where it opened, or it sends a value the user said no to.
|
|
42533
|
+
// where it opened, or it sends a value the user said no to. A
|
|
42534
|
+
// reset is that road too, for a field put back after its own
|
|
42535
|
+
// action failed.
|
|
42533
42536
|
// A correction is the same story once more: the Form sends what
|
|
42534
42537
|
// its fields add up to, and a field that just put its own value
|
|
42535
42538
|
// right has to be counted for the corrected one.
|
|
@@ -44595,6 +44598,13 @@ const INTERNAL_EVENT_SET = new Set([
|
|
|
44595
44598
|
// notification below still happen, exactly as they did on the way in (see
|
|
44596
44599
|
// picker_custom.jsx's onClose).
|
|
44597
44600
|
"cancel_rollback",
|
|
44601
|
+
// A rollback navi asks for (see dispatchRequestResetUIState): the action
|
|
44602
|
+
// failed or was aborted, the edit was cancelled, a reset command aimed here.
|
|
44603
|
+
// Nobody acted on the control, so no command — a `<Picker type="confirm"
|
|
44604
|
+
// action command>` refused by the server would otherwise go where only its
|
|
44605
|
+
// success leads. Where the value moves back, uiAction and the parent
|
|
44606
|
+
// notification below still happen, as for cancel_rollback.
|
|
44607
|
+
"navi_reset_ui_state",
|
|
44598
44608
|
// A constraint allowed to correct the value put it right as the value was
|
|
44599
44609
|
// committed (see applyAutoFix). Nobody pressed anything, so no command and
|
|
44600
44610
|
// no action of the control's own — but what it holds really did move, so
|
|
@@ -72291,6 +72301,9 @@ const createListItems = () => {
|
|
|
72291
72301
|
// what puts the view back once they have (see VirtualFiller).
|
|
72292
72302
|
captureAnchor: () => {},
|
|
72293
72303
|
holdViewStill: () => {},
|
|
72304
|
+
// The list has asked for a window no render has drawn yet: a run holding
|
|
72305
|
+
// the one before has nothing to ask for it (see useRequestMissing).
|
|
72306
|
+
windowOnItsWay: () => false,
|
|
72294
72307
|
horizontal: false,
|
|
72295
72308
|
virtualItemSizeSignal: null,
|
|
72296
72309
|
roomById,
|
|
@@ -74460,6 +74473,7 @@ const useListScrollSync = ({
|
|
|
74460
74473
|
const renderWindowRef = useRef(null);
|
|
74461
74474
|
// A window was asked for that no render has drawn yet (see settleWindow).
|
|
74462
74475
|
const windowAskedRef = useRef(false);
|
|
74476
|
+
listItems.windowOnItsWay = () => windowAskedRef.current;
|
|
74463
74477
|
const windowLeavesItemsOut = () => {
|
|
74464
74478
|
const renderWindow = renderWindowRef.current;
|
|
74465
74479
|
if (!renderWindow) {
|
|
@@ -75931,6 +75945,7 @@ const useListScrollSync = ({
|
|
|
75931
75945
|
}, [scrollerElResolved]);
|
|
75932
75946
|
holdWindow();
|
|
75933
75947
|
// Whatever was asked for until now is what this render draws.
|
|
75948
|
+
const windowWasAsked = windowAskedRef.current;
|
|
75934
75949
|
windowAskedRef.current = false;
|
|
75935
75950
|
const {
|
|
75936
75951
|
start: windowStart,
|
|
@@ -75946,13 +75961,15 @@ const useListScrollSync = ({
|
|
|
75946
75961
|
// column — started anywhere but on a line, every item drawn would sit in the
|
|
75947
75962
|
// column of another. Started on one, an item is in the column its place says
|
|
75948
75963
|
// 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
|
|
75964
|
+
// runs are not told about a window that did not move — except after a window
|
|
75965
|
+
// was asked for: a run waiting for it to ask (see useRequestMissing) must be
|
|
75966
|
+
// told, even when the window framed comes back to the same numbers.
|
|
75950
75967
|
const windowDrawnRef = useRef(null);
|
|
75951
75968
|
const drawnStart = windowStart - windowStart % itemsPerLine;
|
|
75952
75969
|
const endPastLine = windowEnd % itemsPerLine;
|
|
75953
75970
|
const drawnEnd = endPastLine === 0 ? windowEnd : windowEnd + itemsPerLine - endPastLine;
|
|
75954
75971
|
let windowDrawn = windowDrawnRef.current;
|
|
75955
|
-
if (!windowDrawn || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
75972
|
+
if (!windowDrawn || windowWasAsked || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
75956
75973
|
windowDrawn = {
|
|
75957
75974
|
start: drawnStart,
|
|
75958
75975
|
end: drawnEnd,
|
|
@@ -78087,6 +78104,15 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
78087
78104
|
* @param {(range: object) => any} [props.itemsAction]
|
|
78088
78105
|
* Where the items come from when the collection is read a slice at a time:
|
|
78089
78106
|
* a resource's range reader (`RESOURCE.GET_RANGE.bindParams(...)`).
|
|
78107
|
+
* @param {number} [props.count]
|
|
78108
|
+
* How many items the collection holds, when the app knows it before the
|
|
78109
|
+
* first answer: a total shown elsewhere, a profile saying how many there are.
|
|
78110
|
+
* The list is then as long as the collection from its first frame: it opens
|
|
78111
|
+
* where it is held, the scrollbar right, and asks for that window. Without
|
|
78112
|
+
* it, the run stands for skeletons up to the item the list opens on and a
|
|
78113
|
+
* page past it, until the first answer says how many there are. Not worth a
|
|
78114
|
+
* request of its own, since the first answer gives it (see docs/scroll.md,
|
|
78115
|
+
* "What a run knows when it opens").
|
|
78090
78116
|
* @param {number} [props.debounce]
|
|
78091
78117
|
* Milliseconds the reader must hold still before the run draws the
|
|
78092
78118
|
* collection it reads: params following a field as the user types
|
|
@@ -78202,6 +78228,7 @@ const ListItemsRun = ({
|
|
|
78202
78228
|
count,
|
|
78203
78229
|
itemsAction,
|
|
78204
78230
|
settling,
|
|
78231
|
+
pageSize: runProps.pageSize,
|
|
78205
78232
|
memoryBudget,
|
|
78206
78233
|
onRequestStateChange,
|
|
78207
78234
|
onCountChange
|
|
@@ -78470,9 +78497,17 @@ const useRunItems = (store, {
|
|
|
78470
78497
|
// A hole with nothing on either side (the scrollbar was thrown into
|
|
78471
78498
|
// territory never visited): grow it both ways around what is on
|
|
78472
78499
|
// screen.
|
|
78473
|
-
|
|
78474
|
-
|
|
78475
|
-
|
|
78500
|
+
askStart = missingStart - Math.floor((itemsPerPage - holeSize) / 2);
|
|
78501
|
+
askEnd = askStart + itemsPerPage - 1;
|
|
78502
|
+
// Cut by an edge of the run (the first page, the last one), the page
|
|
78503
|
+
// takes the room on the other side.
|
|
78504
|
+
if (askStart < runStart) {
|
|
78505
|
+
askEnd += runStart - askStart;
|
|
78506
|
+
askStart = runStart;
|
|
78507
|
+
} else if (askEnd > runEnd - 1) {
|
|
78508
|
+
askStart -= askEnd - (runEnd - 1);
|
|
78509
|
+
askEnd = runEnd - 1;
|
|
78510
|
+
}
|
|
78476
78511
|
}
|
|
78477
78512
|
}
|
|
78478
78513
|
if (askStart < runStart) {
|
|
@@ -78872,6 +78907,18 @@ const ListItemsFailure = ({
|
|
|
78872
78907
|
const ITEM_STORE_MAX_DEFAULT = 1000;
|
|
78873
78908
|
const ITEM_STORE_KEEP_AROUND = 250;
|
|
78874
78909
|
const COUNT_NOT_SAID = {};
|
|
78910
|
+
|
|
78911
|
+
// The index of the item the list is held at, when the hold names one: a number,
|
|
78912
|
+
// or a position remembered with its index (see placeWhereHeld).
|
|
78913
|
+
const readHeldIndex = scrolled => {
|
|
78914
|
+
if (typeof scrolled === "number") {
|
|
78915
|
+
return scrolled;
|
|
78916
|
+
}
|
|
78917
|
+
if (scrolled && typeof scrolled === "object" && typeof scrolled.index === "number") {
|
|
78918
|
+
return scrolled.index;
|
|
78919
|
+
}
|
|
78920
|
+
return null;
|
|
78921
|
+
};
|
|
78875
78922
|
const rangeIsSame = (a, b) => {
|
|
78876
78923
|
if (!a || !b) {
|
|
78877
78924
|
return a === b;
|
|
@@ -78889,6 +78936,7 @@ const useItemStore = ({
|
|
|
78889
78936
|
count,
|
|
78890
78937
|
itemsAction,
|
|
78891
78938
|
settling,
|
|
78939
|
+
pageSize,
|
|
78892
78940
|
memoryBudget,
|
|
78893
78941
|
onRequestStateChange,
|
|
78894
78942
|
onCountChange
|
|
@@ -79082,8 +79130,17 @@ const useItemStore = ({
|
|
|
79082
79130
|
});
|
|
79083
79131
|
// Before the first answer a run does not know how many items it stands for.
|
|
79084
79132
|
// 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
|
-
|
|
79133
|
+
// like items on their way, not like an empty list. A list held on an item
|
|
79134
|
+
// (see placeWhereHeld) is about to be filled there: the run stands for the
|
|
79135
|
+
// items up to that one and a page past it, so the window frames the item
|
|
79136
|
+
// before any answer, and what the run asks for first is that window.
|
|
79137
|
+
const itemsPerPage = pageSize || listItems.pageSize;
|
|
79138
|
+
const countKnown = pages.count ?? count;
|
|
79139
|
+
let itemCount = countKnown;
|
|
79140
|
+
if (itemCount === undefined) {
|
|
79141
|
+
const heldIndex = readHeldIndex(listItems.scrolled);
|
|
79142
|
+
itemCount = heldIndex === null ? itemsPerPage : heldIndex + itemsPerPage;
|
|
79143
|
+
}
|
|
79087
79144
|
// The items drawn are from before while they are read again, and while the
|
|
79088
79145
|
// collection that replaces them waits for its params to hold still (see
|
|
79089
79146
|
// useSettledReader).
|
|
@@ -79102,7 +79159,6 @@ const useItemStore = ({
|
|
|
79102
79159
|
// say: the count the source answered or the caller gave, never the page the
|
|
79103
79160
|
// run stands for before it knows. Said by each run on its first commit, so a
|
|
79104
79161
|
// run drawing another collection takes back what the one before said.
|
|
79105
|
-
const countKnown = pages.count ?? count;
|
|
79106
79162
|
const onCountChangeRef = useRef(onCountChange);
|
|
79107
79163
|
onCountChangeRef.current = onCountChange;
|
|
79108
79164
|
const countSaidRef = useRef(COUNT_NOT_SAID);
|
|
@@ -79150,6 +79206,8 @@ const useItemStore = ({
|
|
|
79150
79206
|
// A list taken off the screen while it was asking leaves nothing ringing
|
|
79151
79207
|
// behind it: whoever is drawing "looking for items" has to stop.
|
|
79152
79208
|
const mountedRef = useRef(false);
|
|
79209
|
+
// Which render's ask is the one to send (see useRequestMissing).
|
|
79210
|
+
const askTurnRef = useRef(0);
|
|
79153
79211
|
useLayoutEffect(() => {
|
|
79154
79212
|
mountedRef.current = true;
|
|
79155
79213
|
return () => {
|
|
@@ -79213,11 +79271,7 @@ const useItemStore = ({
|
|
|
79213
79271
|
// what it hands over (see rankOf). The one thing read from the list
|
|
79214
79272
|
// itself is where it is being held, which is a list item.
|
|
79215
79273
|
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;
|
|
79274
|
+
const budget = itemsPerPage;
|
|
79221
79275
|
let start = missingStart;
|
|
79222
79276
|
let end = missingEnd;
|
|
79223
79277
|
let around;
|
|
@@ -79287,26 +79341,26 @@ const useItemStore = ({
|
|
|
79287
79341
|
around = firstStale.id;
|
|
79288
79342
|
}
|
|
79289
79343
|
} else if (pages.count === undefined) {
|
|
79344
|
+
// The very first ask is what the window misses, like any other: the
|
|
79345
|
+
// run stands for the items up to where the list opens (see
|
|
79346
|
+
// itemCount), so the window frames that place already.
|
|
79290
79347
|
const scrolled = listItems.scrolled;
|
|
79291
|
-
if (scrolled === "end") {
|
|
79292
|
-
//
|
|
79293
|
-
//
|
|
79294
|
-
// there are.
|
|
79348
|
+
if (scrolled === "end" && countKnown === undefined) {
|
|
79349
|
+
// Except the end, which no window can frame before the count is
|
|
79350
|
+
// known: counted back from, the way an HTTP range does.
|
|
79295
79351
|
start = -budget;
|
|
79296
79352
|
end = -1;
|
|
79297
|
-
} else if (
|
|
79298
|
-
//
|
|
79299
|
-
//
|
|
79300
|
-
|
|
79301
|
-
|
|
79302
|
-
|
|
79303
|
-
|
|
79304
|
-
|
|
79353
|
+
} else if (start === -1) {
|
|
79354
|
+
// The window does not reach the run yet: its first page is asked
|
|
79355
|
+
// for all the same, which says how many items it stands for.
|
|
79356
|
+
start = 0;
|
|
79357
|
+
end = budget - 1;
|
|
79358
|
+
}
|
|
79359
|
+
if (scrolled && scrolled.id !== undefined) {
|
|
79360
|
+
// Asked for by name too, since a place can have changed hands since
|
|
79361
|
+
// it was written down: the answer says where it really landed (see
|
|
79362
|
+
// the page's own `start`).
|
|
79305
79363
|
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
79364
|
}
|
|
79311
79365
|
}
|
|
79312
79366
|
const ask = () => {
|
|
@@ -79320,6 +79374,12 @@ const useItemStore = ({
|
|
|
79320
79374
|
const debugAsk = outcome => {
|
|
79321
79375
|
debugScroll(`ask ${start}-${end}: ${outcome}`, `(revalidating=${revalidating} holdPending=${listItems.holdPending} count=${pages.count})`);
|
|
79322
79376
|
};
|
|
79377
|
+
if (listItems.windowOnItsWay()) {
|
|
79378
|
+
// The list moved its window after this render drew the one it had:
|
|
79379
|
+
// the run draws the new one in the list's next pass, and asks then.
|
|
79380
|
+
debugAsk("the list is moving its window");
|
|
79381
|
+
return;
|
|
79382
|
+
}
|
|
79323
79383
|
if (start === -1) {
|
|
79324
79384
|
// Nothing missing and nothing to revalidate: the run has what it
|
|
79325
79385
|
// draws.
|
|
@@ -79474,9 +79534,20 @@ const useItemStore = ({
|
|
|
79474
79534
|
done(result);
|
|
79475
79535
|
}
|
|
79476
79536
|
};
|
|
79537
|
+
// Asked once the commit is over rather than from the effect: the list
|
|
79538
|
+
// frames its window after its runs, in that same commit — sized on the
|
|
79539
|
+
// screen, aimed at the item it is held on — and what the run asks for is
|
|
79540
|
+
// the window the list settles on. A render coming meanwhile asks instead.
|
|
79477
79541
|
useLayoutEffect(() => {
|
|
79478
|
-
|
|
79479
|
-
|
|
79542
|
+
askTurnRef.current++;
|
|
79543
|
+
const turn = askTurnRef.current;
|
|
79544
|
+
queueMicrotask(() => {
|
|
79545
|
+
if (turn !== askTurnRef.current || !mountedRef.current) {
|
|
79546
|
+
return;
|
|
79547
|
+
}
|
|
79548
|
+
ask();
|
|
79549
|
+
publishRequestState();
|
|
79550
|
+
});
|
|
79480
79551
|
});
|
|
79481
79552
|
}
|
|
79482
79553
|
};
|