@aurodesignsystem-dev/auro-formkit 0.0.0-pr1398.2 → 0.0.0-pr1398.3

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 (61) 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/demo/keyboardBehavior.md +0 -0
  4. package/components/checkbox/dist/index.js +1 -1
  5. package/components/checkbox/dist/registered.js +1 -1
  6. package/components/combobox/demo/api.min.js +129 -166
  7. package/components/combobox/demo/index.min.js +129 -166
  8. package/components/combobox/demo/keyboardBehavior.html +81 -0
  9. package/components/combobox/demo/keyboardBehavior.md +308 -0
  10. package/components/combobox/dist/index.js +87 -134
  11. package/components/combobox/dist/registered.js +87 -134
  12. package/components/counter/demo/api.min.js +157 -160
  13. package/components/counter/demo/index.min.js +157 -160
  14. package/components/counter/demo/keyboardBehavior.html +81 -0
  15. package/components/counter/demo/keyboardBehavior.md +127 -0
  16. package/components/counter/dist/auro-counter.d.ts +0 -7
  17. package/components/counter/dist/index.js +157 -160
  18. package/components/counter/dist/keyboardStrategy.d.ts +4 -0
  19. package/components/counter/dist/registered.js +157 -160
  20. package/components/datepicker/demo/api.min.js +89 -137
  21. package/components/datepicker/demo/index.min.js +89 -137
  22. package/components/datepicker/demo/keyboardBehavior.html +81 -0
  23. package/components/datepicker/demo/keyboardBehavior.md +24 -0
  24. package/components/datepicker/dist/index.js +84 -132
  25. package/components/datepicker/dist/registered.js +84 -132
  26. package/components/dropdown/demo/api.md +0 -1
  27. package/components/dropdown/demo/api.min.js +99 -140
  28. package/components/dropdown/demo/index.md +2 -2
  29. package/components/dropdown/demo/index.min.js +99 -140
  30. package/components/dropdown/demo/keyboardBehavior.html +81 -0
  31. package/components/dropdown/demo/keyboardBehavior.md +77 -0
  32. package/components/dropdown/dist/auro-dropdown.d.ts +0 -8
  33. package/components/dropdown/dist/auro-dropdownBib.d.ts +1 -50
  34. package/components/dropdown/dist/dropdownBibKeyboardStrategy.d.ts +7 -0
  35. package/components/dropdown/dist/index.js +83 -128
  36. package/components/dropdown/dist/registered.js +83 -128
  37. package/components/form/demo/api.min.js +466 -599
  38. package/components/form/demo/index.min.js +466 -599
  39. package/components/form/demo/keyboardBehavior.md +0 -0
  40. package/components/input/demo/api.min.js +1 -1
  41. package/components/input/demo/index.min.js +1 -1
  42. package/components/input/demo/keyboardBehavior.md +0 -0
  43. package/components/input/dist/index.js +1 -1
  44. package/components/input/dist/registered.js +1 -1
  45. package/components/menu/demo/api.min.js +42 -32
  46. package/components/menu/demo/index.min.js +42 -32
  47. package/components/menu/dist/auro-menu.d.ts +3 -11
  48. package/components/menu/dist/index.js +42 -32
  49. package/components/menu/dist/registered.js +42 -32
  50. package/components/radio/demo/api.min.js +1 -1
  51. package/components/radio/demo/index.min.js +1 -1
  52. package/components/radio/dist/index.js +1 -1
  53. package/components/radio/dist/registered.js +1 -1
  54. package/components/select/demo/api.min.js +132 -167
  55. package/components/select/demo/index.min.js +132 -167
  56. package/components/select/demo/keyboardBehavior.html +81 -0
  57. package/components/select/demo/keyboardBehavior.md +246 -0
  58. package/components/select/dist/index.js +90 -135
  59. package/components/select/dist/registered.js +90 -135
  60. package/custom-elements.json +61 -89
  61. package/package.json +2 -2
@@ -1234,6 +1234,57 @@ class IconUtil {
1234
1234
  * SPDX-License-Identifier: BSD-3-Clause
1235
1235
  */let e$1 = class e extends i{constructor(i){if(super(i),this.it=A$2,i.type!==t.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(r){if(r===A$2||null==r)return this._t=void 0,this.it=r;if(r===E)return r;if("string"!=typeof r)throw Error(this.constructor.directiveName+"() called with a non-string value");if(r===this.it)return this._t;this.it=r;const s=[r];return s.raw=s,this._t={_$litType$:this.constructor.resultType,strings:s,values:[]}}};e$1.directiveName="unsafeHTML",e$1.resultType=1;
1236
1236
 
1237
+ /**
1238
+ * Computes display state once per keydown event.
1239
+ * Centralizes null-safety checks and makes the shared/modal/popover branching explicit.
1240
+ *
1241
+ * @param {HTMLElement} component - The component with a dropdown reference.
1242
+ * @param {Object} [options] - Optional config.
1243
+ * @param {HTMLElement} [options.dropdown] - Explicit dropdown reference. Falls back to component.dropdown.
1244
+ * @param {Function} [options.inputResolver] - Called with (component, ctx) to resolve the active input element.
1245
+ * @returns {{isExpanded: boolean, isModal: boolean, isPopover: boolean, activeInput: HTMLElement|null}}
1246
+ * isModal and isPopover reflect the display mode (fullscreen vs not) regardless of expanded state.
1247
+ */
1248
+ function createDisplayContext$1(component, options = {}) {
1249
+ const dd = options.dropdown || component.dropdown;
1250
+ // isPopoverVisible reflects as the `open` attribute.
1251
+ // It reports whether the bib is open in any mode (popover or modal).
1252
+ const isExpanded = Boolean(dd && dd.isPopoverVisible);
1253
+ const isFullscreen = Boolean(dd && dd.isBibFullscreen);
1254
+
1255
+ const ctx = {
1256
+ isExpanded,
1257
+ isModal: isFullscreen,
1258
+ isPopover: !isFullscreen,
1259
+ activeInput: null,
1260
+ };
1261
+
1262
+ if (options.inputResolver) {
1263
+ const resolvedInput = options.inputResolver(component, ctx);
1264
+ // Guard against resolvers returning undefined or non-HTMLElement values.
1265
+ ctx.activeInput = resolvedInput instanceof HTMLElement ? resolvedInput : null;
1266
+ }
1267
+
1268
+ return ctx;
1269
+ }
1270
+
1271
+ /**
1272
+ * Wires up a keydown listener that dispatches to strategy[evt.key] or strategy.default.
1273
+ * Handles both sync and async handlers.
1274
+ * @param {HTMLElement} component - The component to attach the listener to.
1275
+ * @param {Object} strategy - Map of key names to handler functions.
1276
+ * @param {Object} [options] - Optional config passed to createDisplayContext.
1277
+ */
1278
+ function applyKeyboardStrategy$1(component, strategy, options = {}) {
1279
+ component.addEventListener('keydown', async (evt) => {
1280
+ const handler = strategy[evt.key] || strategy.default;
1281
+ if (typeof handler === 'function') {
1282
+ const ctx = createDisplayContext$1(component, options);
1283
+ await handler(component, evt, ctx);
1284
+ }
1285
+ });
1286
+ }
1287
+
1237
1288
  var plusIcon = {"role":"img","color":"currentColor","title":"","desc":"","width":"var(--auro-size-lg, var(--ds-size-300, 1.5rem))","height":"var(--auro-size-lg, var(--ds-size-300, 1.5rem))","xmlns":"http://www.w3.org/2000/svg","xmlns_xlink":"http://www.w3.org/1999/xlink","viewBox":"0 0 24 24","path":"/icons","style":"ico_squareLarge","type":"icon","name":"plus-lg","category":"interface","svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-hidden=\"true\" class=\"ico_squareLarge\" role=\"img\" style=\"min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor\" viewBox=\"0 0 24 24\" part=\"svg\"><title/><path d=\"M11.898 5.007 12 5a.75.75 0 0 1 .743.648l.007.102v5.5h5.5a.75.75 0 0 1 .743.648L19 12a.75.75 0 0 1-.648.743l-.102.007h-5.501l.001 5.5a.75.75 0 0 1-.648.743L12 19a.75.75 0 0 1-.743-.648l-.007-.102-.001-5.5H5.75a.75.75 0 0 1-.743-.648L5 12a.75.75 0 0 1 .648-.743l.102-.007h5.5v-5.5a.75.75 0 0 1 .648-.743L12 5z\"/></svg>"};
1238
1289
 
1239
1290
  var minusIcon = {"role":"img","color":"currentColor","title":"","desc":"","width":"var(--auro-size-lg, var(--ds-size-300, 1.5rem))","height":"var(--auro-size-lg, var(--ds-size-300, 1.5rem))","xmlns":"http://www.w3.org/2000/svg","xmlns_xlink":"http://www.w3.org/1999/xlink","viewBox":"0 0 24 24","path":"/icons","style":"ico_squareLarge","type":"icon","name":"minus-lg","category":"interface","deprecated":true,"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-hidden=\"true\" class=\"ico_squareLarge\" data-deprecated=\"true\" role=\"img\" style=\"min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor\" viewBox=\"0 0 24 24\" part=\"svg\"><title/><path d=\"M5.75 12.75h12.5a.75.75 0 1 0 0-1.5H5.75a.75.75 0 1 0 0 1.5\"/></svg>"};
@@ -1470,7 +1521,30 @@ let AuroHelpText$1 = class AuroHelpText extends i$2 {
1470
1521
  }
1471
1522
  };
1472
1523
 
1473
- var formkitVersion$1 = '202603271519';
1524
+ var formkitVersion$1 = '202604012043';
1525
+
1526
+ // Copyright (c) 2026 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1527
+ // See LICENSE in the project root for license information.
1528
+
1529
+ const keyboardStrategy = {
1530
+ ArrowUp(component, _evt) {
1531
+ if (component.disabled) {
1532
+ return;
1533
+ }
1534
+
1535
+ _evt.preventDefault();
1536
+ component.increment();
1537
+ },
1538
+
1539
+ ArrowDown(component, _evt) {
1540
+ if (component.disabled) {
1541
+ return;
1542
+ }
1543
+
1544
+ _evt.preventDefault();
1545
+ component.decrement();
1546
+ }
1547
+ };
1474
1548
 
1475
1549
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1476
1550
  // See LICENSE in the project root for license information.
@@ -1544,11 +1618,10 @@ class AuroCounter extends i$2 {
1544
1618
 
1545
1619
  connectedCallback() {
1546
1620
  super.connectedCallback();
1547
- this.addEventListener('keydown', this.handleKeyDown);
1621
+ applyKeyboardStrategy$1(this, keyboardStrategy);
1548
1622
  }
1549
1623
 
1550
1624
  disconnectedCallback() {
1551
- this.removeEventListener('keydown', this.handleKeyDown);
1552
1625
  super.disconnectedCallback();
1553
1626
  }
1554
1627
 
@@ -1759,29 +1832,6 @@ class AuroCounter extends i$2 {
1759
1832
  this.validation.validate(this, force);
1760
1833
  }
1761
1834
 
1762
- /**
1763
- * Handles the keydown event for the counter component.
1764
- * @param {KeyboardEvent} event - The keyboard event object.
1765
- * @returns {void}
1766
- * @private
1767
- */
1768
- handleKeyDown(event) {
1769
- if (this.disabled) {
1770
- return;
1771
- }
1772
-
1773
- switch (event.key) {
1774
- case 'ArrowUp':
1775
- event.preventDefault();
1776
- this.increment();
1777
- break;
1778
- case 'ArrowDown':
1779
- event.preventDefault();
1780
- this.decrement();
1781
- break;
1782
- }
1783
- }
1784
-
1785
1835
  firstUpdated() {
1786
1836
  this.initValue();
1787
1837
  this.setTagAttribute("auro-counter");
@@ -4002,11 +4052,10 @@ class AuroFloatingUI {
4002
4052
  return;
4003
4053
  }
4004
4054
 
4005
- const { activeElement } = document;
4006
4055
  // if focus is still inside of trigger or bib, do not close
4007
4056
  if (
4008
- this.element.contains(activeElement) ||
4009
- this.element.bib?.contains(activeElement)
4057
+ this.element.matches(":focus") ||
4058
+ this.element.matches(":focus-within")
4010
4059
  ) {
4011
4060
  return;
4012
4061
  }
@@ -4845,12 +4894,83 @@ let p$2 = class p{registerComponent(t,a){customElements.get(t)||customElements.d
4845
4894
 
4846
4895
  var iconVersion$1 = '9.1.2';
4847
4896
 
4897
+ /**
4898
+ * Computes display state once per keydown event.
4899
+ * Centralizes null-safety checks and makes the shared/modal/popover branching explicit.
4900
+ *
4901
+ * @param {HTMLElement} component - The component with a dropdown reference.
4902
+ * @param {Object} [options] - Optional config.
4903
+ * @param {HTMLElement} [options.dropdown] - Explicit dropdown reference. Falls back to component.dropdown.
4904
+ * @param {Function} [options.inputResolver] - Called with (component, ctx) to resolve the active input element.
4905
+ * @returns {{isExpanded: boolean, isModal: boolean, isPopover: boolean, activeInput: HTMLElement|null}}
4906
+ * isModal and isPopover reflect the display mode (fullscreen vs not) regardless of expanded state.
4907
+ */
4908
+ function createDisplayContext(component, options = {}) {
4909
+ const dd = options.dropdown || component.dropdown;
4910
+ // isPopoverVisible reflects as the `open` attribute.
4911
+ // It reports whether the bib is open in any mode (popover or modal).
4912
+ const isExpanded = Boolean(dd && dd.isPopoverVisible);
4913
+ const isFullscreen = Boolean(dd && dd.isBibFullscreen);
4914
+
4915
+ const ctx = {
4916
+ isExpanded,
4917
+ isModal: isFullscreen,
4918
+ isPopover: !isFullscreen,
4919
+ activeInput: null,
4920
+ };
4921
+
4922
+ if (options.inputResolver) {
4923
+ const resolvedInput = options.inputResolver(component, ctx);
4924
+ // Guard against resolvers returning undefined or non-HTMLElement values.
4925
+ ctx.activeInput = resolvedInput instanceof HTMLElement ? resolvedInput : null;
4926
+ }
4927
+
4928
+ return ctx;
4929
+ }
4930
+
4931
+ /**
4932
+ * Wires up a keydown listener that dispatches to strategy[evt.key] or strategy.default.
4933
+ * Handles both sync and async handlers.
4934
+ * @param {HTMLElement} component - The component to attach the listener to.
4935
+ * @param {Object} strategy - Map of key names to handler functions.
4936
+ * @param {Object} [options] - Optional config passed to createDisplayContext.
4937
+ */
4938
+ function applyKeyboardStrategy(component, strategy, options = {}) {
4939
+ component.addEventListener('keydown', async (evt) => {
4940
+ const handler = strategy[evt.key] || strategy.default;
4941
+ if (typeof handler === 'function') {
4942
+ const ctx = createDisplayContext(component, options);
4943
+ await handler(component, evt, ctx);
4944
+ }
4945
+ });
4946
+ }
4947
+
4848
4948
  var styleCss$2 = i$5`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{width:auto;max-width:none;height:auto;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:relative;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(:popover-open){position:fixed;overflow:visible;padding:0;border:none;margin:0;background:transparent;inset:unset;outline:none}: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}`;
4849
4949
 
4850
4950
  var colorCss$2 = i$5`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
4851
4951
 
4852
4952
  var tokensCss$1 = i$5`:host(:not([ondark])),:host(:not([appearance=inverse])){--ds-auro-dropdown-label-text-color: var(--ds-basic-color-texticon-muted, #676767);--ds-auro-dropdown-trigger-background-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-dropdown-trigger-hover-background-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-dropdown-trigger-container-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-dropdown-trigger-border-color: var(--ds-basic-color-border-bold, #585e67);--ds-auro-dropdown-trigger-outline-color: transparent;--ds-auro-dropdown-trigger-text-color: var(--ds-basic-color-texticon-default, #2a2a2a);--ds-auro-dropdownbib-boxshadow-color: var(--ds-elevation-200, 0px 0px 10px rgba(0, 0, 0, 0.15));--ds-auro-dropdownbib-background-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-dropdownbib-container-color: var(--ds-basic-color-surface-default, #ffffff);--ds-auro-dropdownbib-text-color: var(--ds-basic-color-texticon-default, #2a2a2a)}:host([ondark]),:host([appearance=inverse]){--ds-auro-dropdown-label-text-color: var(--ds-basic-color-texticon-inverse-muted, #ccd2db);--ds-auro-dropdown-trigger-background-color: var(--ds-advanced-color-shared-background-inverse, rgba(255, 255, 255, 0.15));--ds-auro-dropdown-trigger-hover-background-color: var(--ds-advanced-color-shared-background-inverse, rgba(255, 255, 255, 0.15));--ds-auro-dropdown-trigger-container-color: var(--ds-advanced-color-shared-background-inverse, rgba(255, 255, 255, 0.15));--ds-auro-dropdown-trigger-border-color: var(--ds-basic-color-border-inverse, #ffffff);--ds-auro-dropdown-trigger-outline-color: transparent;--ds-auro-dropdown-trigger-text-color: var(--ds-basic-color-texticon-inverse, #ffffff);--ds-auro-dropdownbib-boxshadow-color: var(--ds-elevation-200, 0px 0px 10px rgba(0, 0, 0, 0.15));--ds-auro-dropdownbib-background-color: var(--ds-advanced-color-shared-background-inverse, rgba(255, 255, 255, 0.15));--ds-auro-dropdownbib-container-color: var(--ds-advanced-color-shared-background-inverse, rgba(255, 255, 255, 0.15));--ds-auro-dropdownbib-text-color: var(--ds-basic-color-texticon-inverse, #ffffff)}`;
4853
4953
 
4954
+ /**
4955
+ * Creates a keyboard strategy for dialog-specific key handling.
4956
+ * All other keydown behavior is left to the browser's native bubbling path.
4957
+ * @param {HTMLElement} bib - The dropdown bib element.
4958
+ * @returns {Object} Keyboard handlers keyed by `event.key`.
4959
+ */
4960
+ // eslint-disable-next-line no-unused-vars
4961
+ function createDropdownBibKeyboardStrategy(bib) {
4962
+ return {
4963
+ // eslint-disable-next-line no-unused-vars
4964
+ Enter(_dialog, event) {
4965
+ // Floating UI handles Enter key to open the dropdown
4966
+ },
4967
+ // eslint-disable-next-line no-unused-vars
4968
+ Escape(_dialog, event) {
4969
+ // Floating UI handles Escape key to close the dropdown
4970
+ }
4971
+ };
4972
+ }
4973
+
4854
4974
  // Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
4855
4975
  // See LICENSE in the project root for license information.
4856
4976
  /* eslint-disable max-lines */
@@ -4975,26 +5095,11 @@ class AuroDropdownBib extends i$2 {
4975
5095
  },
4976
5096
 
4977
5097
  /**
4978
- * Set by auro-dropdown when a menu option is highlighted via
4979
- * aria-activedescendant. The dialog keyboard bridge checks this
4980
- * flag so that Enter selects the highlighted option instead of
4981
- * activating the focused interactive element (e.g. the trigger
4982
- * button, or the bibtemplate close button in fullscreen).
5098
+ * Tracks whether a menu option is currently highlighted.
4983
5099
  * @private
4984
5100
  */
4985
5101
  hasActiveDescendant: {
4986
5102
  type: Boolean
4987
- },
4988
-
4989
- /**
4990
- * When true, the keyboard bridge allows native Tab behavior
4991
- * instead of intercepting it. Set this for bib consumers
4992
- * (e.g. counter) whose content contains real focusable elements
4993
- * that need native Tab navigation.
4994
- * @private
4995
- */
4996
- nativeFocusableContent: {
4997
- type: Boolean
4998
5103
  }
4999
5104
  };
5000
5105
  }
@@ -5064,7 +5169,7 @@ class AuroDropdownBib extends i$2 {
5064
5169
 
5065
5170
  const dialog = this.shadowRoot.querySelector('dialog');
5066
5171
  this._setupCancelHandler(dialog);
5067
- this._setupKeyboardBridge(dialog);
5172
+ applyKeyboardStrategy(dialog, createDropdownBibKeyboardStrategy());
5068
5173
 
5069
5174
  this.dispatchEvent(new CustomEvent('auro-dropdownbib-connected', {
5070
5175
  bubbles: true,
@@ -5091,98 +5196,6 @@ class AuroDropdownBib extends i$2 {
5091
5196
  });
5092
5197
  }
5093
5198
 
5094
- /**
5095
- * showModal() creates a closed focus scope — keyboard events inside
5096
- * the dialog's shadow DOM do NOT bubble out to the combobox/select
5097
- * keydown handlers in the parent shadow DOM. This handler bridges
5098
- * that gap by re-dispatching navigation keys so they cross the
5099
- * shadow boundary and reach the menu navigation logic in the parent
5100
- * component.
5101
- *
5102
- * The trade-off: intercepting these keys means native keyboard
5103
- * behaviors that would normally "just work" must be manually
5104
- * re-implemented here:
5105
- *
5106
- * - Enter on buttons: Custom elements (auro-button) don't get the
5107
- * native Enter→click that <button> provides, so we call .click()
5108
- * directly when Enter is pressed on a button-like element.
5109
- *
5110
- * - Tab: Intercepted and re-dispatched so parent components
5111
- * (select/combobox) can select the active option and close the
5112
- * dialog. The <dialog> provides containment and isolation
5113
- * (inert background, VoiceOver focus trapping, top layer), while
5114
- * the content inside is a role="listbox" navigated via
5115
- * aria-activedescendant (options are not focusable). Tab keyboard
5116
- * behavior follows listbox conventions (select + close) because
5117
- * the dialog's native Tab trap only cycles between the close
5118
- * button and browser chrome.
5119
- *
5120
- * - Escape: The native <dialog> fires a `cancel` event on ESC
5121
- * (handled by _setupCancelHandler), so the re-dispatched Escape
5122
- * is a secondary path for parent components that also listen for
5123
- * Escape keydown.
5124
- *
5125
- * @param {HTMLDialogElement} dialog - The dialog element to attach the keyboard bridge to.
5126
- * @private
5127
- */
5128
- _setupKeyboardBridge(dialog) {
5129
- const navKeys = new Set([
5130
- 'ArrowUp',
5131
- 'ArrowDown',
5132
- 'Enter',
5133
- 'Escape',
5134
- 'Tab'
5135
- ]);
5136
-
5137
- dialog.addEventListener('keydown', (event) => {
5138
- if (!navKeys.has(event.key)) {
5139
- return;
5140
- }
5141
-
5142
- // Custom elements (auro-button) don't get the native Enter→click
5143
- // behavior that <button> has. Find the button in the composed path
5144
- // and click it directly — but only when no menu option is
5145
- // highlighted. In fullscreen mode focus stays on the close button
5146
- // while arrow keys move the active-descendant highlight through
5147
- // the listbox. If the user presses Enter with an option
5148
- // highlighted, the intent is to select that option, not to click
5149
- // the close button. In that case we fall through and bridge the
5150
- // Enter key to the parent component's keyboard strategy.
5151
- if (event.key === 'Enter') {
5152
- if (!this.hasActiveDescendant) {
5153
- const buttonSelector = 'button, [role="button"], auro-button, [auro-button]';
5154
- const btn = event.composedPath().find((el) => el.matches && el.matches(buttonSelector));
5155
- if (btn) {
5156
- event.preventDefault();
5157
- event.stopPropagation();
5158
- btn.click();
5159
- return;
5160
- }
5161
- }
5162
- }
5163
-
5164
- // Allow native Tab when the bib contains focusable content
5165
- // (e.g. counter buttons) that needs normal Tab navigation.
5166
- if (event.key === 'Tab' && this.nativeFocusableContent) {
5167
- return;
5168
- }
5169
-
5170
- event.preventDefault();
5171
- event.stopPropagation();
5172
- const newEvent = new KeyboardEvent('keydown', {
5173
- key: event.key,
5174
- code: event.code,
5175
- shiftKey: event.shiftKey,
5176
- altKey: event.altKey,
5177
- ctrlKey: event.ctrlKey,
5178
- metaKey: event.metaKey,
5179
- bubbles: true,
5180
- composed: true,
5181
- cancelable: true
5182
- });
5183
- this.dispatchEvent(newEvent);
5184
- });
5185
- }
5186
5199
 
5187
5200
  /**
5188
5201
  * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
@@ -5557,7 +5570,7 @@ class AuroHelpText extends i$2 {
5557
5570
  }
5558
5571
  }
5559
5572
 
5560
- var formkitVersion = '202603271519';
5573
+ var formkitVersion = '202604012043';
5561
5574
 
5562
5575
  let AuroElement$1 = class AuroElement extends i$2 {
5563
5576
  static get properties() {
@@ -5738,7 +5751,6 @@ class AuroDropdown extends AuroElement$1 {
5738
5751
  this.appearance = 'default';
5739
5752
  this.chevron = false;
5740
5753
  this.disabled = false;
5741
- this.disableFocusTrap = false;
5742
5754
  this.error = false;
5743
5755
  this.tabIndex = 0;
5744
5756
  this.noToggle = false;
@@ -5836,9 +5848,8 @@ class AuroDropdown extends AuroElement$1 {
5836
5848
  // showModal() fires asynchronously via Lit's update cycle, which
5837
5849
  // falls outside the user activation window and causes iOS to
5838
5850
  // dismiss the keyboard.
5839
- if (this.isBibFullscreen && this.bibElement && this.bibElement.value) {
5840
- const useModal = !this.disableFocusTrap;
5841
- this.bibElement.value.open(useModal);
5851
+ if (this.bibElement && this.bibElement.value) {
5852
+ this.bibElement.value.open(this.isBibFullscreen);
5842
5853
  }
5843
5854
  }
5844
5855
 
@@ -5951,14 +5962,6 @@ class AuroDropdown extends AuroElement$1 {
5951
5962
  reflect: true
5952
5963
  },
5953
5964
 
5954
- /**
5955
- * If declared, the focus trap inside of bib will be turned off.
5956
- */
5957
- disableFocusTrap: {
5958
- type: Boolean,
5959
- reflect: true
5960
- },
5961
-
5962
5965
  /**
5963
5966
  * @private
5964
5967
  */
@@ -6232,7 +6235,7 @@ class AuroDropdown extends AuroElement$1 {
6232
6235
  if (this.isPopoverVisible) {
6233
6236
  // Fullscreen: use showModal() for native accessibility (inert outside, focus trap)
6234
6237
  // Desktop: use show() for Floating UI positioning + FocusTrap for focus management
6235
- const useModal = this.isBibFullscreen && !this.disableFocusTrap;
6238
+ const useModal = this.isBibFullscreen;
6236
6239
  this.bibElement.value.open(useModal);
6237
6240
  } else {
6238
6241
  this.bibElement.value.close();
@@ -6242,7 +6245,7 @@ class AuroDropdown extends AuroElement$1 {
6242
6245
  // When fullscreen strategy changes while open, re-open dialog with correct mode
6243
6246
  // (e.g. resizing from desktop → mobile while dropdown is open)
6244
6247
  if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
6245
- const useModal = this.isBibFullscreen && !this.disableFocusTrap;
6248
+ const useModal = this.isBibFullscreen;
6246
6249
  this.bibElement.value.close();
6247
6250
  this.bibElement.value.open(useModal);
6248
6251
  }
@@ -6354,7 +6357,7 @@ class AuroDropdown extends AuroElement$1 {
6354
6357
  * @private
6355
6358
  */
6356
6359
  updateFocusTrap() {
6357
- if (this.isPopoverVisible && !this.disableFocusTrap) {
6360
+ if (this.isPopoverVisible) {
6358
6361
  if (!this.isBibFullscreen) {
6359
6362
  // Desktop: show() doesn't trap focus, so use FocusTrap
6360
6363
  this.focusTrap = new FocusTrap(this.bibContent);
@@ -7706,11 +7709,6 @@ class AuroCounterGroup extends AuroElement {
7706
7709
 
7707
7710
  // Focus close button when fullscreen dialog opens
7708
7711
  this.dropdown.addEventListener('auroDropdown-toggled', () => {
7709
- // Tell the bib's keyboard bridge to allow native Tab so focus
7710
- // moves between the real focusable counter elements in the bib.
7711
- if (this.dropdown.bibContent) {
7712
- this.dropdown.bibContent.nativeFocusableContent = true;
7713
- }
7714
7712
  if (this.dropdown.isPopoverVisible && this.dropdown.isBibFullscreen) {
7715
7713
  doubleRaf(() => {
7716
7714
  this.bibtemplate.focusCloseButton();
@@ -7907,7 +7905,6 @@ class AuroCounterGroup extends AuroElement {
7907
7905
  renderCounterDropdown() {
7908
7906
  return u$5`
7909
7907
  <${this.dropdownTag}
7910
- noHideOnThisFocusLoss
7911
7908
  chevron
7912
7909
  part="dropdown"
7913
7910
  appearance="${this.onDark ? 'inverse' : this.appearance}"
@@ -0,0 +1,81 @@
1
+ <!--
2
+ Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
3
+ See LICENSE in the project root for license information.
4
+
5
+ HTML in this document is standardized and NOT to be edited.
6
+ All demo code should be added/edited in ./demo/index.md
7
+
8
+ With the exception of adding custom elements if needed for the demo.
9
+
10
+ ----------------------- DO NOT EDIT -----------------------------
11
+
12
+ -->
13
+
14
+ <!DOCTYPE html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="UTF-8" />
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
19
+ <title>Auro Web Component Demo | auro-counter</title>
20
+
21
+ <!-- Prism.js Stylesheet -->
22
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/prismjs@1.24.1/themes/prism.css"/>
23
+
24
+ <!-- Legacy reference is still needed to support auro-counter's use of legacy token values at this time -->
25
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/design-tokens@latest/dist/legacy/auro-classic/CSSCustomProperties.css"/>
26
+
27
+ <!-- Design Token Alaska Theme -->
28
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/design-tokens@latest/dist/themes/alaska/CSSCustomProperties--alaska.min.css"/>
29
+
30
+ <!-- Webcore Stylesheet Alaska Theme -->
31
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@latest/dist/bundled/themes/alaska.global.min.css" />
32
+
33
+ <!-- Demo Specific Styles -->
34
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@latest/dist/demoWrapper.css" />
35
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@aurodesignsystem/webcorestylesheets@latest/dist/elementDemoStyles.css" />
36
+ <style>
37
+ table {
38
+ --ds-color-container-secondary-default: transparent;
39
+ }
40
+
41
+ tr:not(:last-of-type) {
42
+ border-bottom: 1px solid var(--ds-color-border-tertiary-default);
43
+ }
44
+
45
+ sup {
46
+ font-size: 60%;
47
+ font-style: italic;
48
+ opacity: 0.6;
49
+ top: -.5rem;
50
+ }
51
+
52
+ .note {
53
+ margin: 10px 0;
54
+ padding: 10px;
55
+ border-radius: 6px;
56
+ font-style: italic;
57
+ color: var(--ds-basic-color-texticon-muted);
58
+ background-color: var(--ds-basic-color-surface-neutral-subtle);
59
+ }
60
+ </style>
61
+ </head>
62
+ <body class="auro-markdown">
63
+ <main></main>
64
+
65
+ <script type="module">
66
+ import 'https://cdn.jsdelivr.net/npm/marked@latest/marked.min.js';
67
+ import 'https://cdn.jsdelivr.net/npm/prismjs@latest/prism.js';
68
+ fetch('./keyboardBehavior.md')
69
+ .then((response) => response.text())
70
+ .then((text) => {
71
+ const rawHtml = marked.parse(text);
72
+ document.querySelector('main').innerHTML = rawHtml;
73
+ Prism.highlightAll();
74
+ })
75
+ </script>
76
+
77
+ <!-- If additional elements are needed for the demo, add them here. -->
78
+ <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-header@latest/+esm" type="module"></script>
79
+ <script src="https://cdn.jsdelivr.net/npm/@aurodesignsystem/auro-hyperlink@latest/+esm" type="module"></script>
80
+ </body>
81
+ </html>
@@ -0,0 +1,127 @@
1
+ <auro-header level="1" id="overview">Counter & Counter Group - Keyboard Behavior</auro-header>
2
+ <div class="contentWrapper">
3
+ <div class="mainContent">
4
+ <div class="scrollWrapper">
5
+ <auro-header level="2" id="tabBehavior">Tab Behavior</auro-header>
6
+ <auro-header level="3" id="tabBehaviorCounter">Counter</auro-header>
7
+ <p>The counter component is itself a focusable element. There are no sub-parts that are separately focusable.</p>
8
+ <auro-header level="3" id="tabBehaviorCounterGroup">Counter Group</auro-header>
9
+ <p>When multiple counters are placed inside a counter group the component behaves as an auro-dropdown. The trigger element renders a counter group label and a summary of all counter labels and current values. The dropdown bib renders all counters inside of the group.</p>
10
+ <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../../dropdown/docs/partials/tabindex.md) -->
11
+ <!-- The below content is automatically added from ./../../dropdown/docs/partials/tabindex.md -->
12
+ <p>The trigger is a focusable element and participates in the standard tab order, responding to <code>Tab</code> and <code>Shift+Tab</code> key events per <auro-hyperlink href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/tabindex">native browser behavior, i.e., these keys step through the browser tabindex sequence.</auro-hyperlink></p>
13
+ <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>
14
+ <p>When the bib is collapsed, the bib content is excluded from the tab sequence. When <strong>expanded</strong>, focusable elements within the bib content are included in the natural tab order. In fullscreen mode, focus is trapped within the bib, and the tab sequence cycles through the bib content focusable elements until the bib is closed or the viewport no longer meets the fullscreen condition and is rendered as a popover.</p>
15
+ <!-- AURO-GENERATED-CONTENT:END -->
16
+ <auro-header level="2" id="keyEvents">Key Events</auro-header>
17
+ <auro-header level="3" id="keyEventsCounter">Counter</auro-header>
18
+ <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../docs/partials/keyEvents-counter.md) -->
19
+ <!-- The below content is automatically added from ./../docs/partials/keyEvents-counter.md -->
20
+ <table>
21
+ <thead>
22
+ <tr>
23
+ <th>Key</th>
24
+ <th>Modifier</th>
25
+ <th>Current State</th>
26
+ <th>Focus Element</th>
27
+ <th>Behavior</th>
28
+ </tr>
29
+ </thead>
30
+ <tbody>
31
+ <tr>
32
+ <td>ArrowDown</td>
33
+ <td>-</td>
34
+ <td>*</td>
35
+ <td>
36
+ Trigger element.
37
+ </td>
38
+ <td>Decrements the counter value by 1.</td>
39
+ </tr>
40
+ <tr>
41
+ <td>ArrowUp</td>
42
+ <td>-</td>
43
+ <td>*</td>
44
+ <td>
45
+ Trigger element.
46
+ </td>
47
+ <td>Increments the counter value by 1.</td>
48
+ </tr>
49
+ </tbody>
50
+ </table>
51
+ <!-- AURO-GENERATED-CONTENT:END -->
52
+ <div class="note">
53
+ <p><strong>Note:</strong> The following keyboard behavior is unsupported at this time:</p>
54
+ <ul>
55
+ <li><strong>Home:</strong> sets the counter value to the defined minimum (default 0)</li>
56
+ <li><strong>End:</strong> sets the counter value to the defined maximum (default 9)</li>
57
+ <li><strong>PageUp:</strong> (optional) Increases the value by a larger step than Up Arrow.</li>
58
+ <li><strong>PageDown:</strong> (optional) Decreases the value by a larger step than Down Arrow.</li>
59
+ </ul>
60
+ </div>
61
+ <auro-header level="3" id="keyEventsCounterGroup">Counter Group</auro-header>
62
+ <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../docs/partials/keyEvents-counterGroup.md) -->
63
+ <!-- The below content is automatically added from ./../docs/partials/keyEvents-counterGroup.md -->
64
+ <p>The counter group itself does not have any key event handlers other than those inherited from the dropdown component. However, when the counter group is <strong>expanded</strong> or <strong>opened</strong> the first counter in the bib receives <strong>focus</strong>. When the counter group is <strong>collapsed</strong> or <strong>closed</strong> by any method other than the <code>Tab</code> key (e.g., the <strong>Esc</strong> key), <strong>focus</strong> moves to the trigger element.</p>
65
+ <!-- AURO-GENERATED-CONTENT:END -->
66
+ <auro-header level="4" id="keyEventsDropdown">Key Events inherited from Auro-Dropdown</auro-header>
67
+ <!-- AURO-GENERATED-CONTENT:START (FILE:src=./../../dropdown/docs/partials/keyEvents.md) -->
68
+ <!-- The below content is automatically added from ./../../dropdown/docs/partials/keyEvents.md -->
69
+ <table>
70
+ <thead>
71
+ <tr>
72
+ <th>Key</th>
73
+ <th>Modifier</th>
74
+ <th>Current State</th>
75
+ <th>Focus Element</th>
76
+ <th>Behavior</th>
77
+ </tr>
78
+ </thead>
79
+ <tbody>
80
+ <tr>
81
+ <td rowspan="2">Enter</td>
82
+ <td>-</td>
83
+ <td>Collapsed</td>
84
+ <td>
85
+ Trigger or any <strong>focusable</strong> element within the trigger.
86
+ </td>
87
+ <td>Opens the bib.</td>
88
+ </tr>
89
+ <tr>
90
+ <td>-</td>
91
+ <td>Expanded</td>
92
+ <td>
93
+ Trigger or any <strong>focusable</strong> element within the trigger.
94
+ </td>
95
+ <td>Closes the bib.</td>
96
+ </tr>
97
+ <tr>
98
+ <td>Escape</td>
99
+ <td>-</td>
100
+ <td>Expanded</td>
101
+ <td>
102
+ Component <code>:host</code> or any <strong>focusable</strong> element within the component.
103
+ </td>
104
+ <td>Closes the bib.</td>
105
+ </tr>
106
+ <tr>
107
+ <td rowspan="2">Space</td>
108
+ <td>-</td>
109
+ <td>Collapsed</td>
110
+ <td>
111
+ Trigger or any <strong>focusable</strong> element within the trigger.
112
+ </td>
113
+ <td>Opens the bib.</td>
114
+ </tr>
115
+ <tr>
116
+ <td>-</td>
117
+ <td>Expanded</td>
118
+ <td>
119
+ Trigger or any <strong>focusable</strong> element within the trigger.
120
+ </td>
121
+ <td>Closes the bib.</td>
122
+ </tr>
123
+ </tbody>
124
+ </table>
125
+ <!-- AURO-GENERATED-CONTENT:END -->
126
+ </div>
127
+ </div>