@jsenv/navi 0.29.486 → 0.29.487

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.
@@ -5689,7 +5689,13 @@ const MessagePropsRefContext = createContext();
5689
5689
  const ControlIdContext = createContext();
5690
5690
  const ControlNameContext = createContext();
5691
5691
  const DisabledContext = createContext();
5692
+ // Truthy when what is above is read-only, and "network-policy" when that is a
5693
+ // write held by the network policy: what it holds is held in turn, and says
5694
+ // so in the policy's words (see readOnlyHandedDown in control_hooks.jsx).
5692
5695
  const ReadOnlyContext = createContext();
5696
+ // The words that go with it, when what handed read-only down gave some (its
5697
+ // readOnlyMessage, or the ones it was handed in turn).
5698
+ const ReadOnlyMessageContext = createContext();
5693
5699
  const RequiredContext = createContext();
5694
5700
  const LoadingContext$1 = createContext();
5695
5701
  createContext();
@@ -18324,8 +18330,9 @@ const useOpenControllerByProps = (props, name) => {
18324
18330
  * opened it, so a section written in a display font gets its tooltips in that
18325
18331
  * font. `color`, `font-size` and `font-weight` are absent too — each surface
18326
18332
  * answers those its own way (a popup and a callout write in
18327
- * --navi-popup-color; a popup follows the size of what opened it, a callout
18328
- * resets to the document's size).
18333
+ * --navi-popup-color; a popup follows the size of what opened it — for a
18334
+ * picker's popup, what is around the picker, see .navi_picker_content — a
18335
+ * callout resets to the document's size).
18329
18336
  *
18330
18337
  * In `@layer navi` so an app that wants one of these back says so on the
18331
18338
  * surface and wins, without having to out-specify anything.
@@ -22015,6 +22022,13 @@ CONSTRAINT_ATTRIBUTE_SET.add("data-readonly");
22015
22022
  CONSTRAINT_ATTRIBUTE_SET.add("data-readonly-reason");
22016
22023
 
22017
22024
  const readOnlyMessage = (field) => {
22025
+ // Read-only from above, in the words of what made it so (a picker's or a
22026
+ // group's readOnlyMessage): what it holds says what it says — over the
22027
+ // network policy's words too, as that control's own words win over them on
22028
+ // itself.
22029
+ if (field.readOnlyMessageFromAbove) {
22030
+ return field.readOnlyMessageFromAbove;
22031
+ }
22018
22032
  // Read-only for a reason the control named itself, read off the reason
22019
22033
  // rather than off the surrounding state: a button read-only for its own
22020
22034
  // reasons, inside a form that happens to be unchanged, is not waiting for
@@ -48155,6 +48169,8 @@ const useUIFacadeStateController = (props, realUIStateController) => {
48155
48169
  s.controller.controlHostProps = realUIStateController.controlHostProps;
48156
48170
  s.controller.readOnlyWithoutRun =
48157
48171
  realUIStateController.readOnlyWithoutRun;
48172
+ s.controller.readOnlyMessageFromAbove =
48173
+ realUIStateController.readOnlyMessageFromAbove;
48158
48174
  realUIStateController.pushStateDownToFacadeChild =
48159
48175
  s.controller.pushStateDownToChild;
48160
48176
 
@@ -48578,6 +48594,7 @@ const ControlgroupChildrenWrapper = ({
48578
48594
  required,
48579
48595
  disabled,
48580
48596
  readOnly,
48597
+ readOnlyMessage,
48581
48598
  loading,
48582
48599
  boundAction,
48583
48600
  actionRequesterSignal
@@ -48591,8 +48608,9 @@ const ControlgroupChildrenWrapper = ({
48591
48608
  value: name,
48592
48609
  children: jsx(DisabledContext.Provider, {
48593
48610
  value: disabled,
48594
- children: jsx(ReadOnlyContext.Provider, {
48595
- value: readOnly,
48611
+ children: jsx(ReadOnlyProvider, {
48612
+ readOnly: readOnly,
48613
+ readOnlyMessage: readOnlyMessage,
48596
48614
  children: jsx(RequiredContext.Provider, {
48597
48615
  value: required,
48598
48616
  children: jsx(LoadingContext$1.Provider, {
@@ -48612,6 +48630,34 @@ const ControlgroupChildrenWrapper = ({
48612
48630
  })
48613
48631
  })
48614
48632
  });
48633
+ // What a control hands to what it holds about being read-only: the state, and
48634
+ // the words it is said in. What it holds writes into it, so it is held for the
48635
+ // same reason and says what the control says. Held by the network policy, it
48636
+ // hands down the hold itself — read-only alone, what it holds would refuse in
48637
+ // navi's generic words. Read-only, it hands down its readOnlyMessage, or the
48638
+ // one it was handed.
48639
+ const readOnlyHandedDown = (uiStateController, readOnly) => {
48640
+ const {
48641
+ controlHostProps,
48642
+ props
48643
+ } = uiStateController;
48644
+ const readOnlyResolved = controlHostProps["data-readonly-reason"] === "network-policy" ? "network-policy" : readOnly;
48645
+ if (!readOnlyResolved) {
48646
+ return [readOnlyResolved, undefined];
48647
+ }
48648
+ return [readOnlyResolved, props.readOnlyMessage ?? uiStateController.readOnlyMessageFromAbove];
48649
+ };
48650
+ const ReadOnlyProvider = ({
48651
+ readOnly,
48652
+ readOnlyMessage,
48653
+ children
48654
+ }) => jsx(ReadOnlyContext.Provider, {
48655
+ value: readOnly,
48656
+ children: jsx(ReadOnlyMessageContext.Provider, {
48657
+ value: readOnlyMessage,
48658
+ children: children
48659
+ })
48660
+ });
48615
48661
 
48616
48662
  // What the caller writes for these is called from the control's own reactions
48617
48663
  // (see `reactions` below), so the split leaves them out of both sides.
@@ -49862,7 +49908,7 @@ const useControlgroupProps = (props, {
49862
49908
  basePseudoState
49863
49909
  } = controlgroupProps;
49864
49910
  const disabled = basePseudoState[":disabled"];
49865
- const readOnly = basePseudoState[":read-only"];
49911
+ const [readOnly, readOnlyMessage] = readOnlyHandedDown(uiGroupStateController, basePseudoState[":read-only"]);
49866
49912
  const loading = basePseudoState[":-navi-loading"];
49867
49913
  const controlgroupChildrenWrapperProps = useMemo(() => ({
49868
49914
  uiGroupStateController,
@@ -49870,10 +49916,11 @@ const useControlgroupProps = (props, {
49870
49916
  required: controlgroupProps.required,
49871
49917
  disabled,
49872
49918
  readOnly,
49919
+ readOnlyMessage,
49873
49920
  loading,
49874
49921
  boundAction,
49875
49922
  actionRequesterSignal
49876
- }), [uiGroupStateController, controlgroupProps.name, controlgroupProps.required, disabled, readOnly, loading, boundAction, actionRequesterSignal]);
49923
+ }), [uiGroupStateController, controlgroupProps.name, controlgroupProps.required, disabled, readOnly, readOnlyMessage, loading, boundAction, actionRequesterSignal]);
49877
49924
  return [controlRootProps, {
49878
49925
  ...controlgroupProps,
49879
49926
  "name": undefined,
@@ -50069,6 +50116,7 @@ const useInteractiveProps = (props, {
50069
50116
  {
50070
50117
  const controlDisabledFromAbove = useContext(DisabledContext);
50071
50118
  const controlReadOnlyFromAbove = useContext(ReadOnlyContext);
50119
+ const controlReadOnlyMessageFromAbove = useContext(ReadOnlyMessageContext);
50072
50120
  const controlRequired = useContext(RequiredContext);
50073
50121
  const controlLoadingFromAbove = useContext(LoadingContext$1);
50074
50122
  // A SIGNAL holding the element whose press the group's action is wearing
@@ -50108,8 +50156,15 @@ const useInteractiveProps = (props, {
50108
50156
  // pressable on a game nobody may edit. Its own props still hold; only what
50109
50157
  // came from above is dropped (see self_interactions.js for the three modes).
50110
50158
  const zoneStateApplies = !selfInteractionsIgnoreBlock(props);
50159
+ // A press that only leaves the popup it sits in writes nothing to the zone
50160
+ // around it either, so that zone being read-only — or holding a write the
50161
+ // network policy keeps — does not hold it: the popup answers the close
50162
+ // itself. A read-only picker's popup then closes from its own button as
50163
+ // it does from a press outside, and refuses both alike when it holds a
50164
+ // change it cannot send.
50165
+ const zoneReadOnlyApplies = zoneStateApplies && !pressOnlyLeaves(props);
50111
50166
  const controlDisabled = zoneStateApplies && controlDisabledFromAbove;
50112
- const controlReadOnly = zoneStateApplies && controlReadOnlyFromAbove;
50167
+ const controlReadOnly = zoneReadOnlyApplies && controlReadOnlyFromAbove;
50113
50168
  const controlLoading = zoneStateApplies && controlLoadingFromAbove;
50114
50169
  const disabledResolved = disabled || controlDisabled;
50115
50170
  const requiredResolved = required || controlRequired;
@@ -50124,10 +50179,11 @@ const useInteractiveProps = (props, {
50124
50179
  // but cannot be taken.
50125
50180
  const readOnlyFromParentMaxLengthGuard = Boolean(zoneStateApplies && uiStateController.parentUIStateController?.isChildBlockedByMaxLengthGuard?.(uiStateController));
50126
50181
  // A write cannot leave under a network policy, so the control asking for
50127
- // one — or sitting in a form that does — refuses before the press and says
50128
- // why (see network_policy.js): a press that looks accepted and fails after
50129
- // is what the policy exists to avoid.
50130
- const heldByNetworkPolicy = networkPolicyReason !== null && (isWriteAction(boundAction) || Boolean(zoneStateApplies && parentAction && isWriteAction(parentAction)));
50182
+ // one — sitting in a form that does, or in what a held control holds (a
50183
+ // picker's popup) — refuses before the press and says why (see
50184
+ // network_policy.js): a press that looks accepted and fails after is what
50185
+ // the policy exists to avoid.
50186
+ const heldByNetworkPolicy = networkPolicyReason !== null && (isWriteAction(boundAction) || Boolean(zoneReadOnlyApplies && parentAction && isWriteAction(parentAction)) || controlReadOnly === "network-policy");
50131
50187
  // Held back because the surface above already has as many runs in flight as
50132
50188
  // it allows (see parallel_guard.js). Only a control that would START one:
50133
50189
  // anything without an action of its own runs nothing and has nothing to
@@ -50146,6 +50202,10 @@ const useInteractiveProps = (props, {
50146
50202
  // still read-only in the tick the run settles — the tick a form's
50147
50203
  // after-send `--navi-reset` asks the form to empty itself.
50148
50204
  uiStateController.readOnlyWithoutRun = Boolean(readOnlyBase);
50205
+ // The words of what made this control read-only from above, when it gave
50206
+ // some: read by READONLY_CONSTRAINT, and handed further down with the
50207
+ // read-only state (see readOnlyHandedDown).
50208
+ uiStateController.readOnlyMessageFromAbove = controlReadOnly ? controlReadOnlyMessageFromAbove : undefined;
50149
50209
  // Read-only, and what this control opens still opens: reading what is in
50150
50210
  // there changes nothing. Read by READONLY_CONSTRAINT, which lets an
50151
50211
  // interaction that only reads through on it.
@@ -50680,6 +50740,12 @@ const splitControlProps = (props, {
50680
50740
  }
50681
50741
  return [controlRootProps, controlHostProps];
50682
50742
  };
50743
+ // What a press leaving a popup asks for: closing it, or cancelling it. Only on
50744
+ // a control running nothing of its own — one with an action writes, wherever
50745
+ // it then leads.
50746
+ const LEAVING_COMMAND_SET = new Set(["--navi-close", "--navi-cancel"]);
50747
+ const pressOnlyLeaves = props => LEAVING_COMMAND_SET.has(props.command) && !props.action;
50748
+
50683
50749
  // `onPointerDown`, `onFocusOut`… but not navi's own events (`onnavi_command`),
50684
50750
  // which keep the element they are dispatched on.
50685
50751
  const isDomListenerProp = key => /^on[A-Z]/.test(key);
@@ -59256,7 +59322,7 @@ const css$S = /* css */`@layer navi {
59256
59322
  --button-color: currentColor;
59257
59323
  --button-cursor: pointer;
59258
59324
  --button-font-size: var(--navi-control-font-size);
59259
- --button-font-family: var(--navi-control-font-family, inherit);
59325
+ --button-font-family: var(--navi-button-font-family, var(--navi-control-font-family, inherit));
59260
59326
  --button-border-color-hover: color-mix(in srgb,
59261
59327
  var(--button-border-color) 70%,
59262
59328
  black);
@@ -61001,14 +61067,16 @@ const css$O = /* css */`@layer navi {
61001
61067
  --checkmark-color-disabled: #eee;
61002
61068
  --border-color-disabled-checked: #d3d3d3;
61003
61069
  --background-color-disabled-checked: #d3d3d3;
61004
- --button-border-color: light-dark(#767676, #8e8e93);
61005
- --button-background-color: light-dark(#f3f4f6, #2d3748);
61070
+ --button-border-color: var(--navi-control-border-color);
61071
+ --button-background-color: var(--navi-button-background-color);
61006
61072
  --button-border-color-hover: color-mix(in srgb,
61007
61073
  var(--button-border-color) 70%,
61008
61074
  black);
61009
61075
  --button-background-color-hover: color-mix(in srgb,
61010
61076
  var(--button-background-color) 95%,
61011
61077
  black);
61078
+ --button-background-color-readonly: var(--button-background-color);
61079
+ --button-background-color-disabled: var(--button-background-color-readonly);
61012
61080
  }
61013
61081
 
61014
61082
  .navi_checkbox[data-variant="switch"] {
@@ -61036,19 +61104,40 @@ const css$O = /* css */`@layer navi {
61036
61104
  --margin: 0;
61037
61105
  --width: auto;
61038
61106
  --height: auto;
61107
+ --font-family: var(--navi-button-font-family, var(--navi-control-font-family, inherit));
61039
61108
  --border-color: var(--button-border-color);
61040
61109
  --border-color-hover: var(--button-border-color-hover);
61041
61110
  --background-color: var(--button-background-color);
61042
61111
  --background-color-hover: var(--button-background-color-hover);
61043
61112
  --background-color-readonly: var(--button-background-color-readonly);
61044
61113
  --background-color-disabled: var(--button-background-color-disabled);
61114
+ }
61115
+
61116
+ .navi_checkbox[data-variant="button"]:not([data-label]) {
61117
+ --button-padding-x-default: 0;
61118
+ --button-padding-y-default: 0;
61045
61119
  --border-color-checked: var(--button-border-color);
61120
+ --border-color-hover-checked: var(--button-border-color-hover);
61046
61121
  --background-color-checked: var(--button-background-color);
61122
+ --background-color-hover-checked: var(--button-background-color-hover);
61123
+ --checkmark-color: currentColor;
61047
61124
  }
61048
61125
 
61049
61126
  .navi_checkbox[data-variant="button"][data-label] {
61050
- --border-color-checked: var(--accent-color);
61051
- --background-color-checked: var(--accent-color);
61127
+ --border-radius: var(--navi-control-border-radius);
61128
+ --button-padding-x-default: var(--navi-button-padding-x-default);
61129
+ --button-padding-y-default: var(--navi-button-padding-y-default);
61130
+ line-height: var(--navi-control-line-height);
61131
+ }
61132
+
61133
+ .navi_checkbox[data-variant="button"][data-button-variant="discrete"] {
61134
+ --border-width: 0;
61135
+ --button-border-color: transparent;
61136
+ --button-border-color-hover: var(--button-border-color);
61137
+ --button-background-color: transparent;
61138
+ --button-background-color-hover: color-mix(in srgb,
61139
+ currentColor 8%,
61140
+ var(--button-background-color));
61052
61141
  }
61053
61142
  }
61054
61143
 
@@ -61156,23 +61245,24 @@ const css$O = /* css */`@layer navi {
61156
61245
  --color-mix: var(--color-mix-light);
61157
61246
  }
61158
61247
 
61248
+ & .navi_checkbox_marker {
61249
+ opacity: 0;
61250
+ stroke: var(--x-checkmark-color);
61251
+ transform: scale(.5);
61252
+ }
61253
+
61254
+ &[data-checked] .navi_checkbox_marker {
61255
+ opacity: 1;
61256
+ transition-property: opacity, transform;
61257
+ transition-duration: .15s;
61258
+ transition-timing-function: ease;
61259
+ transform: scale(1);
61260
+ }
61261
+
61159
61262
  &[data-variant="checkbox"] {
61160
61263
  & .navi_checkbox_marker {
61161
- opacity: 0;
61162
61264
  width: 100%;
61163
61265
  height: 100%;
61164
- stroke: var(--x-checkmark-color);
61165
- transform: scale(.5);
61166
- }
61167
-
61168
- &[data-checked] {
61169
- & .navi_checkbox_marker {
61170
- opacity: 1;
61171
- transition-property: opacity, transform;
61172
- transition-duration: .15s;
61173
- transition-timing-function: ease;
61174
- transform: scale(1);
61175
- }
61176
61266
  }
61177
61267
 
61178
61268
  &[data-accent-very-light] {
@@ -61211,6 +61301,11 @@ const css$O = /* css */`@layer navi {
61211
61301
  justify-content: center;
61212
61302
  align-items: center;
61213
61303
 
61304
+ & .navi_checkbox_marker {
61305
+ width: 1em;
61306
+ height: 1em;
61307
+ }
61308
+
61214
61309
  &[data-label] {
61215
61310
  &[data-checked] .navi_checkbox_label {
61216
61311
  color: var(--x-checkmark-color);
@@ -61270,7 +61365,8 @@ const InputCheckboxFieldInterface = props => {
61270
61365
  const {
61271
61366
  icon,
61272
61367
  switch: switchProp,
61273
- variant = icon ? "icon" : switchProp ? "switch" : "checkbox",
61368
+ buttonVariant,
61369
+ variant = buttonVariant ? "button" : icon ? "icon" : switchProp ? "switch" : "checkbox",
61274
61370
  // "checkbox", "switch", "icon", "button"
61275
61371
  accentColor
61276
61372
  } = props;
@@ -61325,7 +61421,9 @@ const InputCheckboxFieldInterface = props => {
61325
61421
  variant: undefined,
61326
61422
  switch: undefined,
61327
61423
  icon: undefined,
61424
+ buttonVariant: undefined,
61328
61425
  "data-variant": variant,
61426
+ "data-button-variant": variant === "button" ? buttonVariant : undefined,
61329
61427
  "data-label": hasLabel ? "" : undefined,
61330
61428
  baseClassName: "navi_checkbox",
61331
61429
  pseudoStateSelector: ".navi_control_input",
@@ -61799,6 +61897,9 @@ const css$M = /* css */`@layer navi {
61799
61897
  --radiomark-color-disabled: #d3d3d3;
61800
61898
  --border-color-checked-disabled: #d3d3d3;
61801
61899
  --background-color-disabled-checked: var(--background-color);
61900
+ --button-padding-x-default: 0;
61901
+ --button-padding-y-default: 0;
61902
+ --button-border-radius: var(--navi-control-border-radius);
61802
61903
  --button-border-width: 1px;
61803
61904
  --button-border-color: transparent;
61804
61905
  --button-background-color: transparent;
@@ -64023,6 +64124,7 @@ const InputPseudoElements = ["::-navi-loader"];
64023
64124
  * boolean?: boolean,
64024
64125
  * selectOnPress?: boolean,
64025
64126
  * variant?: "checkbox" | "switch" | "icon" | "button",
64127
+ * buttonVariant?: "discrete",
64026
64128
  * actionVerb?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
64027
64129
  * [key: string]: any,
64028
64130
  * }>}
@@ -64038,6 +64140,10 @@ const InputPseudoElements = ["::-navi-loader"];
64038
64140
  * is a square around its checkmark (or its `icon`, which takes an
64039
64141
  * `[off, on]` pair). Bound with `signal`, it follows whatever else writes
64040
64142
  * that signal.
64143
+ * @param {"discrete"} [buttonVariant] A checkbox drawn as a button, drawn as
64144
+ * that variant of `Button`: `"discrete"` has no box at rest and a wash on
64145
+ * hover (checked, its words still fill with the accent). Implies
64146
+ * `variant="button"`.
64041
64147
  * @param {"GET"|"POST"|"PUT"|"PATCH"|"DELETE"} [actionVerb] What a plain
64042
64148
  * function `action` does to the server. A write verb puts the field under
64043
64149
  * the network policy: held read-only up front, with the policy's message,
@@ -73366,6 +73472,10 @@ const UncontrolledDialog = props => {
73366
73472
  // needs the dialog element to look at what it contains.
73367
73473
  const defaultRef = useRef();
73368
73474
  props.ref = props.ref || defaultRef;
73475
+ // The close is built once per opening (see useOpenController below): read
73476
+ // through a ref, an onClose given by a render while open is the one called.
73477
+ const onCloseRef = useRef(null);
73478
+ onCloseRef.current = props.onClose;
73369
73479
  const openController = useOpenController(openEvent => {
73370
73480
  const dialogEl = props.ref.current;
73371
73481
  debugPopup(openEvent, `dialog opened`);
@@ -73393,7 +73503,7 @@ const UncontrolledDialog = props => {
73393
73503
  requestCloseEvent.preventDefault();
73394
73504
  },
73395
73505
  onClose: closeEvent => {
73396
- props.onClose?.(closeEvent);
73506
+ onCloseRef.current?.(closeEvent);
73397
73507
  }
73398
73508
  };
73399
73509
  });
@@ -75340,6 +75450,9 @@ const UncontrolledPopover = props => {
75340
75450
  // the popover element to read what it holds.
75341
75451
  const defaultRef = useRef();
75342
75452
  props.ref = props.ref || defaultRef;
75453
+ // Read at the close, not captured at the opening: see UncontrolledDialog.
75454
+ const onCloseRef = useRef(null);
75455
+ onCloseRef.current = props.onClose;
75343
75456
  // Same as Dialog's own UncontrolledDialog, for the same reasons — see it for
75344
75457
  // the full story: a popover is a surface, so the only thing it has to say
75345
75458
  // about closing is that what it contains must not be interrupted mid-action.
@@ -75365,7 +75478,7 @@ const UncontrolledPopover = props => {
75365
75478
  requestCloseEvent.preventDefault();
75366
75479
  },
75367
75480
  onClose: closeEvent => {
75368
- props.onClose?.(closeEvent);
75481
+ onCloseRef.current?.(closeEvent);
75369
75482
  }
75370
75483
  };
75371
75484
  });
@@ -77225,6 +77338,15 @@ const PickerCustom = props => {
77225
77338
  // closed (see onClose below) — a press that ran before the close would make
77226
77339
  // the picker busy, and a busy picker refuses the very close that follows.
77227
77340
  const confirmEventRef = useRef(null);
77341
+ // The close is built once per opening (see useOpenController below), but
77342
+ // these props answer how it ends: read through a ref, a render while open
77343
+ // (a sheet turned into the next question) is heard by the close.
77344
+ const closePropsRef = useRef(null);
77345
+ closePropsRef.current = {
77346
+ canSendWhileUnchanged,
77347
+ onConfirm,
77348
+ onClose
77349
+ };
77228
77350
  // aria-controls + id
77229
77351
  const popupId = `${props.id}_picker_popup`;
77230
77352
  {
@@ -77325,7 +77447,7 @@ const PickerCustom = props => {
77325
77447
  // and for that picker the same value said again means something.
77326
77448
  // A press outside, Escape, the cross say nothing and keep skipping.
77327
77449
  const said = requestCloseEvent.detail.isSend || confirmEventRef.current;
77328
- if (!(canSendWhileUnchanged && said)) {
77450
+ if (!(closePropsRef.current.canSendWhileUnchanged && said)) {
77329
77451
  // No action to run, but still allow the close.
77330
77452
  return;
77331
77453
  }
@@ -77397,7 +77519,7 @@ const PickerCustom = props => {
77397
77519
  const confirmEvent = confirmEventRef.current;
77398
77520
  confirmEventRef.current = null;
77399
77521
  if (confirmEvent && !closeEvent.detail.isCancel) {
77400
- onConfirm?.(confirmEvent);
77522
+ closePropsRef.current.onConfirm?.(confirmEvent);
77401
77523
  }
77402
77524
  // After the value bookkeeping above — whoever listens reads the
77403
77525
  // picker's value as it ends up, committed or restored on a cancel —
@@ -77408,7 +77530,7 @@ const PickerCustom = props => {
77408
77530
  // spells into the address — a route param naming what was being
77409
77531
  // edited, cleared now that nothing is — has to be written before,
77410
77532
  // or the landing puts it back.
77411
- onClose?.(closeEvent);
77533
+ closePropsRef.current.onClose?.(closeEvent);
77412
77534
  leaveExpanded({
77413
77535
  isBack: closeEvent.detail.isCancel
77414
77536
  });
@@ -77498,6 +77620,11 @@ const PickerCustom = props => {
77498
77620
  // button) hangs its popup off the whole control instead.
77499
77621
  anchor: props.anchor || props.ref,
77500
77622
  openController,
77623
+ // Before the content is built and shown, so it is laid out and placed
77624
+ // at the size it is read at (see .navi_picker_content in picker.jsx).
77625
+ onOpen: () => {
77626
+ writeContainerFont(ref.current, popupRef.current);
77627
+ },
77501
77628
  // A picker whose value was never given to it reads it off the control in
77502
77629
  // its popup (see useUIFacadeStateController), so that control is built as
77503
77630
  // soon as something reads the value: at mount when the picker sees the
@@ -78322,6 +78449,30 @@ const showNativePicker = (pickerInput, event) => {
78322
78449
  }
78323
78450
  };
78324
78451
 
78452
+ // The popup of a picker is opened where the picker stands, so it writes at the
78453
+ // size and weight of what is around it — the ones a Dialog rendered there
78454
+ // inherits — and never at the ones the trigger is drawn at. CSS cannot reach
78455
+ // past the picker's own: its root holds the popup and sets both. Written on the
78456
+ // holder only when they differ, so an opening that changes nothing invalidates
78457
+ // no style.
78458
+ const writeContainerFont = (pickerEl, popupEl) => {
78459
+ const container = pickerEl?.parentElement;
78460
+ const holder = popupEl?.closest("[data-picker-content]");
78461
+ if (!container || !holder) {
78462
+ return;
78463
+ }
78464
+ const {
78465
+ fontSize,
78466
+ fontWeight
78467
+ } = getComputedStyle(container);
78468
+ if (holder.style.fontSize !== fontSize) {
78469
+ holder.style.fontSize = fontSize;
78470
+ }
78471
+ if (holder.style.fontWeight !== fontWeight) {
78472
+ holder.style.fontWeight = fontWeight;
78473
+ }
78474
+ };
78475
+
78325
78476
  installImportMetaCssBuild(import.meta);
78326
78477
  const css$y = /* css */`.navi_picker_confirm_body {
78327
78478
  min-width: 180px;
@@ -80289,11 +80440,16 @@ const ListItemSelectable = props => {
80289
80440
  // A list waiting on something is not unavailable, it is not answering yet —
80290
80441
  // busy rather than read-only (see BUSY_CONSTRAINT, which is asked first),
80291
80442
  // and the wait is about the same thing the read-only would be about.
80443
+ //
80444
+ // Unless what made the list read-only says it better: in words of its own (a
80445
+ // picker's or a list's readOnlyMessage), or because it is the network
80446
+ // policy holding the write the selection would make.
80292
80447
  const readOnlyFromAbove = useContext(ReadOnlyContext);
80448
+ const readOnlyMessageFromAbove = useContext(ReadOnlyMessageContext);
80293
80449
  const readOnlyMessageKey = readOnlyFromAbove ? multiple ? `constraint.readonly.selection` : `constraint.readonly.choice` : `constraint.readonly.option`;
80294
80450
  // Handed to the row as well as to the control: whichever of the two catches
80295
80451
  // the press answers it, and they must not answer it differently.
80296
- const readOnlyMessageResolved = props.readOnlyMessage ?? constraintI18n(readOnlyMessageKey, props);
80452
+ const readOnlyMessageResolved = props.readOnlyMessage ?? (readOnlyFromAbove ? readOnlyMessageFromAbove : undefined) ?? (readOnlyFromAbove === "network-policy" ? getNetworkPolicyReadOnlyMessage() : constraintI18n(readOnlyMessageKey, props));
80297
80453
  const busyMessageResolved = props.busyMessage ?? (loading ? listItemBusyMessage(loading, props) : constraintI18n(multiple ? `constraint.busy.selection` : `constraint.busy.choice`, props));
80298
80454
  const inputRef = useRef();
80299
80455
  const inputType = multiple ? "checkbox" : "radio";
@@ -89086,6 +89242,7 @@ const css$r = /* css */`@layer navi {
89086
89242
  .navi_picker:is([data-variant="button"], [data-variant="border"], [data-variant="discrete-border"]) {
89087
89243
  --picker-padding-x-default: var(--navi-button-padding-x-default);
89088
89244
  --picker-padding-y-default: var(--navi-button-padding-y-default);
89245
+ --picker-font-family: var(--navi-button-font-family, var(--navi-control-font-family, inherit));
89089
89246
  --picker-align-x-default: center;
89090
89247
  --picker-background-color: var(--navi-button-background-color);
89091
89248
  --picker-border-color-readonly: color-mix(in srgb,
@@ -89385,8 +89542,9 @@ const css$r = /* css */`@layer navi {
89385
89542
  --x-corner-top-right-radius: initial;
89386
89543
  --x-corner-bottom-right-radius: initial;
89387
89544
  --x-corner-bottom-left-radius: initial;
89388
- font-size: var(--navi-control-font-size);
89545
+ font-weight: initial;
89389
89546
  text-align: initial;
89547
+ font-size: 1rem;
89390
89548
  display: contents;
89391
89549
  }
89392
89550
 
@@ -89629,9 +89787,10 @@ const PickerButton = props => {
89629
89787
  // The same chain useControlProps resolves (own prop first, then what the
89630
89788
  // group above says), for the two things it does not carry: the popup content,
89631
89789
  // which is read-only along with the picker, and the cursor, which stays a
89632
- // pointer on a picker that still opens.
89790
+ // pointer on a picker that still opens. A hold of the network policy goes
89791
+ // with it: what the popup changes is what the picker would send.
89633
89792
  const readOnlyFromAbove = useContext(ReadOnlyContext);
89634
- const readOnlyResolved = readOnly || readOnlyFromAbove;
89793
+ const [readOnlyResolved, readOnlyMessageResolved] = readOnlyHandedDown(uiStateController, readOnly || readOnlyFromAbove);
89635
89794
  // Read off the controller rather than worked out again: what makes a
89636
89795
  // read-only picker open is settled once, where the gate reads it (see
89637
89796
  // createControlInfo's readOnlyOpens). Needed here for the cursor.
@@ -89679,11 +89838,13 @@ const PickerButton = props => {
89679
89838
  /* Read-only crosses into everything the picker is made of: what it really
89680
89839
  holds is drawn by controls of their own — in the popup, and on the façade
89681
89840
  where an application puts its own affordances — and each of them refuses
89682
- in its own words once told. Said from the read-only state alone, never
89841
+ once told, in the picker's words when it gave some (readOnlyMessage),
89842
+ in its own otherwise. Said from the read-only state alone, never
89683
89843
  from the busy one — an action running for a moment is not the same thing
89684
89844
  as a value nobody may change. */
89685
- jsx(ReadOnlyContext.Provider, {
89686
- value: readOnlyResolved,
89845
+ jsx(ReadOnlyProvider, {
89846
+ readOnly: readOnlyResolved,
89847
+ readOnlyMessage: readOnlyMessageResolved,
89687
89848
  children: jsxs(Box
89688
89849
  // A word in a sentence (variant="text") sits in a <p>, where a <div> is
89689
89850
  // not allowed: phrasing content there, block content elsewhere.