@jsenv/navi 0.29.306 → 0.29.308

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.
@@ -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
 
@@ -8211,6 +8211,75 @@ const languagesSignal = computed(() => {
8211
8211
  // (nor the "languagechange" listener above).
8212
8212
  setRuntimeLangSource(() => languagesSignal.value);
8213
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
+
8214
8283
  /**
8215
8284
  * https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation
8216
8285
  */
@@ -8360,9 +8429,8 @@ const PATTERN_CONSTRAINT = {
8360
8429
  };
8361
8430
  CONSTRAINT_ATTRIBUTE_SET.add("pattern");
8362
8431
 
8363
- // https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/email#validation
8364
- const emailregex =
8365
- /^[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.
8366
8434
  const TYPE_EMAIL_CONSTRAINT = {
8367
8435
  name: "type_email",
8368
8436
  messageAttribute: "data-type-message",
@@ -8376,14 +8444,11 @@ const TYPE_EMAIL_CONSTRAINT = {
8376
8444
  if (!valueAsString) {
8377
8445
  return null;
8378
8446
  }
8379
- if (emailregex.test(valueAsString)) {
8447
+ const result = TYPE_RULE.applyOn("email", valueAsString);
8448
+ if (!result) {
8380
8449
  return null;
8381
8450
  }
8382
-
8383
- if (!valueAsString.includes("@")) {
8384
- return naviI18n("constraint.type.email.at", { value: valueAsString });
8385
- }
8386
- return naviI18n("constraint.type.email.invalid");
8451
+ return naviI18nFromValidityMessage(result);
8387
8452
  },
8388
8453
  };
8389
8454
 
@@ -11542,75 +11607,6 @@ const isFunctionButNotAnActionFunction = (action) => {
11542
11607
  return typeof action === "function" && !action.isAction;
11543
11608
  };
11544
11609
 
11545
- /**
11546
- * Where navi meets @jsenv/validity.
11547
- *
11548
- * validity names a refusal with a key and its parameters rather than a
11549
- * sentence, so that a field and a server can refuse in the same words in the
11550
- * language of the person reading. navi is one of those two callers: it says the
11551
- * sentence, in the browser, through `naviI18n`. The keys line up on purpose —
11552
- * validity's `"single_space.start"` is navi's `"constraint.single_space.start"`
11553
- * — so overriding a message is looking up one key, not going through a
11554
- * translation table.
11555
- *
11556
- * The other direction is `constraintFromValidityRule`: a rule an app wrote for
11557
- * its server, worn by a control as a constraint.
11558
- */
11559
-
11560
-
11561
- /**
11562
- * Turns a @jsenv/validity rule into a constraint a control can wear, so an app
11563
- * rule written once — in the package its server reads too — is checked on both
11564
- * sides instead of being written twice.
11565
- *
11566
- * @param {object} rule
11567
- * `{ name, applyOn(ruleValue, value, ruleConfig) }`, the same object passed
11568
- * to `createValidity({ rules })`.
11569
- * @param {object} [ruleConfig]
11570
- * What parameterizes the rule, under its own name — `{ maxWords: 40 }` for a
11571
- * rule named `maxWords`. Pass `formatMessage` here to say the refusal through
11572
- * the app's own i18n; without it the key is looked up in `naviI18n` under
11573
- * `constraint.<key>`, and a rule answering with a finished sentence is shown
11574
- * as-is.
11575
- *
11576
- * Call it once, at module level: a constraint rebuilt on every render is a new
11577
- * object on every check.
11578
- */
11579
- const constraintFromValidityRule = (rule, ruleConfig = {}) => {
11580
- const { formatMessage, ...ruleParams } = ruleConfig;
11581
- return {
11582
- name: rule.name,
11583
- check: (field) => {
11584
- const result = rule.applyOn(
11585
- ruleParams[rule.name],
11586
- field.uiState,
11587
- ruleParams,
11588
- );
11589
- if (!result) {
11590
- return null;
11591
- }
11592
- if (typeof result === "string") {
11593
- return result;
11594
- }
11595
- if (formatMessage) {
11596
- return formatMessage(result.key, result.params);
11597
- }
11598
- return naviI18nFromValidityMessage(result);
11599
- },
11600
- };
11601
- };
11602
-
11603
- const naviI18nFromValidityMessage = ({ key, params }) => {
11604
- if (params && typeof params.max === "number") {
11605
- // Lets a template pluralize on the bound it names: "[max] retour[s]".
11606
- return naviI18n(`constraint.${key}`, {
11607
- ...params,
11608
- s: params.max > 1 ? "s" : "",
11609
- });
11610
- }
11611
- return naviI18n(`constraint.${key}`, params);
11612
- };
11613
-
11614
11610
  /**
11615
11611
  * `data-displayable` — the value must be something the layout can actually
11616
11612
  * draw. What it refuses and why lives in @jsenv/validity's DISPLAYABLE_RULE:
@@ -40002,7 +39998,15 @@ const LoadingRectangleSvg = ({
40002
39998
  strokeWidth,
40003
39999
  networkSpeed: networkSpeedForced
40004
40000
  }) => {
40005
- const margin = Math.max(2, Math.min(width, height) * 0.03);
40001
+ // The stroke is centered on the path, so half of it falls outside: drawing
40002
+ // at half the stroke width puts its outer edge exactly on the box, whatever
40003
+ // the box. It used to be a share of the smaller dimension (3%, floored at
40004
+ // 2px), which held the line further and further inside as the box grew — a
40005
+ // caller placing the outline with `inset` got the distance it asked for on a
40006
+ // field and something several pixels inward on a card. Nothing needs the
40007
+ // extra room: the svg is `overflow: visible` and whoever positions the
40008
+ // wrapper already reserves the half stroke.
40009
+ const margin = strokeWidth / 2;
40006
40010
 
40007
40011
  // Calculate the drawable area
40008
40012
  const drawableWidth = width - margin * 2;
@@ -48125,10 +48129,11 @@ const ButtonUI = props => {
48125
48129
  cta,
48126
48130
  spacing,
48127
48131
  contentDisplay,
48128
- // Whether the button draws the loading outline itself. A button that is
48129
- // one half of a bigger control says no: what is busy is the control, and
48130
- // the outline belongs around the whole of it (see split_button.jsx).
48131
- loadingOutline = true
48132
+ // Who draws the loading outline. Left unsaid, the button does; "custom"
48133
+ // says someone else does — a button that is one half of a bigger control:
48134
+ // what is busy is the control, and the outline belongs around the whole
48135
+ // of it (see split_button.jsx).
48136
+ loadingOutline
48132
48137
  } = props;
48133
48138
  const [buttonControlRootProps, buttonControlHostProps, controlChildrenWrapperProps] = useControlProps(props, {
48134
48139
  controlType: "button"
@@ -48254,7 +48259,7 @@ const ButtonUI = props => {
48254
48259
  visualSelector: visualSelector,
48255
48260
  hasChildUsingForwardedProps: true,
48256
48261
  children: [jsx(LoadingOutline, {
48257
- loading: loadingOutline && loading,
48262
+ loading: loadingOutline === "custom" ? false : loading,
48258
48263
  inset: -1,
48259
48264
  color: "var(--button-loader-color)"
48260
48265
  }), jsx(ControlChildrenWrapper, {
@@ -60436,6 +60441,9 @@ const DIALOG_STYLE_CSS_VARS = {
60436
60441
  animationDuration: "--popup-animation-duration",
60437
60442
  backdropColor: "--backdrop-background",
60438
60443
  backdropFilter: "--backdrop-filter",
60444
+ backgroundColor: "--dialog-background-color",
60445
+ boxShadow: "--dialog-box-shadow",
60446
+ borderWidth: "--dialog-border-width",
60439
60447
  borderRadius: "--dialog-border-radius",
60440
60448
  minWidth: "--dialog-min-width",
60441
60449
  maxWidth: "--dialog-max-width",
@@ -62023,10 +62031,17 @@ const POPOVER_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible",
62023
62031
  // Lets consumers pass animationDuration="0.5s"/borderRadius="8px" as regular
62024
62032
  // props; Box maps them to the CSS vars for us (see box.jsx's styleCSSVars
62025
62033
  // handling).
62034
+ // The paint props go through the popover's own vars rather than the properties
62035
+ // themselves, so the stylesheet keeps the last word on them (the same reason
62036
+ // dialog.jsx gives for borderRadius).
62026
62037
  const POPUP_STYLE_CSS_VARS = {
62027
62038
  animationDuration: "--popup-animation-duration",
62028
62039
  backdropColor: "--backdrop-background",
62029
62040
  backdropFilter: "--backdrop-filter",
62041
+ backgroundColor: "--popover-background-color",
62042
+ boxShadow: "--popover-box-shadow",
62043
+ borderWidth: "--popover-border-width",
62044
+ borderRadius: "--popover-border-radius",
62030
62045
  minWidth: "--popover-min-width",
62031
62046
  maxWidth: "--popover-max-width",
62032
62047
  minHeight: "--popover-min-height",
@@ -62289,83 +62304,56 @@ const Popup = props => {
62289
62304
  Popup.Close = PopupClose;
62290
62305
 
62291
62306
  installImportMetaCssBuild(import.meta);
62292
- const css$B = /* css */`.navi_picker {
62293
- &[aria-haspopup="listbox"] {
62294
- & .navi_popover {
62295
- --popover-border-radius: var(--picker-popup-border-radius, var(--picker-border-radius));
62296
- --popover-border-width: var(--picker-border-width);
62297
- --popover-border-color: var(--x-picker-border-color);
62298
- --popover-background-color: var(--picker-popup-background-color, var(--navi-popup-background-color));
62299
- --popover-outline-width: var(--picker-outline-width);
62300
- --popover-outline-color: var(--picker-outline-color);
62301
- --popover-box-shadow: var(--picker-popup-box-shadow, var(--navi-popup-box-shadow));
62302
- --popover-max-height: var(--picker-popover-max-height);
62303
- min-width: var(--picker-popover-min-width, var(--anchor-width, 0px));
62304
- cursor: default;
62305
-
62306
- & .navi_list_container {
62307
- --list-border-radius: max(0px,
62308
- var(--popover-border-radius) - var(--popover-border-width));
62309
- overscroll-behavior: none;
62310
- width: 100%;
62307
+ const css$B = /* css */`.navi_popover[data-picker-popup] {
62308
+ --popover-border-radius: var(--picker-border-radius);
62309
+ --popover-border-width: var(--picker-border-width);
62310
+ --popover-border-color: var(--x-picker-border-color);
62311
+ --popover-outline-width: var(--picker-outline-width);
62312
+ --popover-outline-color: var(--picker-outline-color);
62313
+ cursor: default;
62311
62314
 
62312
- &:not([data-overflow-visible]) {
62313
- overflow: auto;
62314
- }
62315
- }
62316
- }
62315
+ &:not([navi-hidden]) {
62316
+ flex-direction: column;
62317
+ display: flex;
62318
+ }
62317
62319
 
62318
- &[aria-expanded="true"] {
62319
- &[navi-popover-mode="overlay"] {
62320
- border-color: #0000;
62321
- }
62320
+ & .navi_list_container {
62321
+ --list-border-radius: max(0px,
62322
+ var(--popover-border-radius) - var(--popover-border-width));
62323
+ overscroll-behavior: none;
62324
+ width: 100%;
62322
62325
 
62323
- & .navi_popover {
62324
- flex-direction: column;
62325
- display: flex;
62326
- }
62326
+ &:not([data-overflow-visible]) {
62327
+ overflow: auto;
62327
62328
  }
62328
62329
  }
62330
+ }
62329
62331
 
62330
- &[aria-haspopup="dialog"] {
62331
- & .navi_dialog {
62332
- --dialog-border-radius: var(--picker-popup-border-radius, var(--picker-border-radius));
62333
- --dialog-border-width: var(--picker-dialog-border-width, 0px);
62334
- --dialog-border-color: var(--x-picker-border-color);
62335
- --dialog-background-color: var(--picker-popup-background-color, var(--navi-popup-background-color));
62336
- --dialog-outline-width: var(--picker-outline-width);
62337
- --dialog-outline-color: var(--picker-outline-color);
62338
- --dialog-box-shadow: var(--picker-popup-box-shadow, var(--navi-popup-box-shadow));
62339
- --dialog-min-width: var(--picker-dialog-min-width);
62340
- --dialog-min-height: var(--picker-dialog-min-height);
62341
- --dialog-max-width: var(--picker-dialog-max-width);
62342
- --dialog-max-height: var(--picker-dialog-max-height);
62343
- cursor: default;
62332
+ .navi_picker[aria-haspopup="listbox"][aria-expanded="true"][navi-popover-mode="overlay"] {
62333
+ border-color: #0000;
62334
+ }
62344
62335
 
62345
- &[open] {
62346
- flex-direction: column;
62347
- }
62336
+ .navi_dialog[data-picker-popup] {
62337
+ --dialog-border-radius: var(--picker-border-radius);
62338
+ --dialog-border-color: var(--x-picker-border-color);
62339
+ --dialog-outline-width: var(--picker-outline-width);
62340
+ --dialog-outline-color: var(--picker-outline-color);
62341
+ cursor: default;
62348
62342
 
62349
- & .navi_list_container {
62350
- --list-border-radius: max(0px,
62351
- var(--dialog-border-radius) - var(--dialog-border-width));
62352
- overscroll-behavior: none;
62353
- width: 100%;
62343
+ &[open] {
62344
+ flex-direction: column;
62345
+ }
62354
62346
 
62355
- &:not([data-overflow-visible]) {
62356
- overflow: auto;
62357
- }
62358
- }
62359
- }
62347
+ & .navi_list_container {
62348
+ --list-border-radius: max(0px,
62349
+ var(--dialog-border-radius) - var(--dialog-border-width));
62350
+ overscroll-behavior: none;
62351
+ width: 100%;
62360
62352
 
62361
- &[data-dialog-size-from-anchor] .navi_dialog {
62362
- --dialog-max-width: var(--picker-dialog-max-width, var(--anchor-width));
62353
+ &:not([data-overflow-visible]) {
62354
+ overflow: auto;
62363
62355
  }
62364
62356
  }
62365
-
62366
- &[data-popup-width-fit-content] {
62367
- --picker-popover-min-width: 0px;
62368
- }
62369
62357
  }
62370
62358
  `;
62371
62359
  const PickerCustomResolver = props => {
@@ -63129,6 +63117,22 @@ const PickerContentInsidePopup = props => {
63129
63117
  // dialogMaxWidth="var(--anchor-width)", as a ceiling) — what keeps a card
63130
63118
  // its own width once lifted. Dialog's own `sizeFromAnchor`.
63131
63119
  dialogSizeFromAnchor,
63120
+ // The caller's word on the popup's own box. Written on the popup element,
63121
+ // where Popover/Dialog map them to their own vars, rather than as vars on
63122
+ // the picker: a var on the picker inherits into everything the popup
63123
+ // holds, and the pickers in there would read it as their own.
63124
+ popupBackgroundColor,
63125
+ popupBorderRadius,
63126
+ popupBoxShadow,
63127
+ popoverMaxHeight,
63128
+ dialogBorderWidth,
63129
+ dialogMinWidth,
63130
+ dialogMinHeight,
63131
+ dialogMaxWidth,
63132
+ dialogMaxHeight,
63133
+ // The popover is at least as wide as the trigger. True when the CONTENT
63134
+ // should size it (a Wheel) instead of being stretched to the trigger.
63135
+ popupWidthFitContent,
63132
63136
  animation,
63133
63137
  grow,
63134
63138
  animationDuration,
@@ -63147,7 +63151,6 @@ const PickerContentInsidePopup = props => {
63147
63151
  return jsx(Next, {
63148
63152
  "aria-haspopup": isPopover ? "listbox" : "dialog",
63149
63153
  "navi-popover-mode": isPopover ? popoverMode : undefined,
63150
- "data-dialog-size-from-anchor": !isPopover && dialogSizeFromAnchor ? "" : undefined,
63151
63154
  ...rest,
63152
63155
  // On popupProps already (see the picker's popup assembly); they mean
63153
63156
  // nothing to the picker element.
@@ -63189,7 +63192,23 @@ const PickerContentInsidePopup = props => {
63189
63192
  })
63190
63193
  }) : jsx(Popup, {
63191
63194
  ...popupProps,
63192
- "data-testid": popupTestId,
63195
+ "data-testid": popupTestId
63196
+ // What the CSS above keys on: this picker's own popup, and no other
63197
+ // popup below the picker.
63198
+ ,
63199
+ "data-picker-popup": "",
63200
+ backgroundColor: popupBackgroundColor,
63201
+ borderRadius: popupBorderRadius,
63202
+ boxShadow: popupBoxShadow,
63203
+ borderWidth: isPopover ? undefined : dialogBorderWidth,
63204
+ minWidth: isPopover ? popupWidthFitContent ? undefined : "var(--anchor-width, 0px)" : dialogMinWidth,
63205
+ minHeight: isPopover ? undefined : dialogMinHeight
63206
+ // As wide as the trigger under dialogSizeFromAnchor: Dialog's own
63207
+ // sizeFromAnchor is a floor, this is the ceiling. A dialogMaxWidth of
63208
+ // the caller's still wins.
63209
+ ,
63210
+ maxWidth: isPopover ? undefined : dialogMaxWidth === undefined && dialogSizeFromAnchor ? "var(--anchor-width)" : dialogMaxWidth,
63211
+ maxHeight: isPopover ? popoverMaxHeight : dialogMaxHeight,
63193
63212
  mode: mode,
63194
63213
  layer: popupLayer,
63195
63214
  animation: animation,
@@ -71229,6 +71248,26 @@ const css$t = /* css */`@layer navi {
71229
71248
  }
71230
71249
  }
71231
71250
 
71251
+ & [data-picker-content] {
71252
+ --picker-padding: initial;
71253
+ --picker-padding-x: initial;
71254
+ --picker-padding-y: initial;
71255
+ --picker-padding-top: initial;
71256
+ --picker-padding-right: initial;
71257
+ --picker-padding-bottom: initial;
71258
+ --picker-padding-left: initial;
71259
+ --picker-press-padding: initial;
71260
+ --picker-press-padding-x: initial;
71261
+ --picker-press-padding-y: initial;
71262
+ --picker-press-padding-top: initial;
71263
+ --picker-press-padding-right: initial;
71264
+ --picker-press-padding-bottom: initial;
71265
+ --picker-press-padding-left: initial;
71266
+ --picker-slot-spacing: initial;
71267
+ --picker-align-x: initial;
71268
+ --picker-align-y: initial;
71269
+ }
71270
+
71232
71271
  & .navi_picker_content {
71233
71272
  --x-corner-top-left-radius: initial;
71234
71273
  --x-corner-top-right-radius: initial;
@@ -71430,11 +71469,6 @@ const PickerButton = props => {
71430
71469
  placeholder,
71431
71470
  ui,
71432
71471
  maxLines: maxLinesProp = 1,
71433
- // By default the popover is at least as wide as the trigger (min-width:
71434
- // --anchor-width). Set true when the CONTENT should dictate the popover width
71435
- // (e.g. a Wheel) instead of being stretched to the trigger — see
71436
- // picker_custom.jsx.
71437
- popupWidthFitContent,
71438
71472
  // Adds a clear button to the right slot, the same one type="search" puts at
71439
71473
  // the end of an input: a picker holds a value the user chose, and unsetting
71440
71474
  // it should not require reopening the popup to hunt for a "none" entry.
@@ -71459,7 +71493,13 @@ const PickerButton = props => {
71459
71493
  // (see PickerInput below).
71460
71494
  role,
71461
71495
  readOnly,
71462
- error
71496
+ error,
71497
+ // Who draws the loading outline. Left unsaid, the picker does, around its
71498
+ // own box; "custom" says someone else does — a bigger surface this box is
71499
+ // a piece of, waiting as a whole (see the `loadingOutline` doc).
71500
+ loadingOutline,
71501
+ // Where the outline runs, in px from the picker's box: negative is outside.
71502
+ loadingOutlineInset = -2
71463
71503
  } = props;
71464
71504
  // A word in a sentence is never truncated — and the clamp's overflow: hidden
71465
71505
  // would cut its dotted underline, which sits on the edge of the line box
@@ -71578,7 +71618,6 @@ const PickerButton = props => {
71578
71618
  "navi-single-line": isSingleLine ? "" : undefined,
71579
71619
  "navi-ui-custom": ui === "default" ? undefined : "",
71580
71620
  "data-readonly-opens": readOnlyOpens ? "" : undefined,
71581
- "data-popup-width-fit-content": popupWidthFitContent ? "" : undefined,
71582
71621
  ...pickerRemainingProps,
71583
71622
  basePseudoState: basePseudoState,
71584
71623
  styleCSSVars: PickerStyleCSSVars,
@@ -71593,9 +71632,10 @@ const PickerButton = props => {
71593
71632
  openWhileReadOnly: undefined,
71594
71633
  ui: undefined,
71595
71634
  maxLines: undefined,
71596
- popupWidthFitContent: undefined,
71597
71635
  error: undefined,
71598
- dayLabel: undefined
71636
+ dayLabel: undefined,
71637
+ loadingOutline: undefined,
71638
+ loadingOutlineInset: undefined
71599
71639
  // This wrapper will receive keyboard event bubbling from the picker popup content
71600
71640
  // we re-dispatch on the input (to get escape to close for instance)
71601
71641
  ,
@@ -71626,9 +71666,9 @@ const PickerButton = props => {
71626
71666
  children: jsxs(PickerContext.Provider, {
71627
71667
  value: pickerContext,
71628
71668
  children: [variant === "headless" ? null : jsx(LoadingOutline, {
71629
- loading: loading,
71669
+ loading: loadingOutline === "custom" ? false : loading,
71630
71670
  color: "var(--picker-loader-color)",
71631
- inset: -2
71671
+ inset: loadingOutlineInset
71632
71672
  }), jsx(PickerInput, {
71633
71673
  role: hostRole,
71634
71674
  "aria-labelledby": hostLabelId,
@@ -71862,7 +71902,7 @@ const PickerClear = ({
71862
71902
  // removed is the whole field's, and the picker already draws the wait
71863
71903
  // around all of it. Two outlines for one wait is one too many.
71864
71904
  ,
71865
- loadingOutline: false
71905
+ loadingOutline: "custom"
71866
71906
  // preventDefault, not just tabIndex="-1": a mousedown focuses its target
71867
71907
  // before any click happens, and this button should never hold focus at
71868
71908
  // all — the field keeps it.
@@ -72025,21 +72065,16 @@ const PICKER_BUTTON_PSEUDO_CLASSES = [":hover", ":focus", ":focus-visible", ":fo
72025
72065
  const PickerInputPseudoClasses = [":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":-navi-has-value", ":-navi-expanded"];
72026
72066
 
72027
72067
  // A pair is [the css variable, the style whose values the prop is written in]:
72028
- // popupBorderRadius takes what borderRadius takes, dialogMinWidth what minWidth
72029
- // takes — see box.jsx's readCSSVarEntry.
72068
+ // slotSpacing takes what margin takes, pressPadding what padding takes — see
72069
+ // box.jsx's readCSSVarEntry.
72070
+ // What the props say about the picker's own box only. The popup props
72071
+ // (popupBackgroundColor, dialogMaxWidth…) are handed to the popup element by
72072
+ // picker_custom.jsx: a variable on the root inherits into everything the popup
72073
+ // holds, and the pickers in there would read it as their own.
72030
72074
  const PickerStyleCSSVars = {
72031
72075
  "outlineWidth": "--picker-outline-width",
72032
72076
  "borderWidth": "--picker-border-width",
72033
72077
  "borderRadius": "--picker-border-radius",
72034
- "popoverMaxHeight": ["--picker-popover-max-height", "maxHeight"],
72035
- "dialogMinWidth": ["--picker-dialog-min-width", "minWidth"],
72036
- "dialogMinHeight": ["--picker-dialog-min-height", "minHeight"],
72037
- "dialogMaxWidth": ["--picker-dialog-max-width", "maxWidth"],
72038
- "dialogMaxHeight": ["--picker-dialog-max-height", "maxHeight"],
72039
- "popupBackgroundColor": "--picker-popup-background-color",
72040
- "popupBorderRadius": ["--picker-popup-border-radius", "borderRadius"],
72041
- "popupBoxShadow": ["--picker-popup-box-shadow", "boxShadow"],
72042
- "dialogBorderWidth": ["--picker-dialog-border-width", "borderWidth"],
72043
72078
  "slotSpacing": ["--picker-slot-spacing", "margin"],
72044
72079
  "pressPadding": ["--picker-press-padding", "padding"],
72045
72080
  "pressPaddingX": ["--picker-press-padding-x", "padding"],
@@ -74604,7 +74639,7 @@ const SplitButton = props => {
74604
74639
  readOnly,
74605
74640
  disabled,
74606
74641
  // The outline belongs around the pair, drawn below.
74607
- loadingOutline: false
74642
+ loadingOutline: "custom"
74608
74643
  };
74609
74644
  return jsxs(Box, {
74610
74645
  ref: rootRef,