@jsenv/navi 0.28.0 → 0.28.1
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 +643 -249
- package/dist/jsenv_navi.js.map +71 -49
- package/dist/jsenv_navi_side_effects.js +1 -1
- package/dist/jsenv_navi_side_effects.js.map +1 -1
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* AI reading this file: read ../docs/AI_INSTRUCTIONS.md for context on
|
|
3
3
|
* using @jsenv/navi as intended.
|
|
4
4
|
*/
|
|
5
|
-
import { installImportMetaCssBuild, windowWidthSignal } from "./jsenv_navi_side_effects.js";
|
|
5
|
+
import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal } from "./jsenv_navi_side_effects.js";
|
|
6
6
|
import { isValidElement, createContext, h, toChildArray, render, Fragment, cloneElement } from "preact";
|
|
7
7
|
import { useErrorBoundary, useLayoutEffect, useEffect, useContext, useMemo, useRef, useState, useCallback, useId } from "preact/hooks";
|
|
8
8
|
import { jsxs, jsx, Fragment as Fragment$1 } from "preact/jsx-runtime";
|
|
@@ -6821,6 +6821,7 @@ const VISUAL_PROPS = {
|
|
|
6821
6821
|
scrollbarWidth: PASS_THROUGH,
|
|
6822
6822
|
scrollbarGutter: PASS_THROUGH,
|
|
6823
6823
|
scrollMarginBlock: PASS_THROUGH,
|
|
6824
|
+
scrollMarginInline: PASS_THROUGH,
|
|
6824
6825
|
scrollMargin: PASS_THROUGH,
|
|
6825
6826
|
};
|
|
6826
6827
|
const CONTENT_PROPS = {
|
|
@@ -7076,7 +7077,37 @@ const sizeSpacingKeySet = new Set(Object.keys(SIZE_MAP));
|
|
|
7076
7077
|
const isSizeSpacingKey = (key) => {
|
|
7077
7078
|
return sizeSpacingKeySet.has(key);
|
|
7078
7079
|
};
|
|
7080
|
+
// Viewport-relative units, resolved to pixels here because a JS consumer (popup
|
|
7081
|
+
// positioning) needs an actual number, not a length only CSS can evaluate.
|
|
7082
|
+
// "vvw"/"vvh" are navi's own: the *visual* viewport, which — unlike vw/dvw —
|
|
7083
|
+
// shrinks when the mobile virtual keyboard opens (see layout/responsive.js), so
|
|
7084
|
+
// they are what a popup meant to stay clear of the keyboard should use.
|
|
7085
|
+
const VIEWPORT_UNIT_SIGNALS = {
|
|
7086
|
+
vvw: visualViewportWidthSignal,
|
|
7087
|
+
vvh: visualViewportHeightSignal,
|
|
7088
|
+
vw: windowWidthSignal,
|
|
7089
|
+
vh: windowHeightSignal,
|
|
7090
|
+
dvw: windowWidthSignal,
|
|
7091
|
+
dvh: windowHeightSignal,
|
|
7092
|
+
};
|
|
7093
|
+
const VIEWPORT_LENGTH_REGEX = /^(-?\d+(?:\.\d+)?)(vvw|vvh|dvw|dvh|vw|vh)$/;
|
|
7094
|
+
const resolveViewportLength = (size) => {
|
|
7095
|
+
if (typeof size !== "string") {
|
|
7096
|
+
return null;
|
|
7097
|
+
}
|
|
7098
|
+
const match = VIEWPORT_LENGTH_REGEX.exec(size);
|
|
7099
|
+
if (!match) {
|
|
7100
|
+
return null;
|
|
7101
|
+
}
|
|
7102
|
+
const [, amount, unit] = match;
|
|
7103
|
+
return (parseFloat(amount) / 100) * VIEWPORT_UNIT_SIGNALS[unit].value;
|
|
7104
|
+
};
|
|
7105
|
+
|
|
7079
7106
|
const resolveSpacingSize = (size, element, property = "padding") => {
|
|
7107
|
+
const viewportLength = resolveViewportLength(size);
|
|
7108
|
+
if (viewportLength !== null) {
|
|
7109
|
+
return viewportLength;
|
|
7110
|
+
}
|
|
7080
7111
|
return normalizeStyle(SIZE_MAP[size] || size, property, "js", element);
|
|
7081
7112
|
};
|
|
7082
7113
|
|
|
@@ -21286,9 +21317,11 @@ const createControlInteraction = (
|
|
|
21286
21317
|
// Check managed controls — a non-interactable child blocks the parent,
|
|
21287
21318
|
// UNLESS the child's failing constraint has `ignoredByParents: true`
|
|
21288
21319
|
// (e.g. a disabled child inside a group should not prevent the group from acting).
|
|
21320
|
+
// Only the children that are reachable alongside the parent take part: a
|
|
21321
|
+
// picker's popup content is excluded, see getInteractionBlockingControls.
|
|
21289
21322
|
failingManagedInteraction = null;
|
|
21290
21323
|
if (!interactionFailedConstraintInfo) {
|
|
21291
|
-
for (const mc of controller.
|
|
21324
|
+
for (const mc of controller.getInteractionBlockingControls()) {
|
|
21292
21325
|
const mci = mc.rules.interaction;
|
|
21293
21326
|
if (!mci) {
|
|
21294
21327
|
continue;
|
|
@@ -22750,6 +22783,7 @@ const useRenderScope = (init, update) => {
|
|
|
22750
22783
|
* props: Object;
|
|
22751
22784
|
* ref: Ref; // Used to dispatch DOM events
|
|
22752
22785
|
* getManagedControls(): UIStateController[]; // Returns controls whose validity is managed by this controller
|
|
22786
|
+
* getInteractionBlockingControls(): UIStateController[]; // Subset of the above whose busy state also blocks interacting with this controller
|
|
22753
22787
|
* }
|
|
22754
22788
|
* ```
|
|
22755
22789
|
*/
|
|
@@ -22867,6 +22901,10 @@ const useUIStateController = (
|
|
|
22867
22901
|
}
|
|
22868
22902
|
return [];
|
|
22869
22903
|
},
|
|
22904
|
+
// A facade child lives inside the control's own popup, so it is out of
|
|
22905
|
+
// reach until that popup opens. Letting it block interaction would make
|
|
22906
|
+
// a picker whose content is loading impossible to open at all.
|
|
22907
|
+
getInteractionBlockingControls: () => [],
|
|
22870
22908
|
onUIAction: (e, { skipCommand } = {}) => {
|
|
22871
22909
|
if (controlType === "button" && controller.controlHostProps.name) {
|
|
22872
22910
|
const buttonName = controller.controlHostProps.name;
|
|
@@ -23798,6 +23836,11 @@ const useUIGroupStateController = (
|
|
|
23798
23836
|
if (!cascadeValidationToChildren) return [];
|
|
23799
23837
|
return childUIStateControllerArray.slice();
|
|
23800
23838
|
},
|
|
23839
|
+
// Group children sit next to the group itself: a busy one really does
|
|
23840
|
+
// prevent the group from acting as a whole.
|
|
23841
|
+
getInteractionBlockingControls: () => {
|
|
23842
|
+
return controller.getManagedControls();
|
|
23843
|
+
},
|
|
23801
23844
|
subscribe: subscribeUIState,
|
|
23802
23845
|
};
|
|
23803
23846
|
const rules = createControlRules(controller, {
|
|
@@ -24033,6 +24076,13 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
24033
24076
|
}
|
|
24034
24077
|
return child.getManagedControls();
|
|
24035
24078
|
},
|
|
24079
|
+
getInteractionBlockingControls: () => {
|
|
24080
|
+
const child = firstChildControllerRef.current;
|
|
24081
|
+
if (!child) {
|
|
24082
|
+
return [];
|
|
24083
|
+
}
|
|
24084
|
+
return child.getInteractionBlockingControls();
|
|
24085
|
+
},
|
|
24036
24086
|
onChildUIAction: (child, e, { stateChanged, silent = false }) => {
|
|
24037
24087
|
if (!stateChanged) {
|
|
24038
24088
|
return;
|
|
@@ -24040,6 +24090,18 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
24040
24090
|
if (child !== firstChildControllerRef.current) {
|
|
24041
24091
|
return;
|
|
24042
24092
|
}
|
|
24093
|
+
if (
|
|
24094
|
+
silent &&
|
|
24095
|
+
child.uiState === undefined &&
|
|
24096
|
+
s.realUIStateController.uiState !== undefined
|
|
24097
|
+
) {
|
|
24098
|
+
// A silent sync means the child's own structure changed (children
|
|
24099
|
+
// mounted/unmounted), not that the user acted. A child that ends up
|
|
24100
|
+
// with no value there is one that currently *cannot* express one —
|
|
24101
|
+
// a <List loading> holds no items yet — which must not read as the
|
|
24102
|
+
// user clearing the picker, nor fire its uiAction.
|
|
24103
|
+
return;
|
|
24104
|
+
}
|
|
24043
24105
|
updatingRef.current = true;
|
|
24044
24106
|
// Use a different event type for silent (mount/unmount) syncs so that
|
|
24045
24107
|
// the picker's setUIState does not fire navi_change or action pipelines.
|
|
@@ -28646,6 +28708,7 @@ installImportMetaCssBuild(import.meta);const css$L = /* css */`
|
|
|
28646
28708
|
--link-text-decoration-hover: var(--link-text-decoration);
|
|
28647
28709
|
--link-cursor: pointer;
|
|
28648
28710
|
--link-loading-outline-size: 1px;
|
|
28711
|
+
--link-outline-width: 2px;
|
|
28649
28712
|
|
|
28650
28713
|
--link-current-indicator-size: 2px;
|
|
28651
28714
|
--link-current-indicator-spacing: 0;
|
|
@@ -28781,6 +28844,10 @@ installImportMetaCssBuild(import.meta);const css$L = /* css */`
|
|
|
28781
28844
|
--x-link-color: var(--link-color);
|
|
28782
28845
|
}
|
|
28783
28846
|
}
|
|
28847
|
+
&[data-anchor] {
|
|
28848
|
+
/* Usually better to have some spacing between the anchor and the scroll top */
|
|
28849
|
+
scroll-margin-block: calc(1em + var(--link-outline-width) + 1px);
|
|
28850
|
+
}
|
|
28784
28851
|
/* Hover */
|
|
28785
28852
|
&[data-hover] {
|
|
28786
28853
|
--x-link-background: var(--x-link-background-hover);
|
|
@@ -28788,7 +28855,7 @@ installImportMetaCssBuild(import.meta);const css$L = /* css */`
|
|
|
28788
28855
|
--x-link-text-decoration: var(--x-link-text-decoration-hover);
|
|
28789
28856
|
}
|
|
28790
28857
|
&[data-focus-visible] {
|
|
28791
|
-
outline-width:
|
|
28858
|
+
outline-width: var(--link-outline-width);
|
|
28792
28859
|
}
|
|
28793
28860
|
/* Pressed */
|
|
28794
28861
|
&[data-pressed] {
|
|
@@ -35947,20 +36014,6 @@ const renderSafe = (value) => {
|
|
|
35947
36014
|
|
|
35948
36015
|
const PickerContext = createContext();
|
|
35949
36016
|
|
|
35950
|
-
// The resolved popup mode of the surrounding Picker: "popover" or "dialog"
|
|
35951
|
-
// (frozen for the lifetime of an opening — see usePopupMode). Provided around the
|
|
35952
|
-
// picker's popup content so that content can render differently per mode.
|
|
35953
|
-
const PickerModeContext = createContext(undefined);
|
|
35954
|
-
|
|
35955
|
-
/**
|
|
35956
|
-
* Read the mode ("popover" | "dialog") of the Picker whose popup this is rendered
|
|
35957
|
-
* inside. Only meaningful for a Picker's popup content (its children); returns
|
|
35958
|
-
* undefined anywhere else.
|
|
35959
|
-
*
|
|
35960
|
-
* @returns {"popover" | "dialog" | undefined}
|
|
35961
|
-
*/
|
|
35962
|
-
const usePickerMode = () => useContext(PickerModeContext);
|
|
35963
|
-
|
|
35964
36017
|
/**
|
|
35965
36018
|
* Mirrors what browsers do when navigating to a page:
|
|
35966
36019
|
* 1. Focus the first element with [navi-autofocus] (but not [navi-autofocus="fallback"]) inside the container
|
|
@@ -36028,14 +36081,16 @@ const prepareFocusTransfer = (prepareEvent, debugFocus) => {
|
|
|
36028
36081
|
}
|
|
36029
36082
|
}
|
|
36030
36083
|
if (!target) {
|
|
36031
|
-
//
|
|
36032
|
-
//
|
|
36033
|
-
//
|
|
36034
|
-
|
|
36035
|
-
|
|
36036
|
-
|
|
36037
|
-
|
|
36038
|
-
|
|
36084
|
+
// A [navi-autofocus="fallback"] INSIDE the container (e.g. a search
|
|
36085
|
+
// input) wins over the container itself. The container is only the
|
|
36086
|
+
// fallback-of-the-fallback: a focusable popup root gets focus solely
|
|
36087
|
+
// when nothing inside it already carries the fallback. (matches() covers
|
|
36088
|
+
// the container-only case since querySelector searches descendants only.)
|
|
36089
|
+
const naviAutoFocusFallback =
|
|
36090
|
+
containerEl.querySelector(`[navi-autofocus="fallback"]`) ||
|
|
36091
|
+
(containerEl.matches(`[navi-autofocus="fallback"]`)
|
|
36092
|
+
? containerEl
|
|
36093
|
+
: null);
|
|
36039
36094
|
if (naviAutoFocusFallback) {
|
|
36040
36095
|
reason = "navi-autofocus fallback";
|
|
36041
36096
|
target = naviAutoFocusFallback;
|
|
@@ -36157,26 +36212,32 @@ const createOpenController = (
|
|
|
36157
36212
|
// open() ignores the request — no need to know *which* element triggers
|
|
36158
36213
|
// it. A bubble-phase listener (runs after everything else, once the click
|
|
36159
36214
|
// reaches document) clears the flag if nothing consumed it, meaning this
|
|
36160
|
-
// click never resulted in an open() call. A
|
|
36161
|
-
//
|
|
36162
|
-
//
|
|
36215
|
+
// click never resulted in an open() call. A timeout is a last-resort safety
|
|
36216
|
+
// net in case the click never reaches document at all (e.g. some ancestor
|
|
36217
|
+
// called stopPropagation()) — a *task*, never a microtask: a microtask
|
|
36218
|
+
// checkpoint runs between two listeners of the same trusted event dispatch,
|
|
36219
|
+
// so it would clear the flag before the bubble-phase handler this is meant
|
|
36220
|
+
// to block ever runs, which is precisely the case it exists for.
|
|
36163
36221
|
const armSuppressNextOpenRequest = () => {
|
|
36164
36222
|
disarmSuppressNextOpenRequest?.();
|
|
36223
|
+
let safetyTimeout = null;
|
|
36165
36224
|
const onCaptureClick = () => {
|
|
36166
36225
|
document.removeEventListener("click", onCaptureClick, {
|
|
36167
36226
|
capture: true,
|
|
36168
36227
|
});
|
|
36169
36228
|
suppressNextOpenRequest = true;
|
|
36170
36229
|
document.addEventListener("click", onBubbleClick);
|
|
36171
|
-
|
|
36230
|
+
safetyTimeout = setTimeout(() => {
|
|
36172
36231
|
suppressNextOpenRequest = false;
|
|
36173
36232
|
});
|
|
36174
36233
|
};
|
|
36175
36234
|
const onBubbleClick = () => {
|
|
36176
36235
|
document.removeEventListener("click", onBubbleClick);
|
|
36236
|
+
clearTimeout(safetyTimeout);
|
|
36177
36237
|
suppressNextOpenRequest = false;
|
|
36178
36238
|
};
|
|
36179
36239
|
disarmSuppressNextOpenRequest = () => {
|
|
36240
|
+
clearTimeout(safetyTimeout);
|
|
36180
36241
|
document.removeEventListener("click", onCaptureClick, {
|
|
36181
36242
|
capture: true,
|
|
36182
36243
|
});
|
|
@@ -36464,6 +36525,61 @@ const useOpenControllerByProps = (props) => {
|
|
|
36464
36525
|
return openController;
|
|
36465
36526
|
};
|
|
36466
36527
|
|
|
36528
|
+
/**
|
|
36529
|
+
* Where the "popover or dialog?" answer lives, for both the components that
|
|
36530
|
+
* decide it and the content that renders inside one.
|
|
36531
|
+
*
|
|
36532
|
+
* `useResolvedPopupMode` is the decision (screen size + maxWidth heuristic),
|
|
36533
|
+
* called by whatever renders the popup — `Popup` itself, or `picker_custom.jsx`
|
|
36534
|
+
* which needs the answer for its own mode-dependent history/ARIA handling on
|
|
36535
|
+
* top of picking a renderer. `usePopupMode` is the read side: any content
|
|
36536
|
+
* rendered inside a popup can call it to lay itself out differently in a
|
|
36537
|
+
* dropdown than in a full-screen modal.
|
|
36538
|
+
*/
|
|
36539
|
+
|
|
36540
|
+
const PopupModeContext = createContext(undefined);
|
|
36541
|
+
|
|
36542
|
+
/**
|
|
36543
|
+
* Read the mode of the popup this is rendered inside — a `Popup`, or a
|
|
36544
|
+
* `Picker`'s own popup. Returns undefined outside of any popup content.
|
|
36545
|
+
*
|
|
36546
|
+
* @returns {"popover" | "dialog" | undefined}
|
|
36547
|
+
*/
|
|
36548
|
+
const usePopupMode = () => useContext(PopupModeContext);
|
|
36549
|
+
|
|
36550
|
+
/**
|
|
36551
|
+
* Resolves which of Popover/Dialog a popup should be. Frozen for the component
|
|
36552
|
+
* instance's lifetime, so a screen resize never switches an already-mounted
|
|
36553
|
+
* popup from one to the other mid-session.
|
|
36554
|
+
*
|
|
36555
|
+
* @param {"dialog"|"popover"} [modeProp] - Forces one mode; `undefined` to
|
|
36556
|
+
* resolve automatically.
|
|
36557
|
+
* @param {string} [maxWidth] - A small enough value is treated as "compact",
|
|
36558
|
+
* staying a popover even on a small screen.
|
|
36559
|
+
* @returns {["dialog"|"popover", () => void]} The resolved mode, and a
|
|
36560
|
+
* `resetMode` function a caller can call (e.g. on close) to force the *next*
|
|
36561
|
+
* call to re-resolve from scratch instead of keeping the frozen value —
|
|
36562
|
+
* `Popup` itself never needs this (it has no notion of open/close of its
|
|
36563
|
+
* own), `picker_custom.jsx` does (re-evaluates screen size on every fresh
|
|
36564
|
+
* open).
|
|
36565
|
+
*/
|
|
36566
|
+
const useResolvedPopupMode = (modeProp, maxWidth) => {
|
|
36567
|
+
const defaultModeRef = useRef(null);
|
|
36568
|
+
if (defaultModeRef.current === null) {
|
|
36569
|
+
defaultModeRef.current = resolvePopupMode(modeProp, maxWidth);
|
|
36570
|
+
}
|
|
36571
|
+
const resetMode = () => {
|
|
36572
|
+
defaultModeRef.current = null;
|
|
36573
|
+
};
|
|
36574
|
+
return [defaultModeRef.current, resetMode];
|
|
36575
|
+
};
|
|
36576
|
+
const resolvePopupMode = (modeProp, maxWidth) => {
|
|
36577
|
+
const isSmallScreen = windowWidthSignal.peek() <= 600;
|
|
36578
|
+
const maxWidthPx = parseFloat(maxWidth);
|
|
36579
|
+
const isCompact = isFinite(maxWidthPx) && maxWidthPx < 150;
|
|
36580
|
+
return modeProp ?? (isSmallScreen && !isCompact ? "dialog" : "popover");
|
|
36581
|
+
};
|
|
36582
|
+
|
|
36467
36583
|
/**
|
|
36468
36584
|
* Entry/exit animation CSS shared by Popover and Dialog.
|
|
36469
36585
|
*
|
|
@@ -36561,7 +36677,8 @@ const popupCss = /* css */ `
|
|
|
36561
36677
|
this list contains (no shared transition-property to clobber, no
|
|
36562
36678
|
propertyName to filter). */
|
|
36563
36679
|
&[navi-animation] {
|
|
36564
|
-
transition-property:
|
|
36680
|
+
transition-property:
|
|
36681
|
+
display, overlay, opacity, translate, scale, box-shadow;
|
|
36565
36682
|
transition-duration:
|
|
36566
36683
|
var(--popup-animation-duration), var(--popup-animation-duration),
|
|
36567
36684
|
var(--popup-opacity-duration), var(--popup-translate-duration),
|
|
@@ -36924,15 +37041,24 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
36924
37041
|
const css$v = /* css */`
|
|
36925
37042
|
@layer navi {
|
|
36926
37043
|
.navi_dialog {
|
|
36927
|
-
/*
|
|
36928
|
-
|
|
36929
|
-
|
|
37044
|
+
/* Min gap between the dialog and the edges of its container. Written
|
|
37045
|
+
from the marginWithContainer prop (see below) — hence --x-, not a knob
|
|
37046
|
+
to set from CSS — so the size caps here and the placement can never
|
|
37047
|
+
disagree. The literal is only what a dialog painted before that ever
|
|
37048
|
+
runs falls back to. Not named "margin" because it isn't implemented
|
|
37049
|
+
with margins (those are needed for centering).
|
|
37050
|
+
|
|
37051
|
+
Capping the *size* here rather than only offsetting the position is
|
|
37052
|
+
what makes a centered dialog follow the mobile virtual keyboard for
|
|
37053
|
+
free: --navi-vvw/--navi-vvh track the visual viewport, so the browser
|
|
37054
|
+
reflows the dialog itself as the keyboard opens. */
|
|
37055
|
+
--x-dialog-viewport-spacing: 3vvw;
|
|
36930
37056
|
|
|
36931
37057
|
--dialog-maxmax-width: calc(
|
|
36932
|
-
var(--navi-vvw) - 2 * var(--dialog-viewport-spacing)
|
|
37058
|
+
var(--navi-vvw) - 2 * var(--x-dialog-viewport-spacing)
|
|
36933
37059
|
);
|
|
36934
37060
|
--dialog-maxmax-height: calc(
|
|
36935
|
-
var(--navi-vvh) - 2 * var(--dialog-viewport-spacing)
|
|
37061
|
+
var(--navi-vvh) - 2 * var(--x-dialog-viewport-spacing)
|
|
36936
37062
|
);
|
|
36937
37063
|
|
|
36938
37064
|
--dialog-border-radius: var(--navi-popup-border-radius);
|
|
@@ -37165,8 +37291,14 @@ const css$v = /* css */`
|
|
|
37165
37291
|
* `bottom-end`/`bottom-left`/`bottom-right`, `left`/`left-start`/
|
|
37166
37292
|
* `left-end`, or `center` — optionally wrapped in `inset(...)` (e.g.
|
|
37167
37293
|
* `inset(top)`) for the overlapping variant.
|
|
37168
|
-
* @param {string|number} [props.marginWithContainer=
|
|
37169
|
-
* between the dialog and the edges of its container
|
|
37294
|
+
* @param {string|number} [props.marginWithContainer="3vvw"] - Minimum gap kept
|
|
37295
|
+
* between the dialog and the edges of its container, whatever its
|
|
37296
|
+
* `positionArea`: it both caps the dialog's own size (via
|
|
37297
|
+
* `--x-dialog-viewport-spacing`, written from this prop) and offsets a docked
|
|
37298
|
+
* one from the edge it docks to. Accepts a spacing token ("s", "m"…), a
|
|
37299
|
+
* number of pixels, or a viewport length — "vvw"/"vvh" being the visual
|
|
37300
|
+
* viewport, which shrinks when the mobile keyboard opens. Pass 0 for a dialog
|
|
37301
|
+
* meant to sit flush (a side panel).
|
|
37170
37302
|
* @param {"close"|"cancel"|"capture"|"none"} [props.pointerInteractionOutsideEffect="close"]
|
|
37171
37303
|
* - `"close"` closes the dialog on an outside click. `"capture"`/`"none"`
|
|
37172
37304
|
* both just absorb the click without closing (visually dimmed backdrop vs.
|
|
@@ -37312,7 +37444,10 @@ const useDialogProps = props => {
|
|
|
37312
37444
|
// Same grammar as Popover's own positionArea — see this file's top
|
|
37313
37445
|
// comment and popup_shared.js's parsePositionArea.
|
|
37314
37446
|
positionArea = "center",
|
|
37315
|
-
|
|
37447
|
+
// A dialog docked against an edge must keep the same gap its own size cap
|
|
37448
|
+
// already guarantees a centered one — so this drives both (see
|
|
37449
|
+
// --x-dialog-viewport-spacing above). Pass 0 to sit flush (side_panel.jsx).
|
|
37450
|
+
marginWithContainer = "3vvw",
|
|
37316
37451
|
// "close" (default) closes on an outside click. "capture"/"none" both
|
|
37317
37452
|
// just absorb it without closing — for the via-attribute renderer,
|
|
37318
37453
|
// showModal() already makes the rest of the page inert, so there's
|
|
@@ -37349,6 +37484,21 @@ const useDialogProps = props => {
|
|
|
37349
37484
|
const debugFocus = useDebugFocus();
|
|
37350
37485
|
const debugInteraction = useDebugInteraction();
|
|
37351
37486
|
const autoFocusProps = useAutoFocus(ref, autoFocus);
|
|
37487
|
+
// positionDialog lives in openEffect's closure — created once, when the
|
|
37488
|
+
// dialog opens. Reading the placement props through a ref instead of that
|
|
37489
|
+
// closure is what lets a change while open take effect on the spot (see the
|
|
37490
|
+
// reposition effect below) rather than only on the next opening.
|
|
37491
|
+
const positionPropsRef = useRef(null);
|
|
37492
|
+
positionPropsRef.current = {
|
|
37493
|
+
positionArea,
|
|
37494
|
+
marginWithContainer
|
|
37495
|
+
};
|
|
37496
|
+
const repositionRef = useRef(null);
|
|
37497
|
+
useEffect(() => {
|
|
37498
|
+
repositionRef.current?.(new CustomEvent("position_props_change", {
|
|
37499
|
+
detail: {}
|
|
37500
|
+
}));
|
|
37501
|
+
}, [positionArea, marginWithContainer]);
|
|
37352
37502
|
const positionAreaParseResult = parsePositionArea(positionArea);
|
|
37353
37503
|
if (!positionAreaParseResult) {
|
|
37354
37504
|
console.warn(`Dialog: invalid positionArea="${positionArea}"`);
|
|
@@ -37492,10 +37642,27 @@ const useDialogProps = props => {
|
|
|
37492
37642
|
// custom renderer. applyNewPosition sets --container-position-remaining-height/-width
|
|
37493
37643
|
// from the result, same as popover.jsx.
|
|
37494
37644
|
const positionDialog = triggerEvent => {
|
|
37645
|
+
const {
|
|
37646
|
+
positionArea,
|
|
37647
|
+
marginWithContainer
|
|
37648
|
+
} = positionPropsRef.current;
|
|
37649
|
+
let marginWithContainerInPixels = resolveSpacingSize(marginWithContainer);
|
|
37650
|
+
if (typeof marginWithContainerInPixels !== "number") {
|
|
37651
|
+
// A value only CSS could evaluate (a spacing token resolving to a var(),
|
|
37652
|
+
// a percentage…) — the placement below needs a real number, and letting
|
|
37653
|
+
// it through would put the dialog at NaN.
|
|
37654
|
+
console.warn(`Dialog: marginWithContainer="${marginWithContainer}" cannot be resolved to pixels. Use a number or a viewport length ("3vvw", "2vvh").`);
|
|
37655
|
+
marginWithContainerInPixels = 0;
|
|
37656
|
+
}
|
|
37657
|
+
// The size caps read the same gap in CSS as the placement below applies
|
|
37658
|
+
// in pixels, so a docked dialog and a centered one keep the same
|
|
37659
|
+
// distance from the edges. Written resolved (not as the raw prop) so a
|
|
37660
|
+
// spacing token stays valid inside the caps' own calc().
|
|
37661
|
+
dialogEl.style.setProperty("--x-dialog-viewport-spacing", `${marginWithContainerInPixels}px`);
|
|
37495
37662
|
const pickOptions = {
|
|
37496
37663
|
positionArea,
|
|
37497
37664
|
container: positionedAncestor,
|
|
37498
|
-
marginWithContainer:
|
|
37665
|
+
marginWithContainer: marginWithContainerInPixels,
|
|
37499
37666
|
event: triggerEvent
|
|
37500
37667
|
};
|
|
37501
37668
|
let position = pickPositionRelativeTo(dialogEl, null, pickOptions);
|
|
@@ -37541,7 +37708,19 @@ const useDialogProps = props => {
|
|
|
37541
37708
|
skipElementResize: true
|
|
37542
37709
|
});
|
|
37543
37710
|
rectEffect.observeSize(dialogEl);
|
|
37711
|
+
// Exposed for the placement-props effect below, which needs to re-place an
|
|
37712
|
+
// already-open dialog.
|
|
37713
|
+
repositionRef.current = repositionEvent => {
|
|
37714
|
+
// data-position-*-current pins an open dialog to the side it first
|
|
37715
|
+
// resolved to, so a resize never makes it jump (pickPositionRelativeTo
|
|
37716
|
+
// reads it back and prefers it over the requested area). A new placement
|
|
37717
|
+
// request is precisely the case where that memory must not win.
|
|
37718
|
+
dialogEl.removeAttribute("data-position-x-current");
|
|
37719
|
+
dialogEl.removeAttribute("data-position-y-current");
|
|
37720
|
+
positionDialog(repositionEvent);
|
|
37721
|
+
};
|
|
37544
37722
|
addCleanup(() => {
|
|
37723
|
+
repositionRef.current = null;
|
|
37545
37724
|
rectEffect.disconnect();
|
|
37546
37725
|
});
|
|
37547
37726
|
// A descendant anchored to something inside this dialog (a Callout, a
|
|
@@ -37833,7 +38012,10 @@ let openLocalPopoverCount = 0;
|
|
|
37833
38012
|
const css$u = /* css */`
|
|
37834
38013
|
@layer navi {
|
|
37835
38014
|
.navi_popover {
|
|
37836
|
-
|
|
38015
|
+
/* soft: user-configurable preferred max-height. Kept as a *default*
|
|
38016
|
+
rather than a value so an outer component can bridge its own prop into
|
|
38017
|
+
--popover-max-height without having to restate 300px (see picker). */
|
|
38018
|
+
--popover-max-height-default: 300px;
|
|
37837
38019
|
--popover-maxmax-height: calc(0.95 * var(--navi-vvh));
|
|
37838
38020
|
--popover-maxmax-width: calc(0.95 * var(--navi-vvw));
|
|
37839
38021
|
|
|
@@ -37884,7 +38066,7 @@ const css$u = /* css */`
|
|
|
37884
38066
|
var(--popover-maxmax-width)
|
|
37885
38067
|
);
|
|
37886
38068
|
--x-popover-max-height: min(
|
|
37887
|
-
var(--popover-max-height),
|
|
38069
|
+
var(--popover-max-height, var(--popover-max-height-default)),
|
|
37888
38070
|
var(--container-position-remaining-height, var(--popover-maxmax-height)),
|
|
37889
38071
|
var(--popover-maxmax-height)
|
|
37890
38072
|
);
|
|
@@ -38313,6 +38495,24 @@ const usePopoverProps = props => {
|
|
|
38313
38495
|
// (see resolveAutoAnimationKind).
|
|
38314
38496
|
const isAutoAnimation = animation === true || animation === "auto";
|
|
38315
38497
|
const hasBackdrop = pointerInteractionOutsideEffect !== "none";
|
|
38498
|
+
// positionPopover lives in openEffect's closure — created once, when the
|
|
38499
|
+
// popover opens. Reading the placement props through a ref instead of that
|
|
38500
|
+
// closure is what lets a change while open take effect on the spot (see the
|
|
38501
|
+
// reposition effect below) rather than only on the next opening.
|
|
38502
|
+
const positionPropsRef = useRef(null);
|
|
38503
|
+
positionPropsRef.current = {
|
|
38504
|
+
positionArea,
|
|
38505
|
+
positionAreaFixed,
|
|
38506
|
+
positionAreaWhenAnchorIsInvalid,
|
|
38507
|
+
marginWithAnchor,
|
|
38508
|
+
marginWithContainer
|
|
38509
|
+
};
|
|
38510
|
+
const repositionRef = useRef(null);
|
|
38511
|
+
useEffect(() => {
|
|
38512
|
+
repositionRef.current?.(new CustomEvent("position_props_change", {
|
|
38513
|
+
detail: {}
|
|
38514
|
+
}));
|
|
38515
|
+
}, [positionArea, positionAreaFixed, positionAreaWhenAnchorIsInvalid, marginWithAnchor, marginWithContainer]);
|
|
38316
38516
|
// The custom renderer's own starting-hidden state is a stylesheet default
|
|
38317
38517
|
// now (&:not([popover]) { display: none } on .navi_popover/
|
|
38318
38518
|
// .navi_popover_backdrop above) rather than set here imperatively — a
|
|
@@ -38509,6 +38709,13 @@ const usePopoverProps = props => {
|
|
|
38509
38709
|
// via-attribute renderer (see its own computation above).
|
|
38510
38710
|
const effectiveAnchor = hasAnchorElement ? anchorElement : positionedAncestor;
|
|
38511
38711
|
const positionPopover = positionEvent => {
|
|
38712
|
+
const {
|
|
38713
|
+
positionArea,
|
|
38714
|
+
positionAreaFixed,
|
|
38715
|
+
positionAreaWhenAnchorIsInvalid,
|
|
38716
|
+
marginWithAnchor,
|
|
38717
|
+
marginWithContainer
|
|
38718
|
+
} = positionPropsRef.current;
|
|
38512
38719
|
let position;
|
|
38513
38720
|
if (hasAnchorElement) {
|
|
38514
38721
|
const {
|
|
@@ -38676,6 +38883,21 @@ const usePopoverProps = props => {
|
|
|
38676
38883
|
// while open (e.g. an expand/collapse toggle inside it) — not just when
|
|
38677
38884
|
// the anchor itself moves/resizes/re-anchors.
|
|
38678
38885
|
rectEffect.observeSize(popoverEl);
|
|
38886
|
+
// Exposed for the placement-props effect above, which needs to re-place an
|
|
38887
|
+
// already-open popover.
|
|
38888
|
+
repositionRef.current = repositionEvent => {
|
|
38889
|
+
// data-position-*-current pins an open popover to the side it first
|
|
38890
|
+
// resolved to, so scrolling or a content resize never makes it jump
|
|
38891
|
+
// (pickPositionRelativeTo reads it back and prefers it over the
|
|
38892
|
+
// requested area). A new placement request is precisely the case where
|
|
38893
|
+
// that memory must not win — drop it before re-resolving.
|
|
38894
|
+
popoverEl.removeAttribute("data-position-x-current");
|
|
38895
|
+
popoverEl.removeAttribute("data-position-y-current");
|
|
38896
|
+
positionPopover(repositionEvent);
|
|
38897
|
+
};
|
|
38898
|
+
addCleanup(() => {
|
|
38899
|
+
repositionRef.current = null;
|
|
38900
|
+
});
|
|
38679
38901
|
// A descendant anchored to something inside this popover (a Callout, a
|
|
38680
38902
|
// further-nested Popover) needing to know about this popover's own
|
|
38681
38903
|
// left/top repositioning transition — not just that the target changed
|
|
@@ -39002,12 +39224,12 @@ const resolvePositionAreaAndAnimationKind = ({
|
|
|
39002
39224
|
installImportMetaCssBuild(import.meta);/**
|
|
39003
39225
|
* A lightweight version of picker_custom.jsx's own Popover/Dialog switch —
|
|
39004
39226
|
* no picker concepts (value/action tracking, keyboard letter/arrow-to-open
|
|
39005
|
-
* shortcuts, history-driven expanded state
|
|
39227
|
+
* shortcuts, history-driven expanded state):
|
|
39006
39228
|
* just picks between rendering a Popover or a Dialog and applies the shared
|
|
39007
39229
|
* "popup box" look (padding, background, border-radius, box-shadow) to
|
|
39008
39230
|
* whichever one it renders.
|
|
39009
39231
|
*
|
|
39010
|
-
* Mode resolution (`
|
|
39232
|
+
* Mode resolution (`useResolvedPopupMode`, popup_mode.jsx) is shared with picker_custom.jsx,
|
|
39011
39233
|
* not just mirrored — the picker needs the resolved mode itself (for its own
|
|
39012
39234
|
* mode-dependent history/ARIA handling), not just to pick which of Popover/
|
|
39013
39235
|
* Dialog to render, so it calls the same hook directly instead of
|
|
@@ -39150,7 +39372,12 @@ const Popup = props => {
|
|
|
39150
39372
|
positionAreaFixed,
|
|
39151
39373
|
...rest
|
|
39152
39374
|
} = props;
|
|
39153
|
-
const [mode] =
|
|
39375
|
+
const [mode] = useResolvedPopupMode(modeProp, maxWidth);
|
|
39376
|
+
// So the content can lay itself out per mode — see usePopupMode.
|
|
39377
|
+
const childrenWithMode = jsx(PopupModeContext.Provider, {
|
|
39378
|
+
value: mode,
|
|
39379
|
+
children: children
|
|
39380
|
+
});
|
|
39154
39381
|
if (mode === "dialog") {
|
|
39155
39382
|
const expandXResolved = expand || expandX;
|
|
39156
39383
|
const expandYResolved = expand || expandY;
|
|
@@ -39161,7 +39388,7 @@ const Popup = props => {
|
|
|
39161
39388
|
className: withPropsClassName("navi_popup", className),
|
|
39162
39389
|
"data-expand-x": expandXResolved ? "" : undefined,
|
|
39163
39390
|
"data-expand-y": expandYResolved ? "" : undefined,
|
|
39164
|
-
children:
|
|
39391
|
+
children: childrenWithMode
|
|
39165
39392
|
});
|
|
39166
39393
|
}
|
|
39167
39394
|
return jsx(Popover, {
|
|
@@ -39173,54 +39400,10 @@ const Popup = props => {
|
|
|
39173
39400
|
focusCapture: focusCapture,
|
|
39174
39401
|
positionAreaFixed: positionAreaFixed,
|
|
39175
39402
|
className: withPropsClassName("navi_popup", className),
|
|
39176
|
-
children:
|
|
39403
|
+
children: childrenWithMode
|
|
39177
39404
|
});
|
|
39178
39405
|
};
|
|
39179
39406
|
|
|
39180
|
-
/**
|
|
39181
|
-
* Frozen for the component instance's lifetime — mirrors `Popup`'s own
|
|
39182
|
-
* mode-resolution timing (a screen resize while already mounted doesn't
|
|
39183
|
-
* switch between Popover and Dialog mid-session).
|
|
39184
|
-
*
|
|
39185
|
-
* @param {"dialog"|"popover"} [modeProp]
|
|
39186
|
-
* @param {string} [maxWidth]
|
|
39187
|
-
* @returns {["dialog"|"popover", () => void]} The resolved mode, and a
|
|
39188
|
-
* `resetMode` function a caller can call (e.g. on close) to force the
|
|
39189
|
-
* *next* call to re-resolve from scratch instead of keeping the frozen
|
|
39190
|
-
* value — `Popup` itself never needs this (it has no notion of
|
|
39191
|
-
* open/close of its own), `picker_custom.jsx` does (re-evaluates screen
|
|
39192
|
-
* size on every fresh open).
|
|
39193
|
-
*/
|
|
39194
|
-
const usePopupMode = (modeProp, maxWidth) => {
|
|
39195
|
-
const defaultModeRef = useRef(null);
|
|
39196
|
-
if (defaultModeRef.current === null) {
|
|
39197
|
-
defaultModeRef.current = resolvePopupMode(modeProp, maxWidth);
|
|
39198
|
-
}
|
|
39199
|
-
const resetMode = () => {
|
|
39200
|
-
defaultModeRef.current = null;
|
|
39201
|
-
};
|
|
39202
|
-
return [defaultModeRef.current, resetMode];
|
|
39203
|
-
};
|
|
39204
|
-
/**
|
|
39205
|
-
* Same small-screen/`maxWidth`-compact heuristic `Popup` uses internally,
|
|
39206
|
-
* exported so `picker_custom.jsx` (which needs the resolved mode itself,
|
|
39207
|
-
* for its own mode-dependent history/ARIA handling — not just to pick which
|
|
39208
|
-
* of Popover/Dialog to render, the way `Popup` only ever needs it) doesn't
|
|
39209
|
-
* have to duplicate it.
|
|
39210
|
-
*
|
|
39211
|
-
* @param {"dialog"|"popover"} [modeProp] - Forces one mode; `undefined` to
|
|
39212
|
-
* resolve automatically.
|
|
39213
|
-
* @param {string} [maxWidth] - A small enough value is treated as
|
|
39214
|
-
* "compact", staying a popover even on a small screen.
|
|
39215
|
-
* @returns {"dialog"|"popover"}
|
|
39216
|
-
*/
|
|
39217
|
-
const resolvePopupMode = (modeProp, maxWidth) => {
|
|
39218
|
-
const isSmallScreen = windowWidthSignal.peek() <= 600;
|
|
39219
|
-
const maxWidthPx = parseFloat(maxWidth);
|
|
39220
|
-
const isCompact = isFinite(maxWidthPx) && maxWidthPx < 150;
|
|
39221
|
-
return modeProp ?? (isSmallScreen && !isCompact ? "dialog" : "popover");
|
|
39222
|
-
};
|
|
39223
|
-
|
|
39224
39407
|
installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
39225
39408
|
.navi_picker {
|
|
39226
39409
|
/* Sizing ceilings (maxmax), background, box-shadow, outline, padding,
|
|
@@ -39228,8 +39411,7 @@ installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
|
39228
39411
|
themselves — nothing to redefine here. Only the picker's own look
|
|
39229
39412
|
(border color/radius/width, background) needs bridging into the vars
|
|
39230
39413
|
Popover/Dialog actually consume, plus a couple of genuinely
|
|
39231
|
-
picker-specific bits below (anchor-width min-width, the
|
|
39232
|
-
the nested list). */
|
|
39414
|
+
picker-specific bits below (anchor-width min-width, the nested list). */
|
|
39233
39415
|
|
|
39234
39416
|
/* popover */
|
|
39235
39417
|
&[aria-haspopup="listbox"] {
|
|
@@ -39240,52 +39422,17 @@ installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
|
39240
39422
|
--popover-background-color: var(--picker-background-color);
|
|
39241
39423
|
--popover-outline-width: var(--picker-outline-width);
|
|
39242
39424
|
--popover-outline-color: var(--picker-outline-color);
|
|
39425
|
+
/* No fallback on purpose: when the picker's own popoverMaxHeight prop
|
|
39426
|
+
is unset this declaration is invalid at computed-value time, which
|
|
39427
|
+
leaves --popover-max-height unset and lets the popover fall back to
|
|
39428
|
+
--popover-max-height-default. */
|
|
39429
|
+
--popover-max-height: var(--picker-popover-max-height);
|
|
39243
39430
|
|
|
39244
39431
|
/* At least as wide as the trigger — unless popupWidthFitContent, then
|
|
39245
39432
|
let the content (e.g. a Wheel) size the popover (see picker.jsx). */
|
|
39246
39433
|
min-width: var(--picker-popover-min-width, var(--anchor-width, 0px));
|
|
39247
39434
|
cursor: default; /* Reset pointer cursor within the select */
|
|
39248
39435
|
|
|
39249
|
-
/* The anchor placeholder is a non-interactive visual clone of the
|
|
39250
|
-
trigger. It makes the popover wrap both the trigger area and the list
|
|
39251
|
-
under a single border/shadow. CSS order places it before the list
|
|
39252
|
-
when the popover is below the trigger, and after when above. */
|
|
39253
|
-
.navi_picker_anchor_clone {
|
|
39254
|
-
display: flex;
|
|
39255
|
-
/* To make clone same height as original we need to force it because context can impact height */
|
|
39256
|
-
/* Like siblings with a bigger height in a flex container */
|
|
39257
|
-
/* We subtract the border sizes as anchor-height includes borders in the dimensions */
|
|
39258
|
-
min-height: var(--anchor-inner-height);
|
|
39259
|
-
/* Mirror the trigger's padding so the clone looks identical */
|
|
39260
|
-
padding-top: var(--x-picker-padding-top);
|
|
39261
|
-
padding-right: var(--x-picker-padding-right);
|
|
39262
|
-
padding-bottom: var(--x-picker-padding-bottom);
|
|
39263
|
-
padding-left: var(--x-picker-padding-left);
|
|
39264
|
-
flex-shrink: 0;
|
|
39265
|
-
flex-direction: column;
|
|
39266
|
-
justify-content: center;
|
|
39267
|
-
gap: var(--navi-s);
|
|
39268
|
-
order: -1; /* before the list — popover is below the trigger */
|
|
39269
|
-
background: var(--x-picker-background-color);
|
|
39270
|
-
border-bottom: var(--picker-border-width) solid
|
|
39271
|
-
var(--x-picker-border-color);
|
|
39272
|
-
|
|
39273
|
-
&:hover {
|
|
39274
|
-
--x-picker-background-color: var(--picker-background-color-hover);
|
|
39275
|
-
--x-picker-border-color: var(--picker-border-color-hover);
|
|
39276
|
-
}
|
|
39277
|
-
}
|
|
39278
|
-
|
|
39279
|
-
&[data-position-y-current="top"],
|
|
39280
|
-
&[data-position-y-current="inset-bottom"] {
|
|
39281
|
-
.navi_picker_anchor_clone {
|
|
39282
|
-
order: 1; /* after the list — popover is above the trigger */
|
|
39283
|
-
border-top: var(--picker-border-width) solid
|
|
39284
|
-
var(--x-picker-border-color);
|
|
39285
|
-
border-bottom: none;
|
|
39286
|
-
}
|
|
39287
|
-
}
|
|
39288
|
-
|
|
39289
39436
|
/* The list scrolls inside the popover */
|
|
39290
39437
|
.navi_list_container {
|
|
39291
39438
|
width: 100%;
|
|
@@ -39299,16 +39446,15 @@ installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
|
39299
39446
|
}
|
|
39300
39447
|
|
|
39301
39448
|
&[aria-expanded="true"] {
|
|
39302
|
-
&[navi-popover-mode="overlay"]
|
|
39303
|
-
&[navi-popover-mode="attached"] {
|
|
39449
|
+
&[navi-popover-mode="overlay"] {
|
|
39304
39450
|
/* When sizes uses float AND the border uses border-radius it's possible it's possible to see some pixels
|
|
39305
39451
|
of the underlying select borders. We hide them to ensure this cannot happen. */
|
|
39306
39452
|
border-color: transparent;
|
|
39307
39453
|
}
|
|
39308
39454
|
|
|
39309
39455
|
/* Popover itself has no opinion on its content's own layout (plain
|
|
39310
|
-
div, block by default) — the picker's content
|
|
39311
|
-
|
|
39456
|
+
div, block by default) — the picker's content needs to stack
|
|
39457
|
+
vertically. */
|
|
39312
39458
|
.navi_popover {
|
|
39313
39459
|
display: flex;
|
|
39314
39460
|
flex-direction: column;
|
|
@@ -39456,11 +39602,11 @@ const PickerCustom = props => {
|
|
|
39456
39602
|
const controlId = useContext(ControlIdContext);
|
|
39457
39603
|
props.id = props.id || controlId || idDefault;
|
|
39458
39604
|
// Same small-screen/maxWidth-compact heuristic Popup itself uses (see
|
|
39459
|
-
//
|
|
39605
|
+
// popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
|
|
39460
39606
|
// (computed when closed, stable while open, so a screen resize mid-session
|
|
39461
39607
|
// doesn't switch between Popover and Dialog), with resetMode called from
|
|
39462
39608
|
// this picker's own onClose below to re-evaluate on the *next* open.
|
|
39463
|
-
const [mode, resetMode] =
|
|
39609
|
+
const [mode, resetMode] = useResolvedPopupMode(modeProp, props.maxWidth);
|
|
39464
39610
|
const pickerProps = {
|
|
39465
39611
|
...props
|
|
39466
39612
|
};
|
|
@@ -39801,6 +39947,7 @@ const PickerContentInsidePopup = props => {
|
|
|
39801
39947
|
// defaulting the now-correctly-named prop to `true` would be a real,
|
|
39802
39948
|
// unintended behavior change riding along with the rename.
|
|
39803
39949
|
focusCapture,
|
|
39950
|
+
positionArea,
|
|
39804
39951
|
popoverMode = "nearby",
|
|
39805
39952
|
popoverSpacing = popoverMode === "nearby" ? 5 : 0,
|
|
39806
39953
|
marginWithContainer = 10,
|
|
@@ -39846,11 +39993,11 @@ const PickerContentInsidePopup = props => {
|
|
|
39846
39993
|
}
|
|
39847
39994
|
});
|
|
39848
39995
|
},
|
|
39849
|
-
children:
|
|
39996
|
+
children: jsx(Popup, {
|
|
39850
39997
|
...popupProps,
|
|
39851
39998
|
mode: mode,
|
|
39852
39999
|
animation: animation,
|
|
39853
|
-
positionArea: isPopover ? popoverMode === "nearby" ? "bottom-start" : "inset(top-left)" :
|
|
40000
|
+
positionArea: isPopover ? positionArea ?? (popoverMode === "nearby" ? "bottom-start" : "inset(top-left)") : positionArea,
|
|
39854
40001
|
marginWithAnchor: isPopover ? popoverSpacing : undefined,
|
|
39855
40002
|
marginWithContainer: isPopover ? marginWithContainer : undefined,
|
|
39856
40003
|
scrollCapture: scrollCapture === "dialog" ? !isPopover : scrollCapture === "popover" ? isPopover : scrollCapture,
|
|
@@ -39858,21 +40005,10 @@ const PickerContentInsidePopup = props => {
|
|
|
39858
40005
|
focusCapture: isPopover ? focusCapture : undefined,
|
|
39859
40006
|
expandX: !isPopover ? expandX : undefined,
|
|
39860
40007
|
expandY: !isPopover ? expandY : undefined,
|
|
39861
|
-
children:
|
|
39862
|
-
className: "navi_picker_anchor_clone",
|
|
39863
|
-
onMouseDown: e => {
|
|
39864
|
-
if (e.button !== 0) {
|
|
39865
|
-
return;
|
|
39866
|
-
}
|
|
39867
|
-
popupProps.openController.requestClose(e, {
|
|
39868
|
-
isCancel: true
|
|
39869
|
-
});
|
|
39870
|
-
},
|
|
39871
|
-
children: props.trigger
|
|
39872
|
-
}) : null, jsx(PickerModeContext.Provider, {
|
|
40008
|
+
children: jsx(PopupModeContext.Provider, {
|
|
39873
40009
|
value: mode,
|
|
39874
40010
|
children: children
|
|
39875
|
-
})
|
|
40011
|
+
})
|
|
39876
40012
|
})
|
|
39877
40013
|
});
|
|
39878
40014
|
};
|
|
@@ -40512,6 +40648,84 @@ const toDate = (value, parseString) => {
|
|
|
40512
40648
|
return null;
|
|
40513
40649
|
};
|
|
40514
40650
|
|
|
40651
|
+
const LoadingDotsSvg = () => {
|
|
40652
|
+
return jsxs("svg", {
|
|
40653
|
+
viewBox: "0 0 200 200",
|
|
40654
|
+
width: "100%",
|
|
40655
|
+
height: "100%",
|
|
40656
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
40657
|
+
children: [jsx("rect", {
|
|
40658
|
+
fill: "currentColor",
|
|
40659
|
+
stroke: "currentColor",
|
|
40660
|
+
"stroke-width": "15",
|
|
40661
|
+
width: "30",
|
|
40662
|
+
height: "30",
|
|
40663
|
+
x: "25",
|
|
40664
|
+
y: "85",
|
|
40665
|
+
children: jsx("animate", {
|
|
40666
|
+
attributeName: "opacity",
|
|
40667
|
+
calcMode: "spline",
|
|
40668
|
+
dur: "2",
|
|
40669
|
+
values: "1;0;1;",
|
|
40670
|
+
keySplines: ".5 0 .5 1;.5 0 .5 1",
|
|
40671
|
+
repeatCount: "indefinite",
|
|
40672
|
+
begin: "-.4"
|
|
40673
|
+
})
|
|
40674
|
+
}), jsx("rect", {
|
|
40675
|
+
fill: "currentColor",
|
|
40676
|
+
stroke: "currentColor",
|
|
40677
|
+
"stroke-width": "15",
|
|
40678
|
+
width: "30",
|
|
40679
|
+
height: "30",
|
|
40680
|
+
x: "85",
|
|
40681
|
+
y: "85",
|
|
40682
|
+
children: jsx("animate", {
|
|
40683
|
+
attributeName: "opacity",
|
|
40684
|
+
calcMode: "spline",
|
|
40685
|
+
dur: "2",
|
|
40686
|
+
values: "1;0;1;",
|
|
40687
|
+
keySplines: ".5 0 .5 1;.5 0 .5 1",
|
|
40688
|
+
repeatCount: "indefinite",
|
|
40689
|
+
begin: "-.2"
|
|
40690
|
+
})
|
|
40691
|
+
}), jsx("rect", {
|
|
40692
|
+
fill: "currentColor",
|
|
40693
|
+
stroke: "currentColor",
|
|
40694
|
+
"stroke-width": "15",
|
|
40695
|
+
width: "30",
|
|
40696
|
+
height: "30",
|
|
40697
|
+
x: "145",
|
|
40698
|
+
y: "85",
|
|
40699
|
+
children: jsx("animate", {
|
|
40700
|
+
attributeName: "opacity",
|
|
40701
|
+
calcMode: "spline",
|
|
40702
|
+
dur: "2",
|
|
40703
|
+
values: "1;0;1;",
|
|
40704
|
+
keySplines: ".5 0 .5 1;.5 0 .5 1",
|
|
40705
|
+
repeatCount: "indefinite",
|
|
40706
|
+
begin: "0"
|
|
40707
|
+
})
|
|
40708
|
+
})]
|
|
40709
|
+
});
|
|
40710
|
+
};
|
|
40711
|
+
|
|
40712
|
+
const LoadingIndicator = ({
|
|
40713
|
+
variant = "circle",
|
|
40714
|
+
...props
|
|
40715
|
+
}) => {
|
|
40716
|
+
if (variant === "dots") {
|
|
40717
|
+
return jsx(Icon, {
|
|
40718
|
+
...props,
|
|
40719
|
+
children: jsx(LoadingDotsSvg, {})
|
|
40720
|
+
});
|
|
40721
|
+
}
|
|
40722
|
+
return jsx(Icon, {
|
|
40723
|
+
circle: true,
|
|
40724
|
+
...props,
|
|
40725
|
+
children: jsx(LoadingIndicatorFluid, {})
|
|
40726
|
+
});
|
|
40727
|
+
};
|
|
40728
|
+
|
|
40515
40729
|
installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
40516
40730
|
@layer navi {
|
|
40517
40731
|
.navi_separator {
|
|
@@ -41266,6 +41480,11 @@ const ListSelectable = props => {
|
|
|
41266
41480
|
"navi-has-selected-background": selectedIndicator === "backgroundColor" ? "" : undefined,
|
|
41267
41481
|
...listControlRootProps,
|
|
41268
41482
|
...listControlProps,
|
|
41483
|
+
// "loading" is a control prop, so useControlgroupProps consumes it (into
|
|
41484
|
+
// aria-busy / the :-navi-loading pseudo state) and it does not survive
|
|
41485
|
+
// into the props below. ListUI needs it too — it is what makes the list
|
|
41486
|
+
// render skeleton rows instead of its (not yet known) items.
|
|
41487
|
+
loading: props.loading,
|
|
41269
41488
|
name: undefined,
|
|
41270
41489
|
value: undefined,
|
|
41271
41490
|
defaultValue: undefined,
|
|
@@ -41789,6 +42008,16 @@ const css$p = /* css */`
|
|
|
41789
42008
|
}
|
|
41790
42009
|
&[data-expand-y] {
|
|
41791
42010
|
--list-max-height: none;
|
|
42011
|
+
|
|
42012
|
+
/* expandY grows the container to fill its parent (flex-grow, applied by
|
|
42013
|
+
Box). The scroll container must then fill that grown height and take
|
|
42014
|
+
over the internal scroll — flex:1 fills it, min-height:0 lets it shrink
|
|
42015
|
+
below its content so overflow:auto scrolls instead of the content
|
|
42016
|
+
pushing past the container (which overflow:hidden would just clip). */
|
|
42017
|
+
.navi_list_scroll_container {
|
|
42018
|
+
min-height: 0;
|
|
42019
|
+
flex: 1;
|
|
42020
|
+
}
|
|
41792
42021
|
}
|
|
41793
42022
|
&[navi-nothing-to-display] {
|
|
41794
42023
|
display: none;
|
|
@@ -41891,7 +42120,11 @@ const css$p = /* css */`
|
|
|
41891
42120
|
scroll-margin-bottom: var(--x-list-scroll-spacing-bottom);
|
|
41892
42121
|
scroll-margin-left: var(--x-list-scroll-spacing-left);
|
|
41893
42122
|
|
|
41894
|
-
|
|
42123
|
+
/* The "invisible_and_inert" search no-match mode keeps items in the DOM
|
|
42124
|
+
(to preserve layout) but hides them — it sets BOTH aria-hidden and inert.
|
|
42125
|
+
Scope to that pair so the presentation placeholders that are only
|
|
42126
|
+
aria-hidden (skeleton rows, the loader) stay visible. */
|
|
42127
|
+
&[aria-hidden="true"][inert] {
|
|
41895
42128
|
opacity: 0;
|
|
41896
42129
|
}
|
|
41897
42130
|
|
|
@@ -42000,6 +42233,39 @@ const css$p = /* css */`
|
|
|
42000
42233
|
user-select: none;
|
|
42001
42234
|
}
|
|
42002
42235
|
}
|
|
42236
|
+
/* Loading placeholders (see List's loading / loadingIndicator / skeletonTemplate).
|
|
42237
|
+
A skeleton row reuses <Text loading> for the shimmer bar; the loader row
|
|
42238
|
+
centers a spinner. */
|
|
42239
|
+
.navi_list_item_skeleton {
|
|
42240
|
+
pointer-events: none;
|
|
42241
|
+
}
|
|
42242
|
+
.navi_list_loader {
|
|
42243
|
+
display: flex;
|
|
42244
|
+
padding: 12px;
|
|
42245
|
+
align-items: center;
|
|
42246
|
+
justify-content: center;
|
|
42247
|
+
color: light-dark(#888, #aaa);
|
|
42248
|
+
}
|
|
42249
|
+
/* Error state (List error prop): an inline callout describing why the list
|
|
42250
|
+
failed to load, shown in place of the items. */
|
|
42251
|
+
.navi_list_error {
|
|
42252
|
+
display: flex;
|
|
42253
|
+
margin: 8px;
|
|
42254
|
+
padding: 10px 12px;
|
|
42255
|
+
align-items: flex-start;
|
|
42256
|
+
gap: 8px;
|
|
42257
|
+
color: light-dark(#b91c1c, #fca5a5);
|
|
42258
|
+
font-size: 0.9em;
|
|
42259
|
+
line-height: 1.4;
|
|
42260
|
+
background: light-dark(#fef2f2, rgba(127, 29, 29, 0.25));
|
|
42261
|
+
border: 1px solid light-dark(#fecaca, rgba(248, 113, 113, 0.4));
|
|
42262
|
+
border-radius: 6px;
|
|
42263
|
+
}
|
|
42264
|
+
.navi_list_error_icon {
|
|
42265
|
+
flex: none;
|
|
42266
|
+
font-size: 1em;
|
|
42267
|
+
line-height: 1.4;
|
|
42268
|
+
}
|
|
42003
42269
|
[navi-virtual-filler="after"] {
|
|
42004
42270
|
/* for some reason preact ends up puttin this element before the list items in some scenarios
|
|
42005
42271
|
I've noticed that removing the ItemIndexToScrollOnMountRefContext.Provider
|
|
@@ -42090,6 +42356,11 @@ const ListUI = props => {
|
|
|
42090
42356
|
columns,
|
|
42091
42357
|
searchText,
|
|
42092
42358
|
searchNoMatchMode = "remove",
|
|
42359
|
+
loading,
|
|
42360
|
+
loadingIndicator = "skeleton",
|
|
42361
|
+
loadingSkeletonCount = 3,
|
|
42362
|
+
skeletonTemplate,
|
|
42363
|
+
error,
|
|
42093
42364
|
horizontal,
|
|
42094
42365
|
spacing,
|
|
42095
42366
|
...rest
|
|
@@ -42161,9 +42432,63 @@ const ListUI = props => {
|
|
|
42161
42432
|
const noMatchCount = tracker.noMatchCountSignal.value;
|
|
42162
42433
|
const itemCount = tracker.countSignal.value;
|
|
42163
42434
|
const allNoMatch = noMatchCount > 0 && noMatchCount === itemCount;
|
|
42435
|
+
const searching = Boolean(searchText);
|
|
42164
42436
|
const fallbackDisabled = fallback !== undefined && !fallback;
|
|
42165
42437
|
const searchFallbackDisabled = searchFallback !== undefined && !searchFallback;
|
|
42166
|
-
|
|
42438
|
+
// No item is visible when the list is empty (filtering may happen outside the
|
|
42439
|
+
// list, dropping itemCount to 0) or when a search removed them all — only the
|
|
42440
|
+
// "remove" mode empties the view; "muted"/"below"/… keep items on screen.
|
|
42441
|
+
const noVisibleItems = itemCount === 0 || allNoMatch && searchNoMatchMode === "remove";
|
|
42442
|
+
// Which fallback message actually renders (mirrors SearchFallback / Fallback
|
|
42443
|
+
// below): during a search an empty/no-match result is a "no match" state (the
|
|
42444
|
+
// search fallback), otherwise an empty list is the "empty" state.
|
|
42445
|
+
const searchFallbackShown = (allNoMatch || searching && itemCount === 0) && !searchFallbackDisabled;
|
|
42446
|
+
const emptyFallbackShown = !searching && itemCount === 0 && !fallbackDisabled;
|
|
42447
|
+
// Hide the whole list — border included — when there is genuinely nothing to
|
|
42448
|
+
// show: no visible items AND no fallback message. Never while loading or in
|
|
42449
|
+
// error (the placeholder / error message ARE the content to display).
|
|
42450
|
+
const nothingToDisplay = !loading && !error && noVisibleItems && !searchFallbackShown && !emptyFallbackShown;
|
|
42451
|
+
|
|
42452
|
+
// Placeholder content replaces the real children: an error message when the
|
|
42453
|
+
// load failed (takes precedence), otherwise — while loading — skeleton rows
|
|
42454
|
+
// (default, count loadingSkeletonCount, look skeletonTemplate) or a single
|
|
42455
|
+
// centered loader when loadingIndicator="loader".
|
|
42456
|
+
let content = children;
|
|
42457
|
+
if (error) {
|
|
42458
|
+
content = jsxs(ListItem, {
|
|
42459
|
+
role: "presentation",
|
|
42460
|
+
baseClassName: "navi_list_item navi_list_error",
|
|
42461
|
+
children: [jsx("span", {
|
|
42462
|
+
className: "navi_list_error_icon",
|
|
42463
|
+
"aria-hidden": "true",
|
|
42464
|
+
children: "\u26A0"
|
|
42465
|
+
}), jsx("span", {
|
|
42466
|
+
children: error === true ? "Something went wrong." : error
|
|
42467
|
+
})]
|
|
42468
|
+
});
|
|
42469
|
+
} else if (loading) {
|
|
42470
|
+
if (loadingIndicator === "loader") {
|
|
42471
|
+
content = jsx(ListItem, {
|
|
42472
|
+
role: "presentation",
|
|
42473
|
+
"aria-hidden": "true",
|
|
42474
|
+
baseClassName: "navi_list_item navi_list_loader",
|
|
42475
|
+
children: jsx(LoadingIndicator, {})
|
|
42476
|
+
});
|
|
42477
|
+
} else {
|
|
42478
|
+
const template = skeletonTemplate ?? jsx(ListItem, {
|
|
42479
|
+
skeleton: true
|
|
42480
|
+
});
|
|
42481
|
+
const skeletons = [];
|
|
42482
|
+
let skeletonIndex = 0;
|
|
42483
|
+
while (skeletonIndex < loadingSkeletonCount) {
|
|
42484
|
+
skeletons.push(cloneElement(template, {
|
|
42485
|
+
key: `navi-list-skeleton-${skeletonIndex}`
|
|
42486
|
+
}));
|
|
42487
|
+
skeletonIndex++;
|
|
42488
|
+
}
|
|
42489
|
+
content = skeletons;
|
|
42490
|
+
}
|
|
42491
|
+
}
|
|
42167
42492
|
return jsx(Box, {
|
|
42168
42493
|
...rest,
|
|
42169
42494
|
ref: ref,
|
|
@@ -42177,6 +42502,8 @@ const ListUI = props => {
|
|
|
42177
42502
|
expand: expand,
|
|
42178
42503
|
"navi-zero-match": allNoMatch ? "" : undefined,
|
|
42179
42504
|
"navi-nothing-to-display": nothingToDisplay ? "" : undefined,
|
|
42505
|
+
"navi-loading": loading ? "" : undefined,
|
|
42506
|
+
"navi-error": error ? "" : undefined,
|
|
42180
42507
|
styleCSSVars: LIST_STYLE_CSS_VARS,
|
|
42181
42508
|
pseudoClasses: LIST_PSEUDO_CLASSES,
|
|
42182
42509
|
hasChildUsingForwardedProps: true,
|
|
@@ -42198,6 +42525,9 @@ const ListUI = props => {
|
|
|
42198
42525
|
role: role,
|
|
42199
42526
|
fallback: fallback,
|
|
42200
42527
|
searchFallback: searchFallback,
|
|
42528
|
+
searching: searching,
|
|
42529
|
+
loading: loading,
|
|
42530
|
+
error: error,
|
|
42201
42531
|
searchNoMatchMode: searchNoMatchMode,
|
|
42202
42532
|
separator: separator,
|
|
42203
42533
|
expandX: expandX || expand,
|
|
@@ -42208,7 +42538,7 @@ const ListUI = props => {
|
|
|
42208
42538
|
renderWindow: renderWindow,
|
|
42209
42539
|
virtualItemSizeSignal: virtualItemSizeSignal,
|
|
42210
42540
|
pendingScrollRef: pendingScrollRef,
|
|
42211
|
-
children:
|
|
42541
|
+
children: content
|
|
42212
42542
|
})
|
|
42213
42543
|
});
|
|
42214
42544
|
};
|
|
@@ -42238,6 +42568,11 @@ const ListFirstResolver = props => {
|
|
|
42238
42568
|
* searchFallback?: import("ignore:preact").ComponentChildren,
|
|
42239
42569
|
* searchText?: string,
|
|
42240
42570
|
* searchNoMatchMode?: "remove" | "invisible_and_inert" | "muted" | "below",
|
|
42571
|
+
* loading?: boolean,
|
|
42572
|
+
* loadingIndicator?: "skeleton" | "loader",
|
|
42573
|
+
* loadingSkeletonCount?: number,
|
|
42574
|
+
* skeletonTemplate?: import("ignore:preact").ComponentChildren,
|
|
42575
|
+
* error?: boolean | import("ignore:preact").ComponentChildren,
|
|
42241
42576
|
* separator?: boolean | import("ignore:preact").ComponentChildren,
|
|
42242
42577
|
* lockSize?: boolean,
|
|
42243
42578
|
* horizontal?: boolean,
|
|
@@ -42255,6 +42590,9 @@ const ListContent = ({
|
|
|
42255
42590
|
role,
|
|
42256
42591
|
fallback,
|
|
42257
42592
|
searchFallback,
|
|
42593
|
+
searching,
|
|
42594
|
+
loading,
|
|
42595
|
+
error,
|
|
42258
42596
|
searchNoMatchMode,
|
|
42259
42597
|
separator,
|
|
42260
42598
|
expandX,
|
|
@@ -42274,6 +42612,9 @@ const ListContent = ({
|
|
|
42274
42612
|
role: role,
|
|
42275
42613
|
fallback: fallback,
|
|
42276
42614
|
searchFallback: searchFallback,
|
|
42615
|
+
searching: searching,
|
|
42616
|
+
loading: loading,
|
|
42617
|
+
error: error,
|
|
42277
42618
|
searchNoMatchMode: searchNoMatchMode,
|
|
42278
42619
|
separator: separator === true ? jsx(Separator, {
|
|
42279
42620
|
margin: "0"
|
|
@@ -42772,6 +43113,9 @@ const UnorderedList = ({
|
|
|
42772
43113
|
virtualItemSizeSignal,
|
|
42773
43114
|
fallback,
|
|
42774
43115
|
searchFallback,
|
|
43116
|
+
searching,
|
|
43117
|
+
loading,
|
|
43118
|
+
error,
|
|
42775
43119
|
searchNoMatchMode,
|
|
42776
43120
|
separator,
|
|
42777
43121
|
horizontal,
|
|
@@ -42780,6 +43124,9 @@ const UnorderedList = ({
|
|
|
42780
43124
|
children,
|
|
42781
43125
|
...rest
|
|
42782
43126
|
}) => {
|
|
43127
|
+
// No empty/no-match message while loading or in error — the placeholder /
|
|
43128
|
+
// error message is the content, even though no items are tracked yet.
|
|
43129
|
+
const suppressFallback = loading || Boolean(error);
|
|
42783
43130
|
return jsxs(Box, {
|
|
42784
43131
|
as: "ul",
|
|
42785
43132
|
flex: columns ? undefined : horizontal ? "x" : "y",
|
|
@@ -42791,11 +43138,13 @@ const UnorderedList = ({
|
|
|
42791
43138
|
children: [jsx(BeforeFiller, {
|
|
42792
43139
|
virtualItemSizeSignal: virtualItemSizeSignal,
|
|
42793
43140
|
renderWindowStart: renderWindow.start
|
|
42794
|
-
}), jsx(SearchFallback, {
|
|
43141
|
+
}), !suppressFallback && jsx(SearchFallback, {
|
|
42795
43142
|
searchFallback: searchFallback,
|
|
43143
|
+
searching: searching,
|
|
42796
43144
|
tracker: tracker
|
|
42797
|
-
}), jsx(Fallback, {
|
|
43145
|
+
}), !suppressFallback && jsx(Fallback, {
|
|
42798
43146
|
fallback: fallback,
|
|
43147
|
+
searching: searching,
|
|
42799
43148
|
tracker: tracker
|
|
42800
43149
|
}), jsx(SearchNoMatchModeContext.Provider, {
|
|
42801
43150
|
value: searchNoMatchMode,
|
|
@@ -42820,16 +43169,18 @@ const UnorderedList = ({
|
|
|
42820
43169
|
});
|
|
42821
43170
|
};
|
|
42822
43171
|
|
|
42823
|
-
//
|
|
42824
|
-
//
|
|
42825
|
-
//
|
|
43172
|
+
// The "no match" message. Shown when a search left nothing to display: either
|
|
43173
|
+
// every matchable item has match=false (in-list filtering), or the list is empty
|
|
43174
|
+
// during an active search (filtering done outside the list, so itemCount is 0).
|
|
42826
43175
|
const SearchFallback = ({
|
|
42827
43176
|
tracker,
|
|
42828
|
-
searchFallback
|
|
43177
|
+
searchFallback,
|
|
43178
|
+
searching
|
|
42829
43179
|
}) => {
|
|
42830
43180
|
const itemCount = tracker.countSignal.value;
|
|
42831
43181
|
const noMatchCount = tracker.noMatchCountSignal.value;
|
|
42832
|
-
const
|
|
43182
|
+
const allNoMatch = noMatchCount > 0 && noMatchCount === itemCount;
|
|
43183
|
+
const showMatchFallback = allNoMatch || searching && itemCount === 0;
|
|
42833
43184
|
if (searchFallback === undefined) {
|
|
42834
43185
|
searchFallback = naviI18n("list.no_match");
|
|
42835
43186
|
}
|
|
@@ -42848,15 +43199,22 @@ const SearchFallback = ({
|
|
|
42848
43199
|
children: searchFallback
|
|
42849
43200
|
});
|
|
42850
43201
|
};
|
|
43202
|
+
// The "empty list" message. Not shown during a search — an empty search result
|
|
43203
|
+
// is a "no match" state (SearchFallback), not an empty-list state.
|
|
42851
43204
|
const Fallback = ({
|
|
42852
43205
|
tracker,
|
|
42853
|
-
fallback
|
|
43206
|
+
fallback,
|
|
43207
|
+
searching
|
|
42854
43208
|
}) => {
|
|
42855
43209
|
const itemCount = tracker.countSignal.value;
|
|
42856
|
-
const showFallback = itemCount === 0;
|
|
43210
|
+
const showFallback = itemCount === 0 && !searching;
|
|
42857
43211
|
if (fallback === undefined) {
|
|
42858
43212
|
fallback = naviI18n("list.empty");
|
|
42859
43213
|
}
|
|
43214
|
+
if (!fallback) {
|
|
43215
|
+
// explicitely disabled by user (<List fallback={false|null|''}>)
|
|
43216
|
+
return null;
|
|
43217
|
+
}
|
|
42860
43218
|
if (!showFallback) {
|
|
42861
43219
|
return null;
|
|
42862
43220
|
}
|
|
@@ -42970,11 +43328,57 @@ const ListItemPresentation = props => {
|
|
|
42970
43328
|
...columnsOverrideProps
|
|
42971
43329
|
});
|
|
42972
43330
|
};
|
|
43331
|
+
// A <List.Item skeleton> — a non-interactive placeholder row shown while a list
|
|
43332
|
+
// is loading. It is presentation-only (not tracked, not selectable, aria-hidden)
|
|
43333
|
+
// and reuses <Text loading> for the shimmer. Box layout props (padding, spacing…)
|
|
43334
|
+
// pass through so a skeletonTemplate can match the real items' metrics; and when
|
|
43335
|
+
// children are provided they render as-is, so a template can reproduce a
|
|
43336
|
+
// multi-part item (e.g. title + subtitle) out of several <Text loading> bars.
|
|
43337
|
+
const ListItemSkeletonResolver = props => {
|
|
43338
|
+
const Next = useNextResolver();
|
|
43339
|
+
if (props.skeleton) {
|
|
43340
|
+
return jsx(ListItemSkeleton, {
|
|
43341
|
+
...props
|
|
43342
|
+
});
|
|
43343
|
+
}
|
|
43344
|
+
return jsx(Next, {
|
|
43345
|
+
...props
|
|
43346
|
+
});
|
|
43347
|
+
};
|
|
43348
|
+
const ListItemSkeleton = props => {
|
|
43349
|
+
// Without vertical padding the bars of consecutive rows touch and read as one
|
|
43350
|
+
// block; "s" is enough air for them to be seen as separate rows.
|
|
43351
|
+
// eslint-disable-next-line no-unused-vars
|
|
43352
|
+
const {
|
|
43353
|
+
skeleton,
|
|
43354
|
+
children,
|
|
43355
|
+
paddingY = "s",
|
|
43356
|
+
...rest
|
|
43357
|
+
} = props;
|
|
43358
|
+
const columnsOverrideProps = useListItemColumnsOverrideProps(rest.style);
|
|
43359
|
+
return jsx(Box, {
|
|
43360
|
+
as: "li",
|
|
43361
|
+
role: "presentation",
|
|
43362
|
+
"aria-hidden": "true",
|
|
43363
|
+
paddingY: paddingY,
|
|
43364
|
+
...rest,
|
|
43365
|
+
...columnsOverrideProps,
|
|
43366
|
+
baseClassName: "navi_list_item navi_list_item_skeleton",
|
|
43367
|
+
children: children ?? jsx(Text, {
|
|
43368
|
+
loading: true
|
|
43369
|
+
})
|
|
43370
|
+
});
|
|
43371
|
+
};
|
|
42973
43372
|
const ListItemUI = props => {
|
|
42974
|
-
|
|
43373
|
+
// A stable id/index only matters when the item's identity must survive
|
|
43374
|
+
// reordering — i.e. it is selectable (selected/pointed state) or participates
|
|
43375
|
+
// in a matching system (search reorders items). A purely presentational,
|
|
43376
|
+
// static list doesn't need either, so don't nag about them there.
|
|
43377
|
+
const identityMatters = props.selectable || Boolean(props.matchInfo) || props.value !== undefined;
|
|
43378
|
+
if (identityMatters && props.id === undefined) {
|
|
42975
43379
|
console.warn("ListItem is missing an explicit id prop. Provide a stable id so pointed/selected state survives search reordering.");
|
|
42976
43380
|
}
|
|
42977
|
-
if (props.index === undefined) {
|
|
43381
|
+
if (identityMatters && props.index === undefined) {
|
|
42978
43382
|
console.warn("ListItem is missing an explicit index prop. Provide an index so item ordering is stable regardless of render order.");
|
|
42979
43383
|
}
|
|
42980
43384
|
const idDefault = useId();
|
|
@@ -42987,6 +43391,13 @@ const ListItemUI = props => {
|
|
|
42987
43391
|
// (e.g. useSearchText's getItemMatchInfo(item): { match, matchScore,
|
|
42988
43392
|
// matchRanges }), so there is exactly one way to wire it up.
|
|
42989
43393
|
const matchInfo = props.matchInfo;
|
|
43394
|
+
// Expose match on the tracked item: the tracker counts non-matching items via
|
|
43395
|
+
// `item.match === false` (drives noMatchCount → allNoMatch → the searchFallback
|
|
43396
|
+
// / hide-when-empty behavior). Without this a matchInfo-based search would
|
|
43397
|
+
// filter items out but never register them as "no match".
|
|
43398
|
+
if (matchInfo) {
|
|
43399
|
+
props.match = matchInfo.match;
|
|
43400
|
+
}
|
|
42990
43401
|
// Derive filtered/hidden/muted from matchInfo.match + searchNoMatchMode context.
|
|
42991
43402
|
if (matchInfo?.match === false) {
|
|
42992
43403
|
if (searchNoMatchMode === "remove") {
|
|
@@ -43160,6 +43571,10 @@ const LIST_ITEM_PSEUDO_ELEMENTS = ["::highlight"];
|
|
|
43160
43571
|
* selectable — when true, the item participates in selection (radio or checkbox
|
|
43161
43572
|
* depending on whether the parent List has `multiple`). Requires
|
|
43162
43573
|
* `value` and typically a <SelectableInput /> child.
|
|
43574
|
+
* skeleton — render a non-interactive placeholder row (a shimmering bar)
|
|
43575
|
+
* instead of a real item. Used as the List `skeletonTemplate`
|
|
43576
|
+
* while `loading`; Box layout props (padding…) pass through so the
|
|
43577
|
+
* placeholder can match the real items' metrics.
|
|
43163
43578
|
* value — the JS value emitted by the list's action/uiAction when this item
|
|
43164
43579
|
* is selected. Can be any type (string, number, object…).
|
|
43165
43580
|
* selected — controlled selected state. Pass `selected === value` (single) or
|
|
@@ -43186,7 +43601,7 @@ const LIST_ITEM_PSEUDO_ELEMENTS = ["::highlight"];
|
|
|
43186
43601
|
* CSS Highlight API.
|
|
43187
43602
|
* ...rest — forwarded to the rendered <li> element
|
|
43188
43603
|
*/
|
|
43189
|
-
const ListItem = createComponentResolver([ListItemFirstResolver, ListItemSelectableResolver, ListItemHeaderOrFooterResolver, ListItemPresentationResolver, ListItemUI]);
|
|
43604
|
+
const ListItem = createComponentResolver([ListItemFirstResolver, ListItemSkeletonResolver, ListItemSelectableResolver, ListItemHeaderOrFooterResolver, ListItemPresentationResolver, ListItemUI]);
|
|
43190
43605
|
List.Item = ListItem;
|
|
43191
43606
|
|
|
43192
43607
|
/**
|
|
@@ -44291,7 +44706,8 @@ const PickerButton = props => {
|
|
|
44291
44706
|
// --anchor-width). Set true when the CONTENT should dictate the popover width
|
|
44292
44707
|
// (e.g. a Wheel) instead of being stretched to the trigger — see
|
|
44293
44708
|
// picker_custom.jsx.
|
|
44294
|
-
popupWidthFitContent
|
|
44709
|
+
popupWidthFitContent,
|
|
44710
|
+
error
|
|
44295
44711
|
} = props;
|
|
44296
44712
|
const isSingleLine = maxLines === 1;
|
|
44297
44713
|
const inputRef = useRef(null);
|
|
@@ -44308,6 +44724,7 @@ const PickerButton = props => {
|
|
|
44308
44724
|
children
|
|
44309
44725
|
} = inputProps;
|
|
44310
44726
|
const loading = basePseudoState[":-navi-loading"];
|
|
44727
|
+
usePickerErrorCallout(uiStateController, error);
|
|
44311
44728
|
return jsxs(Box, {
|
|
44312
44729
|
as: "div",
|
|
44313
44730
|
ref: ref,
|
|
@@ -44327,6 +44744,7 @@ const PickerButton = props => {
|
|
|
44327
44744
|
ui: undefined,
|
|
44328
44745
|
maxLines: undefined,
|
|
44329
44746
|
popupWidthFitContent: undefined,
|
|
44747
|
+
error: undefined,
|
|
44330
44748
|
dayLabel: undefined
|
|
44331
44749
|
// This wrapper will receive keyboard event bubbling from the picker popup content
|
|
44332
44750
|
// we re-dispatch on the input (to get escape to close for instance)
|
|
@@ -44451,6 +44869,33 @@ const PickerButton = props => {
|
|
|
44451
44869
|
const isWithinPickerContent = (el, pickerEl) => {
|
|
44452
44870
|
return pickerEl.querySelector(".navi_picker_content")?.contains(el);
|
|
44453
44871
|
};
|
|
44872
|
+
const PICKER_ERROR_TOKEN = createOpenToken();
|
|
44873
|
+
// The `error` prop rides the control's own callout — the same surface already
|
|
44874
|
+
// used for failing constraints — so a caller never has to decide where to put
|
|
44875
|
+
// the message. It shows whether the popup is open or closed, and dismissing it
|
|
44876
|
+
// discards that error: only a new `error` value raises another one.
|
|
44877
|
+
const usePickerErrorCallout = (uiStateController, error) => {
|
|
44878
|
+
useEffect(() => {
|
|
44879
|
+
const {
|
|
44880
|
+
callout
|
|
44881
|
+
} = uiStateController.rules;
|
|
44882
|
+
const closeEvent = new CustomEvent("picker_error_cleared", {
|
|
44883
|
+
detail: {}
|
|
44884
|
+
});
|
|
44885
|
+
if (!error) {
|
|
44886
|
+
callout.removeOpenToken(PICKER_ERROR_TOKEN, closeEvent);
|
|
44887
|
+
return undefined;
|
|
44888
|
+
}
|
|
44889
|
+
callout.addOpenToken(PICKER_ERROR_TOKEN, {
|
|
44890
|
+
message: error === true ? "Something went wrong." : error,
|
|
44891
|
+
status: "error",
|
|
44892
|
+
skipFocus: true
|
|
44893
|
+
});
|
|
44894
|
+
return () => {
|
|
44895
|
+
callout.removeOpenToken(PICKER_ERROR_TOKEN, closeEvent);
|
|
44896
|
+
};
|
|
44897
|
+
}, [error]);
|
|
44898
|
+
};
|
|
44454
44899
|
const PickerInput = props => {
|
|
44455
44900
|
const {
|
|
44456
44901
|
ui,
|
|
@@ -44577,10 +45022,14 @@ const PickerFirstResolver = props => {
|
|
|
44577
45022
|
* step?: string | number,
|
|
44578
45023
|
* disabled?: boolean,
|
|
44579
45024
|
* readOnly?: boolean,
|
|
45025
|
+
* error?: boolean | string,
|
|
44580
45026
|
* uiAction?: (value: any, event: Event) => void,
|
|
44581
45027
|
* action?: (value: any, event: Event) => void,
|
|
44582
45028
|
* children?: import("ignore:preact").ComponentChildren,
|
|
44583
45029
|
* mode?: "popover" | "dialog",
|
|
45030
|
+
* popoverMode?: "nearby" | "overlay",
|
|
45031
|
+
* positionArea?: string,
|
|
45032
|
+
* popupWidthFitContent?: boolean,
|
|
44584
45033
|
* variant?: "icon" | "headless",
|
|
44585
45034
|
* icon?: import("ignore:preact").ComponentChildren,
|
|
44586
45035
|
* maxLines?: number,
|
|
@@ -44593,6 +45042,24 @@ const PickerFirstResolver = props => {
|
|
|
44593
45042
|
* ref?: import("ignore:preact").RefObject<HTMLElement>,
|
|
44594
45043
|
* [key: string]: any,
|
|
44595
45044
|
* }>}
|
|
45045
|
+
* @param {boolean|string} [error] Something went wrong around this picker (its
|
|
45046
|
+
* content failed to load, its value could not be resolved…). Shown as a
|
|
45047
|
+
* callout on the trigger, open or closed — the caller has nothing to place.
|
|
45048
|
+
* Dismissing it discards that error; a new `error` value raises another one.
|
|
45049
|
+
* @param {"nearby"|"overlay"} [popoverMode="nearby"] "overlay" lays the popover
|
|
45050
|
+
* over the trigger, "nearby" leaves a small gap below it.
|
|
45051
|
+
* @param {string} [positionArea] Where the popup goes — relative to the trigger
|
|
45052
|
+
* in popover mode, relative to the viewport in dialog mode. Same grammar as
|
|
45053
|
+
* Popover/Dialog's own `positionArea` ("top", "right-end", "inset(top-left)",
|
|
45054
|
+
* …). Defaults to "bottom-start" in popover mode ("inset(top-left)" when
|
|
45055
|
+
* popoverMode is "overlay"), and to Dialog's own "center" in dialog mode. A
|
|
45056
|
+
* popover still flips to the opposite side on its own when there isn't
|
|
45057
|
+
* enough room.
|
|
45058
|
+
* @param {boolean} [popupWidthFitContent] By default the popup is at least as
|
|
45059
|
+
* wide as the trigger. Set this to let the content size it instead, so a
|
|
45060
|
+
* popup narrower than the trigger stays narrow.
|
|
45061
|
+
* @param {number|string} [popoverMaxHeight] Soft cap on the popover's height
|
|
45062
|
+
* (default 300px). The popover shrinks below it when space is tight.
|
|
44596
45063
|
*/
|
|
44597
45064
|
const Picker = createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
|
|
44598
45065
|
Picker.UI = PickerDefaultUI;
|
|
@@ -50764,67 +51231,6 @@ const Address = ({
|
|
|
50764
51231
|
});
|
|
50765
51232
|
};
|
|
50766
51233
|
|
|
50767
|
-
const LoadingDotsSvg = () => {
|
|
50768
|
-
return jsxs("svg", {
|
|
50769
|
-
viewBox: "0 0 200 200",
|
|
50770
|
-
width: "100%",
|
|
50771
|
-
height: "100%",
|
|
50772
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
50773
|
-
children: [jsx("rect", {
|
|
50774
|
-
fill: "currentColor",
|
|
50775
|
-
stroke: "currentColor",
|
|
50776
|
-
"stroke-width": "15",
|
|
50777
|
-
width: "30",
|
|
50778
|
-
height: "30",
|
|
50779
|
-
x: "25",
|
|
50780
|
-
y: "85",
|
|
50781
|
-
children: jsx("animate", {
|
|
50782
|
-
attributeName: "opacity",
|
|
50783
|
-
calcMode: "spline",
|
|
50784
|
-
dur: "2",
|
|
50785
|
-
values: "1;0;1;",
|
|
50786
|
-
keySplines: ".5 0 .5 1;.5 0 .5 1",
|
|
50787
|
-
repeatCount: "indefinite",
|
|
50788
|
-
begin: "-.4"
|
|
50789
|
-
})
|
|
50790
|
-
}), jsx("rect", {
|
|
50791
|
-
fill: "currentColor",
|
|
50792
|
-
stroke: "currentColor",
|
|
50793
|
-
"stroke-width": "15",
|
|
50794
|
-
width: "30",
|
|
50795
|
-
height: "30",
|
|
50796
|
-
x: "85",
|
|
50797
|
-
y: "85",
|
|
50798
|
-
children: jsx("animate", {
|
|
50799
|
-
attributeName: "opacity",
|
|
50800
|
-
calcMode: "spline",
|
|
50801
|
-
dur: "2",
|
|
50802
|
-
values: "1;0;1;",
|
|
50803
|
-
keySplines: ".5 0 .5 1;.5 0 .5 1",
|
|
50804
|
-
repeatCount: "indefinite",
|
|
50805
|
-
begin: "-.2"
|
|
50806
|
-
})
|
|
50807
|
-
}), jsx("rect", {
|
|
50808
|
-
fill: "currentColor",
|
|
50809
|
-
stroke: "currentColor",
|
|
50810
|
-
"stroke-width": "15",
|
|
50811
|
-
width: "30",
|
|
50812
|
-
height: "30",
|
|
50813
|
-
x: "145",
|
|
50814
|
-
y: "85",
|
|
50815
|
-
children: jsx("animate", {
|
|
50816
|
-
attributeName: "opacity",
|
|
50817
|
-
calcMode: "spline",
|
|
50818
|
-
dur: "2",
|
|
50819
|
-
values: "1;0;1;",
|
|
50820
|
-
keySplines: ".5 0 .5 1;.5 0 .5 1",
|
|
50821
|
-
repeatCount: "indefinite",
|
|
50822
|
-
begin: "0"
|
|
50823
|
-
})
|
|
50824
|
-
})]
|
|
50825
|
-
});
|
|
50826
|
-
};
|
|
50827
|
-
|
|
50828
51234
|
const formatNumber = (value, { lang = languagesSignal.value } = {}) => {
|
|
50829
51235
|
return new Intl.NumberFormat(lang).format(value);
|
|
50830
51236
|
};
|
|
@@ -52229,23 +52635,6 @@ const Image = ({
|
|
|
52229
52635
|
});
|
|
52230
52636
|
};
|
|
52231
52637
|
|
|
52232
|
-
const LoadingIndicator = ({
|
|
52233
|
-
variant = "circle",
|
|
52234
|
-
...props
|
|
52235
|
-
}) => {
|
|
52236
|
-
if (variant === "dots") {
|
|
52237
|
-
return jsx(Icon, {
|
|
52238
|
-
...props,
|
|
52239
|
-
children: jsx(LoadingDotsSvg, {})
|
|
52240
|
-
});
|
|
52241
|
-
}
|
|
52242
|
-
return jsx(Icon, {
|
|
52243
|
-
circle: true,
|
|
52244
|
-
...props,
|
|
52245
|
-
children: jsx(LoadingIndicatorFluid, {})
|
|
52246
|
-
});
|
|
52247
|
-
};
|
|
52248
|
-
|
|
52249
52638
|
const Svg = props => {
|
|
52250
52639
|
return jsx(Box, {
|
|
52251
52640
|
...props,
|
|
@@ -52744,7 +53133,12 @@ const SidePanel = ({
|
|
|
52744
53133
|
onClose: onClose,
|
|
52745
53134
|
layer: layer,
|
|
52746
53135
|
anchorCustomEventDetail: "ignore",
|
|
52747
|
-
positionArea: side
|
|
53136
|
+
positionArea: side
|
|
53137
|
+
// A side panel is flush against the edge it slides in from — none of
|
|
53138
|
+
// Dialog's own default gap with the container.
|
|
53139
|
+
,
|
|
53140
|
+
|
|
53141
|
+
marginWithContainer: 0,
|
|
52748
53142
|
animation: animation === true ? `slide-from-${side}` : animation,
|
|
52749
53143
|
pointerInteractionOutsideEffect: closeOnClickOutside ? "close" : "none",
|
|
52750
53144
|
focusCapture: closeOnClickOutside,
|
|
@@ -52960,5 +53354,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
52960
53354
|
})
|
|
52961
53355
|
});
|
|
52962
53356
|
|
|
52963
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, Details, Dialog, DialogLayout, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns,
|
|
53357
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, Details, Dialog, DialogLayout, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
52964
53358
|
//# sourceMappingURL=jsenv_navi.js.map
|