@jsenv/navi 0.29.518 → 0.29.520

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.
@@ -22024,6 +22024,11 @@ const BUSY_CONSTRAINT = {
22024
22024
  return null;
22025
22025
  }
22026
22026
 
22027
+ // Same as READONLY_CONSTRAINT: a run inside the popup holds it from there.
22028
+ if (intent === "close") {
22029
+ return null;
22030
+ }
22031
+
22027
22032
  // Busy, and what it opens still opens: a picker's answer lives in a shape
22028
22033
  // only its popup draws, and refusing to open leaves it unreadable for as
22029
22034
  // long as the wait lasts. Opening reads and nothing more — the same
@@ -22118,12 +22123,17 @@ const findBusySource = (field) => {
22118
22123
  const DISABLED_CONSTRAINT = {
22119
22124
  name: "disabled",
22120
22125
  messageAttribute: "data-disabled-message",
22121
- check: (field) => {
22126
+ check: (field, { intent } = {}) => {
22122
22127
  const disabled = field.controlHostProps.disabled;
22123
22128
  if (!disabled) {
22124
22129
  return null;
22125
22130
  }
22126
22131
 
22132
+ // Same as READONLY_CONSTRAINT: a popup still open leaves like any other.
22133
+ if (intent === "close") {
22134
+ return null;
22135
+ }
22136
+
22127
22137
  const type = field.controlHostProps.type;
22128
22138
  let message;
22129
22139
  if (type === "radio") {
@@ -23112,6 +23122,14 @@ const READONLY_CONSTRAINT = {
23112
23122
  return null;
23113
23123
  }
23114
23124
 
23125
+ // Leaving a popup already open is never the control's to refuse, whatever
23126
+ // it would refuse on the way in: closing writes nothing, and what may hold
23127
+ // a popup is the popup's own question (see popup_busy.js and the picker's
23128
+ // onRequestClose).
23129
+ if (intent === "close") {
23130
+ return null;
23131
+ }
23132
+
23115
23133
  // Read-only, and what it opens still opens: a picker's answer often lives
23116
23134
  // in a shape only its popup draws — a plan with one tile ringed, a wheel
23117
23135
  // stopped on a time — and refusing to open leaves that shape unreadable.
@@ -23211,7 +23229,8 @@ const readOnlyMessage = (field) => {
23211
23229
  * → onRequestInteraction
23212
23230
  * → check disabled / read-only / busy (via controller.rules.interaction)
23213
23231
  * against the interaction's `intent` ("write" by default, "read" for one
23214
- * that only shows what is already there — see READONLY_CONSTRAINT)
23232
+ * that only shows what is already there, "close" for one leaving a popup
23233
+ * — see READONLY_CONSTRAINT)
23215
23234
  * → if blocked → prevented()
23216
23235
  * → if allowed → allowed()
23217
23236
  * → (in allowed callback) setUIState(value)
@@ -23494,9 +23513,10 @@ const onRequestInteraction = (
23494
23513
  name,
23495
23514
  // What this interaction would do to the control: write it, or only read it.
23496
23515
  // Everything writes unless it says otherwise — an interaction that merely
23497
- // shows what is already there (opening a picker's popup, closing it again)
23498
- // says "read", and that is what a control held read-only can still let
23499
- // through (see READONLY_CONSTRAINT).
23516
+ // shows what is already there (opening a picker's popup) says "read", and
23517
+ // that is what a control held read-only can still let through. Closing that
23518
+ // popup says "close", which no state of the control refuses (see
23519
+ // READONLY_CONSTRAINT).
23500
23520
  intent = "write",
23501
23521
  bypassInteractivity = false,
23502
23522
  // navi continuing a gesture on its own rather than a user asking for
@@ -44748,11 +44768,8 @@ const clearCommand = (source, event) => {
44748
44768
  return;
44749
44769
  }
44750
44770
  // Only what is open: the clear cross of a picker sits on the closed
44751
- // trigger, and asking that trigger to close is asking it to do nothing —
44752
- // except that the ask goes through the interaction gate, which turns it
44753
- // down while the clear it just sent is still running and says so out loud
44754
- // ("this element is busy"). A clear pressed INSIDE an open popup is the
44755
- // one this is for: it answers the popup, so the popup goes away.
44771
+ // trigger, with nothing to close. A clear pressed INSIDE an open popup is
44772
+ // the one this is for: it answers the popup, so the popup goes away.
44756
44773
  const expandable = resolveClosestExpandable(source);
44757
44774
  if (expandable?.getAttribute("aria-expanded") !== "true") {
44758
44775
  return;
@@ -53155,21 +53172,20 @@ const useInteractiveProps = (props, {
53155
53172
  // — a diskette saving a row into the reader's own address book stays
53156
53173
  // pressable on a game nobody may edit. Its own props still hold; only what
53157
53174
  // came from above is dropped (see self_interactions.js for the three modes).
53158
- const zoneStateApplies = !selfInteractionsIgnoreBlock(props);
53159
53175
  // A press that only leaves the popup it sits in writes nothing to the zone
53160
- // around it either, so that zone being read-only — or holding a write the
53161
- // network policy keeps — does not hold it: the popup answers the close
53162
- // itself. A read-only picker's popup then closes from its own button as
53163
- // it does from a press outside, and refuses both alike when it holds a
53164
- // change it cannot send.
53165
- const zoneReadOnlyApplies = zoneStateApplies && !pressOnlyLeaves(props);
53176
+ // around it either, so that zone being disabled, read-only or busy — or
53177
+ // holding a write the network policy keeps — does not hold it: the popup
53178
+ // answers the close itself. A read-only picker's popup then closes from its
53179
+ // own button as it does from a press outside, and refuses both alike when it
53180
+ // holds a change it cannot send or a run it cannot give up on.
53181
+ const zoneStateApplies = !selfInteractionsIgnoreBlock(props) && !pressOnlyLeaves(controlInfo.controlType, props);
53166
53182
  // A press feeding nothing to the write around it — a travel, a page, a
53167
53183
  // uiAction, a function of its own — has nothing for the network policy to
53168
53184
  // keep: only its own verb holds it, as anywhere else. What the zone holds
53169
53185
  // for its own reasons (read-only, busy) still holds it.
53170
53186
  const asksNothingOfTheWrite = pressAsksNothingOfTheWrite(controlInfo.controlType, props, boundAction);
53171
53187
  const controlDisabled = zoneStateApplies && controlDisabledFromAbove;
53172
- const controlReadOnly = zoneReadOnlyApplies && !(asksNothingOfTheWrite && controlReadOnlyFromAbove === "network-policy") && controlReadOnlyFromAbove;
53188
+ const controlReadOnly = zoneStateApplies && !(asksNothingOfTheWrite && controlReadOnlyFromAbove === "network-policy") && controlReadOnlyFromAbove;
53173
53189
  const controlLoading = zoneStateApplies && controlLoadingFromAbove;
53174
53190
  const disabledResolved = disabled || controlDisabled;
53175
53191
  const requiredResolved = required || controlRequired;
@@ -53188,7 +53204,7 @@ const useInteractiveProps = (props, {
53188
53204
  // picker's popup) — refuses before the press and says why (see
53189
53205
  // network_policy.js): a press that looks accepted and fails after is what
53190
53206
  // the policy exists to avoid.
53191
- const heldByNetworkPolicy = networkPolicyReason !== null && (isWriteAction(boundAction) || Boolean(zoneReadOnlyApplies && !asksNothingOfTheWrite && parentAction && isWriteAction(parentAction)) || controlReadOnly === "network-policy");
53207
+ const heldByNetworkPolicy = networkPolicyReason !== null && (isWriteAction(boundAction) || Boolean(zoneStateApplies && !asksNothingOfTheWrite && parentAction && isWriteAction(parentAction)) || controlReadOnly === "network-policy");
53192
53208
  // Held back because the surface above already has as many runs in flight as
53193
53209
  // it allows (see parallel_guard.js). Only a control that would START one:
53194
53210
  // anything without an action of its own runs nothing and has nothing to
@@ -53762,10 +53778,11 @@ const splitControlProps = (props, {
53762
53778
  return [controlRootProps, controlHostProps];
53763
53779
  };
53764
53780
  // What a press leaving a popup asks for: closing it, or cancelling it. Only on
53765
- // a control running nothing of its own — one with an action writes, wherever
53766
- // it then leads.
53781
+ // a button or a link running nothing of its own — one with an action writes,
53782
+ // wherever it then leads, and on a field or a form `command` is what follows
53783
+ // the value it writes.
53767
53784
  const LEAVING_COMMAND_SET = new Set(["--navi-close", "--navi-cancel"]);
53768
- const pressOnlyLeaves = props => LEAVING_COMMAND_SET.has(props.command) && !props.action;
53785
+ const pressOnlyLeaves = (controlType, props) => (controlType === "button" || controlType === "link") && LEAVING_COMMAND_SET.has(props.command) && !props.action;
53769
53786
  // What these commands move reads only the person's place: the slide shown,
53770
53787
  // the item pointed at, the scroll, the page (see commands.js).
53771
53788
  const COMMANDS_ASKING_NOTHING_OF_THE_WRITE = new Set(["--navi-right", "--navi-left", "--navi-down", "--navi-up", "--navi-previous", "--navi-next", "--navi-first", "--navi-last", "--navi-go-to-slide", "--navi-back", "--navi-scroll", "--navi-nav-to", "--navi-nav-back"]);
@@ -57843,6 +57860,8 @@ const css$$ = /* css */`@layer navi {
57843
57860
  }
57844
57861
 
57845
57862
  &[data-stretch] {
57863
+ z-index: auto;
57864
+ outline-width: 0;
57846
57865
  position: static;
57847
57866
 
57848
57867
  &:after {
@@ -57850,6 +57869,12 @@ const css$$ = /* css */`@layer navi {
57850
57869
  position: absolute;
57851
57870
  inset: 0;
57852
57871
  }
57872
+
57873
+ &[data-focus-visible]:after {
57874
+ border-radius: var(--link-border-radius);
57875
+ outline: var(--link-outline-width) solid var(--link-outline-color);
57876
+ outline-offset: calc(-1 * var(--link-outline-width));
57877
+ }
57853
57878
  }
57854
57879
 
57855
57880
  &[data-readonly] > * {
@@ -58073,7 +58098,10 @@ const LinkPseudoElements = ["::-navi-loader", "::-navi-indicator"];
58073
58098
  * positioned; navi's `Text` is not. A control in the row that keeps its own
58074
58099
  * press sits above the area when it is positioned and comes after the link
58075
58100
  * (navi's `Button` is positioned); one written before the link is raised
58076
- * with a `z-index`. The link is never positioned itself, in any state.
58101
+ * with a `z-index`. The link is never positioned itself, in any state. The
58102
+ * focus ring is drawn inside the press area and cannot read the ancestor's
58103
+ * radius: a rounded ancestor repeats it on the link
58104
+ * (`<Link stretch borderRadius="m">`).
58077
58105
  * @param {boolean} [props.hrefFallback] - Use `href` as the visible text when
58078
58106
  * no children are given; defaults to `true` unless `anchor`.
58079
58107
  * @param {string|{type?: string, duration?: number|string, direction?: "forward"|"back"}} [props.routeTransition] -
@@ -76281,12 +76309,11 @@ const clipOf = (side, distance) => {
76281
76309
  * `--navi-close`, which the nearest popup above it answers; the caller only
76282
76310
  * decides where it goes (the right end of a header, most of the time).
76283
76311
  *
76284
- * It is the way out of a popup, so it is the one affordance in there that the
76285
- * surrounding control's read-only and disabled do not reach: a read-only picker
76286
- * still opens (reading changes nothing) and hands its popup that read-only, and
76287
- * a cross wearing it would trap the user in a popup they were invited to read.
76288
- * A close button written by hand inherits it and refuses the press — which is
76289
- * the reason to use this one rather than a `<Button command="--navi-close" />`.
76312
+ * It is the way out of a popup, so the state of the surrounding control does not
76313
+ * reach it, as with any `--navi-close` button that has no action of its own (see
76314
+ * pressOnlyLeaves in control_hooks.jsx): a read-only picker still opens (reading
76315
+ * changes nothing) and hands its popup that read-only, and a cross wearing it
76316
+ * would trap the user in a popup they were invited to read.
76290
76317
  *
76291
76318
  * Exempt from the state around it, never from the popup's own answer: the cross
76292
76319
  * ASKS, and the popup decides — a dialog holding a form mid-send keeps the
@@ -76313,15 +76340,6 @@ const PopupClose = ({
76313
76340
  command: "--navi-close",
76314
76341
  icon: true,
76315
76342
  variant: "discrete"
76316
- // Dismissing writes nothing to whatever control the popup belongs to, so
76317
- // that control's read-only and disabled are not about this cross: a
76318
- // read-only picker still opens to be read (see readonly_constraint.js),
76319
- // and what is opened has to be closable. Whether closing is allowed at
76320
- // this instant is the popup's own question, answered by the popup — a
76321
- // dialog holds the close while an action inside it runs (see
76322
- // popup_busy.js).
76323
- ,
76324
- whenSelfInteractionsBlocked: "ignore"
76325
76343
  // The cross is drawn at the control size, which is a few millimetres
76326
76344
  // wide; the padding is what makes it a target a thumb can hit.
76327
76345
  ,
@@ -82020,11 +82038,11 @@ const PickerCustom = props => {
82020
82038
  event: e,
82021
82039
  name: "navi_request_open_event",
82022
82040
  // Showing what the picker already holds, in the shape only the popup
82023
- // draws it in — nothing of the value is written on the way in, nor on
82024
- // the way out. Every interaction below says the same, which is what
82025
- // lets a read-only picker be opened and read while everything that
82026
- // would write it (paste, cut, the clear cross) stays refused. See
82027
- // READONLY_CONSTRAINT.
82041
+ // draws it in — nothing of the value is written on the way in. Every
82042
+ // way of opening below says the same, which is what lets a read-only
82043
+ // picker be opened and read while everything that would write it
82044
+ // (paste, cut, the clear cross) stays refused. Every way of closing
82045
+ // says "close". See READONLY_CONSTRAINT.
82028
82046
  intent: "read",
82029
82047
  allowed: () => {
82030
82048
  // What the open said travels with it: a picker opened from
@@ -82049,7 +82067,7 @@ const PickerCustom = props => {
82049
82067
  onnavi_request_close: e => {
82050
82068
  requestInteraction({
82051
82069
  event: e,
82052
- intent: "read",
82070
+ intent: "close",
82053
82071
  allowed: () => {
82054
82072
  const closing = requestClose(e, {
82055
82073
  isCancel: e.detail.isCancel,
@@ -82156,7 +82174,7 @@ const PickerCustom = props => {
82156
82174
  }
82157
82175
  return {
82158
82176
  name: "escape_to_cancel",
82159
- intent: "read",
82177
+ intent: "close",
82160
82178
  allowed: () => {
82161
82179
  // Read with escapeEffect by the controller (see above).
82162
82180
  requestClose(e, {
@@ -82233,7 +82251,7 @@ const PickerCustom = props => {
82233
82251
  pressAnsweredAtMouseDownRef.current = readPressCount();
82234
82252
  return {
82235
82253
  name: "mousedown to close picker",
82236
- intent: "read",
82254
+ intent: "close",
82237
82255
  allowed: () => requestClose(e, {
82238
82256
  isCancel: true
82239
82257
  })
@@ -82281,7 +82299,7 @@ const PickerCustom = props => {
82281
82299
  if (openController.opened && !openedDuringThisPress(openController)) {
82282
82300
  return {
82283
82301
  name: "click to close picker",
82284
- intent: "read",
82302
+ intent: "close",
82285
82303
  prevented: () => {
82286
82304
  e.preventDefault();
82287
82305
  },