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

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 (41) 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 +184 -143
  6. package/components/combobox/demo/index.min.js +184 -143
  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 +184 -143
  11. package/components/combobox/dist/registered.js +184 -143
  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.min.js +44 -22
  17. package/components/datepicker/demo/index.min.js +44 -22
  18. package/components/datepicker/dist/index.js +44 -22
  19. package/components/datepicker/dist/registered.js +44 -22
  20. package/components/dropdown/demo/api.min.js +1 -1
  21. package/components/dropdown/demo/index.min.js +1 -1
  22. package/components/dropdown/dist/index.js +1 -1
  23. package/components/dropdown/dist/registered.js +1 -1
  24. package/components/form/demo/api.min.js +342 -257
  25. package/components/form/demo/index.min.js +342 -257
  26. package/components/input/demo/api.min.js +102 -77
  27. package/components/input/demo/index.min.js +102 -77
  28. package/components/input/dist/auro-input.d.ts +11 -0
  29. package/components/input/dist/base-input.d.ts +1 -0
  30. package/components/input/dist/index.js +32 -18
  31. package/components/input/dist/registered.js +32 -18
  32. package/components/radio/demo/api.min.js +1 -1
  33. package/components/radio/demo/index.min.js +1 -1
  34. package/components/radio/dist/index.js +1 -1
  35. package/components/radio/dist/registered.js +1 -1
  36. package/components/select/demo/api.min.js +13 -4
  37. package/components/select/demo/index.min.js +13 -4
  38. package/components/select/dist/index.js +13 -4
  39. package/components/select/dist/registered.js +13 -4
  40. package/custom-elements.json +1435 -1391
  41. package/package.json +1 -1
@@ -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,32 @@ 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
- return;
1264
- }
1265
-
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
1269
  return;
1286
1270
  }
1287
1271
 
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) {
1291
- 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
-
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
- }
1272
+ // option display and navigation are prevented if there are no available options
1273
+ if (component.availableOptions.length > 0) {
1274
+ // navigate if bib is open otherwise open it
1275
+ if (component.dropdown.isPopoverVisible) {
1276
+ evt.preventDefault();
1322
1277
 
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();
1278
+ if (evt.altKey || evt.metaKey) {
1279
+ component.activateLastEnabledAvailableOption();
1328
1280
  } else {
1329
- clearBtn.focus();
1281
+ navigateArrow(component, 'down');
1330
1282
  }
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();
1283
+ } else {
1284
+ component.showBib();
1338
1285
  }
1339
- component.hideBib();
1340
- return;
1341
1286
  }
1342
-
1343
- // Non-fullscreen: select + close
1344
- if (component.menu.optionActive && component.menu.optionActive.value) {
1345
- component.menu.value = component.menu.optionActive.value;
1346
- }
1347
- component.hideBib();
1348
1287
  },
1349
1288
 
1350
1289
  ArrowUp(component, evt, ctx) {
@@ -1353,31 +1292,85 @@ const comboboxKeyboardStrategy = {
1353
1292
  return;
1354
1293
  }
1355
1294
 
1295
+ // option display and navigation are prevented if there are no available options
1356
1296
  if (component.availableOptions.length > 0) {
1357
- component.showBib();
1297
+ // navigate if bib is open otherwise open it
1298
+ if (component.dropdown.isPopoverVisible) {
1299
+ evt.preventDefault();
1300
+
1301
+ if (evt.altKey || evt.metaKey) {
1302
+ component.activateFirstEnabledAvailableOption();
1303
+ } else {
1304
+ navigateArrow(component, 'up');
1305
+ }
1306
+ } else {
1307
+ component.showBib();
1308
+ }
1358
1309
  }
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) {
1310
+ },
1311
+
1312
+ End(component, evt, ctx) {
1313
+ if (ctx.isExpanded) {
1362
1314
  evt.preventDefault();
1363
- navigateArrow(component, 'up');
1315
+ evt.stopPropagation();
1316
+ component.activateLastEnabledAvailableOption();
1364
1317
  }
1365
1318
  },
1366
1319
 
1367
- ArrowDown(component, evt, ctx) {
1368
- // If the clear button has focus, let the browser handle ArrowDown normally.
1320
+ Enter(component, evt, ctx) {
1369
1321
  if (isClearBtnFocused(ctx)) {
1370
- return;
1371
- }
1322
+ // If the clear button has focus, let the browser activate it normally.
1323
+ // stopPropagation prevents parent containers (e.g., forms) from treating
1324
+ // Enter as a submit, but we must NOT call preventDefault — that would
1325
+ // block the browser's built-in "Enter activates focused button" behavior.
1326
+ evt.stopPropagation();
1327
+ } else if (ctx.isExpanded && component.menu.optionActive) {
1328
+ component.menu.makeSelection();
1372
1329
 
1373
- if (component.availableOptions.length > 0) {
1330
+ if (ctx.isModal) {
1331
+ component.setTriggerInputFocus();
1332
+ }
1333
+
1334
+ evt.preventDefault();
1335
+ evt.stopPropagation();
1336
+ } else {
1337
+ // Prevent the keypress from bubbling to parent containers (e.g., forms)
1338
+ // which could interpret Enter as a submit or trigger other unintended behavior.
1339
+ // This is safe because showBib() opens the dialog programmatically,
1340
+ // not via event propagation.
1341
+ evt.preventDefault();
1342
+ evt.stopPropagation();
1374
1343
  component.showBib();
1375
1344
  }
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) {
1345
+ },
1346
+
1347
+ Escape(component, _evt, ctx) {
1348
+ if (ctx.isExpanded && ctx.isModal) {
1349
+ component.setTriggerInputFocus();
1350
+ }
1351
+ },
1352
+
1353
+ Home(component, evt, ctx) {
1354
+ if (ctx.isExpanded) {
1379
1355
  evt.preventDefault();
1380
- navigateArrow(component, 'down');
1356
+ evt.stopPropagation();
1357
+ component.activateFirstEnabledAvailableOption();
1358
+ }
1359
+ },
1360
+
1361
+ Tab(component, evt, ctx) {
1362
+ if (ctx.isExpanded && !isClearBtnFocused(ctx)) {
1363
+ // ClearBtn will not bubble up tab key events when it's focused, so need to manage it here when focused
1364
+ component.menu.makeSelection();
1365
+ component.hideBib();
1366
+
1367
+ // In fullscreen modal mode, closing the dialog does not
1368
+ // automatically restores focus to the input. In the tab case,
1369
+ // Explicitly move focus to the trigger's clear button so the
1370
+ // user can continues tabbing through the page normally.
1371
+ if (ctx.isModal && !evt.shiftKey) {
1372
+ component.setClearBtnFocus();
1373
+ }
1381
1374
  }
1382
1375
  },
1383
1376
  };
@@ -4983,7 +4976,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$4 {
4983
4976
  }
4984
4977
  };
4985
4978
 
4986
- var formkitVersion$2 = '202604022013';
4979
+ var formkitVersion$2 = '202604031704';
4987
4980
 
4988
4981
  let AuroElement$2 = class AuroElement extends i$4 {
4989
4982
  static get properties() {
@@ -11724,6 +11717,12 @@ class BaseInput extends AuroElement$1 {
11724
11717
  this.wrapperElement = this.shadowRoot.querySelector('.wrapper');
11725
11718
  this.inputElement = this.renderRoot.querySelector('input');
11726
11719
  this.labelElement = this.shadowRoot.querySelector('label');
11720
+ this.clearBtn = this.clearButtonRef.value;
11721
+
11722
+ // This must get moved into inputKeyboardStrategy when implemented
11723
+ this.clearBtn.addEventListener('keydown', (evt) => {
11724
+ evt.stopPropagation();
11725
+ });
11727
11726
 
11728
11727
  this.patchInputEvent(this.inputElement);
11729
11728
 
@@ -12747,7 +12746,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$4 {
12747
12746
  }
12748
12747
  };
12749
12748
 
12750
- var formkitVersion$1 = '202604022013';
12749
+ var formkitVersion$1 = '202604031704';
12751
12750
 
12752
12751
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12753
12752
  // See LICENSE in the project root for license information.
@@ -12804,6 +12803,11 @@ class AuroInput extends BaseInput {
12804
12803
  * @private
12805
12804
  */
12806
12805
  this.iconTag = versioning.generateTag('auro-formkit-input-icon', iconVersion$2, _$2);
12806
+
12807
+ /**
12808
+ * @private
12809
+ */
12810
+ this.clearButtonRef = e$1();
12807
12811
  }
12808
12812
 
12809
12813
  static get styles() {
@@ -12821,6 +12825,19 @@ class AuroInput extends BaseInput {
12821
12825
  ];
12822
12826
  }
12823
12827
 
12828
+ /**
12829
+ * Returns classmap configuration for the clear button visibility.
12830
+ * The button is hidden when the input has no value, is read-only, or is disabled.
12831
+ * @private
12832
+ * @returns {Record<string, boolean>} - Classmap object controlling clear button display state.
12833
+ */
12834
+ get clearBtnClassMap() {
12835
+ return {
12836
+ 'util_displayHidden': !this.hasValue || this.readyOnly || this.disabled
12837
+ };
12838
+ }
12839
+
12840
+
12824
12841
  /**
12825
12842
  * Determines if the HTML input element should be visually hidden.
12826
12843
  * Returns true when display value content exists without focus and has a value,
@@ -13140,10 +13157,11 @@ class AuroInput extends BaseInput {
13140
13157
  <${this.buttonTag}
13141
13158
  @click="${this.handleClickClear}"
13142
13159
  appearance="${this.onDark ? 'inverse' : this.appearance}"
13143
- class="notificationBtn clearBtn"
13160
+ class="notificationBtn clearBtn ${e$3(this.clearBtnClassMap)}"
13144
13161
  shape="circle"
13145
13162
  size="sm"
13146
- variant="ghost">
13163
+ variant="ghost"
13164
+ ${n$2(this.clearButtonRef)}>
13147
13165
  <span><slot name="ariaLabel.clear">Clear Input</slot></span>
13148
13166
  <${this.iconTag}
13149
13167
  aria-hidden="true"
@@ -13288,11 +13306,7 @@ class AuroInput extends BaseInput {
13288
13306
  <div part="accent-right" class="accents right">
13289
13307
  ${this.renderValidationErrorIconHtml()}
13290
13308
  ${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}
13309
+ ${this.renderHtmlActionClear()}
13296
13310
  </div>
13297
13311
  </div>
13298
13312
  <div class="helpTextWrapper leftIndent rightIndent" part="inputHelpText">
@@ -13324,11 +13338,7 @@ class AuroInput extends BaseInput {
13324
13338
  ${this.layout.includes('right') || this.layout === "emphasized" ? u$7`
13325
13339
  ${this.renderValidationErrorIconHtml()}
13326
13340
  ` : undefined}
13327
- ${this.hasValue ? u$7`
13328
- ${!this.disabled && !this.readonly ? u$7`
13329
- ${this.renderHtmlActionClear()}
13330
- ` : undefined}
13331
- ` : undefined}
13341
+ ${this.renderHtmlActionClear()}
13332
13342
  </div>
13333
13343
  </div>
13334
13344
  <div class="${e$3(this.helpTextClasses)}" part="inputHelpText">
@@ -13356,11 +13366,7 @@ class AuroInput extends BaseInput {
13356
13366
  </div>
13357
13367
  <div class="accents right">
13358
13368
  ${this.renderValidationErrorIconHtml()}
13359
- ${this.hasValue ? u$7`
13360
- ${!this.disabled && !this.readonly ? u$7`
13361
- ${this.renderHtmlActionClear()}
13362
- ` : undefined}
13363
- ` : undefined}
13369
+ ${this.renderHtmlActionClear()}
13364
13370
  </div>
13365
13371
  </div>
13366
13372
  <div class="helpTextWrapper leftIndent rightIndent" part="inputHelpText">
@@ -13786,7 +13792,7 @@ class AuroBibtemplate extends i$4 {
13786
13792
  }
13787
13793
  }
13788
13794
 
13789
- var formkitVersion = '202604022013';
13795
+ var formkitVersion = '202604031704';
13790
13796
 
13791
13797
  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
13798
 
@@ -14265,6 +14271,7 @@ class AuroCombobox extends AuroElement {
14265
14271
 
14266
14272
  /**
14267
14273
  * Array of available options to display in the dropdown.
14274
+ * This array contains all non-hidden options (e.g., hidden by filtering on input value).
14268
14275
  * @private
14269
14276
  */
14270
14277
  availableOptions: {
@@ -14642,12 +14649,40 @@ class AuroCombobox extends AuroElement {
14642
14649
  AuroLibraryRuntimeUtils$4.prototype.registerComponent(name, AuroCombobox);
14643
14650
  }
14644
14651
 
14652
+ /**
14653
+ * Mark the first available (non-hidden), enabled option as `active`.
14654
+ * @private
14655
+ * @returns {void}
14656
+ */
14657
+ activateFirstEnabledAvailableOption() {
14658
+ const firstEnabledOptionIndex = this.availableOptions.findIndex((opt) => !opt.disabled);
14659
+ this.updateActiveOption(firstEnabledOptionIndex);
14660
+ }
14661
+
14662
+ /**
14663
+ * Mark the last available (non-hidden), enabled option as `active`.
14664
+ * @private
14665
+ * @returns {void}
14666
+ */
14667
+ activateLastEnabledAvailableOption() {
14668
+ let lastEnabledOptionIndex = -1;
14669
+
14670
+ // Work backwards through the available options array to find the last enabled option
14671
+ for (let index = this.availableOptions.length - 1; index >= 0; index -= 1) {
14672
+ if (!this.availableOptions[index].disabled) {
14673
+ lastEnabledOptionIndex = index;
14674
+ break;
14675
+ }
14676
+ }
14677
+
14678
+ this.updateActiveOption(lastEnabledOptionIndex);
14679
+ }
14680
+
14645
14681
  /**
14646
14682
  * Updates the filter for the available options based on the input value.
14647
14683
  * @private
14648
14684
  */
14649
14685
  updateFilter() {
14650
-
14651
14686
  // Reset available options if noFilter is set to false after being true.
14652
14687
  if (this.noFilter) {
14653
14688
  this.availableOptions = [...this.options];
@@ -14766,6 +14801,10 @@ class AuroCombobox extends AuroElement {
14766
14801
  if (this.value && this.input.value && !this.menu.value) {
14767
14802
  this.syncValuesAndStates();
14768
14803
  }
14804
+
14805
+ if (!this.availableOptions.includes(this.menu.optionActive)) {
14806
+ this.activateFirstEnabledAvailableOption();
14807
+ }
14769
14808
  }
14770
14809
 
14771
14810
  /**
@@ -14839,9 +14878,6 @@ class AuroCombobox extends AuroElement {
14839
14878
  if (this.dropdownOpen) {
14840
14879
  const expandedDelay = 150;
14841
14880
  this._expandedTimeout = setTimeout(() => {
14842
- if (!this.value) {
14843
- this.updateActiveOption(0);
14844
- }
14845
14881
  this.triggerExpandedState = true;
14846
14882
  }, expandedDelay);
14847
14883
  } else {
@@ -14851,22 +14887,16 @@ class AuroCombobox extends AuroElement {
14851
14887
  // Clear aria-activedescendant when dropdown closes
14852
14888
  if (!this.dropdownOpen && this.input) {
14853
14889
  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
14890
 
14865
14891
  // Restore pointer events on the menu in case they were disabled
14866
14892
  // during fullscreen open to prevent touch pass-through.
14867
14893
  this.menu.style.pointerEvents = '';
14868
14894
 
14869
- restoreTriggerAfterClose(this.dropdown, this.input);
14895
+ // When closing a fullscreen bib, restore focus to the trigger so that
14896
+ // keyboard navigation continues from the correct place in the page
14897
+ if (this.dropdown.isBibFullscreen) {
14898
+ restoreTriggerAfterClose(this.dropdown, this.input);
14899
+ }
14870
14900
  }
14871
14901
 
14872
14902
  if (this.dropdownOpen) {
@@ -14901,13 +14931,6 @@ class AuroCombobox extends AuroElement {
14901
14931
  this.setInputFocus();
14902
14932
  this._inFullscreenTransition = false;
14903
14933
  });
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
14934
  }
14912
14935
  }
14913
14936
  });
@@ -14957,7 +14980,25 @@ class AuroCombobox extends AuroElement {
14957
14980
  setClearBtnFocus() {
14958
14981
  const clearBtn = this.input.shadowRoot.querySelector('.clearBtn');
14959
14982
  if (clearBtn) {
14960
- clearBtn.focus();
14983
+ // Wait for the element to fully render across
14984
+ // multiple Lit update cycles before moving focus
14985
+ doubleRaf(() => {
14986
+ clearBtn.focus();
14987
+ });
14988
+ }
14989
+ }
14990
+
14991
+ /**
14992
+ * @private
14993
+ */
14994
+ setTriggerInputFocus() {
14995
+ const input = this.input.shadowRoot.querySelector('input');
14996
+ if (input) {
14997
+ // Wait for the element to fully render across
14998
+ // multiple Lit update cycles before moving focus
14999
+ doubleRaf(() => {
15000
+ input.focus();
15001
+ });
14961
15002
  }
14962
15003
  }
14963
15004
 
@@ -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
  }