@jsenv/navi 0.29.427 → 0.29.429
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/dev/jsenv_navi.js +187 -65
- package/dist/dev/jsenv_navi.js.map +5 -5
- package/dist/jsenv_navi.js +179 -59
- package/dist/jsenv_navi.js.map +5 -5
- package/docs/list_refresh.md +5 -4
- package/docs/scroll.md +4 -1
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -25978,10 +25978,11 @@ const createRangeReader = (
|
|
|
25978
25978
|
});
|
|
25979
25979
|
return { byIndex, count: composition.count };
|
|
25980
25980
|
};
|
|
25981
|
-
// `replace`
|
|
25982
|
-
//
|
|
25983
|
-
// so two lists reading the same
|
|
25984
|
-
// to one composition instead of
|
|
25981
|
+
// `replace` says the list dropped ranks it held (an item the collection moved
|
|
25982
|
+
// elsewhere, ranks past its end): kept here, they would come back at the next
|
|
25983
|
+
// mount. Otherwise the ranks are merged, so two lists reading the same
|
|
25984
|
+
// collection through their own windows add up to one composition instead of
|
|
25985
|
+
// taking turns erasing each other.
|
|
25985
25986
|
readRange.writeComposition = ({ byIndex, count, replace }) => {
|
|
25986
25987
|
const params = currentParams();
|
|
25987
25988
|
let composition = findComposition(params);
|
|
@@ -38774,6 +38775,16 @@ const resolveAfterSend = (target, requester) => {
|
|
|
38774
38775
|
}
|
|
38775
38776
|
return undefined;
|
|
38776
38777
|
};
|
|
38778
|
+
// What follows a send is caused by the send, not by the gesture: the send
|
|
38779
|
+
// answered the gesture and may have prevented it (an Enter must not also click
|
|
38780
|
+
// the submit button, nor activate the trigger of the popup it closed), and a
|
|
38781
|
+
// follow-up judged on that event is refused as a gesture someone cancelled.
|
|
38782
|
+
// Chained, so the gesture stays readable behind it.
|
|
38783
|
+
const createSentEvent = (event) => {
|
|
38784
|
+
const sentEvent = new CustomEvent("sent", { detail: {} });
|
|
38785
|
+
chainEvent(sentEvent, event);
|
|
38786
|
+
return sentEvent;
|
|
38787
|
+
};
|
|
38777
38788
|
|
|
38778
38789
|
registerNaviCommand("--navi-send", (source, event, { requester }) => {
|
|
38779
38790
|
const expandable = resolveExpandableAround(source);
|
|
@@ -38806,7 +38817,9 @@ registerNaviCommand("--navi-send", (source, event, { requester }) => {
|
|
|
38806
38817
|
source.getAttribute("data-after-send") ||
|
|
38807
38818
|
target.getAttribute("data-after-send");
|
|
38808
38819
|
if (afterSend) {
|
|
38809
|
-
triggerNaviCommand(target, afterSend, event, {
|
|
38820
|
+
triggerNaviCommand(target, afterSend, createSentEvent(event), {
|
|
38821
|
+
optional: true,
|
|
38822
|
+
});
|
|
38810
38823
|
}
|
|
38811
38824
|
return result;
|
|
38812
38825
|
},
|
|
@@ -38850,7 +38863,9 @@ registerNaviCommand("--navi-send", (source, event, { requester }) => {
|
|
|
38850
38863
|
if (!afterSend) {
|
|
38851
38864
|
return;
|
|
38852
38865
|
}
|
|
38853
|
-
triggerNaviCommand(source, afterSend, event, {
|
|
38866
|
+
triggerNaviCommand(source, afterSend, createSentEvent(event), {
|
|
38867
|
+
optional: true,
|
|
38868
|
+
});
|
|
38854
38869
|
};
|
|
38855
38870
|
const {
|
|
38856
38871
|
result: sent,
|
|
@@ -72727,9 +72742,12 @@ const useListScrollSync = ({
|
|
|
72727
72742
|
};
|
|
72728
72743
|
const [renderWindow, setRenderWindow] = useState(() => {
|
|
72729
72744
|
// Opening somewhere else than the beginning starts by framing there: the
|
|
72730
|
-
// items the list will draw are the items it will ask for.
|
|
72745
|
+
// items the list will draw are the items it will ask for. A named item is
|
|
72746
|
+
// framed where it stood when it was written down, which is where holdWindow
|
|
72747
|
+
// aims before anyone can say where it is.
|
|
72731
72748
|
const openAt = scrolled ?? defaultScrolled;
|
|
72732
|
-
const
|
|
72749
|
+
const openIndex = typeof openAt === "number" ? openAt : openAt && typeof openAt === "object" && typeof openAt.index === "number" ? openAt.index : null;
|
|
72750
|
+
const start = openIndex === null ? 0 : openIndex - itemsAboveOpening(openAt, firstWindowItemCount);
|
|
72733
72751
|
const startClamped = start < 0 ? 0 : start;
|
|
72734
72752
|
return {
|
|
72735
72753
|
start: startClamped,
|
|
@@ -72855,14 +72873,11 @@ const useListScrollSync = ({
|
|
|
72855
72873
|
if (wantedStart < 0) {
|
|
72856
72874
|
wantedStart = 0;
|
|
72857
72875
|
}
|
|
72858
|
-
|
|
72859
|
-
|
|
72860
|
-
|
|
72861
|
-
|
|
72862
|
-
|
|
72863
|
-
start: wantedStart,
|
|
72864
|
-
end: wantedStart + windowSize
|
|
72865
|
-
};
|
|
72876
|
+
// The window the hold frames goes into the state, not into this render
|
|
72877
|
+
// alone: the next render starts from the state, and once the item stands
|
|
72878
|
+
// inside the window the hold stops framing it — a window left where the
|
|
72879
|
+
// state had it would draw around another place.
|
|
72880
|
+
updateRenderWindow(wantedStart, wantedStart + windowSize);
|
|
72866
72881
|
listItems.holdPending = false;
|
|
72867
72882
|
};
|
|
72868
72883
|
const pendingScrollRef = useRef();
|
|
@@ -73010,6 +73025,13 @@ const useListScrollSync = ({
|
|
|
73010
73025
|
return;
|
|
73011
73026
|
}
|
|
73012
73027
|
hasBeenDisplayedRef.current = true;
|
|
73028
|
+
const place = startPlaceRef.current;
|
|
73029
|
+
if (!place.userTookOver && place.wanted !== "start" && place.wanted !== undefined) {
|
|
73030
|
+
// Held somewhere: where the list opens is the hold's to say (see
|
|
73031
|
+
// placeWhereHeld). Scrolled here too, the list would go to the top or
|
|
73032
|
+
// the selected item for as long as the hold takes to put it back.
|
|
73033
|
+
return;
|
|
73034
|
+
}
|
|
73013
73035
|
const items = listItems.itemsSignal.peek();
|
|
73014
73036
|
const firstSelected = items.find(i => {
|
|
73015
73037
|
if (i.selected) {
|
|
@@ -76192,19 +76214,57 @@ const useRunItems = (store, {
|
|
|
76192
76214
|
// rebuilt as the window slides — a group holds the items of its day that are
|
|
76193
76215
|
// currently drawn, which is exactly the span its label has to survive.
|
|
76194
76216
|
let group = null;
|
|
76217
|
+
// Two groups can still share a key (a failure band standing mid-group, data
|
|
76218
|
+
// not sorted by group): the later ones are told apart by their rank among
|
|
76219
|
+
// them, so the first keeps its element. A group keyed by where it starts
|
|
76220
|
+
// would lose its element each time the window slides past its first item.
|
|
76221
|
+
const groupCountByKey = new Map();
|
|
76195
76222
|
const closeGroup = () => {
|
|
76196
76223
|
if (!group) {
|
|
76197
76224
|
return;
|
|
76198
76225
|
}
|
|
76226
|
+
const groupCount = (groupCountByKey.get(group.key) || 0) + 1;
|
|
76227
|
+
groupCountByKey.set(group.key, groupCount);
|
|
76199
76228
|
nodes.push(jsx(ListItemGroup, {
|
|
76200
76229
|
label: group.label,
|
|
76201
76230
|
labelProps: group.labelProps,
|
|
76202
76231
|
children: group.children
|
|
76203
|
-
}, `${ownerId}_group_${group.key}`));
|
|
76232
|
+
}, groupCount === 1 ? `${ownerId}_group_${group.key}` : `${ownerId}_group_${group.key}_${groupCount}`));
|
|
76204
76233
|
group = null;
|
|
76205
76234
|
};
|
|
76235
|
+
// An item not held has no group of its own. Between two items of the open
|
|
76236
|
+
// group it is a line of that group whose content has not arrived: drawn
|
|
76237
|
+
// outside, it would cut the group in two and show its label twice. A hole at
|
|
76238
|
+
// a group's edge stays outside — nothing says which side it belongs to.
|
|
76239
|
+
let holeTo = -1;
|
|
76240
|
+
let holeGroupKey;
|
|
76241
|
+
const groupKeyOfHole = itemIndex => {
|
|
76242
|
+
if (itemIndex < holeTo) {
|
|
76243
|
+
return holeGroupKey;
|
|
76244
|
+
}
|
|
76245
|
+
holeTo = itemIndex + 1;
|
|
76246
|
+
while (holeTo < windowTo && holeTo !== failureFrom && getItemAt(holeTo) === undefined) {
|
|
76247
|
+
holeTo++;
|
|
76248
|
+
}
|
|
76249
|
+
holeGroupKey = undefined;
|
|
76250
|
+
if (group && holeTo < windowTo && holeTo !== failureFrom) {
|
|
76251
|
+
const keyAfterHole = groupBy(getItemAt(holeTo), holeTo);
|
|
76252
|
+
if (keyAfterHole === group.key) {
|
|
76253
|
+
holeGroupKey = keyAfterHole;
|
|
76254
|
+
}
|
|
76255
|
+
}
|
|
76256
|
+
return holeGroupKey;
|
|
76257
|
+
};
|
|
76206
76258
|
// Which group an item belongs to, or undefined when it belongs to none.
|
|
76207
|
-
const groupKeyOf = (item, itemIndex) =>
|
|
76259
|
+
const groupKeyOf = (item, itemIndex) => {
|
|
76260
|
+
if (!groupBy) {
|
|
76261
|
+
return undefined;
|
|
76262
|
+
}
|
|
76263
|
+
if (item === undefined) {
|
|
76264
|
+
return groupKeyOfHole(itemIndex);
|
|
76265
|
+
}
|
|
76266
|
+
return groupBy(item, itemIndex);
|
|
76267
|
+
};
|
|
76208
76268
|
const pushItem = (itemNode, item, itemIndex, groupKey) => {
|
|
76209
76269
|
if (groupKey === undefined) {
|
|
76210
76270
|
closeGroup();
|
|
@@ -76532,10 +76592,15 @@ const useItemStore = ({
|
|
|
76532
76592
|
}
|
|
76533
76593
|
const pages = pagesRef.current;
|
|
76534
76594
|
const [, setPageVersion] = useState(0);
|
|
76535
|
-
// The
|
|
76536
|
-
//
|
|
76537
|
-
//
|
|
76538
|
-
|
|
76595
|
+
// The ranks whose item is from before: the run came back to the screen, or
|
|
76596
|
+
// the source said the collection moved. They stay drawn until a page confirms
|
|
76597
|
+
// or replaces them, rank by rank. A page covers what the window framed when it
|
|
76598
|
+
// was asked for, and the window keeps growing meanwhile: dropping what the
|
|
76599
|
+
// page did not cover empties items on screen.
|
|
76600
|
+
const staleRanksRef = useRef(null);
|
|
76601
|
+
if (staleRanksRef.current === null) {
|
|
76602
|
+
staleRanksRef.current = restored ? new Set(pages.byIndex.keys()) : new Set();
|
|
76603
|
+
}
|
|
76539
76604
|
const [refreshing, setRefreshing] = useState(false);
|
|
76540
76605
|
// A source that says when what it reads has moved (a resource range reader
|
|
76541
76606
|
// does: see rerunOn.GET_RANGE) is heard here — a write deciding who belongs
|
|
@@ -76546,7 +76611,7 @@ const useItemStore = ({
|
|
|
76546
76611
|
const invalidationRef = useRef(invalidation);
|
|
76547
76612
|
if (invalidationRef.current !== invalidation) {
|
|
76548
76613
|
invalidationRef.current = invalidation;
|
|
76549
|
-
|
|
76614
|
+
staleRanksRef.current = new Set(pages.byIndex.keys());
|
|
76550
76615
|
}
|
|
76551
76616
|
// The one request in flight, with the means to call it off: a page asked for
|
|
76552
76617
|
// a window the list has left is work the server and the browser are doing for
|
|
@@ -76579,11 +76644,6 @@ const useItemStore = ({
|
|
|
76579
76644
|
// It stands for a page of them: a list that is about to be filled looks
|
|
76580
76645
|
// like items on their way, not like an empty list.
|
|
76581
76646
|
const itemCount = pages.count ?? count ?? listItems.pageSize;
|
|
76582
|
-
// A run that never received anything has nothing to keep on screen: asking
|
|
76583
|
-
// again is its first ask, not a refresh.
|
|
76584
|
-
if (staleRef.current && pages.count === undefined) {
|
|
76585
|
-
staleRef.current = false;
|
|
76586
|
-
}
|
|
76587
76647
|
useLayoutEffect(() => {
|
|
76588
76648
|
if (!refreshing) {
|
|
76589
76649
|
return null;
|
|
@@ -76656,9 +76716,11 @@ const useItemStore = ({
|
|
|
76656
76716
|
const keepFrom = windowFrom - ITEM_STORE_KEEP_AROUND;
|
|
76657
76717
|
const keepTo = windowTo + ITEM_STORE_KEEP_AROUND;
|
|
76658
76718
|
if (pages.byIndex.size > budget) {
|
|
76719
|
+
const staleRanks = staleRanksRef.current;
|
|
76659
76720
|
for (const index of pages.byIndex.keys()) {
|
|
76660
76721
|
if (index < keepFrom || index > keepTo) {
|
|
76661
76722
|
pages.byIndex.delete(index);
|
|
76723
|
+
staleRanks.delete(index);
|
|
76662
76724
|
}
|
|
76663
76725
|
}
|
|
76664
76726
|
}
|
|
@@ -76693,10 +76755,24 @@ const useItemStore = ({
|
|
|
76693
76755
|
let start = missingStart;
|
|
76694
76756
|
let end = missingEnd;
|
|
76695
76757
|
let around;
|
|
76696
|
-
//
|
|
76697
|
-
//
|
|
76698
|
-
|
|
76699
|
-
|
|
76758
|
+
// The window draws items from before: they are asked for again, and stay
|
|
76759
|
+
// drawn meanwhile.
|
|
76760
|
+
const staleRanks = staleRanksRef.current;
|
|
76761
|
+
let staleFrom = -1;
|
|
76762
|
+
let staleTo = -1;
|
|
76763
|
+
if (staleRanks.size > 0) {
|
|
76764
|
+
let rank = windowFrom;
|
|
76765
|
+
while (rank < windowTo) {
|
|
76766
|
+
if (staleRanks.has(rank)) {
|
|
76767
|
+
if (staleFrom === -1) {
|
|
76768
|
+
staleFrom = rank;
|
|
76769
|
+
}
|
|
76770
|
+
staleTo = rank;
|
|
76771
|
+
}
|
|
76772
|
+
rank++;
|
|
76773
|
+
}
|
|
76774
|
+
}
|
|
76775
|
+
const revalidating = staleFrom !== -1;
|
|
76700
76776
|
// The list is held on an item nothing on screen leads to: the items it holds
|
|
76701
76777
|
// do not contain it, so no window it could draw will ever bring it. Only
|
|
76702
76778
|
// asking for it by name does.
|
|
@@ -76713,18 +76789,36 @@ const useItemStore = ({
|
|
|
76713
76789
|
start = from < 0 ? 0 : from;
|
|
76714
76790
|
end = start + budget - 1;
|
|
76715
76791
|
} else if (revalidating) {
|
|
76716
|
-
|
|
76717
|
-
|
|
76718
|
-
if (
|
|
76719
|
-
|
|
76720
|
-
|
|
76721
|
-
|
|
76722
|
-
|
|
76723
|
-
|
|
76724
|
-
|
|
76725
|
-
|
|
76726
|
-
|
|
76727
|
-
|
|
76792
|
+
// The items from before that the window draws, and the missing ones
|
|
76793
|
+
// with them: one ask.
|
|
76794
|
+
if (start === -1 || staleFrom < start) {
|
|
76795
|
+
start = staleFrom;
|
|
76796
|
+
}
|
|
76797
|
+
if (staleTo > end) {
|
|
76798
|
+
end = staleTo;
|
|
76799
|
+
}
|
|
76800
|
+
// Grown over the items from before on either side, up to a page: the
|
|
76801
|
+
// window grows past its first picture right after it is painted, and
|
|
76802
|
+
// what it grows onto would otherwise be asked for once more.
|
|
76803
|
+
while (end - start + 1 < budget) {
|
|
76804
|
+
const staleBefore = staleRanks.has(start - 1);
|
|
76805
|
+
const staleAfter = staleRanks.has(end + 1);
|
|
76806
|
+
if (!staleBefore && !staleAfter) {
|
|
76807
|
+
break;
|
|
76808
|
+
}
|
|
76809
|
+
if (staleBefore) {
|
|
76810
|
+
start--;
|
|
76811
|
+
}
|
|
76812
|
+
if (staleAfter && end - start + 1 < budget) {
|
|
76813
|
+
end++;
|
|
76814
|
+
}
|
|
76815
|
+
}
|
|
76816
|
+
// Anchored on the first item from before on screen: a source
|
|
76817
|
+
// paginating by cursor gets an item to count from, and the reading
|
|
76818
|
+
// position is what must survive.
|
|
76819
|
+
const firstStale = pages.byIndex.get(staleFrom);
|
|
76820
|
+
if (firstStale && firstStale.id !== undefined) {
|
|
76821
|
+
around = firstStale.id;
|
|
76728
76822
|
}
|
|
76729
76823
|
} else if (pages.count === undefined) {
|
|
76730
76824
|
const scrolled = listItems.scrolled;
|
|
@@ -76778,7 +76872,8 @@ const useItemStore = ({
|
|
|
76778
76872
|
debugAsk("already revalidating");
|
|
76779
76873
|
return;
|
|
76780
76874
|
}
|
|
76781
|
-
// A page for
|
|
76875
|
+
// A page asked for before the items went stale answers for the
|
|
76876
|
+
// collection as it was.
|
|
76782
76877
|
request.controller?.abort();
|
|
76783
76878
|
request.busy = false;
|
|
76784
76879
|
}
|
|
@@ -76799,11 +76894,11 @@ const useItemStore = ({
|
|
|
76799
76894
|
request.controller?.abort();
|
|
76800
76895
|
request.busy = false;
|
|
76801
76896
|
}
|
|
76802
|
-
const held = pages.byIndex.size;
|
|
76803
76897
|
// Asking again for a range that was already asked for, having received
|
|
76804
|
-
// nothing since, can only produce the same answer
|
|
76805
|
-
//
|
|
76806
|
-
|
|
76898
|
+
// nothing since, can only produce the same answer — unless the
|
|
76899
|
+
// collection moved since, which is what an invalidation says.
|
|
76900
|
+
const held = `${pages.byIndex.size}/${staleRanks.size}/${invalidation}`;
|
|
76901
|
+
if (request.start === start && request.end === end && request.held === held) {
|
|
76807
76902
|
debugAsk("this range was asked for already");
|
|
76808
76903
|
return;
|
|
76809
76904
|
}
|
|
@@ -76839,7 +76934,6 @@ const useItemStore = ({
|
|
|
76839
76934
|
request.revalidating = false;
|
|
76840
76935
|
setFailure(null);
|
|
76841
76936
|
if (revalidating) {
|
|
76842
|
-
staleRef.current = false;
|
|
76843
76937
|
setRefreshing(false);
|
|
76844
76938
|
}
|
|
76845
76939
|
publishRequestState();
|
|
@@ -76858,15 +76952,35 @@ const useItemStore = ({
|
|
|
76858
76952
|
// Before the items land: what is on screen has to stay where it is,
|
|
76859
76953
|
// and the DOM still shows the state to hold onto.
|
|
76860
76954
|
listItems.captureAnchor();
|
|
76861
|
-
|
|
76862
|
-
|
|
76863
|
-
|
|
76864
|
-
|
|
76865
|
-
|
|
76955
|
+
// An item from before is gone from where it stood when the page puts
|
|
76956
|
+
// it at another rank (it would be drawn twice), or when its rank is
|
|
76957
|
+
// past the end of the collection.
|
|
76958
|
+
const staleRanksNow = staleRanksRef.current;
|
|
76959
|
+
const pageEnd = pageStart + pageItems.length;
|
|
76960
|
+
let dropped = false;
|
|
76961
|
+
if (staleRanksNow.size > 0) {
|
|
76962
|
+
const pageIds = new Set();
|
|
76963
|
+
for (const pageItem of pageItems) {
|
|
76964
|
+
if (pageItem && pageItem.id !== undefined) {
|
|
76965
|
+
pageIds.add(pageItem.id);
|
|
76966
|
+
}
|
|
76967
|
+
}
|
|
76968
|
+
for (const rank of staleRanksNow) {
|
|
76969
|
+
if (rank >= pageStart && rank < pageEnd) {
|
|
76970
|
+
continue;
|
|
76971
|
+
}
|
|
76972
|
+
const staleItem = pages.byIndex.get(rank);
|
|
76973
|
+
if (rank >= pageCount || staleItem && pageIds.has(staleItem.id)) {
|
|
76974
|
+
staleRanksNow.delete(rank);
|
|
76975
|
+
pages.byIndex.delete(rank);
|
|
76976
|
+
dropped = true;
|
|
76977
|
+
}
|
|
76978
|
+
}
|
|
76866
76979
|
}
|
|
76867
76980
|
let i = 0;
|
|
76868
76981
|
while (i < pageItems.length) {
|
|
76869
76982
|
pages.byIndex.set(pageStart + i, pageItems[i]);
|
|
76983
|
+
staleRanksNow.delete(pageStart + i);
|
|
76870
76984
|
i++;
|
|
76871
76985
|
}
|
|
76872
76986
|
pages.count = pageCount;
|
|
@@ -76876,7 +76990,7 @@ const useItemStore = ({
|
|
|
76876
76990
|
itemsAction.writeComposition({
|
|
76877
76991
|
byIndex: pages.byIndex,
|
|
76878
76992
|
count: pageCount,
|
|
76879
|
-
replace:
|
|
76993
|
+
replace: dropped
|
|
76880
76994
|
});
|
|
76881
76995
|
}
|
|
76882
76996
|
listItems.pagesSignal.value = listItems.pagesSignal.peek() + 1;
|
|
@@ -76891,9 +77005,14 @@ const useItemStore = ({
|
|
|
76891
77005
|
request.revalidating = false;
|
|
76892
77006
|
publishRequestState();
|
|
76893
77007
|
if (revalidating) {
|
|
76894
|
-
// The items from before stay
|
|
77008
|
+
// The items from before stay, and are not asked for again until the
|
|
77009
|
+
// source says the collection moved: a revalidation that failed has
|
|
76895
77010
|
// nothing better to put in their place.
|
|
76896
|
-
|
|
77011
|
+
let rank = start;
|
|
77012
|
+
while (rank <= end) {
|
|
77013
|
+
staleRanksRef.current.delete(rank);
|
|
77014
|
+
rank++;
|
|
77015
|
+
}
|
|
76897
77016
|
setRefreshing(false);
|
|
76898
77017
|
return;
|
|
76899
77018
|
}
|
|
@@ -77210,7 +77329,8 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
77210
77329
|
* "Many rows"). The window keeps three quarters of what the screen leaves of
|
|
77211
77330
|
* it ahead of the scroll, and moves once that falls under half a screen; it
|
|
77212
77331
|
* has to hold more than the screen shows, with room for that lookahead — the
|
|
77213
|
-
* list warns when it
|
|
77332
|
+
* list warns when it holds no more than the screen. How much more is
|
|
77333
|
+
* measured: fling the list on the slowest device it runs on.
|
|
77214
77334
|
*
|
|
77215
77335
|
* `{ initial, after }` for a list whose first picture is taken as it is
|
|
77216
77336
|
* built — drawn in the click that opens a popup, or in the update callback
|