@jsenv/navi 0.29.57 → 0.29.58

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.
@@ -58451,6 +58451,7 @@ const VISIBILITY_HIDDEN_STYLE = {
58451
58451
  * memoryBudget?: number,
58452
58452
  * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
58453
58453
  * renderError?: (failure: {error: any, retry: () => void, start: number, end: number}) => import("ignore:preact").ComponentChildren,
58454
+ * onRequestStateChange?: (state: {busy: boolean, refreshing: boolean, range: {start: number, end: number}|null}) => void,
58454
58455
  * }>}
58455
58456
  * @param {(item: any, index: number, state: {refreshing: boolean}) => any} props.renderItem
58456
58457
  * What one row is, given the item and where it sits. `state.refreshing` says
@@ -58488,6 +58489,14 @@ const VISIBILITY_HIDDEN_STYLE = {
58488
58489
  * a `retry` to call, and the `start`/`end` of the range that failed. Defaults
58489
58490
  * to an inline message with a retry button, drawn on the row the user is
58490
58491
  * looking at.
58492
+ * @param {(state: {busy: boolean, refreshing: boolean, range: {start: number, end: number}|null}) => void} [props.onRequestStateChange]
58493
+ * Called when the run starts or stops asking for rows — for the screen around
58494
+ * the list to say that it is looking (the rows themselves have skeletons and
58495
+ * `refreshing` already). `busy` covers every ask, first slice and holes
58496
+ * opened by scrolling included; `refreshing` is the subset where rows already
58497
+ * held are being read again; `range` is what is being asked for, `null` once
58498
+ * nothing is. A range called off and asked again right away stays one `busy`,
58499
+ * and a list unmounted while asking says `busy: false` on its way out.
58491
58500
  */
58492
58501
  const ListItems = ({
58493
58502
  renderItem,
@@ -58499,7 +58508,8 @@ const ListItems = ({
58499
58508
  renderGroupLabel,
58500
58509
  groupLabelProps,
58501
58510
  renderSkeleton,
58502
- renderError
58511
+ renderError,
58512
+ onRequestStateChange
58503
58513
  }) => {
58504
58514
  const ownerId = useId();
58505
58515
  const virtual = useContext(ListVirtualContext);
@@ -58508,7 +58518,8 @@ const ListItems = ({
58508
58518
  const store = useItemStore({
58509
58519
  count,
58510
58520
  itemsAction,
58511
- memoryBudget
58521
+ memoryBudget,
58522
+ onRequestStateChange
58512
58523
  });
58513
58524
  const renderRowSkeleton = renderSkeleton === undefined ? virtual.renderSkeleton : renderSkeleton;
58514
58525
  // A row on its way takes the room the list reserves for it: anything else
@@ -58782,6 +58793,12 @@ const ListItemsFailure = ({
58782
58793
  // hits the network again.
58783
58794
  const ITEM_STORE_MAX_DEFAULT = 1000;
58784
58795
  const ITEM_STORE_KEEP_AROUND = 250;
58796
+ const rangeIsSame = (a, b) => {
58797
+ if (!a || !b) {
58798
+ return a === b;
58799
+ }
58800
+ return a.start === b.start && a.end === b.end;
58801
+ };
58785
58802
 
58786
58803
  // The rows a run has, and how it gets more. Two shapes behind one reader: the
58787
58804
  // caller holds them (items/count/itemStart), or the run asked for them and
@@ -58791,12 +58808,17 @@ const ITEM_STORE_KEEP_AROUND = 250;
58791
58808
  const useItemStore = ({
58792
58809
  count,
58793
58810
  itemsAction,
58794
- memoryBudget
58811
+ memoryBudget,
58812
+ onRequestStateChange
58795
58813
  }) => {
58796
58814
  // What the source kept of the collection when the screen it was on went away
58797
58815
  // (a range reader keeps the composition: see resource_range_reader.js). The
58798
58816
  // rows are drawn from it right away and the window is asked for again — the
58799
58817
  // revalidation below, entered from a fresh mount rather than from a write.
58818
+ // A reader is an interface, not just a function that answers a range: a list
58819
+ // handed something else keeps drawing rows and quietly gives up everything
58820
+ // the reader holds for it (see resource_range_reader.js).
58821
+ useRef(false);
58800
58822
  const pagesRef = useRef(null);
58801
58823
  let restored = false;
58802
58824
  if (!pagesRef.current) {
@@ -58864,6 +58886,51 @@ const useItemStore = ({
58864
58886
  virtual.refreshingSignal.value = virtual.refreshingSignal.peek() - 1;
58865
58887
  };
58866
58888
  }, [refreshing]);
58889
+
58890
+ // What the run is doing, for the screen around the list to draw: the list has
58891
+ // its own skeletons, what is around it has to be told. Read from the request
58892
+ // itself rather than pushed from each place that touches it, so a range
58893
+ // called off and asked again right away stays one uninterrupted ask.
58894
+ const requestStateRef = useRef({
58895
+ busy: false,
58896
+ refreshing: false,
58897
+ range: null
58898
+ });
58899
+ const onRequestStateChangeRef = useRef(onRequestStateChange);
58900
+ onRequestStateChangeRef.current = onRequestStateChange;
58901
+ const publishRequestState = () => {
58902
+ const request = requestRef.current;
58903
+ const busy = request.busy;
58904
+ const state = {
58905
+ busy,
58906
+ refreshing: busy && request.revalidating,
58907
+ range: busy ? {
58908
+ start: request.start,
58909
+ end: request.end
58910
+ } : null
58911
+ };
58912
+ const previous = requestStateRef.current;
58913
+ if (previous.busy === state.busy && previous.refreshing === state.refreshing && rangeIsSame(previous.range, state.range)) {
58914
+ return;
58915
+ }
58916
+ requestStateRef.current = state;
58917
+ if (onRequestStateChangeRef.current) {
58918
+ onRequestStateChangeRef.current(state);
58919
+ }
58920
+ };
58921
+ // A list taken off the screen while it was asking leaves nothing ringing
58922
+ // behind it: whoever is drawing "looking for rows" has to stop.
58923
+ useLayoutEffect(() => {
58924
+ return () => {
58925
+ if (requestStateRef.current.busy && onRequestStateChangeRef.current) {
58926
+ onRequestStateChangeRef.current({
58927
+ busy: false,
58928
+ refreshing: false,
58929
+ range: null
58930
+ });
58931
+ }
58932
+ };
58933
+ }, []);
58867
58934
  const store = {
58868
58935
  rowCount,
58869
58936
  failure,
@@ -58954,7 +59021,7 @@ const useItemStore = ({
58954
59021
  end = start + budget - 1;
58955
59022
  }
58956
59023
  }
58957
- useLayoutEffect(() => {
59024
+ const ask = () => {
58958
59025
  if (start === -1) {
58959
59026
  return;
58960
59027
  }
@@ -59027,6 +59094,7 @@ const useItemStore = ({
59027
59094
  staleRef.current = false;
59028
59095
  setRefreshing(false);
59029
59096
  }
59097
+ publishRequestState();
59030
59098
  }
59031
59099
  if (revalidating && !current) {
59032
59100
  // Rows of a composition already superseded by a newer ask: keeping
@@ -59073,6 +59141,7 @@ const useItemStore = ({
59073
59141
  }
59074
59142
  request.busy = false;
59075
59143
  request.revalidating = false;
59144
+ publishRequestState();
59076
59145
  if (revalidating) {
59077
59146
  // The rows from before stay: a revalidation that failed has
59078
59147
  // nothing better to put in their place.
@@ -59101,6 +59170,10 @@ const useItemStore = ({
59101
59170
  } else {
59102
59171
  done(result);
59103
59172
  }
59173
+ };
59174
+ useLayoutEffect(() => {
59175
+ ask();
59176
+ publishRequestState();
59104
59177
  });
59105
59178
  }
59106
59179
  };