@jsenv/navi 0.29.227 → 0.29.229

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.
@@ -3149,6 +3149,12 @@ const actionWeakMap = new WeakMap();
3149
3149
  *
3150
3150
  * @param {Function} callback
3151
3151
  * @param {object} [rootOptions]
3152
+ * @param {(params: any, action: object) => any} [rootOptions.provisionalValue] -
3153
+ * a value the action holds while a run is in flight and it has none of its
3154
+ * own, so a screen already knowing the answer draws it as a refresh rather
3155
+ * than a first load (data_states.md). Called at the start of every run whose
3156
+ * value is undefined; the answer replaces it as usual, and `undefined` means
3157
+ * "nothing known". A resource `GET` uses it to draw the row its store holds.
3152
3158
  * @param {{ ms?: number, max?: number }} [rootOptions.keep] - how long an
3153
3159
  * answer stays good once it has landed. Without it an answer lives exactly as
3154
3160
  * long as something references it: the screen that asked goes away, and
@@ -3188,6 +3194,7 @@ const createAction = (callback, rootOptions = {}) => {
3188
3194
  value,
3189
3195
  resultToValue,
3190
3196
  valueToData,
3197
+ provisionalValue,
3191
3198
  dataDefault,
3192
3199
  data = dataDefault,
3193
3200
 
@@ -3646,6 +3653,12 @@ const createAction = (callback, rootOptions = {}) => {
3646
3653
  actionAbortMap.set(action, abort);
3647
3654
 
3648
3655
  batch(() => {
3656
+ if (provisionalValue && valueSignal.peek() === undefined) {
3657
+ const provisional = provisionalValue(params, action);
3658
+ if (provisional !== undefined) {
3659
+ valueSignal.value = provisional;
3660
+ }
3661
+ }
3649
3662
  runningStateSignal.value = RUNNING;
3650
3663
  if (!isPrerun) {
3651
3664
  isPrerunSignal.value = false;
@@ -14859,17 +14872,15 @@ const resource = (
14859
14872
  return item;
14860
14873
  },
14861
14874
  });
14862
- const createRestActionForRoot = createRestActionFactoryForRoot(name, {
14863
- idKey,
14864
- store,
14865
- declarationSite,
14866
- });
14867
14875
  // The row a GET designates, when the store already holds it — by its params:
14868
14876
  // the value under idKey may be the id or any unique key (a route opening a
14869
14877
  // user by id or by slug names both `id`), and a unique key may be given under
14870
- // its own name; or, when the params name no row (a GET without params, or
14871
- // whose params carry no key), the row the action last completed with.
14872
- // Answers a GET under a network policy (see applyNetworkPolicy).
14878
+ // its own name. findItemByParams is what a GET draws while its request is in
14879
+ // flight (the action's provisionalValue). findItemInStore adds, when the
14880
+ // params name no row (a GET without params, or whose params carry no key),
14881
+ // the row the action last completed with: that fallback answers a GET under
14882
+ // a network policy (see applyNetworkPolicy) and only there — a GET of a row
14883
+ // the store lacks must draw its skeleton, never the row read just before.
14873
14884
  const selectByAnyKey = (value) => {
14874
14885
  const item = store.select(value);
14875
14886
  if (item) {
@@ -14921,6 +14932,12 @@ const resource = (
14921
14932
  return store.select(lastItemId) || null;
14922
14933
  });
14923
14934
  };
14935
+ const createRestActionForRoot = createRestActionFactoryForRoot(name, {
14936
+ idKey,
14937
+ store,
14938
+ findItemByParams,
14939
+ declarationSite,
14940
+ });
14924
14941
  return createResource(name, {
14925
14942
  idKey,
14926
14943
  uniqueKeys,
@@ -15912,6 +15929,7 @@ const createRestActionFactoryForRoot = (
15912
15929
  {
15913
15930
  idKey,
15914
15931
  store, // see array_signal_store.js
15932
+ findItemByParams,
15915
15933
  declarationSite,
15916
15934
  },
15917
15935
  ) => {
@@ -15983,6 +16001,16 @@ const createRestActionFactoryForRoot = (
15983
16001
  return applyResultToValue(result);
15984
16002
  },
15985
16003
  valueToData: (itemId) => store.select(itemId),
16004
+ // While the request is out, the row the store already holds for these
16005
+ // params is drawn: the action starts on the "refresh over a known
16006
+ // answer" line of data_states.md rather than the first-load one.
16007
+ provisionalValue:
16008
+ verb === "GET"
16009
+ ? (params) => {
16010
+ const item = untracked(() => findItemByParams(params));
16011
+ return item ? item[idKey] : undefined;
16012
+ }
16013
+ : undefined,
15986
16014
  completeSideEffect: onActionComplete,
15987
16015
  });
15988
16016
  };
@@ -64932,52 +64960,59 @@ const ListContent = ({
64932
64960
  children
64933
64961
  }) => {
64934
64962
  const listProps = useContext(BoxForwardedPropsContext);
64935
- return jsx(Box, {
64936
- className: "navi_list_scroll_container",
64937
- overflow: overflow,
64938
- overflowX: overflowX,
64939
- overflowY: overflowY,
64940
- ...scrollBoxPaddingProps,
64941
- children: jsx(UnorderedList, {
64942
- role: role,
64943
- fallback: fallback,
64944
- fallbackShown: fallbackShown,
64945
- searchFallback: searchFallback,
64946
- searchFallbackShown: searchFallbackShown,
64947
- loadingPlaceholderShown: loadingPlaceholderShown,
64948
- error: error,
64949
- searchNoMatchMode: searchNoMatchMode,
64950
- separator: separator,
64951
- itemTransition: itemTransition,
64952
- expandX: expandX
64953
- // Deliberately not expandY here (unlike expandX above): the outer
64954
- // .navi_list_container already gets its own expandY treatment (see
64955
- // ListUI's own Box above) to fill whatever space its *own* parent
64956
- // gives it (e.g. a flex-column ancestor's flex-grow) — the <ul>
64957
- // itself must stay auto-height regardless, or it gets capped to
64958
- // match .navi_list_scroll_container's own (possibly much smaller)
64959
- // flex-resolved height instead of its real content height. That
64960
- // breaks two things at once: virtual scroll's own filler sizing
64961
- // (nothing to overflow into the scroll container in the first
64962
- // place) and any sticky List.Item header/footer inside it (their
64963
- // sticky "containing block" — the <ul>'s own box — would be
64964
- // artificially small, so they run out of room to stay stuck well
64965
- // before the user has actually scrolled through all the content).
64966
- ,
64967
- horizontal: horizontal,
64968
- spacing: spacing,
64969
- columns: columns,
64970
- itemColumns: itemColumns,
64971
- ...listProps,
64972
- tracker: tracker,
64973
- renderWindow: renderWindow,
64974
- virtual: virtual,
64975
- children: jsx(PendingScrollRefContext.Provider, {
64976
- value: pendingScrollRef,
64977
- children: children
64963
+ return (
64964
+ // Every provider the list puts around its items stands OUTSIDE the <ul>:
64965
+ // the walk that gives the rows their places (see ListDeclaredChildren) is
64966
+ // over the <ul>'s children, and one component between it and the caller's
64967
+ // rows is one child — all the rows would then stand in a single slot, and
64968
+ // reordering them would move nothing.
64969
+ jsx(PendingScrollRefContext.Provider, {
64970
+ value: pendingScrollRef,
64971
+ children: jsx(Box, {
64972
+ className: "navi_list_scroll_container",
64973
+ overflow: overflow,
64974
+ overflowX: overflowX,
64975
+ overflowY: overflowY,
64976
+ ...scrollBoxPaddingProps,
64977
+ children: jsx(UnorderedList, {
64978
+ role: role,
64979
+ fallback: fallback,
64980
+ fallbackShown: fallbackShown,
64981
+ searchFallback: searchFallback,
64982
+ searchFallbackShown: searchFallbackShown,
64983
+ loadingPlaceholderShown: loadingPlaceholderShown,
64984
+ error: error,
64985
+ searchNoMatchMode: searchNoMatchMode,
64986
+ separator: separator,
64987
+ itemTransition: itemTransition,
64988
+ expandX: expandX
64989
+ // Deliberately not expandY here (unlike expandX above): the outer
64990
+ // .navi_list_container already gets its own expandY treatment (see
64991
+ // ListUI's own Box above) to fill whatever space its *own* parent
64992
+ // gives it (e.g. a flex-column ancestor's flex-grow) — the <ul>
64993
+ // itself must stay auto-height regardless, or it gets capped to
64994
+ // match .navi_list_scroll_container's own (possibly much smaller)
64995
+ // flex-resolved height instead of its real content height. That
64996
+ // breaks two things at once: virtual scroll's own filler sizing
64997
+ // (nothing to overflow into the scroll container in the first
64998
+ // place) and any sticky List.Item header/footer inside it (their
64999
+ // sticky "containing block" — the <ul>'s own box — would be
65000
+ // artificially small, so they run out of room to stay stuck well
65001
+ // before the user has actually scrolled through all the content).
65002
+ ,
65003
+ horizontal: horizontal,
65004
+ spacing: spacing,
65005
+ columns: columns,
65006
+ itemColumns: itemColumns,
65007
+ ...listProps,
65008
+ tracker: tracker,
65009
+ renderWindow: renderWindow,
65010
+ virtual: virtual,
65011
+ children: children
65012
+ })
64978
65013
  })
64979
65014
  })
64980
- });
65015
+ );
64981
65016
  };
64982
65017
  // Where the items sit is a question about the track (the <ul>), not about the
64983
65018
  // frame around it: the frame's only child is the scroll box, which fills it and
@@ -65182,17 +65217,14 @@ const useListScrollSync = ({
65182
65217
  item
65183
65218
  });
65184
65219
  };
65185
- const {
65186
- start,
65187
- end
65188
- } = renderWindowRef.current;
65189
- const isInWindow = index >= start && index < end;
65190
- if (isInWindow) {
65191
- const itemEl = findRowElement(getListEl(), item.id);
65192
- if (itemEl) {
65193
- scrollItemIntoView(itemEl);
65194
- return;
65195
- }
65220
+
65221
+ // Whether the row is drawn is asked of the dom, not of the render window:
65222
+ // the window says what a run draws, and a list whose rows are declared one
65223
+ // by one has them all in the dom whatever the window says.
65224
+ const itemEl = findRowElement(getListEl(), item.id);
65225
+ if (itemEl) {
65226
+ scrollItemIntoView(itemEl);
65227
+ return;
65196
65228
  }
65197
65229
  // Not in DOM — shift the render window. The item will read
65198
65230
  // pendingScrollRef on mount and scroll into view.
@@ -65314,9 +65346,8 @@ const useListScrollSync = ({
65314
65346
  }, []);
65315
65347
  // Watch scores of the top renderBudget items.
65316
65348
  // When scores change during an active search, scroll to top to reveal the most relevant items.
65317
- // When search becomes empty, restore the scroll position from before the search started.
65318
- // We save the first-visible item ID so restoration is item-precise
65319
- // and survives render-window shifts or item reordering.
65349
+ // When search becomes empty, put the list back at the offset (and the render
65350
+ // window) it was at when the search started — see docs/scroll.md.
65320
65351
 
65321
65352
  // NOTE POUR LE JOUR OU ON A LE MULTISELECT:
65322
65353
  // Lorsqu'on selectionne quelque chose pendant une recherche, alors ensuite meme si on clear