@jsenv/navi 0.29.47 → 0.29.49
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 +176 -66
- package/dist/jsenv_navi.js.map +20 -16
- package/dist/jsenv_navi_side_effects.js +1 -1
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/create_and_edit.md +38 -0
- package/docs/popup_open.md +109 -13
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -16993,11 +16993,14 @@ const VISUAL_PROPS = {
|
|
|
16993
16993
|
};
|
|
16994
16994
|
const CONTENT_PROPS = {
|
|
16995
16995
|
align: applyOnTwoProps("alignX", "alignY"),
|
|
16996
|
+
/* The value the caller named is always written out, even when it matches
|
|
16997
|
+
CSS's own initial value ("start" for justify-content, "stretch" for
|
|
16998
|
+
align-items). Skipping it as a no-op only holds for a bare element: a
|
|
16999
|
+
component whose stylesheet already centers its content (Picker in its icon
|
|
17000
|
+
variant, for one) would keep centering, and alignX="start" would silently
|
|
17001
|
+
do nothing — the one case where saying it explicitly matters most. */
|
|
16996
17002
|
alignX: (value, { boxFlow }) => {
|
|
16997
17003
|
if (boxFlow === "flex-y" || boxFlow === "inline-flex-y") {
|
|
16998
|
-
if (value === "stretch") {
|
|
16999
|
-
return undefined; // this is the default
|
|
17000
|
-
}
|
|
17001
17004
|
return { alignItems: value };
|
|
17002
17005
|
}
|
|
17003
17006
|
if (
|
|
@@ -17011,18 +17014,12 @@ const CONTENT_PROPS = {
|
|
|
17011
17014
|
boxFlow === "grid" ||
|
|
17012
17015
|
boxFlow === "inline-grid"
|
|
17013
17016
|
) {
|
|
17014
|
-
if (value === "start") {
|
|
17015
|
-
return undefined; // this is the default
|
|
17016
|
-
}
|
|
17017
17017
|
return { justifyContent: value };
|
|
17018
17018
|
}
|
|
17019
17019
|
return { textAlign: value };
|
|
17020
17020
|
},
|
|
17021
17021
|
alignY: (value, { boxFlow }) => {
|
|
17022
17022
|
if (boxFlow === "flex-y" || boxFlow === "inline-flex-y") {
|
|
17023
|
-
if (value === "start") {
|
|
17024
|
-
return undefined;
|
|
17025
|
-
}
|
|
17026
17023
|
return { justifyContent: value };
|
|
17027
17024
|
}
|
|
17028
17025
|
if (
|
|
@@ -17035,9 +17032,6 @@ const CONTENT_PROPS = {
|
|
|
17035
17032
|
boxFlow === "grid" ||
|
|
17036
17033
|
boxFlow === "inline-grid"
|
|
17037
17034
|
) {
|
|
17038
|
-
if (value === "stretch") {
|
|
17039
|
-
return undefined;
|
|
17040
|
-
}
|
|
17041
17035
|
return { alignItems: value };
|
|
17042
17036
|
}
|
|
17043
17037
|
const verticalAlignMap = {
|
|
@@ -22147,7 +22141,10 @@ const useNavState = useNavStateBasic;
|
|
|
22147
22141
|
* @param {Event} event What the user did.
|
|
22148
22142
|
* @param {object} [options]
|
|
22149
22143
|
* @param {boolean} [options.optional] No suitable target is not a warning.
|
|
22150
|
-
* @param {any} [options.value]
|
|
22144
|
+
* @param {any} [options.value] What the command is about, carried to whoever
|
|
22145
|
+
* answers — and, for `--navi-open`/`--navi-toggle`, what the popup is opened
|
|
22146
|
+
* ON: it reaches its `onOpen` before the popup builds anything. Left out, a
|
|
22147
|
+
* source's own value (`<Button value={id}>`) is read instead.
|
|
22151
22148
|
* @param {Element} [options.anchor] Where a popup this opens should be placed,
|
|
22152
22149
|
* when that is not the element asking: a menu opened by a press belongs at the
|
|
22153
22150
|
* point the press happened, and the row that was pressed is not that point.
|
|
@@ -22176,6 +22173,10 @@ const triggerNaviCommand = (
|
|
|
22176
22173
|
// Whatever followed the colon: "--navi-go-to-slide:edit" → "edit".
|
|
22177
22174
|
argument: command.includes(":") ? commandArgument(command) : undefined,
|
|
22178
22175
|
anchor,
|
|
22176
|
+
// What the command is about, said by the caller rather than read off the
|
|
22177
|
+
// source: the attribute form has a `value` to read (`<Button value={id}>`),
|
|
22178
|
+
// a JS decision has none, and both must be able to say the same thing.
|
|
22179
|
+
value,
|
|
22179
22180
|
});
|
|
22180
22181
|
if (!execute) {
|
|
22181
22182
|
if (optional) {
|
|
@@ -22812,28 +22813,42 @@ registerNaviCommand("--navi-nav-to", (source, event, { argument }) => {
|
|
|
22812
22813
|
};
|
|
22813
22814
|
});
|
|
22814
22815
|
|
|
22815
|
-
registerNaviCommand(
|
|
22816
|
-
|
|
22817
|
-
|
|
22818
|
-
|
|
22819
|
-
|
|
22820
|
-
|
|
22821
|
-
|
|
22822
|
-
|
|
22823
|
-
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
22827
|
-
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
|
|
22831
|
-
|
|
22832
|
-
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22816
|
+
registerNaviCommand(
|
|
22817
|
+
"--navi-toggle",
|
|
22818
|
+
(source, event, { anchor, value } = {}) => {
|
|
22819
|
+
const target =
|
|
22820
|
+
resolveExplicitTarget(source) || resolveClosestExpandable(source);
|
|
22821
|
+
if (!target) {
|
|
22822
|
+
return undefined;
|
|
22823
|
+
}
|
|
22824
|
+
return {
|
|
22825
|
+
target,
|
|
22826
|
+
implementation: () => {
|
|
22827
|
+
const isExpanded = target.getAttribute("aria-expanded") === "true";
|
|
22828
|
+
const customEventName = isExpanded
|
|
22829
|
+
? "navi_request_close"
|
|
22830
|
+
: "navi_request_open";
|
|
22831
|
+
return dispatchCustomEvent(target, customEventName, {
|
|
22832
|
+
event,
|
|
22833
|
+
source: resolveCommandProxySource(source),
|
|
22834
|
+
anchor,
|
|
22835
|
+
// Same as --navi-open below: the half of the toggle that opens says
|
|
22836
|
+
// what it opens ON, and the half that closes carries it too so the
|
|
22837
|
+
// popup never has to ask which half it just heard.
|
|
22838
|
+
value:
|
|
22839
|
+
value === undefined ? resolveCommandValue(source, event) : value,
|
|
22840
|
+
});
|
|
22841
|
+
},
|
|
22842
|
+
};
|
|
22843
|
+
},
|
|
22844
|
+
);
|
|
22845
|
+
// A popup that edits opens ON something, and the press is the only place that
|
|
22846
|
+
// knows which one. It says it with `value` — "what this is about", as
|
|
22847
|
+
// everywhere else — rather than with an argument, which says what the command
|
|
22848
|
+
// DOES: "open" is already a complete instruction, unlike --navi-go-to-slide.
|
|
22849
|
+
// <Button value={radar.id} command="--navi-open" commandfor="radar-dialog">
|
|
22850
|
+
// The popup is told before it opens — see Dialog/Popover's `onOpen`.
|
|
22851
|
+
registerNaviCommand("--navi-open", (source, event, { anchor, value } = {}) => {
|
|
22837
22852
|
const target =
|
|
22838
22853
|
resolveExplicitTarget(source) || resolveClosestExpandable(source);
|
|
22839
22854
|
if (!target) {
|
|
@@ -22849,6 +22864,7 @@ registerNaviCommand("--navi-open", (source, event, { anchor } = {}) => {
|
|
|
22849
22864
|
event,
|
|
22850
22865
|
source: resolveCommandProxySource(source),
|
|
22851
22866
|
anchor,
|
|
22867
|
+
value: value === undefined ? resolveCommandValue(source, event) : value,
|
|
22852
22868
|
});
|
|
22853
22869
|
},
|
|
22854
22870
|
};
|
|
@@ -27799,7 +27815,8 @@ const getFocusedBeforeTransfer = (e) => {
|
|
|
27799
27815
|
* - `onClose(e)`: actually closing, not preventable — final reactions live here.
|
|
27800
27816
|
*
|
|
27801
27817
|
* The controller exposes matching action methods:
|
|
27802
|
-
* - `open()`: requests opening —
|
|
27818
|
+
* - `open()`: requests opening — calls the caller's `onOpen` (see below), then
|
|
27819
|
+
* `mountContent`/`openEffect`, then `openHandler`.
|
|
27803
27820
|
* - `requestClose()`: requests closing — calls `onRequestClose` then `onClose`,
|
|
27804
27821
|
* stopping after the first if denied. The popup may choose to stay open.
|
|
27805
27822
|
* - `close()`: closes for real — calls only `onClose`, skipping
|
|
@@ -27969,6 +27986,13 @@ const createOpenController = (
|
|
|
27969
27986
|
// content is still waiting for a first open to be built. Called below,
|
|
27970
27987
|
// before openEffect, so the popup measures and positions the real thing.
|
|
27971
27988
|
mountContent: null,
|
|
27989
|
+
// The caller's own `onOpen`, set by Dialog/Popover from their props on
|
|
27990
|
+
// every render (like openEffect). Called BEFORE mountContent, so whatever
|
|
27991
|
+
// it decides — which record this dialog is opening on — is already true by
|
|
27992
|
+
// the time the content is built, positioned and shown. That order is the
|
|
27993
|
+
// whole point: learning it afterwards means the content mounted on the
|
|
27994
|
+
// previous subject first.
|
|
27995
|
+
onOpen: null,
|
|
27972
27996
|
// The counterpart, set only when the popup was told to throw its content
|
|
27973
27997
|
// away on close (`unmountWhenClosed`). Called from performClose above.
|
|
27974
27998
|
unmountContent: null,
|
|
@@ -28025,6 +28049,10 @@ const createOpenController = (
|
|
|
28025
28049
|
}
|
|
28026
28050
|
};
|
|
28027
28051
|
};
|
|
28052
|
+
// Before mountContent, which builds the content, and before openEffect,
|
|
28053
|
+
// which shows it: what the popup opens ON has to be known before either
|
|
28054
|
+
// (see `onOpen` above).
|
|
28055
|
+
controller.onOpen?.(requestOpenEvent);
|
|
28028
28056
|
// After prepareFocusTransfer, which has to record what held the focus
|
|
28029
28057
|
// before anything inside the popup can claim it, and before openEffect,
|
|
28030
28058
|
// which measures the popup to place it.
|
|
@@ -29035,16 +29063,16 @@ const css$V = /* css */`
|
|
|
29035
29063
|
backdrop-filter: var(--navi-backdrop-capture-backdrop-filter);
|
|
29036
29064
|
}
|
|
29037
29065
|
|
|
29038
|
-
/*
|
|
29066
|
+
/* backdropVariant, keyed off the originating element (a
|
|
29039
29067
|
pseudo-element carries no attributes of its own — same reasoning as
|
|
29040
29068
|
the capture rule just above). After the rules it overrides: same
|
|
29041
29069
|
specificity, so order is what decides. showModal() still makes the
|
|
29042
29070
|
page inert either way — only the paint goes away. */
|
|
29043
|
-
&[data-backdrop-
|
|
29071
|
+
&[data-backdrop-variant="discrete"]::backdrop {
|
|
29044
29072
|
background: var(--navi-backdrop-discrete-background);
|
|
29045
29073
|
backdrop-filter: none;
|
|
29046
29074
|
}
|
|
29047
|
-
&[data-backdrop-
|
|
29075
|
+
&[data-backdrop-variant="invisible"]::backdrop {
|
|
29048
29076
|
background: transparent;
|
|
29049
29077
|
backdrop-filter: none;
|
|
29050
29078
|
}
|
|
@@ -29171,11 +29199,11 @@ const css$V = /* css */`
|
|
|
29171
29199
|
/* Same override as the via-attribute renderer's own ::backdrop rules
|
|
29172
29200
|
above, on the real element this renderer uses instead — see them for
|
|
29173
29201
|
the specificity/ordering reasoning. */
|
|
29174
|
-
&[data-backdrop-
|
|
29202
|
+
&[data-backdrop-variant="discrete"] {
|
|
29175
29203
|
background: var(--navi-backdrop-discrete-background);
|
|
29176
29204
|
backdrop-filter: none;
|
|
29177
29205
|
}
|
|
29178
|
-
&[data-backdrop-
|
|
29206
|
+
&[data-backdrop-variant="invisible"] {
|
|
29179
29207
|
background: transparent;
|
|
29180
29208
|
backdrop-filter: none;
|
|
29181
29209
|
}
|
|
@@ -29254,11 +29282,11 @@ const css$V = /* css */`
|
|
|
29254
29282
|
* both just absorb the click without closing (visually dimmed backdrop vs.
|
|
29255
29283
|
* not) — a dialog is always modal one way or another, so there's always
|
|
29256
29284
|
* at least a click-absorbing backdrop regardless of this prop.
|
|
29257
|
-
* @param {"auto"|"discrete"|"
|
|
29285
|
+
* @param {"auto"|"discrete"|"invisible"} [props.backdropVariant="auto"] - How
|
|
29258
29286
|
* visible the backdrop is, independently of what it does. `"auto"`: the
|
|
29259
29287
|
* paint `pointerInteractionOutsideEffect` implies (dimmed for
|
|
29260
29288
|
* `"close"`/`"cancel"`, blurred glass for `"capture"`). `"discrete"`: a
|
|
29261
|
-
* barely-there dim. `"
|
|
29289
|
+
* barely-there dim. `"invisible"`: fully transparent. The dialog stays modal
|
|
29262
29290
|
* either way — this only changes how much it insists visually, never what
|
|
29263
29291
|
* an outside click does or whether the page behind stays reachable.
|
|
29264
29292
|
* @param {boolean} [props.scrollCapture] - Traps scroll gestures inside the
|
|
@@ -29314,6 +29342,13 @@ const css$V = /* css */`
|
|
|
29314
29342
|
* for the user to see it transition away from. `"interaction"` says the
|
|
29315
29343
|
* opposite — this dialog is mounted *because* the user just asked for it, so
|
|
29316
29344
|
* the mount is the opening and the entrance plays like any other.
|
|
29345
|
+
* @param {(openEvent: CustomEvent) => void} [props.onOpen] - Called when it
|
|
29346
|
+
* opens, BEFORE its content is built, positioned or shown. What it opens ON
|
|
29347
|
+
* is in `openEvent.detail.value` — the value of whatever asked
|
|
29348
|
+
* (`<Button value={radar.id} command="--navi-open" commandfor="…">`), or the
|
|
29349
|
+
* `value` given to `triggerNaviCommand`. That order is the point: a dialog that
|
|
29350
|
+
* is "new" or "edit X" depending on the press must know which one it is
|
|
29351
|
+
* before what it holds is rendered.
|
|
29317
29352
|
* @param {(event: Event) => void} [props.onClose] - Called when the dialog
|
|
29318
29353
|
* actually closes — not preventable (see `open_controller.js`'s own
|
|
29319
29354
|
* `onRequestClose`/`onClose` distinction; `onRequestClose` is where you'd
|
|
@@ -29388,7 +29423,11 @@ const UncontrolledDialog = props => {
|
|
|
29388
29423
|
openController: openController,
|
|
29389
29424
|
onnavi_request_open: e => {
|
|
29390
29425
|
openController.open(e, {
|
|
29391
|
-
anchor: e.detail?.anchor ?? e.detail?.source
|
|
29426
|
+
anchor: e.detail?.anchor ?? e.detail?.source,
|
|
29427
|
+
// What the command was about — a `<Button value={id}>` that opened
|
|
29428
|
+
// this popup ON that id. Handed to `onOpen` before anything is
|
|
29429
|
+
// built (see open_controller.js).
|
|
29430
|
+
value: e.detail?.value
|
|
29392
29431
|
});
|
|
29393
29432
|
},
|
|
29394
29433
|
onnavi_request_close: e => {
|
|
@@ -29512,7 +29551,7 @@ const useDialogProps = props => {
|
|
|
29512
29551
|
// *does* (that's pointerInteractionOutsideEffect above). A dialog is
|
|
29513
29552
|
// always modal, so "none" here never makes the page behind reachable:
|
|
29514
29553
|
// it only stops the dim from being drawn.
|
|
29515
|
-
|
|
29554
|
+
backdropVariant = "auto",
|
|
29516
29555
|
scrollCapture: scrollCaptureProp,
|
|
29517
29556
|
// "auto" (default) → the dialog follows its content. "frozen" → measured
|
|
29518
29557
|
// once, held at that size while open. See this prop's own JSDoc above.
|
|
@@ -29531,11 +29570,20 @@ const useDialogProps = props => {
|
|
|
29531
29570
|
// instead, so it's read here rather than left in `rest`.
|
|
29532
29571
|
autoFocus = "last-resort",
|
|
29533
29572
|
onKeyDown,
|
|
29573
|
+
// Read here (rather than left in `rest`) for two reasons: it must never
|
|
29574
|
+
// reach the DOM as an `onopen` attribute, and the controller — not this
|
|
29575
|
+
// render — is what calls it, at the one moment that makes it useful (see
|
|
29576
|
+
// openController.onOpen below).
|
|
29577
|
+
onOpen,
|
|
29534
29578
|
children: childrenProp,
|
|
29535
29579
|
mountWhenClosed,
|
|
29536
29580
|
unmountWhenClosed,
|
|
29537
29581
|
...rest
|
|
29538
29582
|
} = props;
|
|
29583
|
+
// Assigned on every render, like openEffect below, so it always closes over
|
|
29584
|
+
// the latest prop. Called by openController.open() before the content is
|
|
29585
|
+
// built: what this popup opens ON is known before anything reads it.
|
|
29586
|
+
openController.onOpen = onOpen || null;
|
|
29539
29587
|
const children = usePopupContentMount(openController, props.ref, {
|
|
29540
29588
|
children: childrenProp,
|
|
29541
29589
|
mountWhenClosed,
|
|
@@ -30034,7 +30082,7 @@ const useDialogProps = props => {
|
|
|
30034
30082
|
"styleCSSVars": DIALOG_STYLE_CSS_VARS,
|
|
30035
30083
|
"animationDuration": rest.animationDuration,
|
|
30036
30084
|
"data-pointer-interaction-outside": pointerInteractionOutsideEffect,
|
|
30037
|
-
"data-backdrop-
|
|
30085
|
+
"data-backdrop-variant": backdropVariant
|
|
30038
30086
|
});
|
|
30039
30087
|
Object.assign(contentProps, {
|
|
30040
30088
|
tabIndex,
|
|
@@ -30067,7 +30115,7 @@ const useDialogProps = props => {
|
|
|
30067
30115
|
// ::backdrop, same "a pseudo-element can't carry attributes" reasoning
|
|
30068
30116
|
// as the prop just above (and harmless for the custom renderer, whose
|
|
30069
30117
|
// real backdrop element gets it via backdropProps).
|
|
30070
|
-
"data-backdrop-
|
|
30118
|
+
"data-backdrop-variant": backdropVariant,
|
|
30071
30119
|
"styleCSSVars": DIALOG_STYLE_CSS_VARS,
|
|
30072
30120
|
...rest,
|
|
30073
30121
|
...autoFocusProps,
|
|
@@ -30472,16 +30520,16 @@ const css$U = /* css */`
|
|
|
30472
30520
|
backdrop-filter: var(--navi-backdrop-capture-backdrop-filter);
|
|
30473
30521
|
}
|
|
30474
30522
|
|
|
30475
|
-
/*
|
|
30523
|
+
/* backdropVariant overrides whatever the effect above picked — same
|
|
30476
30524
|
specificity (class + one attribute), so these have to stay *after*
|
|
30477
30525
|
them to win. Only the paint changes: the element is still rendered
|
|
30478
30526
|
and still pointer-events: auto, so an outside click keeps doing
|
|
30479
30527
|
exactly what pointerInteractionOutsideEffect says. */
|
|
30480
|
-
&[data-backdrop-
|
|
30528
|
+
&[data-backdrop-variant="discrete"] {
|
|
30481
30529
|
background: var(--navi-backdrop-discrete-background);
|
|
30482
30530
|
backdrop-filter: none;
|
|
30483
30531
|
}
|
|
30484
|
-
&[data-backdrop-
|
|
30532
|
+
&[data-backdrop-variant="invisible"] {
|
|
30485
30533
|
background: transparent;
|
|
30486
30534
|
backdrop-filter: none;
|
|
30487
30535
|
}
|
|
@@ -30549,11 +30597,11 @@ const css$U = /* css */`
|
|
|
30549
30597
|
* absorbs the click (dims the backdrop) without closing. Note this
|
|
30550
30598
|
* default differs from `Dialog`'s own (`"close"`) — a popover is
|
|
30551
30599
|
* typically a lightweight, non-modal affordance.
|
|
30552
|
-
* @param {"auto"|"discrete"|"
|
|
30600
|
+
* @param {"auto"|"discrete"|"invisible"} [props.backdropVariant="auto"] - How
|
|
30553
30601
|
* visible the backdrop is, independently of what it does. `"auto"`: the
|
|
30554
30602
|
* paint `pointerInteractionOutsideEffect` implies (dimmed for
|
|
30555
30603
|
* `"close"`/`"cancel"`, blurred glass for `"capture"`). `"discrete"`: a
|
|
30556
|
-
* barely-there dim. `"
|
|
30604
|
+
* barely-there dim. `"invisible"`: fully transparent. The backdrop is still
|
|
30557
30605
|
* rendered and still catches outside clicks in every case — this only
|
|
30558
30606
|
* changes how much the popover insists on being the thing you deal with.
|
|
30559
30607
|
* Ignored when `pointerInteractionOutsideEffect="none"` (there is no
|
|
@@ -30616,6 +30664,13 @@ const css$U = /* css */`
|
|
|
30616
30664
|
* for the user to see it transition away from. `"interaction"` says the
|
|
30617
30665
|
* opposite — this popover is mounted *because* the user just asked for it, so
|
|
30618
30666
|
* the mount is the opening and the entrance plays like any other.
|
|
30667
|
+
* @param {(openEvent: CustomEvent) => void} [props.onOpen] - Called when it
|
|
30668
|
+
* opens, BEFORE its content is built, positioned or shown. What it opens ON
|
|
30669
|
+
* is in `openEvent.detail.value` — the value of whatever asked
|
|
30670
|
+
* (`<Button value={radar.id} command="--navi-open" commandfor="…">`), or the
|
|
30671
|
+
* `value` given to `triggerNaviCommand`. That order is the point: a popover that
|
|
30672
|
+
* is "new" or "edit X" depending on the press must know which one it is
|
|
30673
|
+
* before what it holds is rendered.
|
|
30619
30674
|
* @param {(event: Event) => void} [props.onClose] - Called when the popover
|
|
30620
30675
|
* actually closes — not preventable (see `open_controller.js`'s own
|
|
30621
30676
|
* `onRequestClose`/`onClose` distinction; `onRequestClose` is where you'd
|
|
@@ -30688,7 +30743,11 @@ const UncontrolledPopover = props => {
|
|
|
30688
30743
|
openController: openController,
|
|
30689
30744
|
onnavi_request_open: e => {
|
|
30690
30745
|
openController.open(e, {
|
|
30691
|
-
anchor: e.detail?.anchor ?? e.detail?.source
|
|
30746
|
+
anchor: e.detail?.anchor ?? e.detail?.source,
|
|
30747
|
+
// What the command was about — a `<Button value={id}>` that opened
|
|
30748
|
+
// this popup ON that id. Handed to `onOpen` before anything is
|
|
30749
|
+
// built (see open_controller.js).
|
|
30750
|
+
value: e.detail?.value
|
|
30692
30751
|
});
|
|
30693
30752
|
},
|
|
30694
30753
|
onnavi_request_close: e => {
|
|
@@ -30806,7 +30865,7 @@ const usePopoverProps = props => {
|
|
|
30806
30865
|
// *does* (that's pointerInteractionOutsideEffect above). "auto" keeps
|
|
30807
30866
|
// the paint the effect implies; "discrete"/"none" tone it down or
|
|
30808
30867
|
// remove it entirely without giving up the outside click.
|
|
30809
|
-
|
|
30868
|
+
backdropVariant = "auto",
|
|
30810
30869
|
scrollCapture,
|
|
30811
30870
|
focusCapture,
|
|
30812
30871
|
// "auto" (default) → the popover follows its content. "frozen" → measured
|
|
@@ -30826,11 +30885,20 @@ const usePopoverProps = props => {
|
|
|
30826
30885
|
// instead, so it's read here rather than left in `rest`.
|
|
30827
30886
|
autoFocus = "last-resort",
|
|
30828
30887
|
onKeyDown,
|
|
30888
|
+
// Read here (rather than left in `rest`) for two reasons: it must never
|
|
30889
|
+
// reach the DOM as an `onopen` attribute, and the controller — not this
|
|
30890
|
+
// render — is what calls it, at the one moment that makes it useful (see
|
|
30891
|
+
// openController.onOpen below).
|
|
30892
|
+
onOpen,
|
|
30829
30893
|
children: childrenProp,
|
|
30830
30894
|
mountWhenClosed,
|
|
30831
30895
|
unmountWhenClosed,
|
|
30832
30896
|
...rest
|
|
30833
30897
|
} = props;
|
|
30898
|
+
// Assigned on every render, like openEffect below, so it always closes over
|
|
30899
|
+
// the latest prop. Called by openController.open() before the content is
|
|
30900
|
+
// built: what this popup opens ON is known before anything reads it.
|
|
30901
|
+
openController.onOpen = onOpen || null;
|
|
30834
30902
|
const children = usePopupContentMount(openController, props.ref, {
|
|
30835
30903
|
children: childrenProp,
|
|
30836
30904
|
mountWhenClosed,
|
|
@@ -31480,7 +31548,7 @@ const usePopoverProps = props => {
|
|
|
31480
31548
|
"styleCSSVars": POPUP_STYLE_CSS_VARS,
|
|
31481
31549
|
"animationDuration": rest.animationDuration,
|
|
31482
31550
|
"data-pointer-interaction-outside": pointerInteractionOutsideEffect,
|
|
31483
|
-
"data-backdrop-
|
|
31551
|
+
"data-backdrop-variant": backdropVariant,
|
|
31484
31552
|
"onMouseDown": mouseDownEvent => {
|
|
31485
31553
|
if (mouseDownEvent.button !== 0) {
|
|
31486
31554
|
return;
|
|
@@ -51833,7 +51901,7 @@ const css$z = /* css */`
|
|
|
51833
51901
|
* is unavoidably *more* intrusive once it switches to dialog mode than
|
|
51834
51902
|
* the exact same usage would be as a popover — worth keeping in mind for
|
|
51835
51903
|
* anything that relies on `Popup` and can end up on a small screen.
|
|
51836
|
-
* @param {"auto"|"discrete"|"
|
|
51904
|
+
* @param {"auto"|"discrete"|"invisible"} [props.backdropVariant] - Forwarded
|
|
51837
51905
|
* as-is to whichever component renders (both understand it identically):
|
|
51838
51906
|
* how visible the backdrop is, independently of what an outside click
|
|
51839
51907
|
* does. Unlike `pointerInteractionOutsideEffect` above, this one needs no
|
|
@@ -52239,9 +52307,16 @@ const PickerCustom = props => {
|
|
|
52239
52307
|
const pickerEl = ref.current;
|
|
52240
52308
|
const inputEl = getPickerInput(pickerEl);
|
|
52241
52309
|
const valueAtClose = getUIStateFromElement(inputEl);
|
|
52242
|
-
if (
|
|
52243
|
-
//
|
|
52244
|
-
//
|
|
52310
|
+
if (compareTwoJsValues(valueAtClose, valueAtOpen) && (heldAtOpen || valueAtOpen === undefined)) {
|
|
52311
|
+
// Nothing to say on the way out, for one of two reasons. Either the
|
|
52312
|
+
// value was already held and has not moved — closing on it repeats
|
|
52313
|
+
// what was already the answer. Or there was never anything to
|
|
52314
|
+
// confirm: a picker holding nothing AND showing nothing (a menu of
|
|
52315
|
+
// gestures — no value, no defaultValue, no signal) has no
|
|
52316
|
+
// suggestion to accept, and confirming `undefined` cannot mean
|
|
52317
|
+
// anything. A picker on a defaultValue is untouched by this: it
|
|
52318
|
+
// shows something, so closing on it still confirms it.
|
|
52319
|
+
// No action to run, but still allow the close.
|
|
52245
52320
|
return;
|
|
52246
52321
|
}
|
|
52247
52322
|
dispatchRequestAction(inputEl, {
|
|
@@ -52272,8 +52347,17 @@ const PickerCustom = props => {
|
|
|
52272
52347
|
// answer. Say it here — this is the moment the suggestion becomes
|
|
52273
52348
|
// one. Harmless when the value did change on the way: the state is
|
|
52274
52349
|
// already what it is, and this only re-runs the same reaction.
|
|
52275
|
-
|
|
52276
|
-
|
|
52350
|
+
const inputEl = getPickerInput(ref.current);
|
|
52351
|
+
const valueAtClose = getUIStateFromElement(inputEl);
|
|
52352
|
+
if (valueAtOpen === undefined && compareTwoJsValues(valueAtClose, valueAtOpen)) {
|
|
52353
|
+
// Same third case onRequestClose steps around: nothing held,
|
|
52354
|
+
// nothing shown, nothing picked. There is no suggestion here to
|
|
52355
|
+
// turn into an answer.
|
|
52356
|
+
debugPopup(closeEvent, `picker showed nothing -> nothing to commit`);
|
|
52357
|
+
} else {
|
|
52358
|
+
debugPopup(closeEvent, `picker defined a suggestion -> commit it`);
|
|
52359
|
+
commitUIStateAsAnswer(inputEl?.__uiStateController__, closeEvent);
|
|
52360
|
+
}
|
|
52277
52361
|
}
|
|
52278
52362
|
leaveExpanded({
|
|
52279
52363
|
isBack: closeEvent.detail.isCancel
|
|
@@ -52561,7 +52645,7 @@ const PickerContentInsidePopup = props => {
|
|
|
52561
52645
|
pointerInteractionOutsideEffect = "close",
|
|
52562
52646
|
// Named/forwarded rather than left in ...rest: rest goes to the picker
|
|
52563
52647
|
// element itself, not the popup, and this belongs to the popup.
|
|
52564
|
-
|
|
52648
|
+
backdropVariant,
|
|
52565
52649
|
dialogExpand,
|
|
52566
52650
|
dialogExpandX,
|
|
52567
52651
|
dialogExpandY,
|
|
@@ -52612,7 +52696,7 @@ const PickerContentInsidePopup = props => {
|
|
|
52612
52696
|
marginWithContainer: marginWithContainer === undefined && isPopover ? popoverSpacing : marginWithContainer,
|
|
52613
52697
|
scrollCapture: scrollCapture,
|
|
52614
52698
|
pointerInteractionOutsideEffect: pointerLock ? "capture" : pointerInteractionOutsideEffect,
|
|
52615
|
-
|
|
52699
|
+
backdropVariant: backdropVariant,
|
|
52616
52700
|
focusCapture: isPopover ? focusCapture : undefined,
|
|
52617
52701
|
expand: isPopover ? undefined : dialogExpand,
|
|
52618
52702
|
expandX: isPopover ? undefined : dialogExpandX,
|
|
@@ -59111,6 +59195,12 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
59111
59195
|
transparent
|
|
59112
59196
|
);
|
|
59113
59197
|
--picker-icon-color-disabled: var(--picker-icon-color-readonly);
|
|
59198
|
+
/* Where the slots sit INSIDE the box, visible only once the box is bigger
|
|
59199
|
+
than what it holds (a width/height the caller gave it). Distinct from
|
|
59200
|
+
textAlign, which places the text inside the value slot; this places the
|
|
59201
|
+
slots themselves. */
|
|
59202
|
+
--picker-align-x-default: flex-start;
|
|
59203
|
+
--picker-align-y-default: center;
|
|
59114
59204
|
}
|
|
59115
59205
|
}
|
|
59116
59206
|
|
|
@@ -59147,6 +59237,8 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
59147
59237
|
);
|
|
59148
59238
|
--x-picker-color: var(--picker-color);
|
|
59149
59239
|
--x-picker-icon-color: var(--picker-icon-color);
|
|
59240
|
+
--x-picker-align-x: var(--picker-align-x, var(--picker-align-x-default));
|
|
59241
|
+
--x-picker-align-y: var(--picker-align-y, var(--picker-align-y-default));
|
|
59150
59242
|
|
|
59151
59243
|
/* Deliberately NOT positioned: the popup children live in here, and a
|
|
59152
59244
|
layer="local" Popover/Dialog takes its nearest positioned ancestor as
|
|
@@ -59186,7 +59278,8 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
59186
59278
|
padding-left: 0;
|
|
59187
59279
|
flex: 1 1 auto;
|
|
59188
59280
|
flex-direction: row;
|
|
59189
|
-
align-items:
|
|
59281
|
+
align-items: var(--x-picker-align-y);
|
|
59282
|
+
justify-content: var(--x-picker-align-x);
|
|
59190
59283
|
background-color: var(--x-picker-background-color);
|
|
59191
59284
|
border-width: var(--picker-border-width);
|
|
59192
59285
|
border-style: solid;
|
|
@@ -59288,7 +59381,7 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
59288
59381
|
}
|
|
59289
59382
|
&[navi-single-line] {
|
|
59290
59383
|
.navi_picker_right_slot {
|
|
59291
|
-
align-self:
|
|
59384
|
+
align-self: var(--x-picker-align-y);
|
|
59292
59385
|
}
|
|
59293
59386
|
}
|
|
59294
59387
|
.navi_picker_input {
|
|
@@ -59372,6 +59465,10 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
59372
59465
|
&[data-variant="icon"] {
|
|
59373
59466
|
--picker-padding-x-default: 0;
|
|
59374
59467
|
--picker-padding-y-default: 0;
|
|
59468
|
+
/* Nothing but the icon is drawn here, so a width/height the caller gave
|
|
59469
|
+
it is a target area, not a text column: the icon belongs in its middle.
|
|
59470
|
+
A default, like everything else a variant moves, so alignX still wins. */
|
|
59471
|
+
--picker-align-x-default: center;
|
|
59375
59472
|
--picker-border-width: 0px; /* must carry a unit (px) — used in calc() to offset the custom input overlay */
|
|
59376
59473
|
--picker-border-color: transparent;
|
|
59377
59474
|
--picker-border-color-hover: var(--picker-border-color);
|
|
@@ -59473,7 +59570,15 @@ const PickerButton = props => {
|
|
|
59473
59570
|
usePickerErrorCallout(uiStateController, error);
|
|
59474
59571
|
return jsxs(Box, {
|
|
59475
59572
|
as: "div",
|
|
59476
|
-
ref: ref
|
|
59573
|
+
ref: ref
|
|
59574
|
+
// The flow this element really has (.navi_picker is display:inline-flex).
|
|
59575
|
+
// Left unsaid, Box reads a <div> as block and resolves alignX into a
|
|
59576
|
+
// text-align — which is a different intention entirely (that one is the
|
|
59577
|
+
// textAlign prop, placing the text INSIDE the value slot).
|
|
59578
|
+
,
|
|
59579
|
+
|
|
59580
|
+
inline: true,
|
|
59581
|
+
flex: "x",
|
|
59477
59582
|
baseClassName: "navi_picker",
|
|
59478
59583
|
pseudoClasses: PICKER_BUTTON_PSEUDO_CLASSES,
|
|
59479
59584
|
"data-variant": variant,
|
|
@@ -59731,6 +59836,11 @@ const PickerStyleCSSVars = {
|
|
|
59731
59836
|
"popupBorderRadius": "--picker-popup-border-radius",
|
|
59732
59837
|
"dialogBorderWidth": "--picker-dialog-border-width",
|
|
59733
59838
|
"slotSpacing": "--picker-slot-spacing",
|
|
59839
|
+
// alignX/alignY resolve to these two on a flex-x box; naming the CSS style
|
|
59840
|
+
// (not the prop) is what styleCSSVars matches, so justifyContent/alignItems
|
|
59841
|
+
// passed directly land in the same variables.
|
|
59842
|
+
"justifyContent": "--picker-align-x",
|
|
59843
|
+
"alignItems": "--picker-align-y",
|
|
59734
59844
|
"padding": "--picker-padding",
|
|
59735
59845
|
"paddingX": "--picker-padding-x",
|
|
59736
59846
|
"paddingY": "--picker-padding-y",
|