@aurodesignsystem-dev/auro-formkit 0.0.0-pr1408.16 → 0.0.0-pr1408.18

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 (48) 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 +187 -141
  6. package/components/combobox/demo/index.min.js +187 -141
  7. package/components/combobox/demo/keyboardBehavior.md +9 -36
  8. package/components/combobox/dist/auro-combobox.d.ts +17 -0
  9. package/components/combobox/dist/comboboxKeyboardStrategy.d.ts +6 -3
  10. package/components/combobox/dist/index.js +187 -141
  11. package/components/combobox/dist/registered.js +187 -141
  12. package/components/counter/demo/api.min.js +2 -2
  13. package/components/counter/demo/index.min.js +2 -2
  14. package/components/counter/dist/index.js +2 -2
  15. package/components/counter/dist/registered.js +2 -2
  16. package/components/datepicker/demo/api.md +1 -1
  17. package/components/datepicker/demo/api.min.js +77 -157
  18. package/components/datepicker/demo/index.min.js +77 -157
  19. package/components/datepicker/demo/keyboardBehavior.md +1 -6
  20. package/components/datepicker/dist/auro-datepicker.d.ts +8 -7
  21. package/components/datepicker/dist/datepickerKeyboardStrategy.d.ts +1 -4
  22. package/components/datepicker/dist/index.js +77 -157
  23. package/components/datepicker/dist/registered.js +77 -157
  24. package/components/dropdown/demo/api.min.js +1 -1
  25. package/components/dropdown/demo/index.min.js +1 -1
  26. package/components/dropdown/dist/index.js +1 -1
  27. package/components/dropdown/dist/registered.js +1 -1
  28. package/components/form/demo/api.min.js +382 -390
  29. package/components/form/demo/index.min.js +382 -390
  30. package/components/input/demo/api.min.js +106 -77
  31. package/components/input/demo/index.min.js +106 -77
  32. package/components/input/dist/auro-input.d.ts +11 -0
  33. package/components/input/dist/base-input.d.ts +1 -0
  34. package/components/input/dist/index.js +36 -18
  35. package/components/input/dist/registered.js +36 -18
  36. package/components/menu/demo/keyboardBehavior.md +0 -0
  37. package/components/radio/demo/api.min.js +1 -1
  38. package/components/radio/demo/index.min.js +1 -1
  39. package/components/radio/demo/keyboardBehavior.md +0 -0
  40. package/components/radio/dist/index.js +1 -1
  41. package/components/radio/dist/registered.js +1 -1
  42. package/components/select/demo/api.min.js +13 -4
  43. package/components/select/demo/index.min.js +13 -4
  44. package/components/select/dist/index.js +13 -4
  45. package/components/select/dist/registered.js +13 -4
  46. package/custom-elements.json +1449 -1401
  47. package/package.json +3 -3
  48. /package/components/datepicker/demo/{keyboardBehavior.html → keyboard-behavior.html} +0 -0
@@ -1125,7 +1125,13 @@ function guardTouchPassthrough(element) {
1125
1125
  }
1126
1126
 
1127
1127
  /**
1128
- * Restores the dropdown trigger after a fullscreen dialog closes.
1128
+ * Restores the dropdown trigger after a fullscreen dialog closes
1129
+ * when focus doesn't leave the component (e.g. using Esc or Enter keys).
1130
+ * When leaving the component (e.g., tabbing out of the combobox after closing
1131
+ * the fullscreen dialog), focus restoration is handled by the browser's native
1132
+ * dialog focus restoration behavior, so this function only restores focus
1133
+ * when focus remains inside the component after the dialog closes.
1134
+
1129
1135
  *
1130
1136
  * Removes the `inert` attribute from the trigger so it is accessible again,
1131
1137
  * and restores focus to the given target after one animation frame. The rAF
@@ -1141,8 +1147,11 @@ function guardTouchPassthrough(element) {
1141
1147
  function restoreTriggerAfterClose(dropdown, focusTarget) {
1142
1148
  dropdown.trigger.inert = false;
1143
1149
 
1150
+ // Wait a frame so that dialog.close() has completed and the browser's
1151
+ // native focus restoration has run before we attempt to focus the
1152
+ // trigger / input programmatically.
1144
1153
  requestAnimationFrame(() => {
1145
- if (!dropdown.isPopoverVisible) {
1154
+ if (!dropdown.isPopoverVisible && dropdown.trigger.contains(document.activeElement)) {
1146
1155
  focusTarget.focus();
1147
1156
  }
1148
1157
  });
@@ -1249,102 +1258,33 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
1249
1258
  if (!clearBtn) {
1250
1259
  return false;
1251
1260
  }
1252
- return Boolean(clearBtn.shadowRoot && clearBtn.shadowRoot.activeElement !== null);
1261
+ const isFocused = Boolean(clearBtn.shadowRoot && clearBtn.shadowRoot.activeElement !== null);
1262
+ return isFocused;
1253
1263
  }
1254
1264
 
1255
1265
  const comboboxKeyboardStrategy = {
1256
- async Enter(component, evt, ctx) {
1257
- // If the clear button has focus, let the browser activate it normally.
1258
- // stopPropagation prevents parent containers (e.g., forms) from treating
1259
- // Enter as a submit, but we must NOT call preventDefault — that would
1260
- // block the browser's built-in "Enter activates focused button" behavior.
1266
+ ArrowDown(component, evt, ctx) {
1267
+ // If the clear button has focus, let the browser handle ArrowDown normally.
1261
1268
  if (isClearBtnFocused(ctx)) {
1262
- evt.stopPropagation();
1263
1269
  return;
1264
1270
  }
1265
1271
 
1266
- if (ctx.isExpanded && component.optionActive) {
1267
- component.menu.makeSelection();
1268
- await component.updateComplete;
1269
- evt.preventDefault();
1270
- evt.stopPropagation();
1271
- component.setClearBtnFocus();
1272
- } else {
1273
- // Prevent the keypress from bubbling to parent containers (e.g., forms)
1274
- // which could interpret Enter as a submit or trigger other unintended behavior.
1275
- // This is safe because showBib() opens the dialog programmatically,
1276
- // not via event propagation.
1277
- evt.preventDefault();
1278
- evt.stopPropagation();
1279
- component.showBib();
1280
- }
1281
- },
1282
-
1283
- Tab(component, evt, ctx) {
1284
- if (!ctx.isExpanded) {
1285
- return;
1286
- }
1287
-
1288
- // Shift+Tab moves the highlight to the first non-disabled option
1289
- // without making a selection or closing the bib.
1290
- if (evt.shiftKey) {
1272
+ // option display and navigation are prevented if there are no available options
1273
+ if (component.availableOptions.length > 0) {
1291
1274
  evt.preventDefault();
1292
- const firstActive = component.menu.menuService.menuOptions.find((option) => option.isActive);
1293
- if (firstActive) {
1294
- component.menu.updateActiveOption(firstActive);
1295
- }
1296
- return;
1297
- }
1298
1275
 
1299
- if (ctx.isModal) {
1300
- if (!ctx.activeInput) {
1301
- return;
1302
- }
1303
- const clearBtn = getClearBtn(ctx);
1304
- const clearBtnHasFocus = isClearBtnFocused(ctx, clearBtn);
1305
-
1306
- // Tab from input: if clear button exists and doesn't have focus, focus it
1307
- if (clearBtn && !clearBtnHasFocus && ctx.activeInput.value) {
1308
- // Force clear button container visible to work around Safari not
1309
- // propagating :focus-within through shadow DOM boundaries, which
1310
- // causes .wrapper:not(:focus-within) to hide .notification.clear.
1311
- const clearContainer = clearBtn.closest('.clear');
1312
- if (clearContainer) {
1313
- clearContainer.style.display = 'flex';
1314
- clearBtn.addEventListener('focusout', () => {
1315
- // Delay cleanup so :focus-within settles when focus moves
1316
- // to a sibling (e.g., Shift+Tab back to the input).
1317
- requestAnimationFrame(() => {
1318
- clearContainer.style.display = '';
1319
- });
1320
- }, { once: true });
1321
- }
1276
+ // navigate if bib is open otherwise open it
1277
+ if (component.dropdown.isPopoverVisible) {
1322
1278
 
1323
- // Focus the native button inside auro-button so the browser
1324
- // treats it as a real focusable element inside the dialog.
1325
- const nativeBtn = clearBtn.shadowRoot && clearBtn.shadowRoot.querySelector('button');
1326
- if (nativeBtn) {
1327
- nativeBtn.focus();
1279
+ if (evt.altKey || evt.metaKey) {
1280
+ component.activateLastEnabledAvailableOption();
1328
1281
  } else {
1329
- clearBtn.focus();
1282
+ navigateArrow(component, 'down');
1330
1283
  }
1331
- return;
1332
- }
1333
-
1334
- // Tab from clear button (or no clear button / no value) →
1335
- // select the highlighted option if any, then close
1336
- if (component.optionActive) {
1337
- component.menu.makeSelection();
1284
+ } else {
1285
+ component.showBib();
1338
1286
  }
1339
- component.hideBib();
1340
- return;
1341
- }
1342
-
1343
- // Non-fullscreen: select + close
1344
- if (component.menu.optionActive && component.menu.optionActive.value) {
1345
- component.menu.value = component.menu.optionActive.value;
1346
1287
  }
1347
- component.hideBib();
1348
1288
  },
1349
1289
 
1350
1290
  ArrowUp(component, evt, ctx) {
@@ -1353,31 +1293,85 @@ const comboboxKeyboardStrategy = {
1353
1293
  return;
1354
1294
  }
1355
1295
 
1296
+ // option display and navigation are prevented if there are no available options
1356
1297
  if (component.availableOptions.length > 0) {
1357
- component.showBib();
1298
+ evt.preventDefault();
1299
+
1300
+ // navigate if bib is open otherwise open it
1301
+ if (component.dropdown.isPopoverVisible) {
1302
+ if (evt.altKey || evt.metaKey) {
1303
+ component.activateFirstEnabledAvailableOption();
1304
+ } else {
1305
+ navigateArrow(component, 'up');
1306
+ }
1307
+ } else {
1308
+ component.showBib();
1309
+ }
1358
1310
  }
1359
- // Read live visibility — ctx.isExpanded was computed before showBib() above,
1360
- // so it wouldn't reflect the state change.
1361
- if (component.dropdown.isPopoverVisible) {
1311
+ },
1312
+
1313
+ End(component, evt, ctx) {
1314
+ if (ctx.isExpanded) {
1362
1315
  evt.preventDefault();
1363
- navigateArrow(component, 'up');
1316
+ evt.stopPropagation();
1317
+ component.activateLastEnabledAvailableOption();
1364
1318
  }
1365
1319
  },
1366
1320
 
1367
- ArrowDown(component, evt, ctx) {
1368
- // If the clear button has focus, let the browser handle ArrowDown normally.
1321
+ Enter(component, evt, ctx) {
1369
1322
  if (isClearBtnFocused(ctx)) {
1370
- return;
1371
- }
1323
+ // If the clear button has focus, let the browser activate it normally.
1324
+ // stopPropagation prevents parent containers (e.g., forms) from treating
1325
+ // Enter as a submit, but we must NOT call preventDefault — that would
1326
+ // block the browser's built-in "Enter activates focused button" behavior.
1327
+ evt.stopPropagation();
1328
+ } else if (ctx.isExpanded && component.menu.optionActive) {
1329
+ component.menu.makeSelection();
1372
1330
 
1373
- if (component.availableOptions.length > 0) {
1331
+ if (ctx.isModal) {
1332
+ component.setTriggerInputFocus();
1333
+ }
1334
+
1335
+ evt.preventDefault();
1336
+ evt.stopPropagation();
1337
+ } else {
1338
+ // Prevent the keypress from bubbling to parent containers (e.g., forms)
1339
+ // which could interpret Enter as a submit or trigger other unintended behavior.
1340
+ // This is safe because showBib() opens the dialog programmatically,
1341
+ // not via event propagation.
1342
+ evt.preventDefault();
1343
+ evt.stopPropagation();
1374
1344
  component.showBib();
1375
1345
  }
1376
- // Read live visibility — ctx.isExpanded was computed before showBib() above,
1377
- // so it wouldn't reflect the state change.
1378
- if (component.dropdown.isPopoverVisible) {
1346
+ },
1347
+
1348
+ Escape(component, _evt, ctx) {
1349
+ if (ctx.isExpanded && ctx.isModal) {
1350
+ component.setTriggerInputFocus();
1351
+ }
1352
+ },
1353
+
1354
+ Home(component, evt, ctx) {
1355
+ if (ctx.isExpanded) {
1379
1356
  evt.preventDefault();
1380
- navigateArrow(component, 'down');
1357
+ evt.stopPropagation();
1358
+ component.activateFirstEnabledAvailableOption();
1359
+ }
1360
+ },
1361
+
1362
+ Tab(component, evt, ctx) {
1363
+ if (ctx.isExpanded && !isClearBtnFocused(ctx)) {
1364
+ // ClearBtn will not bubble up tab key events when it's focused, so need to manage it here when focused
1365
+ component.menu.makeSelection();
1366
+ component.hideBib();
1367
+
1368
+ // In fullscreen modal mode, closing the dialog does not
1369
+ // automatically restores focus to the input. In the tab case,
1370
+ // Explicitly move focus to the trigger's clear button so the
1371
+ // user can continues tabbing through the page normally.
1372
+ if (ctx.isModal && !evt.shiftKey) {
1373
+ component.setClearBtnFocus();
1374
+ }
1381
1375
  }
1382
1376
  },
1383
1377
  };
@@ -4983,7 +4977,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$4 {
4983
4977
  }
4984
4978
  };
4985
4979
 
4986
- var formkitVersion$2 = '202604022013';
4980
+ var formkitVersion$2 = '202604032311';
4987
4981
 
4988
4982
  let AuroElement$2 = class AuroElement extends i$4 {
4989
4983
  static get properties() {
@@ -11724,6 +11718,16 @@ class BaseInput extends AuroElement$1 {
11724
11718
  this.wrapperElement = this.shadowRoot.querySelector('.wrapper');
11725
11719
  this.inputElement = this.renderRoot.querySelector('input');
11726
11720
  this.labelElement = this.shadowRoot.querySelector('label');
11721
+ this.clearBtn = this.clearButtonRef.value;
11722
+
11723
+ // This must get moved into inputKeyboardStrategy when implemented
11724
+ this.clearBtn.addEventListener('keydown', (evt) => {
11725
+ evt.stopPropagation();
11726
+ });
11727
+
11728
+ this.clearBtn.addEventListener('click', (evt) => {
11729
+ evt.stopPropagation();
11730
+ });
11727
11731
 
11728
11732
  this.patchInputEvent(this.inputElement);
11729
11733
 
@@ -12747,7 +12751,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$4 {
12747
12751
  }
12748
12752
  };
12749
12753
 
12750
- var formkitVersion$1 = '202604022013';
12754
+ var formkitVersion$1 = '202604032311';
12751
12755
 
12752
12756
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12753
12757
  // See LICENSE in the project root for license information.
@@ -12804,6 +12808,11 @@ class AuroInput extends BaseInput {
12804
12808
  * @private
12805
12809
  */
12806
12810
  this.iconTag = versioning.generateTag('auro-formkit-input-icon', iconVersion$2, _$2);
12811
+
12812
+ /**
12813
+ * @private
12814
+ */
12815
+ this.clearButtonRef = e$1();
12807
12816
  }
12808
12817
 
12809
12818
  static get styles() {
@@ -12821,6 +12830,19 @@ class AuroInput extends BaseInput {
12821
12830
  ];
12822
12831
  }
12823
12832
 
12833
+ /**
12834
+ * Returns classmap configuration for the clear button visibility.
12835
+ * The button is hidden when the input has no value, is read-only, or is disabled.
12836
+ * @private
12837
+ * @returns {Record<string, boolean>} - Classmap object controlling clear button display state.
12838
+ */
12839
+ get clearBtnClassMap() {
12840
+ return {
12841
+ 'util_displayHidden': !this.hasValue || this.readyOnly || this.disabled
12842
+ };
12843
+ }
12844
+
12845
+
12824
12846
  /**
12825
12847
  * Determines if the HTML input element should be visually hidden.
12826
12848
  * Returns true when display value content exists without focus and has a value,
@@ -13140,10 +13162,11 @@ class AuroInput extends BaseInput {
13140
13162
  <${this.buttonTag}
13141
13163
  @click="${this.handleClickClear}"
13142
13164
  appearance="${this.onDark ? 'inverse' : this.appearance}"
13143
- class="notificationBtn clearBtn"
13165
+ class="notificationBtn clearBtn ${e$3(this.clearBtnClassMap)}"
13144
13166
  shape="circle"
13145
13167
  size="sm"
13146
- variant="ghost">
13168
+ variant="ghost"
13169
+ ${n$2(this.clearButtonRef)}>
13147
13170
  <span><slot name="ariaLabel.clear">Clear Input</slot></span>
13148
13171
  <${this.iconTag}
13149
13172
  aria-hidden="true"
@@ -13288,11 +13311,7 @@ class AuroInput extends BaseInput {
13288
13311
  <div part="accent-right" class="accents right">
13289
13312
  ${this.renderValidationErrorIconHtml()}
13290
13313
  ${this.hasValue && this.type === 'password' ? this.renderHtmlNotificationPassword() : undefined}
13291
- ${this.hasValue ? u$7`
13292
- ${!this.disabled && !this.readonly ? u$7`
13293
- ${this.renderHtmlActionClear()}
13294
- ` : undefined}
13295
- ` : undefined}
13314
+ ${this.renderHtmlActionClear()}
13296
13315
  </div>
13297
13316
  </div>
13298
13317
  <div class="helpTextWrapper leftIndent rightIndent" part="inputHelpText">
@@ -13324,11 +13343,7 @@ class AuroInput extends BaseInput {
13324
13343
  ${this.layout.includes('right') || this.layout === "emphasized" ? u$7`
13325
13344
  ${this.renderValidationErrorIconHtml()}
13326
13345
  ` : undefined}
13327
- ${this.hasValue ? u$7`
13328
- ${!this.disabled && !this.readonly ? u$7`
13329
- ${this.renderHtmlActionClear()}
13330
- ` : undefined}
13331
- ` : undefined}
13346
+ ${this.renderHtmlActionClear()}
13332
13347
  </div>
13333
13348
  </div>
13334
13349
  <div class="${e$3(this.helpTextClasses)}" part="inputHelpText">
@@ -13356,11 +13371,7 @@ class AuroInput extends BaseInput {
13356
13371
  </div>
13357
13372
  <div class="accents right">
13358
13373
  ${this.renderValidationErrorIconHtml()}
13359
- ${this.hasValue ? u$7`
13360
- ${!this.disabled && !this.readonly ? u$7`
13361
- ${this.renderHtmlActionClear()}
13362
- ` : undefined}
13363
- ` : undefined}
13374
+ ${this.renderHtmlActionClear()}
13364
13375
  </div>
13365
13376
  </div>
13366
13377
  <div class="helpTextWrapper leftIndent rightIndent" part="inputHelpText">
@@ -13786,7 +13797,7 @@ class AuroBibtemplate extends i$4 {
13786
13797
  }
13787
13798
  }
13788
13799
 
13789
- var formkitVersion = '202604022013';
13800
+ var formkitVersion = '202604032311';
13790
13801
 
13791
13802
  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}`;
13792
13803
 
@@ -14265,6 +14276,7 @@ class AuroCombobox extends AuroElement {
14265
14276
 
14266
14277
  /**
14267
14278
  * Array of available options to display in the dropdown.
14279
+ * This array contains all non-hidden options (e.g., hidden by filtering on input value).
14268
14280
  * @private
14269
14281
  */
14270
14282
  availableOptions: {
@@ -14642,12 +14654,40 @@ class AuroCombobox extends AuroElement {
14642
14654
  AuroLibraryRuntimeUtils$4.prototype.registerComponent(name, AuroCombobox);
14643
14655
  }
14644
14656
 
14657
+ /**
14658
+ * Mark the first available (non-hidden), enabled option as `active`.
14659
+ * @private
14660
+ * @returns {void}
14661
+ */
14662
+ activateFirstEnabledAvailableOption() {
14663
+ const firstEnabledOptionIndex = this.availableOptions.findIndex((opt) => !opt.disabled);
14664
+ this.updateActiveOption(firstEnabledOptionIndex);
14665
+ }
14666
+
14667
+ /**
14668
+ * Mark the last available (non-hidden), enabled option as `active`.
14669
+ * @private
14670
+ * @returns {void}
14671
+ */
14672
+ activateLastEnabledAvailableOption() {
14673
+ let lastEnabledOptionIndex = -1;
14674
+
14675
+ // Work backwards through the available options array to find the last enabled option
14676
+ for (let index = this.availableOptions.length - 1; index >= 0; index -= 1) {
14677
+ if (!this.availableOptions[index].disabled) {
14678
+ lastEnabledOptionIndex = index;
14679
+ break;
14680
+ }
14681
+ }
14682
+
14683
+ this.updateActiveOption(lastEnabledOptionIndex);
14684
+ }
14685
+
14645
14686
  /**
14646
14687
  * Updates the filter for the available options based on the input value.
14647
14688
  * @private
14648
14689
  */
14649
14690
  updateFilter() {
14650
-
14651
14691
  // Reset available options if noFilter is set to false after being true.
14652
14692
  if (this.noFilter) {
14653
14693
  this.availableOptions = [...this.options];
@@ -14766,6 +14806,10 @@ class AuroCombobox extends AuroElement {
14766
14806
  if (this.value && this.input.value && !this.menu.value) {
14767
14807
  this.syncValuesAndStates();
14768
14808
  }
14809
+
14810
+ if (!this.availableOptions.includes(this.menu.optionActive)) {
14811
+ this.activateFirstEnabledAvailableOption();
14812
+ }
14769
14813
  }
14770
14814
 
14771
14815
  /**
@@ -14839,9 +14883,6 @@ class AuroCombobox extends AuroElement {
14839
14883
  if (this.dropdownOpen) {
14840
14884
  const expandedDelay = 150;
14841
14885
  this._expandedTimeout = setTimeout(() => {
14842
- if (!this.value) {
14843
- this.updateActiveOption(0);
14844
- }
14845
14886
  this.triggerExpandedState = true;
14846
14887
  }, expandedDelay);
14847
14888
  } else {
@@ -14851,22 +14892,16 @@ class AuroCombobox extends AuroElement {
14851
14892
  // Clear aria-activedescendant when dropdown closes
14852
14893
  if (!this.dropdownOpen && this.input) {
14853
14894
  this.input.setActiveDescendant(null);
14854
- this.optionActive = null;
14855
-
14856
- // Remove the highlighted state from all menu options so re-opening
14857
- // the dropdown doesn't show a stale highlight.
14858
- if (this.options) {
14859
- this.options.forEach((opt) => {
14860
- opt.active = false;
14861
- opt.classList.remove('active');
14862
- });
14863
- }
14864
14895
 
14865
14896
  // Restore pointer events on the menu in case they were disabled
14866
14897
  // during fullscreen open to prevent touch pass-through.
14867
14898
  this.menu.style.pointerEvents = '';
14868
14899
 
14869
- restoreTriggerAfterClose(this.dropdown, this.input);
14900
+ // When closing a fullscreen bib, restore focus to the trigger so that
14901
+ // keyboard navigation continues from the correct place in the page
14902
+ if (this.dropdown.isBibFullscreen) {
14903
+ restoreTriggerAfterClose(this.dropdown, this.input);
14904
+ }
14870
14905
  }
14871
14906
 
14872
14907
  if (this.dropdownOpen) {
@@ -14901,13 +14936,6 @@ class AuroCombobox extends AuroElement {
14901
14936
  this.setInputFocus();
14902
14937
  this._inFullscreenTransition = false;
14903
14938
  });
14904
- } else {
14905
- // wait a frame in case the bib gets hidden immediately after showing because there is no value
14906
- setTimeout(() => {
14907
- if (this.componentHasFocus) {
14908
- this.setInputFocus();
14909
- }
14910
- }, 0);
14911
14939
  }
14912
14940
  }
14913
14941
  });
@@ -14957,7 +14985,25 @@ class AuroCombobox extends AuroElement {
14957
14985
  setClearBtnFocus() {
14958
14986
  const clearBtn = this.input.shadowRoot.querySelector('.clearBtn');
14959
14987
  if (clearBtn) {
14960
- clearBtn.focus();
14988
+ // Wait for the element to fully render across
14989
+ // multiple Lit update cycles before moving focus
14990
+ doubleRaf(() => {
14991
+ clearBtn.focus();
14992
+ });
14993
+ }
14994
+ }
14995
+
14996
+ /**
14997
+ * @private
14998
+ */
14999
+ setTriggerInputFocus() {
15000
+ const input = this.input.shadowRoot.querySelector('input');
15001
+ if (input) {
15002
+ // Wait for the element to fully render across
15003
+ // multiple Lit update cycles before moving focus
15004
+ doubleRaf(() => {
15005
+ input.focus();
15006
+ });
14961
15007
  }
14962
15008
  }
14963
15009
 
@@ -3,7 +3,7 @@
3
3
  <div class="mainContent">
4
4
  <div class="scrollWrapper">
5
5
  <auro-header level="2" id="tabBehavior">Tab Behavior</auro-header>
6
- <p>The component trigger contains an <code>&lt;auro-input&gt;</code> which has two focusable elements:</p>
6
+ <p>The component trigger contains an <code>&lt;auro-input&gt;</code> which has two elements:</p>
7
7
  <ol>
8
8
  <li><strong>Input</strong></li>
9
9
  <li><strong>Clear button:</strong> only shown when the input has a value.</li>
@@ -11,7 +11,7 @@
11
11
  <p>Each focusable element <em>(when shown)</em> participates in the browser window's default <code>tabindex</code> sequence.</p>
12
12
  <p>When the component is <code>disabled</code> it is removed from the <code>tabindex</code> sequence. VoiceOver's virtual cursor <em>(swipe navigation)</em> can still encounter the component, but standard keyboard <code>Tab</code> navigation skips it.</p>
13
13
  <p>On <strong>large viewport devices</strong> (e.g., desktop browser, tablet) there is no focusable content inside the component bib.</p>
14
- <p>On <strong>small viewport devices</strong> (e.g., phone) the bib opens a modal dialog with a focusable <strong>input</strong> and <strong>clear button</strong> which may be tabbed through naturally.</p>
14
+ <p>On <strong>small viewport devices</strong> (e.g., phone) the bib opens a modal dialog with a focusable <strong>input</strong> and <strong>clear button</strong> which can receive <strong>Click</strong> and <strong>Tap</strong> events.</p>
15
15
  <auro-header level="2" id="keyEvents">Key Events</auro-header>
16
16
  <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../docs/partials/keyEvents.md) -->
17
17
  <!-- The below content is automatically added from ./../docs/partials/keyEvents.md -->
@@ -166,8 +166,8 @@
166
166
  </td>
167
167
  </tr>
168
168
  <tr>
169
- <td rowspan="6">Enter</td>
170
- <td rowspan="6">-</td>
169
+ <td rowspan="4">Enter</td>
170
+ <td rowspan="4">-</td>
171
171
  <td>Collapsed, list options have been populated</td>
172
172
  <td>
173
173
  Trigger input, <strong>NOT</strong> the input clear button
@@ -186,7 +186,7 @@
186
186
  </td>
187
187
  </tr>
188
188
  <tr>
189
- <td rowspan="2">Expanded, large viewport device</td>
189
+ <td>Expanded, large viewport device</td>
190
190
  <td>
191
191
  Trigger input element, <strong>NOT</strong> the trigger input clear button
192
192
  </td>
@@ -195,15 +195,7 @@
195
195
  </td>
196
196
  </tr>
197
197
  <tr>
198
- <td>
199
- Trigger input close button, <strong>NOT</strong> the trigger input
200
- </td>
201
- <td>
202
- The input value is cleared and <strong>focus</strong> is moved to the trigger input element.
203
- </td>
204
- </tr>
205
- <tr>
206
- <td rowspan="2">Expanded, small viewport device</td>
198
+ <td>Expanded, small viewport device</td>
207
199
  <td>
208
200
  Dialog input element, <strong>NOT</strong> the dialog input clear button
209
201
  </td>
@@ -211,14 +203,6 @@
211
203
  The current <code>focused</code> option is selected, closes the bib and <strong>focus</strong> is returned to the trigger input element.
212
204
  </td>
213
205
  </tr>
214
- <tr>
215
- <td>
216
- Dialog input clear button, <strong>NOT</strong> the dialog input element
217
- </td>
218
- <td>
219
- The <strong>input</strong> value is cleared, <strong>focus</strong> moves to the dialog input element.
220
- </td>
221
- </tr>
222
206
  <tr>
223
207
  <td>Escape</td>
224
208
  <td>-</td>
@@ -248,9 +232,9 @@
248
232
  </td>
249
233
  </tr>
250
234
  <tr>
251
- <td rowspan="3">Tab</td>
252
- <td rowspan="2">-</td>
253
- <td rowspan="2">Expanded</td>
235
+ <td rowspan="2">Tab</td>
236
+ <td>-</td>
237
+ <td>Expanded</td>
254
238
  <td>
255
239
  Input element, <strong>NOT</strong> the input clear button
256
240
  <div class="note">
@@ -261,17 +245,6 @@
261
245
  The current <code>focused</code> option is selected, the bib is closed and <strong>focus</strong> is moved to the <strong>clear button</strong> in the component trigger.
262
246
  </td>
263
247
  </tr>
264
- <tr>
265
- <td>
266
- Input clear button, <strong>NOT</strong> the input element
267
- <div class="note">
268
- <strong>Note:</strong> Includes both trigger and bib content input clear buttons.
269
- </div>
270
- </td>
271
- <td>
272
- <span style="background-color: pink; color: red;">&nbsp;What do we do here?&nbsp;&nbsp;</span>
273
- </td>
274
- </tr>
275
248
  <tr>
276
249
  <td>Shift</td>
277
250
  <td>Expanded</td>
@@ -42,6 +42,7 @@ export class AuroCombobox extends AuroElement {
42
42
  };
43
43
  /**
44
44
  * Array of available options to display in the dropdown.
45
+ * This array contains all non-hidden options (e.g., hidden by filtering on input value).
45
46
  * @private
46
47
  */
47
48
  availableOptions: {
@@ -381,6 +382,18 @@ export class AuroCombobox extends AuroElement {
381
382
  * @returns {boolean} - Returns true if the element is valid, false otherwise.
382
383
  */
383
384
  isValid(): boolean;
385
+ /**
386
+ * Mark the first available (non-hidden), enabled option as `active`.
387
+ * @private
388
+ * @returns {void}
389
+ */
390
+ private activateFirstEnabledAvailableOption;
391
+ /**
392
+ * Mark the last available (non-hidden), enabled option as `active`.
393
+ * @private
394
+ * @returns {void}
395
+ */
396
+ private activateLastEnabledAvailableOption;
384
397
  /**
385
398
  * Updates the filter for the available options based on the input value.
386
399
  * @private
@@ -438,6 +451,10 @@ export class AuroCombobox extends AuroElement {
438
451
  * @private
439
452
  */
440
453
  private setClearBtnFocus;
454
+ /**
455
+ * @private
456
+ */
457
+ private setTriggerInputFocus;
441
458
  /**
442
459
  * Suppresses or restores dialog semantics on the bib's dialog element.
443
460
  * On desktop (non-fullscreen), VoiceOver verbosely announces "listbox inside
@@ -1,6 +1,9 @@
1
1
  export namespace comboboxKeyboardStrategy {
2
- function Enter(component: any, evt: any, ctx: any): Promise<void>;
3
- function Tab(component: any, evt: any, ctx: any): void;
4
- function ArrowUp(component: any, evt: any, ctx: any): void;
5
2
  function ArrowDown(component: any, evt: any, ctx: any): void;
3
+ function ArrowUp(component: any, evt: any, ctx: any): void;
4
+ function End(component: any, evt: any, ctx: any): void;
5
+ function Enter(component: any, evt: any, ctx: any): void;
6
+ function Escape(component: any, _evt: any, ctx: any): void;
7
+ function Home(component: any, evt: any, ctx: any): void;
8
+ function Tab(component: any, evt: any, ctx: any): void;
6
9
  }