@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.
@@ -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
- const grow = Math.floor((itemsPerPage - holeSize) / 2);
78474
- askStart = missingStart - grow;
78475
- askEnd = missingEnd + grow;
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
- const itemCount = pages.count ?? count ?? listItems.pageSize;
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
- // The very first ask has nothing to go on: the run does not even know
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
- // Counting back from the end, the way an HTTP range does: a list
79293
- // opening on its last items asks for them before it knows how many
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 (scrolled && scrolled.id !== undefined) {
79298
- // Asked for by name, since a place can have changed hands since it
79299
- // was written down — but the place it had is sent too, so a source
79300
- // that paginates by index has something to work with, and the answer
79301
- // says where it really landed (see the page's own `start`).
79302
- const from = typeof scrolled.index === "number" ? rankOfIndex(scrolled.index) - Math.floor(budget / 2) : 0;
79303
- start = from < 0 ? 0 : from;
79304
- end = start + budget - 1;
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
- ask();
79479
- publishRequestState();
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
  };