@aurodesignsystem-dev/auro-formkit 0.0.0-pr1346.3 → 0.0.0-pr1346.4

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.
Files changed (40) hide show
  1. package/components/checkbox/demo/api.min.js +1 -1
  2. package/components/checkbox/demo/index.min.js +1 -1
  3. package/components/checkbox/dist/index.js +1 -1
  4. package/components/checkbox/dist/registered.js +1 -1
  5. package/components/combobox/demo/api.min.js +224 -45
  6. package/components/combobox/demo/index.min.js +224 -45
  7. package/components/combobox/dist/auro-combobox.d.ts +2 -1
  8. package/components/combobox/dist/index.js +217 -42
  9. package/components/combobox/dist/registered.js +217 -42
  10. package/components/counter/demo/api.min.js +123 -11
  11. package/components/counter/demo/index.min.js +123 -11
  12. package/components/counter/dist/index.js +123 -11
  13. package/components/counter/dist/registered.js +123 -11
  14. package/components/datepicker/demo/api.min.js +133 -20
  15. package/components/datepicker/demo/index.min.js +133 -20
  16. package/components/datepicker/dist/index.js +133 -20
  17. package/components/datepicker/dist/registered.js +133 -20
  18. package/components/dropdown/demo/api.min.js +122 -10
  19. package/components/dropdown/demo/index.min.js +122 -10
  20. package/components/dropdown/dist/auro-dropdownBib.d.ts +18 -2
  21. package/components/dropdown/dist/index.js +122 -10
  22. package/components/dropdown/dist/registered.js +122 -10
  23. package/components/input/demo/api.min.js +10 -9
  24. package/components/input/demo/index.min.js +10 -9
  25. package/components/input/dist/index.js +10 -9
  26. package/components/input/dist/registered.js +10 -9
  27. package/components/menu/demo/api.min.js +7 -3
  28. package/components/menu/demo/index.min.js +7 -3
  29. package/components/menu/dist/index.js +7 -3
  30. package/components/menu/dist/registered.js +7 -3
  31. package/components/radio/demo/api.min.js +1 -1
  32. package/components/radio/demo/index.min.js +1 -1
  33. package/components/radio/dist/index.js +1 -1
  34. package/components/radio/dist/registered.js +1 -1
  35. package/components/select/demo/api.min.js +159 -17
  36. package/components/select/demo/index.min.js +159 -17
  37. package/components/select/dist/index.js +152 -14
  38. package/components/select/dist/registered.js +152 -14
  39. package/custom-elements.json +35 -10
  40. package/package.json +1 -1
@@ -1687,7 +1687,7 @@ class AuroHelpText extends i$2 {
1687
1687
  }
1688
1688
  }
1689
1689
 
1690
- var formkitVersion = '202602232116';
1690
+ var formkitVersion = '202602260152';
1691
1691
 
1692
1692
  // Copyright (c) 2026 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1693
1693
  // See LICENSE in the project root for license information.
@@ -1679,7 +1679,7 @@ class AuroHelpText extends i$2 {
1679
1679
  }
1680
1680
  }
1681
1681
 
1682
- var formkitVersion = '202602232116';
1682
+ var formkitVersion = '202602260152';
1683
1683
 
1684
1684
  // Copyright (c) 2026 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1685
1685
  // See LICENSE in the project root for license information.
@@ -1632,7 +1632,7 @@ class AuroHelpText extends LitElement {
1632
1632
  }
1633
1633
  }
1634
1634
 
1635
- var formkitVersion = '202602232116';
1635
+ var formkitVersion = '202602260152';
1636
1636
 
1637
1637
  // Copyright (c) 2026 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1638
1638
  // See LICENSE in the project root for license information.
@@ -1632,7 +1632,7 @@ class AuroHelpText extends LitElement {
1632
1632
  }
1633
1633
  }
1634
1634
 
1635
- var formkitVersion = '202602232116';
1635
+ var formkitVersion = '202602260152';
1636
1636
 
1637
1637
  // Copyright (c) 2026 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1638
1638
  // See LICENSE in the project root for license information.
@@ -3950,7 +3950,7 @@ let p$4 = class p{registerComponent(t,a){customElements.get(t)||customElements.d
3950
3950
 
3951
3951
  var iconVersion$3 = '9.1.2';
3952
3952
 
3953
- var styleCss$2$1 = i$7`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;inset:unset}:host(:not([isfullscreen])) .container.shape-box{border-radius:unset}:host(:not([isfullscreen])) .container[class*=shape-pill],:host(:not([isfullscreen])) .container[class*=shape-snowflake]{border-radius:30px}:host(:not([isfullscreen])) .container[class*=shape-rounded]{border-radius:16px}:host(:not([matchWidth])) .container{min-width:fit-content}:host([isfullscreen]){position:fixed;top:0;left:0}:host([isfullscreen]) .container{width:100dvw;max-width:none;height:100dvh;max-height:none;border-radius:unset;margin-top:0;box-shadow:unset;overscroll-behavior:contain}:host([data-show]){display:flex}:host([common]:not([isfullscreen])) .container,:host([rounded]:not([isfullscreen])) .container{border-radius:var(--ds-border-radius, 0.375rem)}:host([common][isfullscreen]) .container,:host([rounded][isfullscreen]) .container{border-radius:unset;box-shadow:unset}.container{display:inline-block;overflow:auto;box-sizing:border-box;border-radius:var(--ds-border-radius, 0.375rem);margin:var(--ds-size-50, 0.25rem) 0}.util_displayHiddenVisually{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip-path:inset(50%);white-space:nowrap}`;
3953
+ var styleCss$2$1 = i$7`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{max-width:none;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:absolute;inset:unset}:host(:not([isfullscreen])) .container.shape-box{border-radius:unset}:host(:not([isfullscreen])) .container[class*=shape-pill],:host(:not([isfullscreen])) .container[class*=shape-snowflake]{border-radius:30px}:host(:not([isfullscreen])) .container[class*=shape-rounded]{border-radius:16px}:host(:not([matchWidth])) .container{min-width:fit-content}:host([isfullscreen]){position:fixed;top:0;left:0}:host([isfullscreen]) .container{width:100dvw;max-width:none;height:100dvh;max-height:none;border-radius:unset;margin-top:0;box-shadow:unset;overscroll-behavior:contain}:host([isfullscreen]) .container::backdrop{background:var(--ds-color-background-primary, #fff)}:host([data-show]){display:flex}:host([common]:not([isfullscreen])) .container,:host([rounded]:not([isfullscreen])) .container{border-radius:var(--ds-border-radius, 0.375rem)}:host([common][isfullscreen]) .container,:host([rounded][isfullscreen]) .container{border-radius:unset;box-shadow:unset}.container{display:inline-block;overflow:auto;box-sizing:border-box;border-radius:var(--ds-border-radius, 0.375rem);margin:var(--ds-size-50, 0.25rem) 0}.util_displayHiddenVisually{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip-path:inset(50%);white-space:nowrap}`;
3954
3954
 
3955
3955
  var colorCss$2$1 = i$7`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
3956
3956
 
@@ -3958,6 +3958,8 @@ var tokensCss$1$2 = i$7`:host(:not([ondark])),:host(:not([appearance=inverse])){
3958
3958
 
3959
3959
  // Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
3960
3960
  // See LICENSE in the project root for license information.
3961
+ /* eslint-disable max-lines */
3962
+ // ---------------------------------------------------------------------
3961
3963
 
3962
3964
 
3963
3965
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
@@ -4133,22 +4135,63 @@ class AuroDropdownBib extends i$4 {
4133
4135
  // Handle ESC key via dialog's cancel event
4134
4136
  const dialog = this.shadowRoot.querySelector('dialog');
4135
4137
  dialog.addEventListener('cancel', (event) => {
4136
- event.preventDefault(); // Let parent handle closing
4138
+ // Let parent handle closing
4139
+ event.preventDefault();
4137
4140
  this.dispatchEvent(new CustomEvent('auro-bib-cancel', {
4138
4141
  bubbles: true,
4139
4142
  composed: true
4140
4143
  }));
4141
4144
  });
4142
4145
 
4143
- // Re-dispatch navigation keyboard events so they cross the shadow DOM
4144
- // boundary and reach the combobox/select key handlers.
4145
- // Only intercept keys used for menu navigation let all other keys
4146
- // (characters, Backspace, etc.) through so typing in inputs works.
4147
- const navKeys = new Set(['ArrowUp', 'ArrowDown', 'Enter', 'Escape']);
4146
+ // showModal() creates a closed focus scope keyboard events inside
4147
+ // the dialog's shadow DOM do NOT bubble out to the combobox/select
4148
+ // keydown handlers in the parent shadow DOM. This handler bridges
4149
+ // that gap by re-dispatching navigation keys so they cross the
4150
+ // shadow boundary and reach the menu navigation logic in the parent
4151
+ // component.
4152
+ //
4153
+ // The trade-off: intercepting these keys means native keyboard
4154
+ // behaviors that would normally "just work" must be manually
4155
+ // re-implemented here:
4156
+ //
4157
+ // - Enter on buttons: Custom elements (auro-button) don't get the
4158
+ // native Enter→click that <button> provides, so we call .click()
4159
+ // directly when Enter is pressed on a button-like element.
4160
+ //
4161
+ // - Tab: NOT intercepted — left to the browser's native focus trap
4162
+ // provided by showModal(), which cycles Tab between focusable
4163
+ // elements inside the dialog (e.g. the input and close button).
4164
+ // Intercepting Tab would kill the native focus trap and break
4165
+ // focus management inside the dialog.
4166
+ //
4167
+ // - Escape: The native <dialog> fires a `cancel` event on ESC
4168
+ // (handled above), so the re-dispatched Escape is a secondary
4169
+ // path for parent components that also listen for Escape keydown.
4170
+ const navKeys = new Set([
4171
+ 'ArrowUp',
4172
+ 'ArrowDown',
4173
+ 'Enter',
4174
+ 'Escape'
4175
+ ]);
4148
4176
  dialog.addEventListener('keydown', (event) => {
4149
4177
  if (!navKeys.has(event.key)) {
4150
4178
  return;
4151
4179
  }
4180
+
4181
+ // Custom elements (auro-button) don't get the native Enter→click
4182
+ // behavior that <button> has. Find the button in the composed path
4183
+ // and click it directly.
4184
+ if (event.key === 'Enter') {
4185
+ const buttonSelector = 'button, [role="button"], auro-button, [auro-button]';
4186
+ const btn = event.composedPath().find((el) => el.matches && el.matches(buttonSelector));
4187
+ if (btn) {
4188
+ event.preventDefault();
4189
+ event.stopPropagation();
4190
+ btn.click();
4191
+ return;
4192
+ }
4193
+ }
4194
+
4152
4195
  event.preventDefault();
4153
4196
  event.stopPropagation();
4154
4197
  const newEvent = new KeyboardEvent('keydown', {
@@ -4172,9 +4215,55 @@ class AuroDropdownBib extends i$4 {
4172
4215
  }
4173
4216
 
4174
4217
  /**
4175
- * Opens the dialog using showModal() for accessibility.
4176
- * @param {boolean} modal - If true, uses showModal() (default). If false, uses show().
4218
+ * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
4219
+ *
4220
+ * The showModal() function places the dialog in the browser's **top layer**,
4221
+ * which is a separate rendering layer above the normal DOM. On mobile, the
4222
+ * compositor processes visual-viewport panning before top-layer touch
4223
+ * handling. This means the entire viewport — including the top-layer dialog
4224
+ * — can be panned by a touch gesture, causing the page behind the dialog to
4225
+ * scroll into view. To prevent this, we add a touchmove listener that cancels
4226
+ * the event if the touch started outside the dialog or any scrollable child within it.
4227
+ *
4228
+ * @private
4229
+ */
4230
+ _lockTouchScroll() {
4231
+ const dialog = this.shadowRoot.querySelector('dialog');
4232
+
4233
+ this._touchMoveHandler = (event) => {
4234
+ // Walk the composed path (which crosses shadow DOM boundaries) to
4235
+ // check whether the touch started inside a scrollable element that
4236
+ // lives within the dialog. If so, allow the scroll.
4237
+ for (const el of event.composedPath()) {
4238
+ if (el === dialog) {
4239
+ // Reached the dialog boundary without finding a scrollable child.
4240
+ break;
4241
+ }
4242
+ if (el instanceof HTMLElement && el.scrollHeight > el.clientHeight) {
4243
+ const { overflowY } = getComputedStyle(el);
4244
+ if (overflowY === 'auto' || overflowY === 'scroll') {
4245
+ return;
4246
+ }
4247
+ }
4248
+ }
4249
+
4250
+ event.preventDefault();
4251
+ };
4252
+
4253
+ document.addEventListener('touchmove', this._touchMoveHandler, { passive: false });
4254
+ }
4255
+
4256
+ /**
4257
+ * Removes the touchmove listener added by _lockTouchScroll().
4258
+ * @private
4177
4259
  */
4260
+ _unlockTouchScroll() {
4261
+ if (this._touchMoveHandler) {
4262
+ document.removeEventListener('touchmove', this._touchMoveHandler);
4263
+ this._touchMoveHandler = undefined;
4264
+ }
4265
+ }
4266
+
4178
4267
  open(modal = true) {
4179
4268
  const dialog = this.shadowRoot.querySelector('dialog');
4180
4269
  if (dialog && !dialog.open) {
@@ -4191,6 +4280,8 @@ class AuroDropdownBib extends i$4 {
4191
4280
 
4192
4281
  documentElement.style.overflow = prevOverflow;
4193
4282
 
4283
+ this._lockTouchScroll();
4284
+
4194
4285
  } else {
4195
4286
  // Use setAttribute instead of dialog.show() to avoid the dialog
4196
4287
  // focusing steps which steal focus from the trigger and cause
@@ -4206,6 +4297,7 @@ class AuroDropdownBib extends i$4 {
4206
4297
  close() {
4207
4298
  const dialog = this.shadowRoot.querySelector('dialog');
4208
4299
  if (dialog && dialog.open) {
4300
+ this._unlockTouchScroll();
4209
4301
  dialog.close();
4210
4302
  }
4211
4303
  }
@@ -4471,7 +4563,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$4 {
4471
4563
  }
4472
4564
  };
4473
4565
 
4474
- var formkitVersion$2 = '202602232116';
4566
+ var formkitVersion$2 = '202602260152';
4475
4567
 
4476
4568
  let AuroElement$2 = class AuroElement extends i$4 {
4477
4569
  static get properties() {
@@ -4744,6 +4836,18 @@ class AuroDropdown extends AuroElement$2 {
4744
4836
  */
4745
4837
  show() {
4746
4838
  this.floater.showBib();
4839
+
4840
+ // Open dialog synchronously so callers remain in the user gesture
4841
+ // chain. This is critical for mobile browsers (iOS Safari) to keep
4842
+ // the virtual keyboard open when transitioning from the trigger
4843
+ // input to an input inside the fullscreen dialog. Without this,
4844
+ // showModal() fires asynchronously via Lit's update cycle, which
4845
+ // falls outside the user activation window and causes iOS to
4846
+ // dismiss the keyboard.
4847
+ if (this.isBibFullscreen && this.bibElement && this.bibElement.value) {
4848
+ const useModal = !this.disableFocusTrap;
4849
+ this.bibElement.value.open(useModal);
4850
+ }
4747
4851
  }
4748
4852
 
4749
4853
  /**
@@ -5130,6 +5234,14 @@ class AuroDropdown extends AuroElement$2 {
5130
5234
  this.bibElement.value.close();
5131
5235
  }
5132
5236
  }
5237
+
5238
+ // When fullscreen strategy changes while open, re-open dialog with correct mode
5239
+ // (e.g. resizing from desktop → mobile while dropdown is open)
5240
+ if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
5241
+ const useModal = this.isBibFullscreen && !this.disableFocusTrap;
5242
+ this.bibElement.value.close();
5243
+ this.bibElement.value.open(useModal);
5244
+ }
5133
5245
  }
5134
5246
 
5135
5247
  /**
@@ -10631,6 +10743,15 @@ class BaseInput extends AuroElement$1 {
10631
10743
  constructor() {
10632
10744
  super();
10633
10745
 
10746
+ // Delegate focus to the native <input> inside the shadow root so that
10747
+ // showModal()'s dialog focusing steps reach the input element.
10748
+ // This keeps the mobile virtual keyboard open when the fullscreen dialog
10749
+ // opens, because the browser sees an input-to-input focus transfer.
10750
+ this.constructor.shadowRootOptions = {
10751
+ ...AuroElement$1.shadowRootOptions,
10752
+ delegatesFocus: true,
10753
+ };
10754
+
10634
10755
  this._initializeDefaults();
10635
10756
  }
10636
10757
 
@@ -10752,14 +10873,6 @@ class BaseInput extends AuroElement$1 {
10752
10873
  reflect: true
10753
10874
  },
10754
10875
 
10755
- /**
10756
- * The value for the aria-controls attribute.
10757
- */
10758
- a11yControls: {
10759
- type: String,
10760
- reflect: true
10761
- },
10762
-
10763
10876
  /**
10764
10877
  * The value for the aria-activedescendant attribute.
10765
10878
  * Points to the ID of the currently active/highlighted option in a listbox.
@@ -12179,7 +12292,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$4 {
12179
12292
  }
12180
12293
  };
12181
12294
 
12182
- var formkitVersion$1 = '202602232116';
12295
+ var formkitVersion$1 = '202602260152';
12183
12296
 
12184
12297
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12185
12298
  // See LICENSE in the project root for license information.
@@ -13206,7 +13319,7 @@ class AuroBibtemplate extends i$4 {
13206
13319
  }
13207
13320
  }
13208
13321
 
13209
- var formkitVersion = '202602232116';
13322
+ var formkitVersion = '202602260152';
13210
13323
 
13211
13324
  var styleCss$3 = i$7`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
13212
13325
 
@@ -13624,6 +13737,7 @@ class AuroCombobox extends AuroElement {
13624
13737
  this.dropdownOpen = false;
13625
13738
  this.triggerExpandedState = false;
13626
13739
  this._expandedTimeout = null;
13740
+ this._inFullscreenTransition = false;
13627
13741
  this.errorMessage = null;
13628
13742
  this.isHiddenWhileLoading = false;
13629
13743
  this.largeFullscreenHeadline = false;
@@ -13644,7 +13758,7 @@ class AuroCombobox extends AuroElement {
13644
13758
 
13645
13759
  /**
13646
13760
  * Defines whether the component will be on lighter or darker backgrounds.
13647
- * @property {'default' | 'inverse'}
13761
+ * @property {'default' | 'inverse'} appearance - The visual appearance of the component.
13648
13762
  * @default 'default'
13649
13763
  */
13650
13764
  appearance: {
@@ -14199,7 +14313,7 @@ class AuroCombobox extends AuroElement {
14199
14313
  * @returns {void}
14200
14314
  */
14201
14315
  showBib() {
14202
- if (!this.input.value) {
14316
+ if (!this.input.value && !this.dropdown.isBibFullscreen) {
14203
14317
  this.dropdown.hide();
14204
14318
  return;
14205
14319
  }
@@ -14268,6 +14382,18 @@ class AuroCombobox extends AuroElement {
14268
14382
 
14269
14383
  // Restore trigger accessibility when closing fullscreen
14270
14384
  this.dropdown.trigger.inert = false;
14385
+
14386
+ // Restore focus to the trigger input after closing the
14387
+ // fullscreen dialog. The browser's native dialog focus restoration
14388
+ // fails because the trigger was set to inert before showModal().
14389
+ // Use rAF to run after Lit's microtask update cycle calls dialog.close().
14390
+ if (this.dropdown.isBibFullscreen) {
14391
+ requestAnimationFrame(() => {
14392
+ if (!this.dropdown.isPopoverVisible) {
14393
+ this.input.focus();
14394
+ }
14395
+ });
14396
+ }
14271
14397
  }
14272
14398
 
14273
14399
  if (this.dropdownOpen) {
@@ -14277,6 +14403,19 @@ class AuroCombobox extends AuroElement {
14277
14403
  this.updateBibDialogRole();
14278
14404
 
14279
14405
  if (this.dropdown.isBibFullscreen) {
14406
+ // Guard against spurious validation during the focus transition
14407
+ // from trigger to bib input. Setting trigger.inert = true removes
14408
+ // focus, which fires focusout on the child auro-input before the
14409
+ // bib input receives focus. That focusout triggers the input's own
14410
+ // validate(), which dispatches a composed auroFormElement-validated
14411
+ // event. Because composed events are retargetted at each shadow DOM
14412
+ // boundary, the event appears to originate from the combobox itself
14413
+ // and its listener unconditionally sets this.validity — causing
14414
+ // premature validation. This flag suppresses all validation paths
14415
+ // (focusout handler, handleInputValueChange, validate(), and the
14416
+ // auroFormElement-validated listener) until focus settles in the bib.
14417
+ this._inFullscreenTransition = true;
14418
+
14280
14419
  // Hide the trigger from assistive technology so VoiceOver cannot reach it
14281
14420
  // behind the fullscreen dialog
14282
14421
  this.dropdown.trigger.inert = true;
@@ -14308,6 +14447,7 @@ class AuroCombobox extends AuroElement {
14308
14447
  requestAnimationFrame(() => {
14309
14448
  requestAnimationFrame(() => {
14310
14449
  this.setInputFocus();
14450
+ this._inFullscreenTransition = false;
14311
14451
  });
14312
14452
  });
14313
14453
  } else {
@@ -14402,7 +14542,7 @@ class AuroCombobox extends AuroElement {
14402
14542
  * @private
14403
14543
  */
14404
14544
  updateBibDialogRole() {
14405
- const bibEl = this.dropdown.bibElement?.value;
14545
+ const bibEl = this.dropdown.bibElement && this.dropdown.bibElement.value;
14406
14546
  if (!bibEl) {
14407
14547
  return;
14408
14548
  }
@@ -14531,9 +14671,10 @@ class AuroCombobox extends AuroElement {
14531
14671
  // Announce the selection after the dropdown closes so it isn't
14532
14672
  // overridden by VoiceOver's "collapsed" announcement from aria-expanded.
14533
14673
  const selectedValue = event.detail.stringValue;
14674
+ const announcementDelay = 300;
14534
14675
  setTimeout(() => {
14535
14676
  this.announceToScreenReader(`${selectedValue}, selected`);
14536
- }, 300);
14677
+ }, announcementDelay);
14537
14678
  }
14538
14679
  });
14539
14680
 
@@ -14581,7 +14722,7 @@ class AuroCombobox extends AuroElement {
14581
14722
  * Validate every time we remove focus from the combo box.
14582
14723
  */
14583
14724
  this.addEventListener('focusout', () => {
14584
- if (!this.componentHasFocus) {
14725
+ if (!this.componentHasFocus && !this._inFullscreenTransition) {
14585
14726
  this.validate();
14586
14727
  }
14587
14728
  });
@@ -14632,8 +14773,11 @@ class AuroCombobox extends AuroElement {
14632
14773
  }
14633
14774
  this.handleMenuOptions();
14634
14775
 
14635
- // Validate only if the value was set programmatically
14636
- if (!this.componentHasFocus) {
14776
+ // Validate only if the value was set programmatically (not during user
14777
+ // interaction). In fullscreen dialog mode, componentHasFocus returns false
14778
+ // because focus is inside the top-layer dialog, so also check
14779
+ // dropdownOpen and the fullscreen transition flag.
14780
+ if (!this.componentHasFocus && !this.dropdownOpen && !this._inFullscreenTransition) {
14637
14781
  this.validate();
14638
14782
  }
14639
14783
 
@@ -14648,6 +14792,22 @@ class AuroCombobox extends AuroElement {
14648
14792
  this.hideBib();
14649
14793
  }
14650
14794
 
14795
+ // iOS virtual keyboard retention: when in fullscreen mode, ensure the
14796
+ // dialog opens and the bib input is focused synchronously within the
14797
+ // input event (user gesture) chain. Without this, Lit's async update
14798
+ // cycle delays showModal() past the user activation window, causing
14799
+ // iOS Safari to dismiss the virtual keyboard when the fullscreen
14800
+ // dialog opens — the user then has to tap the input again to resume
14801
+ // typing.
14802
+ if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
14803
+ if (!this.dropdown.isPopoverVisible) {
14804
+ this.showBib();
14805
+ }
14806
+ if (this.dropdown.isPopoverVisible) {
14807
+ this.setInputFocus();
14808
+ }
14809
+ }
14810
+
14651
14811
  this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
14652
14812
  }
14653
14813
 
@@ -14673,23 +14833,20 @@ class AuroCombobox extends AuroElement {
14673
14833
  }
14674
14834
 
14675
14835
  if (evt.key === 'Tab' && this.dropdown.isPopoverVisible) {
14676
- if (this.dropdown.isBibFullscreen) {
14677
-
14678
- // when focus is on the input, move focus back to close button with Tab key
14679
- if (document.activeElement.shadowRoot.activeElement === this.inputInBib) {
14680
- evt.preventDefault();
14681
- this.dropdown.focus();
14682
- }
14683
- } else {
14836
+ // Non-fullscreen (combobox pattern per WAI-ARIA APG):
14837
+ // Tab accepts the focused option and closes the popup, moving focus
14838
+ // to the next focusable element on the page.
14839
+ // https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
14840
+ //
14841
+ // Fullscreen (dialog / modal pattern): Tab navigates between
14842
+ // focusable elements inside the modal (e.g. the close button) via
14843
+ // the native focus trap provided by showModal(). The dropdown is
14844
+ // closed by the close button or Escape instead.
14845
+ if (!this.dropdown.isBibFullscreen) {
14684
14846
  if (this.menu.optionActive && this.menu.optionActive.value) {
14685
14847
  this.menu.value = this.menu.optionActive.value;
14686
14848
  }
14687
-
14688
- setTimeout(() => {
14689
- if (!this.componentHasFocus) {
14690
- this.hideBib();
14691
- }
14692
- }, 0);
14849
+ this.hideBib();
14693
14850
  }
14694
14851
  }
14695
14852
 
@@ -14720,6 +14877,14 @@ class AuroCombobox extends AuroElement {
14720
14877
  });
14721
14878
 
14722
14879
  this.addEventListener('auroFormElement-validated', (evt) => {
14880
+ // During the fullscreen transition, child elements (auro-input) may fire
14881
+ // their own validation events when the trigger becomes inert and loses
14882
+ // focus. Those composed events bubble up through shadow DOM boundaries
14883
+ // and would incorrectly set combobox validity. Ignore them.
14884
+ if (this._inFullscreenTransition) {
14885
+ return;
14886
+ }
14887
+
14723
14888
  this.input.validity = evt.detail.validity;
14724
14889
  this.input.errorMessage = evt.detail.message;
14725
14890
  this.validity = evt.detail.validity;
@@ -14823,6 +14988,9 @@ class AuroCombobox extends AuroElement {
14823
14988
  * @param {boolean} [force=false] - Whether to force validation.
14824
14989
  */
14825
14990
  validate(force = false) {
14991
+ if (this._inFullscreenTransition) {
14992
+ return;
14993
+ }
14826
14994
  this.validation.validate(this, force);
14827
14995
  }
14828
14996
 
@@ -14876,7 +15044,13 @@ class AuroCombobox extends AuroElement {
14876
15044
  }
14877
15045
 
14878
15046
  if (changedProperties.has('availableOptions')) {
14879
- if ((this.availableOptions.length > 0 && this.componentHasFocus) || this.menu.loading || (this.availableOptions.length === 0 && this.noMatchOption)) {
15047
+ // dropdownOpen is set synchronously by the auroDropdown-toggled event
15048
+ // handler during showBib() → floater.showBib() → dispatchEventDropdownToggle(),
15049
+ // so it's already true by the time updated() runs. This prevents the else
15050
+ // branch from calling hideBib() when the dropdown was just opened but
15051
+ // :focus-within hasn't propagated through the top-layer dialog's nested
15052
+ // shadow DOM boundaries.
15053
+ if ((this.availableOptions.length > 0 && (this.componentHasFocus || this.dropdownOpen)) || this.menu.loading || (this.availableOptions.length === 0 && this.noMatchOption)) {
14880
15054
  this.showBib();
14881
15055
  } else {
14882
15056
  this.hideBib();
@@ -15051,6 +15225,7 @@ class AuroCombobox extends AuroElement {
15051
15225
  <slot @slotchange="${this.handleSlotChange}"></slot>
15052
15226
  <${this.inputTag}
15053
15227
  id="inputInBib"
15228
+ autofocus
15054
15229
  @input="${this.handleInputValueChange}"
15055
15230
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
15056
15231
  .a11yControls=${`${this.dropdownId}-floater-bib`}
@@ -15389,7 +15564,11 @@ class AuroMenuOption extends AuroElement {
15389
15564
 
15390
15565
  // Generate unique ID if not already set (required for aria-activedescendant)
15391
15566
  if (!this.id) {
15392
- this.id = `menuoption-${Math.random().toString(36).slice(2, 8)}`;
15567
+ const idBase = 36;
15568
+ const sliceStart = 2;
15569
+ const sliceEnd = 8;
15570
+ this.id = `menuoption-${Math.random().toString(idBase).
15571
+ slice(sliceStart, sliceEnd)}`;
15393
15572
  }
15394
15573
 
15395
15574
  this.setAttribute('role', 'option');
@@ -16296,7 +16475,7 @@ class MenuService {
16296
16475
 
16297
16476
  const MenuContext = n('menu-context');
16298
16477
 
16299
- /* eslint-disable no-underscore-dangle, curly */
16478
+ /* eslint-disable no-underscore-dangle */
16300
16479
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
16301
16480
  // See LICENSE in the project root for license information.
16302
16481
 
@@ -16317,7 +16496,7 @@ const MenuContext = n('menu-context');
16317
16496
  * @slot - Slot for insertion of menu options.
16318
16497
  */
16319
16498
 
16320
- /* eslint-disable no-magic-numbers, max-lines, no-extra-parens */
16499
+ /* eslint-disable max-lines */
16321
16500
 
16322
16501
  class AuroMenu extends AuroElement {
16323
16502