@jsenv/navi 0.29.56 → 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.
- package/dist/jsenv_navi.js +223 -29
- package/dist/jsenv_navi.js.map +17 -7
- package/docs/AI_INSTRUCTIONS.md +2 -1
- package/docs/list_refresh.md +39 -7
- package/docs/resource.md +8 -2
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -6,7 +6,7 @@ import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visua
|
|
|
6
6
|
export { coarsePointerSignal } from "./jsenv_navi_side_effects.js";
|
|
7
7
|
import { elementIsFocusable, createPubSub, dispatchInternalCustomEvent, dispatchCustomEvent, getElementSignature, findEvent, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createIterableWeakSet, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, mergeTwoStyles, normalizeStyles, resolveCSSSize, hasCSSSizeUnit, resolveOklchLightness, contrastColor, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, scrollRoomTowards, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, findBefore, findAfter, initFocusGroup, scrollIntoViewScoped, getScrollContainer, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, stringifyStyle as stringifyStyle$1 } from "@jsenv/dom";
|
|
8
8
|
export { contrastColor, findEvent, startDragTo } from "@jsenv/dom";
|
|
9
|
-
import { signal, computed, effect, batch, useSignal } from "@preact/signals";
|
|
9
|
+
import { signal, computed, effect, batch, untracked, useSignal } from "@preact/signals";
|
|
10
10
|
import { createContext, isValidElement, h, Fragment, render, toChildArray, options, cloneElement } from "preact";
|
|
11
11
|
import { useContext, useLayoutEffect, useCallback, useRef, useState, useEffect, useMemo, useId, useErrorBoundary } from "preact/hooks";
|
|
12
12
|
import { jsx, jsxs, Fragment as Fragment$1 } from "preact/jsx-runtime";
|
|
@@ -33903,31 +33903,61 @@ const getParamScope = (params) => {
|
|
|
33903
33903
|
* replays it, and takes a place in the rerun graph — two things a slice must
|
|
33904
33904
|
* not do: the list already holds the slices it received and glues them back
|
|
33905
33905
|
* together, and a mutation would otherwise send every slice ever loaded back to
|
|
33906
|
-
* the network at once. So the reader keeps
|
|
33907
|
-
* the range the list asks for, writes what comes back into the store, and
|
|
33908
|
-
* the rows back as store items — never a copy of the JSON, so the
|
|
33909
|
-
* row reads are the shared ones and a request sent from a row is
|
|
33910
|
-
* it. A row following its own fields through a write reads them
|
|
33911
|
-
* (`RESOURCE.useById(id)`): an update replaces the item object,
|
|
33912
|
-
* list is holding is the one it was given.
|
|
33906
|
+
* the network at once. So the reader keeps no response. It runs the callback
|
|
33907
|
+
* with the range the list asks for, writes what comes back into the store, and
|
|
33908
|
+
* hands the rows back as store items — never a copy of the JSON, so the
|
|
33909
|
+
* relations a row reads are the shared ones and a request sent from a row is
|
|
33910
|
+
* read back on it. A row following its own fields through a write reads them
|
|
33911
|
+
* from the store (`RESOURCE.useById(id)`): an update replaces the item object,
|
|
33912
|
+
* and the one the list is holding is the one it was given.
|
|
33913
|
+
*
|
|
33914
|
+
* What the reader does keep is the collection's composition: which rank holds
|
|
33915
|
+
* which id, and how many ranks there are, per resolved bound params. The rows
|
|
33916
|
+
* themselves are in the store already; the composition is the one thing about a
|
|
33917
|
+
* paginated collection the store cannot model, and without it a list that left
|
|
33918
|
+
* the screen comes back to skeletons and a request for rows it had a second
|
|
33919
|
+
* before. Ids and a count, so nothing here can hold a stale copy of a row, and
|
|
33920
|
+
* a row dropped from the store simply stops resolving. A list that comes back
|
|
33921
|
+
* draws the composition it left and asks again for the window it draws — the
|
|
33922
|
+
* revalidation an invalidation goes through, from a fresh mount.
|
|
33913
33923
|
*
|
|
33914
33924
|
* The reader is a function, so a list feeds on it the way it feeds on any other
|
|
33915
33925
|
* source: `itemsAction={GAME.GET_RANGE.bindParams({ radar })}`.
|
|
33916
33926
|
*
|
|
33917
|
-
*
|
|
33918
|
-
*
|
|
33919
|
-
*
|
|
33920
|
-
* and asks again — the counterpart, for a reader,
|
|
33921
|
-
* action. Every reader made by `bindParams` shares
|
|
33922
|
-
*
|
|
33927
|
+
* A mutation that decides who belongs to the collection (a POST, a DELETE,
|
|
33928
|
+
* whatever `rerunOn.GET_RANGE` says) bumps `invalidationSignal` and drops the
|
|
33929
|
+
* compositions: they stand for an order that is gone. Whoever reads slices
|
|
33930
|
+
* through the reader then goes and asks again — the counterpart, for a reader,
|
|
33931
|
+
* of what a rerun is for an action. Every reader made by `bindParams` shares
|
|
33932
|
+
* the signal and the compositions of the one it comes from: the params say
|
|
33933
|
+
* which slices are read, not which collection.
|
|
33923
33934
|
*/
|
|
33924
33935
|
|
|
33925
33936
|
|
|
33926
33937
|
const createRangeReader = (
|
|
33927
33938
|
actionName,
|
|
33928
33939
|
callback,
|
|
33929
|
-
{
|
|
33940
|
+
{
|
|
33941
|
+
store,
|
|
33942
|
+
params: boundParams,
|
|
33943
|
+
invalidationSignal = signal(0),
|
|
33944
|
+
compositionSet = new Set(),
|
|
33945
|
+
},
|
|
33930
33946
|
) => {
|
|
33947
|
+
// Which composition this reader is about: the values its params hold, not its
|
|
33948
|
+
// own identity, so two `bindParams({ scope: "thread" })` made in two places
|
|
33949
|
+
// read and write the same one. There are as many compositions as there are
|
|
33950
|
+
// collections read through this reader — a handful, walked with the deep
|
|
33951
|
+
// comparison the rest of the codebase memoizes on.
|
|
33952
|
+
const currentParams = () => untracked(() => resolveParams(boundParams));
|
|
33953
|
+
const findComposition = (params) => {
|
|
33954
|
+
for (const composition of compositionSet) {
|
|
33955
|
+
if (compareTwoJsValues(composition.params, params)) {
|
|
33956
|
+
return composition;
|
|
33957
|
+
}
|
|
33958
|
+
}
|
|
33959
|
+
return null;
|
|
33960
|
+
};
|
|
33931
33961
|
const readRange = async (range = {}) => {
|
|
33932
33962
|
const { signal, ...rangeParams } = range;
|
|
33933
33963
|
const paramsResolved = { ...resolveParams(boundParams), ...rangeParams };
|
|
@@ -33959,14 +33989,80 @@ const createRangeReader = (
|
|
|
33959
33989
|
// stand for a composition that is gone.
|
|
33960
33990
|
readRange.invalidationSignal = invalidationSignal;
|
|
33961
33991
|
readRange.invalidate = () => {
|
|
33992
|
+
compositionSet.clear();
|
|
33962
33993
|
invalidationSignal.value = invalidationSignal.peek() + 1;
|
|
33963
33994
|
};
|
|
33995
|
+
// The rows of a composition, drawn from the store: a rank whose row is gone
|
|
33996
|
+
// from the store resolves to nothing and is asked for again.
|
|
33997
|
+
readRange.readComposition = () => {
|
|
33998
|
+
const composition = findComposition(currentParams());
|
|
33999
|
+
if (!composition) {
|
|
34000
|
+
return null;
|
|
34001
|
+
}
|
|
34002
|
+
const byIndex = new Map();
|
|
34003
|
+
untracked(() => {
|
|
34004
|
+
for (const [index, id] of composition.idByIndex) {
|
|
34005
|
+
const item = store.select(id);
|
|
34006
|
+
if (item) {
|
|
34007
|
+
byIndex.set(index, item);
|
|
34008
|
+
}
|
|
34009
|
+
}
|
|
34010
|
+
});
|
|
34011
|
+
return { byIndex, count: composition.count };
|
|
34012
|
+
};
|
|
34013
|
+
// `replace` is a revalidation: the ranks that are not in what just came back
|
|
34014
|
+
// stood for a composition that has moved on. Otherwise the ranks are merged,
|
|
34015
|
+
// so two lists reading the same collection through their own windows add up
|
|
34016
|
+
// to one composition instead of taking turns erasing each other.
|
|
34017
|
+
readRange.writeComposition = ({ byIndex, count, replace }) => {
|
|
34018
|
+
const params = currentParams();
|
|
34019
|
+
let composition = findComposition(params);
|
|
34020
|
+
if (!composition) {
|
|
34021
|
+
composition = { params, idByIndex: new Map(), count };
|
|
34022
|
+
compositionSet.add(composition);
|
|
34023
|
+
} else if (replace) {
|
|
34024
|
+
composition.idByIndex = new Map();
|
|
34025
|
+
}
|
|
34026
|
+
composition.count = count;
|
|
34027
|
+
for (const [index, item] of byIndex) {
|
|
34028
|
+
const id = item ? item[store.idKey] : undefined;
|
|
34029
|
+
if (id !== undefined) {
|
|
34030
|
+
composition.idByIndex.set(index, id);
|
|
34031
|
+
}
|
|
34032
|
+
}
|
|
34033
|
+
};
|
|
34034
|
+
// The same trade a list makes with the rows it holds, applied to what is kept
|
|
34035
|
+
// for the next mount. Two lists on one collection each trim by their own
|
|
34036
|
+
// window; the rows a list is drawing stay on its screen regardless — a rank
|
|
34037
|
+
// dropped here is one that gets asked for again after a remount.
|
|
34038
|
+
readRange.trimComposition = (keepFrom, keepTo, budget) => {
|
|
34039
|
+
const composition = findComposition(currentParams());
|
|
34040
|
+
if (!composition || !budget || composition.idByIndex.size <= budget) {
|
|
34041
|
+
return;
|
|
34042
|
+
}
|
|
34043
|
+
for (const index of composition.idByIndex.keys()) {
|
|
34044
|
+
if (index < keepFrom || index > keepTo) {
|
|
34045
|
+
composition.idByIndex.delete(index);
|
|
34046
|
+
}
|
|
34047
|
+
}
|
|
34048
|
+
};
|
|
34049
|
+
// Memoized for the reasons an action's bindParams is (see actions.js): params
|
|
34050
|
+
// and the reader they make have synchronized lifetimes, and params equal in
|
|
34051
|
+
// value give back the reader that already exists instead of a second one.
|
|
34052
|
+
const readerByParams = createJsValueWeakMap();
|
|
33964
34053
|
readRange.bindParams = (paramsToBind) => {
|
|
33965
|
-
|
|
34054
|
+
const existing = readerByParams.get(paramsToBind);
|
|
34055
|
+
if (existing) {
|
|
34056
|
+
return existing;
|
|
34057
|
+
}
|
|
34058
|
+
const reader = createRangeReader(actionName, callback, {
|
|
33966
34059
|
store,
|
|
33967
34060
|
params: boundParams ? { ...boundParams, ...paramsToBind } : paramsToBind,
|
|
33968
34061
|
invalidationSignal,
|
|
34062
|
+
compositionSet,
|
|
33969
34063
|
});
|
|
34064
|
+
readerByParams.set(paramsToBind, reader);
|
|
34065
|
+
return reader;
|
|
33970
34066
|
};
|
|
33971
34067
|
return readRange;
|
|
33972
34068
|
};
|
|
@@ -58355,6 +58451,7 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
58355
58451
|
* memoryBudget?: number,
|
|
58356
58452
|
* renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
|
|
58357
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,
|
|
58358
58455
|
* }>}
|
|
58359
58456
|
* @param {(item: any, index: number, state: {refreshing: boolean}) => any} props.renderItem
|
|
58360
58457
|
* What one row is, given the item and where it sits. `state.refreshing` says
|
|
@@ -58392,6 +58489,14 @@ const VISIBILITY_HIDDEN_STYLE = {
|
|
|
58392
58489
|
* a `retry` to call, and the `start`/`end` of the range that failed. Defaults
|
|
58393
58490
|
* to an inline message with a retry button, drawn on the row the user is
|
|
58394
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.
|
|
58395
58500
|
*/
|
|
58396
58501
|
const ListItems = ({
|
|
58397
58502
|
renderItem,
|
|
@@ -58403,7 +58508,8 @@ const ListItems = ({
|
|
|
58403
58508
|
renderGroupLabel,
|
|
58404
58509
|
groupLabelProps,
|
|
58405
58510
|
renderSkeleton,
|
|
58406
|
-
renderError
|
|
58511
|
+
renderError,
|
|
58512
|
+
onRequestStateChange
|
|
58407
58513
|
}) => {
|
|
58408
58514
|
const ownerId = useId();
|
|
58409
58515
|
const virtual = useContext(ListVirtualContext);
|
|
@@ -58412,7 +58518,8 @@ const ListItems = ({
|
|
|
58412
58518
|
const store = useItemStore({
|
|
58413
58519
|
count,
|
|
58414
58520
|
itemsAction,
|
|
58415
|
-
memoryBudget
|
|
58521
|
+
memoryBudget,
|
|
58522
|
+
onRequestStateChange
|
|
58416
58523
|
});
|
|
58417
58524
|
const renderRowSkeleton = renderSkeleton === undefined ? virtual.renderSkeleton : renderSkeleton;
|
|
58418
58525
|
// A row on its way takes the room the list reserves for it: anything else
|
|
@@ -58686,6 +58793,12 @@ const ListItemsFailure = ({
|
|
|
58686
58793
|
// hits the network again.
|
|
58687
58794
|
const ITEM_STORE_MAX_DEFAULT = 1000;
|
|
58688
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
|
+
};
|
|
58689
58802
|
|
|
58690
58803
|
// The rows a run has, and how it gets more. Two shapes behind one reader: the
|
|
58691
58804
|
// caller holds them (items/count/itemStart), or the run asked for them and
|
|
@@ -58695,21 +58808,37 @@ const ITEM_STORE_KEEP_AROUND = 250;
|
|
|
58695
58808
|
const useItemStore = ({
|
|
58696
58809
|
count,
|
|
58697
58810
|
itemsAction,
|
|
58698
|
-
memoryBudget
|
|
58811
|
+
memoryBudget,
|
|
58812
|
+
onRequestStateChange
|
|
58699
58813
|
}) => {
|
|
58814
|
+
// What the source kept of the collection when the screen it was on went away
|
|
58815
|
+
// (a range reader keeps the composition: see resource_range_reader.js). The
|
|
58816
|
+
// rows are drawn from it right away and the window is asked for again — the
|
|
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);
|
|
58700
58822
|
const pagesRef = useRef(null);
|
|
58823
|
+
let restored = false;
|
|
58701
58824
|
if (!pagesRef.current) {
|
|
58702
|
-
|
|
58703
|
-
|
|
58704
|
-
|
|
58705
|
-
|
|
58825
|
+
const composition = typeof itemsAction === "function" && itemsAction.readComposition ? itemsAction.readComposition() : null;
|
|
58826
|
+
if (composition && composition.count !== undefined) {
|
|
58827
|
+
pagesRef.current = composition;
|
|
58828
|
+
restored = true;
|
|
58829
|
+
} else {
|
|
58830
|
+
pagesRef.current = {
|
|
58831
|
+
byIndex: new Map(),
|
|
58832
|
+
count: undefined
|
|
58833
|
+
};
|
|
58834
|
+
}
|
|
58706
58835
|
}
|
|
58707
58836
|
const pages = pagesRef.current;
|
|
58708
58837
|
const [, setPageVersion] = useState(0);
|
|
58709
58838
|
// The rows held are out of date and the run has not asked for the new ones
|
|
58710
58839
|
// yet. They stay on screen until the answer comes: what is drawn is from
|
|
58711
58840
|
// before, which is not the same thing as nothing to draw.
|
|
58712
|
-
const staleRef = useRef(
|
|
58841
|
+
const staleRef = useRef(restored);
|
|
58713
58842
|
const [refreshing, setRefreshing] = useState(false);
|
|
58714
58843
|
// A source that says when what it reads has moved (a resource range reader
|
|
58715
58844
|
// does: see rerunOn.GET_RANGE) is heard here — a write deciding who belongs
|
|
@@ -58757,6 +58886,51 @@ const useItemStore = ({
|
|
|
58757
58886
|
virtual.refreshingSignal.value = virtual.refreshingSignal.peek() - 1;
|
|
58758
58887
|
};
|
|
58759
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
|
+
}, []);
|
|
58760
58934
|
const store = {
|
|
58761
58935
|
rowCount,
|
|
58762
58936
|
failure,
|
|
@@ -58767,16 +58941,21 @@ const useItemStore = ({
|
|
|
58767
58941
|
// trade the render window makes, one order of magnitude further out.
|
|
58768
58942
|
forget: (windowFrom, windowTo) => {
|
|
58769
58943
|
const budget = memoryBudget === undefined ? ITEM_STORE_MAX_DEFAULT : memoryBudget;
|
|
58770
|
-
if (!budget
|
|
58944
|
+
if (!budget) {
|
|
58771
58945
|
return;
|
|
58772
58946
|
}
|
|
58773
58947
|
const keepFrom = windowFrom - ITEM_STORE_KEEP_AROUND;
|
|
58774
58948
|
const keepTo = windowTo + ITEM_STORE_KEEP_AROUND;
|
|
58775
|
-
|
|
58776
|
-
|
|
58777
|
-
|
|
58949
|
+
if (pages.byIndex.size > budget) {
|
|
58950
|
+
for (const index of pages.byIndex.keys()) {
|
|
58951
|
+
if (index < keepFrom || index > keepTo) {
|
|
58952
|
+
pages.byIndex.delete(index);
|
|
58953
|
+
}
|
|
58778
58954
|
}
|
|
58779
58955
|
}
|
|
58956
|
+
if (typeof itemsAction === "function" && itemsAction.trimComposition) {
|
|
58957
|
+
itemsAction.trimComposition(keepFrom, keepTo, budget);
|
|
58958
|
+
}
|
|
58780
58959
|
},
|
|
58781
58960
|
retry: () => {
|
|
58782
58961
|
const request = requestRef.current;
|
|
@@ -58842,7 +59021,7 @@ const useItemStore = ({
|
|
|
58842
59021
|
end = start + budget - 1;
|
|
58843
59022
|
}
|
|
58844
59023
|
}
|
|
58845
|
-
|
|
59024
|
+
const ask = () => {
|
|
58846
59025
|
if (start === -1) {
|
|
58847
59026
|
return;
|
|
58848
59027
|
}
|
|
@@ -58915,6 +59094,7 @@ const useItemStore = ({
|
|
|
58915
59094
|
staleRef.current = false;
|
|
58916
59095
|
setRefreshing(false);
|
|
58917
59096
|
}
|
|
59097
|
+
publishRequestState();
|
|
58918
59098
|
}
|
|
58919
59099
|
if (revalidating && !current) {
|
|
58920
59100
|
// Rows of a composition already superseded by a newer ask: keeping
|
|
@@ -58942,6 +59122,15 @@ const useItemStore = ({
|
|
|
58942
59122
|
i++;
|
|
58943
59123
|
}
|
|
58944
59124
|
pages.count = pageCount;
|
|
59125
|
+
// Which rank holds which id, kept by the source so a list drawing
|
|
59126
|
+
// this collection again finds it drawn (see readComposition above).
|
|
59127
|
+
if (itemsAction.writeComposition) {
|
|
59128
|
+
itemsAction.writeComposition({
|
|
59129
|
+
byIndex: pages.byIndex,
|
|
59130
|
+
count: pageCount,
|
|
59131
|
+
replace: revalidating
|
|
59132
|
+
});
|
|
59133
|
+
}
|
|
58945
59134
|
virtual.pagesSignal.value = virtual.pagesSignal.peek() + 1;
|
|
58946
59135
|
setPageVersion(version => version + 1);
|
|
58947
59136
|
};
|
|
@@ -58952,6 +59141,7 @@ const useItemStore = ({
|
|
|
58952
59141
|
}
|
|
58953
59142
|
request.busy = false;
|
|
58954
59143
|
request.revalidating = false;
|
|
59144
|
+
publishRequestState();
|
|
58955
59145
|
if (revalidating) {
|
|
58956
59146
|
// The rows from before stay: a revalidation that failed has
|
|
58957
59147
|
// nothing better to put in their place.
|
|
@@ -58980,6 +59170,10 @@ const useItemStore = ({
|
|
|
58980
59170
|
} else {
|
|
58981
59171
|
done(result);
|
|
58982
59172
|
}
|
|
59173
|
+
};
|
|
59174
|
+
useLayoutEffect(() => {
|
|
59175
|
+
ask();
|
|
59176
|
+
publishRequestState();
|
|
58983
59177
|
});
|
|
58984
59178
|
}
|
|
58985
59179
|
};
|