@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/dev/jsenv_navi.js
CHANGED
|
@@ -16881,9 +16881,9 @@ const armOutsidePressClose = (
|
|
|
16881
16881
|
* `font-family` is deliberately absent: a surface keeps the face of what
|
|
16882
16882
|
* opened it, so a section written in a display font gets its tooltips in that
|
|
16883
16883
|
* font. `color`, `font-size` and `font-weight` are absent too — each surface
|
|
16884
|
-
* answers those its own way (a popup
|
|
16885
|
-
* the size of what opened it
|
|
16886
|
-
* size).
|
|
16884
|
+
* answers those its own way (a popup and a callout write in
|
|
16885
|
+
* --navi-popup-color; a popup follows the size of what opened it, a callout
|
|
16886
|
+
* resets to the document's size).
|
|
16887
16887
|
*
|
|
16888
16888
|
* In `@layer navi` so an app that wants one of these back says so on the
|
|
16889
16889
|
* surface and wins, without having to out-specify anything.
|
|
@@ -17113,8 +17113,7 @@ const css$14 = /* css */ `
|
|
|
17113
17113
|
--callout-error-color: var(--navi-callout-error-color);
|
|
17114
17114
|
--callout-neutral-color: var(--navi-callout-neutral-color);
|
|
17115
17115
|
|
|
17116
|
-
--callout-background-color:
|
|
17117
|
-
--callout-icon-color: black;
|
|
17116
|
+
--callout-background-color: var(--navi-popup-background-color);
|
|
17118
17117
|
--callout-padding: 8px;
|
|
17119
17118
|
--callout-z-index: var(--navi-z-index-callout);
|
|
17120
17119
|
/* The page's line, said here rather than inherited: the icon and the
|
|
@@ -17168,11 +17167,11 @@ const css$14 = /* css */ `
|
|
|
17168
17167
|
height: auto; /* User agent reset */
|
|
17169
17168
|
margin: 0;
|
|
17170
17169
|
padding: 0; /* User agent reset */
|
|
17171
|
-
/*
|
|
17172
|
-
|
|
17173
|
-
color
|
|
17174
|
-
navi_css_vars.js
|
|
17175
|
-
color:
|
|
17170
|
+
/* A callout sits inside the element but writes on its own paper: the
|
|
17171
|
+
popup's ink, paired with the popup's paper it is drawn on, so the two
|
|
17172
|
+
follow color-scheme and the theme together. The color keywords are
|
|
17173
|
+
re-declared against that same ink in navi_css_vars.js. */
|
|
17174
|
+
color: var(--navi-popup-color);
|
|
17176
17175
|
font-weight: initial; /* Callout fells disconnected from the element, font weight should be predictible and stable */
|
|
17177
17176
|
font-size: initial; /* Callout fells disconnected from the element, font size should be predictible and stable */
|
|
17178
17177
|
line-height: var(
|
|
@@ -19484,16 +19483,18 @@ const findBusySource = (field) => {
|
|
|
19484
19483
|
// a new interaction is queued behind the run (see the action queue in
|
|
19485
19484
|
// control_hooks.jsx) rather than refused.
|
|
19486
19485
|
if (!field.optimistic && boundAction) {
|
|
19487
|
-
// The
|
|
19488
|
-
//
|
|
19489
|
-
//
|
|
19490
|
-
//
|
|
19491
|
-
//
|
|
19492
|
-
//
|
|
19493
|
-
//
|
|
19494
|
-
//
|
|
19495
|
-
|
|
19496
|
-
|
|
19486
|
+
// The instance the run in flight uses, as the render reads it (see
|
|
19487
|
+
// runningActionSignal in control_hooks.jsx): the state a proxy follows can
|
|
19488
|
+
// move mid-run — a popup content unmounting takes the group's value — and
|
|
19489
|
+
// the proxy would resolve to an idle instance. With no run out, the
|
|
19490
|
+
// instance the proxy resolves to right now, never the proxy's own signal:
|
|
19491
|
+
// a MIRROR synced by an effect the settling batch defers, which still says
|
|
19492
|
+
// RUNNING mid-batch (a state echo, an automatic follow-up) for an action
|
|
19493
|
+
// already over. An instance is COMPLETED the moment it settles.
|
|
19494
|
+
const liveAction =
|
|
19495
|
+
field.runningActionSignal.value ??
|
|
19496
|
+
boundAction.getCurrentAction?.() ??
|
|
19497
|
+
boundAction;
|
|
19497
19498
|
if (liveAction.runningStateSignal.value === RUNNING) {
|
|
19498
19499
|
return { field, action: liveAction };
|
|
19499
19500
|
}
|
|
@@ -20531,11 +20532,10 @@ const READONLY_CONSTRAINT = {
|
|
|
20531
20532
|
name: "readonly",
|
|
20532
20533
|
messageAttribute: "data-readonly-message",
|
|
20533
20534
|
check: (field, { intent } = {}) => {
|
|
20534
|
-
|
|
20535
|
-
|
|
20536
|
-
|
|
20537
|
-
)
|
|
20538
|
-
if (!readOnly) {
|
|
20535
|
+
// Not off the rendered `readOnly`/`aria-readonly`: they also carry the run
|
|
20536
|
+
// of the control's own action, a frame behind (see readOnlyWithoutRun in
|
|
20537
|
+
// control_hooks.jsx). That run is BUSY_CONSTRAINT's, which reads it live.
|
|
20538
|
+
if (!field.readOnlyWithoutRun) {
|
|
20539
20539
|
return null;
|
|
20540
20540
|
}
|
|
20541
20541
|
|
|
@@ -44065,6 +44065,7 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
44065
44065
|
ref: realUIStateController.ref,
|
|
44066
44066
|
uiStateSignal: realUIStateController.uiStateSignal,
|
|
44067
44067
|
controlHostProps: realUIStateController.controlHostProps,
|
|
44068
|
+
readOnlyWithoutRun: realUIStateController.readOnlyWithoutRun,
|
|
44068
44069
|
registerChild: (child) => {
|
|
44069
44070
|
if (!canRegisterAsFacadeChild(child)) {
|
|
44070
44071
|
return;
|
|
@@ -44194,6 +44195,8 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
44194
44195
|
s.controller.ref = realUIStateController.ref;
|
|
44195
44196
|
s.controller.uiStateSignal = realUIStateController.uiStateSignal;
|
|
44196
44197
|
s.controller.controlHostProps = realUIStateController.controlHostProps;
|
|
44198
|
+
s.controller.readOnlyWithoutRun =
|
|
44199
|
+
realUIStateController.readOnlyWithoutRun;
|
|
44197
44200
|
realUIStateController.pushStateDownToFacadeChild =
|
|
44198
44201
|
s.controller.pushStateDownToChild;
|
|
44199
44202
|
|
|
@@ -46102,6 +46105,11 @@ const useInteractiveProps = (props, {
|
|
|
46102
46105
|
const actionLoading = optimistic ? false : actionRunning;
|
|
46103
46106
|
const loadingResolved = loadingBase || actionLoading;
|
|
46104
46107
|
const readOnlyResolved = readOnlyBase || actionLoading;
|
|
46108
|
+
// What READONLY_CONSTRAINT refuses for. The run of the control's own action
|
|
46109
|
+
// is BUSY_CONSTRAINT's, read live: readOnlyResolved is a render snapshot,
|
|
46110
|
+
// still read-only in the tick the run settles — the tick a form's
|
|
46111
|
+
// after-send `--navi-reset` asks the form to empty itself.
|
|
46112
|
+
uiStateController.readOnlyWithoutRun = Boolean(readOnlyBase);
|
|
46105
46113
|
// Read-only, and what this control opens still opens: reading what is in
|
|
46106
46114
|
// there changes nothing. Read by READONLY_CONSTRAINT, which lets an
|
|
46107
46115
|
// interaction that only reads through on it.
|
|
@@ -46556,10 +46564,10 @@ const useInteractiveProps = (props, {
|
|
|
46556
46564
|
});
|
|
46557
46565
|
}
|
|
46558
46566
|
// controlHostProps is a curated subset of props with resolved values applied
|
|
46559
|
-
// (e.g. readOnly resolved from context + action loading). The
|
|
46560
|
-
//
|
|
46561
|
-
//
|
|
46562
|
-
//
|
|
46567
|
+
// (e.g. readOnly resolved from context + action loading). The constraints
|
|
46568
|
+
// read off uiStateController.controlHostProps at runtime (e.g. DISABLED_CONSTRAINT,
|
|
46569
|
+
// the readonly reason), so pointing the controller at controlHostProps keeps
|
|
46570
|
+
// those reads current without any extra bookkeeping.
|
|
46563
46571
|
const firstRender = uiStateController.controlHostProps === undefined;
|
|
46564
46572
|
uiStateController.controlHostProps = controlHostProps;
|
|
46565
46573
|
// The action itself, not just what the last render made of it: its running
|
|
@@ -55616,14 +55624,34 @@ const ButtonConfirm = ({
|
|
|
55616
55624
|
}
|
|
55617
55625
|
return jsxs("span", {
|
|
55618
55626
|
className: "navi_button_confirm",
|
|
55619
|
-
ref: askingRef
|
|
55627
|
+
ref: askingRef
|
|
55628
|
+
// A place for the focus, so a press anywhere in the question keeps it
|
|
55629
|
+
// inside: WebKit does not focus a pressed button, and a press on
|
|
55630
|
+
// "Confirmer" would otherwise blur toward <body> — a focusout with no
|
|
55631
|
+
// relatedTarget, read below as the person leaving. The callout answers
|
|
55632
|
+
// the same ambiguity the same way.
|
|
55633
|
+
,
|
|
55634
|
+
tabIndex: -1
|
|
55635
|
+
// "not me, unless you have nothing else": the ladder in the effect above
|
|
55636
|
+
// walks past the question to what it holds.
|
|
55637
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
55638
|
+
,
|
|
55639
|
+
"navi-autofocus": "last-resort",
|
|
55620
55640
|
onKeyDown: e => {
|
|
55621
55641
|
if (e.key === "Escape") {
|
|
55622
55642
|
cancel(true);
|
|
55623
55643
|
}
|
|
55624
55644
|
},
|
|
55625
55645
|
onFocusOut: e => {
|
|
55626
|
-
|
|
55646
|
+
// Chrome also fires this from the question's own removal, once preact
|
|
55647
|
+
// has cleared the ref: nobody left, and cancel(false) would undo the
|
|
55648
|
+
// focus the answer gives back. That focus cannot go back before the
|
|
55649
|
+
// removal: the button it lands on is mounted by the same commit.
|
|
55650
|
+
const askingEl = askingRef.current;
|
|
55651
|
+
if (!askingEl) {
|
|
55652
|
+
return;
|
|
55653
|
+
}
|
|
55654
|
+
if (!askingEl.contains(e.relatedTarget)) {
|
|
55627
55655
|
cancel(false);
|
|
55628
55656
|
}
|
|
55629
55657
|
},
|
|
@@ -73247,7 +73275,7 @@ const css$x = /* css */`@property --list-group-label-height {
|
|
|
73247
73275
|
}
|
|
73248
73276
|
}
|
|
73249
73277
|
|
|
73250
|
-
.navi_list[navi-box-flow="grid"] > .navi_list_virtual_filler, .navi_list[navi-box-flow="grid"] > .navi_separator {
|
|
73278
|
+
.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 {
|
|
73251
73279
|
grid-column: 1 / -1;
|
|
73252
73280
|
}
|
|
73253
73281
|
|
|
@@ -73624,7 +73652,8 @@ const ListUI = props => {
|
|
|
73624
73652
|
listId: rest.id,
|
|
73625
73653
|
scroller,
|
|
73626
73654
|
searchText,
|
|
73627
|
-
horizontal
|
|
73655
|
+
horizontal,
|
|
73656
|
+
columns
|
|
73628
73657
|
});
|
|
73629
73658
|
|
|
73630
73659
|
// renderBudget frames the items of a run; a list whose items are all declared
|
|
@@ -73939,7 +73968,8 @@ const useListScrollSync = ({
|
|
|
73939
73968
|
listId,
|
|
73940
73969
|
scroller,
|
|
73941
73970
|
searchText,
|
|
73942
|
-
horizontal
|
|
73971
|
+
horizontal,
|
|
73972
|
+
columns
|
|
73943
73973
|
}) => {
|
|
73944
73974
|
const debugScroll = useDebugScroll();
|
|
73945
73975
|
// The items drawn, [start, end) among the list's own. A ref as well as a
|
|
@@ -73954,6 +73984,7 @@ const useListScrollSync = ({
|
|
|
73954
73984
|
return renderWindow.start > 0 || renderWindow.end < listItems.totalSignal.peek();
|
|
73955
73985
|
};
|
|
73956
73986
|
const virtualItemSizeSignal = useVirtualItemSizeSignal(ref, virtualItemSize, horizontal, {
|
|
73987
|
+
inLines: Boolean(columns),
|
|
73957
73988
|
windowLeavesItemsOut,
|
|
73958
73989
|
scrolledWanted: scrolled ?? defaultScrolled,
|
|
73959
73990
|
// The fillers hold the room of items above the screen at this size: a
|
|
@@ -74022,6 +74053,34 @@ const useListScrollSync = ({
|
|
|
74022
74053
|
});
|
|
74023
74054
|
};
|
|
74024
74055
|
|
|
74056
|
+
// How many items share a line: the tracks a grid of items (`columns`)
|
|
74057
|
+
// resolves to, one otherwise. A layout result under auto-fill, so it is read
|
|
74058
|
+
// off the list once laid out, at each commit and each resize — for the runs
|
|
74059
|
+
// alone, which draw in lines: items declared one by one are all drawn. A
|
|
74060
|
+
// line of another length rewraps every item drawn: the item at the top of
|
|
74061
|
+
// the view is held where it is across it.
|
|
74062
|
+
const itemsPerLineRef = useRef(1);
|
|
74063
|
+
const [itemsPerLine, setItemsPerLine] = useState(1);
|
|
74064
|
+
const updateItemsPerLine = () => {
|
|
74065
|
+
if (!ref.current) {
|
|
74066
|
+
return;
|
|
74067
|
+
}
|
|
74068
|
+
const itemsPerLineNow = columns && listItems.hasRuns() ? readItemsPerLine(getListEl()) : 1;
|
|
74069
|
+
if (itemsPerLineNow === null || itemsPerLineNow === itemsPerLineRef.current) {
|
|
74070
|
+
return;
|
|
74071
|
+
}
|
|
74072
|
+
captureAnchor();
|
|
74073
|
+
itemsPerLineRef.current = itemsPerLineNow;
|
|
74074
|
+
setItemsPerLine(itemsPerLineNow);
|
|
74075
|
+
};
|
|
74076
|
+
const updateItemsPerLineRef = useRef(null);
|
|
74077
|
+
updateItemsPerLineRef.current = updateItemsPerLine;
|
|
74078
|
+
useLayoutEffect(() => {
|
|
74079
|
+
if (columns || itemsPerLineRef.current !== 1) {
|
|
74080
|
+
updateItemsPerLine();
|
|
74081
|
+
}
|
|
74082
|
+
});
|
|
74083
|
+
|
|
74025
74084
|
// What the window holds goes through three stages (see settleWindow):
|
|
74026
74085
|
// - "first": the first commit, a count of items since nothing is laid out
|
|
74027
74086
|
// yet to measure a size with — `initial` or the budget when they are one,
|
|
@@ -74423,6 +74482,7 @@ const useListScrollSync = ({
|
|
|
74423
74482
|
listEl: getListEl(),
|
|
74424
74483
|
listItems,
|
|
74425
74484
|
virtualItemSizeSignal,
|
|
74485
|
+
itemsPerLine: itemsPerLineRef.current,
|
|
74426
74486
|
renderWindowRef,
|
|
74427
74487
|
horizontal
|
|
74428
74488
|
});
|
|
@@ -74763,20 +74823,14 @@ const useListScrollSync = ({
|
|
|
74763
74823
|
}
|
|
74764
74824
|
const scrollerEl = getScroller();
|
|
74765
74825
|
const listEl = getListEl();
|
|
74766
|
-
|
|
74767
|
-
|
|
74768
|
-
|
|
74769
|
-
|
|
74770
|
-
|
|
74771
|
-
|
|
74772
|
-
|
|
74773
|
-
|
|
74774
|
-
// growing is its own content settling: only a list holding itself
|
|
74775
|
-
// somewhere cares (the end it aims at has moved), and a list the user is
|
|
74776
|
-
// reading must not be touched — its items are held still by the anchoring,
|
|
74777
|
-
// which this would undo. The SCROLLER resizing is the window around it
|
|
74778
|
-
// changing shape, and then the item that was at the top goes back where it
|
|
74779
|
-
// was.
|
|
74826
|
+
// Two things resize here, and they call for opposite answers. The LIST
|
|
74827
|
+
// growing is its own content settling: only a list holding itself
|
|
74828
|
+
// somewhere cares (the end it aims at has moved), and a list the user is
|
|
74829
|
+
// reading must not be touched — its items are held still by the anchoring,
|
|
74830
|
+
// which this would undo. The SCROLLER resizing is the window around it
|
|
74831
|
+
// changing shape, and then the item that was at the top goes back where it
|
|
74832
|
+
// was.
|
|
74833
|
+
const putTopItemBack = entries => {
|
|
74780
74834
|
const listResized = entries.some(entry => entry.target === listEl);
|
|
74781
74835
|
if (listResized && onGeometryChangeRef.current()) {
|
|
74782
74836
|
return;
|
|
@@ -74805,6 +74859,19 @@ const useListScrollSync = ({
|
|
|
74805
74859
|
} else {
|
|
74806
74860
|
scrollerEl.scrollTop += delta;
|
|
74807
74861
|
}
|
|
74862
|
+
};
|
|
74863
|
+
const observer = new ResizeObserver(entries => {
|
|
74864
|
+
// A box that grows may be a box that starts to scroll — the scroller is
|
|
74865
|
+
// resolved again before anything is done about the resize, so that a
|
|
74866
|
+
// list which has outgrown a bounded ancestor stops holding on to the
|
|
74867
|
+
// page.
|
|
74868
|
+
resolveScroller();
|
|
74869
|
+
rememberPosition();
|
|
74870
|
+
putTopItemBack(entries);
|
|
74871
|
+
// Once the top item is back where it was: that is the place a line of
|
|
74872
|
+
// another length has to keep, and putting it back drops any anchor
|
|
74873
|
+
// captured before.
|
|
74874
|
+
updateItemsPerLineRef.current();
|
|
74808
74875
|
});
|
|
74809
74876
|
observer.observe(scrollerEl);
|
|
74810
74877
|
observer.observe(listEl);
|
|
@@ -74960,7 +75027,10 @@ const useListScrollSync = ({
|
|
|
74960
75027
|
start,
|
|
74961
75028
|
end
|
|
74962
75029
|
} = renderWindowRef.current;
|
|
74963
|
-
|
|
75030
|
+
// What an item not drawn weighs: its share of a line, which the items side
|
|
75031
|
+
// by side on it take together.
|
|
75032
|
+
const itemsPerLineNow = itemsPerLineRef.current;
|
|
75033
|
+
const itemSize = virtualItemSizeSignal.peek() / itemsPerLineNow;
|
|
74964
75034
|
const pixelsOf = createItemSizeReader(items, start, end, itemSize);
|
|
74965
75035
|
const walkBefore = (from, sizeWanted, sizeOf) => {
|
|
74966
75036
|
let index = from;
|
|
@@ -75028,10 +75098,10 @@ const useListScrollSync = ({
|
|
|
75028
75098
|
const behindSize = countsItems ? Math.floor(spare / 4) : spare / 4;
|
|
75029
75099
|
const aheadSize = spare - behindSize;
|
|
75030
75100
|
if (stage === "steady" && !budgetWarnedRef.current && bandEnd > bandStart) {
|
|
75031
|
-
const
|
|
75032
|
-
if (spare <
|
|
75101
|
+
const twoLines = (countsItems ? 2 : 2 * (bandTo - bandFrom) / (bandEnd - bandStart)) * itemsPerLineNow;
|
|
75102
|
+
if (spare < twoLines) {
|
|
75033
75103
|
budgetWarnedRef.current = true;
|
|
75034
|
-
console.warn(`List: renderBudget=${renderBudget.value}${renderBudget.unit} leaves less than two items beyond what the screen shows (${bandEnd - bandStart} items): items will go blank as it scrolls. Give it room for a screen ahead or more — "300%" of the box that scrolls it, say.`);
|
|
75104
|
+
console.warn(`List: renderBudget=${renderBudget.value}${renderBudget.unit} leaves less than two ${itemsPerLineNow > 1 ? "lines of items" : "items"} beyond what the screen shows (${bandEnd - bandStart} items): items will go blank as it scrolls. Give it room for a screen ahead or more — "300%" of the box that scrolls it, say.`);
|
|
75035
75105
|
}
|
|
75036
75106
|
}
|
|
75037
75107
|
if (!force) {
|
|
@@ -75237,9 +75307,28 @@ const useListScrollSync = ({
|
|
|
75237
75307
|
ifChanged: true
|
|
75238
75308
|
});
|
|
75239
75309
|
}, [windowStart, windowEnd]);
|
|
75310
|
+
// What the runs draw: the window widened to whole lines. The window itself
|
|
75311
|
+
// moves an item at a time, and a grid lays the first item drawn in its first
|
|
75312
|
+
// column — started anywhere but on a line, every item drawn would sit in the
|
|
75313
|
+
// column of another. Started on one, an item is in the column its place says
|
|
75314
|
+
// whatever the window frames. The same object while the numbers hold, so the
|
|
75315
|
+
// runs are not told about a window that did not move.
|
|
75316
|
+
const windowDrawnRef = useRef(null);
|
|
75317
|
+
const drawnStart = windowStart - windowStart % itemsPerLine;
|
|
75318
|
+
const endPastLine = windowEnd % itemsPerLine;
|
|
75319
|
+
const drawnEnd = endPastLine === 0 ? windowEnd : windowEnd + itemsPerLine - endPastLine;
|
|
75320
|
+
let windowDrawn = windowDrawnRef.current;
|
|
75321
|
+
if (!windowDrawn || windowDrawn.start !== drawnStart || windowDrawn.end !== drawnEnd || windowDrawn.itemsPerLine !== itemsPerLine) {
|
|
75322
|
+
windowDrawn = {
|
|
75323
|
+
start: drawnStart,
|
|
75324
|
+
end: drawnEnd,
|
|
75325
|
+
itemsPerLine
|
|
75326
|
+
};
|
|
75327
|
+
windowDrawnRef.current = windowDrawn;
|
|
75328
|
+
}
|
|
75240
75329
|
return {
|
|
75241
75330
|
virtualItemSizeSignal,
|
|
75242
|
-
renderWindow:
|
|
75331
|
+
renderWindow: windowDrawn,
|
|
75243
75332
|
pendingScrollRef,
|
|
75244
75333
|
scrollToItem,
|
|
75245
75334
|
captureAnchor
|
|
@@ -75263,6 +75352,21 @@ const getScrollerViewportRect = scrollerEl => {
|
|
|
75263
75352
|
}
|
|
75264
75353
|
return scrollerEl.getBoundingClientRect();
|
|
75265
75354
|
};
|
|
75355
|
+
// The tracks a grid resolved its template to, as its computed
|
|
75356
|
+
// grid-template-columns lists them once laid out: "150px 150px [end] 150px".
|
|
75357
|
+
// Not laid out (a closed popup), that is the template as written, which says
|
|
75358
|
+
// nothing yet under auto-fill: `null`.
|
|
75359
|
+
const readItemsPerLine = listEl => {
|
|
75360
|
+
const tracks = getComputedStyle(listEl).gridTemplateColumns;
|
|
75361
|
+
if (tracks === "none" || tracks.includes("(")) {
|
|
75362
|
+
return null;
|
|
75363
|
+
}
|
|
75364
|
+
const sizes = tracks.replace(/\[[^\]]*\]/g, " ").trim();
|
|
75365
|
+
if (!sizes) {
|
|
75366
|
+
return null;
|
|
75367
|
+
}
|
|
75368
|
+
return sizes.split(/\s+/).length;
|
|
75369
|
+
};
|
|
75266
75370
|
// A window that cannot move is the one failure of a virtualized list that
|
|
75267
75371
|
// looks like nothing: the items outside it are fillers holding their room, so
|
|
75268
75372
|
// the list simply ends on blank space, at exactly the height of the items never
|
|
@@ -76150,6 +76254,7 @@ const getScrollInfo = ({
|
|
|
76150
76254
|
listEl,
|
|
76151
76255
|
listItems,
|
|
76152
76256
|
virtualItemSizeSignal,
|
|
76257
|
+
itemsPerLine,
|
|
76153
76258
|
renderWindowRef,
|
|
76154
76259
|
horizontal
|
|
76155
76260
|
}) => {
|
|
@@ -76187,7 +76292,9 @@ const getScrollInfo = ({
|
|
|
76187
76292
|
// mean we don't know the real on-screen index, only the scroll position,
|
|
76188
76293
|
// so estimate from it rather than assume nothing changed.
|
|
76189
76294
|
const estimateFromScrollPos = reasonPrefix => {
|
|
76190
|
-
|
|
76295
|
+
// An item's share of its line: the items side by side on it take it
|
|
76296
|
+
// together.
|
|
76297
|
+
const virtualItemSize = virtualItemSizeSignal.peek() / itemsPerLine;
|
|
76191
76298
|
if (virtualItemSize === 0) {
|
|
76192
76299
|
return null;
|
|
76193
76300
|
}
|
|
@@ -76243,8 +76350,10 @@ const getScrollInfo = ({
|
|
|
76243
76350
|
const VIRTUAL_ITEM_SIZE_EPSILON = 0.5;
|
|
76244
76351
|
// Measures the items currently in the DOM, edge to edge: what a filler stands in
|
|
76245
76352
|
// for is the room a run of items takes together — separators and group labels
|
|
76246
|
-
// included — not the height of one <li>.
|
|
76247
|
-
|
|
76353
|
+
// included — not the height of one <li>. The size of a line of them: one item
|
|
76354
|
+
// in a list, the items side by side in a grid (`inLines`), counted where a new
|
|
76355
|
+
// line starts.
|
|
76356
|
+
const measureItemSize = (listEl, horizontal, inLines) => {
|
|
76248
76357
|
let fromSkeletons = false;
|
|
76249
76358
|
let itemEls = listEl.querySelectorAll(REAL_LIST_ITEM_SELECTOR);
|
|
76250
76359
|
if (itemEls.length === 0) {
|
|
@@ -76266,13 +76375,27 @@ const measureItemSize = (listEl, horizontal) => {
|
|
|
76266
76375
|
if (span <= 0) {
|
|
76267
76376
|
return null;
|
|
76268
76377
|
}
|
|
76378
|
+
let lineCount = itemEls.length;
|
|
76379
|
+
if (inLines) {
|
|
76380
|
+
lineCount = 0;
|
|
76381
|
+
let lineFrom = -Infinity;
|
|
76382
|
+
for (const itemEl of itemEls) {
|
|
76383
|
+
const rect = itemEl.getBoundingClientRect();
|
|
76384
|
+
const from = horizontal ? rect.left : rect.top;
|
|
76385
|
+
if (from > lineFrom + 0.5) {
|
|
76386
|
+
lineCount++;
|
|
76387
|
+
lineFrom = from;
|
|
76388
|
+
}
|
|
76389
|
+
}
|
|
76390
|
+
}
|
|
76269
76391
|
return {
|
|
76270
|
-
size: span /
|
|
76271
|
-
|
|
76392
|
+
size: span / lineCount,
|
|
76393
|
+
lineCount,
|
|
76272
76394
|
fromSkeletons
|
|
76273
76395
|
};
|
|
76274
76396
|
};
|
|
76275
76397
|
const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
76398
|
+
inLines,
|
|
76276
76399
|
windowLeavesItemsOut,
|
|
76277
76400
|
scrolledWanted,
|
|
76278
76401
|
beforeSizeChange
|
|
@@ -76316,8 +76439,8 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
|
76316
76439
|
return;
|
|
76317
76440
|
}
|
|
76318
76441
|
}
|
|
76319
|
-
samples.sum += measure.size * measure.
|
|
76320
|
-
samples.count += measure.
|
|
76442
|
+
samples.sum += measure.size * measure.lineCount;
|
|
76443
|
+
samples.count += measure.lineCount;
|
|
76321
76444
|
const next = samples.sum / samples.count;
|
|
76322
76445
|
const current = virtualSizeSignal.peek();
|
|
76323
76446
|
if (Math.abs(next - current) > VIRTUAL_ITEM_SIZE_EPSILON) {
|
|
@@ -76337,7 +76460,7 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
|
76337
76460
|
const itemsHeldOffScreen = windowLeavesItemsOut();
|
|
76338
76461
|
if (!virtualItemSizeProp && sizeAlreadyKnown && itemsHeldOffScreen && ref.current) {
|
|
76339
76462
|
const listEl = ref.current.querySelector(".navi_list");
|
|
76340
|
-
const measure = listEl ? measureItemSize(listEl, horizontal) : null;
|
|
76463
|
+
const measure = listEl ? measureItemSize(listEl, horizontal, inLines) : null;
|
|
76341
76464
|
if (measure) {
|
|
76342
76465
|
feedSample(measure);
|
|
76343
76466
|
}
|
|
@@ -76358,11 +76481,11 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
|
|
|
76358
76481
|
if (!listEl) {
|
|
76359
76482
|
return undefined;
|
|
76360
76483
|
}
|
|
76361
|
-
const measure = measureItemSize(listEl, horizontal);
|
|
76484
|
+
const measure = measureItemSize(listEl, horizontal, inLines);
|
|
76362
76485
|
if (measure) {
|
|
76363
76486
|
const samples = samplesRef.current;
|
|
76364
|
-
samples.sum = measure.size * measure.
|
|
76365
|
-
samples.count = measure.
|
|
76487
|
+
samples.sum = measure.size * measure.lineCount;
|
|
76488
|
+
samples.count = measure.lineCount;
|
|
76366
76489
|
samples.fromSkeletons = measure.fromSkeletons;
|
|
76367
76490
|
virtualSizeSignal.value = measure.size;
|
|
76368
76491
|
return undefined;
|
|
@@ -76507,14 +76630,17 @@ const Fallback = ({
|
|
|
76507
76630
|
// Reads the item size itself: it is what the size is for, and a run holding
|
|
76508
76631
|
// every item it draws must not be redrawn — every item of it — because the size
|
|
76509
76632
|
// settled after the first commit.
|
|
76633
|
+
// It holds lines: the items side by side on one take its room together, and a
|
|
76634
|
+
// last line left short takes it all the same.
|
|
76510
76635
|
const VirtualFiller = ({
|
|
76511
76636
|
edge,
|
|
76512
76637
|
itemCount,
|
|
76638
|
+
itemsPerLine,
|
|
76513
76639
|
findChunks
|
|
76514
76640
|
}) => {
|
|
76515
76641
|
const listItems = useContext(ListItemsContext);
|
|
76516
|
-
const
|
|
76517
|
-
const sizeToFill = itemCount *
|
|
76642
|
+
const lineSize = listItems.virtualItemSizeSignal.value;
|
|
76643
|
+
const sizeToFill = Math.ceil(itemCount / itemsPerLine) * lineSize;
|
|
76518
76644
|
// A filler resizing moves what stands below it — the items on screen, when it
|
|
76519
76645
|
// holds the room of items above them — and it resizes in a commit of its own
|
|
76520
76646
|
// when the item size settles after the list has rendered: the list puts its
|
|
@@ -76533,7 +76659,7 @@ const VirtualFiller = ({
|
|
|
76533
76659
|
"aria-hidden": true,
|
|
76534
76660
|
style: {
|
|
76535
76661
|
"--size-to-fill": `${sizeToFill}px`,
|
|
76536
|
-
"--x-find-line-size": findChunks ? `${
|
|
76662
|
+
"--x-find-line-size": findChunks ? `${lineSize / itemsPerLine}px` : undefined
|
|
76537
76663
|
},
|
|
76538
76664
|
children: findChunks && findChunks.map(chunk => jsx("div", {
|
|
76539
76665
|
className: "navi_list_find_stand_in",
|
|
@@ -77416,6 +77542,9 @@ const useRunItems = (store, {
|
|
|
77416
77542
|
const rankOf = itemIndex => itemIndex - runStart;
|
|
77417
77543
|
const indexOfRank = rank => rank + runStart;
|
|
77418
77544
|
const getItemAt = itemIndex => store.getItem(rankOf(itemIndex));
|
|
77545
|
+
const {
|
|
77546
|
+
itemsPerLine
|
|
77547
|
+
} = renderWindow;
|
|
77419
77548
|
const windowFrom = renderWindow.start > runStart ? renderWindow.start : runStart;
|
|
77420
77549
|
const windowTo = renderWindow.end < runEnd ? renderWindow.end : runEnd;
|
|
77421
77550
|
store.forget(rankOf(windowFrom), rankOf(windowTo));
|
|
@@ -77523,8 +77652,20 @@ const useRunItems = (store, {
|
|
|
77523
77652
|
// band is drawn among the items. A negative start is not a rank but a count
|
|
77524
77653
|
// back from the end (the very first ask, before the count is known) — there
|
|
77525
77654
|
// is no item to convert it to, and the band falls back to the window.
|
|
77526
|
-
|
|
77527
|
-
|
|
77655
|
+
let failureFrom = store.failure === null ? -1 : store.failure.start < 0 || indexOfRank(store.failure.start) < windowFrom ? windowFrom : indexOfRank(store.failure.start);
|
|
77656
|
+
let failureTo = store.failure === null ? -1 : store.failure.end < 0 || indexOfRank(store.failure.end) > windowTo - 1 ? windowTo - 1 : indexOfRank(store.failure.end);
|
|
77657
|
+
if (store.failure !== null && itemsPerLine > 1) {
|
|
77658
|
+
// In a grid the band takes whole lines, like the window: begun mid-line,
|
|
77659
|
+
// it would push every item after it into another column.
|
|
77660
|
+
failureFrom -= failureFrom % itemsPerLine;
|
|
77661
|
+
if (failureFrom < windowFrom) {
|
|
77662
|
+
failureFrom = windowFrom;
|
|
77663
|
+
}
|
|
77664
|
+
failureTo += itemsPerLine - 1 - failureTo % itemsPerLine;
|
|
77665
|
+
if (failureTo > windowTo - 1) {
|
|
77666
|
+
failureTo = windowTo - 1;
|
|
77667
|
+
}
|
|
77668
|
+
}
|
|
77528
77669
|
const nodes = [];
|
|
77529
77670
|
// Items that belong together, as the data says (a day of messages, a month of
|
|
77530
77671
|
// games): consecutive items sharing a group key are wrapped in one group, so
|
|
@@ -77601,6 +77742,7 @@ const useRunItems = (store, {
|
|
|
77601
77742
|
nodes.push(jsx(VirtualFiller, {
|
|
77602
77743
|
edge: "before",
|
|
77603
77744
|
itemCount: windowFrom - runStart,
|
|
77745
|
+
itemsPerLine: itemsPerLine,
|
|
77604
77746
|
findChunks: getFindChunks(runStart, windowFrom)
|
|
77605
77747
|
}, "navi-list-filler-before"));
|
|
77606
77748
|
}
|
|
@@ -77615,7 +77757,7 @@ const useRunItems = (store, {
|
|
|
77615
77757
|
nodes.push(jsx("li", {
|
|
77616
77758
|
className: "navi_list_failed_items",
|
|
77617
77759
|
style: {
|
|
77618
|
-
"--size-to-fill": `${failedItemCount * listItems.virtualItemSizeSignal.value}px`
|
|
77760
|
+
"--size-to-fill": `${Math.ceil(failedItemCount / itemsPerLine) * listItems.virtualItemSizeSignal.value}px`
|
|
77619
77761
|
},
|
|
77620
77762
|
children: renderError ? renderError({
|
|
77621
77763
|
error: store.failure.error,
|
|
@@ -77705,6 +77847,7 @@ const useRunItems = (store, {
|
|
|
77705
77847
|
nodes.push(jsx(VirtualFiller, {
|
|
77706
77848
|
edge: "after",
|
|
77707
77849
|
itemCount: runEnd - windowTo,
|
|
77850
|
+
itemsPerLine: itemsPerLine,
|
|
77708
77851
|
findChunks: getFindChunks(windowTo, runEnd)
|
|
77709
77852
|
}, "navi-list-filler-after"));
|
|
77710
77853
|
}
|
|
@@ -78437,12 +78580,16 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
78437
78580
|
* @param {string} [props.columns]
|
|
78438
78581
|
* The list's own columns: a `grid-template-columns` value the ITEMS are laid
|
|
78439
78582
|
* into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
|
|
78440
|
-
* choices (`repeat(3, minmax(0, 1fr))`)
|
|
78441
|
-
*
|
|
78442
|
-
*
|
|
78443
|
-
*
|
|
78444
|
-
*
|
|
78445
|
-
*
|
|
78583
|
+
* choices (`repeat(3, minmax(0, 1fr))`), a feed of covers
|
|
78584
|
+
* (`repeat(auto-fill, minmax(150px, 1fr))` over a `<List.Items>`). Each item
|
|
78585
|
+
* takes one cell, and an item meant to take a whole line says so for itself
|
|
78586
|
+
* (`style={{ gridColumn: "1 / -1" }}`). A `<List.Items>` run is drawn in whole
|
|
78587
|
+
* lines — as many items to a line as the template resolves to, read again
|
|
78588
|
+
* when the list's width changes: its window starts and ends on a line, so an
|
|
78589
|
+
* item is always in the column its place says, and the fillers hold the room
|
|
78590
|
+
* of the lines it does not draw (`virtualItemSize` is then a line's size).
|
|
78591
|
+
* That count holds only while every item of the run takes one cell: an item
|
|
78592
|
+
* spanning a line, or a `groupBy`, puts the ones after it in other columns.
|
|
78446
78593
|
* @param {string} [props.itemColumns]
|
|
78447
78594
|
* The columns inside an ITEM: a `grid-template-columns` value each item fills
|
|
78448
78595
|
* with its own children, a table whose cells line up down the list. Every item
|
|
@@ -78458,8 +78605,9 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
78458
78605
|
* scroll box, which fills it and has nothing to arrange.
|
|
78459
78606
|
* @param {boolean} [props.flexWrap]
|
|
78460
78607
|
* Lets a horizontal list's items fall to the next line instead of running
|
|
78461
|
-
* past the edge — a row of choices under a `maxWidth`, say.
|
|
78462
|
-
*
|
|
78608
|
+
* past the edge — a row of choices under a `maxWidth`, say. A wrapped line
|
|
78609
|
+
* holding several items is not virtualizable: a long collection laid several
|
|
78610
|
+
* to a line goes in `columns`.
|
|
78463
78611
|
* @param {string} [props.overflow]
|
|
78464
78612
|
* `"visible"` lets the items paint outside the list — a check in an item's
|
|
78465
78613
|
* corner, a badge crossing the edge. A list clips by default, which is what
|
|
@@ -78578,7 +78726,8 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
|
|
|
78578
78726
|
* one-line size) — an item guessed too small builds one more, an item
|
|
78579
78727
|
* guessed too big leaves a blank (see docs/scroll.md, "What the list knows,
|
|
78580
78728
|
* and what it guesses"). The items drawn are measured either way: the render
|
|
78581
|
-
* window is sized on them.
|
|
78729
|
+
* window is sized on them. In a grid (`columns`), the size is a line's: a
|
|
78730
|
+
* card's, which the items side by side on it share.
|
|
78582
78731
|
* @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
|
|
78583
78732
|
* Which box scrolls — and with it, which box the render window follows and
|
|
78584
78733
|
* which box a scroll position is read from (`onScrolledChange`). `"self"`
|