@aurodesignsystem/auro-formkit 6.0.0 → 6.0.1

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 (46) hide show
  1. package/CHANGELOG.md +5 -240
  2. package/components/checkbox/demo/customize.min.js +1 -1
  3. package/components/checkbox/demo/getting-started.min.js +1 -1
  4. package/components/checkbox/demo/index.min.js +1 -1
  5. package/components/checkbox/dist/index.js +1 -1
  6. package/components/checkbox/dist/registered.js +1 -1
  7. package/components/combobox/demo/customize.min.js +172 -46
  8. package/components/combobox/demo/getting-started.min.js +172 -46
  9. package/components/combobox/demo/index.min.js +172 -46
  10. package/components/combobox/dist/index.js +172 -46
  11. package/components/combobox/dist/registered.js +172 -46
  12. package/components/counter/demo/customize.min.js +59 -7
  13. package/components/counter/demo/index.min.js +59 -7
  14. package/components/counter/dist/index.js +59 -7
  15. package/components/counter/dist/registered.js +59 -7
  16. package/components/datepicker/demo/customize.min.js +106 -18
  17. package/components/datepicker/demo/index.min.js +106 -18
  18. package/components/datepicker/dist/index.js +106 -18
  19. package/components/datepicker/dist/registered.js +106 -18
  20. package/components/dropdown/demo/customize.min.js +58 -6
  21. package/components/dropdown/demo/getting-started.min.js +58 -6
  22. package/components/dropdown/demo/index.min.js +58 -6
  23. package/components/dropdown/dist/auro-dropdown.d.ts +16 -1
  24. package/components/dropdown/dist/index.js +58 -6
  25. package/components/dropdown/dist/registered.js +58 -6
  26. package/components/form/demo/customize.min.js +445 -91
  27. package/components/form/demo/getting-started.min.js +445 -91
  28. package/components/form/demo/index.min.js +445 -91
  29. package/components/form/demo/registerDemoDeps.min.js +445 -91
  30. package/components/input/demo/customize.min.js +47 -11
  31. package/components/input/demo/getting-started.min.js +47 -11
  32. package/components/input/demo/index.min.js +47 -11
  33. package/components/input/dist/index.js +47 -11
  34. package/components/input/dist/registered.js +47 -11
  35. package/components/radio/demo/customize.min.js +1 -1
  36. package/components/radio/demo/getting-started.min.js +1 -1
  37. package/components/radio/demo/index.min.js +1 -1
  38. package/components/radio/dist/index.js +1 -1
  39. package/components/radio/dist/registered.js +1 -1
  40. package/components/select/demo/customize.min.js +59 -7
  41. package/components/select/demo/getting-started.min.js +59 -7
  42. package/components/select/demo/index.min.js +59 -7
  43. package/components/select/dist/index.js +59 -7
  44. package/components/select/dist/registered.js +59 -7
  45. package/custom-elements.json +1500 -1484
  46. package/package.json +1 -1
@@ -4904,7 +4904,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
4904
4904
  }
4905
4905
  };
4906
4906
 
4907
- var formkitVersion$2 = '202607102309';
4907
+ var formkitVersion$2 = '202607161943';
4908
4908
 
4909
4909
  let AuroElement$2 = class AuroElement extends LitElement {
4910
4910
  static get properties() {
@@ -5033,7 +5033,6 @@ class AuroDropdown extends AuroElement$2 {
5033
5033
  static get shadowRootOptions() {
5034
5034
  return {
5035
5035
  ...AuroElement$2.shadowRootOptions,
5036
- delegatesFocus: true,
5037
5036
  };
5038
5037
  }
5039
5038
 
@@ -5045,6 +5044,15 @@ class AuroDropdown extends AuroElement$2 {
5045
5044
  this.matchWidth = false;
5046
5045
  this.noHideOnThisFocusLoss = false;
5047
5046
 
5047
+ /**
5048
+ * When true, the dropdown skips its generic focus restoration on close.
5049
+ * Set by consumers (e.g. combobox) that manage their own focus routing
5050
+ * via setClearBtnFocus / setInputFocus / keyboard strategy.
5051
+ * Separate from noHideOnThisFocusLoss (which controls auto-close behavior).
5052
+ * @private
5053
+ */
5054
+ this.noFocusRestoreOnClose = false;
5055
+
5048
5056
  this.errorMessage = undefined; // TODO - check with Doug if there is still more to do here
5049
5057
 
5050
5058
  // Layout Config
@@ -5200,7 +5208,49 @@ class AuroDropdown extends AuroElement$2 {
5200
5208
  focusables[0].focus();
5201
5209
  }
5202
5210
  } else {
5211
+ this.focusTrigger();
5212
+ }
5213
+ }
5214
+
5215
+ /**
5216
+ * Focus the trigger content. When the trigger wrapper itself is focusable
5217
+ * (has tabindex), focus it directly. Otherwise, focus the first focusable
5218
+ * element slotted into the trigger slot (e.g. the auro-input).
5219
+ * @private
5220
+ */
5221
+ focusTrigger() {
5222
+ if (this.trigger.hasAttribute('tabindex')) {
5203
5223
  this.trigger.focus();
5224
+ } else {
5225
+ // Slotted content isn't a DOM child of the trigger div, so
5226
+ // getFocusableElements can't find it. Query assigned nodes directly.
5227
+ const slot = this.trigger.querySelector('slot[name="trigger"]');
5228
+ if (slot) {
5229
+ const assigned = slot.assignedElements();
5230
+ for (const el of assigned) {
5231
+ if (el.hasAttribute('disabled')) {
5232
+ continue;
5233
+ }
5234
+ // Try finding a focusable descendant first (handles non-focusable
5235
+ // wrappers like <div> containing a <button>). If none found, try
5236
+ // focusing the element directly (works for custom elements like
5237
+ // auro-input that have delegatesFocus or a custom focus() method).
5238
+ const descendants = getFocusableElements(el);
5239
+ if (descendants.length > 0) {
5240
+ descendants[0].focus();
5241
+ return;
5242
+ }
5243
+ el.focus();
5244
+ if (document.activeElement === el) {
5245
+ return;
5246
+ }
5247
+ }
5248
+ }
5249
+ // Fallback: try DOM children (non-slotted content)
5250
+ const focusables = getFocusableElements(this.trigger);
5251
+ if (focusables.length > 0) {
5252
+ focusables[0].focus();
5253
+ }
5204
5254
  }
5205
5255
  }
5206
5256
 
@@ -5653,15 +5703,17 @@ class AuroDropdown extends AuroElement$2 {
5653
5703
  }
5654
5704
 
5655
5705
  const eventType = event.detail.eventType || "unknown";
5656
- if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
5657
- this.trigger.focus();
5706
+ if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown" && !this.noFocusRestoreOnClose) {
5707
+ this.focusTrigger();
5658
5708
  }
5659
5709
 
5660
5710
 
5661
5711
  // On Tab-driven close (eventType "focusloss"), let focus advance naturally
5662
5712
  // — restoring to the trigger would trap the user on this dropdown, forcing
5663
5713
  // an extra Tab to move on. Escape and outside-click still restore.
5664
- if (!this.isPopoverVisible && eventType !== "focusloss") {
5714
+ // When noFocusRestoreOnClose is true, the consumer (e.g. combobox) manages
5715
+ // its own focus restoration — skip the generic trigger focus to avoid races.
5716
+ if (!this.isPopoverVisible && eventType !== "focusloss" && !this.noFocusRestoreOnClose) {
5665
5717
  // wait til the bib gets fully closed and rendered
5666
5718
  setTimeout(() => {
5667
5719
  // Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
@@ -5673,7 +5725,7 @@ class AuroDropdown extends AuroElement$2 {
5673
5725
  return;
5674
5726
  }
5675
5727
  // Restore focus to the trigger.
5676
- this.trigger.focus();
5728
+ this.focusTrigger();
5677
5729
  });
5678
5730
  }
5679
5731
  }
@@ -16847,9 +16899,10 @@ class DomHandler {
16847
16899
  class BaseInput extends AuroElement$1 {
16848
16900
 
16849
16901
  // Delegate focus to the native <input> inside the shadow root so that
16850
- // showModal()'s dialog focusing steps reach the input element.
16851
- // This keeps the mobile virtual keyboard open when the fullscreen dialog
16852
- // opens, because the browser sees an input-to-input focus transfer.
16902
+ // clicking anywhere on the input wrapper focuses the native input, and
16903
+ // programmatic .focus() calls on elements in the shadow DOM work correctly.
16904
+ // The initial-load focus theft was caused by the static `autofocus`
16905
+ // attribute on the bib input (now conditional), not by delegatesFocus.
16853
16906
  static get shadowRootOptions() {
16854
16907
  return {
16855
16908
  ...AuroElement$1.shadowRootOptions,
@@ -18526,7 +18579,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
18526
18579
  }
18527
18580
  };
18528
18581
 
18529
- var formkitVersion$1 = '202607102309';
18582
+ var formkitVersion$1 = '202607161943';
18530
18583
 
18531
18584
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18532
18585
  // See LICENSE in the project root for license information.
@@ -18812,15 +18865,50 @@ class AuroInput extends BaseInput {
18812
18865
  * @returns {void}
18813
18866
  */
18814
18867
  checkDisplayValueSlotChange() {
18815
- // flatten:true resolves through auro-combobox's forwarding slot
18816
- // (<slot name="displayValue" slot="displayValue">) so a clone appended
18817
- // directly to auro-input's light DOM alongside the forwarder still
18818
- // counts as content. The prior nodes[0].tagName === 'SLOT' recursion
18819
- // discarded any siblings past the forwarder.
18820
- const slot = this.shadowRoot.querySelector('slot[name="displayValue"]');
18868
+ const slot = this.shadowRoot?.querySelector('slot[name="displayValue"]');
18869
+ if (!slot) {
18870
+ // Shadow slot not yet rendered fall back to checking light-DOM
18871
+ // children so hasDisplayValueContent isn't stuck false when called
18872
+ // before firstUpdated (e.g. from auro-combobox during mount).
18873
+ // Check for any element (including custom elements like <auro-icon>
18874
+ // that render via shadow DOM with no text content).
18875
+ const lightDomNodes = Array.from(this.querySelectorAll('[slot="displayValue"]:not(slot)'));
18876
+ const hasContent = lightDomNodes.some((node) => (
18877
+ node.textContent.trim().length > 0 ||
18878
+ node.children.length > 0 ||
18879
+ node.shadowRoot !== null
18880
+ ));
18881
+ if (this.hasDisplayValueContent !== hasContent) {
18882
+ this.hasDisplayValueContent = hasContent;
18883
+ this.requestUpdate();
18884
+ }
18885
+ return;
18886
+ }
18821
18887
  const nodes = slot.assignedNodes({ flatten: true });
18822
18888
 
18823
- this.hasDisplayValueContent = nodes.length > 0;
18889
+ // Check for actual visible content, not just node existence.
18890
+ // An empty <span slot="displayValue"></span> forwarded from the
18891
+ // consumer should not be treated as "has content" — it causes the
18892
+ // displayValue wrapper to render (with hasContent class) but show
18893
+ // nothing, blocking the combobox's synthetic displayValue from
18894
+ // being visible on preset/deeplink load.
18895
+ // Custom elements (e.g. <auro-icon>) that render via shadow DOM are
18896
+ // treated as content even when they have no text or light-DOM children.
18897
+ const hasContent = nodes.some((node) => {
18898
+ if (node.nodeType === Node.TEXT_NODE) {
18899
+ return node.textContent.trim().length > 0;
18900
+ }
18901
+ if (node.nodeType === Node.ELEMENT_NODE) {
18902
+ return node.textContent.trim().length > 0 ||
18903
+ node.children.length > 0 ||
18904
+ node.shadowRoot !== null;
18905
+ }
18906
+ return false;
18907
+ });
18908
+ if (this.hasDisplayValueContent !== hasContent) {
18909
+ this.hasDisplayValueContent = hasContent;
18910
+ this.requestUpdate();
18911
+ }
18824
18912
  }
18825
18913
 
18826
18914
  firstUpdated() {
@@ -19647,7 +19735,7 @@ class AuroBibtemplate extends LitElement {
19647
19735
  }
19648
19736
  }
19649
19737
 
19650
- var formkitVersion = '202607102309';
19738
+ var formkitVersion = '202607161943';
19651
19739
 
19652
19740
  var styleCss$1 = css`.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}`;
19653
19741
 
@@ -20684,14 +20772,34 @@ class AuroCombobox extends AuroElement {
20684
20772
  const displayValueEl = this.menu.optionSelected.querySelector("[slot='displayValue']");
20685
20773
  if (displayValueEl) {
20686
20774
  this.input.appendChild(displayValueEl.cloneNode(true));
20687
- // auro-input's hasDisplayValueContent is non-reactive; nudge it to
20688
- // re-evaluate the slot and re-render so the displayValue wrapper
20689
- // gets its hasContent class. Without this, the apple/icon stays
20690
- // hidden when input.value is set in the same tick as the append.
20691
- if (typeof this.input.checkDisplayValueSlotChange === 'function') {
20692
- this.input.checkDisplayValueSlotChange();
20693
- this.input.requestUpdate();
20694
- }
20775
+ }
20776
+
20777
+ // auro-input's hasDisplayValueContent is non-reactive; nudge it to
20778
+ // re-evaluate the slot and re-render so the displayValue wrapper
20779
+ // gets its hasContent class. Without this, the apple/icon stays
20780
+ // hidden when input.value is set in the same tick as the append.
20781
+ if (typeof this.input.checkDisplayValueSlotChange === 'function') {
20782
+ this.input.checkDisplayValueSlotChange();
20783
+ }
20784
+ } else if (this.value) {
20785
+ // No optionSelected yet (e.g. setMenuValue was called but the menu's
20786
+ // auroMenu-selectedOption event hasn't fired yet, or we're on remount
20787
+ // with no options loaded). Synthesize a displayValue from the value so
20788
+ // the overlay isn't blank.
20789
+ //
20790
+ // NOTE: This renders the raw machine value (e.g. "SEA"), not the
20791
+ // human-readable label (e.g. "Seattle, WA"). For consumers where
20792
+ // value ≠ label, this produces a brief flash of the raw value until
20793
+ // the option elements load and updateTriggerTextDisplay re-runs with
20794
+ // the correct label. This is intentionally preferred over a blank
20795
+ // state — the displayValueInTrigger.remove() cleanup above ensures
20796
+ // this placeholder is replaced once the real option is available.
20797
+ const syntheticDV = document.createElement('span');
20798
+ syntheticDV.setAttribute('slot', 'displayValue');
20799
+ syntheticDV.textContent = this.value;
20800
+ this.input.appendChild(syntheticDV);
20801
+ if (typeof this.input.checkDisplayValueSlotChange === 'function') {
20802
+ this.input.checkDisplayValueSlotChange();
20695
20803
  }
20696
20804
  }
20697
20805
 
@@ -20864,6 +20972,10 @@ class AuroCombobox extends AuroElement {
20864
20972
  // from combobox's light DOM and won't be detected by dropdown's contains() check.
20865
20973
  this.dropdown.noHideOnThisFocusLoss = true;
20866
20974
 
20975
+ // Suppress the dropdown's generic focus restoration on close — the combobox
20976
+ // manages its own focus via setClearBtnFocus / setInputFocus / keyboard strategy.
20977
+ this.dropdown.noFocusRestoreOnClose = true;
20978
+
20867
20979
  // Listen for the ID to be added to the dropdown so we can capture it and use it for accessibility.
20868
20980
  this.dropdown.addEventListener('auroDropdown-idAdded', (event) => {
20869
20981
  this.dropdownId = event.detail.id;
@@ -20937,7 +21049,11 @@ class AuroCombobox extends AuroElement {
20937
21049
  // after the dropdown layout settles.
20938
21050
 
20939
21051
  doubleRaf(() => {
20940
- this.setInputFocus();
21052
+ // Guard: skip if the dropdown closed before this rAF fired
21053
+ // (e.g. user selected an option immediately after the bib opened).
21054
+ if (this.dropdownOpen) {
21055
+ this.setInputFocus();
21056
+ }
20941
21057
  if (this._inFullscreenTransition) {
20942
21058
  this._inFullscreenTransition = false;
20943
21059
  }
@@ -20984,7 +21100,9 @@ class AuroCombobox extends AuroElement {
20984
21100
  }
20985
21101
 
20986
21102
  this._scheduleTimer(() => {
20987
- this.setInputFocus();
21103
+ if (this.dropdown.isPopoverVisible) {
21104
+ this.setInputFocus();
21105
+ }
20988
21106
  }, 0);
20989
21107
  });
20990
21108
  }
@@ -20993,14 +21111,19 @@ class AuroCombobox extends AuroElement {
20993
21111
  * @private
20994
21112
  */
20995
21113
  setClearBtnFocus() {
20996
- const clearBtn = this.input.shadowRoot.querySelector('.clearBtn');
20997
- if (clearBtn) {
20998
- // Wait for the element to fully render across
20999
- // multiple Lit update cycles before moving focus
21000
- doubleRaf(() => {
21114
+ doubleRaf(() => {
21115
+ // First establish focus inside the input's shadow root.
21116
+ // Without this, clearBtn.focus() silently fails when focus is on
21117
+ // document.body (e.g. after a click selection closes the bib dialog).
21118
+ // delegatesFocus on BaseInput routes this to the native <input>.
21119
+ this.input.focus();
21120
+
21121
+ // Now move focus to the clear button within the same shadow root.
21122
+ const clearBtn = this.input.shadowRoot.querySelector('.clearBtn');
21123
+ if (clearBtn) {
21001
21124
  clearBtn.focus();
21002
- });
21003
- }
21125
+ }
21126
+ });
21004
21127
  }
21005
21128
 
21006
21129
  /**
@@ -21211,6 +21334,9 @@ class AuroCombobox extends AuroElement {
21211
21334
  // only — fresh user selections take the existing hideBib path.
21212
21335
  if (isEcho && this.menu.optionSelected) {
21213
21336
  this._programmaticFilterRefresh = true;
21337
+ this.updateComplete.then(() => {
21338
+ this._programmaticFilterRefresh = false;
21339
+ });
21214
21340
  }
21215
21341
  });
21216
21342
 
@@ -21463,10 +21589,6 @@ class AuroCombobox extends AuroElement {
21463
21589
  if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
21464
21590
  this.setInputFocus();
21465
21591
  }
21466
-
21467
- if (this._programmaticFilterRefresh) {
21468
- this._programmaticFilterRefresh = false;
21469
- }
21470
21592
  }
21471
21593
 
21472
21594
  /**
@@ -21480,10 +21602,12 @@ class AuroCombobox extends AuroElement {
21480
21602
  inputResolver: (comp, ctx) => (ctx.isModal && comp.inputInBib ? comp.inputInBib : comp.input),
21481
21603
  });
21482
21604
 
21483
- this.addEventListener('focusin', () => {
21605
+ this.addEventListener('focusin', (event) => {
21484
21606
  this.touched = true;
21485
21607
 
21486
- this.focus();
21608
+ if (event.composedPath()[0] === this) {
21609
+ this.focus();
21610
+ }
21487
21611
  });
21488
21612
 
21489
21613
  this.addEventListener('auroFormElement-validated', (evt) => {
@@ -21663,6 +21787,12 @@ class AuroCombobox extends AuroElement {
21663
21787
  // setting the flag unconditionally here masks the user-typed open path.
21664
21788
  if (!this._userTyped) {
21665
21789
  this._programmaticFilterRefresh = true;
21790
+ // Self-clear after this update cycle completes. This collapses
21791
+ // the previous scattered clear-points into one and prevents the
21792
+ // flag from surviving into the next user interaction.
21793
+ this.updateComplete.then(() => {
21794
+ this._programmaticFilterRefresh = false;
21795
+ });
21666
21796
  }
21667
21797
 
21668
21798
  if (this.input.value !== this.value) {
@@ -21752,10 +21882,6 @@ class AuroCombobox extends AuroElement {
21752
21882
  } else if (!this.dropdown.isBibFullscreen) {
21753
21883
  this.hideBib();
21754
21884
  }
21755
-
21756
- if (this._programmaticFilterRefresh) {
21757
- this._programmaticFilterRefresh = false;
21758
- }
21759
21885
  }
21760
21886
 
21761
21887
  if (changedProperties.has('error')) {
@@ -21937,7 +22063,7 @@ class AuroCombobox extends AuroElement {
21937
22063
  <slot @slotchange="${this.handleSlotChange}"></slot>
21938
22064
  <${this.inputTag}
21939
22065
  id="inputInBib"
21940
- autofocus
22066
+ ?autofocus="${this.dropdownOpen && this.dropdown?.isBibFullscreen}"
21941
22067
  @input="${this.handleInputValueChange}"
21942
22068
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
21943
22069
  .a11yControls=${`${this.dropdownId}-floater-bib`}
@@ -1114,7 +1114,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$2 {
1114
1114
  }
1115
1115
  };
1116
1116
 
1117
- var formkitVersion$1 = '202607102309';
1117
+ var formkitVersion$1 = '202607161943';
1118
1118
 
1119
1119
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1120
1120
  // See LICENSE in the project root for license information.
@@ -5280,7 +5280,7 @@ class AuroHelpText extends i$2 {
5280
5280
  }
5281
5281
  }
5282
5282
 
5283
- var formkitVersion = '202607102309';
5283
+ var formkitVersion = '202607161943';
5284
5284
 
5285
5285
  let AuroElement$1 = class AuroElement extends i$2 {
5286
5286
  static get properties() {
@@ -5409,7 +5409,6 @@ class AuroDropdown extends AuroElement$1 {
5409
5409
  static get shadowRootOptions() {
5410
5410
  return {
5411
5411
  ...AuroElement$1.shadowRootOptions,
5412
- delegatesFocus: true,
5413
5412
  };
5414
5413
  }
5415
5414
 
@@ -5421,6 +5420,15 @@ class AuroDropdown extends AuroElement$1 {
5421
5420
  this.matchWidth = false;
5422
5421
  this.noHideOnThisFocusLoss = false;
5423
5422
 
5423
+ /**
5424
+ * When true, the dropdown skips its generic focus restoration on close.
5425
+ * Set by consumers (e.g. combobox) that manage their own focus routing
5426
+ * via setClearBtnFocus / setInputFocus / keyboard strategy.
5427
+ * Separate from noHideOnThisFocusLoss (which controls auto-close behavior).
5428
+ * @private
5429
+ */
5430
+ this.noFocusRestoreOnClose = false;
5431
+
5424
5432
  this.errorMessage = undefined; // TODO - check with Doug if there is still more to do here
5425
5433
 
5426
5434
  // Layout Config
@@ -5576,7 +5584,49 @@ class AuroDropdown extends AuroElement$1 {
5576
5584
  focusables[0].focus();
5577
5585
  }
5578
5586
  } else {
5587
+ this.focusTrigger();
5588
+ }
5589
+ }
5590
+
5591
+ /**
5592
+ * Focus the trigger content. When the trigger wrapper itself is focusable
5593
+ * (has tabindex), focus it directly. Otherwise, focus the first focusable
5594
+ * element slotted into the trigger slot (e.g. the auro-input).
5595
+ * @private
5596
+ */
5597
+ focusTrigger() {
5598
+ if (this.trigger.hasAttribute('tabindex')) {
5579
5599
  this.trigger.focus();
5600
+ } else {
5601
+ // Slotted content isn't a DOM child of the trigger div, so
5602
+ // getFocusableElements can't find it. Query assigned nodes directly.
5603
+ const slot = this.trigger.querySelector('slot[name="trigger"]');
5604
+ if (slot) {
5605
+ const assigned = slot.assignedElements();
5606
+ for (const el of assigned) {
5607
+ if (el.hasAttribute('disabled')) {
5608
+ continue;
5609
+ }
5610
+ // Try finding a focusable descendant first (handles non-focusable
5611
+ // wrappers like <div> containing a <button>). If none found, try
5612
+ // focusing the element directly (works for custom elements like
5613
+ // auro-input that have delegatesFocus or a custom focus() method).
5614
+ const descendants = getFocusableElements(el);
5615
+ if (descendants.length > 0) {
5616
+ descendants[0].focus();
5617
+ return;
5618
+ }
5619
+ el.focus();
5620
+ if (document.activeElement === el) {
5621
+ return;
5622
+ }
5623
+ }
5624
+ }
5625
+ // Fallback: try DOM children (non-slotted content)
5626
+ const focusables = getFocusableElements(this.trigger);
5627
+ if (focusables.length > 0) {
5628
+ focusables[0].focus();
5629
+ }
5580
5630
  }
5581
5631
  }
5582
5632
 
@@ -6029,15 +6079,17 @@ class AuroDropdown extends AuroElement$1 {
6029
6079
  }
6030
6080
 
6031
6081
  const eventType = event.detail.eventType || "unknown";
6032
- if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
6033
- this.trigger.focus();
6082
+ if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown" && !this.noFocusRestoreOnClose) {
6083
+ this.focusTrigger();
6034
6084
  }
6035
6085
 
6036
6086
 
6037
6087
  // On Tab-driven close (eventType "focusloss"), let focus advance naturally
6038
6088
  // — restoring to the trigger would trap the user on this dropdown, forcing
6039
6089
  // an extra Tab to move on. Escape and outside-click still restore.
6040
- if (!this.isPopoverVisible && eventType !== "focusloss") {
6090
+ // When noFocusRestoreOnClose is true, the consumer (e.g. combobox) manages
6091
+ // its own focus restoration — skip the generic trigger focus to avoid races.
6092
+ if (!this.isPopoverVisible && eventType !== "focusloss" && !this.noFocusRestoreOnClose) {
6041
6093
  // wait til the bib gets fully closed and rendered
6042
6094
  setTimeout(() => {
6043
6095
  // Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
@@ -6049,7 +6101,7 @@ class AuroDropdown extends AuroElement$1 {
6049
6101
  return;
6050
6102
  }
6051
6103
  // Restore focus to the trigger.
6052
- this.trigger.focus();
6104
+ this.focusTrigger();
6053
6105
  });
6054
6106
  }
6055
6107
  }
@@ -1114,7 +1114,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$2 {
1114
1114
  }
1115
1115
  };
1116
1116
 
1117
- var formkitVersion$1 = '202607102309';
1117
+ var formkitVersion$1 = '202607161943';
1118
1118
 
1119
1119
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1120
1120
  // See LICENSE in the project root for license information.
@@ -5280,7 +5280,7 @@ class AuroHelpText extends i$2 {
5280
5280
  }
5281
5281
  }
5282
5282
 
5283
- var formkitVersion = '202607102309';
5283
+ var formkitVersion = '202607161943';
5284
5284
 
5285
5285
  let AuroElement$1 = class AuroElement extends i$2 {
5286
5286
  static get properties() {
@@ -5409,7 +5409,6 @@ class AuroDropdown extends AuroElement$1 {
5409
5409
  static get shadowRootOptions() {
5410
5410
  return {
5411
5411
  ...AuroElement$1.shadowRootOptions,
5412
- delegatesFocus: true,
5413
5412
  };
5414
5413
  }
5415
5414
 
@@ -5421,6 +5420,15 @@ class AuroDropdown extends AuroElement$1 {
5421
5420
  this.matchWidth = false;
5422
5421
  this.noHideOnThisFocusLoss = false;
5423
5422
 
5423
+ /**
5424
+ * When true, the dropdown skips its generic focus restoration on close.
5425
+ * Set by consumers (e.g. combobox) that manage their own focus routing
5426
+ * via setClearBtnFocus / setInputFocus / keyboard strategy.
5427
+ * Separate from noHideOnThisFocusLoss (which controls auto-close behavior).
5428
+ * @private
5429
+ */
5430
+ this.noFocusRestoreOnClose = false;
5431
+
5424
5432
  this.errorMessage = undefined; // TODO - check with Doug if there is still more to do here
5425
5433
 
5426
5434
  // Layout Config
@@ -5576,7 +5584,49 @@ class AuroDropdown extends AuroElement$1 {
5576
5584
  focusables[0].focus();
5577
5585
  }
5578
5586
  } else {
5587
+ this.focusTrigger();
5588
+ }
5589
+ }
5590
+
5591
+ /**
5592
+ * Focus the trigger content. When the trigger wrapper itself is focusable
5593
+ * (has tabindex), focus it directly. Otherwise, focus the first focusable
5594
+ * element slotted into the trigger slot (e.g. the auro-input).
5595
+ * @private
5596
+ */
5597
+ focusTrigger() {
5598
+ if (this.trigger.hasAttribute('tabindex')) {
5579
5599
  this.trigger.focus();
5600
+ } else {
5601
+ // Slotted content isn't a DOM child of the trigger div, so
5602
+ // getFocusableElements can't find it. Query assigned nodes directly.
5603
+ const slot = this.trigger.querySelector('slot[name="trigger"]');
5604
+ if (slot) {
5605
+ const assigned = slot.assignedElements();
5606
+ for (const el of assigned) {
5607
+ if (el.hasAttribute('disabled')) {
5608
+ continue;
5609
+ }
5610
+ // Try finding a focusable descendant first (handles non-focusable
5611
+ // wrappers like <div> containing a <button>). If none found, try
5612
+ // focusing the element directly (works for custom elements like
5613
+ // auro-input that have delegatesFocus or a custom focus() method).
5614
+ const descendants = getFocusableElements(el);
5615
+ if (descendants.length > 0) {
5616
+ descendants[0].focus();
5617
+ return;
5618
+ }
5619
+ el.focus();
5620
+ if (document.activeElement === el) {
5621
+ return;
5622
+ }
5623
+ }
5624
+ }
5625
+ // Fallback: try DOM children (non-slotted content)
5626
+ const focusables = getFocusableElements(this.trigger);
5627
+ if (focusables.length > 0) {
5628
+ focusables[0].focus();
5629
+ }
5580
5630
  }
5581
5631
  }
5582
5632
 
@@ -6029,15 +6079,17 @@ class AuroDropdown extends AuroElement$1 {
6029
6079
  }
6030
6080
 
6031
6081
  const eventType = event.detail.eventType || "unknown";
6032
- if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
6033
- this.trigger.focus();
6082
+ if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown" && !this.noFocusRestoreOnClose) {
6083
+ this.focusTrigger();
6034
6084
  }
6035
6085
 
6036
6086
 
6037
6087
  // On Tab-driven close (eventType "focusloss"), let focus advance naturally
6038
6088
  // — restoring to the trigger would trap the user on this dropdown, forcing
6039
6089
  // an extra Tab to move on. Escape and outside-click still restore.
6040
- if (!this.isPopoverVisible && eventType !== "focusloss") {
6090
+ // When noFocusRestoreOnClose is true, the consumer (e.g. combobox) manages
6091
+ // its own focus restoration — skip the generic trigger focus to avoid races.
6092
+ if (!this.isPopoverVisible && eventType !== "focusloss" && !this.noFocusRestoreOnClose) {
6041
6093
  // wait til the bib gets fully closed and rendered
6042
6094
  setTimeout(() => {
6043
6095
  // Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
@@ -6049,7 +6101,7 @@ class AuroDropdown extends AuroElement$1 {
6049
6101
  return;
6050
6102
  }
6051
6103
  // Restore focus to the trigger.
6052
- this.trigger.focus();
6104
+ this.focusTrigger();
6053
6105
  });
6054
6106
  }
6055
6107
  }