@jsenv/navi 0.29.305 → 0.29.307
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/jsenv_navi.js +298 -197
- package/dist/dev/jsenv_navi.js.map +13 -13
- package/dist/dev/jsenv_navi_side_effects.js +2 -0
- package/dist/dev/jsenv_navi_side_effects.js.map +2 -2
- package/dist/jsenv_navi.js +257 -188
- package/dist/jsenv_navi.js.map +13 -13
- package/dist/jsenv_navi_side_effects.js +2 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/dialog_shape.md +2 -2
- package/docs/popup_backdrop.md +28 -5
- package/docs/popup_open.md +6 -2
- package/package.json +3 -3
package/dist/jsenv_navi.js
CHANGED
|
@@ -12,7 +12,7 @@ import { useErrorBoundary, useLayoutEffect, useContext, useCallback, useRef, use
|
|
|
12
12
|
import { humanizeI18n, prefixFirstAndIndentRemainingLines, setRuntimeLangSource, formatDuration, formatMonth, formatDay, resolveTimeRangePrecision, formatDatePlaceholder, toDate, getRelativeDay, formatDayRelative, formatMonthPlaceholder, formatWeekPlaceholder, formatDatetimePlaceholder, formatDatetime, toTimeOfDay, formatTimeOfDay, formatTime, formatMinuteDuration, formatSecondDuration, formatHourDuration, formatTimeRelative, formatNumber, interpolateText, installInterpolateJsx } from "@jsenv/humanize";
|
|
13
13
|
export { createI18n, formatDatetime, formatDay, formatDayRelative, formatDuration, formatHourDuration, formatMinuteDuration, formatMonth, formatNumber, formatSecondDuration, formatTime, formatTimeOfDay, formatTimeRange, formatTimeRelative, interpolateText } from "@jsenv/humanize";
|
|
14
14
|
import { jsxs, jsx, Fragment } from "preact/jsx-runtime";
|
|
15
|
-
import { durationContainsNaN, compareTwoDurations, durationToSeconds, DISPLAYABLE_RULE, MAX_LINE_BREAKS_RULE, NO_EMOJI_RULE, SINGLE_SPACE_RULE, createValidity, resolveCharClass, getCharClassMessageKey, compileCharClassAnchored, compileCharClass, CHAR_CLASS_PRESETS, parseDuration, durationToISOString } from "@jsenv/validity";
|
|
15
|
+
import { TYPE_RULE, durationContainsNaN, compareTwoDurations, durationToSeconds, DISPLAYABLE_RULE, MAX_LINE_BREAKS_RULE, NO_EMOJI_RULE, SINGLE_SPACE_RULE, createValidity, resolveCharClass, getCharClassMessageKey, compileCharClassAnchored, compileCharClass, CHAR_CLASS_PRESETS, parseDuration, durationToISOString } from "@jsenv/validity";
|
|
16
16
|
export { compareTwoDurations, durationContainsNaN, durationToHours, durationToISOString, durationToMinutes, durationToNumber, durationToSeconds, durationToString, parseDuration } from "@jsenv/validity";
|
|
17
17
|
import { Suspense, createPortal, forwardRef } from "preact/compat";
|
|
18
18
|
|
|
@@ -4910,8 +4910,15 @@ const onUIStateControllerCreated = (uiStateController) => {
|
|
|
4910
4910
|
const onUIStateControllerDestroyed = (uiStateController) => {
|
|
4911
4911
|
const { id, name, controlType } = uiStateController;
|
|
4912
4912
|
if (id) {
|
|
4913
|
-
|
|
4914
|
-
|
|
4913
|
+
// Only the controller the id currently points at may take the entry away:
|
|
4914
|
+
// when two controls share an id, the one leaving would otherwise unregister
|
|
4915
|
+
// the one staying (see the warning in onUIStateControllerCreated), and the
|
|
4916
|
+
// same holds while a control is being replaced by its successor, which
|
|
4917
|
+
// registers during its render, before this cleanup runs.
|
|
4918
|
+
if (controllersById.get(id) === uiStateController) {
|
|
4919
|
+
controllersById.delete(id);
|
|
4920
|
+
naviJsRegistry.delete(id);
|
|
4921
|
+
}
|
|
4915
4922
|
}
|
|
4916
4923
|
const proxyFor = uiStateController.props["navi-control-proxy-for"];
|
|
4917
4924
|
if (proxyFor) {
|
|
@@ -8204,6 +8211,75 @@ const languagesSignal = computed(() => {
|
|
|
8204
8211
|
// (nor the "languagechange" listener above).
|
|
8205
8212
|
setRuntimeLangSource(() => languagesSignal.value);
|
|
8206
8213
|
|
|
8214
|
+
/**
|
|
8215
|
+
* Where navi meets @jsenv/validity.
|
|
8216
|
+
*
|
|
8217
|
+
* validity names a refusal with a key and its parameters rather than a
|
|
8218
|
+
* sentence, so that a field and a server can refuse in the same words in the
|
|
8219
|
+
* language of the person reading. navi is one of those two callers: it says the
|
|
8220
|
+
* sentence, in the browser, through `naviI18n`. The keys line up on purpose —
|
|
8221
|
+
* validity's `"single_space.start"` is navi's `"constraint.single_space.start"`
|
|
8222
|
+
* — so overriding a message is looking up one key, not going through a
|
|
8223
|
+
* translation table.
|
|
8224
|
+
*
|
|
8225
|
+
* The other direction is `constraintFromValidityRule`: a rule an app wrote for
|
|
8226
|
+
* its server, worn by a control as a constraint.
|
|
8227
|
+
*/
|
|
8228
|
+
|
|
8229
|
+
|
|
8230
|
+
/**
|
|
8231
|
+
* Turns a @jsenv/validity rule into a constraint a control can wear, so an app
|
|
8232
|
+
* rule written once — in the package its server reads too — is checked on both
|
|
8233
|
+
* sides instead of being written twice.
|
|
8234
|
+
*
|
|
8235
|
+
* @param {object} rule
|
|
8236
|
+
* `{ name, applyOn(ruleValue, value, ruleConfig) }`, the same object passed
|
|
8237
|
+
* to `createValidity({ rules })`.
|
|
8238
|
+
* @param {object} [ruleConfig]
|
|
8239
|
+
* What parameterizes the rule, under its own name — `{ maxWords: 40 }` for a
|
|
8240
|
+
* rule named `maxWords`. Pass `formatMessage` here to say the refusal through
|
|
8241
|
+
* the app's own i18n; without it the key is looked up in `naviI18n` under
|
|
8242
|
+
* `constraint.<key>`, and a rule answering with a finished sentence is shown
|
|
8243
|
+
* as-is.
|
|
8244
|
+
*
|
|
8245
|
+
* Call it once, at module level: a constraint rebuilt on every render is a new
|
|
8246
|
+
* object on every check.
|
|
8247
|
+
*/
|
|
8248
|
+
const constraintFromValidityRule = (rule, ruleConfig = {}) => {
|
|
8249
|
+
const { formatMessage, ...ruleParams } = ruleConfig;
|
|
8250
|
+
return {
|
|
8251
|
+
name: rule.name,
|
|
8252
|
+
check: (field) => {
|
|
8253
|
+
const result = rule.applyOn(
|
|
8254
|
+
ruleParams[rule.name],
|
|
8255
|
+
field.uiState,
|
|
8256
|
+
ruleParams,
|
|
8257
|
+
);
|
|
8258
|
+
if (!result) {
|
|
8259
|
+
return null;
|
|
8260
|
+
}
|
|
8261
|
+
if (typeof result === "string") {
|
|
8262
|
+
return result;
|
|
8263
|
+
}
|
|
8264
|
+
if (formatMessage) {
|
|
8265
|
+
return formatMessage(result.key, result.params);
|
|
8266
|
+
}
|
|
8267
|
+
return naviI18nFromValidityMessage(result);
|
|
8268
|
+
},
|
|
8269
|
+
};
|
|
8270
|
+
};
|
|
8271
|
+
|
|
8272
|
+
const naviI18nFromValidityMessage = ({ key, params }) => {
|
|
8273
|
+
if (params && typeof params.max === "number") {
|
|
8274
|
+
// Lets a template pluralize on the bound it names: "[max] retour[s]".
|
|
8275
|
+
return naviI18n(`constraint.${key}`, {
|
|
8276
|
+
...params,
|
|
8277
|
+
s: params.max > 1 ? "s" : "",
|
|
8278
|
+
});
|
|
8279
|
+
}
|
|
8280
|
+
return naviI18n(`constraint.${key}`, params);
|
|
8281
|
+
};
|
|
8282
|
+
|
|
8207
8283
|
/**
|
|
8208
8284
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation
|
|
8209
8285
|
*/
|
|
@@ -8353,9 +8429,8 @@ const PATTERN_CONSTRAINT = {
|
|
|
8353
8429
|
};
|
|
8354
8430
|
CONSTRAINT_ATTRIBUTE_SET.add("pattern");
|
|
8355
8431
|
|
|
8356
|
-
//
|
|
8357
|
-
|
|
8358
|
-
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
8432
|
+
// What an address is, is @jsenv/validity's `type: "email"`: a server checking
|
|
8433
|
+
// the value again refuses it for the same reason and in the same words.
|
|
8359
8434
|
const TYPE_EMAIL_CONSTRAINT = {
|
|
8360
8435
|
name: "type_email",
|
|
8361
8436
|
messageAttribute: "data-type-message",
|
|
@@ -8369,14 +8444,11 @@ const TYPE_EMAIL_CONSTRAINT = {
|
|
|
8369
8444
|
if (!valueAsString) {
|
|
8370
8445
|
return null;
|
|
8371
8446
|
}
|
|
8372
|
-
|
|
8447
|
+
const result = TYPE_RULE.applyOn("email", valueAsString);
|
|
8448
|
+
if (!result) {
|
|
8373
8449
|
return null;
|
|
8374
8450
|
}
|
|
8375
|
-
|
|
8376
|
-
if (!valueAsString.includes("@")) {
|
|
8377
|
-
return naviI18n("constraint.type.email.at", { value: valueAsString });
|
|
8378
|
-
}
|
|
8379
|
-
return naviI18n("constraint.type.email.invalid");
|
|
8451
|
+
return naviI18nFromValidityMessage(result);
|
|
8380
8452
|
},
|
|
8381
8453
|
};
|
|
8382
8454
|
|
|
@@ -11535,75 +11607,6 @@ const isFunctionButNotAnActionFunction = (action) => {
|
|
|
11535
11607
|
return typeof action === "function" && !action.isAction;
|
|
11536
11608
|
};
|
|
11537
11609
|
|
|
11538
|
-
/**
|
|
11539
|
-
* Where navi meets @jsenv/validity.
|
|
11540
|
-
*
|
|
11541
|
-
* validity names a refusal with a key and its parameters rather than a
|
|
11542
|
-
* sentence, so that a field and a server can refuse in the same words in the
|
|
11543
|
-
* language of the person reading. navi is one of those two callers: it says the
|
|
11544
|
-
* sentence, in the browser, through `naviI18n`. The keys line up on purpose —
|
|
11545
|
-
* validity's `"single_space.start"` is navi's `"constraint.single_space.start"`
|
|
11546
|
-
* — so overriding a message is looking up one key, not going through a
|
|
11547
|
-
* translation table.
|
|
11548
|
-
*
|
|
11549
|
-
* The other direction is `constraintFromValidityRule`: a rule an app wrote for
|
|
11550
|
-
* its server, worn by a control as a constraint.
|
|
11551
|
-
*/
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
/**
|
|
11555
|
-
* Turns a @jsenv/validity rule into a constraint a control can wear, so an app
|
|
11556
|
-
* rule written once — in the package its server reads too — is checked on both
|
|
11557
|
-
* sides instead of being written twice.
|
|
11558
|
-
*
|
|
11559
|
-
* @param {object} rule
|
|
11560
|
-
* `{ name, applyOn(ruleValue, value, ruleConfig) }`, the same object passed
|
|
11561
|
-
* to `createValidity({ rules })`.
|
|
11562
|
-
* @param {object} [ruleConfig]
|
|
11563
|
-
* What parameterizes the rule, under its own name — `{ maxWords: 40 }` for a
|
|
11564
|
-
* rule named `maxWords`. Pass `formatMessage` here to say the refusal through
|
|
11565
|
-
* the app's own i18n; without it the key is looked up in `naviI18n` under
|
|
11566
|
-
* `constraint.<key>`, and a rule answering with a finished sentence is shown
|
|
11567
|
-
* as-is.
|
|
11568
|
-
*
|
|
11569
|
-
* Call it once, at module level: a constraint rebuilt on every render is a new
|
|
11570
|
-
* object on every check.
|
|
11571
|
-
*/
|
|
11572
|
-
const constraintFromValidityRule = (rule, ruleConfig = {}) => {
|
|
11573
|
-
const { formatMessage, ...ruleParams } = ruleConfig;
|
|
11574
|
-
return {
|
|
11575
|
-
name: rule.name,
|
|
11576
|
-
check: (field) => {
|
|
11577
|
-
const result = rule.applyOn(
|
|
11578
|
-
ruleParams[rule.name],
|
|
11579
|
-
field.uiState,
|
|
11580
|
-
ruleParams,
|
|
11581
|
-
);
|
|
11582
|
-
if (!result) {
|
|
11583
|
-
return null;
|
|
11584
|
-
}
|
|
11585
|
-
if (typeof result === "string") {
|
|
11586
|
-
return result;
|
|
11587
|
-
}
|
|
11588
|
-
if (formatMessage) {
|
|
11589
|
-
return formatMessage(result.key, result.params);
|
|
11590
|
-
}
|
|
11591
|
-
return naviI18nFromValidityMessage(result);
|
|
11592
|
-
},
|
|
11593
|
-
};
|
|
11594
|
-
};
|
|
11595
|
-
|
|
11596
|
-
const naviI18nFromValidityMessage = ({ key, params }) => {
|
|
11597
|
-
if (params && typeof params.max === "number") {
|
|
11598
|
-
// Lets a template pluralize on the bound it names: "[max] retour[s]".
|
|
11599
|
-
return naviI18n(`constraint.${key}`, {
|
|
11600
|
-
...params,
|
|
11601
|
-
s: params.max > 1 ? "s" : "",
|
|
11602
|
-
});
|
|
11603
|
-
}
|
|
11604
|
-
return naviI18n(`constraint.${key}`, params);
|
|
11605
|
-
};
|
|
11606
|
-
|
|
11607
11610
|
/**
|
|
11608
11611
|
* `data-displayable` — the value must be something the layout can actually
|
|
11609
11612
|
* draw. What it refuses and why lives in @jsenv/validity's DISPLAYABLE_RULE:
|
|
@@ -34723,17 +34726,21 @@ const useUIStateController = (
|
|
|
34723
34726
|
if (el) {
|
|
34724
34727
|
el.__uiStateController__ = controller;
|
|
34725
34728
|
}
|
|
34726
|
-
// Re-register so the radio registry stays in sync when props.ref changes
|
|
34727
|
-
// identity (e.g. across a Suspense boundary). The render-phase call in
|
|
34728
|
-
// control_hooks.jsx handles the initial mount; this call handles re-runs.
|
|
34729
|
-
onUIStateControllerCreated(controller);
|
|
34730
34729
|
return () => {
|
|
34731
34730
|
if (el && el.__uiStateController__ === controller) {
|
|
34732
34731
|
delete el.__uiStateController__;
|
|
34733
34732
|
}
|
|
34734
|
-
onUIStateControllerDestroyed(controller);
|
|
34735
34733
|
};
|
|
34736
34734
|
}, [controllerRef]);
|
|
34735
|
+
// The registry entry lives as long as the component does. Registration is
|
|
34736
|
+
// done by the render (see useInteractiveProps); this cleanup is the only
|
|
34737
|
+
// thing that takes the entry away, so it must not follow the ref: a ref
|
|
34738
|
+
// identity change would run it right after the render that registered.
|
|
34739
|
+
useLayoutEffect(() => {
|
|
34740
|
+
return () => {
|
|
34741
|
+
onUIStateControllerDestroyed(controller);
|
|
34742
|
+
};
|
|
34743
|
+
}, []);
|
|
34737
34744
|
|
|
34738
34745
|
const { parentUIStateController: parentController } = scope;
|
|
34739
34746
|
useLayoutEffect(() => {
|
|
@@ -38254,11 +38261,13 @@ const useInteractiveProps = (props, {
|
|
|
38254
38261
|
// an action's own completion side effect — has to read the signal to get an
|
|
38255
38262
|
// answer that is not one frame late (see BUSY_CONSTRAINT).
|
|
38256
38263
|
uiStateController.boundAction = boundAction;
|
|
38264
|
+
// "This id is that controller", stated on every render: a Suspense boundary
|
|
38265
|
+
// parking a subtree runs every hook cleanup below it — the unregistration
|
|
38266
|
+
// included — while the controllers themselves survive, and readers look a
|
|
38267
|
+
// controller up by id while rendering, before any effect can run again.
|
|
38268
|
+
// Placed after controlHostProps is set, which the constraints read.
|
|
38269
|
+
onUIStateControllerCreated(uiStateController);
|
|
38257
38270
|
if (firstRender) {
|
|
38258
|
-
// Deferred from the factory so these run after controlHostProps is set.
|
|
38259
|
-
// Constraints like READONLY_CONSTRAINT and findControlProxyTargetController
|
|
38260
|
-
// read controlHostProps — calling these earlier would throw or produce wrong results.
|
|
38261
|
-
onUIStateControllerCreated(uiStateController);
|
|
38262
38271
|
uiStateController.rules.validation.checkValidity();
|
|
38263
38272
|
}
|
|
38264
38273
|
return [controlRootProps, controlHostProps];
|
|
@@ -48112,10 +48121,11 @@ const ButtonUI = props => {
|
|
|
48112
48121
|
cta,
|
|
48113
48122
|
spacing,
|
|
48114
48123
|
contentDisplay,
|
|
48115
|
-
//
|
|
48116
|
-
//
|
|
48117
|
-
// the outline belongs around the whole
|
|
48118
|
-
|
|
48124
|
+
// Who draws the loading outline. Left unsaid, the button does; "custom"
|
|
48125
|
+
// says someone else does — a button that is one half of a bigger control:
|
|
48126
|
+
// what is busy is the control, and the outline belongs around the whole
|
|
48127
|
+
// of it (see split_button.jsx).
|
|
48128
|
+
loadingOutline
|
|
48119
48129
|
} = props;
|
|
48120
48130
|
const [buttonControlRootProps, buttonControlHostProps, controlChildrenWrapperProps] = useControlProps(props, {
|
|
48121
48131
|
controlType: "button"
|
|
@@ -48241,7 +48251,7 @@ const ButtonUI = props => {
|
|
|
48241
48251
|
visualSelector: visualSelector,
|
|
48242
48252
|
hasChildUsingForwardedProps: true,
|
|
48243
48253
|
children: [jsx(LoadingOutline, {
|
|
48244
|
-
loading: loadingOutline
|
|
48254
|
+
loading: loadingOutline === "custom" ? false : loading,
|
|
48245
48255
|
inset: -1,
|
|
48246
48256
|
color: "var(--button-loader-color)"
|
|
48247
48257
|
}), jsx(ControlChildrenWrapper, {
|
|
@@ -58765,6 +58775,15 @@ const css$E = /* css */`
|
|
|
58765
58775
|
to filter). An entrance animation moves the dialog through scale and
|
|
58766
58776
|
transform instead, which compose under it: see popup_css.js. */
|
|
58767
58777
|
|
|
58778
|
+
/* animation="growing" brings its own wall: the popup is the anchor
|
|
58779
|
+
continued, not a surface shown over a page that goes on being read (see
|
|
58780
|
+
navi_css_vars.js for the paint). Before the rules below, so everything
|
|
58781
|
+
said out loud — an outside press that captures, a backdropVariant, the
|
|
58782
|
+
two paint props — still wins over what the animation assumes. */
|
|
58783
|
+
&[data-growing] {
|
|
58784
|
+
--backdrop-background: var(--navi-backdrop-grow-background);
|
|
58785
|
+
--backdrop-filter: var(--navi-backdrop-grow-backdrop-filter);
|
|
58786
|
+
}
|
|
58768
58787
|
&[data-pointer-interaction-outside="capture"] {
|
|
58769
58788
|
--backdrop-background: var(--navi-backdrop-capture-background);
|
|
58770
58789
|
--backdrop-filter: var(--navi-backdrop-capture-backdrop-filter);
|
|
@@ -58935,6 +58954,15 @@ const css$E = /* css */`
|
|
|
58935
58954
|
--backdrop-background: var(--navi-backdrop-close-background);
|
|
58936
58955
|
--backdrop-filter: var(--navi-backdrop-close-backdrop-filter);
|
|
58937
58956
|
}
|
|
58957
|
+
/* animation="growing" brings its own wall: the popup is the anchor
|
|
58958
|
+
continued, not a surface shown over a page that goes on being read (see
|
|
58959
|
+
navi_css_vars.js for the paint). Before the rules below, so everything
|
|
58960
|
+
said out loud — an outside press that captures, a backdropVariant, the
|
|
58961
|
+
two paint props — still wins over what the animation assumes. */
|
|
58962
|
+
&[data-growing] {
|
|
58963
|
+
--backdrop-background: var(--navi-backdrop-grow-background);
|
|
58964
|
+
--backdrop-filter: var(--navi-backdrop-grow-backdrop-filter);
|
|
58965
|
+
}
|
|
58938
58966
|
&[data-pointer-interaction-outside="capture"] {
|
|
58939
58967
|
--backdrop-background: var(--navi-backdrop-capture-background);
|
|
58940
58968
|
--backdrop-filter: var(--navi-backdrop-capture-backdrop-filter);
|
|
@@ -59152,7 +59180,8 @@ const css$E = /* css */`
|
|
|
59152
59180
|
* @param {"auto"|"discrete"|"invisible"} [props.backdropVariant="auto"] - How
|
|
59153
59181
|
* visible the backdrop is, independently of what it does. `"auto"`: the
|
|
59154
59182
|
* paint `pointerInteractionOutsideEffect` implies (dimmed for
|
|
59155
|
-
* `"close"`/`"cancel"`, blurred glass for `"capture"`)
|
|
59183
|
+
* `"close"`/`"cancel"`, blurred glass for `"capture"`), or the opaque wall
|
|
59184
|
+
* `animation="growing"` asks for. `"discrete"`: a
|
|
59156
59185
|
* barely-there dim. `"invisible"`: fully transparent — a wall that is not
|
|
59157
59186
|
* seen, still catching every press. This only changes how much the dialog
|
|
59158
59187
|
* insists visually, never what an outside click does; whether there is a
|
|
@@ -59185,7 +59214,11 @@ const css$E = /* css */`
|
|
|
59185
59214
|
* over it. It needs an anchor (whatever opened the dialog, or the `anchor`
|
|
59186
59215
|
* prop) and grows into whatever inside the dialog carries `data-grow`, the
|
|
59187
59216
|
* dialog itself when nothing does. `"auto"` never picks it: only the caller
|
|
59188
|
-
* knows the two boxes are one object.
|
|
59217
|
+
* knows the two boxes are one object. It also brings its own backdrop —
|
|
59218
|
+
* opaque and blurred (`--navi-backdrop-grow-*`), the page it came out of
|
|
59219
|
+
* being what the movement leaves rather than a context to keep readable;
|
|
59220
|
+
* `backdropVariant="discrete"` asks for the light wash back. See
|
|
59221
|
+
* `popup_grow.js`.
|
|
59189
59222
|
* @param {"box"|"scene"} [props.grow="box"] - Under `animation="growing"`,
|
|
59190
59223
|
* what the anchor and what it grows into are to each other, which decides
|
|
59191
59224
|
* how their pictures sit in the box moving between them. `"box"`: one
|
|
@@ -60206,6 +60239,7 @@ const useDialogProps = props => {
|
|
|
60206
60239
|
"animationDuration": rest.animationDuration,
|
|
60207
60240
|
"data-pointer-interaction-outside": pointerInteractionOutsideEffect,
|
|
60208
60241
|
"data-backdrop-variant": backdropVariant,
|
|
60242
|
+
"data-growing": growing ? "" : undefined,
|
|
60209
60243
|
backdropColor,
|
|
60210
60244
|
backdropFilter
|
|
60211
60245
|
});
|
|
@@ -60241,6 +60275,11 @@ const useDialogProps = props => {
|
|
|
60241
60275
|
// (and harmless for a local dialog, whose real backdrop element gets it
|
|
60242
60276
|
// via backdropProps).
|
|
60243
60277
|
"data-backdrop-variant": backdropVariant,
|
|
60278
|
+
// That this popup is the anchor continued rather than a surface shown
|
|
60279
|
+
// over the page, which is what its backdrop is painted from (see this
|
|
60280
|
+
// file's CSS). It cannot be read off navi-animation: growing arms no CSS
|
|
60281
|
+
// animation of its own, so that attribute is deliberately absent here.
|
|
60282
|
+
"data-growing": growing ? "" : undefined,
|
|
60244
60283
|
// That this dialog has no wall, said in the DOM: the browser generates a
|
|
60245
60284
|
// ::backdrop for the popover a wall-less top-layer dialog is shown as,
|
|
60246
60285
|
// and the CSS above keys off this to leave it unpainted. Also the only
|
|
@@ -60394,6 +60433,9 @@ const DIALOG_STYLE_CSS_VARS = {
|
|
|
60394
60433
|
animationDuration: "--popup-animation-duration",
|
|
60395
60434
|
backdropColor: "--backdrop-background",
|
|
60396
60435
|
backdropFilter: "--backdrop-filter",
|
|
60436
|
+
backgroundColor: "--dialog-background-color",
|
|
60437
|
+
boxShadow: "--dialog-box-shadow",
|
|
60438
|
+
borderWidth: "--dialog-border-width",
|
|
60397
60439
|
borderRadius: "--dialog-border-radius",
|
|
60398
60440
|
minWidth: "--dialog-min-width",
|
|
60399
60441
|
maxWidth: "--dialog-max-width",
|
|
@@ -61981,10 +62023,17 @@ const POPOVER_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible",
|
|
|
61981
62023
|
// Lets consumers pass animationDuration="0.5s"/borderRadius="8px" as regular
|
|
61982
62024
|
// props; Box maps them to the CSS vars for us (see box.jsx's styleCSSVars
|
|
61983
62025
|
// handling).
|
|
62026
|
+
// The paint props go through the popover's own vars rather than the properties
|
|
62027
|
+
// themselves, so the stylesheet keeps the last word on them (the same reason
|
|
62028
|
+
// dialog.jsx gives for borderRadius).
|
|
61984
62029
|
const POPUP_STYLE_CSS_VARS = {
|
|
61985
62030
|
animationDuration: "--popup-animation-duration",
|
|
61986
62031
|
backdropColor: "--backdrop-background",
|
|
61987
62032
|
backdropFilter: "--backdrop-filter",
|
|
62033
|
+
backgroundColor: "--popover-background-color",
|
|
62034
|
+
boxShadow: "--popover-box-shadow",
|
|
62035
|
+
borderWidth: "--popover-border-width",
|
|
62036
|
+
borderRadius: "--popover-border-radius",
|
|
61988
62037
|
minWidth: "--popover-min-width",
|
|
61989
62038
|
maxWidth: "--popover-max-width",
|
|
61990
62039
|
minHeight: "--popover-min-height",
|
|
@@ -62247,83 +62296,56 @@ const Popup = props => {
|
|
|
62247
62296
|
Popup.Close = PopupClose;
|
|
62248
62297
|
|
|
62249
62298
|
installImportMetaCssBuild(import.meta);
|
|
62250
|
-
const css$B = /* css */`.
|
|
62251
|
-
|
|
62252
|
-
|
|
62253
|
-
|
|
62254
|
-
|
|
62255
|
-
|
|
62256
|
-
|
|
62257
|
-
--popover-outline-width: var(--picker-outline-width);
|
|
62258
|
-
--popover-outline-color: var(--picker-outline-color);
|
|
62259
|
-
--popover-box-shadow: var(--picker-popup-box-shadow, var(--navi-popup-box-shadow));
|
|
62260
|
-
--popover-max-height: var(--picker-popover-max-height);
|
|
62261
|
-
min-width: var(--picker-popover-min-width, var(--anchor-width, 0px));
|
|
62262
|
-
cursor: default;
|
|
62263
|
-
|
|
62264
|
-
& .navi_list_container {
|
|
62265
|
-
--list-border-radius: max(0px,
|
|
62266
|
-
var(--popover-border-radius) - var(--popover-border-width));
|
|
62267
|
-
overscroll-behavior: none;
|
|
62268
|
-
width: 100%;
|
|
62299
|
+
const css$B = /* css */`.navi_popover[data-picker-popup] {
|
|
62300
|
+
--popover-border-radius: var(--picker-border-radius);
|
|
62301
|
+
--popover-border-width: var(--picker-border-width);
|
|
62302
|
+
--popover-border-color: var(--x-picker-border-color);
|
|
62303
|
+
--popover-outline-width: var(--picker-outline-width);
|
|
62304
|
+
--popover-outline-color: var(--picker-outline-color);
|
|
62305
|
+
cursor: default;
|
|
62269
62306
|
|
|
62270
|
-
|
|
62271
|
-
|
|
62272
|
-
|
|
62273
|
-
|
|
62274
|
-
}
|
|
62307
|
+
&:not([navi-hidden]) {
|
|
62308
|
+
flex-direction: column;
|
|
62309
|
+
display: flex;
|
|
62310
|
+
}
|
|
62275
62311
|
|
|
62276
|
-
|
|
62277
|
-
|
|
62278
|
-
border-
|
|
62279
|
-
|
|
62312
|
+
& .navi_list_container {
|
|
62313
|
+
--list-border-radius: max(0px,
|
|
62314
|
+
var(--popover-border-radius) - var(--popover-border-width));
|
|
62315
|
+
overscroll-behavior: none;
|
|
62316
|
+
width: 100%;
|
|
62280
62317
|
|
|
62281
|
-
|
|
62282
|
-
|
|
62283
|
-
display: flex;
|
|
62284
|
-
}
|
|
62318
|
+
&:not([data-overflow-visible]) {
|
|
62319
|
+
overflow: auto;
|
|
62285
62320
|
}
|
|
62286
62321
|
}
|
|
62322
|
+
}
|
|
62287
62323
|
|
|
62288
|
-
|
|
62289
|
-
|
|
62290
|
-
|
|
62291
|
-
--dialog-border-width: var(--picker-dialog-border-width, 0px);
|
|
62292
|
-
--dialog-border-color: var(--x-picker-border-color);
|
|
62293
|
-
--dialog-background-color: var(--picker-popup-background-color, var(--navi-popup-background-color));
|
|
62294
|
-
--dialog-outline-width: var(--picker-outline-width);
|
|
62295
|
-
--dialog-outline-color: var(--picker-outline-color);
|
|
62296
|
-
--dialog-box-shadow: var(--picker-popup-box-shadow, var(--navi-popup-box-shadow));
|
|
62297
|
-
--dialog-min-width: var(--picker-dialog-min-width);
|
|
62298
|
-
--dialog-min-height: var(--picker-dialog-min-height);
|
|
62299
|
-
--dialog-max-width: var(--picker-dialog-max-width);
|
|
62300
|
-
--dialog-max-height: var(--picker-dialog-max-height);
|
|
62301
|
-
cursor: default;
|
|
62324
|
+
.navi_picker[aria-haspopup="listbox"][aria-expanded="true"][navi-popover-mode="overlay"] {
|
|
62325
|
+
border-color: #0000;
|
|
62326
|
+
}
|
|
62302
62327
|
|
|
62303
|
-
|
|
62304
|
-
|
|
62305
|
-
|
|
62328
|
+
.navi_dialog[data-picker-popup] {
|
|
62329
|
+
--dialog-border-radius: var(--picker-border-radius);
|
|
62330
|
+
--dialog-border-color: var(--x-picker-border-color);
|
|
62331
|
+
--dialog-outline-width: var(--picker-outline-width);
|
|
62332
|
+
--dialog-outline-color: var(--picker-outline-color);
|
|
62333
|
+
cursor: default;
|
|
62306
62334
|
|
|
62307
|
-
|
|
62308
|
-
|
|
62309
|
-
|
|
62310
|
-
overscroll-behavior: none;
|
|
62311
|
-
width: 100%;
|
|
62335
|
+
&[open] {
|
|
62336
|
+
flex-direction: column;
|
|
62337
|
+
}
|
|
62312
62338
|
|
|
62313
|
-
|
|
62314
|
-
|
|
62315
|
-
|
|
62316
|
-
|
|
62317
|
-
|
|
62339
|
+
& .navi_list_container {
|
|
62340
|
+
--list-border-radius: max(0px,
|
|
62341
|
+
var(--dialog-border-radius) - var(--dialog-border-width));
|
|
62342
|
+
overscroll-behavior: none;
|
|
62343
|
+
width: 100%;
|
|
62318
62344
|
|
|
62319
|
-
|
|
62320
|
-
|
|
62345
|
+
&:not([data-overflow-visible]) {
|
|
62346
|
+
overflow: auto;
|
|
62321
62347
|
}
|
|
62322
62348
|
}
|
|
62323
|
-
|
|
62324
|
-
&[data-popup-width-fit-content] {
|
|
62325
|
-
--picker-popover-min-width: 0px;
|
|
62326
|
-
}
|
|
62327
62349
|
}
|
|
62328
62350
|
`;
|
|
62329
62351
|
const PickerCustomResolver = props => {
|
|
@@ -63087,6 +63109,22 @@ const PickerContentInsidePopup = props => {
|
|
|
63087
63109
|
// dialogMaxWidth="var(--anchor-width)", as a ceiling) — what keeps a card
|
|
63088
63110
|
// its own width once lifted. Dialog's own `sizeFromAnchor`.
|
|
63089
63111
|
dialogSizeFromAnchor,
|
|
63112
|
+
// The caller's word on the popup's own box. Written on the popup element,
|
|
63113
|
+
// where Popover/Dialog map them to their own vars, rather than as vars on
|
|
63114
|
+
// the picker: a var on the picker inherits into everything the popup
|
|
63115
|
+
// holds, and the pickers in there would read it as their own.
|
|
63116
|
+
popupBackgroundColor,
|
|
63117
|
+
popupBorderRadius,
|
|
63118
|
+
popupBoxShadow,
|
|
63119
|
+
popoverMaxHeight,
|
|
63120
|
+
dialogBorderWidth,
|
|
63121
|
+
dialogMinWidth,
|
|
63122
|
+
dialogMinHeight,
|
|
63123
|
+
dialogMaxWidth,
|
|
63124
|
+
dialogMaxHeight,
|
|
63125
|
+
// The popover is at least as wide as the trigger. True when the CONTENT
|
|
63126
|
+
// should size it (a Wheel) instead of being stretched to the trigger.
|
|
63127
|
+
popupWidthFitContent,
|
|
63090
63128
|
animation,
|
|
63091
63129
|
grow,
|
|
63092
63130
|
animationDuration,
|
|
@@ -63105,7 +63143,6 @@ const PickerContentInsidePopup = props => {
|
|
|
63105
63143
|
return jsx(Next, {
|
|
63106
63144
|
"aria-haspopup": isPopover ? "listbox" : "dialog",
|
|
63107
63145
|
"navi-popover-mode": isPopover ? popoverMode : undefined,
|
|
63108
|
-
"data-dialog-size-from-anchor": !isPopover && dialogSizeFromAnchor ? "" : undefined,
|
|
63109
63146
|
...rest,
|
|
63110
63147
|
// On popupProps already (see the picker's popup assembly); they mean
|
|
63111
63148
|
// nothing to the picker element.
|
|
@@ -63147,7 +63184,23 @@ const PickerContentInsidePopup = props => {
|
|
|
63147
63184
|
})
|
|
63148
63185
|
}) : jsx(Popup, {
|
|
63149
63186
|
...popupProps,
|
|
63150
|
-
"data-testid": popupTestId
|
|
63187
|
+
"data-testid": popupTestId
|
|
63188
|
+
// What the CSS above keys on: this picker's own popup, and no other
|
|
63189
|
+
// popup below the picker.
|
|
63190
|
+
,
|
|
63191
|
+
"data-picker-popup": "",
|
|
63192
|
+
backgroundColor: popupBackgroundColor,
|
|
63193
|
+
borderRadius: popupBorderRadius,
|
|
63194
|
+
boxShadow: popupBoxShadow,
|
|
63195
|
+
borderWidth: isPopover ? undefined : dialogBorderWidth,
|
|
63196
|
+
minWidth: isPopover ? popupWidthFitContent ? undefined : "var(--anchor-width, 0px)" : dialogMinWidth,
|
|
63197
|
+
minHeight: isPopover ? undefined : dialogMinHeight
|
|
63198
|
+
// As wide as the trigger under dialogSizeFromAnchor: Dialog's own
|
|
63199
|
+
// sizeFromAnchor is a floor, this is the ceiling. A dialogMaxWidth of
|
|
63200
|
+
// the caller's still wins.
|
|
63201
|
+
,
|
|
63202
|
+
maxWidth: isPopover ? undefined : dialogMaxWidth === undefined && dialogSizeFromAnchor ? "var(--anchor-width)" : dialogMaxWidth,
|
|
63203
|
+
maxHeight: isPopover ? popoverMaxHeight : dialogMaxHeight,
|
|
63151
63204
|
mode: mode,
|
|
63152
63205
|
layer: popupLayer,
|
|
63153
63206
|
animation: animation,
|
|
@@ -71187,6 +71240,26 @@ const css$t = /* css */`@layer navi {
|
|
|
71187
71240
|
}
|
|
71188
71241
|
}
|
|
71189
71242
|
|
|
71243
|
+
& [data-picker-content] {
|
|
71244
|
+
--picker-padding: initial;
|
|
71245
|
+
--picker-padding-x: initial;
|
|
71246
|
+
--picker-padding-y: initial;
|
|
71247
|
+
--picker-padding-top: initial;
|
|
71248
|
+
--picker-padding-right: initial;
|
|
71249
|
+
--picker-padding-bottom: initial;
|
|
71250
|
+
--picker-padding-left: initial;
|
|
71251
|
+
--picker-press-padding: initial;
|
|
71252
|
+
--picker-press-padding-x: initial;
|
|
71253
|
+
--picker-press-padding-y: initial;
|
|
71254
|
+
--picker-press-padding-top: initial;
|
|
71255
|
+
--picker-press-padding-right: initial;
|
|
71256
|
+
--picker-press-padding-bottom: initial;
|
|
71257
|
+
--picker-press-padding-left: initial;
|
|
71258
|
+
--picker-slot-spacing: initial;
|
|
71259
|
+
--picker-align-x: initial;
|
|
71260
|
+
--picker-align-y: initial;
|
|
71261
|
+
}
|
|
71262
|
+
|
|
71190
71263
|
& .navi_picker_content {
|
|
71191
71264
|
--x-corner-top-left-radius: initial;
|
|
71192
71265
|
--x-corner-top-right-radius: initial;
|
|
@@ -71388,11 +71461,6 @@ const PickerButton = props => {
|
|
|
71388
71461
|
placeholder,
|
|
71389
71462
|
ui,
|
|
71390
71463
|
maxLines: maxLinesProp = 1,
|
|
71391
|
-
// By default the popover is at least as wide as the trigger (min-width:
|
|
71392
|
-
// --anchor-width). Set true when the CONTENT should dictate the popover width
|
|
71393
|
-
// (e.g. a Wheel) instead of being stretched to the trigger — see
|
|
71394
|
-
// picker_custom.jsx.
|
|
71395
|
-
popupWidthFitContent,
|
|
71396
71464
|
// Adds a clear button to the right slot, the same one type="search" puts at
|
|
71397
71465
|
// the end of an input: a picker holds a value the user chose, and unsetting
|
|
71398
71466
|
// it should not require reopening the popup to hunt for a "none" entry.
|
|
@@ -71417,7 +71485,13 @@ const PickerButton = props => {
|
|
|
71417
71485
|
// (see PickerInput below).
|
|
71418
71486
|
role,
|
|
71419
71487
|
readOnly,
|
|
71420
|
-
error
|
|
71488
|
+
error,
|
|
71489
|
+
// Who draws the loading outline. Left unsaid, the picker does, around its
|
|
71490
|
+
// own box; "custom" says someone else does — a bigger surface this box is
|
|
71491
|
+
// a piece of, waiting as a whole (see the `loadingOutline` doc).
|
|
71492
|
+
loadingOutline,
|
|
71493
|
+
// Where the outline runs, in px from the picker's box: negative is outside.
|
|
71494
|
+
loadingOutlineInset = -2
|
|
71421
71495
|
} = props;
|
|
71422
71496
|
// A word in a sentence is never truncated — and the clamp's overflow: hidden
|
|
71423
71497
|
// would cut its dotted underline, which sits on the edge of the line box
|
|
@@ -71536,7 +71610,6 @@ const PickerButton = props => {
|
|
|
71536
71610
|
"navi-single-line": isSingleLine ? "" : undefined,
|
|
71537
71611
|
"navi-ui-custom": ui === "default" ? undefined : "",
|
|
71538
71612
|
"data-readonly-opens": readOnlyOpens ? "" : undefined,
|
|
71539
|
-
"data-popup-width-fit-content": popupWidthFitContent ? "" : undefined,
|
|
71540
71613
|
...pickerRemainingProps,
|
|
71541
71614
|
basePseudoState: basePseudoState,
|
|
71542
71615
|
styleCSSVars: PickerStyleCSSVars,
|
|
@@ -71551,9 +71624,10 @@ const PickerButton = props => {
|
|
|
71551
71624
|
openWhileReadOnly: undefined,
|
|
71552
71625
|
ui: undefined,
|
|
71553
71626
|
maxLines: undefined,
|
|
71554
|
-
popupWidthFitContent: undefined,
|
|
71555
71627
|
error: undefined,
|
|
71556
|
-
dayLabel: undefined
|
|
71628
|
+
dayLabel: undefined,
|
|
71629
|
+
loadingOutline: undefined,
|
|
71630
|
+
loadingOutlineInset: undefined
|
|
71557
71631
|
// This wrapper will receive keyboard event bubbling from the picker popup content
|
|
71558
71632
|
// we re-dispatch on the input (to get escape to close for instance)
|
|
71559
71633
|
,
|
|
@@ -71584,9 +71658,9 @@ const PickerButton = props => {
|
|
|
71584
71658
|
children: jsxs(PickerContext.Provider, {
|
|
71585
71659
|
value: pickerContext,
|
|
71586
71660
|
children: [variant === "headless" ? null : jsx(LoadingOutline, {
|
|
71587
|
-
loading: loading,
|
|
71661
|
+
loading: loadingOutline === "custom" ? false : loading,
|
|
71588
71662
|
color: "var(--picker-loader-color)",
|
|
71589
|
-
inset:
|
|
71663
|
+
inset: loadingOutlineInset
|
|
71590
71664
|
}), jsx(PickerInput, {
|
|
71591
71665
|
role: hostRole,
|
|
71592
71666
|
"aria-labelledby": hostLabelId,
|
|
@@ -71820,7 +71894,7 @@ const PickerClear = ({
|
|
|
71820
71894
|
// removed is the whole field's, and the picker already draws the wait
|
|
71821
71895
|
// around all of it. Two outlines for one wait is one too many.
|
|
71822
71896
|
,
|
|
71823
|
-
loadingOutline:
|
|
71897
|
+
loadingOutline: "custom"
|
|
71824
71898
|
// preventDefault, not just tabIndex="-1": a mousedown focuses its target
|
|
71825
71899
|
// before any click happens, and this button should never hold focus at
|
|
71826
71900
|
// all — the field keeps it.
|
|
@@ -71983,21 +72057,16 @@ const PICKER_BUTTON_PSEUDO_CLASSES = [":hover", ":focus", ":focus-visible", ":fo
|
|
|
71983
72057
|
const PickerInputPseudoClasses = [":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":-navi-has-value", ":-navi-expanded"];
|
|
71984
72058
|
|
|
71985
72059
|
// A pair is [the css variable, the style whose values the prop is written in]:
|
|
71986
|
-
//
|
|
71987
|
-
//
|
|
72060
|
+
// slotSpacing takes what margin takes, pressPadding what padding takes — see
|
|
72061
|
+
// box.jsx's readCSSVarEntry.
|
|
72062
|
+
// What the props say about the picker's own box only. The popup props
|
|
72063
|
+
// (popupBackgroundColor, dialogMaxWidth…) are handed to the popup element by
|
|
72064
|
+
// picker_custom.jsx: a variable on the root inherits into everything the popup
|
|
72065
|
+
// holds, and the pickers in there would read it as their own.
|
|
71988
72066
|
const PickerStyleCSSVars = {
|
|
71989
72067
|
"outlineWidth": "--picker-outline-width",
|
|
71990
72068
|
"borderWidth": "--picker-border-width",
|
|
71991
72069
|
"borderRadius": "--picker-border-radius",
|
|
71992
|
-
"popoverMaxHeight": ["--picker-popover-max-height", "maxHeight"],
|
|
71993
|
-
"dialogMinWidth": ["--picker-dialog-min-width", "minWidth"],
|
|
71994
|
-
"dialogMinHeight": ["--picker-dialog-min-height", "minHeight"],
|
|
71995
|
-
"dialogMaxWidth": ["--picker-dialog-max-width", "maxWidth"],
|
|
71996
|
-
"dialogMaxHeight": ["--picker-dialog-max-height", "maxHeight"],
|
|
71997
|
-
"popupBackgroundColor": "--picker-popup-background-color",
|
|
71998
|
-
"popupBorderRadius": ["--picker-popup-border-radius", "borderRadius"],
|
|
71999
|
-
"popupBoxShadow": ["--picker-popup-box-shadow", "boxShadow"],
|
|
72000
|
-
"dialogBorderWidth": ["--picker-dialog-border-width", "borderWidth"],
|
|
72001
72070
|
"slotSpacing": ["--picker-slot-spacing", "margin"],
|
|
72002
72071
|
"pressPadding": ["--picker-press-padding", "padding"],
|
|
72003
72072
|
"pressPaddingX": ["--picker-press-padding-x", "padding"],
|
|
@@ -74562,7 +74631,7 @@ const SplitButton = props => {
|
|
|
74562
74631
|
readOnly,
|
|
74563
74632
|
disabled,
|
|
74564
74633
|
// The outline belongs around the pair, drawn below.
|
|
74565
|
-
loadingOutline:
|
|
74634
|
+
loadingOutline: "custom"
|
|
74566
74635
|
};
|
|
74567
74636
|
return jsxs(Box, {
|
|
74568
74637
|
ref: rootRef,
|