@jsenv/navi 0.29.140 → 0.29.142
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 +266 -245
- package/dist/jsenv_navi.js.map +26 -32
- package/docs/popup_open.md +18 -5
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal, getAppHeight, getAppWidth, coarsePointerSignal, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
|
|
6
6
|
export { disableVirtualKeyboardOverlay } from "./jsenv_navi_side_effects.js";
|
|
7
7
|
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, initFocusGroup, scrollIntoViewScoped, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, isTouchDrivenEvent, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
8
|
-
export { clickIsSuppressed, contrastColor, findEvent, startDragTo } from "@jsenv/dom";
|
|
8
|
+
export { chainEvent, clickIsSuppressed, contrastColor, findEvent, startDragTo } from "@jsenv/dom";
|
|
9
9
|
import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
|
|
10
10
|
import { isValidElement, h, Fragment, createContext, render, toChildArray, options, cloneElement } from "preact";
|
|
11
11
|
import { useErrorBoundary, useLayoutEffect, useContext, useCallback, useRef, useState, useEffect, useMemo, useId } from "preact/hooks";
|
|
@@ -30465,7 +30465,16 @@ const anyMatchingRouteSignal = (routes) => {
|
|
|
30465
30465
|
* @param {Element} element The element asking — the command's source, and the
|
|
30466
30466
|
* anchor a popup opens on unless `anchor` says otherwise.
|
|
30467
30467
|
* @param {string} command
|
|
30468
|
-
* @param {Event} event What
|
|
30468
|
+
* @param {Event} event What caused this. Mandatory: it is what makes a command
|
|
30469
|
+
* traceable back to its origin — the debug panel groups everything a gesture
|
|
30470
|
+
* set off under it, and the gates below read it to know whether the default
|
|
30471
|
+
* was already prevented and which mouse button was pressed. When nothing was
|
|
30472
|
+
* handed over — a timer firing, an action settling, a signal changing — build
|
|
30473
|
+
* a `CustomEvent` that names what happened and chain it to whatever preceded
|
|
30474
|
+
* it, rather than leaving the origin unsaid:
|
|
30475
|
+
* const expiredEvent = new CustomEvent("session_expired");
|
|
30476
|
+
* chainEvent(expiredEvent, causeEvent); // when something did precede it
|
|
30477
|
+
* triggerNaviCommand(dialogEl, "--navi-open", expiredEvent);
|
|
30469
30478
|
* @param {object} [options]
|
|
30470
30479
|
* @param {boolean} [options.optional] No suitable target is not a warning.
|
|
30471
30480
|
* @param {any} [options.value] What the command is about, carried to whoever
|
|
@@ -30482,6 +30491,11 @@ const triggerNaviCommand = (
|
|
|
30482
30491
|
event,
|
|
30483
30492
|
{ optional, value, anchor } = {},
|
|
30484
30493
|
) => {
|
|
30494
|
+
if (!event) {
|
|
30495
|
+
throw new Error(
|
|
30496
|
+
`"${command}" triggered without an event: it is mandatory, a command must say what caused it. Pass the gesture, or a CustomEvent naming the cause when no gesture did — see triggerNaviCommand's jsdoc.`,
|
|
30497
|
+
);
|
|
30498
|
+
}
|
|
30485
30499
|
const naviCommand =
|
|
30486
30500
|
NAVI_COMMANDS[command] || NAVI_COMMANDS[commandName(command)];
|
|
30487
30501
|
if (!naviCommand) {
|
|
@@ -33597,6 +33611,12 @@ const GROUP_DEFAULTS = {
|
|
|
33597
33611
|
*
|
|
33598
33612
|
* **Filtering**: `childControlFilter` can exclude certain child types from aggregation
|
|
33599
33613
|
* (e.g. ignoring buttons inside a selectable list).
|
|
33614
|
+
*
|
|
33615
|
+
* **Aggregating**: `aggregateChildStates(children, fallbackState, stateNow)` — what the
|
|
33616
|
+
* children add up to. `fallbackState` is the empty of the declared `stateType`;
|
|
33617
|
+
* `stateNow` is what the group is worth as it is asked, which an aggregate reads to
|
|
33618
|
+
* answer for children that are not there (a selectable list whose selected row is not
|
|
33619
|
+
* drawn).
|
|
33600
33620
|
*/
|
|
33601
33621
|
const useUIGroupStateController = (
|
|
33602
33622
|
props,
|
|
@@ -33753,16 +33773,22 @@ const useUIGroupStateController = (
|
|
|
33753
33773
|
// taking that for an answer is how a value handed to it evaporates on
|
|
33754
33774
|
// the way in, and how that emptiness then travels back up to whoever
|
|
33755
33775
|
// handed it (a picker showing its row as unanswered).
|
|
33756
|
-
|
|
33776
|
+
//
|
|
33777
|
+
// `stateNow` is what the group is worth as it is asked: what it keeps
|
|
33778
|
+
// when there is nobody to ask, and what an aggregate reads to answer for
|
|
33779
|
+
// what its children do not say — a selection whose row is not drawn (see
|
|
33780
|
+
// ListSelectable) lives there and nowhere else.
|
|
33781
|
+
const aggregateGroupUIState = (stateNow) => {
|
|
33757
33782
|
const someChildCanAnswer = childUIStateControllerArray.some(
|
|
33758
33783
|
shouldPropagateStateToChild,
|
|
33759
33784
|
);
|
|
33760
33785
|
if (!someChildCanAnswer) {
|
|
33761
|
-
return
|
|
33786
|
+
return stateNow;
|
|
33762
33787
|
}
|
|
33763
33788
|
const aggChildState = resolvedAggregateChildStates(
|
|
33764
33789
|
childUIStateControllerArray,
|
|
33765
33790
|
fallbackState,
|
|
33791
|
+
stateNow,
|
|
33766
33792
|
);
|
|
33767
33793
|
if (aggChildState !== undefined) {
|
|
33768
33794
|
return aggChildState;
|
|
@@ -34286,8 +34312,6 @@ const useUIGroupStateController = (
|
|
|
34286
34312
|
// ── update: runs every render after the first ─────────────────────────
|
|
34287
34313
|
(s) => {
|
|
34288
34314
|
const { controller } = s;
|
|
34289
|
-
const prevValue = controller.value;
|
|
34290
|
-
const prevHasValueProp = controller.hasValueProp;
|
|
34291
34315
|
const prevDefaultValue = controller.defaultValue;
|
|
34292
34316
|
controller.props = props;
|
|
34293
34317
|
controller.ref = ref;
|
|
@@ -34305,10 +34329,14 @@ const useUIGroupStateController = (
|
|
|
34305
34329
|
controller.placeChildrenUIState(groupUIState, propagateDownEvent);
|
|
34306
34330
|
controller.syncInternalState(groupUIState);
|
|
34307
34331
|
};
|
|
34308
|
-
|
|
34309
|
-
|
|
34310
|
-
|
|
34311
|
-
|
|
34332
|
+
// A controlled group goes on showing the value it is given. A child
|
|
34333
|
+
// answering for itself moves what the group is worth — that is what
|
|
34334
|
+
// `uiAction` reports — but the value stays the owner's, so the test is
|
|
34335
|
+
// against what the children are showing, not against the value handed
|
|
34336
|
+
// down last time. A popup reopened on another subject hands down the very
|
|
34337
|
+
// same empty value it did before, and the selection left inside it from
|
|
34338
|
+
// the previous opening is what has to go.
|
|
34339
|
+
if (hasValueProp && !compareTwoJsValues(value, controller.uiState)) {
|
|
34312
34340
|
placeChildrenFrom(value);
|
|
34313
34341
|
}
|
|
34314
34342
|
if (
|
|
@@ -61346,23 +61374,22 @@ const ListSelectable = props => {
|
|
|
61346
61374
|
focusGroupDirection,
|
|
61347
61375
|
focusGroupWrap
|
|
61348
61376
|
} = props;
|
|
61349
|
-
//
|
|
61350
|
-
// draws the rows it needs and no more
|
|
61351
|
-
// of the window
|
|
61352
|
-
//
|
|
61353
|
-
//
|
|
61354
|
-
|
|
61355
|
-
|
|
61356
|
-
|
|
61357
|
-
|
|
61377
|
+
// `kept` is what the list holds as it is asked, which is not the same as what
|
|
61378
|
+
// its rows say: a list draws the rows it needs and no more, so the selected
|
|
61379
|
+
// one may be scrolled out of the window or filtered out of the view, and a
|
|
61380
|
+
// row that is not there cannot say it is not selected. Reading it off the
|
|
61381
|
+
// group rather than remembering it here is what makes a value put ON the list
|
|
61382
|
+
// (a `value` prop, a signal, a reopened popup) replace the whole selection —
|
|
61383
|
+
// a private memory of its own would go on holding the rows it could not see
|
|
61384
|
+
// being unselected.
|
|
61385
|
+
//
|
|
61358
61386
|
// `fallbackState` is the empty of the shape the list declared below
|
|
61359
61387
|
// (`stateType`): `[]` for a multiple list, nothing for a single one. Taking
|
|
61360
61388
|
// it is what lets an emptied list say "empty" — `undefined` is the word for
|
|
61361
61389
|
// "unset", and a bound stateSignal reads that as "nothing decided here, go
|
|
61362
61390
|
// back to the default" (see docs/control_value.md), which is how a list
|
|
61363
61391
|
// emptied down to its last row puts that row back on reload.
|
|
61364
|
-
const aggregateChildStates = (children, fallbackState) => {
|
|
61365
|
-
const kept = selectionRef.current;
|
|
61392
|
+
const aggregateChildStates = (children, fallbackState, kept) => {
|
|
61366
61393
|
if (multiple) {
|
|
61367
61394
|
const drawnValues = new Set(children.map(child => child.props.value));
|
|
61368
61395
|
const stillSelected = Array.isArray(kept) ? kept.filter(value => !drawnValues.has(value)) : [];
|
|
@@ -61371,13 +61398,10 @@ const ListSelectable = props => {
|
|
|
61371
61398
|
stillSelected.push(child.uiState);
|
|
61372
61399
|
}
|
|
61373
61400
|
}
|
|
61374
|
-
|
|
61375
|
-
selectionRef.current = values;
|
|
61376
|
-
return values;
|
|
61401
|
+
return stillSelected.length === 0 ? fallbackState : stillSelected;
|
|
61377
61402
|
}
|
|
61378
61403
|
for (const child of children) {
|
|
61379
61404
|
if (child.uiState !== undefined) {
|
|
61380
|
-
selectionRef.current = child.uiState;
|
|
61381
61405
|
return child.uiState;
|
|
61382
61406
|
}
|
|
61383
61407
|
}
|
|
@@ -61385,7 +61409,6 @@ const ListSelectable = props => {
|
|
|
61385
61409
|
// deselected; if it is not, the list keeps what it holds.
|
|
61386
61410
|
const keptIsDrawn = children.some(child => child.props.value === kept);
|
|
61387
61411
|
if (keptIsDrawn) {
|
|
61388
|
-
selectionRef.current = undefined;
|
|
61389
61412
|
return undefined;
|
|
61390
61413
|
}
|
|
61391
61414
|
return kept;
|
|
@@ -61984,12 +62007,9 @@ const ListVirtualContext = createContext(null);
|
|
|
61984
62007
|
// that returning a component of one's own — instead of a bare <List.Item> —
|
|
61985
62008
|
// works the same way.
|
|
61986
62009
|
const ListRowContext = createContext(null);
|
|
61987
|
-
// The slot a child of the list stands in
|
|
61988
|
-
//
|
|
61989
|
-
//
|
|
61990
|
-
// context subscriber is re-rendered even when preact skipped the component
|
|
61991
|
-
// that holds it, which is what lets a row nobody rebuilt follow the list all
|
|
61992
|
-
// the same.
|
|
62010
|
+
// The slot a child of the list stands in, by id (see ListDeclaredChildren). A
|
|
62011
|
+
// row takes its place in the collection by slot: the place is then the list's
|
|
62012
|
+
// to move, and the row's to follow — see createListVirtual.
|
|
61993
62013
|
const ListSlotContext = createContext(null);
|
|
61994
62014
|
const css$x = /* css */`
|
|
61995
62015
|
@layer navi {
|
|
@@ -64947,9 +64967,7 @@ const ListItemUI = props => {
|
|
|
64947
64967
|
// registered the row, decided it is inside the render window, and placed its
|
|
64948
64968
|
// separator. All that is left here is to draw it.
|
|
64949
64969
|
const row = useContext(ListRowContext);
|
|
64950
|
-
|
|
64951
|
-
// ListDeclaredChildren).
|
|
64952
|
-
const slot = useContext(ListSlotContext);
|
|
64970
|
+
const slotId = useContext(ListSlotContext);
|
|
64953
64971
|
// There is no standalone match/matchScore/highlight prop — participation
|
|
64954
64972
|
// in a matching system (search, filter…) only goes through `matchInfo`
|
|
64955
64973
|
// (e.g. useSearchText's getItemMatchInfo(item): { match, matchScore,
|
|
@@ -64975,18 +64993,20 @@ const ListItemUI = props => {
|
|
|
64975
64993
|
// Where a row sits is where it was declared, full stop: a list is written in
|
|
64976
64994
|
// the order it reads. Its slot is what says that — a search that reorders
|
|
64977
64995
|
// rows reorders the rows it declares, and the slots follow. A row drawn by a
|
|
64978
|
-
// run already knows its place; the run gave it.
|
|
64996
|
+
// run already knows its place; the run gave it. The place is taken in the
|
|
64997
|
+
// name of this very component (idDefault, not the row's id): two components
|
|
64998
|
+
// may stand for the same row for a moment, one leaving as the other arrives,
|
|
64999
|
+
// and the one leaving must give back its own place, not the newcomer's.
|
|
64979
65000
|
if (!row) {
|
|
64980
65001
|
if (props.filtered) {
|
|
64981
|
-
virtual.drop(
|
|
65002
|
+
virtual.drop(idDefault);
|
|
64982
65003
|
} else {
|
|
64983
|
-
props.index = virtual.take(
|
|
65004
|
+
props.index = virtual.take(idDefault, 1, slotId);
|
|
64984
65005
|
}
|
|
64985
65006
|
}
|
|
64986
|
-
const rowId = props.id;
|
|
64987
65007
|
useLayoutEffect(() => {
|
|
64988
65008
|
return () => {
|
|
64989
|
-
virtual.drop(
|
|
65009
|
+
virtual.drop(idDefault);
|
|
64990
65010
|
};
|
|
64991
65011
|
}, []);
|
|
64992
65012
|
// Every row that is drawn registers itself, whether it was declared one by
|
|
@@ -65355,9 +65375,10 @@ List.Item = ListItem;
|
|
|
65355
65375
|
// So the places are read off the walk instead of off the renders: the list
|
|
65356
65376
|
// names a slot for each of its children and declares them here, in order,
|
|
65357
65377
|
// before any of them renders (see ListDeclaredChildren). A child then takes
|
|
65358
|
-
// its place BY SLOT and
|
|
65359
|
-
//
|
|
65360
|
-
//
|
|
65378
|
+
// its place BY SLOT, and the place is a signal: it moves when what stands
|
|
65379
|
+
// before it changes — a row filtered out, a run taking in rows, a slot added
|
|
65380
|
+
// or moved — and the child follows, rendered again for it whether or not
|
|
65381
|
+
// anything else would have rendered it.
|
|
65361
65382
|
const createListVirtual = () => {
|
|
65362
65383
|
const totalSignal = signal(0);
|
|
65363
65384
|
// Bumped whenever a run takes in rows. The list itself has to hear about it:
|
|
@@ -65369,100 +65390,108 @@ const createListVirtual = () => {
|
|
|
65369
65390
|
// an attribute: what is drawn is from before, and the app may want to say so
|
|
65370
65391
|
// without taking anything away.
|
|
65371
65392
|
const refreshingSignal = signal(0);
|
|
65372
|
-
//
|
|
65373
|
-
//
|
|
65374
|
-
//
|
|
65375
|
-
const
|
|
65376
|
-
//
|
|
65377
|
-
//
|
|
65393
|
+
// The slots each walk declared, in order, by the slot the walk stands in
|
|
65394
|
+
// (null for the list's own children). Together they are a tree: a group's
|
|
65395
|
+
// rows live inside the group's slot.
|
|
65396
|
+
const slotIdsByParent = new Map();
|
|
65397
|
+
// Who took a place — a row, or a run of rows — and how many rows of the
|
|
65398
|
+
// collection it stands for. The place itself is a signal, see take.
|
|
65378
65399
|
const ownerById = new Map();
|
|
65379
|
-
|
|
65380
|
-
//
|
|
65381
|
-
//
|
|
65382
|
-
|
|
65383
|
-
// going over the others again.
|
|
65384
|
-
const orderedOwnerIds = [];
|
|
65385
|
-
let rowTotal = 0;
|
|
65400
|
+
// The owners standing in each slot, in the order they took their place.
|
|
65401
|
+
// One, as a rule; a child that renders several rows keeps them in the order
|
|
65402
|
+
// they first rendered, which is all it can be told.
|
|
65403
|
+
const ownerIdsBySlot = new Map();
|
|
65386
65404
|
const locatorByOwner = new Map();
|
|
65387
|
-
|
|
65388
|
-
//
|
|
65389
|
-
|
|
65390
|
-
|
|
65405
|
+
// The slots as the tree reads, first to last, and where each stands in it.
|
|
65406
|
+
// Rebuilt once a walk has changed the tree, read to place the owners.
|
|
65407
|
+
const slotWalk = [];
|
|
65408
|
+
const rankBySlot = new Map();
|
|
65409
|
+
let rowTotal = 0;
|
|
65410
|
+
// Owners have left and the others have not been moved up yet. Done on the
|
|
65411
|
+
// next ask rather than on the spot: rows leave many at a time (a search, a
|
|
65412
|
+
// list unmounting), and moving the others up once is enough.
|
|
65391
65413
|
let placesStale = false;
|
|
65392
|
-
|
|
65393
|
-
//
|
|
65394
|
-
//
|
|
65395
|
-
|
|
65396
|
-
|
|
65397
|
-
|
|
65398
|
-
|
|
65399
|
-
|
|
65400
|
-
|
|
65414
|
+
// Where the last slot holding an owner stands: an owner arriving at or after
|
|
65415
|
+
// it is placed at the end without going over the others — a whole first
|
|
65416
|
+
// render, rows arriving in order, costs each row nothing but itself.
|
|
65417
|
+
let rankOwnedLast = -1;
|
|
65418
|
+
const rebuildWalk = () => {
|
|
65419
|
+
slotWalk.length = 0;
|
|
65420
|
+
rankBySlot.clear();
|
|
65421
|
+
const visit = parentSlotId => {
|
|
65422
|
+
const slotIds = slotIdsByParent.get(parentSlotId);
|
|
65423
|
+
if (!slotIds) {
|
|
65424
|
+
return;
|
|
65401
65425
|
}
|
|
65402
|
-
|
|
65403
|
-
|
|
65404
|
-
|
|
65405
|
-
|
|
65426
|
+
for (const slotId of slotIds) {
|
|
65427
|
+
rankBySlot.set(slotId, slotWalk.length);
|
|
65428
|
+
slotWalk.push(slotId);
|
|
65429
|
+
visit(slotId);
|
|
65406
65430
|
}
|
|
65407
|
-
}
|
|
65408
|
-
|
|
65409
|
-
// Where a slot sits, said as the ranks of the slots it lives in, outermost
|
|
65410
|
-
// first: comparing two of them keeps a group's rows inside their group and
|
|
65411
|
-
// the groups where the list declared them.
|
|
65412
|
-
const pathOf = slotId => {
|
|
65413
|
-
const pathCached = pathCache.get(slotId);
|
|
65414
|
-
if (pathCached) {
|
|
65415
|
-
return pathCached;
|
|
65416
|
-
}
|
|
65417
|
-
const slot = slotById.get(slotId);
|
|
65418
|
-
if (!slot) {
|
|
65419
|
-
return null;
|
|
65420
|
-
}
|
|
65421
|
-
let path;
|
|
65422
|
-
if (slot.parentId === null) {
|
|
65423
|
-
path = [slot.order];
|
|
65424
|
-
} else {
|
|
65425
|
-
const parentPath = pathOf(slot.parentId);
|
|
65426
|
-
path = parentPath === null ? [slot.order] : [...parentPath, slot.order];
|
|
65427
|
-
}
|
|
65428
|
-
pathCache.set(slotId, path);
|
|
65429
|
-
return path;
|
|
65431
|
+
};
|
|
65432
|
+
visit(null);
|
|
65430
65433
|
};
|
|
65431
|
-
//
|
|
65432
|
-
//
|
|
65433
|
-
//
|
|
65434
|
-
// slot moving or a child taking a different number of rows.
|
|
65434
|
+
// Every place, in one go: a place is the sum of what stands before it, so
|
|
65435
|
+
// there is nothing to hand out one at a time. Writing a place that did not
|
|
65436
|
+
// change wakes nobody — a signal ignores a value equal to its own.
|
|
65435
65437
|
const refreshPlaces = () => {
|
|
65436
65438
|
placesStale = false;
|
|
65437
|
-
const owners = [...ownerById.entries()];
|
|
65438
|
-
owners.sort(([, leftOwner], [, rightOwner]) => {
|
|
65439
|
-
const comparison = comparePath(pathOf(leftOwner.slotId), pathOf(rightOwner.slotId));
|
|
65440
|
-
if (comparison !== 0) {
|
|
65441
|
-
return comparison;
|
|
65442
|
-
}
|
|
65443
|
-
return leftOwner.sequence - rightOwner.sequence;
|
|
65444
|
-
});
|
|
65445
|
-
orderedOwnerIds.length = 0;
|
|
65446
65439
|
let index = 0;
|
|
65447
|
-
|
|
65448
|
-
|
|
65449
|
-
|
|
65450
|
-
|
|
65440
|
+
let rank = 0;
|
|
65441
|
+
rankOwnedLast = -1;
|
|
65442
|
+
while (rank < slotWalk.length) {
|
|
65443
|
+
const ownerIds = ownerIdsBySlot.get(slotWalk[rank]);
|
|
65444
|
+
if (ownerIds) {
|
|
65445
|
+
for (const ownerId of ownerIds) {
|
|
65446
|
+
const owner = ownerById.get(ownerId);
|
|
65447
|
+
owner.placeSignal.value = index;
|
|
65448
|
+
index += owner.rowCount;
|
|
65449
|
+
}
|
|
65450
|
+
rankOwnedLast = rank;
|
|
65451
|
+
}
|
|
65452
|
+
rank++;
|
|
65451
65453
|
}
|
|
65452
65454
|
rowTotal = index;
|
|
65453
|
-
totalSignal.value =
|
|
65455
|
+
totalSignal.value = index;
|
|
65454
65456
|
};
|
|
65455
|
-
|
|
65456
|
-
|
|
65457
|
-
if (
|
|
65458
|
-
|
|
65457
|
+
const addToSlot = (slotId, ownerId) => {
|
|
65458
|
+
const ownerIds = ownerIdsBySlot.get(slotId);
|
|
65459
|
+
if (ownerIds) {
|
|
65460
|
+
ownerIds.push(ownerId);
|
|
65461
|
+
} else {
|
|
65462
|
+
ownerIdsBySlot.set(slotId, [ownerId]);
|
|
65463
|
+
}
|
|
65464
|
+
};
|
|
65465
|
+
const removeFromSlot = (slotId, ownerId) => {
|
|
65466
|
+
const ownerIds = ownerIdsBySlot.get(slotId);
|
|
65467
|
+
if (!ownerIds) {
|
|
65468
|
+
return;
|
|
65459
65469
|
}
|
|
65460
|
-
const
|
|
65461
|
-
|
|
65462
|
-
|
|
65463
|
-
|
|
65470
|
+
const index = ownerIds.indexOf(ownerId);
|
|
65471
|
+
if (index !== -1) {
|
|
65472
|
+
ownerIds.splice(index, 1);
|
|
65473
|
+
}
|
|
65474
|
+
if (ownerIds.length === 0) {
|
|
65475
|
+
ownerIdsBySlot.delete(slotId);
|
|
65476
|
+
}
|
|
65477
|
+
};
|
|
65478
|
+
// A slot the walk no longer names: whatever stood in it is gone, and so is
|
|
65479
|
+
// whatever a walk inside it had declared.
|
|
65480
|
+
const dropSlot = slotId => {
|
|
65481
|
+
const ownerIds = ownerIdsBySlot.get(slotId);
|
|
65482
|
+
if (ownerIds) {
|
|
65483
|
+
for (const ownerId of ownerIds) {
|
|
65484
|
+
ownerById.delete(ownerId);
|
|
65485
|
+
}
|
|
65486
|
+
ownerIdsBySlot.delete(slotId);
|
|
65487
|
+
}
|
|
65488
|
+
const childSlotIds = slotIdsByParent.get(slotId);
|
|
65489
|
+
if (childSlotIds) {
|
|
65490
|
+
slotIdsByParent.delete(slotId);
|
|
65491
|
+
for (const childSlotId of childSlotIds) {
|
|
65492
|
+
dropSlot(childSlotId);
|
|
65493
|
+
}
|
|
65464
65494
|
}
|
|
65465
|
-
return lastOwner.sequence < owner.sequence;
|
|
65466
65495
|
};
|
|
65467
65496
|
const virtual = {
|
|
65468
65497
|
totalSignal,
|
|
@@ -65486,40 +65515,30 @@ const createListVirtual = () => {
|
|
|
65486
65515
|
renderSkeleton: undefined,
|
|
65487
65516
|
// The children a walk stands over, in order — said in one call, before any
|
|
65488
65517
|
// of them renders, so that what a child asks next is answered against the
|
|
65489
|
-
// whole picture and not against the children that happened to render
|
|
65518
|
+
// whole picture and not against the children that happened to render
|
|
65519
|
+
// first. Said again on every render of the walk, and heard only when
|
|
65520
|
+
// something moved.
|
|
65490
65521
|
declareSlots: (parentSlotId, slotIds) => {
|
|
65491
|
-
const
|
|
65492
|
-
|
|
65493
|
-
|
|
65494
|
-
dropSlot(slotId);
|
|
65495
|
-
pathCache.clear();
|
|
65496
|
-
placesStale = true;
|
|
65497
|
-
}
|
|
65522
|
+
const slotIdsPrevious = slotIdsByParent.get(parentSlotId);
|
|
65523
|
+
if (slotIdsPrevious && sameSlotIds(slotIdsPrevious, slotIds)) {
|
|
65524
|
+
return;
|
|
65498
65525
|
}
|
|
65499
|
-
|
|
65500
|
-
|
|
65501
|
-
const
|
|
65502
|
-
|
|
65503
|
-
|
|
65504
|
-
|
|
65505
|
-
slotById.set(slotId, {
|
|
65506
|
-
parentId: parentSlotId,
|
|
65507
|
-
order
|
|
65508
|
-
});
|
|
65509
|
-
} else if (slot.order !== order) {
|
|
65510
|
-
slot.order = order;
|
|
65511
|
-
pathCache.clear();
|
|
65512
|
-
placesStale = true;
|
|
65526
|
+
if (slotIdsPrevious) {
|
|
65527
|
+
const slotIdSet = new Set(slotIds);
|
|
65528
|
+
for (const slotId of slotIdsPrevious) {
|
|
65529
|
+
if (!slotIdSet.has(slotId)) {
|
|
65530
|
+
dropSlot(slotId);
|
|
65531
|
+
}
|
|
65513
65532
|
}
|
|
65514
|
-
order++;
|
|
65515
|
-
}
|
|
65516
|
-
// Now, rather than when the first child asks: the walk has just said
|
|
65517
|
-
// where everything stands, so every place can be settled once instead of
|
|
65518
|
-
// once per child.
|
|
65519
|
-
if (placesStale) {
|
|
65520
|
-
refreshPlaces();
|
|
65521
65533
|
}
|
|
65534
|
+
slotIdsByParent.set(parentSlotId, slotIds);
|
|
65535
|
+
rebuildWalk();
|
|
65536
|
+
refreshPlaces();
|
|
65522
65537
|
},
|
|
65538
|
+
// Whether something has taken this slot for its own: what it renders
|
|
65539
|
+
// inside is then its to place (a run draws its groups with their rows
|
|
65540
|
+
// already placed), and no walk inside it has anything to declare.
|
|
65541
|
+
slotHasOwner: slotId => ownerIdsBySlot.has(slotId),
|
|
65523
65542
|
// Whether any run of rows lives in this list: what makes a render window
|
|
65524
65543
|
// mean anything (see List's renderBudget).
|
|
65525
65544
|
hasRuns: () => locatorByOwner.size > 0,
|
|
@@ -65539,127 +65558,129 @@ const createListVirtual = () => {
|
|
|
65539
65558
|
}
|
|
65540
65559
|
return null;
|
|
65541
65560
|
},
|
|
65542
|
-
// The place the
|
|
65543
|
-
//
|
|
65544
|
-
// and answered
|
|
65545
|
-
//
|
|
65561
|
+
// The place the owner's rows start at — read from a signal, so that the
|
|
65562
|
+
// owner is rendered again when it moves (see createListVirtual). Asked on
|
|
65563
|
+
// every render, and answered without a second look for as long as the
|
|
65564
|
+
// owner stands in the same slot for the same number of rows.
|
|
65546
65565
|
take: (ownerId, rowCount, slotId) => {
|
|
65547
|
-
|
|
65548
|
-
if (
|
|
65566
|
+
let owner = ownerById.get(ownerId);
|
|
65567
|
+
if (owner) {
|
|
65568
|
+
if (owner.slotId !== slotId || owner.rowCount !== rowCount) {
|
|
65569
|
+
removeFromSlot(owner.slotId, ownerId);
|
|
65570
|
+
addToSlot(slotId, ownerId);
|
|
65571
|
+
owner.slotId = slotId;
|
|
65572
|
+
owner.rowCount = rowCount;
|
|
65573
|
+
placesStale = true;
|
|
65574
|
+
}
|
|
65549
65575
|
if (placesStale) {
|
|
65550
65576
|
refreshPlaces();
|
|
65551
65577
|
}
|
|
65552
|
-
return
|
|
65578
|
+
return owner.placeSignal.value;
|
|
65579
|
+
}
|
|
65580
|
+
if (placesStale) {
|
|
65581
|
+
refreshPlaces();
|
|
65582
|
+
}
|
|
65583
|
+
const rank = rankBySlot.get(slotId);
|
|
65584
|
+
addToSlot(slotId, ownerId);
|
|
65585
|
+
if (rank !== undefined && rank >= rankOwnedLast) {
|
|
65586
|
+
owner = {
|
|
65587
|
+
slotId,
|
|
65588
|
+
rowCount,
|
|
65589
|
+
placeSignal: signal(rowTotal)
|
|
65590
|
+
};
|
|
65591
|
+
ownerById.set(ownerId, owner);
|
|
65592
|
+
rowTotal += rowCount;
|
|
65593
|
+
rankOwnedLast = rank;
|
|
65594
|
+
totalSignal.value = rowTotal;
|
|
65595
|
+
return owner.placeSignal.value;
|
|
65553
65596
|
}
|
|
65554
|
-
|
|
65597
|
+
owner = {
|
|
65555
65598
|
slotId,
|
|
65556
65599
|
rowCount,
|
|
65557
|
-
|
|
65600
|
+
placeSignal: signal(0)
|
|
65558
65601
|
};
|
|
65559
65602
|
ownerById.set(ownerId, owner);
|
|
65560
|
-
if (!previous && !placesStale && standsLast(owner)) {
|
|
65561
|
-
orderedOwnerIds.push(ownerId);
|
|
65562
|
-
placeByOwner.set(ownerId, rowTotal);
|
|
65563
|
-
rowTotal += rowCount;
|
|
65564
|
-
totalSignal.value = rowTotal;
|
|
65565
|
-
return placeByOwner.get(ownerId);
|
|
65566
|
-
}
|
|
65567
|
-
placesStale = true;
|
|
65568
65603
|
refreshPlaces();
|
|
65569
|
-
return
|
|
65604
|
+
return owner.placeSignal.value;
|
|
65570
65605
|
},
|
|
65571
|
-
// The
|
|
65572
|
-
//
|
|
65606
|
+
// The owner stands for no row of the collection: it was filtered out by a
|
|
65607
|
+
// search, or it is gone.
|
|
65573
65608
|
drop: ownerId => {
|
|
65574
|
-
|
|
65609
|
+
const owner = ownerById.get(ownerId);
|
|
65610
|
+
if (!owner) {
|
|
65575
65611
|
return;
|
|
65576
65612
|
}
|
|
65577
|
-
|
|
65578
|
-
|
|
65613
|
+
ownerById.delete(ownerId);
|
|
65614
|
+
removeFromSlot(owner.slotId, ownerId);
|
|
65615
|
+
if (placesStale) {
|
|
65616
|
+
return;
|
|
65617
|
+
}
|
|
65618
|
+
placesStale = true;
|
|
65619
|
+
queueMicrotask(() => {
|
|
65620
|
+
if (placesStale) {
|
|
65621
|
+
refreshPlaces();
|
|
65622
|
+
}
|
|
65623
|
+
});
|
|
65579
65624
|
}
|
|
65580
65625
|
};
|
|
65581
65626
|
return virtual;
|
|
65582
65627
|
};
|
|
65583
|
-
|
|
65584
|
-
|
|
65585
|
-
|
|
65586
|
-
|
|
65628
|
+
const sameSlotIds = (left, right) => {
|
|
65629
|
+
if (left.length !== right.length) {
|
|
65630
|
+
return false;
|
|
65631
|
+
}
|
|
65632
|
+
let index = 0;
|
|
65633
|
+
while (index < left.length) {
|
|
65634
|
+
if (left[index] !== right[index]) {
|
|
65635
|
+
return false;
|
|
65636
|
+
}
|
|
65637
|
+
index++;
|
|
65638
|
+
}
|
|
65639
|
+
return true;
|
|
65640
|
+
};
|
|
65641
|
+
|
|
65642
|
+
// The walk that gives the list's children their places: a slot for each of
|
|
65643
|
+
// them, declared to the list's virtual all at once before any child renders,
|
|
65644
|
+
// and handed to the child through a provider of its own — which is what lets
|
|
65645
|
+
// the row reach it however deep the caller buried it in components of theirs.
|
|
65587
65646
|
//
|
|
65588
|
-
//
|
|
65589
|
-
//
|
|
65590
|
-
//
|
|
65591
|
-
//
|
|
65647
|
+
// A slot is named the way preact tells the child apart: by key when it has
|
|
65648
|
+
// one, by position otherwise, and inside the array it was given in — a nested
|
|
65649
|
+
// array is one child to preact, so what follows the array keeps its name
|
|
65650
|
+
// however many rows the array holds. A child preact would not render (null,
|
|
65651
|
+
// a boolean) has no slot: it is not there.
|
|
65592
65652
|
const ListDeclaredChildren = ({
|
|
65593
65653
|
children
|
|
65594
65654
|
}) => {
|
|
65595
65655
|
const virtual = useContext(ListVirtualContext);
|
|
65596
|
-
const
|
|
65597
|
-
|
|
65598
|
-
|
|
65599
|
-
const slotsPrevious = slotsRef.current;
|
|
65600
|
-
const childArray = flattenChildren(children);
|
|
65601
|
-
const slots = new Map();
|
|
65602
|
-
const slotIds = [];
|
|
65603
|
-
let index = 0;
|
|
65604
|
-
for (const child of childArray) {
|
|
65605
|
-
const childKey = child && child.key !== undefined && child.key !== null ? `k${child.key}` : `i${index}`;
|
|
65606
|
-
let slotId = parentSlotId === null ? childKey : `${parentSlotId}/${childKey}`;
|
|
65607
|
-
if (slots.has(slotId)) {
|
|
65608
|
-
// Two children answering to the same key: preact cannot tell them apart
|
|
65609
|
-
// either, but a slot each is what keeps them both on screen.
|
|
65610
|
-
slotId = `${slotId}#${index}`;
|
|
65611
|
-
}
|
|
65612
|
-
const slotPrevious = slotsPrevious.get(slotId);
|
|
65613
|
-
slots.set(slotId, slotPrevious && slotPrevious.order === index ? slotPrevious : {
|
|
65614
|
-
id: slotId,
|
|
65615
|
-
order: index
|
|
65616
|
-
});
|
|
65617
|
-
slotIds.push(slotId);
|
|
65618
|
-
index++;
|
|
65656
|
+
const parentSlotId = useContext(ListSlotContext);
|
|
65657
|
+
if (parentSlotId !== null && virtual.slotHasOwner(parentSlotId)) {
|
|
65658
|
+
return children;
|
|
65619
65659
|
}
|
|
65620
|
-
|
|
65660
|
+
const slotIds = [];
|
|
65661
|
+
const declared = [];
|
|
65662
|
+
declareChildren(children, parentSlotId === null ? "" : `${parentSlotId}/`, slotIds, declared);
|
|
65621
65663
|
virtual.declareSlots(parentSlotId, slotIds);
|
|
65622
65664
|
return jsx(Fragment$1, {
|
|
65623
|
-
children:
|
|
65624
|
-
const slotId = slotIds[childIndex];
|
|
65625
|
-
return jsx(ListSlotContext.Provider, {
|
|
65626
|
-
value: slots.get(slotId),
|
|
65627
|
-
children: child
|
|
65628
|
-
}, slotId);
|
|
65629
|
-
})
|
|
65665
|
+
children: declared
|
|
65630
65666
|
});
|
|
65631
65667
|
};
|
|
65632
|
-
|
|
65633
|
-
|
|
65634
|
-
|
|
65635
|
-
|
|
65636
|
-
|
|
65637
|
-
|
|
65638
|
-
|
|
65639
|
-
|
|
65640
|
-
|
|
65641
|
-
|
|
65642
|
-
|
|
65643
|
-
|
|
65644
|
-
};
|
|
65645
|
-
|
|
65646
|
-
// Two slots compared by where they sit (see pathOf). A slot no walk declared
|
|
65647
|
-
// sorts after them all: it stands nowhere the list knows of.
|
|
65648
|
-
const comparePath = (leftPath, rightPath) => {
|
|
65649
|
-
if (leftPath === null) {
|
|
65650
|
-
return rightPath === null ? 0 : 1;
|
|
65651
|
-
}
|
|
65652
|
-
if (rightPath === null) {
|
|
65653
|
-
return -1;
|
|
65654
|
-
}
|
|
65655
|
-
let depth = 0;
|
|
65656
|
-
while (depth < leftPath.length && depth < rightPath.length) {
|
|
65657
|
-
if (leftPath[depth] !== rightPath[depth]) {
|
|
65658
|
-
return leftPath[depth] - rightPath[depth];
|
|
65668
|
+
const declareChildren = (children, prefix, slotIds, declared) => {
|
|
65669
|
+
const childArray = Array.isArray(children) ? children : [children];
|
|
65670
|
+
let index = 0;
|
|
65671
|
+
for (const child of childArray) {
|
|
65672
|
+
if (Array.isArray(child)) {
|
|
65673
|
+
declareChildren(child, `${prefix}${index}/`, slotIds, declared);
|
|
65674
|
+
} else if (child !== null && child !== undefined && child !== false && child !== true) {
|
|
65675
|
+
const slotId = child.key === undefined || child.key === null ? `${prefix}i${index}` : `${prefix}k${child.key}`;
|
|
65676
|
+
slotIds.push(slotId);
|
|
65677
|
+
declared.push(jsx(ListSlotContext.Provider, {
|
|
65678
|
+
value: slotId,
|
|
65679
|
+
children: child
|
|
65680
|
+
}, slotId));
|
|
65659
65681
|
}
|
|
65660
|
-
|
|
65682
|
+
index++;
|
|
65661
65683
|
}
|
|
65662
|
-
return leftPath.length - rightPath.length;
|
|
65663
65684
|
};
|
|
65664
65685
|
const VISIBILITY_HIDDEN_STYLE = {
|
|
65665
65686
|
visibility: "hidden"
|
|
@@ -65795,7 +65816,7 @@ const ListItems = ({
|
|
|
65795
65816
|
}) => {
|
|
65796
65817
|
const ownerId = useId();
|
|
65797
65818
|
const virtual = useContext(ListVirtualContext);
|
|
65798
|
-
const
|
|
65819
|
+
const slotId = useContext(ListSlotContext);
|
|
65799
65820
|
const renderWindow = useContext(RenderWindowContext);
|
|
65800
65821
|
const separator = useContext(SeparatorContext);
|
|
65801
65822
|
const store = useItemStore({
|
|
@@ -65817,7 +65838,7 @@ const ListItems = ({
|
|
|
65817
65838
|
skeletonRow.rowMinHeight = `${virtualItemSize}px`;
|
|
65818
65839
|
}
|
|
65819
65840
|
}
|
|
65820
|
-
const runStart = virtual.take(ownerId, store.rowCount,
|
|
65841
|
+
const runStart = virtual.take(ownerId, store.rowCount, slotId);
|
|
65821
65842
|
const runEnd = runStart + store.rowCount;
|
|
65822
65843
|
const getItemAt = index => store.getItem(index);
|
|
65823
65844
|
const windowFrom = renderWindow.start > runStart ? renderWindow.start : runStart;
|