@aurodesignsystem-dev/auro-formkit 0.0.0-pr1492.4 → 0.0.0-pr1493.0

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 (153) hide show
  1. package/components/bibtemplate/dist/auro-bibtemplate.d.ts +7 -0
  2. package/components/bibtemplate/dist/index.js +9 -1
  3. package/components/bibtemplate/dist/registered.js +9 -1
  4. package/components/checkbox/demo/css-only.html +57 -0
  5. package/components/checkbox/demo/css-only.md +146 -0
  6. package/components/checkbox/demo/customize.min.js +2 -2
  7. package/components/checkbox/demo/getting-started.min.js +2 -2
  8. package/components/checkbox/demo/index.min.js +2 -2
  9. package/components/checkbox/demo/pages.json +1 -1
  10. package/components/checkbox/demo/styles.min.css +1 -1
  11. package/components/checkbox/demo/why-checkbox.html +57 -0
  12. package/components/checkbox/demo/why-checkbox.md +86 -0
  13. package/components/checkbox/dist/index.js +2 -2
  14. package/components/checkbox/dist/registered.js +2 -2
  15. package/components/combobox/demo/css-only.html +57 -0
  16. package/components/combobox/demo/css-only.md +209 -0
  17. package/components/combobox/demo/customize.md +7 -3
  18. package/components/combobox/demo/customize.min.js +1846 -1804
  19. package/components/combobox/demo/getting-started.min.js +1846 -1804
  20. package/components/combobox/demo/index.min.js +1846 -1804
  21. package/components/combobox/demo/pages.json +1 -1
  22. package/components/combobox/demo/styles.min.css +1 -1
  23. package/components/combobox/demo/why-combobox.html +57 -0
  24. package/components/combobox/demo/why-combobox.md +113 -0
  25. package/components/combobox/dist/auro-combobox.d.ts +5 -0
  26. package/components/combobox/dist/index.js +796 -179
  27. package/components/combobox/dist/registered.js +796 -179
  28. package/components/counter/demo/css-only.html +57 -0
  29. package/components/counter/demo/css-only.md +184 -0
  30. package/components/counter/demo/customize.min.js +276 -16
  31. package/components/counter/demo/index.min.js +276 -16
  32. package/components/counter/demo/keyboard-behavior.md +1 -0
  33. package/components/counter/demo/pages.json +1 -1
  34. package/components/counter/demo/styles.min.css +1 -1
  35. package/components/counter/demo/why-counter.html +57 -0
  36. package/components/counter/demo/why-counter.md +108 -0
  37. package/components/counter/dist/index.js +5457 -193
  38. package/components/counter/dist/registered.js +5457 -193
  39. package/components/datepicker/demo/accessibility.md +54 -3
  40. package/components/datepicker/demo/api.md +65 -55
  41. package/components/datepicker/demo/css-only.html +57 -0
  42. package/components/datepicker/demo/css-only.md +151 -0
  43. package/components/datepicker/demo/customize.html +2 -0
  44. package/components/datepicker/demo/customize.js +19 -0
  45. package/components/datepicker/demo/customize.md +474 -298
  46. package/components/datepicker/demo/customize.min.js +36243 -0
  47. package/components/datepicker/demo/design.md +5 -3
  48. package/components/datepicker/demo/getting-started.md +6 -6
  49. package/components/datepicker/demo/index.js +5 -1
  50. package/components/datepicker/demo/index.md +113 -16
  51. package/components/datepicker/demo/index.min.js +2736 -398
  52. package/components/datepicker/demo/keyboard-behavior.md +201 -2
  53. package/components/datepicker/demo/pages.json +1 -1
  54. package/components/datepicker/demo/styles.min.css +1 -1
  55. package/components/datepicker/demo/voiceover.md +21 -12
  56. package/components/datepicker/demo/why-datepicker.html +57 -0
  57. package/components/datepicker/demo/why-datepicker.md +133 -0
  58. package/components/datepicker/dist/index.js +2650 -399
  59. package/components/datepicker/dist/registered.js +2650 -399
  60. package/components/datepicker/dist/src/auro-calendar-cell.d.ts +116 -14
  61. package/components/datepicker/dist/src/auro-calendar-month.d.ts +54 -1
  62. package/components/datepicker/dist/src/auro-calendar.d.ts +264 -0
  63. package/components/datepicker/dist/src/auro-datepicker.d.ts +189 -53
  64. package/components/datepicker/dist/src/datepickerKeyboardStrategy.d.ts +5 -3
  65. package/components/datepicker/dist/src/utilities.d.ts +0 -14
  66. package/components/dropdown/demo/accessibility.md +11 -0
  67. package/components/dropdown/demo/api.md +1 -0
  68. package/components/dropdown/demo/css-only.html +57 -0
  69. package/components/dropdown/demo/css-only.md +219 -0
  70. package/components/dropdown/demo/customize.md +3 -0
  71. package/components/dropdown/demo/customize.min.js +265 -13
  72. package/components/dropdown/demo/getting-started.min.js +265 -13
  73. package/components/dropdown/demo/index.min.js +265 -13
  74. package/components/dropdown/demo/keyboard-behavior.md +1 -0
  75. package/components/dropdown/demo/pages.json +1 -1
  76. package/components/dropdown/demo/styles.min.css +1 -1
  77. package/components/dropdown/demo/why-dropdown.html +57 -0
  78. package/components/dropdown/demo/why-dropdown.md +97 -0
  79. package/components/dropdown/dist/auro-dropdown.d.ts +36 -1
  80. package/components/dropdown/dist/index.js +265 -13
  81. package/components/dropdown/dist/registered.js +265 -13
  82. package/components/form/demo/css-only.html +57 -0
  83. package/components/form/demo/css-only.md +156 -0
  84. package/components/form/demo/customize.min.js +11085 -8074
  85. package/components/form/demo/getting-started.min.js +11085 -8074
  86. package/components/form/demo/index.min.js +11085 -8074
  87. package/components/form/demo/pages.json +1 -1
  88. package/components/form/demo/registerDemoDeps.min.js +11084 -8073
  89. package/components/form/demo/styles.min.css +1 -1
  90. package/components/form/demo/why-form.html +57 -0
  91. package/components/form/demo/why-form.md +101 -0
  92. package/components/input/demo/css-only.html +57 -0
  93. package/components/input/demo/css-only.md +199 -0
  94. package/components/input/demo/customize.md +625 -636
  95. package/components/input/demo/customize.min.js +150 -75
  96. package/components/input/demo/getting-started.md +16 -6
  97. package/components/input/demo/getting-started.min.js +150 -75
  98. package/components/input/demo/index.min.js +150 -75
  99. package/components/input/demo/pages.json +1 -1
  100. package/components/input/demo/styles.min.css +1 -1
  101. package/components/input/demo/why-input.html +57 -0
  102. package/components/input/demo/why-input.md +121 -0
  103. package/components/input/dist/auro-input-util.d.ts +1 -1
  104. package/components/input/dist/base-input.d.ts +16 -0
  105. package/components/input/dist/index.js +150 -75
  106. package/components/input/dist/registered.js +150 -75
  107. package/components/input/dist/utilities.d.ts +1 -2
  108. package/components/menu/demo/api.md +41 -45
  109. package/components/menu/demo/css-only.html +57 -0
  110. package/components/menu/demo/css-only.md +166 -0
  111. package/components/menu/demo/customize.md +0 -28
  112. package/components/menu/demo/index.min.js +779 -1354
  113. package/components/menu/demo/pages.json +1 -1
  114. package/components/menu/demo/styles.min.css +1 -1
  115. package/components/menu/demo/why-menu.html +57 -0
  116. package/components/menu/demo/why-menu.md +104 -0
  117. package/components/menu/dist/auro-menu.d.ts +95 -110
  118. package/components/menu/dist/auro-menuoption.d.ts +32 -138
  119. package/components/menu/dist/index.js +753 -1308
  120. package/components/menu/dist/registered.js +765 -1308
  121. package/components/radio/demo/css-only.html +57 -0
  122. package/components/radio/demo/css-only.md +151 -0
  123. package/components/radio/demo/customize.min.js +1764 -0
  124. package/components/radio/demo/demo-support.min.js +55807 -0
  125. package/components/radio/demo/getting-started.js +1 -1
  126. package/components/radio/demo/getting-started.md +1 -1
  127. package/components/radio/demo/getting-started.min.js +1783 -0
  128. package/components/radio/demo/index.min.js +2 -2
  129. package/components/radio/demo/pages.json +1 -1
  130. package/components/radio/demo/styles.min.css +1 -1
  131. package/components/radio/demo/why-radio.html +57 -0
  132. package/components/radio/demo/why-radio.md +92 -0
  133. package/components/radio/dist/index.js +2 -2
  134. package/components/radio/dist/registered.js +2 -2
  135. package/components/select/demo/api.md +1 -0
  136. package/components/select/demo/css-only.html +57 -0
  137. package/components/select/demo/css-only.md +203 -0
  138. package/components/select/demo/customize.md +78 -3
  139. package/components/select/demo/customize.min.js +1341 -1533
  140. package/components/select/demo/getting-started.min.js +1341 -1533
  141. package/components/select/demo/index.min.js +1341 -1533
  142. package/components/select/demo/keyboard-behavior.md +19 -0
  143. package/components/select/demo/pages.json +1 -1
  144. package/components/select/demo/styles.min.css +1 -1
  145. package/components/select/demo/why-select.html +57 -0
  146. package/components/select/demo/why-select.md +128 -0
  147. package/components/select/dist/auro-select.d.ts +38 -2
  148. package/components/select/dist/index.js +435 -52
  149. package/components/select/dist/registered.js +435 -52
  150. package/components/select/dist/selectUtils.d.ts +12 -0
  151. package/custom-elements.json +3051 -2553
  152. package/package.json +8 -8
  153. package/components/menu/dist/auro-menu.context.d.ts +0 -238
@@ -623,7 +623,7 @@ class AuroFormValidation {
623
623
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
624
624
  const input = elem.renderRoot.querySelector('input');
625
625
 
626
- if (input.validationMessage.length > 0) {
626
+ if (input && input.validationMessage.length > 0) {
627
627
  elem.errorMessage = input.validationMessage;
628
628
  }
629
629
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
@@ -835,6 +835,21 @@ function navigateArrow(component, direction, options = {}) {
835
835
  }
836
836
  }
837
837
 
838
+ /**
839
+ * Returns the enabled (non-disabled) options for a menu, safely.
840
+ *
841
+ * Auro-menu's `options` getter returns `undefined` when the menu has no items
842
+ * (initItems sets `items` to undefined for empty menus). Callers that reach for
843
+ * `.find()` / `[...spread]` would otherwise crash; this helper normalizes the
844
+ * empty case to `[]` so array methods are always safe.
845
+ *
846
+ * @param {HTMLElement | null | undefined} menu - The auro-menu element.
847
+ * @returns {Array<HTMLElement>} Non-disabled options, empty array when none.
848
+ */
849
+ function getEnabledOptions(menu) {
850
+ return (menu?.options || []).filter((option) => !option.disabled);
851
+ }
852
+
838
853
  /* eslint-disable new-cap */
839
854
 
840
855
  const selectKeyboardStrategy = {
@@ -867,6 +882,14 @@ const selectKeyboardStrategy = {
867
882
  },
868
883
 
869
884
  Escape(component, evt, ctx) {
885
+ // Always clear the type-ahead buffer — Escape is a universal cancel.
886
+ // Safe to call when the buffer is empty.
887
+ // eslint-disable-next-line no-underscore-dangle
888
+ if (typeof component._clearTypeaheadBuffer === 'function') {
889
+ // eslint-disable-next-line no-underscore-dangle
890
+ component._clearTypeaheadBuffer();
891
+ }
892
+
870
893
  if (!ctx.isExpanded) {
871
894
  return;
872
895
  }
@@ -883,7 +906,8 @@ const selectKeyboardStrategy = {
883
906
  }
884
907
  evt.preventDefault();
885
908
  evt.stopPropagation();
886
- const lastOption = [...component.menu.menuService.menuOptions].reverse().find((option) => !option.disabled);
909
+ // `pop()` is safe here: getEnabledOptions returns a fresh filtered array.
910
+ const lastOption = getEnabledOptions(component.menu).pop();
887
911
  if (lastOption) {
888
912
  component.menu.updateActiveOption(lastOption);
889
913
  }
@@ -905,7 +929,7 @@ const selectKeyboardStrategy = {
905
929
  }
906
930
  evt.preventDefault();
907
931
  evt.stopPropagation();
908
- const firstOption = component.menu.menuService.menuOptions.find((option) => !option.disabled);
932
+ const [firstOption] = getEnabledOptions(component.menu);
909
933
  if (firstOption) {
910
934
  component.menu.updateActiveOption(firstOption);
911
935
  }
@@ -925,16 +949,25 @@ const selectKeyboardStrategy = {
925
949
  },
926
950
 
927
951
  default(component, evt, ctx) {
928
- component.updateActiveOptionBasedOnKey(evt.key);
952
+ // Space resolves to either typeahead-buffer extension or bib toggle
953
+ // depending on whether a type-ahead buffer is active. Mirrors native
954
+ // <select> and the WAI-ARIA APG Listbox guidance: mid-typeahead space
955
+ // is a search character (e.g. "San Francisco"); otherwise it toggles.
929
956
  if (evt.key === ' ') {
930
957
  evt.preventDefault();
931
958
  evt.stopPropagation();
959
+ if (component.typeaheadBuffer && component.typeaheadBuffer.length > 0) {
960
+ component.updateActiveOptionBasedOnKey(evt.key);
961
+ return;
962
+ }
932
963
  if (ctx.isExpanded) {
933
964
  component.dropdown.hide();
934
- return;
965
+ } else {
966
+ component.dropdown.show();
935
967
  }
936
- component.dropdown.show();
968
+ return;
937
969
  }
970
+ component.updateActiveOptionBasedOnKey(evt.key);
938
971
  },
939
972
  };
940
973
 
@@ -4215,7 +4248,7 @@ function applyKeyboardStrategy(component, strategy, options = {}) {
4215
4248
  });
4216
4249
  }
4217
4250
 
4218
- var styleCss$2$1 = css`: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}`;
4251
+ var styleCss$2$1 = css`: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([desktopmodal]:popover-open)::backdrop{background:transparent}: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}`;
4219
4252
 
4220
4253
  var colorCss$2 = css`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
4221
4254
 
@@ -4846,7 +4879,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
4846
4879
  }
4847
4880
  };
4848
4881
 
4849
- var formkitVersion$1 = '202606031929';
4882
+ var formkitVersion$1 = '202606181613';
4850
4883
 
4851
4884
  class AuroElement extends LitElement {
4852
4885
  static get properties() {
@@ -5026,6 +5059,7 @@ class AuroDropdown extends AuroElement {
5026
5059
  _intializeDefaults() {
5027
5060
  this.appearance = 'default';
5028
5061
  this.chevron = false;
5062
+ this.desktopModal = false;
5029
5063
  this.disabled = false;
5030
5064
  this.disableKeyboardHandling = false;
5031
5065
  this.error = false;
@@ -5206,6 +5240,14 @@ class AuroDropdown extends AuroElement {
5206
5240
  reflect: true
5207
5241
  },
5208
5242
 
5243
+ /**
5244
+ * If declared, the dropdown will behave as a modal dialog when in a desktop viewport size.
5245
+ */
5246
+ desktopModal: {
5247
+ type: Boolean,
5248
+ reflect: true
5249
+ },
5250
+
5209
5251
  /**
5210
5252
  * If declared, the dropdown will only show by calling the API .show() public method.
5211
5253
  */
@@ -5493,6 +5535,15 @@ class AuroDropdown extends AuroElement {
5493
5535
 
5494
5536
  disconnectedCallback() {
5495
5537
  super.disconnectedCallback();
5538
+ this._clearPageInert();
5539
+ if (this._bibTabHandler) {
5540
+ this.removeEventListener('keydown', this._bibTabHandler);
5541
+ this._bibTabHandler = undefined;
5542
+ }
5543
+ if (this.focusTrap) {
5544
+ this.focusTrap.disconnect();
5545
+ this.focusTrap = undefined;
5546
+ }
5496
5547
  if (this.floater) {
5497
5548
  this.floater.hideBib('disconnect');
5498
5549
  this.floater.disconnect();
@@ -5520,19 +5571,45 @@ class AuroDropdown extends AuroElement {
5520
5571
  if (this.isPopoverVisible) {
5521
5572
  // Fullscreen: use showModal() for native accessibility (inert outside, focus trap)
5522
5573
  // Desktop: use show() for Floating UI positioning + FocusTrap for focus management
5523
- const useModal = this.isBibFullscreen;
5524
- this.bibElement.value.open(useModal);
5574
+ this.bibElement.value.open(this.isBibFullscreen);
5575
+ this.updateFocusTrap();
5576
+
5577
+ // Desktop modal: make siblings inert so content outside is not interactive
5578
+ if (this.desktopModal && !this.isBibFullscreen) {
5579
+ this._setPageInert();
5580
+ }
5525
5581
  } else {
5526
5582
  this.bibElement.value.close();
5583
+ this._clearPageInert();
5527
5584
  }
5528
5585
  }
5529
5586
 
5530
5587
  // When fullscreen strategy changes while open, re-open dialog with correct mode
5531
5588
  // (e.g. resizing from desktop → mobile while dropdown is open)
5532
5589
  if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
5533
- const useModal = this.isBibFullscreen;
5534
5590
  this.bibElement.value.close();
5535
- this.bibElement.value.open(useModal);
5591
+ this.bibElement.value.open(this.isBibFullscreen);
5592
+
5593
+ // Re-initialize focus management for the new strategy
5594
+ this.updateFocusTrap();
5595
+
5596
+ // Toggle inert: desktop modal needs it, fullscreen showModal() handles it natively
5597
+ if (this.desktopModal && !this.isBibFullscreen) {
5598
+ this._setPageInert();
5599
+ } else {
5600
+ this._clearPageInert();
5601
+ }
5602
+ }
5603
+
5604
+ // Handle desktopModal toggled while the dropdown is already open.
5605
+ // Re-initialize focus trapping and page inert state to match the new mode.
5606
+ if (changedProperties.has('desktopModal') && this.isPopoverVisible && !this.isBibFullscreen) {
5607
+ this.updateFocusTrap();
5608
+ if (this.desktopModal) {
5609
+ this._setPageInert();
5610
+ } else {
5611
+ this._clearPageInert();
5612
+ }
5536
5613
  }
5537
5614
  }
5538
5615
 
@@ -5542,8 +5619,14 @@ class AuroDropdown extends AuroElement {
5542
5619
  * @param {CustomEvent} event - The custom event that contains the dropdown toggle information.
5543
5620
  */
5544
5621
  handleDropdownToggle(event) {
5545
- this.updateFocusTrap();
5546
5622
  this.isPopoverVisible = event.detail.expanded;
5623
+
5624
+ // Tear down FocusTrap when closing. Creation happens in updated()
5625
+ // after the dialog is open so getFocusableElements can find content.
5626
+ if (!this.isPopoverVisible) {
5627
+ this.updateFocusTrap();
5628
+ }
5629
+
5547
5630
  const eventType = event.detail.eventType || "unknown";
5548
5631
  if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
5549
5632
  this.trigger.focus();
@@ -5642,19 +5725,220 @@ class AuroDropdown extends AuroElement {
5642
5725
  * @private
5643
5726
  */
5644
5727
  updateFocusTrap() {
5728
+ // Always clean up existing handlers/traps before setting up new ones
5729
+ // to prevent duplicate listeners on repeated calls.
5730
+ if (this._bibTabHandler) {
5731
+ this.removeEventListener('keydown', this._bibTabHandler);
5732
+ this._bibTabHandler = undefined;
5733
+ }
5734
+
5735
+ if (this.focusTrap) {
5736
+ this.focusTrap.disconnect();
5737
+ this.focusTrap = undefined;
5738
+ }
5739
+
5740
+ // Restore the user-set noHideOnThisFocusLoss value, if we overrode it.
5741
+ if (this._noHideOverridden) {
5742
+ this.noHideOnThisFocusLoss = this._priorNoHide;
5743
+ this._noHideOverridden = false;
5744
+ this._priorNoHide = undefined;
5745
+ }
5746
+
5645
5747
  if (this.isPopoverVisible) {
5646
5748
  if (!this.isBibFullscreen) {
5647
- // Desktop: show() doesn't trap focus, so use FocusTrap
5648
- this.focusTrap = new FocusTrap(this.bibContent);
5649
- this.focusTrap.focusFirstElement();
5749
+ if (this.desktopModal) {
5750
+ // The floater's focus-loss check uses :focus-within, which does not
5751
+ // match the host when focus is on a slotted element projected through
5752
+ // multiple shadow roots. Without this override, focusing a button
5753
+ // inside the bib would be misread as focus leaving the dropdown and
5754
+ // close the bib immediately. desktopModal traps focus inside the
5755
+ // bib and inerts siblings, so suppressing focus-loss dismissal here
5756
+ // is safe — Escape and outside-click still close the bib.
5757
+ this._priorNoHide = this.noHideOnThisFocusLoss;
5758
+ this._noHideOverridden = true;
5759
+ this.noHideOnThisFocusLoss = true;
5760
+
5761
+ // Desktop modal: trap focus only within the bib content.
5762
+ // Can't use FocusTrap on the bib element because keydown events
5763
+ // from slotted content bubble through the dropdown host (light DOM),
5764
+ // not through the bib (shadow projection target). Using FocusTrap
5765
+ // on the dropdown would include the trigger in the tab cycle.
5766
+ // Instead, listen for Tab on the dropdown and manually wrap focus
5767
+ // within the bib's focusable elements.
5768
+ this._bibTabHandler = (event) => {
5769
+ if (event.key !== 'Tab') {
5770
+ return;
5771
+ }
5772
+
5773
+ // Collect focusable elements from the bib content.
5774
+ const focusables = getFocusableElements(this.bibContent);
5775
+
5776
+ // Fallback: try from slotted content directly
5777
+ if (!focusables.length) {
5778
+ const slot = this.shadowRoot.querySelector('.slotContent slot');
5779
+ const assignedNodes = slot.assignedNodes({ flatten: true });
5780
+
5781
+ for (const node of assignedNodes) {
5782
+ if (node.nodeType === Node.ELEMENT_NODE) {
5783
+ focusables.push(...getFocusableElements(node));
5784
+ }
5785
+ }
5786
+ }
5787
+
5788
+ if (!focusables.length) {
5789
+ return;
5790
+ }
5791
+
5792
+ event.preventDefault();
5793
+
5794
+ const direction = event.shiftKey ? -1 : 1; // eslint-disable-line no-magic-numbers
5795
+
5796
+ // Walk the active element chain through shadow roots
5797
+ const actives = this._getActiveElements();
5798
+
5799
+ let idx = focusables.findIndex((el) => actives.includes(el));
5800
+
5801
+ if (idx === -1) { // eslint-disable-line no-magic-numbers
5802
+ // Focus is not on a known element — move to first/last
5803
+ idx = direction === 1 ? -1 : focusables.length; // eslint-disable-line no-magic-numbers
5804
+ }
5805
+
5806
+ // Try each element in order, skipping any that can't receive focus
5807
+ // (e.g. hidden elements, elements in collapsed sections)
5808
+ for (let index = 0; index < focusables.length; index++) { // eslint-disable-line no-plusplus
5809
+ let nextIdx = idx + direction;
5810
+
5811
+ // Wrap around
5812
+ if (nextIdx < 0) {
5813
+ nextIdx = focusables.length - 1;
5814
+ } else if (nextIdx >= focusables.length) {
5815
+ nextIdx = 0;
5816
+ }
5817
+
5818
+ focusables[nextIdx].focus();
5819
+
5820
+ // Verify focus actually moved to the target
5821
+ const newActives = this._getActiveElements();
5822
+
5823
+ if (newActives.includes(focusables[nextIdx])) {
5824
+ return;
5825
+ }
5826
+
5827
+ // Focus didn't stick — skip this element and try the next
5828
+ idx = nextIdx;
5829
+ }
5830
+ };
5831
+ this.addEventListener('keydown', this._bibTabHandler);
5832
+
5833
+ // Move initial focus into the bib content, matching FocusTrap behavior
5834
+ requestAnimationFrame(() => {
5835
+ const focusables = getFocusableElements(this.bibContent);
5836
+ if (focusables.length) {
5837
+ focusables[0].focus();
5838
+ }
5839
+ });
5840
+ } else {
5841
+ // Normal desktop: use FocusTrap on the bib element.
5842
+ // Defer focusFirstElement to the next frame because the popover
5843
+ // is positioned asynchronously by Floating UI — calling focus()
5844
+ // synchronously here would target elements with zero dimensions
5845
+ // and the focus call would be silently ignored.
5846
+ this.focusTrap = new FocusTrap(this.bibContent);
5847
+ requestAnimationFrame(() => {
5848
+ if (this.focusTrap) {
5849
+ this.focusTrap.focusFirstElement();
5850
+ }
5851
+ });
5852
+ }
5650
5853
  }
5651
5854
  // Fullscreen: showModal() provides native focus trapping
5855
+ }
5856
+ }
5857
+
5858
+ /**
5859
+ * Returns the chain of active (focused) elements through shadow roots.
5860
+ * @private
5861
+ * @returns {Array<HTMLElement>}
5862
+ */
5863
+ _getActiveElements() {
5864
+ let { activeElement } = document;
5865
+ const actives = [activeElement];
5866
+
5867
+ while (activeElement?.shadowRoot?.activeElement) {
5868
+ activeElement = activeElement.shadowRoot.activeElement;
5869
+ actives.push(activeElement);
5870
+ }
5871
+
5872
+ return actives;
5873
+ }
5874
+
5875
+ /**
5876
+ * Sets `inert` on sibling elements of the dropdown's top-level host
5877
+ * so that content outside the dropdown is not interactive while the modal is open.
5878
+ * Walks up through shadow DOM boundaries to find the outermost host element
5879
+ * in the light DOM, then sets `inert` on siblings at each ancestor level
5880
+ * to ensure all page content outside the host subtree is inert.
5881
+ * @private
5882
+ */
5883
+ _setPageInert() {
5884
+ if (this._inertSiblings) {
5652
5885
  return;
5653
5886
  }
5654
5887
 
5655
- if (this.focusTrap) {
5656
- this.focusTrap.disconnect();
5657
- this.focusTrap = undefined;
5888
+ this._inertSiblings = [];
5889
+
5890
+ // Walk up through shadow DOM boundaries to find the topmost host
5891
+ // element in the light DOM. For example, if this dropdown is inside
5892
+ // auro-datepicker's shadow DOM, we walk up to the datepicker element
5893
+ // so we set inert on its siblings — not on the datepicker itself.
5894
+ let host = this;
5895
+ while (host.getRootNode() instanceof ShadowRoot) {
5896
+ host = host.getRootNode().host;
5897
+ }
5898
+
5899
+ // Walk up the ancestor chain, inerting siblings at each level
5900
+ // to ensure the entire page outside the host subtree is inert.
5901
+ // Uses a reference counter (data-auro-inert-count) so multiple
5902
+ // simultaneous modal dropdowns share inert state safely.
5903
+ let current = host;
5904
+ while (current.parentElement) {
5905
+ const parent = current.parentElement;
5906
+ for (const sibling of parent.children) {
5907
+ if (sibling !== current) {
5908
+ const count = parseInt(sibling.dataset.auroInertCount || '0', 10);
5909
+ if (count === 0) {
5910
+ sibling.dataset.auroInertWas = sibling.inert ? 'true' : 'false';
5911
+ }
5912
+ sibling.dataset.auroInertCount = String(count + 1);
5913
+ sibling.inert = true;
5914
+ this._inertSiblings.push(sibling);
5915
+ }
5916
+ }
5917
+ current = parent;
5918
+ }
5919
+ }
5920
+
5921
+ /**
5922
+ * Restores `inert` state on siblings that were tracked by `_setPageInert`.
5923
+ * Uses reference counting so inert is only cleared when the last modal
5924
+ * dropdown releases a given element. Preserves the original inert state
5925
+ * so externally-inerted elements are not inadvertently re-enabled.
5926
+ * @private
5927
+ */
5928
+ _clearPageInert() {
5929
+ if (this._inertSiblings) {
5930
+ for (const sibling of this._inertSiblings) {
5931
+ const count = parseInt(sibling.dataset.auroInertCount || '1', 10) - 1;
5932
+ if (count <= 0) {
5933
+ const wasInert = sibling.dataset.auroInertWas === 'true';
5934
+ delete sibling.dataset.auroInertCount;
5935
+ delete sibling.dataset.auroInertWas;
5936
+ sibling.inert = wasInert;
5937
+ } else {
5938
+ sibling.dataset.auroInertCount = String(count);
5939
+ }
5940
+ }
5941
+ this._inertSiblings = undefined;
5658
5942
  }
5659
5943
  }
5660
5944
 
@@ -5893,6 +6177,7 @@ class AuroDropdown extends AuroElement {
5893
6177
  shape="${this.shape}"
5894
6178
  ?data-show="${this.isPopoverVisible}"
5895
6179
  ?isfullscreen="${this.isBibFullscreen}"
6180
+ ?desktopmodal="${this.desktopModal}"
5896
6181
  .dialogLabel="${this.bibDialogLabel}"
5897
6182
  ${ref(this.bibElement)}
5898
6183
  >
@@ -6260,6 +6545,14 @@ class AuroBibtemplate extends LitElement {
6260
6545
  large: {
6261
6546
  type: Boolean,
6262
6547
  reflect: true
6548
+ },
6549
+
6550
+ /**
6551
+ * If declared, the footer slot will be rendered even when not in fullscreen mode.
6552
+ */
6553
+ showFooter: {
6554
+ type: Boolean,
6555
+ reflect: true
6263
6556
  }
6264
6557
  };
6265
6558
  }
@@ -6362,7 +6655,7 @@ class AuroBibtemplate extends LitElement {
6362
6655
  <slot></slot>
6363
6656
  </div>
6364
6657
 
6365
- ${this.isFullscreen ? html`
6658
+ ${this.isFullscreen || this.showFooter ? html`
6366
6659
  <div id="footerContainer">
6367
6660
  <slot name="footer"></slot>
6368
6661
  </div>` : null}
@@ -6599,7 +6892,7 @@ class AuroHelpText extends LitElement {
6599
6892
  }
6600
6893
  }
6601
6894
 
6602
- var formkitVersion = '202606031929';
6895
+ var formkitVersion = '202606181613';
6603
6896
 
6604
6897
  var styleCss = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}.body-default{font-size:var(--wcss-body-default-font-size, 1rem);font-weight:var(--wcss-body-default-weight, );line-height:var(--wcss-body-default-line-height, 1.5rem)}.body-default,.body-default-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-default-emphasized{font-size:var(--wcss-body-default-emphasized-font-size, 1rem);font-weight:var(--wcss-body-default-emphasized-weight, );line-height:var(--wcss-body-default-emphasized-line-height, 1.5rem)}.body-lg{font-size:var(--wcss-body-lg-font-size, 1.125rem);font-weight:var(--wcss-body-lg-weight, );line-height:var(--wcss-body-lg-line-height, 1.625rem)}.body-lg,.body-lg-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-lg-emphasized{font-size:var(--wcss-body-lg-emphasized-font-size, 1.125rem);font-weight:var(--wcss-body-lg-emphasized-weight, );line-height:var(--wcss-body-lg-emphasized-line-height, 1.625rem)}.body-sm{font-size:var(--wcss-body-sm-font-size, 0.875rem);font-weight:var(--wcss-body-sm-weight, );line-height:var(--wcss-body-sm-line-height, 1.25rem)}.body-sm,.body-sm-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-sm-emphasized{font-size:var(--wcss-body-sm-emphasized-font-size, 0.875rem);font-weight:var(--wcss-body-sm-emphasized-weight, );line-height:var(--wcss-body-sm-emphasized-line-height, 1.25rem)}.body-xs{font-size:var(--wcss-body-xs-font-size, 0.75rem);font-weight:var(--wcss-body-xs-weight, );line-height:var(--wcss-body-xs-line-height, 1rem)}.body-xs,.body-xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-xs-emphasized{font-size:var(--wcss-body-xs-emphasized-font-size, 0.75rem);font-weight:var(--wcss-body-xs-emphasized-weight, );line-height:var(--wcss-body-xs-emphasized-line-height, 1rem)}.body-2xs{font-size:var(--wcss-body-2xs-font-size, 0.625rem);font-weight:var(--wcss-body-2xs-weight, );line-height:var(--wcss-body-2xs-line-height, 0.875rem)}.body-2xs,.body-2xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-2xs-emphasized{font-size:var(--wcss-body-2xs-emphasized-font-size, 0.625rem);font-weight:var(--wcss-body-2xs-emphasized-weight, );line-height:var(--wcss-body-2xs-emphasized-line-height, 0.875rem)}.display-2xl{font-family:var(--wcss-display-2xl-family, "AS Circular"),var(--wcss-display-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-2xl-font-size, clamp(3.5rem, 6vw, 5.375rem));font-weight:var(--wcss-display-2xl-weight, 300);letter-spacing:var(--wcss-display-2xl-letter-spacing, 0);line-height:var(--wcss-display-2xl-line-height, 1.3)}.display-xl{font-family:var(--wcss-display-xl-family, "AS Circular"),var(--wcss-display-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-xl-font-size, clamp(3rem, 5.3333333333vw, 4.5rem));font-weight:var(--wcss-display-xl-weight, 300);letter-spacing:var(--wcss-display-xl-letter-spacing, 0);line-height:var(--wcss-display-xl-line-height, 1.3)}.display-lg{font-family:var(--wcss-display-lg-family, "AS Circular"),var(--wcss-display-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-lg-font-size, clamp(2.75rem, 4.6666666667vw, 4rem));font-weight:var(--wcss-display-lg-weight, 300);letter-spacing:var(--wcss-display-lg-letter-spacing, 0);line-height:var(--wcss-display-lg-line-height, 1.3)}.display-md{font-family:var(--wcss-display-md-family, "AS Circular"),var(--wcss-display-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-md-font-size, clamp(2.5rem, 4vw, 3.5rem));font-weight:var(--wcss-display-md-weight, 300);letter-spacing:var(--wcss-display-md-letter-spacing, 0);line-height:var(--wcss-display-md-line-height, 1.3)}.display-sm{font-family:var(--wcss-display-sm-family, "AS Circular"),var(--wcss-display-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-sm-font-size, clamp(2rem, 3.6666666667vw, 3rem));font-weight:var(--wcss-display-sm-weight, 300);letter-spacing:var(--wcss-display-sm-letter-spacing, 0);line-height:var(--wcss-display-sm-line-height, 1.3)}.display-xs{font-family:var(--wcss-display-xs-family, "AS Circular"),var(--wcss-display-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-xs-font-size, clamp(1.75rem, 3vw, 2.375rem));font-weight:var(--wcss-display-xs-weight, 300);letter-spacing:var(--wcss-display-xs-letter-spacing, 0);line-height:var(--wcss-display-xs-line-height, 1.3)}.heading-xl{font-family:var(--wcss-heading-xl-family, "AS Circular"),var(--wcss-heading-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-xl-font-size, clamp(2rem, 3vw, 2.5rem));font-weight:var(--wcss-heading-xl-weight, 300);letter-spacing:var(--wcss-heading-xl-letter-spacing, 0);line-height:var(--wcss-heading-xl-line-height, 1.3)}.heading-lg{font-family:var(--wcss-heading-lg-family, "AS Circular"),var(--wcss-heading-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-lg-font-size, clamp(1.75rem, 2.6666666667vw, 2.25rem));font-weight:var(--wcss-heading-lg-weight, 300);letter-spacing:var(--wcss-heading-lg-letter-spacing, 0);line-height:var(--wcss-heading-lg-line-height, 1.3)}.heading-md{font-family:var(--wcss-heading-md-family, "AS Circular"),var(--wcss-heading-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-md-font-size, clamp(1.625rem, 2.3333333333vw, 1.75rem));font-weight:var(--wcss-heading-md-weight, 300);letter-spacing:var(--wcss-heading-md-letter-spacing, 0);line-height:var(--wcss-heading-md-line-height, 1.3)}.heading-sm{font-family:var(--wcss-heading-sm-family, "AS Circular"),var(--wcss-heading-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-sm-font-size, clamp(1.375rem, 2vw, 1.5rem));font-weight:var(--wcss-heading-sm-weight, 300);letter-spacing:var(--wcss-heading-sm-letter-spacing, 0);line-height:var(--wcss-heading-sm-line-height, 1.3)}.heading-xs{font-family:var(--wcss-heading-xs-family, "AS Circular"),var(--wcss-heading-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-xs-font-size, clamp(1.25rem, 1.6666666667vw, 1.25rem));font-weight:var(--wcss-heading-xs-weight, 300);letter-spacing:var(--wcss-heading-xs-letter-spacing, 0);line-height:var(--wcss-heading-xs-line-height, 1.3)}.heading-2xs{font-family:var(--wcss-heading-2xs-family, "AS Circular"),var(--wcss-heading-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-2xs-font-size, clamp(1.125rem, 1.5vw, 1.125rem));font-weight:var(--wcss-heading-2xs-weight, 300);letter-spacing:var(--wcss-heading-2xs-letter-spacing, 0);line-height:var(--wcss-heading-2xs-line-height, 1.3)}.accent-2xl{font-family:var(--wcss-accent-2xl-family, "Good OT"),var(--wcss-accent-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-2xl-font-size, clamp(2rem, 3.1666666667vw, 2.375rem));font-weight:var(--wcss-accent-2xl-weight, 450);letter-spacing:var(--wcss-accent-2xl-letter-spacing, 0.05em);line-height:var(--wcss-accent-2xl-line-height, 1)}.accent-2xl,.accent-xl{text-transform:uppercase}.accent-xl{font-family:var(--wcss-accent-xl-family, "Good OT"),var(--wcss-accent-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-xl-font-size, clamp(1.625rem, 2.3333333333vw, 2rem));font-weight:var(--wcss-accent-xl-weight, 450);letter-spacing:var(--wcss-accent-xl-letter-spacing, 0.05em);line-height:var(--wcss-accent-xl-line-height, 1.3)}.accent-lg{font-family:var(--wcss-accent-lg-family, "Good OT"),var(--wcss-accent-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-lg-font-size, clamp(1.5rem, 2.1666666667vw, 1.75rem));font-weight:var(--wcss-accent-lg-weight, 450);letter-spacing:var(--wcss-accent-lg-letter-spacing, 0.05em);line-height:var(--wcss-accent-lg-line-height, 1.3)}.accent-lg,.accent-md{text-transform:uppercase}.accent-md{font-family:var(--wcss-accent-md-family, "Good OT"),var(--wcss-accent-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-md-font-size, clamp(1.375rem, 1.8333333333vw, 1.5rem));font-weight:var(--wcss-accent-md-weight, 500);letter-spacing:var(--wcss-accent-md-letter-spacing, 0.05em);line-height:var(--wcss-accent-md-line-height, 1.3)}.accent-sm{font-family:var(--wcss-accent-sm-family, "Good OT"),var(--wcss-accent-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-sm-font-size, clamp(1.125rem, 1.5vw, 1.25rem));font-weight:var(--wcss-accent-sm-weight, 500);letter-spacing:var(--wcss-accent-sm-letter-spacing, 0.05em);line-height:var(--wcss-accent-sm-line-height, 1.3)}.accent-sm,.accent-xs{text-transform:uppercase}.accent-xs{font-family:var(--wcss-accent-xs-family, "Good OT"),var(--wcss-accent-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-xs-font-size, clamp(1rem, 1.3333333333vw, 1rem));font-weight:var(--wcss-accent-xs-weight, 500);letter-spacing:var(--wcss-accent-xs-letter-spacing, 0.1em);line-height:var(--wcss-accent-xs-line-height, 1.3)}.accent-2xs{font-family:var(--wcss-accent-2xs-family, "Good OT"),var(--wcss-accent-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-2xs-font-size, clamp(0.875rem, 1.1666666667vw, 0.875rem));font-weight:var(--wcss-accent-2xs-weight, 450);letter-spacing:var(--wcss-accent-2xs-letter-spacing, 0.1em);line-height:var(--wcss-accent-2xs-line-height, 1.3);text-transform:uppercase}[auro-dropdown]{--ds-auro-dropdown-trigger-border-color: var(--ds-auro-select-border-color);--ds-auro-dropdown-trigger-background-color: var(--ds-auro-select-background-color);--ds-auro-dropdown-trigger-container-color: var(--ds-auro-select-background-color);--ds-auro-dropdown-trigger-outline-color: var(--ds-auro-select-outline-color)}:host{display:inline-block;text-align:left;vertical-align:top}:host([layout*=emphasized]) [auro-dropdown],:host([layout*=snowflake]) [auro-dropdown]{--ds-auro-select-border-color: transparent}:host([layout*=emphasized]) .mainContent,:host([layout*=snowflake]) .mainContent{text-align:center}.mainContent{position:relative;display:flex;overflow:hidden;flex:1;flex-direction:column;align-items:center;justify-content:center}.valueContainer [slot=displayValue]{display:none}.accents{display:flex;flex-direction:row;align-items:center;justify-content:center}::slotted([slot=typeIcon]){margin-right:var(--ds-size-100, 0.5rem)}.displayValue{display:block}.displayValue:not(.force){display:none}.displayValue:not(.force).hasContent:is(.withValue):not(.hasFocus){display:block}.triggerContent{display:flex;width:100%;align-items:center;justify-content:center}:host([layout*=emphasized]) .triggerContent{padding:0 var(--ds-size-100, 0.5rem) 0 var(--ds-size-300, 1.5rem)}:host([layout*=snowflake]) .triggerContent{padding:0 var(--ds-size-100, 0.5rem) 0 var(--ds-size-200, 1rem)}:host([layout*=snowflake]) label{padding-block:var(--ds-size-25, 0.125rem)}:host([layout*=classic]) .triggerContent{padding:0 var(--ds-size-100, 0.5rem)}:host([layout*=classic]) .mainContent{align-items:start}:host([layout*=classic]) label{overflow:hidden;cursor:text;text-overflow:ellipsis;white-space:nowrap}:host([layout*=classic]) .value{height:auto}label{color:var(--ds-auro-select-label-text-color)}:host(:is([validity]:not([validity=valid]))) [auro-dropdown]{--ds-auro-select-border-color: var(--ds-basic-color-status-error, #e31f26);--ds-auro-select-outline-color: var(--ds-basic-color-status-error, #e31f26);--ds-auro-dropdown-helptext-text-color: var(--ds-basic-color-texticon-default, #2a2a2a)}:host([ondark]:is([validity]:not([validity=valid]))) [auro-dropdown],:host([appearance=inverse]:is([validity]:not([validity=valid]))) [auro-dropdown]{--ds-auro-select-border-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8);--ds-auro-select-outline-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8);--ds-auro-dropdown-helptext-text-color: var(--ds-basic-color-texticon-inverse, #ffffff)}#slotHolder{display:none}:host([fluid]){width:100%}:host([disabled]){pointer-events:none;user-select:none}:host([disabled]:not([ondark])) [auro-dropdown],:host([disabled]:not([appearance=inverse])) [auro-dropdown]{--ds-auro-select-border-color: var(--ds-basic-color-border-subtle, #dddddd)}:host(:not([layout*=classic])[disabled][ondark]) [auro-dropdown],:host(:not([layout*=classic])[disabled][appearance=inverse]) [auro-dropdown]{--ds-auro-select-border-color: transparent}`;
6605
6898
 
@@ -6704,6 +6997,16 @@ class AuroSelect extends AuroElement$1 {
6704
6997
  * @private
6705
6998
  */
6706
6999
  this.hasDisplayValueContent = false;
7000
+
7001
+ /**
7002
+ * @private
7003
+ */
7004
+ this.typeaheadBuffer = '';
7005
+
7006
+ /**
7007
+ * @private
7008
+ */
7009
+ this._typeaheadTimeout = null;
6707
7010
  }
6708
7011
 
6709
7012
  /**
@@ -6716,6 +7019,7 @@ class AuroSelect extends AuroElement$1 {
6716
7019
  this.fullscreenBreakpoint = 'sm';
6717
7020
  this.onDark = false;
6718
7021
  this.isPopoverVisible = false;
7022
+ this.typeaheadTimeoutMs = 500;
6719
7023
 
6720
7024
  // Layout Config
6721
7025
  this.layout = 'classic';
@@ -7019,6 +7323,16 @@ class AuroSelect extends AuroElement$1 {
7019
7323
  attribute: false
7020
7324
  },
7021
7325
 
7326
+ /**
7327
+ * Milliseconds of keyboard inactivity before the type-ahead buffer resets.
7328
+ * Increase for users who type slowly.
7329
+ * @default 500
7330
+ */
7331
+ typeaheadTimeoutMs: {
7332
+ type: Number,
7333
+ reflect: true
7334
+ },
7335
+
7022
7336
  /**
7023
7337
  * Specifies the `validityState` this element is in.
7024
7338
  */
@@ -7151,7 +7465,7 @@ class AuroSelect extends AuroElement$1 {
7151
7465
  this.menu.updateActiveOption(this.optionSelected[0]);
7152
7466
  } else {
7153
7467
  // If no activeOption has yet to be set, then make the first enabled option active by default
7154
- const firstActive = this.menu.menuService.menuOptions.find((option) => !option.disabled);
7468
+ const [firstActive] = getEnabledOptions(this.menu);
7155
7469
  this.menu.updateActiveOption(firstActive);
7156
7470
  }
7157
7471
  }
@@ -7375,12 +7689,23 @@ class AuroSelect extends AuroElement$1 {
7375
7689
  this.menu.multiSelect = this.multiSelect;
7376
7690
  }
7377
7691
 
7692
+ // Menu's items are populated by initItems() during its firstUpdated/slotchange.
7693
+ // Since the parent select's firstUpdated runs before the child menu's, call initItems()
7694
+ // here so renderNativeSelect can render the native <option> list on first paint.
7695
+ if (typeof this.menu.initItems === 'function') {
7696
+ this.menu.initItems();
7697
+ }
7378
7698
  this.options = this.menu.options;
7379
7699
  this.updateOptionPositions();
7380
7700
  this.menu.addEventListener("auroMenu-loadingChange", (event) => this.handleMenuLoadingChange(event));
7381
7701
 
7382
- this.menu.addEventListener("auroMenu-deselectPrevented", () => {
7383
- this.hideBib();
7702
+ this.menu.addEventListener("auroMenu-selectValueFailure", () => {
7703
+ this.value = undefined;
7704
+ this.optionSelected = this.multiSelect ? [] : undefined;
7705
+ // The trigger label is rendered imperatively into #value, so a property
7706
+ // change alone won't clear it. Refresh so stale text doesn't linger when
7707
+ // a runtime value change fails to match any option.
7708
+ this.updateDisplayedValue();
7384
7709
  });
7385
7710
 
7386
7711
  this.menu.addEventListener('auroMenu-activatedOption', (evt) => {
@@ -7402,16 +7727,20 @@ class AuroSelect extends AuroElement$1 {
7402
7727
  }
7403
7728
  });
7404
7729
 
7405
- this.menu.addEventListener('auroMenu-selectedOption', (event) => {
7730
+ this.menu.addEventListener('auroMenu-selectedOption', () => {
7406
7731
 
7407
7732
  // Update the displayed value
7408
7733
  this.updateDisplayedValue();
7409
7734
 
7410
- const options = event.detail.options || [];
7411
-
7412
- this.value = event.detail.stringValue;
7735
+ this.value = this.menu.value;
7413
7736
 
7414
- this.optionSelected = this.multiSelect ? options : options[0];
7737
+ // Clone the multiselect array so external mutation of select.optionSelected
7738
+ // can't reach back into menu's internal state through a shared reference.
7739
+ let nextSelected = this.menu.optionSelected;
7740
+ if (this.multiSelect) {
7741
+ nextSelected = Array.isArray(nextSelected) ? [...nextSelected] : [];
7742
+ }
7743
+ this.optionSelected = nextSelected;
7415
7744
 
7416
7745
  if (this.dropdown.isPopoverVisible && !this.multiSelect) {
7417
7746
  this.dropdown.hide();
@@ -7420,7 +7749,7 @@ class AuroSelect extends AuroElement$1 {
7420
7749
 
7421
7750
  // Announce the selection after the dropdown closes so it isn't
7422
7751
  // overridden by VoiceOver's "collapsed" announcement from aria-expanded.
7423
- const selectedValue = event.detail.stringValue;
7752
+ const selectedValue = this.menu.currentLabel;
7424
7753
  const announcementDelay = 300;
7425
7754
  setTimeout(() => {
7426
7755
  announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
@@ -7443,6 +7772,7 @@ class AuroSelect extends AuroElement$1 {
7443
7772
  this.addEventListener('blur', () => {
7444
7773
  this.validate();
7445
7774
  this.hasFocus = false;
7775
+ this._clearTypeaheadBuffer();
7446
7776
  });
7447
7777
  }
7448
7778
 
@@ -7456,41 +7786,89 @@ class AuroSelect extends AuroElement$1 {
7456
7786
  }
7457
7787
  }
7458
7788
 
7789
+ /**
7790
+ * Returns the lowercase, trimmed text content of a menu option.
7791
+ * @private
7792
+ * @param {HTMLElement} option - The menu option element.
7793
+ * @returns {string}
7794
+ */
7795
+ _getOptionDisplayText(option) {
7796
+ return (option.textContent || '').trim().toLowerCase();
7797
+ }
7798
+
7799
+ /**
7800
+ * Empties the type-ahead buffer and cancels any pending reset timeout.
7801
+ * Called when focus leaves the component, when Escape closes the bib, and on disconnect
7802
+ * so a stale buffer never bridges into a fresh interaction.
7803
+ * @private
7804
+ */
7805
+ _clearTypeaheadBuffer() {
7806
+ if (this._typeaheadTimeout) {
7807
+ clearTimeout(this._typeaheadTimeout);
7808
+ this._typeaheadTimeout = null;
7809
+ }
7810
+ this.typeaheadBuffer = '';
7811
+ }
7812
+
7459
7813
  /**
7460
7814
  * Updates the active option in the menu based on keyboard input.
7815
+ *
7816
+ * Implements the WAI-ARIA APG Listbox type-ahead pattern: accumulates printable
7817
+ * keystrokes into a buffer that resets after `typeaheadTimeoutMs` of inactivity.
7818
+ * A multi-character buffer matches the first option whose displayed text starts
7819
+ * with the buffer; repeating a single character cycles through options that start
7820
+ * with that character.
7461
7821
  * @private
7462
7822
  * @param {string} _key - The key pressed by the user.
7463
7823
  * @returns {void}
7464
7824
  */
7465
7825
  updateActiveOptionBasedOnKey(_key) {
7826
+ // Ignore non-printable keys (Shift, ArrowDown, Tab, etc.)
7827
+ if (typeof _key !== 'string' || _key.length !== 1) {
7828
+ return;
7829
+ }
7466
7830
 
7467
- // Get a lowercase version of the key pressed
7468
- const key = _key.toLowerCase();
7469
-
7470
- // Calculate how many times the same letter has been pressed
7471
- this.sameLetterTimes = key === this.lastLetter ? this.sameLetterTimes + 1 : 0;
7831
+ // No selectable options to match against clear any stale buffer/timer so
7832
+ // Space stays a bib toggle and the next keystroke after fresh options load
7833
+ // starts cleanly. Uses auro-menuoption's `isActive` getter, which excludes
7834
+ // disabled, hidden, and static options (e.g. `static nomatch` placeholders).
7835
+ // Checked BEFORE mutating the buffer.
7836
+ const options = (this.menu && this.menu.options ? this.menu.options : []).filter((option) => option.isActive);
7837
+ if (!options.length) {
7838
+ this._clearTypeaheadBuffer();
7839
+ return;
7840
+ }
7472
7841
 
7473
- // Set last letter for tracking
7474
- this.lastLetter = key;
7842
+ const key = _key.toLowerCase();
7475
7843
 
7476
- // Get all the options that start with the last letter pressed
7477
- const letterOptions = this.menu.options.filter((option) => {
7478
- const optionText = option.value || '';
7479
- return optionText.toLowerCase().startsWith(this.lastLetter);
7480
- });
7844
+ // Reset the buffer after a period of inactivity
7845
+ if (this._typeaheadTimeout) {
7846
+ clearTimeout(this._typeaheadTimeout);
7847
+ }
7848
+ this._typeaheadTimeout = setTimeout(() => {
7849
+ this._clearTypeaheadBuffer();
7850
+ }, this.typeaheadTimeoutMs);
7481
7851
 
7482
- // If we have options that match the letter pressed
7483
- if (letterOptions.length) {
7852
+ this.typeaheadBuffer += key;
7484
7853
 
7485
- // Show the dropdown if it is not already visible
7486
- this.dropdown.show();
7854
+ const isRepeatedChar = this.typeaheadBuffer.length > 1 && new Set(this.typeaheadBuffer).size === 1;
7487
7855
 
7488
- // Get the index we're after based on how many times the letter has been pressed and the length of the letterOptions array
7489
- const index = this.sameLetterTimes < letterOptions.length ? this.sameLetterTimes : this.sameLetterTimes % letterOptions.length;
7856
+ let match = null;
7857
+ if (isRepeatedChar) {
7858
+ const matches = options.filter((option) => this._getOptionDisplayText(option).startsWith(key));
7859
+ if (matches.length) {
7860
+ const cycleIndex = (this.typeaheadBuffer.length - 1) % matches.length;
7861
+ match = matches[cycleIndex];
7862
+ }
7863
+ } else {
7864
+ match = options.find((option) => this._getOptionDisplayText(option).startsWith(this.typeaheadBuffer));
7865
+ }
7490
7866
 
7491
- // Select the new option in the menu
7492
- const newOption = letterOptions[index];
7493
- this.menu.updateActiveOption(newOption);
7867
+ if (match) {
7868
+ if (!this.dropdown.isPopoverVisible) {
7869
+ this.dropdown.show();
7870
+ }
7871
+ this.menu.updateActiveOption(match);
7494
7872
  }
7495
7873
  }
7496
7874
 
@@ -7604,6 +7982,11 @@ class AuroSelect extends AuroElement$1 {
7604
7982
  }
7605
7983
  }
7606
7984
 
7985
+ disconnectedCallback() {
7986
+ super.disconnectedCallback();
7987
+ this._clearTypeaheadBuffer();
7988
+ }
7989
+
7607
7990
  // lifecycle runs only after the element's DOM has been updated the first time
7608
7991
  firstUpdated() {
7609
7992
  // Add the tag name as an attribute if it is different than the component name
@@ -7616,7 +7999,7 @@ class AuroSelect extends AuroElement$1 {
7616
7999
 
7617
8000
  setMenuValue(value) {
7618
8001
  if (!this.menu) return;
7619
- this.menu.value = value;
8002
+ this.menu.selectByValue(value);
7620
8003
  }
7621
8004
 
7622
8005
  /**