@jsenv/navi 0.29.57 → 0.29.59

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.
@@ -56347,6 +56347,11 @@ const useListScrollSync = ({
56347
56347
  virtual.holdPending = scrolledWanted !== "start" && scrolledWanted !== undefined;
56348
56348
  const total = virtual.totalSignal.peek();
56349
56349
  if (total <= renderBudget) {
56350
+ // The whole collection is what the list draws: wherever in it the list is
56351
+ // held, the window is already its place. Nowhere to move to means nothing
56352
+ // to wait for — a hold left standing here is a list that never asks for
56353
+ // anything again.
56354
+ virtual.holdPending = false;
56350
56355
  return;
56351
56356
  }
56352
56357
  const half = Math.floor(renderBudget / 2);
@@ -56367,6 +56372,9 @@ const useListScrollSync = ({
56367
56372
  }
56368
56373
  }
56369
56374
  if (wantedStart === null) {
56375
+ // Held on a row nobody can place yet: the window frames the start, which
56376
+ // is not where the list is going. The hold stands until the row comes
56377
+ // back — the run asks for it by name (see useRequestMissing).
56370
56378
  return;
56371
56379
  }
56372
56380
  if (wantedStart < 0) {
@@ -58451,6 +58459,7 @@ const VISIBILITY_HIDDEN_STYLE = {
58451
58459
  * memoryBudget?: number,
58452
58460
  * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
58453
58461
  * renderError?: (failure: {error: any, retry: () => void, start: number, end: number}) => import("ignore:preact").ComponentChildren,
58462
+ * onRequestStateChange?: (state: {busy: boolean, refreshing: boolean, range: {start: number, end: number}|null}) => void,
58454
58463
  * }>}
58455
58464
  * @param {(item: any, index: number, state: {refreshing: boolean}) => any} props.renderItem
58456
58465
  * What one row is, given the item and where it sits. `state.refreshing` says
@@ -58488,6 +58497,14 @@ const VISIBILITY_HIDDEN_STYLE = {
58488
58497
  * a `retry` to call, and the `start`/`end` of the range that failed. Defaults
58489
58498
  * to an inline message with a retry button, drawn on the row the user is
58490
58499
  * looking at.
58500
+ * @param {(state: {busy: boolean, refreshing: boolean, range: {start: number, end: number}|null}) => void} [props.onRequestStateChange]
58501
+ * Called when the run starts or stops asking for rows — for the screen around
58502
+ * the list to say that it is looking (the rows themselves have skeletons and
58503
+ * `refreshing` already). `busy` covers every ask, first slice and holes
58504
+ * opened by scrolling included; `refreshing` is the subset where rows already
58505
+ * held are being read again; `range` is what is being asked for, `null` once
58506
+ * nothing is. A range called off and asked again right away stays one `busy`,
58507
+ * and a list unmounted while asking says `busy: false` on its way out.
58491
58508
  */
58492
58509
  const ListItems = ({
58493
58510
  renderItem,
@@ -58499,7 +58516,8 @@ const ListItems = ({
58499
58516
  renderGroupLabel,
58500
58517
  groupLabelProps,
58501
58518
  renderSkeleton,
58502
- renderError
58519
+ renderError,
58520
+ onRequestStateChange
58503
58521
  }) => {
58504
58522
  const ownerId = useId();
58505
58523
  const virtual = useContext(ListVirtualContext);
@@ -58508,7 +58526,8 @@ const ListItems = ({
58508
58526
  const store = useItemStore({
58509
58527
  count,
58510
58528
  itemsAction,
58511
- memoryBudget
58529
+ memoryBudget,
58530
+ onRequestStateChange
58512
58531
  });
58513
58532
  const renderRowSkeleton = renderSkeleton === undefined ? virtual.renderSkeleton : renderSkeleton;
58514
58533
  // A row on its way takes the room the list reserves for it: anything else
@@ -58782,6 +58801,12 @@ const ListItemsFailure = ({
58782
58801
  // hits the network again.
58783
58802
  const ITEM_STORE_MAX_DEFAULT = 1000;
58784
58803
  const ITEM_STORE_KEEP_AROUND = 250;
58804
+ const rangeIsSame = (a, b) => {
58805
+ if (!a || !b) {
58806
+ return a === b;
58807
+ }
58808
+ return a.start === b.start && a.end === b.end;
58809
+ };
58785
58810
 
58786
58811
  // The rows a run has, and how it gets more. Two shapes behind one reader: the
58787
58812
  // caller holds them (items/count/itemStart), or the run asked for them and
@@ -58791,12 +58816,17 @@ const ITEM_STORE_KEEP_AROUND = 250;
58791
58816
  const useItemStore = ({
58792
58817
  count,
58793
58818
  itemsAction,
58794
- memoryBudget
58819
+ memoryBudget,
58820
+ onRequestStateChange
58795
58821
  }) => {
58796
58822
  // What the source kept of the collection when the screen it was on went away
58797
58823
  // (a range reader keeps the composition: see resource_range_reader.js). The
58798
58824
  // rows are drawn from it right away and the window is asked for again — the
58799
58825
  // revalidation below, entered from a fresh mount rather than from a write.
58826
+ // A reader is an interface, not just a function that answers a range: a list
58827
+ // handed something else keeps drawing rows and quietly gives up everything
58828
+ // the reader holds for it (see resource_range_reader.js).
58829
+ useRef(false);
58800
58830
  const pagesRef = useRef(null);
58801
58831
  let restored = false;
58802
58832
  if (!pagesRef.current) {
@@ -58864,6 +58894,51 @@ const useItemStore = ({
58864
58894
  virtual.refreshingSignal.value = virtual.refreshingSignal.peek() - 1;
58865
58895
  };
58866
58896
  }, [refreshing]);
58897
+
58898
+ // What the run is doing, for the screen around the list to draw: the list has
58899
+ // its own skeletons, what is around it has to be told. Read from the request
58900
+ // itself rather than pushed from each place that touches it, so a range
58901
+ // called off and asked again right away stays one uninterrupted ask.
58902
+ const requestStateRef = useRef({
58903
+ busy: false,
58904
+ refreshing: false,
58905
+ range: null
58906
+ });
58907
+ const onRequestStateChangeRef = useRef(onRequestStateChange);
58908
+ onRequestStateChangeRef.current = onRequestStateChange;
58909
+ const publishRequestState = () => {
58910
+ const request = requestRef.current;
58911
+ const busy = request.busy;
58912
+ const state = {
58913
+ busy,
58914
+ refreshing: busy && request.revalidating,
58915
+ range: busy ? {
58916
+ start: request.start,
58917
+ end: request.end
58918
+ } : null
58919
+ };
58920
+ const previous = requestStateRef.current;
58921
+ if (previous.busy === state.busy && previous.refreshing === state.refreshing && rangeIsSame(previous.range, state.range)) {
58922
+ return;
58923
+ }
58924
+ requestStateRef.current = state;
58925
+ if (onRequestStateChangeRef.current) {
58926
+ onRequestStateChangeRef.current(state);
58927
+ }
58928
+ };
58929
+ // A list taken off the screen while it was asking leaves nothing ringing
58930
+ // behind it: whoever is drawing "looking for rows" has to stop.
58931
+ useLayoutEffect(() => {
58932
+ return () => {
58933
+ if (requestStateRef.current.busy && onRequestStateChangeRef.current) {
58934
+ onRequestStateChangeRef.current({
58935
+ busy: false,
58936
+ refreshing: false,
58937
+ range: null
58938
+ });
58939
+ }
58940
+ };
58941
+ }, []);
58867
58942
  const store = {
58868
58943
  rowCount,
58869
58944
  failure,
@@ -58917,7 +58992,22 @@ const useItemStore = ({
58917
58992
  // anchored on the row at its top — a source paginating by cursor gets a
58918
58993
  // row to count from, and the reading position is what must survive.
58919
58994
  const revalidating = staleRef.current;
58920
- if (revalidating) {
58995
+ // The list is held on a row nothing on screen leads to: the rows it holds
58996
+ // do not contain it, so no window it could draw will ever bring it. Only
58997
+ // asking for it by name does.
58998
+ const wanted = virtual.scrolled;
58999
+ const askingAroundWantedRow = revalidating &&
59000
+ // Only while the hold stands: once the user has taken the list over,
59001
+ // the reading position is where they are, not where it opened.
59002
+ virtual.holdPending && wanted && typeof wanted === "object" && wanted.id !== undefined && virtual.locateRow(wanted.id) === null;
59003
+ if (askingAroundWantedRow) {
59004
+ around = wanted.id;
59005
+ // Where it stood when it was written down is enough to frame the ask;
59006
+ // the answer says where it really landed.
59007
+ const from = typeof wanted.index === "number" ? wanted.index - Math.floor(budget / 2) : 0;
59008
+ start = from < 0 ? 0 : from;
59009
+ end = start + budget - 1;
59010
+ } else if (revalidating) {
58921
59011
  start = windowFrom;
58922
59012
  end = windowTo - 1;
58923
59013
  if (end < start) {
@@ -58954,11 +59044,13 @@ const useItemStore = ({
58954
59044
  end = start + budget - 1;
58955
59045
  }
58956
59046
  }
58957
- useLayoutEffect(() => {
59047
+ const ask = () => {
58958
59048
  if (start === -1) {
58959
59049
  return;
58960
59050
  }
58961
- if (virtual.holdPending && pages.count !== undefined) {
59051
+ if (virtual.holdPending && pages.count !== undefined && !askingAroundWantedRow) {
59052
+ // The one ask a hold lets through: the row the list is held on is
59053
+ // what would lift the hold, and nothing else is going to bring it.
58962
59054
  return;
58963
59055
  }
58964
59056
  const request = requestRef.current;
@@ -59027,6 +59119,7 @@ const useItemStore = ({
59027
59119
  staleRef.current = false;
59028
59120
  setRefreshing(false);
59029
59121
  }
59122
+ publishRequestState();
59030
59123
  }
59031
59124
  if (revalidating && !current) {
59032
59125
  // Rows of a composition already superseded by a newer ask: keeping
@@ -59073,6 +59166,7 @@ const useItemStore = ({
59073
59166
  }
59074
59167
  request.busy = false;
59075
59168
  request.revalidating = false;
59169
+ publishRequestState();
59076
59170
  if (revalidating) {
59077
59171
  // The rows from before stay: a revalidation that failed has
59078
59172
  // nothing better to put in their place.
@@ -59101,6 +59195,10 @@ const useItemStore = ({
59101
59195
  } else {
59102
59196
  done(result);
59103
59197
  }
59198
+ };
59199
+ useLayoutEffect(() => {
59200
+ ask();
59201
+ publishRequestState();
59104
59202
  });
59105
59203
  }
59106
59204
  };