@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.
@@ -73697,6 +73697,9 @@ const createListItems = () => {
73697
73697
  // what puts the view back once they have (see VirtualFiller).
73698
73698
  captureAnchor: () => {},
73699
73699
  holdViewStill: () => {},
73700
+ // The list has asked for a window no render has drawn yet: a run holding
73701
+ // the one before has nothing to ask for it (see useRequestMissing).
73702
+ windowOnItsWay: () => false,
73700
73703
  horizontal: false,
73701
73704
  virtualItemSizeSignal: null,
73702
73705
  roomById,
@@ -75905,6 +75908,7 @@ const useListScrollSync = ({
75905
75908
  const renderWindowRef = useRef(null);
75906
75909
  // A window was asked for that no render has drawn yet (see settleWindow).
75907
75910
  const windowAskedRef = useRef(false);
75911
+ listItems.windowOnItsWay = () => windowAskedRef.current;
75908
75912
  const windowLeavesItemsOut = () => {
75909
75913
  const renderWindow = renderWindowRef.current;
75910
75914
  if (!renderWindow) {
@@ -77399,6 +77403,7 @@ const useListScrollSync = ({
77399
77403
  }, [scrollerElResolved]);
77400
77404
  holdWindow();
77401
77405
  // Whatever was asked for until now is what this render draws.
77406
+ const windowWasAsked = windowAskedRef.current;
77402
77407
  windowAskedRef.current = false;
77403
77408
  const {
77404
77409
  start: windowStart,
@@ -77414,13 +77419,15 @@ const useListScrollSync = ({
77414
77419
  // column — started anywhere but on a line, every item drawn would sit in the
77415
77420
  // column of another. Started on one, an item is in the column its place says
77416
77421
  // 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.
77422
+ // runs are not told about a window that did not move — except after a window
77423
+ // was asked for: a run waiting for it to ask (see useRequestMissing) must be
77424
+ // told, even when the window framed comes back to the same numbers.
77418
77425
  const windowDrawnRef = useRef(null);
77419
77426
  const drawnStart = windowStart - windowStart % itemsPerLine;
77420
77427
  const endPastLine = windowEnd % itemsPerLine;
77421
77428
  const drawnEnd = endPastLine === 0 ? windowEnd : windowEnd + itemsPerLine - endPastLine;
77422
77429
  let windowDrawn = windowDrawnRef.current;
77423
- if (!windowDrawn || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
77430
+ if (!windowDrawn || windowWasAsked || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
77424
77431
  windowDrawn = {
77425
77432
  start: drawnStart,
77426
77433
  end: drawnEnd,
@@ -79659,6 +79666,15 @@ const VISIBILITY_HIDDEN_STYLE = {
79659
79666
  * @param {(range: object) => any} [props.itemsAction]
79660
79667
  * Where the items come from when the collection is read a slice at a time:
79661
79668
  * a resource's range reader (`RESOURCE.GET_RANGE.bindParams(...)`).
79669
+ * @param {number} [props.count]
79670
+ * How many items the collection holds, when the app knows it before the
79671
+ * first answer: a total shown elsewhere, a profile saying how many there are.
79672
+ * The list is then as long as the collection from its first frame: it opens
79673
+ * where it is held, the scrollbar right, and asks for that window. Without
79674
+ * it, the run stands for skeletons up to the item the list opens on and a
79675
+ * page past it, until the first answer says how many there are. Not worth a
79676
+ * request of its own, since the first answer gives it (see docs/scroll.md,
79677
+ * "What a run knows when it opens").
79662
79678
  * @param {number} [props.debounce]
79663
79679
  * Milliseconds the reader must hold still before the run draws the
79664
79680
  * collection it reads: params following a field as the user types
@@ -79774,6 +79790,7 @@ const ListItemsRun = ({
79774
79790
  count,
79775
79791
  itemsAction,
79776
79792
  settling,
79793
+ pageSize: runProps.pageSize,
79777
79794
  memoryBudget,
79778
79795
  onRequestStateChange,
79779
79796
  onCountChange
@@ -80042,9 +80059,17 @@ const useRunItems = (store, {
80042
80059
  // A hole with nothing on either side (the scrollbar was thrown into
80043
80060
  // territory never visited): grow it both ways around what is on
80044
80061
  // screen.
80045
- const grow = Math.floor((itemsPerPage - holeSize) / 2);
80046
- askStart = missingStart - grow;
80047
- askEnd = missingEnd + grow;
80062
+ askStart = missingStart - Math.floor((itemsPerPage - holeSize) / 2);
80063
+ askEnd = askStart + itemsPerPage - 1;
80064
+ // Cut by an edge of the run (the first page, the last one), the page
80065
+ // takes the room on the other side.
80066
+ if (askStart < runStart) {
80067
+ askEnd += runStart - askStart;
80068
+ askStart = runStart;
80069
+ } else if (askEnd > runEnd - 1) {
80070
+ askStart -= askEnd - (runEnd - 1);
80071
+ askEnd = runEnd - 1;
80072
+ }
80048
80073
  }
80049
80074
  }
80050
80075
  if (askStart < runStart) {
@@ -80444,6 +80469,18 @@ const ListItemsFailure = ({
80444
80469
  const ITEM_STORE_MAX_DEFAULT = 1000;
80445
80470
  const ITEM_STORE_KEEP_AROUND = 250;
80446
80471
  const COUNT_NOT_SAID = {};
80472
+
80473
+ // The index of the item the list is held at, when the hold names one: a number,
80474
+ // or a position remembered with its index (see placeWhereHeld).
80475
+ const readHeldIndex = scrolled => {
80476
+ if (typeof scrolled === "number") {
80477
+ return scrolled;
80478
+ }
80479
+ if (scrolled && typeof scrolled === "object" && typeof scrolled.index === "number") {
80480
+ return scrolled.index;
80481
+ }
80482
+ return null;
80483
+ };
80447
80484
  const rangeIsSame = (a, b) => {
80448
80485
  if (!a || !b) {
80449
80486
  return a === b;
@@ -80461,6 +80498,7 @@ const useItemStore = ({
80461
80498
  count,
80462
80499
  itemsAction,
80463
80500
  settling,
80501
+ pageSize,
80464
80502
  memoryBudget,
80465
80503
  onRequestStateChange,
80466
80504
  onCountChange
@@ -80663,8 +80701,17 @@ const useItemStore = ({
80663
80701
  });
80664
80702
  // Before the first answer a run does not know how many items it stands for.
80665
80703
  // 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
- const itemCount = pages.count ?? count ?? listItems.pageSize;
80704
+ // like items on their way, not like an empty list. A list held on an item
80705
+ // (see placeWhereHeld) is about to be filled there: the run stands for the
80706
+ // items up to that one and a page past it, so the window frames the item
80707
+ // before any answer, and what the run asks for first is that window.
80708
+ const itemsPerPage = pageSize || listItems.pageSize;
80709
+ const countKnown = pages.count ?? count;
80710
+ let itemCount = countKnown;
80711
+ if (itemCount === undefined) {
80712
+ const heldIndex = readHeldIndex(listItems.scrolled);
80713
+ itemCount = heldIndex === null ? itemsPerPage : heldIndex + itemsPerPage;
80714
+ }
80668
80715
  // The items drawn are from before while they are read again, and while the
80669
80716
  // collection that replaces them waits for its params to hold still (see
80670
80717
  // useSettledReader).
@@ -80683,7 +80730,6 @@ const useItemStore = ({
80683
80730
  // say: the count the source answered or the caller gave, never the page the
80684
80731
  // run stands for before it knows. Said by each run on its first commit, so a
80685
80732
  // run drawing another collection takes back what the one before said.
80686
- const countKnown = pages.count ?? count;
80687
80733
  const onCountChangeRef = useRef(onCountChange);
80688
80734
  onCountChangeRef.current = onCountChange;
80689
80735
  const countSaidRef = useRef(COUNT_NOT_SAID);
@@ -80731,6 +80777,8 @@ const useItemStore = ({
80731
80777
  // A list taken off the screen while it was asking leaves nothing ringing
80732
80778
  // behind it: whoever is drawing "looking for items" has to stop.
80733
80779
  const mountedRef = useRef(false);
80780
+ // Which render's ask is the one to send (see useRequestMissing).
80781
+ const askTurnRef = useRef(0);
80734
80782
  useLayoutEffect(() => {
80735
80783
  mountedRef.current = true;
80736
80784
  return () => {
@@ -80794,11 +80842,7 @@ const useItemStore = ({
80794
80842
  // what it hands over (see rankOf). The one thing read from the list
80795
80843
  // itself is where it is being held, which is a list item.
80796
80844
  const rankOfIndex = itemIndex => itemIndex - runStart;
80797
- // The very first ask has nothing to go on: the run does not even know
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;
80845
+ const budget = itemsPerPage;
80802
80846
  let start = missingStart;
80803
80847
  let end = missingEnd;
80804
80848
  let around;
@@ -80868,26 +80912,26 @@ const useItemStore = ({
80868
80912
  around = firstStale.id;
80869
80913
  }
80870
80914
  } else if (pages.count === undefined) {
80915
+ // The very first ask is what the window misses, like any other: the
80916
+ // run stands for the items up to where the list opens (see
80917
+ // itemCount), so the window frames that place already.
80871
80918
  const scrolled = listItems.scrolled;
80872
- if (scrolled === "end") {
80873
- // Counting back from the end, the way an HTTP range does: a list
80874
- // opening on its last items asks for them before it knows how many
80875
- // there are.
80919
+ if (scrolled === "end" && countKnown === undefined) {
80920
+ // Except the end, which no window can frame before the count is
80921
+ // known: counted back from, the way an HTTP range does.
80876
80922
  start = -budget;
80877
80923
  end = -1;
80878
- } else if (scrolled && scrolled.id !== undefined) {
80879
- // Asked for by name, since a place can have changed hands since it
80880
- // was written down — but the place it had is sent too, so a source
80881
- // that paginates by index has something to work with, and the answer
80882
- // says where it really landed (see the page's own `start`).
80883
- const from = typeof scrolled.index === "number" ? rankOfIndex(scrolled.index) - Math.floor(budget / 2) : 0;
80884
- start = from < 0 ? 0 : from;
80885
- end = start + budget - 1;
80924
+ } else if (start === -1) {
80925
+ // The window does not reach the run yet: its first page is asked
80926
+ // for all the same, which says how many items it stands for.
80927
+ start = 0;
80928
+ end = budget - 1;
80929
+ }
80930
+ if (scrolled && scrolled.id !== undefined) {
80931
+ // Asked for by name too, since a place can have changed hands since
80932
+ // it was written down: the answer says where it really landed (see
80933
+ // the page's own `start`).
80886
80934
  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
80935
  }
80892
80936
  }
80893
80937
  const ask = () => {
@@ -80901,6 +80945,12 @@ const useItemStore = ({
80901
80945
  const debugAsk = outcome => {
80902
80946
  debugScroll(`ask ${start}-${end}: ${outcome}`, `(revalidating=${revalidating} holdPending=${listItems.holdPending} count=${pages.count})`);
80903
80947
  };
80948
+ if (listItems.windowOnItsWay()) {
80949
+ // The list moved its window after this render drew the one it had:
80950
+ // the run draws the new one in the list's next pass, and asks then.
80951
+ debugAsk("the list is moving its window");
80952
+ return;
80953
+ }
80904
80954
  if (start === -1) {
80905
80955
  // Nothing missing and nothing to revalidate: the run has what it
80906
80956
  // draws.
@@ -81055,9 +81105,20 @@ const useItemStore = ({
81055
81105
  done(result);
81056
81106
  }
81057
81107
  };
81108
+ // Asked once the commit is over rather than from the effect: the list
81109
+ // frames its window after its runs, in that same commit — sized on the
81110
+ // screen, aimed at the item it is held on — and what the run asks for is
81111
+ // the window the list settles on. A render coming meanwhile asks instead.
81058
81112
  useLayoutEffect(() => {
81059
- ask();
81060
- publishRequestState();
81113
+ askTurnRef.current++;
81114
+ const turn = askTurnRef.current;
81115
+ queueMicrotask(() => {
81116
+ if (turn !== askTurnRef.current || !mountedRef.current) {
81117
+ return;
81118
+ }
81119
+ ask();
81120
+ publishRequestState();
81121
+ });
81061
81122
  });
81062
81123
  }
81063
81124
  };