@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/dev/jsenv_navi.js
CHANGED
|
@@ -43577,6 +43577,7 @@ const useUIStateController = (
|
|
|
43577
43577
|
if (
|
|
43578
43578
|
e.type === "facade_propagate_up" ||
|
|
43579
43579
|
e.type === "cancel_rollback" ||
|
|
43580
|
+
e.type === "navi_reset_ui_state" ||
|
|
43580
43581
|
e.type === "auto_fix"
|
|
43581
43582
|
) {
|
|
43582
43583
|
// Exception: when the facade propagates a child state change up to the
|
|
@@ -43586,7 +43587,9 @@ const useUIStateController = (
|
|
|
43586
43587
|
// the Form's uiAction fires on every value change.
|
|
43587
43588
|
// A cancel takes the same road back: the Form was told what the
|
|
43588
43589
|
// popup was picking, so it has to be told the picker went back to
|
|
43589
|
-
// where it opened, or it sends a value the user said no to.
|
|
43590
|
+
// where it opened, or it sends a value the user said no to. A
|
|
43591
|
+
// reset is that road too, for a field put back after its own
|
|
43592
|
+
// action failed.
|
|
43590
43593
|
// A correction is the same story once more: the Form sends what
|
|
43591
43594
|
// its fields add up to, and a field that just put its own value
|
|
43592
43595
|
// right has to be counted for the corrected one.
|
|
@@ -45705,6 +45708,13 @@ const INTERNAL_EVENT_SET = new Set([
|
|
|
45705
45708
|
// notification below still happen, exactly as they did on the way in (see
|
|
45706
45709
|
// picker_custom.jsx's onClose).
|
|
45707
45710
|
"cancel_rollback",
|
|
45711
|
+
// A rollback navi asks for (see dispatchRequestResetUIState): the action
|
|
45712
|
+
// failed or was aborted, the edit was cancelled, a reset command aimed here.
|
|
45713
|
+
// Nobody acted on the control, so no command — a `<Picker type="confirm"
|
|
45714
|
+
// action command>` refused by the server would otherwise go where only its
|
|
45715
|
+
// success leads. Where the value moves back, uiAction and the parent
|
|
45716
|
+
// notification below still happen, as for cancel_rollback.
|
|
45717
|
+
"navi_reset_ui_state",
|
|
45708
45718
|
// A constraint allowed to correct the value put it right as the value was
|
|
45709
45719
|
// committed (see applyAutoFix). Nobody pressed anything, so no command and
|
|
45710
45720
|
// no action of the control's own — but what it holds really did move, so
|
|
@@ -73697,6 +73707,9 @@ const createListItems = () => {
|
|
|
73697
73707
|
// what puts the view back once they have (see VirtualFiller).
|
|
73698
73708
|
captureAnchor: () => {},
|
|
73699
73709
|
holdViewStill: () => {},
|
|
73710
|
+
// The list has asked for a window no render has drawn yet: a run holding
|
|
73711
|
+
// the one before has nothing to ask for it (see useRequestMissing).
|
|
73712
|
+
windowOnItsWay: () => false,
|
|
73700
73713
|
horizontal: false,
|
|
73701
73714
|
virtualItemSizeSignal: null,
|
|
73702
73715
|
roomById,
|
|
@@ -75905,6 +75918,7 @@ const useListScrollSync = ({
|
|
|
75905
75918
|
const renderWindowRef = useRef(null);
|
|
75906
75919
|
// A window was asked for that no render has drawn yet (see settleWindow).
|
|
75907
75920
|
const windowAskedRef = useRef(false);
|
|
75921
|
+
listItems.windowOnItsWay = () => windowAskedRef.current;
|
|
75908
75922
|
const windowLeavesItemsOut = () => {
|
|
75909
75923
|
const renderWindow = renderWindowRef.current;
|
|
75910
75924
|
if (!renderWindow) {
|
|
@@ -77399,6 +77413,7 @@ const useListScrollSync = ({
|
|
|
77399
77413
|
}, [scrollerElResolved]);
|
|
77400
77414
|
holdWindow();
|
|
77401
77415
|
// Whatever was asked for until now is what this render draws.
|
|
77416
|
+
const windowWasAsked = windowAskedRef.current;
|
|
77402
77417
|
windowAskedRef.current = false;
|
|
77403
77418
|
const {
|
|
77404
77419
|
start: windowStart,
|
|
@@ -77414,13 +77429,15 @@ const useListScrollSync = ({
|
|
|
77414
77429
|
// column — started anywhere but on a line, every item drawn would sit in the
|
|
77415
77430
|
// column of another. Started on one, an item is in the column its place says
|
|
77416
77431
|
// whatever the window frames. The same object while the numbers hold, so the
|
|
77417
|
-
// runs are not told about a window that did not move
|
|
77432
|
+
// runs are not told about a window that did not move — except after a window
|
|
77433
|
+
// was asked for: a run waiting for it to ask (see useRequestMissing) must be
|
|
77434
|
+
// told, even when the window framed comes back to the same numbers.
|
|
77418
77435
|
const windowDrawnRef = useRef(null);
|
|
77419
77436
|
const drawnStart = windowStart - windowStart % itemsPerLine;
|
|
77420
77437
|
const endPastLine = windowEnd % itemsPerLine;
|
|
77421
77438
|
const drawnEnd = endPastLine === 0 ? windowEnd : windowEnd + itemsPerLine - endPastLine;
|
|
77422
77439
|
let windowDrawn = windowDrawnRef.current;
|
|
77423
|
-
if (!windowDrawn || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
77440
|
+
if (!windowDrawn || windowWasAsked || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
77424
77441
|
windowDrawn = {
|
|
77425
77442
|
start: drawnStart,
|
|
77426
77443
|
end: drawnEnd,
|
|
@@ -79659,6 +79676,15 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
79659
79676
|
* @param {(range: object) => any} [props.itemsAction]
|
|
79660
79677
|
* Where the items come from when the collection is read a slice at a time:
|
|
79661
79678
|
* a resource's range reader (`RESOURCE.GET_RANGE.bindParams(...)`).
|
|
79679
|
+
* @param {number} [props.count]
|
|
79680
|
+
* How many items the collection holds, when the app knows it before the
|
|
79681
|
+
* first answer: a total shown elsewhere, a profile saying how many there are.
|
|
79682
|
+
* The list is then as long as the collection from its first frame: it opens
|
|
79683
|
+
* where it is held, the scrollbar right, and asks for that window. Without
|
|
79684
|
+
* it, the run stands for skeletons up to the item the list opens on and a
|
|
79685
|
+
* page past it, until the first answer says how many there are. Not worth a
|
|
79686
|
+
* request of its own, since the first answer gives it (see docs/scroll.md,
|
|
79687
|
+
* "What a run knows when it opens").
|
|
79662
79688
|
* @param {number} [props.debounce]
|
|
79663
79689
|
* Milliseconds the reader must hold still before the run draws the
|
|
79664
79690
|
* collection it reads: params following a field as the user types
|
|
@@ -79774,6 +79800,7 @@ const ListItemsRun = ({
|
|
|
79774
79800
|
count,
|
|
79775
79801
|
itemsAction,
|
|
79776
79802
|
settling,
|
|
79803
|
+
pageSize: runProps.pageSize,
|
|
79777
79804
|
memoryBudget,
|
|
79778
79805
|
onRequestStateChange,
|
|
79779
79806
|
onCountChange
|
|
@@ -80042,9 +80069,17 @@ const useRunItems = (store, {
|
|
|
80042
80069
|
// A hole with nothing on either side (the scrollbar was thrown into
|
|
80043
80070
|
// territory never visited): grow it both ways around what is on
|
|
80044
80071
|
// screen.
|
|
80045
|
-
|
|
80046
|
-
|
|
80047
|
-
|
|
80072
|
+
askStart = missingStart - Math.floor((itemsPerPage - holeSize) / 2);
|
|
80073
|
+
askEnd = askStart + itemsPerPage - 1;
|
|
80074
|
+
// Cut by an edge of the run (the first page, the last one), the page
|
|
80075
|
+
// takes the room on the other side.
|
|
80076
|
+
if (askStart < runStart) {
|
|
80077
|
+
askEnd += runStart - askStart;
|
|
80078
|
+
askStart = runStart;
|
|
80079
|
+
} else if (askEnd > runEnd - 1) {
|
|
80080
|
+
askStart -= askEnd - (runEnd - 1);
|
|
80081
|
+
askEnd = runEnd - 1;
|
|
80082
|
+
}
|
|
80048
80083
|
}
|
|
80049
80084
|
}
|
|
80050
80085
|
if (askStart < runStart) {
|
|
@@ -80444,6 +80479,18 @@ const ListItemsFailure = ({
|
|
|
80444
80479
|
const ITEM_STORE_MAX_DEFAULT = 1000;
|
|
80445
80480
|
const ITEM_STORE_KEEP_AROUND = 250;
|
|
80446
80481
|
const COUNT_NOT_SAID = {};
|
|
80482
|
+
|
|
80483
|
+
// The index of the item the list is held at, when the hold names one: a number,
|
|
80484
|
+
// or a position remembered with its index (see placeWhereHeld).
|
|
80485
|
+
const readHeldIndex = scrolled => {
|
|
80486
|
+
if (typeof scrolled === "number") {
|
|
80487
|
+
return scrolled;
|
|
80488
|
+
}
|
|
80489
|
+
if (scrolled && typeof scrolled === "object" && typeof scrolled.index === "number") {
|
|
80490
|
+
return scrolled.index;
|
|
80491
|
+
}
|
|
80492
|
+
return null;
|
|
80493
|
+
};
|
|
80447
80494
|
const rangeIsSame = (a, b) => {
|
|
80448
80495
|
if (!a || !b) {
|
|
80449
80496
|
return a === b;
|
|
@@ -80461,6 +80508,7 @@ const useItemStore = ({
|
|
|
80461
80508
|
count,
|
|
80462
80509
|
itemsAction,
|
|
80463
80510
|
settling,
|
|
80511
|
+
pageSize,
|
|
80464
80512
|
memoryBudget,
|
|
80465
80513
|
onRequestStateChange,
|
|
80466
80514
|
onCountChange
|
|
@@ -80663,8 +80711,17 @@ const useItemStore = ({
|
|
|
80663
80711
|
});
|
|
80664
80712
|
// Before the first answer a run does not know how many items it stands for.
|
|
80665
80713
|
// It stands for a page of them: a list that is about to be filled looks
|
|
80666
|
-
// like items on their way, not like an empty list.
|
|
80667
|
-
|
|
80714
|
+
// like items on their way, not like an empty list. A list held on an item
|
|
80715
|
+
// (see placeWhereHeld) is about to be filled there: the run stands for the
|
|
80716
|
+
// items up to that one and a page past it, so the window frames the item
|
|
80717
|
+
// before any answer, and what the run asks for first is that window.
|
|
80718
|
+
const itemsPerPage = pageSize || listItems.pageSize;
|
|
80719
|
+
const countKnown = pages.count ?? count;
|
|
80720
|
+
let itemCount = countKnown;
|
|
80721
|
+
if (itemCount === undefined) {
|
|
80722
|
+
const heldIndex = readHeldIndex(listItems.scrolled);
|
|
80723
|
+
itemCount = heldIndex === null ? itemsPerPage : heldIndex + itemsPerPage;
|
|
80724
|
+
}
|
|
80668
80725
|
// The items drawn are from before while they are read again, and while the
|
|
80669
80726
|
// collection that replaces them waits for its params to hold still (see
|
|
80670
80727
|
// useSettledReader).
|
|
@@ -80683,7 +80740,6 @@ const useItemStore = ({
|
|
|
80683
80740
|
// say: the count the source answered or the caller gave, never the page the
|
|
80684
80741
|
// run stands for before it knows. Said by each run on its first commit, so a
|
|
80685
80742
|
// run drawing another collection takes back what the one before said.
|
|
80686
|
-
const countKnown = pages.count ?? count;
|
|
80687
80743
|
const onCountChangeRef = useRef(onCountChange);
|
|
80688
80744
|
onCountChangeRef.current = onCountChange;
|
|
80689
80745
|
const countSaidRef = useRef(COUNT_NOT_SAID);
|
|
@@ -80731,6 +80787,8 @@ const useItemStore = ({
|
|
|
80731
80787
|
// A list taken off the screen while it was asking leaves nothing ringing
|
|
80732
80788
|
// behind it: whoever is drawing "looking for items" has to stop.
|
|
80733
80789
|
const mountedRef = useRef(false);
|
|
80790
|
+
// Which render's ask is the one to send (see useRequestMissing).
|
|
80791
|
+
const askTurnRef = useRef(0);
|
|
80734
80792
|
useLayoutEffect(() => {
|
|
80735
80793
|
mountedRef.current = true;
|
|
80736
80794
|
return () => {
|
|
@@ -80794,11 +80852,7 @@ const useItemStore = ({
|
|
|
80794
80852
|
// what it hands over (see rankOf). The one thing read from the list
|
|
80795
80853
|
// itself is where it is being held, which is a list item.
|
|
80796
80854
|
const rankOfIndex = itemIndex => itemIndex - runStart;
|
|
80797
|
-
|
|
80798
|
-
// how many items there are, so it asks for the items the list would open
|
|
80799
|
-
// on — counting back from the end when that is where it opens, the way
|
|
80800
|
-
// an HTTP range does.
|
|
80801
|
-
const budget = listItems.pageSize;
|
|
80855
|
+
const budget = itemsPerPage;
|
|
80802
80856
|
let start = missingStart;
|
|
80803
80857
|
let end = missingEnd;
|
|
80804
80858
|
let around;
|
|
@@ -80868,26 +80922,26 @@ const useItemStore = ({
|
|
|
80868
80922
|
around = firstStale.id;
|
|
80869
80923
|
}
|
|
80870
80924
|
} else if (pages.count === undefined) {
|
|
80925
|
+
// The very first ask is what the window misses, like any other: the
|
|
80926
|
+
// run stands for the items up to where the list opens (see
|
|
80927
|
+
// itemCount), so the window frames that place already.
|
|
80871
80928
|
const scrolled = listItems.scrolled;
|
|
80872
|
-
if (scrolled === "end") {
|
|
80873
|
-
//
|
|
80874
|
-
//
|
|
80875
|
-
// there are.
|
|
80929
|
+
if (scrolled === "end" && countKnown === undefined) {
|
|
80930
|
+
// Except the end, which no window can frame before the count is
|
|
80931
|
+
// known: counted back from, the way an HTTP range does.
|
|
80876
80932
|
start = -budget;
|
|
80877
80933
|
end = -1;
|
|
80878
|
-
} else if (
|
|
80879
|
-
//
|
|
80880
|
-
//
|
|
80881
|
-
|
|
80882
|
-
|
|
80883
|
-
|
|
80884
|
-
|
|
80885
|
-
|
|
80934
|
+
} else if (start === -1) {
|
|
80935
|
+
// The window does not reach the run yet: its first page is asked
|
|
80936
|
+
// for all the same, which says how many items it stands for.
|
|
80937
|
+
start = 0;
|
|
80938
|
+
end = budget - 1;
|
|
80939
|
+
}
|
|
80940
|
+
if (scrolled && scrolled.id !== undefined) {
|
|
80941
|
+
// Asked for by name too, since a place can have changed hands since
|
|
80942
|
+
// it was written down: the answer says where it really landed (see
|
|
80943
|
+
// the page's own `start`).
|
|
80886
80944
|
around = scrolled.id;
|
|
80887
|
-
} else {
|
|
80888
|
-
const first = typeof scrolled === "number" ? rankOfIndex(scrolled) - Math.floor(budget / 2) : 0;
|
|
80889
|
-
start = first < 0 ? 0 : first;
|
|
80890
|
-
end = start + budget - 1;
|
|
80891
80945
|
}
|
|
80892
80946
|
}
|
|
80893
80947
|
const ask = () => {
|
|
@@ -80901,6 +80955,12 @@ const useItemStore = ({
|
|
|
80901
80955
|
const debugAsk = outcome => {
|
|
80902
80956
|
debugScroll(`ask ${start}-${end}: ${outcome}`, `(revalidating=${revalidating} holdPending=${listItems.holdPending} count=${pages.count})`);
|
|
80903
80957
|
};
|
|
80958
|
+
if (listItems.windowOnItsWay()) {
|
|
80959
|
+
// The list moved its window after this render drew the one it had:
|
|
80960
|
+
// the run draws the new one in the list's next pass, and asks then.
|
|
80961
|
+
debugAsk("the list is moving its window");
|
|
80962
|
+
return;
|
|
80963
|
+
}
|
|
80904
80964
|
if (start === -1) {
|
|
80905
80965
|
// Nothing missing and nothing to revalidate: the run has what it
|
|
80906
80966
|
// draws.
|
|
@@ -81055,9 +81115,20 @@ const useItemStore = ({
|
|
|
81055
81115
|
done(result);
|
|
81056
81116
|
}
|
|
81057
81117
|
};
|
|
81118
|
+
// Asked once the commit is over rather than from the effect: the list
|
|
81119
|
+
// frames its window after its runs, in that same commit — sized on the
|
|
81120
|
+
// screen, aimed at the item it is held on — and what the run asks for is
|
|
81121
|
+
// the window the list settles on. A render coming meanwhile asks instead.
|
|
81058
81122
|
useLayoutEffect(() => {
|
|
81059
|
-
|
|
81060
|
-
|
|
81123
|
+
askTurnRef.current++;
|
|
81124
|
+
const turn = askTurnRef.current;
|
|
81125
|
+
queueMicrotask(() => {
|
|
81126
|
+
if (turn !== askTurnRef.current || !mountedRef.current) {
|
|
81127
|
+
return;
|
|
81128
|
+
}
|
|
81129
|
+
ask();
|
|
81130
|
+
publishRequestState();
|
|
81131
|
+
});
|
|
81061
81132
|
});
|
|
81062
81133
|
}
|
|
81063
81134
|
};
|