@jsenv/navi 0.29.426 → 0.29.427
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 +228 -79
- package/dist/dev/jsenv_navi.js.map +9 -9
- package/dist/dev/jsenv_navi_side_effects.js +1 -5
- package/dist/dev/jsenv_navi_side_effects.js.map +2 -2
- package/dist/jsenv_navi.js +225 -76
- package/dist/jsenv_navi.js.map +9 -9
- package/dist/jsenv_navi_side_effects.js +1 -5
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/css_architecture.md +1 -1
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -16238,9 +16238,9 @@ const armOutsidePressClose = (
|
|
|
16238
16238
|
* `font-family` is deliberately absent: a surface keeps the face of what
|
|
16239
16239
|
* opened it, so a section written in a display font gets its tooltips in that
|
|
16240
16240
|
* font. `color`, `font-size` and `font-weight` are absent too — each surface
|
|
16241
|
-
* answers those its own way (a popup
|
|
16242
|
-
* the size of what opened it
|
|
16243
|
-
* size).
|
|
16241
|
+
* answers those its own way (a popup and a callout write in
|
|
16242
|
+
* --navi-popup-color; a popup follows the size of what opened it, a callout
|
|
16243
|
+
* resets to the document's size).
|
|
16244
16244
|
*
|
|
16245
16245
|
* In `@layer navi` so an app that wants one of these back says so on the
|
|
16246
16246
|
* surface and wins, without having to out-specify anything.
|
|
@@ -16470,8 +16470,7 @@ const css$14 = /* css */ `
|
|
|
16470
16470
|
--callout-error-color: var(--navi-callout-error-color);
|
|
16471
16471
|
--callout-neutral-color: var(--navi-callout-neutral-color);
|
|
16472
16472
|
|
|
16473
|
-
--callout-background-color:
|
|
16474
|
-
--callout-icon-color: black;
|
|
16473
|
+
--callout-background-color: var(--navi-popup-background-color);
|
|
16475
16474
|
--callout-padding: 8px;
|
|
16476
16475
|
--callout-z-index: var(--navi-z-index-callout);
|
|
16477
16476
|
/* The page's line, said here rather than inherited: the icon and the
|
|
@@ -16525,11 +16524,11 @@ const css$14 = /* css */ `
|
|
|
16525
16524
|
height: auto; /* User agent reset */
|
|
16526
16525
|
margin: 0;
|
|
16527
16526
|
padding: 0; /* User agent reset */
|
|
16528
|
-
/*
|
|
16529
|
-
|
|
16530
|
-
color
|
|
16531
|
-
navi_css_vars.js
|
|
16532
|
-
color:
|
|
16527
|
+
/* A callout sits inside the element but writes on its own paper: the
|
|
16528
|
+
popup's ink, paired with the popup's paper it is drawn on, so the two
|
|
16529
|
+
follow color-scheme and the theme together. The color keywords are
|
|
16530
|
+
re-declared against that same ink in navi_css_vars.js. */
|
|
16531
|
+
color: var(--navi-popup-color);
|
|
16533
16532
|
font-weight: initial; /* Callout fells disconnected from the element, font weight should be predictible and stable */
|
|
16534
16533
|
font-size: initial; /* Callout fells disconnected from the element, font size should be predictible and stable */
|
|
16535
16534
|
line-height: var(
|
|
@@ -18752,16 +18751,18 @@ const findBusySource = (field) => {
|
|
|
18752
18751
|
// a new interaction is queued behind the run (see the action queue in
|
|
18753
18752
|
// control_hooks.jsx) rather than refused.
|
|
18754
18753
|
if (!field.optimistic && boundAction) {
|
|
18755
|
-
// The
|
|
18756
|
-
//
|
|
18757
|
-
//
|
|
18758
|
-
//
|
|
18759
|
-
//
|
|
18760
|
-
//
|
|
18761
|
-
//
|
|
18762
|
-
//
|
|
18763
|
-
|
|
18764
|
-
|
|
18754
|
+
// The instance the run in flight uses, as the render reads it (see
|
|
18755
|
+
// runningActionSignal in control_hooks.jsx): the state a proxy follows can
|
|
18756
|
+
// move mid-run — a popup content unmounting takes the group's value — and
|
|
18757
|
+
// the proxy would resolve to an idle instance. With no run out, the
|
|
18758
|
+
// instance the proxy resolves to right now, never the proxy's own signal:
|
|
18759
|
+
// a MIRROR synced by an effect the settling batch defers, which still says
|
|
18760
|
+
// RUNNING mid-batch (a state echo, an automatic follow-up) for an action
|
|
18761
|
+
// already over. An instance is COMPLETED the moment it settles.
|
|
18762
|
+
const liveAction =
|
|
18763
|
+
field.runningActionSignal.value ??
|
|
18764
|
+
boundAction.getCurrentAction?.() ??
|
|
18765
|
+
boundAction;
|
|
18765
18766
|
if (liveAction.runningStateSignal.value === RUNNING) {
|
|
18766
18767
|
return { field, action: liveAction };
|
|
18767
18768
|
}
|
|
@@ -19799,11 +19800,10 @@ const READONLY_CONSTRAINT = {
|
|
|
19799
19800
|
name: "readonly",
|
|
19800
19801
|
messageAttribute: "data-readonly-message",
|
|
19801
19802
|
check: (field, { intent } = {}) => {
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
)
|
|
19806
|
-
if (!readOnly) {
|
|
19803
|
+
// Not off the rendered `readOnly`/`aria-readonly`: they also carry the run
|
|
19804
|
+
// of the control's own action, a frame behind (see readOnlyWithoutRun in
|
|
19805
|
+
// control_hooks.jsx). That run is BUSY_CONSTRAINT's, which reads it live.
|
|
19806
|
+
if (!field.readOnlyWithoutRun) {
|
|
19807
19807
|
return null;
|
|
19808
19808
|
}
|
|
19809
19809
|
|
|
@@ -42988,6 +42988,7 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
42988
42988
|
ref: realUIStateController.ref,
|
|
42989
42989
|
uiStateSignal: realUIStateController.uiStateSignal,
|
|
42990
42990
|
controlHostProps: realUIStateController.controlHostProps,
|
|
42991
|
+
readOnlyWithoutRun: realUIStateController.readOnlyWithoutRun,
|
|
42991
42992
|
registerChild: (child) => {
|
|
42992
42993
|
if (!canRegisterAsFacadeChild(child)) {
|
|
42993
42994
|
return;
|
|
@@ -43116,6 +43117,8 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
43116
43117
|
s.controller.ref = realUIStateController.ref;
|
|
43117
43118
|
s.controller.uiStateSignal = realUIStateController.uiStateSignal;
|
|
43118
43119
|
s.controller.controlHostProps = realUIStateController.controlHostProps;
|
|
43120
|
+
s.controller.readOnlyWithoutRun =
|
|
43121
|
+
realUIStateController.readOnlyWithoutRun;
|
|
43119
43122
|
realUIStateController.pushStateDownToFacadeChild =
|
|
43120
43123
|
s.controller.pushStateDownToChild;
|
|
43121
43124
|
|
|
@@ -44922,6 +44925,11 @@ const useInteractiveProps = (props, {
|
|
|
44922
44925
|
const actionLoading = optimistic ? false : actionRunning;
|
|
44923
44926
|
const loadingResolved = loadingBase || actionLoading;
|
|
44924
44927
|
const readOnlyResolved = readOnlyBase || actionLoading;
|
|
44928
|
+
// What READONLY_CONSTRAINT refuses for. The run of the control's own action
|
|
44929
|
+
// is BUSY_CONSTRAINT's, read live: readOnlyResolved is a render snapshot,
|
|
44930
|
+
// still read-only in the tick the run settles — the tick a form's
|
|
44931
|
+
// after-send `--navi-reset` asks the form to empty itself.
|
|
44932
|
+
uiStateController.readOnlyWithoutRun = Boolean(readOnlyBase);
|
|
44925
44933
|
// Read-only, and what this control opens still opens: reading what is in
|
|
44926
44934
|
// there changes nothing. Read by READONLY_CONSTRAINT, which lets an
|
|
44927
44935
|
// interaction that only reads through on it.
|
|
@@ -45374,10 +45382,10 @@ const useInteractiveProps = (props, {
|
|
|
45374
45382
|
});
|
|
45375
45383
|
}
|
|
45376
45384
|
// controlHostProps is a curated subset of props with resolved values applied
|
|
45377
|
-
// (e.g. readOnly resolved from context + action loading). The
|
|
45378
|
-
//
|
|
45379
|
-
//
|
|
45380
|
-
//
|
|
45385
|
+
// (e.g. readOnly resolved from context + action loading). The constraints
|
|
45386
|
+
// read off uiStateController.controlHostProps at runtime (e.g. DISABLED_CONSTRAINT,
|
|
45387
|
+
// the readonly reason), so pointing the controller at controlHostProps keeps
|
|
45388
|
+
// those reads current without any extra bookkeeping.
|
|
45381
45389
|
const firstRender = uiStateController.controlHostProps === undefined;
|
|
45382
45390
|
uiStateController.controlHostProps = controlHostProps;
|
|
45383
45391
|
// The action itself, not just what the last render made of it: its running
|
|
@@ -54421,14 +54429,34 @@ const ButtonConfirm = ({
|
|
|
54421
54429
|
}
|
|
54422
54430
|
return jsxs("span", {
|
|
54423
54431
|
className: "navi_button_confirm",
|
|
54424
|
-
ref: askingRef
|
|
54432
|
+
ref: askingRef
|
|
54433
|
+
// A place for the focus, so a press anywhere in the question keeps it
|
|
54434
|
+
// inside: WebKit does not focus a pressed button, and a press on
|
|
54435
|
+
// "Confirmer" would otherwise blur toward <body> — a focusout with no
|
|
54436
|
+
// relatedTarget, read below as the person leaving. The callout answers
|
|
54437
|
+
// the same ambiguity the same way.
|
|
54438
|
+
,
|
|
54439
|
+
tabIndex: -1
|
|
54440
|
+
// "not me, unless you have nothing else": the ladder in the effect above
|
|
54441
|
+
// walks past the question to what it holds.
|
|
54442
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
54443
|
+
,
|
|
54444
|
+
"navi-autofocus": "last-resort",
|
|
54425
54445
|
onKeyDown: e => {
|
|
54426
54446
|
if (e.key === "Escape") {
|
|
54427
54447
|
cancel(true);
|
|
54428
54448
|
}
|
|
54429
54449
|
},
|
|
54430
54450
|
onFocusOut: e => {
|
|
54431
|
-
|
|
54451
|
+
// Chrome also fires this from the question's own removal, once preact
|
|
54452
|
+
// has cleared the ref: nobody left, and cancel(false) would undo the
|
|
54453
|
+
// focus the answer gives back. That focus cannot go back before the
|
|
54454
|
+
// removal: the button it lands on is mounted by the same commit.
|
|
54455
|
+
const askingEl = askingRef.current;
|
|
54456
|
+
if (!askingEl) {
|
|
54457
|
+
return;
|
|
54458
|
+
}
|
|
54459
|
+
if (!askingEl.contains(e.relatedTarget)) {
|
|
54432
54460
|
cancel(false);
|
|
54433
54461
|
}
|
|
54434
54462
|
},
|
|
@@ -71864,7 +71892,7 @@ const css$x = /* css */`@property --list-group-label-height {
|
|
|
71864
71892
|
}
|
|
71865
71893
|
}
|
|
71866
71894
|
|
|
71867
|
-
.navi_list[navi-box-flow="grid"] > .navi_list_virtual_filler, .navi_list[navi-box-flow="grid"] > .navi_separator {
|
|
71895
|
+
.navi_list[navi-box-flow="grid"] > .navi_list_virtual_filler, .navi_list[navi-box-flow="grid"] > .navi_list_failed_items, .navi_list[navi-box-flow="grid"] > .navi_separator {
|
|
71868
71896
|
grid-column: 1 / -1;
|
|
71869
71897
|
}
|
|
71870
71898
|
|
|
@@ -72241,7 +72269,8 @@ const ListUI = props => {
|
|
|
72241
72269
|
listId: rest.id,
|
|
72242
72270
|
scroller,
|
|
72243
72271
|
searchText,
|
|
72244
|
-
horizontal
|
|
72272
|
+
horizontal,
|
|
72273
|
+
columns
|
|
72245
72274
|
});
|
|
72246
72275
|
|
|
72247
72276
|
// renderBudget frames the items of a run; a list whose items are all declared
|
|
@@ -72556,7 +72585,8 @@ const useListScrollSync = ({
|
|
|
72556
72585
|
listId,
|
|
72557
72586
|
scroller,
|
|
72558
72587
|
searchText,
|
|
72559
|
-
horizontal
|
|
72588
|
+
horizontal,
|
|
72589
|
+
columns
|
|
72560
72590
|
}) => {
|
|
72561
72591
|
const debugScroll = useDebugScroll();
|
|
72562
72592
|
// The items drawn, [start, end) among the list's own. A ref as well as a
|
|
@@ -72571,6 +72601,7 @@ const useListScrollSync = ({
|
|
|
72571
72601
|
return renderWindow.start > 0 || renderWindow.end < listItems.totalSignal.peek();
|
|
72572
72602
|
};
|
|
72573
72603
|
const virtualItemSizeSignal = useVirtualItemSizeSignal(ref, virtualItemSize, horizontal, {
|
|
72604
|
+
inLines: Boolean(columns),
|
|
72574
72605
|
windowLeavesItemsOut,
|
|
72575
72606
|
scrolledWanted: scrolled ?? defaultScrolled,
|
|
72576
72607
|
// The fillers hold the room of items above the screen at this size: a
|
|
@@ -72639,6 +72670,34 @@ const useListScrollSync = ({
|
|
|
72639
72670
|
});
|
|
72640
72671
|
};
|
|
72641
72672
|
|
|
72673
|
+
// How many items share a line: the tracks a grid of items (`columns`)
|
|
72674
|
+
// resolves to, one otherwise. A layout result under auto-fill, so it is read
|
|
72675
|
+
// off the list once laid out, at each commit and each resize — for the runs
|
|
72676
|
+
// alone, which draw in lines: items declared one by one are all drawn. A
|
|
72677
|
+
// line of another length rewraps every item drawn: the item at the top of
|
|
72678
|
+
// the view is held where it is across it.
|
|
72679
|
+
const itemsPerLineRef = useRef(1);
|
|
72680
|
+
const [itemsPerLine, setItemsPerLine] = useState(1);
|
|
72681
|
+
const updateItemsPerLine = () => {
|
|
72682
|
+
if (!ref.current) {
|
|
72683
|
+
return;
|
|
72684
|
+
}
|
|
72685
|
+
const itemsPerLineNow = columns && listItems.hasRuns() ? readItemsPerLine(getListEl()) : 1;
|
|
72686
|
+
if (itemsPerLineNow === null || itemsPerLineNow === itemsPerLineRef.current) {
|
|
72687
|
+
return;
|
|
72688
|
+
}
|
|
72689
|
+
captureAnchor();
|
|
72690
|
+
itemsPerLineRef.current = itemsPerLineNow;
|
|
72691
|
+
setItemsPerLine(itemsPerLineNow);
|
|
72692
|
+
};
|
|
72693
|
+
const updateItemsPerLineRef = useRef(null);
|
|
72694
|
+
updateItemsPerLineRef.current = updateItemsPerLine;
|
|
72695
|
+
useLayoutEffect(() => {
|
|
72696
|
+
if (columns || itemsPerLineRef.current !== 1) {
|
|
72697
|
+
updateItemsPerLine();
|
|
72698
|
+
}
|
|
72699
|
+
});
|
|
72700
|
+
|
|
72642
72701
|
// What the window holds goes through three stages (see settleWindow):
|
|
72643
72702
|
// - "first": the first commit, a count of items since nothing is laid out
|
|
72644
72703
|
// yet to measure a size with — `initial` or the budget when they are one,
|
|
@@ -73037,6 +73096,7 @@ const useListScrollSync = ({
|
|
|
73037
73096
|
listEl: getListEl(),
|
|
73038
73097
|
listItems,
|
|
73039
73098
|
virtualItemSizeSignal,
|
|
73099
|
+
itemsPerLine: itemsPerLineRef.current,
|
|
73040
73100
|
renderWindowRef,
|
|
73041
73101
|
horizontal
|
|
73042
73102
|
});
|
|
@@ -73377,20 +73437,14 @@ const useListScrollSync = ({
|
|
|
73377
73437
|
}
|
|
73378
73438
|
const scrollerEl = getScroller();
|
|
73379
73439
|
const listEl = getListEl();
|
|
73380
|
-
|
|
73381
|
-
|
|
73382
|
-
|
|
73383
|
-
|
|
73384
|
-
|
|
73385
|
-
|
|
73386
|
-
|
|
73387
|
-
|
|
73388
|
-
// growing is its own content settling: only a list holding itself
|
|
73389
|
-
// somewhere cares (the end it aims at has moved), and a list the user is
|
|
73390
|
-
// reading must not be touched — its items are held still by the anchoring,
|
|
73391
|
-
// which this would undo. The SCROLLER resizing is the window around it
|
|
73392
|
-
// changing shape, and then the item that was at the top goes back where it
|
|
73393
|
-
// was.
|
|
73440
|
+
// Two things resize here, and they call for opposite answers. The LIST
|
|
73441
|
+
// growing is its own content settling: only a list holding itself
|
|
73442
|
+
// somewhere cares (the end it aims at has moved), and a list the user is
|
|
73443
|
+
// reading must not be touched — its items are held still by the anchoring,
|
|
73444
|
+
// which this would undo. The SCROLLER resizing is the window around it
|
|
73445
|
+
// changing shape, and then the item that was at the top goes back where it
|
|
73446
|
+
// was.
|
|
73447
|
+
const putTopItemBack = entries => {
|
|
73394
73448
|
const listResized = entries.some(entry => entry.target === listEl);
|
|
73395
73449
|
if (listResized && onGeometryChangeRef.current()) {
|
|
73396
73450
|
return;
|
|
@@ -73419,6 +73473,19 @@ const useListScrollSync = ({
|
|
|
73419
73473
|
} else {
|
|
73420
73474
|
scrollerEl.scrollTop += delta;
|
|
73421
73475
|
}
|
|
73476
|
+
};
|
|
73477
|
+
const observer = new ResizeObserver(entries => {
|
|
73478
|
+
// A box that grows may be a box that starts to scroll — the scroller is
|
|
73479
|
+
// resolved again before anything is done about the resize, so that a
|
|
73480
|
+
// list which has outgrown a bounded ancestor stops holding on to the
|
|
73481
|
+
// page.
|
|
73482
|
+
resolveScroller();
|
|
73483
|
+
rememberPosition();
|
|
73484
|
+
putTopItemBack(entries);
|
|
73485
|
+
// Once the top item is back where it was: that is the place a line of
|
|
73486
|
+
// another length has to keep, and putting it back drops any anchor
|
|
73487
|
+
// captured before.
|
|
73488
|
+
updateItemsPerLineRef.current();
|
|
73422
73489
|
});
|
|
73423
73490
|
observer.observe(scrollerEl);
|
|
73424
73491
|
observer.observe(listEl);
|
|
@@ -73574,7 +73641,10 @@ const useListScrollSync = ({
|
|
|
73574
73641
|
start,
|
|
73575
73642
|
end
|
|
73576
73643
|
} = renderWindowRef.current;
|
|
73577
|
-
|
|
73644
|
+
// What an item not drawn weighs: its share of a line, which the items side
|
|
73645
|
+
// by side on it take together.
|
|
73646
|
+
const itemsPerLineNow = itemsPerLineRef.current;
|
|
73647
|
+
const itemSize = virtualItemSizeSignal.peek() / itemsPerLineNow;
|
|
73578
73648
|
const pixelsOf = createItemSizeReader(items, start, end, itemSize);
|
|
73579
73649
|
const walkBefore = (from, sizeWanted, sizeOf) => {
|
|
73580
73650
|
let index = from;
|
|
@@ -73844,9 +73914,28 @@ const useListScrollSync = ({
|
|
|
73844
73914
|
ifChanged: true
|
|
73845
73915
|
});
|
|
73846
73916
|
}, [windowStart, windowEnd]);
|
|
73917
|
+
// What the runs draw: the window widened to whole lines. The window itself
|
|
73918
|
+
// moves an item at a time, and a grid lays the first item drawn in its first
|
|
73919
|
+
// column — started anywhere but on a line, every item drawn would sit in the
|
|
73920
|
+
// column of another. Started on one, an item is in the column its place says
|
|
73921
|
+
// whatever the window frames. The same object while the numbers hold, so the
|
|
73922
|
+
// runs are not told about a window that did not move.
|
|
73923
|
+
const windowDrawnRef = useRef(null);
|
|
73924
|
+
const drawnStart = windowStart - windowStart % itemsPerLine;
|
|
73925
|
+
const endPastLine = windowEnd % itemsPerLine;
|
|
73926
|
+
const drawnEnd = endPastLine === 0 ? windowEnd : windowEnd + itemsPerLine - endPastLine;
|
|
73927
|
+
let windowDrawn = windowDrawnRef.current;
|
|
73928
|
+
if (!windowDrawn || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
73929
|
+
windowDrawn = {
|
|
73930
|
+
start: drawnStart,
|
|
73931
|
+
end: drawnEnd,
|
|
73932
|
+
itemsPerLine
|
|
73933
|
+
};
|
|
73934
|
+
windowDrawnRef.current = windowDrawn;
|
|
73935
|
+
}
|
|
73847
73936
|
return {
|
|
73848
73937
|
virtualItemSizeSignal,
|
|
73849
|
-
renderWindow:
|
|
73938
|
+
renderWindow: windowDrawn,
|
|
73850
73939
|
pendingScrollRef,
|
|
73851
73940
|
scrollToItem,
|
|
73852
73941
|
captureAnchor
|
|
@@ -73870,6 +73959,21 @@ const getScrollerViewportRect = scrollerEl => {
|
|
|
73870
73959
|
}
|
|
73871
73960
|
return scrollerEl.getBoundingClientRect();
|
|
73872
73961
|
};
|
|
73962
|
+
// The tracks a grid resolved its template to, as its computed
|
|
73963
|
+
// grid-template-columns lists them once laid out: "150px 150px [end] 150px".
|
|
73964
|
+
// Not laid out (a closed popup), that is the template as written, which says
|
|
73965
|
+
// nothing yet under auto-fill: `null`.
|
|
73966
|
+
const readItemsPerLine = listEl => {
|
|
73967
|
+
const tracks = getComputedStyle(listEl).gridTemplateColumns;
|
|
73968
|
+
if (tracks === "none" || tracks.includes("(")) {
|
|
73969
|
+
return null;
|
|
73970
|
+
}
|
|
73971
|
+
const sizes = tracks.replace(/\[[^\]]*\]/g, " ").trim();
|
|
73972
|
+
if (!sizes) {
|
|
73973
|
+
return null;
|
|
73974
|
+
}
|
|
73975
|
+
return sizes.split(/\s+/).length;
|
|
73976
|
+
};
|
|
73873
73977
|
// A window that cannot move is the one failure of a virtualized list that
|
|
73874
73978
|
// looks like nothing: the items outside it are fillers holding their room, so
|
|
73875
73979
|
// the list simply ends on blank space, at exactly the height of the items never
|
|
@@ -74672,6 +74776,7 @@ const getScrollInfo = ({
|
|
|
74672
74776
|
listEl,
|
|
74673
74777
|
listItems,
|
|
74674
74778
|
virtualItemSizeSignal,
|
|
74779
|
+
itemsPerLine,
|
|
74675
74780
|
renderWindowRef,
|
|
74676
74781
|
horizontal
|
|
74677
74782
|
}) => {
|
|
@@ -74709,7 +74814,9 @@ const getScrollInfo = ({
|
|
|
74709
74814
|
// mean we don't know the real on-screen index, only the scroll position,
|
|
74710
74815
|
// so estimate from it rather than assume nothing changed.
|
|
74711
74816
|
const estimateFromScrollPos = reasonPrefix => {
|
|
74712
|
-
|
|
74817
|
+
// An item's share of its line: the items side by side on it take it
|
|
74818
|
+
// together.
|
|
74819
|
+
const virtualItemSize = virtualItemSizeSignal.peek() / itemsPerLine;
|
|
74713
74820
|
if (virtualItemSize === 0) {
|
|
74714
74821
|
return null;
|
|
74715
74822
|
}
|
|
@@ -74765,8 +74872,10 @@ const getScrollInfo = ({
|
|
|
74765
74872
|
const VIRTUAL_ITEM_SIZE_EPSILON = 0.5;
|
|
74766
74873
|
// Measures the items currently in the DOM, edge to edge: what a filler stands in
|
|
74767
74874
|
// for is the room a run of items takes together — separators and group labels
|
|
74768
|
-
// included — not the height of one <li>.
|
|
74769
|
-
|
|
74875
|
+
// included — not the height of one <li>. The size of a line of them: one item
|
|
74876
|
+
// in a list, the items side by side in a grid (`inLines`), counted where a new
|
|
74877
|
+
// line starts.
|
|
74878
|
+
const measureItemSize = (listEl, horizontal, inLines) => {
|
|
74770
74879
|
let fromSkeletons = false;
|
|
74771
74880
|
let itemEls = listEl.querySelectorAll(REAL_LIST_ITEM_SELECTOR);
|
|
74772
74881
|
if (itemEls.length === 0) {
|
|
@@ -74788,13 +74897,27 @@ const measureItemSize = (listEl, horizontal) => {
|
|
|
74788
74897
|
if (span <= 0) {
|
|
74789
74898
|
return null;
|
|
74790
74899
|
}
|
|
74900
|
+
let lineCount = itemEls.length;
|
|
74901
|
+
if (inLines) {
|
|
74902
|
+
lineCount = 0;
|
|
74903
|
+
let lineFrom = -Infinity;
|
|
74904
|
+
for (const itemEl of itemEls) {
|
|
74905
|
+
const rect = itemEl.getBoundingClientRect();
|
|
74906
|
+
const from = horizontal ? rect.left : rect.top;
|
|
74907
|
+
if (from > lineFrom + 0.5) {
|
|
74908
|
+
lineCount++;
|
|
74909
|
+
lineFrom = from;
|
|
74910
|
+
}
|
|
74911
|
+
}
|
|
74912
|
+
}
|
|
74791
74913
|
return {
|
|
74792
|
-
size: span /
|
|
74793
|
-
|
|
74914
|
+
size: span / lineCount,
|
|
74915
|
+
lineCount,
|
|
74794
74916
|
fromSkeletons
|
|
74795
74917
|
};
|
|
74796
74918
|
};
|
|
74797
74919
|
const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
74920
|
+
inLines,
|
|
74798
74921
|
windowLeavesItemsOut,
|
|
74799
74922
|
scrolledWanted,
|
|
74800
74923
|
beforeSizeChange
|
|
@@ -74838,8 +74961,8 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
|
74838
74961
|
return;
|
|
74839
74962
|
}
|
|
74840
74963
|
}
|
|
74841
|
-
samples.sum += measure.size * measure.
|
|
74842
|
-
samples.count += measure.
|
|
74964
|
+
samples.sum += measure.size * measure.lineCount;
|
|
74965
|
+
samples.count += measure.lineCount;
|
|
74843
74966
|
const next = samples.sum / samples.count;
|
|
74844
74967
|
const current = virtualSizeSignal.peek();
|
|
74845
74968
|
if (Math.abs(next - current) > VIRTUAL_ITEM_SIZE_EPSILON) {
|
|
@@ -74859,7 +74982,7 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
|
74859
74982
|
const itemsHeldOffScreen = windowLeavesItemsOut();
|
|
74860
74983
|
if (!virtualItemSizeProp && sizeAlreadyKnown && itemsHeldOffScreen && ref.current) {
|
|
74861
74984
|
const listEl = ref.current.querySelector(".navi_list");
|
|
74862
|
-
const measure = listEl ? measureItemSize(listEl, horizontal) : null;
|
|
74985
|
+
const measure = listEl ? measureItemSize(listEl, horizontal, inLines) : null;
|
|
74863
74986
|
if (measure) {
|
|
74864
74987
|
feedSample(measure);
|
|
74865
74988
|
}
|
|
@@ -74880,11 +75003,11 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
|
74880
75003
|
if (!listEl) {
|
|
74881
75004
|
return undefined;
|
|
74882
75005
|
}
|
|
74883
|
-
const measure = measureItemSize(listEl, horizontal);
|
|
75006
|
+
const measure = measureItemSize(listEl, horizontal, inLines);
|
|
74884
75007
|
if (measure) {
|
|
74885
75008
|
const samples = samplesRef.current;
|
|
74886
|
-
samples.sum = measure.size * measure.
|
|
74887
|
-
samples.count = measure.
|
|
75009
|
+
samples.sum = measure.size * measure.lineCount;
|
|
75010
|
+
samples.count = measure.lineCount;
|
|
74888
75011
|
samples.fromSkeletons = measure.fromSkeletons;
|
|
74889
75012
|
virtualSizeSignal.value = measure.size;
|
|
74890
75013
|
return undefined;
|
|
@@ -75029,14 +75152,17 @@ const Fallback = ({
|
|
|
75029
75152
|
// Reads the item size itself: it is what the size is for, and a run holding
|
|
75030
75153
|
// every item it draws must not be redrawn — every item of it — because the size
|
|
75031
75154
|
// settled after the first commit.
|
|
75155
|
+
// It holds lines: the items side by side on one take its room together, and a
|
|
75156
|
+
// last line left short takes it all the same.
|
|
75032
75157
|
const VirtualFiller = ({
|
|
75033
75158
|
edge,
|
|
75034
75159
|
itemCount,
|
|
75160
|
+
itemsPerLine,
|
|
75035
75161
|
findChunks
|
|
75036
75162
|
}) => {
|
|
75037
75163
|
const listItems = useContext(ListItemsContext);
|
|
75038
|
-
const
|
|
75039
|
-
const sizeToFill = itemCount *
|
|
75164
|
+
const lineSize = listItems.virtualItemSizeSignal.value;
|
|
75165
|
+
const sizeToFill = Math.ceil(itemCount / itemsPerLine) * lineSize;
|
|
75040
75166
|
// A filler resizing moves what stands below it — the items on screen, when it
|
|
75041
75167
|
// holds the room of items above them — and it resizes in a commit of its own
|
|
75042
75168
|
// when the item size settles after the list has rendered: the list puts its
|
|
@@ -75055,7 +75181,7 @@ const VirtualFiller = ({
|
|
|
75055
75181
|
"aria-hidden": true,
|
|
75056
75182
|
style: {
|
|
75057
75183
|
"--size-to-fill": `${sizeToFill}px`,
|
|
75058
|
-
"--x-find-line-size": findChunks ? `${
|
|
75184
|
+
"--x-find-line-size": findChunks ? `${lineSize / itemsPerLine}px` : undefined
|
|
75059
75185
|
},
|
|
75060
75186
|
children: findChunks && findChunks.map(chunk => jsx("div", {
|
|
75061
75187
|
className: "navi_list_find_stand_in",
|
|
@@ -75935,6 +76061,9 @@ const useRunItems = (store, {
|
|
|
75935
76061
|
const rankOf = itemIndex => itemIndex - runStart;
|
|
75936
76062
|
const indexOfRank = rank => rank + runStart;
|
|
75937
76063
|
const getItemAt = itemIndex => store.getItem(rankOf(itemIndex));
|
|
76064
|
+
const {
|
|
76065
|
+
itemsPerLine
|
|
76066
|
+
} = renderWindow;
|
|
75938
76067
|
const windowFrom = renderWindow.start > runStart ? renderWindow.start : runStart;
|
|
75939
76068
|
const windowTo = renderWindow.end < runEnd ? renderWindow.end : runEnd;
|
|
75940
76069
|
store.forget(rankOf(windowFrom), rankOf(windowTo));
|
|
@@ -76042,8 +76171,20 @@ const useRunItems = (store, {
|
|
|
76042
76171
|
// band is drawn among the items. A negative start is not a rank but a count
|
|
76043
76172
|
// back from the end (the very first ask, before the count is known) — there
|
|
76044
76173
|
// is no item to convert it to, and the band falls back to the window.
|
|
76045
|
-
|
|
76046
|
-
|
|
76174
|
+
let failureFrom = store.failure === null ? -1 : store.failure.start < 0 || indexOfRank(store.failure.start) < windowFrom ? windowFrom : indexOfRank(store.failure.start);
|
|
76175
|
+
let failureTo = store.failure === null ? -1 : store.failure.end < 0 || indexOfRank(store.failure.end) > windowTo - 1 ? windowTo - 1 : indexOfRank(store.failure.end);
|
|
76176
|
+
if (store.failure !== null && itemsPerLine > 1) {
|
|
76177
|
+
// In a grid the band takes whole lines, like the window: begun mid-line,
|
|
76178
|
+
// it would push every item after it into another column.
|
|
76179
|
+
failureFrom -= failureFrom % itemsPerLine;
|
|
76180
|
+
if (failureFrom < windowFrom) {
|
|
76181
|
+
failureFrom = windowFrom;
|
|
76182
|
+
}
|
|
76183
|
+
failureTo += itemsPerLine - 1 - failureTo % itemsPerLine;
|
|
76184
|
+
if (failureTo > windowTo - 1) {
|
|
76185
|
+
failureTo = windowTo - 1;
|
|
76186
|
+
}
|
|
76187
|
+
}
|
|
76047
76188
|
const nodes = [];
|
|
76048
76189
|
// Items that belong together, as the data says (a day of messages, a month of
|
|
76049
76190
|
// games): consecutive items sharing a group key are wrapped in one group, so
|
|
@@ -76120,6 +76261,7 @@ const useRunItems = (store, {
|
|
|
76120
76261
|
nodes.push(jsx(VirtualFiller, {
|
|
76121
76262
|
edge: "before",
|
|
76122
76263
|
itemCount: windowFrom - runStart,
|
|
76264
|
+
itemsPerLine: itemsPerLine,
|
|
76123
76265
|
findChunks: getFindChunks(runStart, windowFrom)
|
|
76124
76266
|
}, "navi-list-filler-before"));
|
|
76125
76267
|
}
|
|
@@ -76134,7 +76276,7 @@ const useRunItems = (store, {
|
|
|
76134
76276
|
nodes.push(jsx("li", {
|
|
76135
76277
|
className: "navi_list_failed_items",
|
|
76136
76278
|
style: {
|
|
76137
|
-
"--size-to-fill": `${failedItemCount * listItems.virtualItemSizeSignal.value}px`
|
|
76279
|
+
"--size-to-fill": `${Math.ceil(failedItemCount / itemsPerLine) * listItems.virtualItemSizeSignal.value}px`
|
|
76138
76280
|
},
|
|
76139
76281
|
children: renderError ? renderError({
|
|
76140
76282
|
error: store.failure.error,
|
|
@@ -76224,6 +76366,7 @@ const useRunItems = (store, {
|
|
|
76224
76366
|
nodes.push(jsx(VirtualFiller, {
|
|
76225
76367
|
edge: "after",
|
|
76226
76368
|
itemCount: runEnd - windowTo,
|
|
76369
|
+
itemsPerLine: itemsPerLine,
|
|
76227
76370
|
findChunks: getFindChunks(windowTo, runEnd)
|
|
76228
76371
|
}, "navi-list-filler-after"));
|
|
76229
76372
|
}
|
|
@@ -76947,12 +77090,16 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
76947
77090
|
* @param {string} [props.columns]
|
|
76948
77091
|
* The list's own columns: a `grid-template-columns` value the ITEMS are laid
|
|
76949
77092
|
* into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
|
|
76950
|
-
* choices (`repeat(3, minmax(0, 1fr))`)
|
|
76951
|
-
*
|
|
76952
|
-
*
|
|
76953
|
-
*
|
|
76954
|
-
*
|
|
76955
|
-
*
|
|
77093
|
+
* choices (`repeat(3, minmax(0, 1fr))`), a feed of covers
|
|
77094
|
+
* (`repeat(auto-fill, minmax(150px, 1fr))` over a `<List.Items>`). Each item
|
|
77095
|
+
* takes one cell, and an item meant to take a whole line says so for itself
|
|
77096
|
+
* (`style={{ gridColumn: "1 / -1" }}`). A `<List.Items>` run is drawn in whole
|
|
77097
|
+
* lines — as many items to a line as the template resolves to, read again
|
|
77098
|
+
* when the list's width changes: its window starts and ends on a line, so an
|
|
77099
|
+
* item is always in the column its place says, and the fillers hold the room
|
|
77100
|
+
* of the lines it does not draw (`virtualItemSize` is then a line's size).
|
|
77101
|
+
* That count holds only while every item of the run takes one cell: an item
|
|
77102
|
+
* spanning a line, or a `groupBy`, puts the ones after it in other columns.
|
|
76956
77103
|
* @param {string} [props.itemColumns]
|
|
76957
77104
|
* The columns inside an ITEM: a `grid-template-columns` value each item fills
|
|
76958
77105
|
* with its own children, a table whose cells line up down the list. Every item
|
|
@@ -76968,8 +77115,9 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
76968
77115
|
* scroll box, which fills it and has nothing to arrange.
|
|
76969
77116
|
* @param {boolean} [props.flexWrap]
|
|
76970
77117
|
* Lets a horizontal list's items fall to the next line instead of running
|
|
76971
|
-
* past the edge — a row of choices under a `maxWidth`, say.
|
|
76972
|
-
*
|
|
77118
|
+
* past the edge — a row of choices under a `maxWidth`, say. A wrapped line
|
|
77119
|
+
* holding several items is not virtualizable: a long collection laid several
|
|
77120
|
+
* to a line goes in `columns`.
|
|
76973
77121
|
* @param {string} [props.overflow]
|
|
76974
77122
|
* `"visible"` lets the items paint outside the list — a check in an item's
|
|
76975
77123
|
* corner, a badge crossing the edge. A list clips by default, which is what
|
|
@@ -77088,7 +77236,8 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
77088
77236
|
* one-line size) — an item guessed too small builds one more, an item
|
|
77089
77237
|
* guessed too big leaves a blank (see docs/scroll.md, "What the list knows,
|
|
77090
77238
|
* and what it guesses"). The items drawn are measured either way: the render
|
|
77091
|
-
* window is sized on them.
|
|
77239
|
+
* window is sized on them. In a grid (`columns`), the size is a line's: a
|
|
77240
|
+
* card's, which the items side by side on it share.
|
|
77092
77241
|
* @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
|
|
77093
77242
|
* Which box scrolls — and with it, which box the render window follows and
|
|
77094
77243
|
* which box a scroll position is read from (`onScrolledChange`). `"self"`
|