@jsenv/navi 0.29.470 → 0.29.472

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.
@@ -39055,8 +39055,29 @@ const resolveFirstChildControl = (el) => {
39055
39055
  }
39056
39056
  return startEl.querySelector("[navi-control-host]");
39057
39057
  };
39058
+ // The element a popup command is about: the closest one keeping an open state.
39058
39059
  const resolveClosestExpandable = (el) => {
39059
- return el.closest("[aria-expanded]");
39060
+ const expandable = el.closest("[aria-expanded]");
39061
+ if (!expandable) {
39062
+ return null;
39063
+ }
39064
+ return expandableRepeatedBy(expandable) || expandable;
39065
+ };
39066
+ // A control's host repeats the aria-expanded of its root, beside its role, for
39067
+ // assistive tech to read (a picker's <input>, a combobox or a button). The root
39068
+ // is the expandable: the host is a leaf the walk lands on only when it is the
39069
+ // source itself, and it carries the control's own `command`/`commandfor`, which
39070
+ // a command resolved again from it (a close after a clear) would follow.
39071
+ const expandableRepeatedBy = (el) => {
39072
+ const controlRoot = findControlRoot(el);
39073
+ if (
39074
+ controlRoot &&
39075
+ controlRoot !== el &&
39076
+ controlRoot.hasAttribute("aria-expanded")
39077
+ ) {
39078
+ return controlRoot;
39079
+ }
39080
+ return null;
39060
39081
  };
39061
39082
  // The surface a send is decided on: the expandable holding the CONTROL that
39062
39083
  // sends, never one that control is itself. A picker's own root carries
@@ -39067,7 +39088,7 @@ const resolveClosestExpandable = (el) => {
39067
39088
  const resolveExpandableAround = (source) => {
39068
39089
  const sender = findControlRoot(source) || source;
39069
39090
  const parent = sender.parentElement;
39070
- return parent ? parent.closest("[aria-expanded]") : null;
39091
+ return parent ? resolveClosestExpandable(parent) : null;
39071
39092
  };
39072
39093
  // An element carrying navi-command-proxy-for="anchorId" stands in for that
39073
39094
  // other element as the command's source when the source is forwarded to
@@ -39368,8 +39389,12 @@ const sendCommand = (source, event, { requester }) => {
39368
39389
  // looking at, and a decision just taken there is a reason to leave it. The
39369
39390
  // NEAREST surface answers, and only that one — a form inside a slide inside a
39370
39391
  // dialog goes back a slide, it does not also close the dialog.
39371
- // send inside expandable
39372
- if (target.getAttribute("aria-expanded") === "true") {
39392
+ // send inside expandable — the control itself when it sends from its host,
39393
+ // whose aria-expanded is its root's (see expandableRepeatedBy)
39394
+ if (
39395
+ target.getAttribute("aria-expanded") === "true" &&
39396
+ !expandableRepeatedBy(target)
39397
+ ) {
39373
39398
  return {
39374
39399
  target,
39375
39400
  implementation: () => {
@@ -39804,7 +39829,7 @@ const requestCloseUpward = (target, detail) => {
39804
39829
  return false;
39805
39830
  }
39806
39831
  const parent = expandable.parentElement;
39807
- expandable = parent ? parent.closest("[aria-expanded]") : null;
39832
+ expandable = parent ? resolveClosestExpandable(parent) : null;
39808
39833
  }
39809
39834
  return true;
39810
39835
  };
@@ -51639,6 +51664,9 @@ const LinkWithRoute = ({
51639
51664
  currentExcept,
51640
51665
  currentAlso,
51641
51666
  children,
51667
+ // Kept out of `rest`: spread after the route's url, an `href={undefined}`
51668
+ // riding along would erase it.
51669
+ href,
51642
51670
  ...rest
51643
51671
  }) => {
51644
51672
  const url = route.buildUrl(routeParams);
@@ -56811,11 +56839,14 @@ const ButtonRouteResolver = props => {
56811
56839
  };
56812
56840
  const ButtonWithRoute = props => {
56813
56841
  const Next = useNextResolver();
56842
+ // `href` is kept out of `rest`: spread after the route's url, an
56843
+ // `href={undefined}` riding along would erase it.
56814
56844
  const {
56815
56845
  route,
56816
56846
  routeParams,
56817
56847
  children,
56818
56848
  pseudoState,
56849
+ href,
56819
56850
  ...rest
56820
56851
  } = props;
56821
56852
  const url = route.buildUrl(routeParams);
@@ -58184,6 +58215,10 @@ const Label = props => {
58184
58215
  requiredIndicator
58185
58216
  } = props;
58186
58217
  const controlId = useContext(ControlIdContext);
58218
+ // What a popup opened from the control is named by (see getTriggerLabelIds
58219
+ // in picker_custom.jsx): aria-labelledby can only point at an id.
58220
+ const idDefault = useId();
58221
+ props.id = props.id || `label_${idDefault}`;
58187
58222
  const [disabled, setDisabled] = useState(false);
58188
58223
  const [readOnly, setReadOnly] = useState(false);
58189
58224
  const [required, setRequired] = useState(false);
@@ -67877,9 +67912,297 @@ const PopupClose = ({
67877
67912
  });
67878
67913
  };
67879
67914
 
67915
+ const SuccessSvg = () => {
67916
+ return jsx("svg", {
67917
+ viewBox: "0 0 16 16",
67918
+ fill: "currentColor",
67919
+ children: jsx("path", {
67920
+ d: "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
67921
+ })
67922
+ });
67923
+ };
67924
+ const ErrorSvg = () => {
67925
+ return jsx("svg", {
67926
+ viewBox: "0 0 16 16",
67927
+ fill: "currentColor",
67928
+ children: jsx("path", {
67929
+ d: "M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"
67930
+ })
67931
+ });
67932
+ };
67933
+ const InfoSvg$1 = () => {
67934
+ return jsx("svg", {
67935
+ viewBox: "0 0 16 16",
67936
+ fill: "currentColor",
67937
+ children: jsx("path", {
67938
+ d: "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"
67939
+ })
67940
+ });
67941
+ };
67942
+ const WarningSvg = () => {
67943
+ return jsx("svg", {
67944
+ viewBox: "0 0 16 16",
67945
+ fill: "currentColor",
67946
+ children: jsx("path", {
67947
+ d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
67948
+ })
67949
+ });
67950
+ };
67951
+
67952
+ installImportMetaCssBuild(import.meta);
67953
+ const css$E = /* css */`@layer navi {
67954
+ .navi_message_box {
67955
+ --background-color-info: var(--navi-info-color-light);
67956
+ --color-info: var(--navi-info-color);
67957
+ --background-color-success: var(--navi-success-color-light);
67958
+ --color-success: var(--navi-success-color);
67959
+ --background-color-warning: var(--navi-warning-color-light);
67960
+ --color-warning: var(--navi-warning-color);
67961
+ --background-color-error: var(--navi-error-color-light);
67962
+ --color-error: var(--navi-error-color);
67963
+ }
67964
+ }
67965
+
67966
+ .navi_message_box {
67967
+ --x-message-background-color: var(--background-color-info);
67968
+ --x-message-color: var(--color-info);
67969
+ background-color: var(--x-message-background-color);
67970
+
67971
+ & > .navi_icon {
67972
+ flex-shrink: 0;
67973
+ }
67974
+ }
67975
+
67976
+ .navi_message_box[data-status-info] {
67977
+ --x-message-background-color: var(--background-color-info);
67978
+ --x-message-color: var(--color-info);
67979
+ }
67980
+
67981
+ .navi_message_box[data-status-success] {
67982
+ --x-message-background-color: var(--background-color-success);
67983
+ --x-message-color: var(--color-success);
67984
+ }
67985
+
67986
+ .navi_message_box[data-status-warning] {
67987
+ --x-message-background-color: var(--background-color-warning);
67988
+ --x-message-color: var(--color-warning);
67989
+ }
67990
+
67991
+ .navi_message_box[data-status-error] {
67992
+ --x-message-background-color: var(--background-color-error);
67993
+ --x-message-color: var(--color-error);
67994
+ }
67995
+
67996
+ .navi_message_box[data-left-stripe] {
67997
+ border-left: 6px solid var(--x-message-color);
67998
+ border-top-left-radius: 6px;
67999
+ border-bottom-left-radius: 6px;
68000
+ }
68001
+ `;
68002
+ const MessageBox = ({
68003
+ status = "info",
68004
+ padding = "sm",
68005
+ icon,
68006
+ leftStripe,
68007
+ children,
68008
+ onClose,
68009
+ ...rest
68010
+ }) => {
68011
+ import.meta.css = [css$E, "@jsenv/navi/src/text/message_box.jsx"];
68012
+ const [hasTitleChild, setHasTitleChild] = useState(false);
68013
+ const innerLeftStripe = leftStripe === undefined ? hasTitleChild : leftStripe;
68014
+ if (icon === true) {
68015
+ icon = status === "info" ? jsx(InfoSvg$1, {}) : status === "success" ? jsx(SuccessSvg, {}) : status === "warning" ? jsx(WarningSvg, {}) : status === "error" ? jsx(ErrorSvg, {}) : null;
68016
+ } else if (typeof icon === "function") {
68017
+ const Comp = icon;
68018
+ icon = jsx(Comp, {});
68019
+ }
68020
+ return jsx(Box, {
68021
+ as: "div",
68022
+ role: status === "info" ? "status" : "alert",
68023
+ "data-left-stripe": innerLeftStripe ? "" : undefined,
68024
+ inline: true,
68025
+ column: true,
68026
+ alignY: "center",
68027
+ spacing: "s",
68028
+ ...rest,
68029
+ className: withPropsClassName("navi_message_box", rest.className),
68030
+ padding: padding,
68031
+ pseudoClasses: MessageBoxPseudoClasses,
68032
+ basePseudoState: {
68033
+ ":-navi-status-info": status === "info",
68034
+ ":-navi-status-success": status === "success",
68035
+ ":-navi-status-warning": status === "warning",
68036
+ ":-navi-status-error": status === "error"
68037
+ },
68038
+ children: jsx(MessageBoxStatusContext.Provider, {
68039
+ value: status,
68040
+ children: jsxs(MessageBoxReportTitleChildContext.Provider, {
68041
+ value: setHasTitleChild,
68042
+ children: [icon && jsx(Icon, {
68043
+ color: "var(--x-message-color)",
68044
+ height: "1lh",
68045
+ maxHeight: "auto",
68046
+ selfAlignY: "start",
68047
+ aspectRatio: "auto",
68048
+ children: icon
68049
+ }), jsx(Text, {
68050
+ children: children
68051
+ }), onClose &&
68052
+ // A column as tall as the first line of the message, pinned to the
68053
+ // top, the button centered in it: the close button stays level
68054
+ // with the first line however many lines the message takes (same
68055
+ // layout as Callout).
68056
+ jsx(Box, {
68057
+ flex: true,
68058
+ alignY: "center",
68059
+ height: "1lh",
68060
+ selfAlignY: "start",
68061
+ shrink: false,
68062
+ children: jsx(Button, {
68063
+ action: onClose,
68064
+ icon: true,
68065
+ border: "none",
68066
+ style: {
68067
+ ":hover": {
68068
+ backgroundColor: "rgba(0, 0, 0, 0.1)"
68069
+ }
68070
+ },
68071
+ children: jsx(Icon, {
68072
+ children: jsx(CloseSvg, {})
68073
+ })
68074
+ })
68075
+ })]
68076
+ })
68077
+ })
68078
+ });
68079
+ };
68080
+ const MessageBoxPseudoClasses = [":-navi-status-info", ":-navi-status-success", ":-navi-status-warning", ":-navi-status-error"];
68081
+ const MessageBoxStatusContext = createContext();
68082
+ const MessageBoxReportTitleChildContext = createContext();
68083
+
68084
+ installImportMetaCssBuild(import.meta);
68085
+ const css$D = /* css */`.navi_message_box {
68086
+ & .navi_title {
68087
+ margin-top: 0;
68088
+ margin-bottom: var(--navi-s);
68089
+ color: var(--x-message-color);
68090
+ }
68091
+ }
68092
+ `;
68093
+ const Title = props => {
68094
+ import.meta.css = [css$D, "@jsenv/navi/src/text/title.jsx"];
68095
+ const messageBoxStatus = useContext(MessageBoxStatusContext);
68096
+ const innerAs = props.as || (messageBoxStatus ? "h4" : "h1");
68097
+ const titleLevel = parseInt(innerAs.slice(1));
68098
+ const reportTitleToMessageBox = useContext(MessageBoxReportTitleChildContext);
68099
+ reportTitleToMessageBox?.(true);
68100
+ return jsx(TitleLevelContext.Provider, {
68101
+ value: titleLevel,
68102
+ children: jsx(Text, {
68103
+ bold: true,
68104
+ className: withPropsClassName("navi_title"),
68105
+ as: messageBoxStatus ? "h4" : "h1",
68106
+ ...props,
68107
+ pseudoClasses: TitlePseudoClasses,
68108
+ children: props.children
68109
+ })
68110
+ });
68111
+ };
68112
+ const TitleLevelContext = createContext();
68113
+ const useTitleLevel = () => {
68114
+ return useContext(TitleLevelContext);
68115
+ };
68116
+ const TitlePseudoClasses = [":hover"];
68117
+
68118
+ /**
68119
+ * What a popup is called, decided by Dialog and Popover alike: the name the
68120
+ * caller writes on it (aria-label/aria-labelledby), else the title it holds
68121
+ * (`<Popup.Title>`), else the name of what opened it (`openerAriaLabelledBy` /
68122
+ * `openerAriaLabel`, which a Picker fills with its trigger's). A popup no one
68123
+ * names is announced as "dialog" and nothing else.
68124
+ *
68125
+ * The title tells the popup it exists rather than the popup looking for it:
68126
+ * the content is built after the popup and thrown away before it (see
68127
+ * popup_content_mount.js), and a title may sit anywhere in it — a header, a
68128
+ * step of a form — so only the title knows when it is there.
68129
+ */
68130
+
68131
+ const PopupTitleContext = createContext(null);
68132
+
68133
+ /**
68134
+ * The title of the popup it sits in — reached as `<Popup.Title>`,
68135
+ * `<Dialog.Title>` or `<Popover.Title>`. A heading (`h2` unless `as` says
68136
+ * otherwise) that is also the popup's accessible name: a screen reader
68137
+ * announces the popup by it, and `getByRole("dialog", { name })` finds it.
68138
+ * An `aria-label`/`aria-labelledby` written on the popup itself wins over it.
68139
+ *
68140
+ * @type {import("ignore:preact").FunctionComponent<{
68141
+ * as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6",
68142
+ * id?: string,
68143
+ * children?: import("ignore:preact").ComponentChildren,
68144
+ * } & Record<string, any>>}
68145
+ */
68146
+ const PopupTitle = props => {
68147
+ const registerTitle = useContext(PopupTitleContext);
68148
+ const idDefault = useId();
68149
+ const id = props.id || `popup_title_${idDefault}`;
68150
+ useLayoutEffect(() => {
68151
+ if (!registerTitle) {
68152
+ return undefined;
68153
+ }
68154
+ return registerTitle(id);
68155
+ }, [registerTitle, id]);
68156
+ return jsx(Title, {
68157
+ as: "h2",
68158
+ ...props,
68159
+ id: id
68160
+ });
68161
+ };
68162
+
68163
+ /**
68164
+ * The name props of a popup element, and what its titles register with —
68165
+ * handed to its children through PopupTitleContext.
68166
+ */
68167
+ const usePopupName = ({
68168
+ ariaLabel,
68169
+ ariaLabelledBy,
68170
+ openerAriaLabel,
68171
+ openerAriaLabelledBy
68172
+ }) => {
68173
+ // Several titles is a content of the caller's own making (two steps both
68174
+ // mounted): the first one registered names the popup.
68175
+ const [titleIds, setTitleIds] = useState(NO_TITLE);
68176
+ const registerTitle = useMemo(() => {
68177
+ return id => {
68178
+ setTitleIds(ids => [...ids, id]);
68179
+ return () => {
68180
+ setTitleIds(ids => ids.filter(each => each !== id));
68181
+ };
68182
+ };
68183
+ }, []);
68184
+ let labelledBy;
68185
+ let label;
68186
+ if (ariaLabelledBy !== undefined || ariaLabel !== undefined) {
68187
+ labelledBy = ariaLabelledBy;
68188
+ label = ariaLabel;
68189
+ } else if (titleIds.length > 0) {
68190
+ labelledBy = titleIds[0];
68191
+ } else if (openerAriaLabelledBy !== undefined) {
68192
+ labelledBy = openerAriaLabelledBy;
68193
+ } else {
68194
+ label = openerAriaLabel;
68195
+ }
68196
+ return [{
68197
+ "aria-labelledby": labelledBy,
68198
+ "aria-label": label
68199
+ }, registerTitle];
68200
+ };
68201
+ const NO_TITLE = [];
68202
+
67880
68203
  installImportMetaCssBuild(import.meta);
67881
68204
  let openLocalDialogCount = 0;
67882
- const css$E = /* css */`
68205
+ const css$C = /* css */`
67883
68206
  /* jsenv-css-opaque: popupCss overlaps these rules, own sheet = cascade
67884
68207
  order depending on which popup renders first. */
67885
68208
 
@@ -68948,10 +69271,14 @@ const css$E = /* css */`
68948
69271
  * under `"while-opened"`, only a press on what opens the popup does (a
68949
69272
  * `--navi-open` button, a picker's trigger), never with an `onOpen`, and a
68950
69273
  * press that does not open the popup throws it away.
69274
+ * @param {string} [props.openerAriaLabelledBy] - The id(s) naming what opened
69275
+ * the dialog: its accessible name when neither `aria-label`/`aria-labelledby`
69276
+ * nor a `<Dialog.Title>` names it. A Picker passes its trigger's.
69277
+ * @param {string} [props.openerAriaLabel] - The same, as a string.
68951
69278
  * @param {import("ignore:preact").ComponentChildren} props.children
68952
69279
  */
68953
69280
  const Dialog = props => {
68954
- import.meta.css = [css$E, "@jsenv/navi/src/layout/dialog.jsx"];
69281
+ import.meta.css = [css$C, "@jsenv/navi/src/layout/dialog.jsx"];
68955
69282
  if (props.openController) {
68956
69283
  return jsx(ControlledDialog, {
68957
69284
  ...props
@@ -69325,6 +69652,9 @@ const useDialogProps = props => {
69325
69652
  onOpen,
69326
69653
  children: childrenProp,
69327
69654
  mount,
69655
+ // Read by usePopupName below, never written on the element.
69656
+ openerAriaLabel,
69657
+ openerAriaLabelledBy,
69328
69658
  ...rest
69329
69659
  } = props;
69330
69660
  // Assigned on every render, like openEffect below, so it always closes over
@@ -69335,7 +69665,16 @@ const useDialogProps = props => {
69335
69665
  mount,
69336
69666
  anchor
69337
69667
  });
69338
- const children = contentMounted ? childrenProp : null;
69668
+ const [nameProps, registerTitle] = usePopupName({
69669
+ ariaLabel: rest["aria-label"],
69670
+ ariaLabelledBy: rest["aria-labelledby"],
69671
+ openerAriaLabel,
69672
+ openerAriaLabelledBy
69673
+ });
69674
+ const children = contentMounted ? jsx(PopupTitleContext.Provider, {
69675
+ value: registerTitle,
69676
+ children: childrenProp
69677
+ }) : null;
69339
69678
  // Where it is placed, and whether there is a wall — the two questions that
69340
69679
  // pick the show call (see this file's top comment).
69341
69680
  const isTopLayer = layer === "top";
@@ -70125,6 +70464,7 @@ const useDialogProps = props => {
70125
70464
  "backdropFilter": backdrop ? backdropFilter : undefined,
70126
70465
  "styleCSSVars": DIALOG_STYLE_CSS_VARS,
70127
70466
  ...rest,
70467
+ ...nameProps,
70128
70468
  ...autoFocusProps,
70129
70469
  "as": "dialog",
70130
70470
  ref,
@@ -70298,10 +70638,11 @@ const DIALOG_STYLE_CSS_VARS = {
70298
70638
  maxHeight: "--dialog-max-height"
70299
70639
  };
70300
70640
  Dialog.Close = PopupClose;
70641
+ Dialog.Title = PopupTitle;
70301
70642
 
70302
70643
  installImportMetaCssBuild(import.meta);
70303
70644
  let openLocalPopoverCount = 0;
70304
- const css$D = /* css */`
70645
+ const css$B = /* css */`
70305
70646
  /* jsenv-css-opaque: popupCss overlaps these rules, own sheet = cascade
70306
70647
  order depending on which popup renders first. */
70307
70648
 
@@ -70900,10 +71241,17 @@ const css$D = /* css */`
70900
71241
  * under `"while-opened"`, only a press on what opens the popup does (a
70901
71242
  * `--navi-open` button, a picker's trigger), never with an `onOpen`, and a
70902
71243
  * press that does not open the popup throws it away.
71244
+ * @param {string} [props.openerAriaLabelledBy] - The id(s) naming what opened
71245
+ * the popover: its accessible name when neither `aria-label`/`aria-labelledby`
71246
+ * nor a `<Popover.Title>` names it. A Picker passes its trigger's.
71247
+ * @param {string} [props.openerAriaLabel] - The same, as a string. A popover
71248
+ * something names — these, `aria-label`/`aria-labelledby`, a
71249
+ * `<Popover.Title>` — is a `dialog`, unless given another `role`: a name on
71250
+ * an element with no role is read by nobody.
70903
71251
  * @param {import("ignore:preact").ComponentChildren} props.children
70904
71252
  */
70905
71253
  const Popover = props => {
70906
- import.meta.css = [css$D, "@jsenv/navi/src/layout/popover.jsx"];
71254
+ import.meta.css = [css$B, "@jsenv/navi/src/layout/popover.jsx"];
70907
71255
  if (props.openController) {
70908
71256
  return jsx(ControlledPopover, {
70909
71257
  ...props
@@ -71140,6 +71488,9 @@ const usePopoverProps = props => {
71140
71488
  onOpen,
71141
71489
  children: childrenProp,
71142
71490
  mount,
71491
+ // Read by usePopupName below, never written on the element.
71492
+ openerAriaLabel,
71493
+ openerAriaLabelledBy,
71143
71494
  ...rest
71144
71495
  } = props;
71145
71496
  // Assigned on every render, like openEffect below, so it always closes over
@@ -71150,7 +71501,16 @@ const usePopoverProps = props => {
71150
71501
  mount,
71151
71502
  anchor
71152
71503
  });
71153
- const children = contentMounted ? childrenProp : null;
71504
+ const [nameProps, registerTitle] = usePopupName({
71505
+ ariaLabel: rest["aria-label"],
71506
+ ariaLabelledBy: rest["aria-labelledby"],
71507
+ openerAriaLabel,
71508
+ openerAriaLabelledBy
71509
+ });
71510
+ const children = contentMounted ? jsx(PopupTitleContext.Provider, {
71511
+ value: registerTitle,
71512
+ children: childrenProp
71513
+ }) : null;
71154
71514
  const isTopLayer = layer === "top";
71155
71515
  const ref = props.ref;
71156
71516
  const backdropRef = useRef();
@@ -71955,6 +72315,10 @@ const usePopoverProps = props => {
71955
72315
  "navi-hidden": openController.openedInDom ? undefined : "",
71956
72316
  "styleCSSVars": POPUP_STYLE_CSS_VARS,
71957
72317
  ...rest,
72318
+ ...nameProps,
72319
+ // A <div> has no role, and a name on an element without one is read by
72320
+ // nobody: a popover something names is a dialog, unless told what else.
72321
+ "role": rest.role ?? (nameProps["aria-labelledby"] === undefined && nameProps["aria-label"] === undefined ? undefined : "dialog"),
71958
72322
  ...autoFocusProps,
71959
72323
  ref,
71960
72324
  // Not a control, but still something the rest of navi has to be able to
@@ -72070,9 +72434,10 @@ const resolvePositionAreaAndAnimationKind = ({
72070
72434
  };
72071
72435
  };
72072
72436
  Popover.Close = PopupClose;
72437
+ Popover.Title = PopupTitle;
72073
72438
 
72074
72439
  installImportMetaCssBuild(import.meta);
72075
- const css$C = /* css */`@layer navi {
72440
+ const css$A = /* css */`@layer navi {
72076
72441
  .navi_popup {
72077
72442
  --popup-border-radius: var(--navi-popup-border-radius);
72078
72443
  --popup-border-width: 1px;
@@ -72213,6 +72578,10 @@ const css$C = /* css */`@layer navi {
72213
72578
  * @param {string} [props.className] - Merged with the shared
72214
72579
  * `"navi_popup"` class (see this file's own CSS) rather than replacing
72215
72580
  * it.
72581
+ * @param {string} [props.openerAriaLabelledBy] - Forwarded as-is: the name the
72582
+ * popup takes when neither `aria-label`/`aria-labelledby` nor a
72583
+ * `<Popup.Title>` in `children` names it (see `Dialog`/`Popover`).
72584
+ * @param {string} [props.openerAriaLabel] - Forwarded as-is, same.
72216
72585
  * @param {"always"|"idle"|"from-first-open"|"while-opened"} [props.mount] - When
72217
72586
  * `children` are built and thrown away (see popup_content_mount.js).
72218
72587
  * `"from-first-open"` (the default) builds them on the first open and keeps
@@ -72228,7 +72597,7 @@ const css$C = /* css */`@layer navi {
72228
72597
  * @param {import("ignore:preact").ComponentChildren} props.children
72229
72598
  */
72230
72599
  const Popup = props => {
72231
- import.meta.css = [css$C, "@jsenv/navi/src/layout/popup.jsx"];
72600
+ import.meta.css = [css$A, "@jsenv/navi/src/layout/popup.jsx"];
72232
72601
  const {
72233
72602
  mode: modeProp,
72234
72603
  maxWidth,
@@ -72311,6 +72680,7 @@ const Popup = props => {
72311
72680
  });
72312
72681
  };
72313
72682
  Popup.Close = PopupClose;
72683
+ Popup.Title = PopupTitle;
72314
72684
 
72315
72685
  // What Dialog's own animation values mean to a popover, which has no lift
72316
72686
  // (see the `animation` doc above).
@@ -72359,6 +72729,54 @@ const pickerUIIsNaviOwn = ui => {
72359
72729
  return Boolean(ui) && typeof ui === "object" && Boolean(ui.type?.isPickerOwnUI);
72360
72730
  };
72361
72731
 
72732
+ /*
72733
+ * What the picker really is, said on the element that holds the name and takes
72734
+ * the focus (the host input in picker.jsx), which is both what a screen reader
72735
+ * announces and what getByRole(role, { name }) finds:
72736
+ * - a picker that picks nothing is a door — its popup is a question
72737
+ * (type="confirm") or something to read (mode="callout", which says it for
72738
+ * itself) — and a door is pressed: "button";
72739
+ * - a picker holding a value and opening a popup of navi's is a select-only
72740
+ * combobox: it shows a value the user does not type, and opens what changes
72741
+ * it. A textbox could not say the rest — aria-expanded is not allowed on one,
72742
+ * so "open" and "closed" would never be announced;
72743
+ * - a picker showing the browser's own picker (no popup of navi's) stays the
72744
+ * textbox its <input> is.
72745
+ */
72746
+ const getPickerHostRole = props => {
72747
+ if (props.role !== undefined) {
72748
+ return props.role;
72749
+ }
72750
+ if (props.picksNothing) {
72751
+ return "button";
72752
+ }
72753
+ if (props["aria-haspopup"] !== undefined) {
72754
+ return "combobox";
72755
+ }
72756
+ return undefined;
72757
+ };
72758
+
72759
+ /*
72760
+ * And what that button is called, when the caller wrote it as a drawing rather
72761
+ * than as a word: a door draws no value, so what sits in its value slot IS its
72762
+ * label — the `ui` a caller gave it, or its placeholder. Undefined the moment
72763
+ * the caller names it themselves (aria-label/aria-labelledby), which is the
72764
+ * only way to name a door drawn as an icon. Read by the popup too: what the
72765
+ * user pressed is a fair name for what it opened.
72766
+ */
72767
+ const getPickerHostLabelId = (id, props) => {
72768
+ if (getPickerHostRole(props) !== "button") {
72769
+ return undefined;
72770
+ }
72771
+ if (props["aria-label"] !== undefined || props["aria-labelledby"] !== undefined) {
72772
+ return undefined;
72773
+ }
72774
+ if (props.variant === "headless" || props.ui === "default") {
72775
+ return undefined;
72776
+ }
72777
+ return `${id}_picker_label`;
72778
+ };
72779
+
72362
72780
  /**
72363
72781
  * What the picker holds, read from inside its `ui` — for a drawing given as
72364
72782
  * an element (`ui={<MyCard game={game} />}`), which the picker cannot hand
@@ -72388,7 +72806,7 @@ const usePickerState = () => {
72388
72806
  };
72389
72807
 
72390
72808
  installImportMetaCssBuild(import.meta);
72391
- const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
72809
+ const css$z = /* css */`.navi_picker_callout_dock > [data-picker-content] {
72392
72810
  display: none;
72393
72811
  }
72394
72812
 
@@ -72417,7 +72835,7 @@ const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
72417
72835
  }
72418
72836
  }
72419
72837
 
72420
- .navi_picker[aria-haspopup="listbox"][aria-expanded="true"][navi-popover-mode="overlay"] {
72838
+ .navi_picker[navi-popover-mode="overlay"][aria-expanded="true"] {
72421
72839
  border-color: #0000;
72422
72840
  }
72423
72841
 
@@ -72445,7 +72863,7 @@ const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
72445
72863
  }
72446
72864
  `;
72447
72865
  const PickerCustomResolver = props => {
72448
- import.meta.css = [css$B, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
72866
+ import.meta.css = [css$z, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
72449
72867
  if (props.children === undefined) {
72450
72868
  return renderResolver(PickerNative, props);
72451
72869
  }
@@ -72616,8 +73034,9 @@ const PickerCustom = props => {
72616
73034
  props.id = props.id || controlId || idDefault;
72617
73035
  // Given no value, the picker holds what the control in its popup holds (see
72618
73036
  // the `mount` decision below) — and a group around it collects that from the
72619
- // moment both exist.
72620
- const valueInPopup = !isControlValueGivenByProps(props);
73037
+ // moment both exist. A picker that picks nothing holds none: its popup's
73038
+ // controls are never adopted (see canRegisterAsFacadeChild).
73039
+ const valueInPopup = !props.picksNothing && !isControlValueGivenByProps(props);
72621
73040
  const parentController = useContext(ParentUIStateControllerContext);
72622
73041
  const answersToGroup = Boolean(parentController) && !props.standalone;
72623
73042
  // Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
@@ -73254,6 +73673,24 @@ const isPressDisputedByHold = pickerEl => Boolean(pickerEl?.parentElement?.close
73254
73673
  const getPickerInput = pickerEl => {
73255
73674
  return pickerEl.querySelector(".navi_picker_input");
73256
73675
  };
73676
+
73677
+ // The <label>s naming a picker's trigger, by id. Read off the document: it is
73678
+ // the one place a label[for] and its control meet (navi's own Label carries an
73679
+ // id for this). A label wrapping the picker is left out — it holds the trigger
73680
+ // and the popup with it, and the popup would be named after its own content.
73681
+ const getTriggerLabelIds = pickerEl => {
73682
+ const labels = pickerEl && getPickerInput(pickerEl)?.labels;
73683
+ if (!labels) {
73684
+ return undefined;
73685
+ }
73686
+ const ids = [];
73687
+ for (const label of labels) {
73688
+ if (label.id && !label.contains(pickerEl)) {
73689
+ ids.push(label.id);
73690
+ }
73691
+ }
73692
+ return ids.length === 0 ? undefined : ids.join(" ");
73693
+ };
73257
73694
  const getPickerInputUIState = pickerEl => {
73258
73695
  const pickerInput = getPickerInput(pickerEl);
73259
73696
  return getUIStateFromElement(pickerInput);
@@ -73276,6 +73713,11 @@ const PickerContentInsidePopup = props => {
73276
73713
  // Same reason: a `data-testid` on the picker names the trigger (see
73277
73714
  // docs/testid.md) — this one names the popup.
73278
73715
  popupTestId,
73716
+ // Same again for the accessible name: aria-label/aria-labelledby on the
73717
+ // picker name the trigger (see control_context.js), these the popup.
73718
+ popupAriaLabel,
73719
+ popupAriaLabelledBy,
73720
+ popupAriaDescribedBy,
73279
73721
  positionArea,
73280
73722
  popoverMode = "nearby",
73281
73723
  popoverSpacing = popoverMode === "nearby" ? 5 : 0,
@@ -73337,8 +73779,25 @@ const PickerContentInsidePopup = props => {
73337
73779
  } = props;
73338
73780
  const isPopover = mode === "popover";
73339
73781
  const isCallout = mode === "callout";
73340
- return jsx(Next, {
73341
- "aria-haspopup": isPopover ? "listbox" : "dialog",
73782
+ // The trigger's name, in the order the browser reads it, for the popup to
73783
+ // take when neither the caller nor a <Popup.Title> names it: "Report this
73784
+ // book" is a fair name for the sheet it opens, "Start date" for a calendar.
73785
+ let openerAriaLabelledBy = rest["aria-labelledby"] ?? getPickerHostLabelId(props.id, props);
73786
+ let openerAriaLabel;
73787
+ if (openerAriaLabelledBy === undefined) {
73788
+ openerAriaLabel = rest["aria-label"];
73789
+ // Read only while open: the labels are a question for the document, and
73790
+ // a closed popup has nobody to announce it.
73791
+ if (openerAriaLabel === undefined && rest["aria-expanded"]) {
73792
+ openerAriaLabelledBy = getTriggerLabelIds(props.ref.current);
73793
+ }
73794
+ }
73795
+ return jsx(Next
73796
+ // A dialog in either mode: what a picker opens is content of the
73797
+ // caller's, never a listbox (a selectable List is a group of radios or
73798
+ // checkboxes). The popover says so with its role, below.
73799
+ , {
73800
+ "aria-haspopup": "dialog",
73342
73801
  "navi-popover-mode": isPopover ? popoverMode : undefined,
73343
73802
  ...rest,
73344
73803
  // On popupProps already (see the picker's popup assembly); they mean
@@ -73383,11 +73842,23 @@ const PickerContentInsidePopup = props => {
73383
73842
  pressOutside: pressOutside,
73384
73843
  children: jsx(PopupModeContext.Provider, {
73385
73844
  value: mode,
73386
- children: children
73845
+ children: jsx(PopupTitleContext.Provider, {
73846
+ value: null,
73847
+ children: children
73848
+ })
73387
73849
  })
73388
73850
  }) : jsx(Popup, {
73389
73851
  ...popupProps,
73390
73852
  "data-testid": popupTestId
73853
+ // What the trigger's aria-haspopup announces; a <dialog> says it
73854
+ // for itself.
73855
+ ,
73856
+ role: isPopover ? "dialog" : undefined,
73857
+ "aria-label": popupAriaLabel,
73858
+ "aria-labelledby": popupAriaLabelledBy,
73859
+ "aria-describedby": popupAriaDescribedBy,
73860
+ openerAriaLabel: openerAriaLabel,
73861
+ openerAriaLabelledBy: openerAriaLabelledBy
73391
73862
  // What the CSS above keys on: this picker's own popup, and no other
73392
73863
  // popup below the picker.
73393
73864
  ,
@@ -73657,7 +74128,7 @@ const showNativePicker = (pickerInput, event) => {
73657
74128
  };
73658
74129
 
73659
74130
  installImportMetaCssBuild(import.meta);
73660
- const css$A = /* css */`.navi_picker_confirm_body {
74131
+ const css$y = /* css */`.navi_picker_confirm_body {
73661
74132
  min-width: 180px;
73662
74133
  max-width: 320px;
73663
74134
  padding: var(--navi-m);
@@ -73673,7 +74144,7 @@ const css$A = /* css */`.navi_picker_confirm_body {
73673
74144
  }
73674
74145
  `;
73675
74146
  const PickerConfirmResolver = props => {
73676
- import.meta.css = [css$A, "@jsenv/navi/src/control/picker/picker_confirm.jsx"];
74147
+ import.meta.css = [css$y, "@jsenv/navi/src/control/picker/picker_confirm.jsx"];
73677
74148
  const Next = useNextResolver();
73678
74149
  if (props.type !== "confirm") {
73679
74150
  return jsx(Next, {
@@ -73747,12 +74218,7 @@ const PickerConfirmResolver = props => {
73747
74218
  mode: mode,
73748
74219
  focusCapture: focusCapture,
73749
74220
  openWhileReadOnly: openWhileReadOnly,
73750
- onConfirm: onConfirm
73751
- // A question holds no value to read before it is asked: the popup is
73752
- // built on the first open, like that of a picker told its value (see
73753
- // the `mount` resolution in picker_custom.jsx).
73754
- ,
73755
- mount: MOUNT_DEFAULT,
74221
+ onConfirm: onConfirm,
73756
74222
  message: undefined,
73757
74223
  confirmLabel: undefined,
73758
74224
  cancelLabel: undefined,
@@ -74199,7 +74665,7 @@ const LoadingIndicator = ({
74199
74665
  };
74200
74666
 
74201
74667
  installImportMetaCssBuild(import.meta);
74202
- const css$z = /* css */`@layer navi {
74668
+ const css$x = /* css */`@layer navi {
74203
74669
  .navi_separator {
74204
74670
  --size: 1px;
74205
74671
  --color: var(--navi-separator-color-default);
@@ -74273,7 +74739,7 @@ const Separator = ({
74273
74739
  style,
74274
74740
  ...props
74275
74741
  }) => {
74276
- import.meta.css = [css$z, "@jsenv/navi/src/layout/separator.jsx"];
74742
+ import.meta.css = [css$x, "@jsenv/navi/src/layout/separator.jsx"];
74277
74743
  return jsx(Box, {
74278
74744
  as: vertical ? "span" : "hr",
74279
74745
  ...props,
@@ -75067,7 +75533,7 @@ const listItemBlockedMessage = (loading, props) => {
75067
75533
  };
75068
75534
 
75069
75535
  installImportMetaCssBuild(import.meta);
75070
- const css$y = /* css */`@layer navi {
75536
+ const css$w = /* css */`@layer navi {
75071
75537
  .navi_list_container[navi-selectable] {
75072
75538
  --list-item-outline-width: var(--navi-focus-outline-width);
75073
75539
  --list-item-outline-offset: calc(-1 * var(--list-item-outline-width));
@@ -75240,7 +75706,7 @@ const ListSelectableResolver = props => {
75240
75706
  };
75241
75707
  const ListSelectable = props => {
75242
75708
  const Next = useNextResolver();
75243
- import.meta.css = [css$y, "@jsenv/navi/src/control/list/list_selectable.jsx"];
75709
+ import.meta.css = [css$w, "@jsenv/navi/src/control/list/list_selectable.jsx"];
75244
75710
  // we allow ourselves to auto-generate a name
75245
75711
  const defaultName = useId();
75246
75712
  props.name = props.name || `listbox_${defaultName}`;
@@ -75909,7 +76375,7 @@ const ListRunItemContext = createContext(null);
75909
76375
  // item takes its place in the collection by slot: the place is then the list's
75910
76376
  // to move, and the item's to follow — see list_items.js.
75911
76377
  const ListSlotContext = createContext(null);
75912
- const css$x = /* css */`@property --list-group-label-height {
76378
+ const css$v = /* css */`@property --list-group-label-height {
75913
76379
  syntax: "<length>";
75914
76380
  inherits: false;
75915
76381
  initial-value: 0;
@@ -76437,7 +76903,7 @@ const resolveRenderBudget = renderBudget => {
76437
76903
  };
76438
76904
  };
76439
76905
  const ListUI = props => {
76440
- import.meta.css = [css$x, "@jsenv/navi/src/control/list/list.jsx"];
76906
+ import.meta.css = [css$v, "@jsenv/navi/src/control/list/list.jsx"];
76441
76907
  const {
76442
76908
  ref,
76443
76909
  renderBudget: renderBudgetProp,
@@ -82552,7 +83018,7 @@ const createBadgeRegistry = () => {
82552
83018
  };
82553
83019
 
82554
83020
  installImportMetaCssBuild(import.meta);
82555
- const css$w = /* css */`@layer navi;
83021
+ const css$u = /* css */`@layer navi;
82556
83022
 
82557
83023
  .navi_badge {
82558
83024
  --font-size: .7em;
@@ -82655,7 +83121,7 @@ const BadgeUI = ({
82655
83121
  className,
82656
83122
  ...props
82657
83123
  }) => {
82658
- import.meta.css = [css$w, "@jsenv/navi/src/text/badge.jsx"];
83124
+ import.meta.css = [css$u, "@jsenv/navi/src/text/badge.jsx"];
82659
83125
  const defaultRef = useRef();
82660
83126
  props.ref = props.ref || defaultRef;
82661
83127
  const {
@@ -82763,7 +83229,7 @@ const wrapLabels = childArray => {
82763
83229
  };
82764
83230
 
82765
83231
  installImportMetaCssBuild(import.meta);
82766
- const css$v = /* css */`@layer navi;
83232
+ const css$t = /* css */`@layer navi;
82767
83233
 
82768
83234
  .navi_badge_list {
82769
83235
  flex-wrap: wrap;
@@ -82898,7 +83364,7 @@ const useBadgeRegistry = (children, enabled) => {
82898
83364
  * component grants (a <Picker>, see max_lines_context.js).
82899
83365
  */
82900
83366
  const BadgeList = props => {
82901
- import.meta.css = [css$v, "@jsenv/navi/src/text/badge_list.jsx"];
83367
+ import.meta.css = [css$t, "@jsenv/navi/src/text/badge_list.jsx"];
82902
83368
  const {
82903
83369
  maxLines,
82904
83370
  max,
@@ -83242,7 +83708,7 @@ const BadgeListContent = ({
83242
83708
  };
83243
83709
 
83244
83710
  installImportMetaCssBuild(import.meta);
83245
- const css$u = /* css */`.navi_color {
83711
+ const css$s = /* css */`.navi_color {
83246
83712
  aspect-ratio: 1;
83247
83713
  background-color: currentColor;
83248
83714
  border: 1px solid #0003;
@@ -83262,7 +83728,7 @@ const Color = ({
83262
83728
  children,
83263
83729
  ...rest
83264
83730
  }) => {
83265
- import.meta.css = [css$u, "@jsenv/navi/src/text/color.jsx"];
83731
+ import.meta.css = [css$s, "@jsenv/navi/src/text/color.jsx"];
83266
83732
  const color = children || undefined;
83267
83733
  return jsx(Box, {
83268
83734
  as: "span",
@@ -83749,7 +84215,7 @@ const PickerFileUI = /*#__PURE__*/asPickerOwnUI(() => {
83749
84215
  });
83750
84216
 
83751
84217
  installImportMetaCssBuild(import.meta);
83752
- const css$t = /* css */`@layer navi {
84218
+ const css$r = /* css */`@layer navi {
83753
84219
  .navi_picker {
83754
84220
  --picker-border-radius: var(--navi-control-border-radius);
83755
84221
  --picker-border-width: var(--navi-control-border-width);
@@ -84218,7 +84684,7 @@ const css$t = /* css */`@layer navi {
84218
84684
  }
84219
84685
  `;
84220
84686
  const PickerButton = props => {
84221
- import.meta.css = [css$t, "@jsenv/navi/src/control/picker/picker.jsx"];
84687
+ import.meta.css = [css$r, "@jsenv/navi/src/control/picker/picker.jsx"];
84222
84688
  if (typeof props.maxLines === "string") {
84223
84689
  props.maxLines = parseInt(props.maxLines);
84224
84690
  }
@@ -84259,11 +84725,6 @@ const PickerButton = props => {
84259
84725
  // confirm resolver (see picker_confirm.jsx), which is the picker that is
84260
84726
  // one — a caller whose popup is a menu of actions says it for themselves.
84261
84727
  picksNothing,
84262
- // Where the caller's own role goes: onto the host, beside the name (see
84263
- // control_context.js) — a role and a name on two different elements name
84264
- // nothing at all. Left unsaid, a picker that picks nothing takes "button"
84265
- // (see PickerInput below).
84266
- role,
84267
84728
  readOnly,
84268
84729
  error,
84269
84730
  // Who draws the loading outline. Left unsaid, the picker does, around its
@@ -84344,21 +84805,8 @@ const PickerButton = props => {
84344
84805
  // clearing being a modification like any other.
84345
84806
  const interactive = !basePseudoState[":disabled"] && !basePseudoState[":read-only"] && !loading;
84346
84807
  usePickerErrorCallout(uiStateController, error);
84347
- // What the picker really is, said on the element that holds the name and
84348
- // takes the focus (the host input below): a picker that picks nothing is a
84349
- // door — its popup is a question (type="confirm") or something to read
84350
- // (mode="callout", which says it for itself) — and a door is pressed. So it
84351
- // answers to "button" rather than to the "textbox" its <input> would
84352
- // otherwise be, which is both what a screen reader announces and what
84353
- // getByRole("button", { name }) finds. A picker holding a value stays a
84354
- // textbox: it does hold text.
84355
- const hostRole = role ?? (picksNothing ? "button" : undefined);
84356
- // And what that button is called, when the caller wrote it as a drawing
84357
- // rather than as a word: a door draws no value, so what sits in its value
84358
- // slot IS its label — the `ui` a caller gave it, or its placeholder. Skipped
84359
- // the moment the caller names it themselves (aria-label/aria-labelledby),
84360
- // which is the only way to name a door drawn as an icon.
84361
- const hostLabelId = hostRole === "button" && inputProps["aria-label"] === undefined && inputProps["aria-labelledby"] === undefined && variant !== "headless" && ui !== "default" ? `${inputProps.id}_picker_label` : undefined;
84808
+ const hostRole = getPickerHostRole(props);
84809
+ const hostLabelId = getPickerHostLabelId(inputProps.id, props);
84362
84810
  // What the picker knows about itself, for the pieces it does not place: the
84363
84811
  // drawings of its value (Picker.UI.*), and the affordances a caller may put
84364
84812
  // in their own `ui` (Picker.Clear) as much as the ones it puts in its slot.
@@ -84427,8 +84875,16 @@ const PickerButton = props => {
84427
84875
  basePseudoState: basePseudoState,
84428
84876
  styleCSSVars: PickerStyleCSSVars,
84429
84877
  variant: undefined,
84430
- icon: undefined,
84878
+ icon: undefined
84879
+ // The caller's own role goes onto the host, beside the name (see
84880
+ // control_context.js): a role and a name on two different elements
84881
+ // name nothing at all. What the trigger opens is said there too, and
84882
+ // aria-haspopup with it. aria-expanded stays here as well: commands.js
84883
+ // and the CSS read the open state off the root (see
84884
+ // expandableRepeatedBy in commands.js).
84885
+ ,
84431
84886
  role: undefined,
84887
+ "aria-haspopup": undefined,
84432
84888
  rightSlotIcon: undefined,
84433
84889
  rightSlotIconSize: undefined,
84434
84890
  rightSlot: undefined,
@@ -84468,6 +84924,8 @@ const PickerButton = props => {
84468
84924
  inset: loadingOutlineInset
84469
84925
  }), jsx(PickerInput, {
84470
84926
  role: hostRole,
84927
+ "aria-haspopup": props["aria-haspopup"],
84928
+ "aria-expanded": props["aria-expanded"],
84471
84929
  "aria-labelledby": hostLabelId,
84472
84930
  tabIndex: variant === "headless" ? -1 : undefined,
84473
84931
  "aria-hidden": variant === "headless" ? "true" : undefined,
@@ -84993,7 +85451,7 @@ const Picker = /*#__PURE__*/Object.assign(PickerResolved, {
84993
85451
  });
84994
85452
 
84995
85453
  installImportMetaCssBuild(import.meta);
84996
- const css$s = /* css */`@layer navi {
85454
+ const css$q = /* css */`@layer navi {
84997
85455
  .navi_picker_spin {
84998
85456
  --picker-spin-padding-x-default: var(--navi-picker-padding-x-default);
84999
85457
  --picker-spin-padding-y-default: var(--navi-picker-padding-y-default);
@@ -85343,7 +85801,7 @@ const Spin = ({
85343
85801
  nextLabel,
85344
85802
  ...rest
85345
85803
  }) => {
85346
- import.meta.css = [css$s, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
85804
+ import.meta.css = [css$q, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
85347
85805
  const id = useId();
85348
85806
  // What the group around it says, when there is one: how big the whole thing
85349
85807
  // is written is said once, on the group, and every spin in it follows.
@@ -86054,7 +86512,7 @@ const renderValueDefault = value => String(value ?? "");
86054
86512
  * are passed on to the spins sitting in them.
86055
86513
  */
86056
86514
  const SpinGroup = props => {
86057
- import.meta.css = [css$s, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
86515
+ import.meta.css = [css$q, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
86058
86516
  const {
86059
86517
  size
86060
86518
  } = props;
@@ -86563,7 +87021,7 @@ const TimeRangeSpin = ({
86563
87021
  };
86564
87022
 
86565
87023
  installImportMetaCssBuild(import.meta);
86566
- const css$r = /* css */`.navi_checkbox_group {
87024
+ const css$p = /* css */`.navi_checkbox_group {
86567
87025
  border-style: solid;
86568
87026
 
86569
87027
  &[data-callout] {
@@ -86623,7 +87081,7 @@ const CheckboxGroup = props => {
86623
87081
  return checkboxGroup;
86624
87082
  };
86625
87083
  const CheckboxGroupInterface = props => {
86626
- import.meta.css = [css$r, "@jsenv/navi/src/control/input/checkbox_group.jsx"];
87084
+ import.meta.css = [css$p, "@jsenv/navi/src/control/input/checkbox_group.jsx"];
86627
87085
  const {
86628
87086
  ref
86629
87087
  } = props;
@@ -86741,7 +87199,7 @@ const formatIntlUnit = (unit, {
86741
87199
  };
86742
87200
 
86743
87201
  installImportMetaCssBuild(import.meta);
86744
- const css$q = /* css */`.navi_input_duration {
87202
+ const css$o = /* css */`.navi_input_duration {
86745
87203
  --duration-separator-spacing: 4px;
86746
87204
  --loader-color: var(--navi-loader-color);
86747
87205
  position: relative;
@@ -86806,7 +87264,7 @@ const css$q = /* css */`.navi_input_duration {
86806
87264
  * "auto" aligns each field toward its neighbouring separator (first→right, last→left, middle/solo→center).
86807
87265
  */
86808
87266
  const InputDuration = props => {
86809
- import.meta.css = [css$q, "@jsenv/navi/src/control/input/input_duration.jsx"];
87267
+ import.meta.css = [css$o, "@jsenv/navi/src/control/input/input_duration.jsx"];
86810
87268
  const defaultRef = useRef();
86811
87269
  props.ref = props.ref || defaultRef;
86812
87270
  props.max = props.max || "23h59";
@@ -87307,7 +87765,7 @@ const InputDurationPart = ({
87307
87765
  };
87308
87766
 
87309
87767
  installImportMetaCssBuild(import.meta);
87310
- const css$p = /* css */`.navi_radio_group {
87768
+ const css$n = /* css */`.navi_radio_group {
87311
87769
  border-style: solid;
87312
87770
 
87313
87771
  &[data-callout] {
@@ -87347,7 +87805,7 @@ const RadioGroup = props => {
87347
87805
  return radioGroup;
87348
87806
  };
87349
87807
  const RadioGroupInterface = props => {
87350
- import.meta.css = [css$p, "@jsenv/navi/src/control/input/radio_group.jsx"];
87808
+ import.meta.css = [css$n, "@jsenv/navi/src/control/input/radio_group.jsx"];
87351
87809
  const {
87352
87810
  ref
87353
87811
  } = props;
@@ -87377,7 +87835,7 @@ const RadioGroupInterface = props => {
87377
87835
  };
87378
87836
 
87379
87837
  installImportMetaCssBuild(import.meta);
87380
- const css$o = /* css */`.navi_input.navi_select {
87838
+ const css$m = /* css */`.navi_input.navi_select {
87381
87839
  & .navi_control_input {
87382
87840
  padding-right: calc(var(--x-padding-right) + 1em);
87383
87841
  line-height: var(--navi-control-line-height);
@@ -87424,7 +87882,7 @@ const Select = ({
87424
87882
  ...props
87425
87883
  }) => {
87426
87884
  installInputCss();
87427
- import.meta.css = [css$o, "@jsenv/navi/src/control/input/select.jsx"];
87885
+ import.meta.css = [css$m, "@jsenv/navi/src/control/input/select.jsx"];
87428
87886
  const defaultRef = useRef(null);
87429
87887
  props.ref = props.ref || defaultRef;
87430
87888
  seedDefaultValueFromSignal(props);
@@ -87474,7 +87932,7 @@ const Select = ({
87474
87932
  };
87475
87933
 
87476
87934
  installImportMetaCssBuild(import.meta);
87477
- const css$n = /* css */`.navi_split_button {
87935
+ const css$l = /* css */`.navi_split_button {
87478
87936
  border-radius: var(--navi-control-border-radius);
87479
87937
  display: inline-flex;
87480
87938
  position: relative;
@@ -87596,7 +88054,7 @@ const css$n = /* css */`.navi_split_button {
87596
88054
  * on the split button's own box.
87597
88055
  */
87598
88056
  const SplitButton = props => {
87599
- import.meta.css = [css$n, "@jsenv/navi/src/control/input/split_button.jsx"];
88057
+ import.meta.css = [css$l, "@jsenv/navi/src/control/input/split_button.jsx"];
87600
88058
  const {
87601
88059
  options = [],
87602
88060
  value,
@@ -88615,7 +89073,7 @@ const createItemTracker = (onChange) => {
88615
89073
  };
88616
89074
 
88617
89075
  installImportMetaCssBuild(import.meta);
88618
- const css$m = /* css */`.navi_wheel_container {
89076
+ const css$k = /* css */`.navi_wheel_container {
88619
89077
  --wheel-item-height: round(1.8em, 1px);
88620
89078
  --wheel-emphasis-size: round(1.5em, 1px);
88621
89079
  --wheel-item-width: 3.5ch;
@@ -89563,7 +90021,7 @@ const useWheelKeyboard = ({
89563
90021
  }, [isHorizontal, interactive, isLoop]);
89564
90022
  };
89565
90023
  function WheelUI(props) {
89566
- import.meta.css = [css$m, "@jsenv/navi/src/control/wheel/wheel.jsx"];
90024
+ import.meta.css = [css$k, "@jsenv/navi/src/control/wheel/wheel.jsx"];
89567
90025
  const {
89568
90026
  ref,
89569
90027
  visibleCount = 3,
@@ -90553,7 +91011,7 @@ Wheel.Item = WheelItem;
90553
91011
  * @param {boolean|number} [props.zoom] - Enlarge the centered value of every wheel (see Wheel's zoom prop) with one prop for the whole group.
90554
91012
  */
90555
91013
  const WheelGroup = props => {
90556
- import.meta.css = [css$m, "@jsenv/navi/src/control/wheel/wheel.jsx"];
91014
+ import.meta.css = [css$k, "@jsenv/navi/src/control/wheel/wheel.jsx"];
90557
91015
  // WheelGroup IS a control group: it aggregates its named wheels ("hours",
90558
91016
  // "minutes"…) into one object value, so it can sit directly inside a Form or a
90559
91017
  // Picker with no extra <ControlGroup> wrapper. The wheel-specific presentation
@@ -90726,7 +91184,7 @@ const WheelColon = props => {
90726
91184
  Wheel.Colon = WheelColon;
90727
91185
 
90728
91186
  installImportMetaCssBuild(import.meta);
90729
- const css$l = /* css */`.navi_time_range_label {
91187
+ const css$j = /* css */`.navi_time_range_label {
90730
91188
  --wheel-item-height: round(1.8em, 1px);
90731
91189
  color: var(--wheel-color, light-dark(#111, #eee));
90732
91190
  line-height: var(--wheel-item-height);
@@ -90950,7 +91408,7 @@ const TimeRangeWheel = ({
90950
91408
  endTimeProps,
90951
91409
  ...rest
90952
91410
  }) => {
90953
- import.meta.css = [css$l, "@jsenv/navi/src/control/wheel/wheel_time.jsx"];
91411
+ import.meta.css = [css$j, "@jsenv/navi/src/control/wheel/wheel_time.jsx"];
90954
91412
  const startId = useId();
90955
91413
  const startRef = useRef(null);
90956
91414
  const endRef = useRef(null);
@@ -91538,7 +91996,7 @@ const Z_INDEX_DROP_PREVIEW = Z_INDEX_STICKY_CORNER + 1;
91538
91996
  const Z_INDEX_TABLE_UI = Z_INDEX_STICKY_CORNER + 1;
91539
91997
 
91540
91998
  installImportMetaCssBuild(import.meta);
91541
- const css$k = /* css */`.navi_table_drag_clone_container {
91999
+ const css$i = /* css */`.navi_table_drag_clone_container {
91542
92000
  top: var(--table-visual-top);
91543
92001
  left: var(--table-visual-left);
91544
92002
  width: var(--table-visual-width);
@@ -91693,7 +92151,7 @@ const useTableDragContextValue = ({
91693
92151
  }, [grabTarget, canChangeColumnOrder]);
91694
92152
  };
91695
92153
  const TableDragCloneContainer = /*#__PURE__*/forwardRef((props, ref) => {
91696
- import.meta.css = [css$k, "@jsenv/navi/src/control/table/drag/table_drag.jsx"];
92154
+ import.meta.css = [css$i, "@jsenv/navi/src/control/table/drag/table_drag.jsx"];
91697
92155
  const {
91698
92156
  tableId
91699
92157
  } = props;
@@ -91992,7 +92450,7 @@ const ROW_MIN_HEIGHT = 30;
91992
92450
  const ROW_MAX_HEIGHT = 100;
91993
92451
  const COLUMN_MIN_WIDTH = 50;
91994
92452
  const COLUMN_MAX_WIDTH = 500;
91995
- const css$j = /* css */`@layer navi {
92453
+ const css$h = /* css */`@layer navi {
91996
92454
  .navi_table {
91997
92455
  --table-resizer-handle-color: #063b7c;
91998
92456
  --table-resizer-color: #387ec9;
@@ -92161,7 +92619,7 @@ const css$j = /* css */`@layer navi {
92161
92619
 
92162
92620
  // Column resize components
92163
92621
  const TableColumnResizer = props => {
92164
- import.meta.css = [css$j, "@jsenv/navi/src/control/table/resize/table_resize.jsx"];
92622
+ import.meta.css = [css$h, "@jsenv/navi/src/control/table/resize/table_resize.jsx"];
92165
92623
  const defaultRef = useRef();
92166
92624
  const ref = props.ref || defaultRef;
92167
92625
  return jsxs("div", {
@@ -92633,7 +93091,7 @@ const findPreviousTableRow = currentRow => {
92633
93091
  };
92634
93092
 
92635
93093
  installImportMetaCssBuild(import.meta);
92636
- const css$i = /* css */`@layer navi {
93094
+ const css$g = /* css */`@layer navi {
92637
93095
  .navi_table {
92638
93096
  --selection-border-color: var(--navi-selection-border-color, #0078d4);
92639
93097
  --selection-background-color: var(--navi-selection-background-color, #eaf1fd);
@@ -92727,7 +93185,7 @@ const useTableSelectionController = ({
92727
93185
  onSelectionChange,
92728
93186
  selectionColor
92729
93187
  }) => {
92730
- import.meta.css = [css$i, "@jsenv/navi/src/control/table/selection/table_selection.jsx"];
93188
+ import.meta.css = [css$g, "@jsenv/navi/src/control/table/selection/table_selection.jsx"];
92731
93189
  const selectionController = useSelectionController({
92732
93190
  elementRef: tableRef,
92733
93191
  layout: "grid",
@@ -93198,7 +93656,7 @@ const useTableStickyContextValue = ({
93198
93656
  };
93199
93657
 
93200
93658
  installImportMetaCssBuild(import.meta);
93201
- const css$h = /* css */`@layer navi {
93659
+ const css$f = /* css */`@layer navi {
93202
93660
  .navi_table {
93203
93661
  --sticky-frontier-color: silver;
93204
93662
  --sticky-frontier-size: 12px;
@@ -93371,7 +93829,7 @@ const css$h = /* css */`@layer navi {
93371
93829
  const TableStickyFrontier = ({
93372
93830
  tableRef
93373
93831
  }) => {
93374
- import.meta.css = [css$h, "@jsenv/navi/src/control/table/sticky/table_sticky.jsx"];
93832
+ import.meta.css = [css$f, "@jsenv/navi/src/control/table/sticky/table_sticky.jsx"];
93375
93833
  const stickyLeftFrontierGhostRef = useRef();
93376
93834
  const stickyLeftFrontierPreviewRef = useRef();
93377
93835
  const stickyTopFrontierGhostRef = useRef();
@@ -93602,7 +94060,7 @@ installImportMetaCssBuild(import.meta);
93602
94060
  * inset 0 -1px 0 0 color; // Bottom border
93603
94061
  */
93604
94062
 
93605
- const css$g = /* css */ `.navi_table_root {
94063
+ const css$e = /* css */ `.navi_table_root {
93606
94064
  max-width: var(--table-max-width, none);
93607
94065
  max-height: var(--table-max-height, none);
93608
94066
  position: relative;
@@ -93787,11 +94245,11 @@ const css$g = /* css */ `.navi_table_root {
93787
94245
  // table must not carry this sheet, and a build that sees no caller drops the
93788
94246
  // css with the function.
93789
94247
  const installTableCss = () => {
93790
- import.meta.css = [css$g, "@jsenv/navi/src/control/table/table_css.js"];
94248
+ import.meta.css = [css$e, "@jsenv/navi/src/control/table/table_css.js"];
93791
94249
  };
93792
94250
 
93793
94251
  installImportMetaCssBuild(import.meta);
93794
- const css$f = /* css */`.navi_table_ui {
94252
+ const css$d = /* css */`.navi_table_ui {
93795
94253
  z-index: ${Z_INDEX_TABLE_UI};
93796
94254
  pointer-events: none;
93797
94255
  position: fixed;
@@ -93800,7 +94258,7 @@ const css$f = /* css */`.navi_table_ui {
93800
94258
  }
93801
94259
  `;
93802
94260
  const TableUI = /*#__PURE__*/forwardRef((props, ref) => {
93803
- import.meta.css = [css$f, "@jsenv/navi/src/control/table/table_ui.jsx"];
94261
+ import.meta.css = [css$d, "@jsenv/navi/src/control/table/table_ui.jsx"];
93804
94262
  const {
93805
94263
  tableRef,
93806
94264
  tableId,
@@ -94722,7 +95180,7 @@ const normalizeKey = (key) => {
94722
95180
  };
94723
95181
 
94724
95182
  installImportMetaCssBuild(import.meta);
94725
- const css$e = /* css */`.navi_shortcut_container[data-visually-hidden], .navi_shortcut_button[data-visually-hidden] {
95183
+ const css$c = /* css */`.navi_shortcut_container[data-visually-hidden], .navi_shortcut_button[data-visually-hidden] {
94726
95184
  clip: rect(0, 0, 0, 0);
94727
95185
  white-space: nowrap;
94728
95186
  opacity: 0;
@@ -94739,7 +95197,7 @@ const css$e = /* css */`.navi_shortcut_container[data-visually-hidden], .navi_sh
94739
95197
  const ActiveKeyboardShortcuts = ({
94740
95198
  visible
94741
95199
  }) => {
94742
- import.meta.css = [css$e, "@jsenv/navi/src/keyboard/active_keyboard_shortcuts.jsx"];
95200
+ import.meta.css = [css$c, "@jsenv/navi/src/keyboard/active_keyboard_shortcuts.jsx"];
94743
95201
  const activeShortcuts = activeShortcutsSignal.value;
94744
95202
  return jsx("div", {
94745
95203
  className: "navi_shortcut_container",
@@ -94779,7 +95237,7 @@ const KeyboardShortcutAriaElement = ({
94779
95237
  };
94780
95238
 
94781
95239
  installImportMetaCssBuild(import.meta);
94782
- const css$d = /* css */`@layer navi {
95240
+ const css$b = /* css */`@layer navi {
94783
95241
  .navi_clipboard_container {
94784
95242
  --height: 1.5em;
94785
95243
  --notif-spacing: .5em;
@@ -94810,7 +95268,7 @@ const ButtonCopyToClipboard = ({
94810
95268
  children,
94811
95269
  ...props
94812
95270
  }) => {
94813
- import.meta.css = [css$d, "@jsenv/navi/src/control/input/button_copy_to_clipboard.jsx"];
95271
+ import.meta.css = [css$b, "@jsenv/navi/src/control/input/button_copy_to_clipboard.jsx"];
94814
95272
  const [copied, setCopied] = useState(false);
94815
95273
  const renderedRef = useRef();
94816
95274
  useEffect(() => {
@@ -94890,7 +95348,7 @@ const Address = ({
94890
95348
  };
94891
95349
 
94892
95350
  installImportMetaCssBuild(import.meta);
94893
- const css$c = /* css */`@layer navi;
95351
+ const css$a = /* css */`@layer navi;
94894
95352
 
94895
95353
  .navi_text.navi_badge_count {
94896
95354
  white-space: nowrap;
@@ -95019,7 +95477,7 @@ const BadgeCount = ({
95019
95477
  lineLayout,
95020
95478
  ...props
95021
95479
  }) => {
95022
- import.meta.css = [css$c, "@jsenv/navi/src/text/badge_count.jsx"];
95480
+ import.meta.css = [css$a, "@jsenv/navi/src/text/badge_count.jsx"];
95023
95481
  const defaultRef = useRef();
95024
95482
  props.ref = props.ref || defaultRef;
95025
95483
  const {
@@ -95162,7 +95620,7 @@ const BadgeCountCircle = ({
95162
95620
  };
95163
95621
 
95164
95622
  installImportMetaCssBuild(import.meta);
95165
- const css$b = /* css */`@layer navi {
95623
+ const css$9 = /* css */`@layer navi {
95166
95624
  .navi_caption {
95167
95625
  --color: #6b7280;
95168
95626
  }
@@ -95182,7 +95640,7 @@ const Caption = ({
95182
95640
  className,
95183
95641
  ...rest
95184
95642
  }) => {
95185
- import.meta.css = [css$b, "@jsenv/navi/src/text/caption.jsx"];
95643
+ import.meta.css = [css$9, "@jsenv/navi/src/text/caption.jsx"];
95186
95644
  return jsx(Text, {
95187
95645
  as: "small",
95188
95646
  size: "0.8em" // We use em to be relative to the parent (we want to be smaller than the surrounding text)
@@ -95578,177 +96036,8 @@ const Interpolate = ({
95578
96036
  });
95579
96037
  };
95580
96038
 
95581
- const SuccessSvg = () => {
95582
- return jsx("svg", {
95583
- viewBox: "0 0 16 16",
95584
- fill: "currentColor",
95585
- children: jsx("path", {
95586
- d: "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"
95587
- })
95588
- });
95589
- };
95590
- const ErrorSvg = () => {
95591
- return jsx("svg", {
95592
- viewBox: "0 0 16 16",
95593
- fill: "currentColor",
95594
- children: jsx("path", {
95595
- d: "M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"
95596
- })
95597
- });
95598
- };
95599
- const InfoSvg$1 = () => {
95600
- return jsx("svg", {
95601
- viewBox: "0 0 16 16",
95602
- fill: "currentColor",
95603
- children: jsx("path", {
95604
- d: "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"
95605
- })
95606
- });
95607
- };
95608
- const WarningSvg = () => {
95609
- return jsx("svg", {
95610
- viewBox: "0 0 16 16",
95611
- fill: "currentColor",
95612
- children: jsx("path", {
95613
- d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
95614
- })
95615
- });
95616
- };
95617
-
95618
96039
  installImportMetaCssBuild(import.meta);
95619
- const css$a = /* css */`@layer navi {
95620
- .navi_message_box {
95621
- --background-color-info: var(--navi-info-color-light);
95622
- --color-info: var(--navi-info-color);
95623
- --background-color-success: var(--navi-success-color-light);
95624
- --color-success: var(--navi-success-color);
95625
- --background-color-warning: var(--navi-warning-color-light);
95626
- --color-warning: var(--navi-warning-color);
95627
- --background-color-error: var(--navi-error-color-light);
95628
- --color-error: var(--navi-error-color);
95629
- }
95630
- }
95631
-
95632
- .navi_message_box {
95633
- --x-message-background-color: var(--background-color-info);
95634
- --x-message-color: var(--color-info);
95635
- background-color: var(--x-message-background-color);
95636
-
95637
- & > .navi_icon {
95638
- flex-shrink: 0;
95639
- }
95640
- }
95641
-
95642
- .navi_message_box[data-status-info] {
95643
- --x-message-background-color: var(--background-color-info);
95644
- --x-message-color: var(--color-info);
95645
- }
95646
-
95647
- .navi_message_box[data-status-success] {
95648
- --x-message-background-color: var(--background-color-success);
95649
- --x-message-color: var(--color-success);
95650
- }
95651
-
95652
- .navi_message_box[data-status-warning] {
95653
- --x-message-background-color: var(--background-color-warning);
95654
- --x-message-color: var(--color-warning);
95655
- }
95656
-
95657
- .navi_message_box[data-status-error] {
95658
- --x-message-background-color: var(--background-color-error);
95659
- --x-message-color: var(--color-error);
95660
- }
95661
-
95662
- .navi_message_box[data-left-stripe] {
95663
- border-left: 6px solid var(--x-message-color);
95664
- border-top-left-radius: 6px;
95665
- border-bottom-left-radius: 6px;
95666
- }
95667
- `;
95668
- const MessageBox = ({
95669
- status = "info",
95670
- padding = "sm",
95671
- icon,
95672
- leftStripe,
95673
- children,
95674
- onClose,
95675
- ...rest
95676
- }) => {
95677
- import.meta.css = [css$a, "@jsenv/navi/src/text/message_box.jsx"];
95678
- const [hasTitleChild, setHasTitleChild] = useState(false);
95679
- const innerLeftStripe = leftStripe === undefined ? hasTitleChild : leftStripe;
95680
- if (icon === true) {
95681
- icon = status === "info" ? jsx(InfoSvg$1, {}) : status === "success" ? jsx(SuccessSvg, {}) : status === "warning" ? jsx(WarningSvg, {}) : status === "error" ? jsx(ErrorSvg, {}) : null;
95682
- } else if (typeof icon === "function") {
95683
- const Comp = icon;
95684
- icon = jsx(Comp, {});
95685
- }
95686
- return jsx(Box, {
95687
- as: "div",
95688
- role: status === "info" ? "status" : "alert",
95689
- "data-left-stripe": innerLeftStripe ? "" : undefined,
95690
- inline: true,
95691
- column: true,
95692
- alignY: "center",
95693
- spacing: "s",
95694
- ...rest,
95695
- className: withPropsClassName("navi_message_box", rest.className),
95696
- padding: padding,
95697
- pseudoClasses: MessageBoxPseudoClasses,
95698
- basePseudoState: {
95699
- ":-navi-status-info": status === "info",
95700
- ":-navi-status-success": status === "success",
95701
- ":-navi-status-warning": status === "warning",
95702
- ":-navi-status-error": status === "error"
95703
- },
95704
- children: jsx(MessageBoxStatusContext.Provider, {
95705
- value: status,
95706
- children: jsxs(MessageBoxReportTitleChildContext.Provider, {
95707
- value: setHasTitleChild,
95708
- children: [icon && jsx(Icon, {
95709
- color: "var(--x-message-color)",
95710
- height: "1lh",
95711
- maxHeight: "auto",
95712
- selfAlignY: "start",
95713
- aspectRatio: "auto",
95714
- children: icon
95715
- }), jsx(Text, {
95716
- children: children
95717
- }), onClose &&
95718
- // A column as tall as the first line of the message, pinned to the
95719
- // top, the button centered in it: the close button stays level
95720
- // with the first line however many lines the message takes (same
95721
- // layout as Callout).
95722
- jsx(Box, {
95723
- flex: true,
95724
- alignY: "center",
95725
- height: "1lh",
95726
- selfAlignY: "start",
95727
- shrink: false,
95728
- children: jsx(Button, {
95729
- action: onClose,
95730
- icon: true,
95731
- border: "none",
95732
- style: {
95733
- ":hover": {
95734
- backgroundColor: "rgba(0, 0, 0, 0.1)"
95735
- }
95736
- },
95737
- children: jsx(Icon, {
95738
- children: jsx(CloseSvg, {})
95739
- })
95740
- })
95741
- })]
95742
- })
95743
- })
95744
- });
95745
- };
95746
- const MessageBoxPseudoClasses = [":-navi-status-info", ":-navi-status-success", ":-navi-status-warning", ":-navi-status-error"];
95747
- const MessageBoxStatusContext = createContext();
95748
- const MessageBoxReportTitleChildContext = createContext();
95749
-
95750
- installImportMetaCssBuild(import.meta);
95751
- const css$9 = /* css */`@layer navi;
96040
+ const css$8 = /* css */`@layer navi;
95752
96041
 
95753
96042
  .navi_quantity {
95754
96043
  flex-direction: column;
@@ -95849,7 +96138,7 @@ const Quantity = ({
95849
96138
  bold = true,
95850
96139
  ...props
95851
96140
  }) => {
95852
- import.meta.css = [css$9, "@jsenv/navi/src/text/quantity.jsx"];
96141
+ import.meta.css = [css$8, "@jsenv/navi/src/text/quantity.jsx"];
95853
96142
  const value = parseQuantityValue(children);
95854
96143
  const valueRounded = integer && typeof value === "number" ? Math.round(value) : value;
95855
96144
  const valueFormatted = typeof valueRounded === "number" ? formatNumber(valueRounded, {
@@ -95908,7 +96197,7 @@ const parseQuantityValue = children => {
95908
96197
  };
95909
96198
 
95910
96199
  installImportMetaCssBuild(import.meta);
95911
- const css$8 = /* css */`@layer navi {
96200
+ const css$7 = /* css */`@layer navi {
95912
96201
  .navi_meter {
95913
96202
  --loader-color: var(--navi-loader-color);
95914
96203
  --track-color: #efefef;
@@ -96047,7 +96336,7 @@ const Meter = ({
96047
96336
  style,
96048
96337
  ...rest
96049
96338
  }) => {
96050
- import.meta.css = [css$8, "@jsenv/navi/src/text/meter.jsx"];
96339
+ import.meta.css = [css$7, "@jsenv/navi/src/text/meter.jsx"];
96051
96340
  const defaultRef = useRef();
96052
96341
  const ref = rest.ref || defaultRef;
96053
96342
  value = Number(value);
@@ -96162,7 +96451,7 @@ const Paragraph = props => {
96162
96451
  };
96163
96452
 
96164
96453
  installImportMetaCssBuild(import.meta);
96165
- const css$7 = /* css */`.navi_text_box {
96454
+ const css$6 = /* css */`.navi_text_box {
96166
96455
  align-items: flex-start;
96167
96456
  min-width: 0;
96168
96457
  }
@@ -96204,7 +96493,7 @@ const TextBox = ({
96204
96493
  children,
96205
96494
  ...rest
96206
96495
  }) => {
96207
- import.meta.css = [css$7, "@jsenv/navi/src/text/text_box.jsx"];
96496
+ import.meta.css = [css$6, "@jsenv/navi/src/text/text_box.jsx"];
96208
96497
  const boxRef = useRef(null);
96209
96498
  const contentRef = useRef(null);
96210
96499
  useLayoutEffect(() => {
@@ -96250,40 +96539,6 @@ const adjustWidth = (boxEl, contentEl) => {
96250
96539
  contentEl.style.width = `${Math.ceil(optimalWidth)}px`;
96251
96540
  };
96252
96541
 
96253
- installImportMetaCssBuild(import.meta);
96254
- const css$6 = /* css */`.navi_message_box {
96255
- & .navi_title {
96256
- margin-top: 0;
96257
- margin-bottom: var(--navi-s);
96258
- color: var(--x-message-color);
96259
- }
96260
- }
96261
- `;
96262
- const Title = props => {
96263
- import.meta.css = [css$6, "@jsenv/navi/src/text/title.jsx"];
96264
- const messageBoxStatus = useContext(MessageBoxStatusContext);
96265
- const innerAs = props.as || (messageBoxStatus ? "h4" : "h1");
96266
- const titleLevel = parseInt(innerAs.slice(1));
96267
- const reportTitleToMessageBox = useContext(MessageBoxReportTitleChildContext);
96268
- reportTitleToMessageBox?.(true);
96269
- return jsx(TitleLevelContext.Provider, {
96270
- value: titleLevel,
96271
- children: jsx(Text, {
96272
- bold: true,
96273
- className: withPropsClassName("navi_title"),
96274
- as: messageBoxStatus ? "h4" : "h1",
96275
- ...props,
96276
- pseudoClasses: TitlePseudoClasses,
96277
- children: props.children
96278
- })
96279
- });
96280
- };
96281
- const TitleLevelContext = createContext();
96282
- const useTitleLevel = () => {
96283
- return useContext(TitleLevelContext);
96284
- };
96285
- const TitlePseudoClasses = [":hover"];
96286
-
96287
96542
  installImportMetaCssBuild(import.meta);
96288
96543
  const css$5 = /* css */`@keyframes navi_image_shimmer {
96289
96544
  0% {