@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.
@@ -5420,7 +5420,13 @@ const MessagePropsRefContext = createContext();
5420
5420
  const ControlIdContext = createContext();
5421
5421
  const ControlNameContext = createContext();
5422
5422
  const DisabledContext = createContext();
5423
+ // Truthy when what is above is read-only, and "network-policy" when that is a
5424
+ // write held by the network policy: what it holds is held in turn, and says
5425
+ // so in the policy's words (see readOnlyHandedDown in control_hooks.jsx).
5423
5426
  const ReadOnlyContext = createContext();
5427
+ // The words that go with it, when what handed read-only down gave some (its
5428
+ // readOnlyMessage, or the ones it was handed in turn).
5429
+ const ReadOnlyMessageContext = createContext();
5424
5430
  const RequiredContext = createContext();
5425
5431
  const LoadingContext$1 = createContext();
5426
5432
  createContext();
@@ -17538,8 +17544,9 @@ const useOpenControllerByProps = (props, name) => {
17538
17544
  * opened it, so a section written in a display font gets its tooltips in that
17539
17545
  * font. `color`, `font-size` and `font-weight` are absent too — each surface
17540
17546
  * answers those its own way (a popup and a callout write in
17541
- * --navi-popup-color; a popup follows the size of what opened it, a callout
17542
- * resets to the document's size).
17547
+ * --navi-popup-color; a popup follows the size of what opened it — for a
17548
+ * picker's popup, what is around the picker, see .navi_picker_content — a
17549
+ * callout resets to the document's size).
17543
17550
  *
17544
17551
  * In `@layer navi` so an app that wants one of these back says so on the
17545
17552
  * surface and wins, without having to out-specify anything.
@@ -21140,6 +21147,13 @@ CONSTRAINT_ATTRIBUTE_SET.add("data-readonly");
21140
21147
  CONSTRAINT_ATTRIBUTE_SET.add("data-readonly-reason");
21141
21148
 
21142
21149
  const readOnlyMessage = (field) => {
21150
+ // Read-only from above, in the words of what made it so (a picker's or a
21151
+ // group's readOnlyMessage): what it holds says what it says — over the
21152
+ // network policy's words too, as that control's own words win over them on
21153
+ // itself.
21154
+ if (field.readOnlyMessageFromAbove) {
21155
+ return field.readOnlyMessageFromAbove;
21156
+ }
21143
21157
  // Read-only for a reason the control named itself, read off the reason
21144
21158
  // rather than off the surrounding state: a button read-only for its own
21145
21159
  // reasons, inside a form that happens to be unchanged, is not waiting for
@@ -46798,6 +46812,8 @@ const useUIFacadeStateController = (props, realUIStateController) => {
46798
46812
  s.controller.controlHostProps = realUIStateController.controlHostProps;
46799
46813
  s.controller.readOnlyWithoutRun =
46800
46814
  realUIStateController.readOnlyWithoutRun;
46815
+ s.controller.readOnlyMessageFromAbove =
46816
+ realUIStateController.readOnlyMessageFromAbove;
46801
46817
  realUIStateController.pushStateDownToFacadeChild =
46802
46818
  s.controller.pushStateDownToChild;
46803
46819
 
@@ -47143,6 +47159,7 @@ const ControlgroupChildrenWrapper = ({
47143
47159
  required,
47144
47160
  disabled,
47145
47161
  readOnly,
47162
+ readOnlyMessage,
47146
47163
  loading,
47147
47164
  boundAction,
47148
47165
  actionRequesterSignal
@@ -47156,8 +47173,9 @@ const ControlgroupChildrenWrapper = ({
47156
47173
  value: name,
47157
47174
  children: jsx(DisabledContext.Provider, {
47158
47175
  value: disabled,
47159
- children: jsx(ReadOnlyContext.Provider, {
47160
- value: readOnly,
47176
+ children: jsx(ReadOnlyProvider, {
47177
+ readOnly: readOnly,
47178
+ readOnlyMessage: readOnlyMessage,
47161
47179
  children: jsx(RequiredContext.Provider, {
47162
47180
  value: required,
47163
47181
  children: jsx(LoadingContext$1.Provider, {
@@ -47177,6 +47195,34 @@ const ControlgroupChildrenWrapper = ({
47177
47195
  })
47178
47196
  })
47179
47197
  });
47198
+ // What a control hands to what it holds about being read-only: the state, and
47199
+ // the words it is said in. What it holds writes into it, so it is held for the
47200
+ // same reason and says what the control says. Held by the network policy, it
47201
+ // hands down the hold itself — read-only alone, what it holds would refuse in
47202
+ // navi's generic words. Read-only, it hands down its readOnlyMessage, or the
47203
+ // one it was handed.
47204
+ const readOnlyHandedDown = (uiStateController, readOnly) => {
47205
+ const {
47206
+ controlHostProps,
47207
+ props
47208
+ } = uiStateController;
47209
+ const readOnlyResolved = controlHostProps["data-readonly-reason"] === "network-policy" ? "network-policy" : readOnly;
47210
+ if (!readOnlyResolved) {
47211
+ return [readOnlyResolved, undefined];
47212
+ }
47213
+ return [readOnlyResolved, props.readOnlyMessage ?? uiStateController.readOnlyMessageFromAbove];
47214
+ };
47215
+ const ReadOnlyProvider = ({
47216
+ readOnly,
47217
+ readOnlyMessage,
47218
+ children
47219
+ }) => jsx(ReadOnlyContext.Provider, {
47220
+ value: readOnly,
47221
+ children: jsx(ReadOnlyMessageContext.Provider, {
47222
+ value: readOnlyMessage,
47223
+ children: children
47224
+ })
47225
+ });
47180
47226
 
47181
47227
  // What the caller writes for these is called from the control's own reactions
47182
47228
  // (see `reactions` below), so the split leaves them out of both sides.
@@ -48403,7 +48449,7 @@ const useControlgroupProps = (props, {
48403
48449
  basePseudoState
48404
48450
  } = controlgroupProps;
48405
48451
  const disabled = basePseudoState[":disabled"];
48406
- const readOnly = basePseudoState[":read-only"];
48452
+ const [readOnly, readOnlyMessage] = readOnlyHandedDown(uiGroupStateController, basePseudoState[":read-only"]);
48407
48453
  const loading = basePseudoState[":-navi-loading"];
48408
48454
  const controlgroupChildrenWrapperProps = useMemo(() => ({
48409
48455
  uiGroupStateController,
@@ -48411,10 +48457,11 @@ const useControlgroupProps = (props, {
48411
48457
  required: controlgroupProps.required,
48412
48458
  disabled,
48413
48459
  readOnly,
48460
+ readOnlyMessage,
48414
48461
  loading,
48415
48462
  boundAction,
48416
48463
  actionRequesterSignal
48417
- }), [uiGroupStateController, controlgroupProps.name, controlgroupProps.required, disabled, readOnly, loading, boundAction, actionRequesterSignal]);
48464
+ }), [uiGroupStateController, controlgroupProps.name, controlgroupProps.required, disabled, readOnly, readOnlyMessage, loading, boundAction, actionRequesterSignal]);
48418
48465
  return [controlRootProps, {
48419
48466
  ...controlgroupProps,
48420
48467
  "name": undefined,
@@ -48610,6 +48657,7 @@ const useInteractiveProps = (props, {
48610
48657
  {
48611
48658
  const controlDisabledFromAbove = useContext(DisabledContext);
48612
48659
  const controlReadOnlyFromAbove = useContext(ReadOnlyContext);
48660
+ const controlReadOnlyMessageFromAbove = useContext(ReadOnlyMessageContext);
48613
48661
  const controlRequired = useContext(RequiredContext);
48614
48662
  const controlLoadingFromAbove = useContext(LoadingContext$1);
48615
48663
  // A SIGNAL holding the element whose press the group's action is wearing
@@ -48649,8 +48697,15 @@ const useInteractiveProps = (props, {
48649
48697
  // pressable on a game nobody may edit. Its own props still hold; only what
48650
48698
  // came from above is dropped (see self_interactions.js for the three modes).
48651
48699
  const zoneStateApplies = !selfInteractionsIgnoreBlock(props);
48700
+ // A press that only leaves the popup it sits in writes nothing to the zone
48701
+ // around it either, so that zone being read-only — or holding a write the
48702
+ // network policy keeps — does not hold it: the popup answers the close
48703
+ // itself. A read-only picker's popup then closes from its own button as
48704
+ // it does from a press outside, and refuses both alike when it holds a
48705
+ // change it cannot send.
48706
+ const zoneReadOnlyApplies = zoneStateApplies && !pressOnlyLeaves(props);
48652
48707
  const controlDisabled = zoneStateApplies && controlDisabledFromAbove;
48653
- const controlReadOnly = zoneStateApplies && controlReadOnlyFromAbove;
48708
+ const controlReadOnly = zoneReadOnlyApplies && controlReadOnlyFromAbove;
48654
48709
  const controlLoading = zoneStateApplies && controlLoadingFromAbove;
48655
48710
  const disabledResolved = disabled || controlDisabled;
48656
48711
  const requiredResolved = required || controlRequired;
@@ -48665,10 +48720,11 @@ const useInteractiveProps = (props, {
48665
48720
  // but cannot be taken.
48666
48721
  const readOnlyFromParentMaxLengthGuard = Boolean(zoneStateApplies && uiStateController.parentUIStateController?.isChildBlockedByMaxLengthGuard?.(uiStateController));
48667
48722
  // A write cannot leave under a network policy, so the control asking for
48668
- // one — or sitting in a form that does — refuses before the press and says
48669
- // why (see network_policy.js): a press that looks accepted and fails after
48670
- // is what the policy exists to avoid.
48671
- const heldByNetworkPolicy = networkPolicyReason !== null && (isWriteAction(boundAction) || Boolean(zoneStateApplies && parentAction && isWriteAction(parentAction)));
48723
+ // one — sitting in a form that does, or in what a held control holds (a
48724
+ // picker's popup) — refuses before the press and says why (see
48725
+ // network_policy.js): a press that looks accepted and fails after is what
48726
+ // the policy exists to avoid.
48727
+ const heldByNetworkPolicy = networkPolicyReason !== null && (isWriteAction(boundAction) || Boolean(zoneReadOnlyApplies && parentAction && isWriteAction(parentAction)) || controlReadOnly === "network-policy");
48672
48728
  // Held back because the surface above already has as many runs in flight as
48673
48729
  // it allows (see parallel_guard.js). Only a control that would START one:
48674
48730
  // anything without an action of its own runs nothing and has nothing to
@@ -48687,6 +48743,10 @@ const useInteractiveProps = (props, {
48687
48743
  // still read-only in the tick the run settles — the tick a form's
48688
48744
  // after-send `--navi-reset` asks the form to empty itself.
48689
48745
  uiStateController.readOnlyWithoutRun = Boolean(readOnlyBase);
48746
+ // The words of what made this control read-only from above, when it gave
48747
+ // some: read by READONLY_CONSTRAINT, and handed further down with the
48748
+ // read-only state (see readOnlyHandedDown).
48749
+ uiStateController.readOnlyMessageFromAbove = controlReadOnly ? controlReadOnlyMessageFromAbove : undefined;
48690
48750
  // Read-only, and what this control opens still opens: reading what is in
48691
48751
  // there changes nothing. Read by READONLY_CONSTRAINT, which lets an
48692
48752
  // interaction that only reads through on it.
@@ -49219,6 +49279,12 @@ const splitControlProps = (props, {
49219
49279
  }
49220
49280
  return [controlRootProps, controlHostProps];
49221
49281
  };
49282
+ // What a press leaving a popup asks for: closing it, or cancelling it. Only on
49283
+ // a control running nothing of its own — one with an action writes, wherever
49284
+ // it then leads.
49285
+ const LEAVING_COMMAND_SET = new Set(["--navi-close", "--navi-cancel"]);
49286
+ const pressOnlyLeaves = props => LEAVING_COMMAND_SET.has(props.command) && !props.action;
49287
+
49222
49288
  // `onPointerDown`, `onFocusOut`… but not navi's own events (`onnavi_command`),
49223
49289
  // which keep the element they are dispatched on.
49224
49290
  const isDomListenerProp = key => /^on[A-Z]/.test(key);
@@ -57779,7 +57845,7 @@ const css$S = /* css */`@layer navi {
57779
57845
  --button-color: currentColor;
57780
57846
  --button-cursor: pointer;
57781
57847
  --button-font-size: var(--navi-control-font-size);
57782
- --button-font-family: var(--navi-control-font-family, inherit);
57848
+ --button-font-family: var(--navi-button-font-family, var(--navi-control-font-family, inherit));
57783
57849
  --button-border-color-hover: color-mix(in srgb,
57784
57850
  var(--button-border-color) 70%,
57785
57851
  black);
@@ -59507,14 +59573,16 @@ const css$O = /* css */`@layer navi {
59507
59573
  --checkmark-color-disabled: #eee;
59508
59574
  --border-color-disabled-checked: #d3d3d3;
59509
59575
  --background-color-disabled-checked: #d3d3d3;
59510
- --button-border-color: light-dark(#767676, #8e8e93);
59511
- --button-background-color: light-dark(#f3f4f6, #2d3748);
59576
+ --button-border-color: var(--navi-control-border-color);
59577
+ --button-background-color: var(--navi-button-background-color);
59512
59578
  --button-border-color-hover: color-mix(in srgb,
59513
59579
  var(--button-border-color) 70%,
59514
59580
  black);
59515
59581
  --button-background-color-hover: color-mix(in srgb,
59516
59582
  var(--button-background-color) 95%,
59517
59583
  black);
59584
+ --button-background-color-readonly: var(--button-background-color);
59585
+ --button-background-color-disabled: var(--button-background-color-readonly);
59518
59586
  }
59519
59587
 
59520
59588
  .navi_checkbox[data-variant="switch"] {
@@ -59542,19 +59610,40 @@ const css$O = /* css */`@layer navi {
59542
59610
  --margin: 0;
59543
59611
  --width: auto;
59544
59612
  --height: auto;
59613
+ --font-family: var(--navi-button-font-family, var(--navi-control-font-family, inherit));
59545
59614
  --border-color: var(--button-border-color);
59546
59615
  --border-color-hover: var(--button-border-color-hover);
59547
59616
  --background-color: var(--button-background-color);
59548
59617
  --background-color-hover: var(--button-background-color-hover);
59549
59618
  --background-color-readonly: var(--button-background-color-readonly);
59550
59619
  --background-color-disabled: var(--button-background-color-disabled);
59620
+ }
59621
+
59622
+ .navi_checkbox[data-variant="button"]:not([data-label]) {
59623
+ --button-padding-x-default: 0;
59624
+ --button-padding-y-default: 0;
59551
59625
  --border-color-checked: var(--button-border-color);
59626
+ --border-color-hover-checked: var(--button-border-color-hover);
59552
59627
  --background-color-checked: var(--button-background-color);
59628
+ --background-color-hover-checked: var(--button-background-color-hover);
59629
+ --checkmark-color: currentColor;
59553
59630
  }
59554
59631
 
59555
59632
  .navi_checkbox[data-variant="button"][data-label] {
59556
- --border-color-checked: var(--accent-color);
59557
- --background-color-checked: var(--accent-color);
59633
+ --border-radius: var(--navi-control-border-radius);
59634
+ --button-padding-x-default: var(--navi-button-padding-x-default);
59635
+ --button-padding-y-default: var(--navi-button-padding-y-default);
59636
+ line-height: var(--navi-control-line-height);
59637
+ }
59638
+
59639
+ .navi_checkbox[data-variant="button"][data-button-variant="discrete"] {
59640
+ --border-width: 0;
59641
+ --button-border-color: transparent;
59642
+ --button-border-color-hover: var(--button-border-color);
59643
+ --button-background-color: transparent;
59644
+ --button-background-color-hover: color-mix(in srgb,
59645
+ currentColor 8%,
59646
+ var(--button-background-color));
59558
59647
  }
59559
59648
  }
59560
59649
 
@@ -59662,23 +59751,24 @@ const css$O = /* css */`@layer navi {
59662
59751
  --color-mix: var(--color-mix-light);
59663
59752
  }
59664
59753
 
59754
+ & .navi_checkbox_marker {
59755
+ opacity: 0;
59756
+ stroke: var(--x-checkmark-color);
59757
+ transform: scale(.5);
59758
+ }
59759
+
59760
+ &[data-checked] .navi_checkbox_marker {
59761
+ opacity: 1;
59762
+ transition-property: opacity, transform;
59763
+ transition-duration: .15s;
59764
+ transition-timing-function: ease;
59765
+ transform: scale(1);
59766
+ }
59767
+
59665
59768
  &[data-variant="checkbox"] {
59666
59769
  & .navi_checkbox_marker {
59667
- opacity: 0;
59668
59770
  width: 100%;
59669
59771
  height: 100%;
59670
- stroke: var(--x-checkmark-color);
59671
- transform: scale(.5);
59672
- }
59673
-
59674
- &[data-checked] {
59675
- & .navi_checkbox_marker {
59676
- opacity: 1;
59677
- transition-property: opacity, transform;
59678
- transition-duration: .15s;
59679
- transition-timing-function: ease;
59680
- transform: scale(1);
59681
- }
59682
59772
  }
59683
59773
 
59684
59774
  &[data-accent-very-light] {
@@ -59717,6 +59807,11 @@ const css$O = /* css */`@layer navi {
59717
59807
  justify-content: center;
59718
59808
  align-items: center;
59719
59809
 
59810
+ & .navi_checkbox_marker {
59811
+ width: 1em;
59812
+ height: 1em;
59813
+ }
59814
+
59720
59815
  &[data-label] {
59721
59816
  &[data-checked] .navi_checkbox_label {
59722
59817
  color: var(--x-checkmark-color);
@@ -59765,7 +59860,8 @@ const InputCheckboxFieldInterface = props => {
59765
59860
  const {
59766
59861
  icon,
59767
59862
  switch: switchProp,
59768
- variant = icon ? "icon" : switchProp ? "switch" : "checkbox",
59863
+ buttonVariant,
59864
+ variant = buttonVariant ? "button" : icon ? "icon" : switchProp ? "switch" : "checkbox",
59769
59865
  // "checkbox", "switch", "icon", "button"
59770
59866
  accentColor
59771
59867
  } = props;
@@ -59820,7 +59916,9 @@ const InputCheckboxFieldInterface = props => {
59820
59916
  variant: undefined,
59821
59917
  switch: undefined,
59822
59918
  icon: undefined,
59919
+ buttonVariant: undefined,
59823
59920
  "data-variant": variant,
59921
+ "data-button-variant": variant === "button" ? buttonVariant : undefined,
59824
59922
  "data-label": hasLabel ? "" : undefined,
59825
59923
  baseClassName: "navi_checkbox",
59826
59924
  pseudoStateSelector: ".navi_control_input",
@@ -60294,6 +60392,9 @@ const css$M = /* css */`@layer navi {
60294
60392
  --radiomark-color-disabled: #d3d3d3;
60295
60393
  --border-color-checked-disabled: #d3d3d3;
60296
60394
  --background-color-disabled-checked: var(--background-color);
60395
+ --button-padding-x-default: 0;
60396
+ --button-padding-y-default: 0;
60397
+ --button-border-radius: var(--navi-control-border-radius);
60297
60398
  --button-border-width: 1px;
60298
60399
  --button-border-color: transparent;
60299
60400
  --button-background-color: transparent;
@@ -62518,6 +62619,7 @@ const InputPseudoElements = ["::-navi-loader"];
62518
62619
  * boolean?: boolean,
62519
62620
  * selectOnPress?: boolean,
62520
62621
  * variant?: "checkbox" | "switch" | "icon" | "button",
62622
+ * buttonVariant?: "discrete",
62521
62623
  * actionVerb?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
62522
62624
  * [key: string]: any,
62523
62625
  * }>}
@@ -62533,6 +62635,10 @@ const InputPseudoElements = ["::-navi-loader"];
62533
62635
  * is a square around its checkmark (or its `icon`, which takes an
62534
62636
  * `[off, on]` pair). Bound with `signal`, it follows whatever else writes
62535
62637
  * that signal.
62638
+ * @param {"discrete"} [buttonVariant] A checkbox drawn as a button, drawn as
62639
+ * that variant of `Button`: `"discrete"` has no box at rest and a wash on
62640
+ * hover (checked, its words still fill with the accent). Implies
62641
+ * `variant="button"`.
62536
62642
  * @param {"GET"|"POST"|"PUT"|"PATCH"|"DELETE"} [actionVerb] What a plain
62537
62643
  * function `action` does to the server. A write verb puts the field under
62538
62644
  * the network policy: held read-only up front, with the policy's message,
@@ -71770,6 +71876,10 @@ const UncontrolledDialog = props => {
71770
71876
  // needs the dialog element to look at what it contains.
71771
71877
  const defaultRef = useRef();
71772
71878
  props.ref = props.ref || defaultRef;
71879
+ // The close is built once per opening (see useOpenController below): read
71880
+ // through a ref, an onClose given by a render while open is the one called.
71881
+ const onCloseRef = useRef(null);
71882
+ onCloseRef.current = props.onClose;
71773
71883
  const openController = useOpenController(openEvent => {
71774
71884
  const dialogEl = props.ref.current;
71775
71885
  return {
@@ -71796,7 +71906,7 @@ const UncontrolledDialog = props => {
71796
71906
  requestCloseEvent.preventDefault();
71797
71907
  },
71798
71908
  onClose: closeEvent => {
71799
- props.onClose?.(closeEvent);
71909
+ onCloseRef.current?.(closeEvent);
71800
71910
  }
71801
71911
  };
71802
71912
  });
@@ -73655,6 +73765,9 @@ const UncontrolledPopover = props => {
73655
73765
  // the popover element to read what it holds.
73656
73766
  const defaultRef = useRef();
73657
73767
  props.ref = props.ref || defaultRef;
73768
+ // Read at the close, not captured at the opening: see UncontrolledDialog.
73769
+ const onCloseRef = useRef(null);
73770
+ onCloseRef.current = props.onClose;
73658
73771
  // Same as Dialog's own UncontrolledDialog, for the same reasons — see it for
73659
73772
  // the full story: a popover is a surface, so the only thing it has to say
73660
73773
  // about closing is that what it contains must not be interrupted mid-action.
@@ -73679,7 +73792,7 @@ const UncontrolledPopover = props => {
73679
73792
  requestCloseEvent.preventDefault();
73680
73793
  },
73681
73794
  onClose: closeEvent => {
73682
- props.onClose?.(closeEvent);
73795
+ onCloseRef.current?.(closeEvent);
73683
73796
  }
73684
73797
  };
73685
73798
  });
@@ -75529,6 +75642,15 @@ const PickerCustom = props => {
75529
75642
  // closed (see onClose below) — a press that ran before the close would make
75530
75643
  // the picker busy, and a busy picker refuses the very close that follows.
75531
75644
  const confirmEventRef = useRef(null);
75645
+ // The close is built once per opening (see useOpenController below), but
75646
+ // these props answer how it ends: read through a ref, a render while open
75647
+ // (a sheet turned into the next question) is heard by the close.
75648
+ const closePropsRef = useRef(null);
75649
+ closePropsRef.current = {
75650
+ canSendWhileUnchanged,
75651
+ onConfirm,
75652
+ onClose
75653
+ };
75532
75654
  // aria-controls + id
75533
75655
  const popupId = `${props.id}_picker_popup`;
75534
75656
  {
@@ -75627,7 +75749,7 @@ const PickerCustom = props => {
75627
75749
  // and for that picker the same value said again means something.
75628
75750
  // A press outside, Escape, the cross say nothing and keep skipping.
75629
75751
  const said = requestCloseEvent.detail.isSend || confirmEventRef.current;
75630
- if (!(canSendWhileUnchanged && said)) {
75752
+ if (!(closePropsRef.current.canSendWhileUnchanged && said)) {
75631
75753
  // No action to run, but still allow the close.
75632
75754
  return;
75633
75755
  }
@@ -75688,7 +75810,7 @@ const PickerCustom = props => {
75688
75810
  const confirmEvent = confirmEventRef.current;
75689
75811
  confirmEventRef.current = null;
75690
75812
  if (confirmEvent && !closeEvent.detail.isCancel) {
75691
- onConfirm?.(confirmEvent);
75813
+ closePropsRef.current.onConfirm?.(confirmEvent);
75692
75814
  }
75693
75815
  // After the value bookkeeping above — whoever listens reads the
75694
75816
  // picker's value as it ends up, committed or restored on a cancel —
@@ -75699,7 +75821,7 @@ const PickerCustom = props => {
75699
75821
  // spells into the address — a route param naming what was being
75700
75822
  // edited, cleared now that nothing is — has to be written before,
75701
75823
  // or the landing puts it back.
75702
- onClose?.(closeEvent);
75824
+ closePropsRef.current.onClose?.(closeEvent);
75703
75825
  leaveExpanded({
75704
75826
  isBack: closeEvent.detail.isCancel
75705
75827
  });
@@ -75789,6 +75911,11 @@ const PickerCustom = props => {
75789
75911
  // button) hangs its popup off the whole control instead.
75790
75912
  anchor: props.anchor || props.ref,
75791
75913
  openController,
75914
+ // Before the content is built and shown, so it is laid out and placed
75915
+ // at the size it is read at (see .navi_picker_content in picker.jsx).
75916
+ onOpen: () => {
75917
+ writeContainerFont(ref.current, popupRef.current);
75918
+ },
75792
75919
  // A picker whose value was never given to it reads it off the control in
75793
75920
  // its popup (see useUIFacadeStateController), so that control is built as
75794
75921
  // soon as something reads the value: at mount when the picker sees the
@@ -76612,6 +76739,30 @@ const showNativePicker = (pickerInput, event) => {
76612
76739
  }
76613
76740
  };
76614
76741
 
76742
+ // The popup of a picker is opened where the picker stands, so it writes at the
76743
+ // size and weight of what is around it — the ones a Dialog rendered there
76744
+ // inherits — and never at the ones the trigger is drawn at. CSS cannot reach
76745
+ // past the picker's own: its root holds the popup and sets both. Written on the
76746
+ // holder only when they differ, so an opening that changes nothing invalidates
76747
+ // no style.
76748
+ const writeContainerFont = (pickerEl, popupEl) => {
76749
+ const container = pickerEl?.parentElement;
76750
+ const holder = popupEl?.closest("[data-picker-content]");
76751
+ if (!container || !holder) {
76752
+ return;
76753
+ }
76754
+ const {
76755
+ fontSize,
76756
+ fontWeight
76757
+ } = getComputedStyle(container);
76758
+ if (holder.style.fontSize !== fontSize) {
76759
+ holder.style.fontSize = fontSize;
76760
+ }
76761
+ if (holder.style.fontWeight !== fontWeight) {
76762
+ holder.style.fontWeight = fontWeight;
76763
+ }
76764
+ };
76765
+
76615
76766
  installImportMetaCssBuild(import.meta);
76616
76767
  const css$y = /* css */`.navi_picker_confirm_body {
76617
76768
  min-width: 180px;
@@ -78579,11 +78730,16 @@ const ListItemSelectable = props => {
78579
78730
  // A list waiting on something is not unavailable, it is not answering yet —
78580
78731
  // busy rather than read-only (see BUSY_CONSTRAINT, which is asked first),
78581
78732
  // and the wait is about the same thing the read-only would be about.
78733
+ //
78734
+ // Unless what made the list read-only says it better: in words of its own (a
78735
+ // picker's or a list's readOnlyMessage), or because it is the network
78736
+ // policy holding the write the selection would make.
78582
78737
  const readOnlyFromAbove = useContext(ReadOnlyContext);
78738
+ const readOnlyMessageFromAbove = useContext(ReadOnlyMessageContext);
78583
78739
  const readOnlyMessageKey = readOnlyFromAbove ? multiple ? `constraint.readonly.selection` : `constraint.readonly.choice` : `constraint.readonly.option`;
78584
78740
  // Handed to the row as well as to the control: whichever of the two catches
78585
78741
  // the press answers it, and they must not answer it differently.
78586
- const readOnlyMessageResolved = props.readOnlyMessage ?? constraintI18n(readOnlyMessageKey, props);
78742
+ const readOnlyMessageResolved = props.readOnlyMessage ?? (readOnlyFromAbove ? readOnlyMessageFromAbove : undefined) ?? (readOnlyFromAbove === "network-policy" ? getNetworkPolicyReadOnlyMessage() : constraintI18n(readOnlyMessageKey, props));
78587
78743
  const busyMessageResolved = props.busyMessage ?? (loading ? listItemBusyMessage(loading, props) : constraintI18n(multiple ? `constraint.busy.selection` : `constraint.busy.choice`, props));
78588
78744
  const inputRef = useRef();
78589
78745
  const inputType = multiple ? "checkbox" : "radio";
@@ -87185,6 +87341,7 @@ const css$r = /* css */`@layer navi {
87185
87341
  .navi_picker:is([data-variant="button"], [data-variant="border"], [data-variant="discrete-border"]) {
87186
87342
  --picker-padding-x-default: var(--navi-button-padding-x-default);
87187
87343
  --picker-padding-y-default: var(--navi-button-padding-y-default);
87344
+ --picker-font-family: var(--navi-button-font-family, var(--navi-control-font-family, inherit));
87188
87345
  --picker-align-x-default: center;
87189
87346
  --picker-background-color: var(--navi-button-background-color);
87190
87347
  --picker-border-color-readonly: color-mix(in srgb,
@@ -87484,8 +87641,9 @@ const css$r = /* css */`@layer navi {
87484
87641
  --x-corner-top-right-radius: initial;
87485
87642
  --x-corner-bottom-right-radius: initial;
87486
87643
  --x-corner-bottom-left-radius: initial;
87487
- font-size: var(--navi-control-font-size);
87644
+ font-weight: initial;
87488
87645
  text-align: initial;
87646
+ font-size: 1rem;
87489
87647
  display: contents;
87490
87648
  }
87491
87649
 
@@ -87723,9 +87881,10 @@ const PickerButton = props => {
87723
87881
  // The same chain useControlProps resolves (own prop first, then what the
87724
87882
  // group above says), for the two things it does not carry: the popup content,
87725
87883
  // which is read-only along with the picker, and the cursor, which stays a
87726
- // pointer on a picker that still opens.
87884
+ // pointer on a picker that still opens. A hold of the network policy goes
87885
+ // with it: what the popup changes is what the picker would send.
87727
87886
  const readOnlyFromAbove = useContext(ReadOnlyContext);
87728
- const readOnlyResolved = readOnly || readOnlyFromAbove;
87887
+ const [readOnlyResolved, readOnlyMessageResolved] = readOnlyHandedDown(uiStateController, readOnly || readOnlyFromAbove);
87729
87888
  // Read off the controller rather than worked out again: what makes a
87730
87889
  // read-only picker open is settled once, where the gate reads it (see
87731
87890
  // createControlInfo's readOnlyOpens). Needed here for the cursor.
@@ -87773,11 +87932,13 @@ const PickerButton = props => {
87773
87932
  /* Read-only crosses into everything the picker is made of: what it really
87774
87933
  holds is drawn by controls of their own — in the popup, and on the façade
87775
87934
  where an application puts its own affordances — and each of them refuses
87776
- in its own words once told. Said from the read-only state alone, never
87935
+ once told, in the picker's words when it gave some (readOnlyMessage),
87936
+ in its own otherwise. Said from the read-only state alone, never
87777
87937
  from the busy one — an action running for a moment is not the same thing
87778
87938
  as a value nobody may change. */
87779
- jsx(ReadOnlyContext.Provider, {
87780
- value: readOnlyResolved,
87939
+ jsx(ReadOnlyProvider, {
87940
+ readOnly: readOnlyResolved,
87941
+ readOnlyMessage: readOnlyMessageResolved,
87781
87942
  children: jsxs(Box
87782
87943
  // A word in a sentence (variant="text") sits in a <p>, where a <div> is
87783
87944
  // not allowed: phrasing content there, block content elsewhere.