@aurodesignsystem-dev/auro-formkit 0.0.0-pr1503.3 → 0.0.0-pr1503.5

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 (190) hide show
  1. package/components/bibtemplate/dist/index.js +1 -1
  2. package/components/bibtemplate/dist/registered.js +1 -1
  3. package/components/checkbox/demo/accessibility.md +1 -1
  4. package/components/checkbox/demo/customize.html +1 -2
  5. package/components/checkbox/demo/customize.min.js +23 -8
  6. package/components/checkbox/demo/getting-started.min.js +23 -8
  7. package/components/checkbox/demo/index.min.js +23 -8
  8. package/components/checkbox/dist/index.js +23 -8
  9. package/components/checkbox/dist/registered.js +23 -8
  10. package/components/combobox/README.md +1 -1
  11. package/components/combobox/demo/accessibility.md +1 -1
  12. package/components/combobox/demo/api.md +2 -2
  13. package/components/combobox/demo/customize.html +1 -2
  14. package/components/combobox/demo/customize.md +166 -142
  15. package/components/combobox/demo/customize.min.js +2515 -2316
  16. package/components/combobox/demo/getting-started.min.js +2515 -2312
  17. package/components/combobox/demo/index.md +2 -1
  18. package/components/combobox/demo/index.min.js +2515 -2312
  19. package/components/combobox/demo/keyboard-behavior.md +3 -146
  20. package/components/combobox/demo/readme.md +1 -1
  21. package/components/combobox/demo/why-combobox.md +2 -2
  22. package/components/combobox/dist/auro-combobox.d.ts +49 -15
  23. package/components/combobox/dist/index.js +1462 -753
  24. package/components/combobox/dist/registered.js +1462 -753
  25. package/components/counter/demo/customize.min.js +188 -244
  26. package/components/counter/demo/index.min.js +188 -244
  27. package/components/counter/dist/auro-counter.d.ts +0 -8
  28. package/components/counter/dist/index.js +188 -244
  29. package/components/counter/dist/registered.js +188 -244
  30. package/components/datepicker/demo/accessibility.md +20 -10
  31. package/components/datepicker/demo/api.md +65 -62
  32. package/components/datepicker/demo/customize.md +180 -40
  33. package/components/datepicker/demo/customize.min.js +1690 -777
  34. package/components/datepicker/demo/getting-started.md +118 -2
  35. package/components/datepicker/demo/index.min.js +1690 -759
  36. package/components/datepicker/demo/keyboard-behavior.md +3 -3
  37. package/components/datepicker/demo/voiceover.md +4 -4
  38. package/components/datepicker/demo/why-datepicker.md +2 -2
  39. package/components/datepicker/dist/{src/auro-calendar-cell.d.ts → auro-calendar-cell.d.ts} +48 -3
  40. package/components/datepicker/dist/{src/auro-calendar.d.ts → auro-calendar.d.ts} +188 -10
  41. package/components/datepicker/dist/{src/auro-datepicker.d.ts → auro-datepicker.d.ts} +89 -6
  42. package/components/datepicker/dist/blackoutUtils.d.ts +43 -0
  43. package/components/datepicker/dist/index.js +1690 -759
  44. package/components/datepicker/dist/registered.js +1690 -759
  45. package/components/dropdown/demo/customize.html +3 -0
  46. package/components/dropdown/demo/customize.min.js +142 -208
  47. package/components/dropdown/demo/getting-started.min.js +131 -207
  48. package/components/dropdown/demo/index.min.js +131 -207
  49. package/components/dropdown/dist/auro-dropdown.d.ts +17 -1
  50. package/components/dropdown/dist/index.js +131 -207
  51. package/components/dropdown/dist/registered.js +131 -207
  52. package/components/form/demo/api.md +3 -3
  53. package/components/form/demo/customize.html +6 -6
  54. package/components/form/demo/customize.md +535 -135
  55. package/components/form/demo/customize.min.js +6307 -4378
  56. package/components/form/demo/getting-started.md +344 -96
  57. package/components/form/demo/getting-started.min.js +6255 -4378
  58. package/components/form/demo/index.min.js +6206 -4378
  59. package/components/form/demo/registerDemoDeps.min.js +5827 -4333
  60. package/components/form/dist/auro-form.d.ts +128 -10
  61. package/components/form/dist/index.js +377 -43
  62. package/components/form/dist/registered.js +377 -43
  63. package/components/input/demo/accessibility.md +1 -1
  64. package/components/input/demo/api.md +58 -57
  65. package/components/input/demo/customize.html +1 -2
  66. package/components/input/demo/customize.md +62 -61
  67. package/components/input/demo/customize.min.js +396 -275
  68. package/components/input/demo/getting-started.min.js +396 -275
  69. package/components/input/demo/index.min.js +396 -275
  70. package/components/input/dist/auro-input.d.ts +1 -1
  71. package/components/input/dist/base-input.d.ts +60 -81
  72. package/components/input/dist/index.js +409 -276
  73. package/components/input/dist/registered.js +396 -275
  74. package/components/input/dist/utilities.d.ts +10 -1
  75. package/components/menu/demo/accessibility.md +9 -4
  76. package/components/menu/demo/api.md +48 -45
  77. package/components/menu/demo/css-only.md +26 -19
  78. package/components/menu/demo/customize.md +307 -75
  79. package/components/menu/demo/design.md +1 -1
  80. package/components/menu/demo/getting-started.md +144 -7
  81. package/components/menu/demo/index.min.js +961 -1341
  82. package/components/menu/demo/keyboard-behavior.md +83 -4
  83. package/components/menu/demo/voiceover.md +21 -14
  84. package/components/menu/demo/why-menu.md +8 -9
  85. package/components/menu/dist/auro-menu-utils.d.ts +19 -0
  86. package/components/menu/dist/auro-menu.d.ts +97 -116
  87. package/components/menu/dist/auro-menuoption.d.ts +39 -133
  88. package/components/menu/dist/index.js +823 -1309
  89. package/components/menu/dist/registered.js +835 -1309
  90. package/components/radio/demo/accessibility.md +1 -1
  91. package/components/radio/demo/customize.min.js +23 -8
  92. package/components/radio/demo/getting-started.min.js +23 -8
  93. package/components/radio/demo/index.min.js +23 -8
  94. package/components/radio/dist/index.js +23 -8
  95. package/components/radio/dist/registered.js +23 -8
  96. package/components/select/demo/accessibility.md +6 -1
  97. package/components/select/demo/api.md +3 -2
  98. package/components/select/demo/customize.html +1 -2
  99. package/components/select/demo/customize.md +210 -5
  100. package/components/select/demo/customize.min.js +1613 -1854
  101. package/components/select/demo/design.md +10 -10
  102. package/components/select/demo/getting-started.md +1 -1
  103. package/components/select/demo/getting-started.min.js +1617 -1903
  104. package/components/select/demo/index.md +2 -2
  105. package/components/select/demo/index.min.js +1613 -1854
  106. package/components/select/demo/keyboard-behavior.md +81 -54
  107. package/components/select/demo/voiceover.md +28 -15
  108. package/components/select/dist/auro-select.d.ts +70 -12
  109. package/components/select/dist/index.js +580 -315
  110. package/components/select/dist/registered.js +580 -315
  111. package/components/select/dist/selectUtils.d.ts +24 -0
  112. package/custom-elements.json +8071 -7919
  113. package/package.json +7 -3
  114. package/components/checkbox/demo/customize.js +0 -22
  115. package/components/checkbox/demo/demo-support.js +0 -1
  116. package/components/checkbox/demo/getting-started.js +0 -22
  117. package/components/checkbox/demo/index.js +0 -8
  118. package/components/checkbox/demo/styles.scss +0 -1
  119. package/components/combobox/demo/customize.js +0 -24
  120. package/components/combobox/demo/demo-support.js +0 -1
  121. package/components/combobox/demo/getting-started.js +0 -21
  122. package/components/combobox/demo/index.js +0 -23
  123. package/components/combobox/demo/styles.scss +0 -1
  124. package/components/counter/demo/customize.js +0 -21
  125. package/components/counter/demo/demo-support.js +0 -1
  126. package/components/counter/demo/index.js +0 -21
  127. package/components/counter/demo/styles.scss +0 -1
  128. package/components/datepicker/demo/customize.js +0 -19
  129. package/components/datepicker/demo/demo-support.js +0 -1
  130. package/components/datepicker/demo/index.js +0 -23
  131. package/components/datepicker/demo/styles.scss +0 -1
  132. package/components/dropdown/demo/customize.js +0 -9
  133. package/components/dropdown/demo/demo-support.js +0 -1
  134. package/components/dropdown/demo/getting-started.js +0 -9
  135. package/components/dropdown/demo/index.js +0 -16
  136. package/components/dropdown/demo/styles.scss +0 -1
  137. package/components/form/demo/customize.js +0 -9
  138. package/components/form/demo/demo-support.js +0 -1
  139. package/components/form/demo/getting-started.js +0 -9
  140. package/components/form/demo/index.js +0 -5
  141. package/components/form/demo/registerDemoDeps.js +0 -23
  142. package/components/form/demo/styles.scss +0 -1
  143. package/components/input/demo/customize.js +0 -25
  144. package/components/input/demo/demo-support.js +0 -1
  145. package/components/input/demo/getting-started.js +0 -8
  146. package/components/input/demo/index.js +0 -8
  147. package/components/input/demo/styles.css +0 -974
  148. package/components/input/demo/styles.scss +0 -1
  149. package/components/menu/demo/demo-support.js +0 -1
  150. package/components/menu/demo/index.js +0 -25
  151. package/components/menu/demo/styles.scss +0 -1
  152. package/components/menu/dist/auro-menu.context.d.ts +0 -238
  153. package/components/radio/demo/customize.js +0 -22
  154. package/components/radio/demo/demo-support.js +0 -1
  155. package/components/radio/demo/getting-started.js +0 -24
  156. package/components/radio/demo/index.js +0 -8
  157. package/components/radio/demo/styles.scss +0 -1
  158. package/components/select/demo/customize.js +0 -11
  159. package/components/select/demo/demo-support.js +0 -1
  160. package/components/select/demo/getting-started.js +0 -26
  161. package/components/select/demo/index.js +0 -11
  162. package/components/select/demo/styles.scss +0 -1
  163. /package/components/counter/dist/styles/{counter-wrapper-color-css.d.ts → counter-group-color-css.d.ts} +0 -0
  164. /package/components/datepicker/dist/{src/auro-calendar-month.d.ts → auro-calendar-month.d.ts} +0 -0
  165. /package/components/datepicker/dist/{src/buttonVersion.d.ts → buttonVersion.d.ts} +0 -0
  166. /package/components/datepicker/dist/{src/datepickerKeyboardStrategy.d.ts → datepickerKeyboardStrategy.d.ts} +0 -0
  167. /package/components/datepicker/dist/{src/iconVersion.d.ts → iconVersion.d.ts} +0 -0
  168. /package/components/datepicker/dist/{src/index.d.ts → index.d.ts} +0 -0
  169. /package/components/datepicker/dist/{src/popoverVersion.d.ts → popoverVersion.d.ts} +0 -0
  170. /package/components/datepicker/dist/{src/styles → styles}/classic/color-css.d.ts +0 -0
  171. /package/components/datepicker/dist/{src/styles → styles}/classic/style-css.d.ts +0 -0
  172. /package/components/datepicker/dist/{src/styles → styles}/color-calendar-css.d.ts +0 -0
  173. /package/components/datepicker/dist/{src/styles → styles}/color-cell-css.d.ts +0 -0
  174. /package/components/datepicker/dist/{src/styles → styles}/color-css.d.ts +0 -0
  175. /package/components/datepicker/dist/{src/styles → styles}/color-month-css.d.ts +0 -0
  176. /package/components/datepicker/dist/{src/styles → styles}/shapeSize-css.d.ts +0 -0
  177. /package/components/datepicker/dist/{src/styles → styles}/snowflake/color-css.d.ts +0 -0
  178. /package/components/datepicker/dist/{src/styles → styles}/snowflake/style-css.d.ts +0 -0
  179. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-cell-css.d.ts +0 -0
  180. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-css.d.ts +0 -0
  181. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-month-css.d.ts +0 -0
  182. /package/components/datepicker/dist/{src/styles → styles}/style-css.d.ts +0 -0
  183. /package/components/datepicker/dist/{src/styles → styles}/tokens-css.d.ts +0 -0
  184. /package/components/datepicker/dist/{src/utilities.d.ts → utilities.d.ts} +0 -0
  185. /package/components/datepicker/dist/{src/utilitiesCalendar.d.ts → utilitiesCalendar.d.ts} +0 -0
  186. /package/components/datepicker/dist/{src/utilitiesCalendarRender.d.ts → utilitiesCalendarRender.d.ts} +0 -0
  187. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/day.d.ts +0 -0
  188. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker-calendar.d.ts +0 -0
  189. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker-cell.d.ts +0 -0
  190. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker.d.ts +0 -0
@@ -350,8 +350,21 @@ let AuroFormValidation$1 = class AuroFormValidation {
350
350
  return;
351
351
  }
352
352
 
353
- // Validate that the date passed was the correct format and is a valid date
353
+ // Validate that the date passed was the correct format and is a valid date.
354
+ // For partial date formats, valueObject is never populated; validate them directly.
354
355
  if (elem.value && !elem.valueObject) {
356
+ const isPartialDateFormat = elem.util && !elem.util.isFullDateFormat(elem.type, elem.format);
357
+
358
+ if (isPartialDateFormat) {
359
+ if (!elem.util.isValidPartialDate(elem.value, elem.format)) {
360
+ elem.validity = 'patternMismatch';
361
+ elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
362
+ }
363
+ // Partial date format — validate directly and skip max/min checks since valueObject is undefined.
364
+ return;
365
+ }
366
+
367
+ // Full date format with no valueObject means the value is not a valid calendar date.
355
368
  elem.validity = 'patternMismatch';
356
369
  elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
357
370
  return;
@@ -448,15 +461,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
448
461
  );
449
462
  }
450
463
 
451
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
452
- if (this.auroInputElements?.length === 2) {
453
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
464
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
465
+
466
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
467
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
468
+ // field (datepicker is the intended consumer — start/end are independently required).
469
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
470
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
454
471
  hasValue = false;
455
472
  }
456
473
  }
457
474
 
458
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
459
-
460
475
  if (isCombobox) {
461
476
 
462
477
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -555,7 +570,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
555
570
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
556
571
  const input = elem.renderRoot.querySelector('input');
557
572
 
558
- if (input.validationMessage.length > 0) {
573
+ if (input && input.validationMessage.length > 0) {
559
574
  elem.errorMessage = input.validationMessage;
560
575
  }
561
576
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
@@ -770,6 +785,190 @@ function navigateArrow(component, direction, options = {}) {
770
785
  /* es-module-lexer 1.7.0 */
771
786
  var ImportType$1;!function(A){A[A.Static=1]="Static",A[A.Dynamic=2]="Dynamic",A[A.ImportMeta=3]="ImportMeta",A[A.StaticSourcePhase=4]="StaticSourcePhase",A[A.DynamicSourcePhase=5]="DynamicSourcePhase",A[A.StaticDeferPhase=6]="StaticDeferPhase",A[A.DynamicDeferPhase=7]="DynamicDeferPhase";}(ImportType$1||(ImportType$1={}));1===new Uint8Array(new Uint16Array([1]).buffer)[0];const E$1=()=>{return A="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GzYCHAtBAEEAKAKwCkECajYCsAoCQAJAAkACQEEAECkiAUHhAEYNACABQfcARg0BQQAoArAKIQEMAgtBACgCsAoiAUECakHACEEKEC8NAUEGIQIMAgtBACgCsAoiAS8BAkHpAEcNACABLwEEQfQARw0AQQQhAiABLwEGQegARg0BC0EAIAFBfmo2ArAKDwtBACABIAJBAXRqNgKwCgJAQQEQKUH7AEYNAEEAIAE2ArAKDwtBACgCsAoiACECA0BBACACQQJqNgKwCgJAAkACQEEBECkiAkEiRg0AIAJBJ0cNAUEnEBpBAEEAKAKwCkECajYCsApBARApIQIMAgtBIhAaQQBBACgCsApBAmo2ArAKQQEQKSECDAELIAIQLCECCwJAIAJBOkYNAEEAIAE2ArAKDwtBAEEAKAKwCkECajYCsAoCQEEBECkiAkEiRg0AIAJBJ0YNAEEAIAE2ArAKDwsgAhAaQQBBACgCsApBAmo2ArAKAkACQEEBECkiAkEsRg0AIAJB/QBGDQFBACABNgKwCg8LQQBBACgCsApBAmo2ArAKQQEQKUH9AEYNAEEAKAKwCiECDAELC0EAKALwCSIBIAA2AhAgAUEAKAKwCkECajYCDAttAQJ/AkACQANAAkAgAEH//wNxIgFBd2oiAkEXSw0AQQEgAnRBn4CABHENAgsgAUGgAUYNASAAIQIgARAoDQJBACECQQBBACgCsAoiAEECajYCsAogAC8BAiIADQAMAgsLIAAhAgsgAkH//wNxC6sBAQR/AkACQEEAKAKwCiICLwEAIgNB4QBGDQAgASEEIAAhBQwBC0EAIAJBBGo2ArAKQQEQKSECQQAoArAKIQUCQAJAIAJBIkYNACACQSdGDQAgAhAsGkEAKAKwCiEEDAELIAIQGkEAQQAoArAKQQJqIgQ2ArAKC0EBECkhA0EAKAKwCiECCwJAIAIgBUYNACAFIARBACAAIAAgAUYiAhtBACABIAIbEAILIAMLcgEEf0EAKAKwCiEAQQAoArQKIQECQAJAA0AgAEECaiECIAAgAU8NAQJAAkAgAi8BACIDQaR/ag4CAQQACyACIQAgA0F2ag4EAgEBAgELIABBBGohAAwACwtBACACNgKwChAlQQAPC0EAIAI2ArAKQd0AC0kBA39BACEDAkAgAkUNAAJAA0AgAC0AACIEIAEtAAAiBUcNASABQQFqIQEgAEEBaiEAIAJBf2oiAg0ADAILCyAEIAVrIQMLIAMLC+wBAgBBgAgLzgEAAHgAcABvAHIAdABtAHAAbwByAHQAZgBvAHIAZQB0AGEAbwB1AHIAYwBlAHIAbwBtAHUAbgBjAHQAaQBvAG4AcwBzAGUAcgB0AHYAbwB5AGkAZQBkAGUAbABlAGMAbwBuAHQAaQBuAGkAbgBzAHQAYQBuAHQAeQBiAHIAZQBhAHIAZQB0AHUAcgBkAGUAYgB1AGcAZwBlAGEAdwBhAGkAdABoAHIAdwBoAGkAbABlAGkAZgBjAGEAdABjAGYAaQBuAGEAbABsAGUAbABzAABB0AkLEAEAAAACAAAAAAQAAEA5AAA=","undefined"!=typeof Buffer?Buffer.from(A,"base64"):Uint8Array.from(atob(A),(A=>A.charCodeAt(0)));var A;};WebAssembly.compile(E$1()).then(WebAssembly.instantiate).then((({exports:A})=>{}));
772
787
 
788
+ // Selectors for focusable elements
789
+ const FOCUSABLE_SELECTORS$1 = [
790
+ "a[href]",
791
+ "button:not([disabled])",
792
+ "textarea:not([disabled])",
793
+ "input:not([disabled])",
794
+ "select:not([disabled])",
795
+ '[role="tab"]:not([disabled])',
796
+ '[role="link"]:not([disabled])',
797
+ '[role="button"]:not([disabled])',
798
+ '[tabindex]:not([tabindex="-1"])',
799
+ '[contenteditable]:not([contenteditable="false"])',
800
+ ];
801
+
802
+ // List of custom components that are known to be focusable
803
+ const FOCUSABLE_COMPONENTS$1 = [
804
+ "auro-checkbox",
805
+ "auro-radio",
806
+ "auro-dropdown",
807
+ "auro-button",
808
+ "auro-combobox",
809
+ "auro-input",
810
+ "auro-counter",
811
+ // 'auro-menu', // Auro menu is not focusable by default, it uses a different interaction model
812
+ "auro-select",
813
+ "auro-datepicker",
814
+ "auro-hyperlink",
815
+ "auro-accordion",
816
+ ];
817
+
818
+ /**
819
+ * Determines if a given element is a custom focusable component.
820
+ * Returns true if the element matches a known focusable component and is not disabled.
821
+ *
822
+ * @param {HTMLElement} element The element to check for focusability.
823
+ * @returns {boolean} True if the element is a focusable custom component, false otherwise.
824
+ */
825
+ function isFocusableComponent$1(element) {
826
+ const componentName = element.tagName.toLowerCase();
827
+
828
+ // Guard Clause: Element is a focusable component
829
+ if (
830
+ !FOCUSABLE_COMPONENTS$1.some(
831
+ (name) => element.hasAttribute(name) || componentName === name,
832
+ )
833
+ )
834
+ return false;
835
+
836
+ // Guard Clause: Element is not disabled
837
+ if (element.hasAttribute("disabled")) return false;
838
+
839
+ // Guard Clause: The element is a hyperlink and has no href attribute
840
+ if (componentName.match("hyperlink") && !element.hasAttribute("href"))
841
+ return false;
842
+
843
+ // If all guard clauses pass, the element is a focusable component
844
+ return true;
845
+ }
846
+
847
+ /**
848
+ * Safely get a numeric tabindex for an element.
849
+ * Returns a number if the tabindex is a valid integer, otherwise null.
850
+ *
851
+ * @param {HTMLElement} element The element whose tabindex to read.
852
+ * @returns {?number} The numeric tabindex or null if missing/invalid.
853
+ */
854
+ function getNumericTabIndex$1(element) {
855
+ const raw = element.getAttribute("tabindex");
856
+ if (raw == null) return null;
857
+
858
+ const value = Number.parseInt(raw, 10);
859
+ return Number.isNaN(value) ? null : value;
860
+ }
861
+
862
+ /**
863
+ * Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
864
+ * Returns a unique, ordered array of elements that can receive focus.
865
+ * Also sorts elements with tabindex first, preserving their order.
866
+ *
867
+ * @param {HTMLElement} container The container to search within
868
+ * @returns {Array<HTMLElement>} An array of focusable elements within the container.
869
+ */
870
+ function getFocusableElements$1(container) {
871
+ // Get elements in DOM order by walking the tree
872
+ const orderedFocusableElements = [];
873
+
874
+ // Define a recursive function to collect focusable elements in DOM order
875
+ const collectFocusableElements = (root) => {
876
+ // Check if current element is focusable
877
+ if (root.nodeType === Node.ELEMENT_NODE) {
878
+ // Check if this is a custom component that is focusable
879
+ const isComponentFocusable = isFocusableComponent$1(root);
880
+
881
+ if (isComponentFocusable) {
882
+ // Add the component itself as a focusable element and don't traverse its shadow DOM
883
+ orderedFocusableElements.push(root);
884
+ return; // Skip traversing inside this component
885
+ }
886
+
887
+ // Check if the element itself matches any selector
888
+ for (const selector of FOCUSABLE_SELECTORS$1) {
889
+ if (root.matches?.(selector)) {
890
+ orderedFocusableElements.push(root);
891
+ break; // Once we know it's focusable, no need to check other selectors
892
+ }
893
+ }
894
+
895
+ // Process shadow DOM only for non-Auro components
896
+ if (root.shadowRoot) {
897
+ // Process shadow DOM children in order
898
+ if (root.shadowRoot.children) {
899
+ Array.from(root.shadowRoot.children).forEach((child) => {
900
+ collectFocusableElements(child);
901
+ });
902
+ }
903
+ }
904
+
905
+ // Process slots and their assigned nodes in order
906
+ if (root.tagName === "SLOT") {
907
+ const assignedNodes = root.assignedNodes({ flatten: true });
908
+ for (const node of assignedNodes) {
909
+ collectFocusableElements(node);
910
+ }
911
+ } else {
912
+ // Process light DOM children in order
913
+ if (root.children) {
914
+ Array.from(root.children).forEach((child) => {
915
+ collectFocusableElements(child);
916
+ });
917
+ }
918
+ }
919
+ }
920
+ };
921
+
922
+ // Start the traversal from the container
923
+ collectFocusableElements(container);
924
+
925
+ // Remove duplicates that might have been collected through different paths
926
+ // while preserving order
927
+ const uniqueElements = [];
928
+ const seen = new Set();
929
+
930
+ for (const element of orderedFocusableElements) {
931
+ if (!seen.has(element)) {
932
+ seen.add(element);
933
+ uniqueElements.push(element);
934
+ }
935
+ }
936
+
937
+ // Move tab-indexed elements to the front while preserving their order
938
+ // This ensures that elements with tabindex are prioritized in the focus order
939
+
940
+ // First extract elements with valid positive tabindex
941
+ const elementsWithTabindex = uniqueElements.filter((el) => {
942
+ const tabindex = getNumericTabIndex$1(el);
943
+ return tabindex !== null && tabindex > 0;
944
+ });
945
+
946
+ // Sort these elements by their tabindex value
947
+ elementsWithTabindex.sort((a, b) => {
948
+ const aIndex = getNumericTabIndex$1(a) ?? 0;
949
+ const bIndex = getNumericTabIndex$1(b) ?? 0;
950
+ return aIndex - bIndex;
951
+ });
952
+
953
+ // Elements without tabindex (preserving their original order)
954
+ const elementsWithoutTabindex = uniqueElements.filter((el) => {
955
+ const tabindex = getNumericTabIndex$1(el);
956
+
957
+ // Elements without tabindex or with tabindex of 0 stay in DOM order
958
+ return tabindex === null || tabindex === 0;
959
+ });
960
+
961
+ // Combine both arrays with tabindex elements first
962
+ const tabIndexedUniqueElements = [
963
+ ...elementsWithTabindex,
964
+ ...elementsWithoutTabindex,
965
+ ];
966
+
967
+ return tabIndexedUniqueElements;
968
+ }
969
+
970
+ /* eslint-disable no-underscore-dangle */
971
+
773
972
  /**
774
973
  * Returns the clear button element from the active input's shadow
775
974
  * DOM, if available.
@@ -800,6 +999,95 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
800
999
  return isFocused;
801
1000
  }
802
1001
 
1002
+ /**
1003
+ * Reconcile the menu `_index` from `optionActive` so subsequent `makeSelection` calls find the correct option after async clearSelection has reset it.
1004
+ * @param {Object} menu - The menu component.
1005
+ */
1006
+ function reconcileMenuIndex(menu) {
1007
+ if (menu._index < 0 && menu.optionActive && menu.items) {
1008
+ const idx = menu.items.indexOf(menu.optionActive);
1009
+ if (idx >= 0) {
1010
+ menu._index = idx;
1011
+ }
1012
+ }
1013
+ }
1014
+
1015
+ /**
1016
+ * Commit the highlighted option and close the bib.
1017
+ * @param {Element} component - The auro-combobox host element.
1018
+ */
1019
+ function selectAndClose(component) {
1020
+ reconcileMenuIndex(component.menu);
1021
+ component.menu.makeSelection();
1022
+ component.hideBib();
1023
+ }
1024
+
1025
+ /**
1026
+ * Whether `el` is currently visible enough to receive focus. Prefers the
1027
+ * browser's `checkVisibility()` (Chrome/Safari; Firefox behind a flag until
1028
+ * recently); otherwise combines a display/visibility check with an
1029
+ * ancestor-hidden probe that keeps position:fixed elements visible.
1030
+ * @param {Element} el - The element to test.
1031
+ * @returns {boolean}
1032
+ */
1033
+ function isVisibleForFocus(el) {
1034
+ if (typeof el.checkVisibility === 'function') {
1035
+ return el.checkVisibility();
1036
+ }
1037
+ const style = el.ownerDocument.defaultView.getComputedStyle(el);
1038
+ if (style.display === 'none' || style.visibility === 'hidden') {
1039
+ return false;
1040
+ }
1041
+ return el.offsetParent !== null || style.position === 'fixed';
1042
+ }
1043
+
1044
+ /**
1045
+ * Return the tab stop that comes before `component` in page tab order,
1046
+ * skipping any hidden entries the walker doesn't filter itself.
1047
+ * @param {Element} component - The auro-combobox host element.
1048
+ * @returns {Element|null}
1049
+ */
1050
+ function getPreviousTabStop(component) {
1051
+ const tabStops = getFocusableElements$1(component.ownerDocument.body);
1052
+ const componentIndex = tabStops.indexOf(component);
1053
+ for (let index = componentIndex - 1; index >= 0; index -= 1) {
1054
+ if (isVisibleForFocus(tabStops[index])) {
1055
+ return tabStops[index];
1056
+ }
1057
+ }
1058
+ return null;
1059
+ }
1060
+
1061
+ /**
1062
+ * Commit the highlighted option, close the bib, and move focus to the tab
1063
+ * stop before the combobox — the Shift+Tab exit path (AB#1592239).
1064
+ * Returns true when this function moved focus and the caller should
1065
+ * preventDefault; false when no previous tab stop was found and the browser's
1066
+ * native traversal should run.
1067
+ * @param {Element} component - The auro-combobox host element.
1068
+ * @returns {boolean}
1069
+ */
1070
+ function selectAndExitBackward(component) {
1071
+ const previousTabStop = getPreviousTabStop(component);
1072
+
1073
+ // Opts this selection out of setClearBtnFocus so the focus move below
1074
+ // isn't clobbered. Consumed by auro-combobox's selection listener; also
1075
+ // cleared via microtask in case makeSelection is a no-op and the listener
1076
+ // never fires (microtasks run before the next user event, so this can't
1077
+ // leak into an unrelated selection).
1078
+ component._suppressClearBtnFocusOnSelection = true;
1079
+ selectAndClose(component);
1080
+ queueMicrotask(() => {
1081
+ component._suppressClearBtnFocusOnSelection = false;
1082
+ });
1083
+
1084
+ if (!previousTabStop) {
1085
+ return false;
1086
+ }
1087
+ doubleRaf(() => previousTabStop.focus());
1088
+ return true;
1089
+ }
1090
+
803
1091
  const comboboxKeyboardStrategy = {
804
1092
  ArrowDown(component, evt, ctx) {
805
1093
  // If the clear button has focus, let the browser handle ArrowDown normally.
@@ -813,11 +1101,7 @@ const comboboxKeyboardStrategy = {
813
1101
 
814
1102
  // navigate if bib is open otherwise open it
815
1103
  if (component.dropdown.isPopoverVisible) {
816
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
817
- component.activateLastEnabledAvailableOption();
818
- } else {
819
- navigateArrow(component, 'down');
820
- }
1104
+ navigateArrow(component, 'down');
821
1105
  } else {
822
1106
  component.showBib();
823
1107
  }
@@ -836,11 +1120,7 @@ const comboboxKeyboardStrategy = {
836
1120
 
837
1121
  // navigate if bib is open otherwise open it
838
1122
  if (component.dropdown.isPopoverVisible) {
839
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
840
- component.activateFirstEnabledAvailableOption();
841
- } else {
842
- navigateArrow(component, 'up');
843
- }
1123
+ navigateArrow(component, 'up');
844
1124
  } else {
845
1125
  component.showBib();
846
1126
  }
@@ -856,13 +1136,19 @@ const comboboxKeyboardStrategy = {
856
1136
  },
857
1137
 
858
1138
  Enter(component, evt, ctx) {
1139
+ // Forms should not submit on Enter from a combobox, regardless of which
1140
+ // child element (input, clear button, menu) is focused.
1141
+ evt.stopPropagation();
1142
+
859
1143
  if (isClearBtnFocused(ctx)) {
860
- // If the clear button has focus, let the browser activate it normally.
861
- // stopPropagation prevents parent containers (e.g., forms) from treating
862
- // Enter as a submit, but we must NOT call preventDefault — that would
863
- // block the browser's built-in "Enter activates focused button" behavior.
864
- evt.stopPropagation();
865
- } else if (ctx.isExpanded && component.menu.optionActive) {
1144
+ // Let the browser dispatch Enter to the focused clear button so its
1145
+ // built-in activation fires and clears the selection. Do NOT call
1146
+ // preventDefault — that would block the activation.
1147
+ return;
1148
+ }
1149
+
1150
+ if (ctx.isExpanded && component.menu.optionActive) {
1151
+ reconcileMenuIndex(component.menu);
866
1152
  component.menu.makeSelection();
867
1153
 
868
1154
  if (ctx.isModal) {
@@ -870,14 +1156,8 @@ const comboboxKeyboardStrategy = {
870
1156
  }
871
1157
 
872
1158
  evt.preventDefault();
873
- evt.stopPropagation();
874
1159
  } else {
875
- // Prevent the keypress from bubbling to parent containers (e.g., forms)
876
- // which could interpret Enter as a submit or trigger other unintended behavior.
877
- // This is safe because showBib() opens the dialog programmatically,
878
- // not via event propagation.
879
1160
  evt.preventDefault();
880
- evt.stopPropagation();
881
1161
  component.showBib();
882
1162
  }
883
1163
  },
@@ -907,21 +1187,21 @@ const comboboxKeyboardStrategy = {
907
1187
 
908
1188
  Tab(component, evt, ctx) {
909
1189
  if (ctx.isExpanded && !isClearBtnFocused(ctx)) {
910
- // When the clear button is focused, Tab events do not bubble out of
911
- // its shadow DOM, so this handler only fires when the clear button
912
- // is NOT focused. In that case, select the active option and close.
913
- component.menu.makeSelection();
914
- component.hideBib();
1190
+ if (evt.shiftKey) {
1191
+ if (selectAndExitBackward(component)) {
1192
+ evt.preventDefault();
1193
+ }
1194
+ return;
1195
+ }
915
1196
 
916
- // In fullscreen modal mode, closing the dialog does not
917
- // automatically restores focus to the input. In the tab case,
918
- // Explicitly move focus to the trigger's clear button so the
919
- // user can continues tabbing through the page normally.
920
- if (ctx.isModal && !evt.shiftKey) {
1197
+ selectAndClose(component);
1198
+
1199
+ if (ctx.isModal) {
1200
+ // Fullscreen close does not automatically restore focus to the input.
921
1201
  component.setClearBtnFocus();
922
1202
  }
923
1203
  }
924
- },
1204
+ }
925
1205
  };
926
1206
 
927
1207
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
@@ -3176,12 +3456,40 @@ class AuroFloatingUI {
3176
3456
  return;
3177
3457
  }
3178
3458
 
3459
+ // Chrome-specific: during popover top-layer promotion after a click on a
3460
+ // slotted focusable, :focus-within can briefly evaluate false while the
3461
+ // active element is still structurally inside the trigger/bib. Fall back
3462
+ // to a shadow-piercing ancestry walk from the deep active element before
3463
+ // treating this as a real focus loss.
3464
+ try {
3465
+ let active = document.activeElement;
3466
+ while (active && active.shadowRoot && active.shadowRoot.activeElement) {
3467
+ active = active.shadowRoot.activeElement;
3468
+ }
3469
+ const targets = [element, element.trigger, element.bib].filter(Boolean);
3470
+ let node = active;
3471
+ while (node) {
3472
+ if (targets.includes(node)) {
3473
+ return;
3474
+ }
3475
+ node =
3476
+ node.parentElement ||
3477
+ (node.getRootNode && node.getRootNode().host) ||
3478
+ null;
3479
+ }
3480
+ } catch (e) {
3481
+ // Defensive: fall through to the existing close path if traversal fails.
3482
+ }
3483
+
3179
3484
  // if fullscreen bib is in fullscreen mode, do not close
3180
3485
  if (element.bib.hasAttribute("isfullscreen")) {
3181
3486
  return;
3182
3487
  }
3183
3488
 
3184
- this.hideBib("keydown");
3489
+ // eventType "focusloss" distinguishes a Tab/click-driven close from an
3490
+ // Escape keydown close. Consumers use this to decide whether to restore
3491
+ // focus to the trigger (Escape) or let it advance naturally (Tab).
3492
+ this.hideBib("focusloss");
3185
3493
  }
3186
3494
 
3187
3495
  setupHideHandlers() {
@@ -3838,210 +4146,17 @@ function getFocusableElements(container) {
3838
4146
  const elementsWithoutTabindex = uniqueElements.filter((el) => {
3839
4147
  const tabindex = getNumericTabIndex(el);
3840
4148
 
3841
- // Elements without tabindex or with tabindex of 0 stay in DOM order
3842
- return tabindex === null || tabindex === 0;
3843
- });
3844
-
3845
- // Combine both arrays with tabindex elements first
3846
- const tabIndexedUniqueElements = [
3847
- ...elementsWithTabindex,
3848
- ...elementsWithoutTabindex,
3849
- ];
3850
-
3851
- return tabIndexedUniqueElements;
3852
- }
3853
-
3854
- /**
3855
- * FocusTrap manages keyboard focus within a specified container element, ensuring that focus does not leave the container when tabbing.
3856
- * It is commonly used for modal dialogs or overlays to improve accessibility by trapping focus within interactive UI components.
3857
- */
3858
- class FocusTrap {
3859
- /**
3860
- * Creates a new FocusTrap instance for the given container element.
3861
- * Initializes event listeners and prepares the container for focus management.
3862
- *
3863
- * @param {HTMLElement} container The DOM element to trap focus within.
3864
- * @param {boolean} [controlTabOrder=false] If true enables manual control of the tab order by the FocusTrap.
3865
- * @throws {Error} If the provided container is not a valid HTMLElement.
3866
- */
3867
- constructor(container, controlTabOrder = false) {
3868
- if (!container || !(container instanceof HTMLElement)) {
3869
- throw new Error("FocusTrap requires a valid HTMLElement.");
3870
- }
3871
-
3872
- this.container = container;
3873
- this.tabDirection = "forward"; // or 'backward';
3874
- this.controlTabOrder = controlTabOrder;
3875
-
3876
- this._init();
3877
- }
3878
-
3879
- /**
3880
- * Initializes the focus trap by setting up event listeners and attributes on the container.
3881
- * Prepares the container for focus management, including support for shadow DOM and inert attributes.
3882
- *
3883
- * @private
3884
- */
3885
- _init() {
3886
- // Add inert attribute to prevent focusing programmatically as well (if supported)
3887
- if ("inert" in HTMLElement.prototype) {
3888
- this.container.inert = false; // Ensure the container isn't inert
3889
- this.container.setAttribute("data-focus-trap-container", true); // Mark for identification
3890
- }
3891
-
3892
- // Track tab direction
3893
- this.container.addEventListener("keydown", this._onKeydown);
3894
- }
3895
-
3896
- /**
3897
- * Gets an array of currently active (focused) elements in the document and shadow DOM.
3898
- * @returns {Array<HTMLElement>} An array of focusable elements within the container.
3899
- * @private
3900
- */
3901
- _getActiveElements() {
3902
- // Get the active element(s) in the document and shadow root
3903
- // This will include the active element in the shadow DOM if it exists
3904
- // Active element may be inside the shadow DOM depending on delegatesFocus, so we need to check both
3905
- let { activeElement } = document;
3906
- const actives = [activeElement];
3907
- while (activeElement?.shadowRoot?.activeElement) {
3908
- actives.push(activeElement.shadowRoot.activeElement);
3909
- activeElement = activeElement.shadowRoot.activeElement;
3910
- }
3911
- return actives;
3912
- }
3913
-
3914
- /**
3915
- * Gets the next focus index based on the current index and focusable elements.
3916
- * @param {number} currentIndex The current index of the focused element.
3917
- * @param {Array<HTMLElement>} focusables The array of focusable elements.
3918
- * @returns {number|null} The next focus index or null if not determined.
3919
- */
3920
- _getNextFocusIndex(currentIndex, focusables, actives) {
3921
- if (this.controlTabOrder) {
3922
- // Calculate the new index based on the current index and tab direction
3923
- let newFocusIndex =
3924
- currentIndex + (this.tabDirection === "forward" ? 1 : -1);
3925
-
3926
- // Wrap-around logic
3927
- if (newFocusIndex < 0) newFocusIndex = focusables.length - 1;
3928
- if (newFocusIndex >= focusables.length) newFocusIndex = 0;
3929
-
3930
- // Early return with the new index
3931
- return newFocusIndex;
3932
- }
3933
-
3934
- // Determine if we need to wrap
3935
- const atFirst =
3936
- actives.includes(focusables[0]) || actives.includes(this.container);
3937
- const atLast = actives.includes(focusables[focusables.length - 1]);
3938
-
3939
- // Only wrap if at the ends
3940
- if (this.tabDirection === "backward" && atFirst) {
3941
- return focusables.length - 1;
3942
- }
3943
-
3944
- if (this.tabDirection === "forward" && atLast) {
3945
- return 0;
3946
- }
3947
-
3948
- // No wrap, so don't change focus, return early
3949
- return null;
3950
- }
3951
-
3952
- /**
3953
- * Handles the Tab key press event to manage focus within the container.
3954
- * @param {KeyboardEvent} e The keyboard event triggered by the user.
3955
- * @returns {void}
3956
- */
3957
- _handleTabKey(e) {
3958
- // Update the focusable elements
3959
- const focusables = this._getFocusableElements();
3960
-
3961
- // If there are no focusable elements, exit
3962
- if (!focusables.length) return;
3963
-
3964
- // Set the tab direction based on the key pressed
3965
- this.tabDirection = e.shiftKey ? "backward" : "forward";
3966
-
3967
- // Get the active elements that are currently focused
3968
- const actives = this._getActiveElements();
3969
-
3970
- // If we're at either end of the focusable elements, wrap around to the other end
3971
- let focusIndex = focusables.findIndex((el) => actives.includes(el));
3972
-
3973
- // Fallback if we have no focused element
3974
- if (focusIndex === -1) focusIndex = 0;
3975
-
3976
- // Get the next focus index based on the current focus index, tab direction, and controlTabOrder setting
3977
- // Is null if no new focus index is determined
3978
- const newFocusIndex = this._getNextFocusIndex(
3979
- focusIndex,
3980
- focusables,
3981
- actives,
3982
- );
3983
-
3984
- // If we have a new focus index, set focus to that element
3985
- if (newFocusIndex !== null) {
3986
- e.preventDefault();
3987
- focusables[newFocusIndex].focus();
3988
- }
3989
- }
3990
-
3991
- /**
3992
- * Catches the keydown event
3993
- * @param {KeyboardEvent} e The keyboard event triggered by user interaction.
3994
- * @private
3995
- */
3996
- _onKeydown = (e) => {
3997
- // Handle tab
3998
- if (e.key === "Tab") this._handleTabKey(e);
3999
- };
4000
-
4001
- /**
4002
- * Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
4003
- * Returns a unique, ordered array of elements that can receive focus.
4004
- *
4005
- * @returns {Array<HTMLElement>} An array of focusable elements within the container.
4006
- * @private
4007
- */
4008
- _getFocusableElements() {
4009
- // Use the imported utility function to get focusable elements
4010
- const elements = getFocusableElements(this.container);
4011
-
4012
- // Return the elements found
4013
- return elements;
4014
- }
4015
-
4016
- /**
4017
- * Moves focus to the first focusable element within the container.
4018
- * Useful for setting initial focus when activating the focus trap.
4019
- */
4020
- focusFirstElement() {
4021
- const focusables = this._getFocusableElements();
4022
- if (focusables.length) focusables[0].focus();
4023
- }
4024
-
4025
- /**
4026
- * Moves focus to the last focusable element within the container.
4027
- * Useful for setting focus when deactivating or cycling focus in reverse.
4028
- */
4029
- focusLastElement() {
4030
- const focusables = this._getFocusableElements();
4031
- if (focusables.length) focusables[focusables.length - 1].focus();
4032
- }
4149
+ // Elements without tabindex or with tabindex of 0 stay in DOM order
4150
+ return tabindex === null || tabindex === 0;
4151
+ });
4033
4152
 
4034
- /**
4035
- * Removes event listeners and attributes added by the focus trap.
4036
- * Call this method to clean up when the focus trap is no longer needed.
4037
- */
4038
- disconnect() {
4039
- if (this.container.hasAttribute("data-focus-trap-container")) {
4040
- this.container.removeAttribute("data-focus-trap-container");
4041
- }
4153
+ // Combine both arrays with tabindex elements first
4154
+ const tabIndexedUniqueElements = [
4155
+ ...elementsWithTabindex,
4156
+ ...elementsWithoutTabindex,
4157
+ ];
4042
4158
 
4043
- this.container.removeEventListener("keydown", this._onKeydown);
4044
- }
4159
+ return tabIndexedUniqueElements;
4045
4160
  }
4046
4161
 
4047
4162
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
@@ -4544,7 +4659,7 @@ class AuroDropdownBib extends LitElement {
4544
4659
  classes[`shape-${this.shape}`] = true;
4545
4660
 
4546
4661
  return html`
4547
- <dialog class="${classMap(classes)}" part="bibContainer" role="${ifDefined(this.dialogRole)}" aria-labelledby="${ifDefined(this.dialogLabel ? 'dialogLabel' : undefined)}">
4662
+ <dialog tabindex="-1" class="${classMap(classes)}" part="bibContainer" role="${ifDefined(this.dialogRole)}" aria-labelledby="${ifDefined(this.dialogLabel ? 'dialogLabel' : undefined)}">
4548
4663
  ${this.dialogLabel ? html`<span id="dialogLabel" class="util_displayHiddenVisually">${this.dialogLabel}</span>` : ''}
4549
4664
  <slot></slot>
4550
4665
  <span id="srAnnouncement" class="util_displayHiddenVisually" aria-live="polite" role="status"></span>
@@ -4795,7 +4910,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
4795
4910
  }
4796
4911
  };
4797
4912
 
4798
- var formkitVersion$2 = '202606191810';
4913
+ var formkitVersion$2 = '202607221815';
4799
4914
 
4800
4915
  let AuroElement$2 = class AuroElement extends LitElement {
4801
4916
  static get properties() {
@@ -4924,7 +5039,6 @@ class AuroDropdown extends AuroElement$2 {
4924
5039
  static get shadowRootOptions() {
4925
5040
  return {
4926
5041
  ...AuroElement$2.shadowRootOptions,
4927
- delegatesFocus: true,
4928
5042
  };
4929
5043
  }
4930
5044
 
@@ -4936,6 +5050,15 @@ class AuroDropdown extends AuroElement$2 {
4936
5050
  this.matchWidth = false;
4937
5051
  this.noHideOnThisFocusLoss = false;
4938
5052
 
5053
+ /**
5054
+ * When true, the dropdown skips its generic focus restoration on close.
5055
+ * Set by consumers (e.g. combobox) that manage their own focus routing
5056
+ * via setClearBtnFocus / setInputFocus / keyboard strategy.
5057
+ * Separate from noHideOnThisFocusLoss (which controls auto-close behavior).
5058
+ * @private
5059
+ */
5060
+ this.noFocusRestoreOnClose = false;
5061
+
4939
5062
  this.errorMessage = undefined; // TODO - check with Doug if there is still more to do here
4940
5063
 
4941
5064
  // Layout Config
@@ -5091,7 +5214,49 @@ class AuroDropdown extends AuroElement$2 {
5091
5214
  focusables[0].focus();
5092
5215
  }
5093
5216
  } else {
5217
+ this.focusTrigger();
5218
+ }
5219
+ }
5220
+
5221
+ /**
5222
+ * Focus the trigger content. When the trigger wrapper itself is focusable
5223
+ * (has tabindex), focus it directly. Otherwise, focus the first focusable
5224
+ * element slotted into the trigger slot (e.g. the auro-input).
5225
+ * @private
5226
+ */
5227
+ focusTrigger() {
5228
+ if (this.trigger.hasAttribute('tabindex')) {
5094
5229
  this.trigger.focus();
5230
+ } else {
5231
+ // Slotted content isn't a DOM child of the trigger div, so
5232
+ // getFocusableElements can't find it. Query assigned nodes directly.
5233
+ const slot = this.trigger.querySelector('slot[name="trigger"]');
5234
+ if (slot) {
5235
+ const assigned = slot.assignedElements();
5236
+ for (const el of assigned) {
5237
+ if (el.hasAttribute('disabled')) {
5238
+ continue; // eslint-disable-line no-continue
5239
+ }
5240
+ // Try finding a focusable descendant first (handles non-focusable
5241
+ // wrappers like <div> containing a <button>). If none found, try
5242
+ // focusing the element directly (works for custom elements like
5243
+ // auro-input that have delegatesFocus or a custom focus() method).
5244
+ const descendants = getFocusableElements(el);
5245
+ if (descendants.length > 0) {
5246
+ descendants[0].focus();
5247
+ return;
5248
+ }
5249
+ el.focus();
5250
+ if (document.activeElement === el) {
5251
+ return;
5252
+ }
5253
+ }
5254
+ }
5255
+ // Fallback: try DOM children (non-slotted content)
5256
+ const focusables = getFocusableElements(this.trigger);
5257
+ if (focusables.length > 0) {
5258
+ focusables[0].focus();
5259
+ }
5095
5260
  }
5096
5261
  }
5097
5262
 
@@ -5467,10 +5632,17 @@ class AuroDropdown extends AuroElement$2 {
5467
5632
  this.clearTriggerFocusEventBinding();
5468
5633
  }
5469
5634
 
5635
+ // eslint-disable-next-line complexity
5470
5636
  updated(changedProperties) {
5471
5637
  super.updated(changedProperties);
5472
5638
  this.floater.handleUpdate(changedProperties);
5473
5639
 
5640
+ // Close the dropdown if the disabled property is set to true
5641
+ if (changedProperties.has('disabled') && this.disabled && this.isPopoverVisible) {
5642
+ this.hide();
5643
+ this.isPopoverVisible = false;
5644
+ }
5645
+
5474
5646
  // Note: `disabled` is not a breakpoint (it is not a screen size),
5475
5647
  // so it looks like we never consume this - however, dropdownBib handles this in the setter as "undefined"
5476
5648
  if (changedProperties.has('fullscreenBreakpoint')) {
@@ -5544,8 +5716,30 @@ class AuroDropdown extends AuroElement$2 {
5544
5716
  }
5545
5717
 
5546
5718
  const eventType = event.detail.eventType || "unknown";
5547
- if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
5548
- this.trigger.focus();
5719
+ if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown" && !this.noFocusRestoreOnClose) {
5720
+ this.focusTrigger();
5721
+ }
5722
+
5723
+
5724
+ // On Tab-driven close (eventType "focusloss"), let focus advance naturally
5725
+ // — restoring to the trigger would trap the user on this dropdown, forcing
5726
+ // an extra Tab to move on. Escape and outside-click still restore.
5727
+ // When noFocusRestoreOnClose is true, the consumer (e.g. combobox) manages
5728
+ // its own focus restoration — skip the generic trigger focus to avoid races.
5729
+ if (!this.isPopoverVisible && eventType !== "focusloss" && !this.noFocusRestoreOnClose) {
5730
+ // wait til the bib gets fully closed and rendered
5731
+ setTimeout(() => {
5732
+ // Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
5733
+ // Restore focus to trigger when focus is still inside the bib (:focus-within) or fell to body.
5734
+ if (this.isPopoverVisible ||
5735
+ // eslint-disable-next-line no-extra-parens
5736
+ (!this.bibContent?.matches(':focus-within') &&
5737
+ document.activeElement !== document.body)) {
5738
+ return;
5739
+ }
5740
+ // Restore focus to the trigger.
5741
+ this.focusTrigger();
5742
+ });
5549
5743
  }
5550
5744
  }
5551
5745
 
@@ -5746,6 +5940,14 @@ class AuroDropdown extends AuroElement$2 {
5746
5940
  };
5747
5941
  this.addEventListener('keydown', this._bibTabHandler);
5748
5942
 
5943
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
5944
+ // desktopModal trap owns focus management. Without this, the very
5945
+ // first focus move into the bib (from the RAF below) triggers
5946
+ // handleFocusLoss → hideBib, tearing down the trap before the
5947
+ // user can press Tab.
5948
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
5949
+ this.noHideOnThisFocusLoss = true;
5950
+
5749
5951
  // Move initial focus into the bib content, matching FocusTrap behavior
5750
5952
  requestAnimationFrame(() => {
5751
5953
  const focusables = getFocusableElements(this.bibContent);
@@ -5754,15 +5956,16 @@ class AuroDropdown extends AuroElement$2 {
5754
5956
  }
5755
5957
  });
5756
5958
  } else {
5757
- // Normal desktop: use FocusTrap on the bib element.
5758
- // Defer focusFirstElement to the next frame because the popover
5759
- // is positioned asynchronously by Floating UI — calling focus()
5760
- // synchronously here would target elements with zero dimensions
5761
- // and the focus call would be silently ignored.
5762
- this.focusTrap = new FocusTrap(this.bibContent);
5959
+ // Normal desktop (non-modal): move initial focus into the bib but
5960
+ // don't trap Tab. Tab should exit the bib and let the floater's
5961
+ // handleFocusLoss close it, matching native <select>/<details>
5962
+ // behavior. Deferred one frame because Floating UI positions the
5963
+ // popover asynchronously a synchronous focus() would target
5964
+ // zero-dimension elements and be silently ignored.
5763
5965
  requestAnimationFrame(() => {
5764
- if (this.focusTrap) {
5765
- this.focusTrap.focusFirstElement();
5966
+ const focusables = getFocusableElements(this.bibContent);
5967
+ if (focusables.length) {
5968
+ focusables[0].focus();
5766
5969
  }
5767
5970
  });
5768
5971
  }
@@ -6059,6 +6262,7 @@ class AuroDropdown extends AuroElement$2 {
6059
6262
  role="${ifDefined(this.triggerContentFocusable ? undefined : this.a11yRole)}"
6060
6263
  aria-expanded="${ifDefined(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
6061
6264
  aria-controls="${ifDefined(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.dropdownId)}"
6265
+ aria-haspopup="${ifDefined(this.a11yRole === 'combobox' && !this.triggerContentFocusable ? 'listbox' : undefined)}"
6062
6266
  aria-labelledby="${ifDefined(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
6063
6267
  aria-disabled="${ifDefined(this.disabled ? 'true' : undefined)}"
6064
6268
  @focusin="${this.handleFocusin}"
@@ -6185,7 +6389,7 @@ class AuroDropdown extends AuroElement$2 {
6185
6389
 
6186
6390
  var shapeSizeCss = css`.shape-classic-xl,.shape-classic-lg,.shape-classic-md,.shape-classic-sm,.shape-classic-xs{min-height:56px;max-height:56px;border-style:solid;border-width:1px;border-radius:var(--ds-border-radius, 0.375rem)}.shape-classic-xl.simple,.shape-classic-lg.simple,.shape-classic-md.simple,.shape-classic-sm.simple,.shape-classic-xs.simple{border-width:0px;min-height:58px;max-height:58px;background-color:unset;box-shadow:none}.shape-classic-xl.thin,.shape-classic-lg.thin,.shape-classic-md.thin,.shape-classic-sm.thin,.shape-classic-xs.thin{border-width:1px;min-height:56px;max-height:56px;background-color:unset}.shape-classic-xl.parentBorder,.shape-classic-lg.parentBorder,.shape-classic-md.parentBorder,.shape-classic-sm.parentBorder,.shape-classic-xs.parentBorder{border:0;box-shadow:unset;min-height:54px;max-height:54px}.shape-snowflake-xl,.shape-snowflake-lg,.shape-snowflake-md,.shape-snowflake-sm,.shape-snowflake-xs{min-height:56px;max-height:56px;border-style:solid;border-width:2px;border-color:transparent;border-radius:30px}.shape-snowflake-xl.simple,.shape-snowflake-lg.simple,.shape-snowflake-md.simple,.shape-snowflake-sm.simple,.shape-snowflake-xs.simple{border-width:0px;min-height:60px;max-height:60px;background-color:unset;box-shadow:none}.shape-snowflake-xl.thin,.shape-snowflake-lg.thin,.shape-snowflake-md.thin,.shape-snowflake-sm.thin,.shape-snowflake-xs.thin{border-width:1px;min-height:58px;max-height:58px;background-color:unset}.shape-snowflake-xl.parentBorder,.shape-snowflake-lg.parentBorder,.shape-snowflake-md.parentBorder,.shape-snowflake-sm.parentBorder,.shape-snowflake-xs.parentBorder{border:0;box-shadow:unset;min-height:56px;max-height:56px}.shape-box-xl{min-height:68px;max-height:68px;border-style:solid;border-width:2px;border-color:transparent}.shape-box-xl.simple{border-width:0px;min-height:72px;max-height:72px;background-color:unset;box-shadow:none}.shape-box-xl.thin{border-width:1px;min-height:70px;max-height:70px;background-color:unset}.shape-box-xl.parentBorder{border:0;box-shadow:unset;min-height:68px;max-height:68px}.shape-box-lg{min-height:52px;max-height:52px;border-style:solid;border-width:2px;border-color:transparent}.shape-box-lg.simple{border-width:0px;min-height:56px;max-height:56px;background-color:unset;box-shadow:none}.shape-box-lg.thin{border-width:1px;min-height:54px;max-height:54px;background-color:unset}.shape-box-lg.parentBorder{border:0;box-shadow:unset;min-height:52px;max-height:52px}.shape-box-md{min-height:44px;max-height:44px;border-style:solid;border-width:2px;border-color:transparent}.shape-box-md.simple{border-width:0px;min-height:48px;max-height:48px;background-color:unset;box-shadow:none}.shape-box-md.thin{border-width:1px;min-height:46px;max-height:46px;background-color:unset}.shape-box-md.parentBorder{border:0;box-shadow:unset;min-height:44px;max-height:44px}.shape-box-sm{min-height:32px;max-height:32px;border-style:solid;border-width:2px;border-color:transparent}.shape-box-sm.simple{border-width:0px;min-height:36px;max-height:36px;background-color:unset;box-shadow:none}.shape-box-sm.thin{border-width:1px;min-height:34px;max-height:34px;background-color:unset}.shape-box-sm.parentBorder{border:0;box-shadow:unset;min-height:32px;max-height:32px}.shape-box-xs{min-height:20px;max-height:20px;border-style:solid;border-width:2px;border-color:transparent}.shape-box-xs.simple{border-width:0px;min-height:24px;max-height:24px;background-color:unset;box-shadow:none}.shape-box-xs.thin{border-width:1px;min-height:22px;max-height:22px;background-color:unset}.shape-box-xs.parentBorder{border:0;box-shadow:unset;min-height:20px;max-height:20px}.shape-rounded-lg{min-height:56px;max-height:56px;border-style:solid;border-width:2px;border-color:transparent;border-radius:6px}.shape-rounded-lg.simple{border-width:0px;min-height:56px;max-height:56px;background-color:unset;box-shadow:none}.shape-rounded-lg.thin{border-width:1px;min-height:54px;max-height:54px;background-color:unset}.shape-rounded-lg.parentBorder{border:0;box-shadow:unset;min-height:52px;max-height:52px}.shape-pill-xl{min-height:68px;max-height:68px;border-style:solid;border-width:2px;border-color:transparent;border-radius:36px}.shape-pill-xl.simple{border-width:0px;min-height:72px;max-height:72px;background-color:unset;box-shadow:none}.shape-pill-xl.thin{border-width:1px;min-height:70px;max-height:70px;background-color:unset}.shape-pill-xl.parentBorder{border:0;box-shadow:unset;min-height:68px;max-height:68px}.shape-pill-left-xl{min-height:68px;max-height:68px;border-style:solid;border-width:2px;border-color:transparent;border-radius:36px 0 0 36px}.shape-pill-left-xl.simple{border-width:0px;min-height:72px;max-height:72px;background-color:unset;box-shadow:none}.shape-pill-left-xl.thin{border-width:1px;min-height:70px;max-height:70px;background-color:unset}.shape-pill-left-xl.parentBorder{border:0;box-shadow:unset;min-height:68px;max-height:68px}.shape-pill-right-xl{min-height:68px;max-height:68px;border-style:solid;border-width:2px;border-color:transparent;border-radius:0 36px 36px 0}.shape-pill-right-xl.simple{border-width:0px;min-height:72px;max-height:72px;background-color:unset;box-shadow:none}.shape-pill-right-xl.thin{border-width:1px;min-height:70px;max-height:70px;background-color:unset}.shape-pill-right-xl.parentBorder{border:0;box-shadow:unset;min-height:68px;max-height:68px}.shape-pill-md{min-height:44px;max-height:44px;border-style:solid;border-width:2px;border-color:transparent;border-radius:36px}.shape-pill-md.simple{border-width:0px;min-height:48px;max-height:48px;background-color:unset;box-shadow:none}.shape-pill-md.thin{border-width:1px;min-height:46px;max-height:46px;background-color:unset}.shape-pill-md.parentBorder{border:0;box-shadow:unset;min-height:44px;max-height:44px}.shape-pill-left-md{min-height:44px;max-height:44px;border-style:solid;border-width:2px;border-color:transparent;border-radius:36px 0 0 36px}.shape-pill-left-md.simple{border-width:0px;min-height:48px;max-height:48px;background-color:unset;box-shadow:none}.shape-pill-left-md.thin{border-width:1px;min-height:46px;max-height:46px;background-color:unset}.shape-pill-left-md.parentBorder{border:0;box-shadow:unset;min-height:44px;max-height:44px}.shape-pill-right-md{min-height:44px;max-height:44px;border-style:solid;border-width:2px;border-color:transparent;border-radius:0 36px 36px 0}.shape-pill-right-md.simple{border-width:0px;min-height:48px;max-height:48px;background-color:unset;box-shadow:none}.shape-pill-right-md.thin{border-width:1px;min-height:46px;max-height:46px;background-color:unset}.shape-pill-right-md.parentBorder{border:0;box-shadow:unset;min-height:44px;max-height:44px}`;
6187
6391
 
6188
- var styleCss$1$1 = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}.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}:host{display:block}.wrapper:has(:enabled){cursor:text}.wrapper:has(:enabled) label{cursor:text}.helpTextClasses{cursor:default}input{overflow:clip !important;width:100%;padding:0;border:0;background:unset;outline:none;overflow-clip-margin:0 !important;text-overflow:ellipsis}input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none}.displayValue{display:none}.displayValue.hasContent:is(.withValue):not(.hasFocus){display:flex;align-items:center}.accents:not(.util_displayHidden){display:flex;flex-direction:row;align-items:center;justify-content:center;gap:8px}.wrapper:not(:focus-within):not(:hover) .notificationBtn.passwordBtn,.wrapper:not(:focus-within):not(:hover) .notification.clear{display:none}.notification{display:flex;align-items:center;justify-content:center}`;
6392
+ var styleCss$1$1 = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}.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}:host{display:block}.wrapper:has(:enabled){cursor:text}.wrapper:has(:enabled) label{cursor:text}.helpTextClasses{cursor:default}input{overflow:clip !important;width:100%;padding:0;border:0;background:unset;outline:none;overflow-clip-margin:0 !important;text-overflow:ellipsis}input[type=number]{appearance:textfield}input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none}.displayValue{display:none}.displayValue.hasContent:is(.withValue):not(.hasFocus){display:flex;align-items:center}.accents:not(.util_displayHidden){display:flex;flex-direction:row;align-items:center;justify-content:center;gap:8px}.wrapper:not(:focus-within):not(:hover) .notificationBtn.passwordBtn,.wrapper:not(:focus-within):not(:hover) .notification.clear{display:none}.notification{display:flex;align-items:center;justify-content:center}`;
6189
6393
 
6190
6394
  var styleDefaultCss = css`.layoutDefault .typeIcon,:host(:not([layout])) .typeIcon{display:flex;flex-direction:row;align-items:center}.layoutDefault .typeIcon [auro-icon],:host(:not([layout])) .typeIcon [auro-icon]{--ds-auro-icon-size: var(--ds-size-300, 1.5rem);height:var(--ds-size-300, 1.5rem);margin-right:var(--ds-size-100, 0.5rem)}.layoutDefault .notificationIcons,:host(:not([layout])) .notificationIcons{display:flex;flex-direction:row;padding-right:var(--ds-size-100, 0.5rem)}:host([class=layoutDefault][bordered]) .typeIcon,:host(:not([layout])[bordered]) .typeIcon{padding-left:var(--ds-size-100, 0.5rem)}:host([class=layoutDefault][bordered]) .notificationIcons,:host(:not([layout])[bordered]) .notificationIcons{align-items:center}:host([class=layoutDefault][bordered]) .notification:not(:first-of-type),:host(:not([layout])[bordered]) .notification:not(:first-of-type){margin-left:var(--ds-size-100, 0.5rem)}:host([class=layoutDefault][bordered]) .alertNotification,:host(:not([layout])[bordered]) .alertNotification{width:calc(var(--ds-size-300, 1.5rem) + var(--ds-size-25, 0.125rem));height:calc(var(--ds-size-300, 1.5rem) + var(--ds-size-25, 0.125rem))}:host([class=layoutDefault][bordered]) .passwordBtn,:host(:not([layout])[bordered]) .passwordBtn{width:calc(var(--ds-size-300, 1.5rem));height:calc(var(--ds-size-300, 1.5rem))}:host([class=layoutDefault][bordered]) .notificationBtn,:host(:not([layout])[bordered]) .notificationBtn{display:block;width:var(--ds-size-300, 1.5rem);height:var(--ds-size-300, 1.5rem);padding:0;border:0;background:unset;cursor:pointer}:host([class=layoutDefault][bordered]) .notificationBtn [auro-icon],:host(:not([layout])[bordered]) .notificationBtn [auro-icon]{display:block;height:var(--ds-size-300, 1.5rem);--ds-auro-icon-size: var(--ds-size-300, 1.5rem)}:host([class=layoutDefault][bordered]) .notificationBtn [auro-icon][hidden],:host(:not([layout])[bordered]) .notificationBtn [auro-icon][hidden]{display:none}:host([class=layoutDefault]:not([bordered])) .typeIcon,:host([class=layoutDefault]:not([bordered])) .notificationIcons,:host(:not([layout]):not([bordered])) .typeIcon,:host(:not([layout]):not([bordered])) .notificationIcons{align-items:flex-end;padding-bottom:var(--ds-size-50, 0.25rem)}:host([class=layoutDefault]:not([bordered])) .clearBtn,:host(:not([layout]):not([bordered])) .clearBtn{transition:all .3s cubic-bezier(0.215, 0.61, 0.355, 1);overflow:hidden;width:0;opacity:0}.layoutDefault .wrapper:hover .clearBtn,.layoutDefault .wrapper:focus-within .clearBtn,:host(:not([layout])) .wrapper:hover .clearBtn,:host(:not([layout])) .wrapper:focus-within .clearBtn{width:calc(var(--ds-size-200, 1rem) + var(--ds-size-25, 0.125rem));height:calc(var(--ds-size-200, 1rem) + var(--ds-size-25, 0.125rem));opacity:1}:host([class=layoutDefault]:focus-within[type=password]) .notificationIcons[hasValue] .alertNotification,:host(:not([layout]):focus-within[type=password]) .notificationIcons[hasValue] .alertNotification{overflow:hidden;width:0;height:0;padding:0;margin:0;visibility:hidden}.layoutDefault input,:host(:not([layout])) input{border:unset}.layoutDefault .wrapper,:host(:not([layout])) .wrapper{position:relative;overflow:hidden;border-style:solid}:host([class=layoutDefault]:not([bordered],[borderless])) .wrapper,:host(:not([layout]):not([bordered],[borderless])) .wrapper{border-width:1px 0}:host([class=layoutDefault][bordered]) .wrapper,:host(:not([layout])[bordered]) .wrapper{border-width:1px;border-radius:var(--ds-border-radius, 0.375rem)}:host([class=layoutDefault]:not([borderless])) .wrapper:focus-within:before,:host(:not([layout]):not([borderless])) .wrapper:focus-within:before{position:absolute;display:block;border-bottom-width:1px;border-bottom-style:solid;content:"";inset:0;pointer-events:none}:host([class=layoutDefault][validity]:not([validity=valid])) .wrapper:before,:host(:not([layout])[validity]:not([validity=valid])) .wrapper:before{border-bottom:0}:focus:not(:focus-visible){outline:3px solid transparent}.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock,:host{display:block}.util_displayFlex{display:flex}.util_displayHidden,:host([hidden]:not(:focus):not(:active)){display:none}.util_displayHiddenVisually,:host([hiddenVisually]:not(:focus):not(:active)){position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{text-align:left}.layoutDefault :host,:host(:not([layout])) :host{position:relative;display:block}.layoutDefault .wrapper,:host(:not([layout])) .wrapper{display:flex;flex-direction:row}.layoutDefault .main,:host(:not([layout])) .main{display:flex;flex-direction:row;position:relative;flex:1}`;
6191
6395
 
@@ -10463,8 +10667,21 @@ class AuroFormValidation {
10463
10667
  return;
10464
10668
  }
10465
10669
 
10466
- // Validate that the date passed was the correct format and is a valid date
10670
+ // Validate that the date passed was the correct format and is a valid date.
10671
+ // For partial date formats, valueObject is never populated; validate them directly.
10467
10672
  if (elem.value && !elem.valueObject) {
10673
+ const isPartialDateFormat = elem.util && !elem.util.isFullDateFormat(elem.type, elem.format);
10674
+
10675
+ if (isPartialDateFormat) {
10676
+ if (!elem.util.isValidPartialDate(elem.value, elem.format)) {
10677
+ elem.validity = 'patternMismatch';
10678
+ elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
10679
+ }
10680
+ // Partial date format — validate directly and skip max/min checks since valueObject is undefined.
10681
+ return;
10682
+ }
10683
+
10684
+ // Full date format with no valueObject means the value is not a valid calendar date.
10468
10685
  elem.validity = 'patternMismatch';
10469
10686
  elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
10470
10687
  return;
@@ -10561,15 +10778,17 @@ class AuroFormValidation {
10561
10778
  );
10562
10779
  }
10563
10780
 
10564
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
10565
- if (this.auroInputElements?.length === 2) {
10566
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
10781
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10782
+
10783
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
10784
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
10785
+ // field (datepicker is the intended consumer — start/end are independently required).
10786
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
10787
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
10567
10788
  hasValue = false;
10568
10789
  }
10569
10790
  }
10570
10791
 
10571
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10572
-
10573
10792
  if (isCombobox) {
10574
10793
 
10575
10794
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -10668,7 +10887,7 @@ class AuroFormValidation {
10668
10887
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
10669
10888
  const input = elem.renderRoot.querySelector('input');
10670
10889
 
10671
- if (input.validationMessage.length > 0) {
10890
+ if (input && input.validationMessage.length > 0) {
10672
10891
  elem.errorMessage = input.validationMessage;
10673
10892
  }
10674
10893
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
@@ -16250,8 +16469,6 @@ function cleanEscapedString(input) {
16250
16469
  return input.match(escapedStringRegExp)[1].replace(doubleQuoteRegExp, "'");
16251
16470
  }
16252
16471
 
16253
- /* eslint-disable max-lines */
16254
-
16255
16472
  class AuroInputUtilities {
16256
16473
 
16257
16474
  /**
@@ -16294,7 +16511,7 @@ class AuroInputUtilities {
16294
16511
  /**
16295
16512
  * Converts an IMask-style date mask to a date-fns compatible format string.
16296
16513
  * @param {string} mask - IMask date mask (e.g. "MM/DD/YYYY").
16297
- * @returns {string} date-fns format string (e.g. "MM/dd/yyyy").
16514
+ * @returns {string} A date-fns format string (e.g. "MM/dd/yyyy").
16298
16515
  */
16299
16516
  toDateFnsMask(mask) {
16300
16517
  return mask
@@ -16394,10 +16611,11 @@ class AuroInputUtilities {
16394
16611
  const dateFormat = format$1 || this.overrideFormat || pattern || 'mm/dd/yyyy';
16395
16612
 
16396
16613
  if (dateFormat === 'dd' || dateFormat === 'yy' || dateFormat === 'yyyy') {
16397
- const maxValue = dateFormat === 'dd' ? 31 : (dateFormat === 'yy' ? 99 : 9999);
16614
+ const fromValue = dateFormat === 'yyyy' ? 1900 : (dateFormat === 'yy' ? 0 : 1);
16615
+ const maxValue = dateFormat === 'dd' ? 31 : (dateFormat === 'yy' ? 99 : 2100);
16398
16616
  return {
16399
16617
  mask: IMask.MaskedRange,
16400
- from: 1,
16618
+ from: fromValue,
16401
16619
  to: maxValue,
16402
16620
  lazy: true,
16403
16621
  placeholderChar: '',
@@ -16405,7 +16623,8 @@ class AuroInputUtilities {
16405
16623
  return value.toString().padStart(dateFormat.length, '0');
16406
16624
  },
16407
16625
  parse(str) {
16408
- return parseInt(str) || null;
16626
+ const num = parseInt(str, 10);
16627
+ return isNaN(num) ? null : num;
16409
16628
  }
16410
16629
  };
16411
16630
  }
@@ -16469,6 +16688,49 @@ class AuroInputUtilities {
16469
16688
  return type === 'date' && normalizedFormat.includes('yy') && normalizedFormat.includes('mm') && normalizedFormat.includes('dd');
16470
16689
  }
16471
16690
 
16691
+ /**
16692
+ * Validates a value against a partial date format (one that lacks yy/mm/dd all three).
16693
+ * Day- and year-only formats (dd/yy/yyyy) are checked as integer ranges; other partial formats use
16694
+ * a date-fns parse + round-trip to confirm both validity and exact formatting.
16695
+ * @param {string} value - The user-facing display value.
16696
+ * @param {string} format - The partial date format string (e.g. "mm/yyyy", "yyyy", "dd").
16697
+ * @returns {boolean}
16698
+ */
16699
+ isValidPartialDate(value, format$1) {
16700
+ if (!value || !format$1) {
16701
+ return false;
16702
+ }
16703
+ const normalizedFormat = format$1.toLowerCase();
16704
+
16705
+ if (normalizedFormat === 'dd') {
16706
+ const num = Number(value);
16707
+ return (/^\d{2}$/u).test(value) && num >= 1 && num <= 31;
16708
+ }
16709
+ if (normalizedFormat === 'yy') {
16710
+ const num = Number(value);
16711
+ return (/^\d{2}$/u).test(value) && num >= 0 && num <= 99;
16712
+ }
16713
+ if (normalizedFormat === 'yyyy') {
16714
+ const num = Number(value);
16715
+ return (/^\d{4}$/u).test(value) && num >= 1900 && num <= 2100;
16716
+ }
16717
+
16718
+ const dateFnsMask = this.toDateFnsMask(normalizedFormat);
16719
+ // Use the 1st of the current month as the reference so that formats
16720
+ // omitting a day (e.g. MM/yyyy) never roll over on days 29–31.
16721
+ const referenceDate = new Date();
16722
+ referenceDate.setDate(1);
16723
+ const parsed = parse(value, dateFnsMask, referenceDate);
16724
+ if (!isValid(parsed) || format(parsed, dateFnsMask) !== value) {
16725
+ return false;
16726
+ }
16727
+ if (normalizedFormat.includes('yyyy')) {
16728
+ const year = parsed.getFullYear();
16729
+ return year >= 1900 && year <= 2100;
16730
+ }
16731
+ return true;
16732
+ }
16733
+
16472
16734
  /**
16473
16735
  * Converts a display string to its model value.
16474
16736
  * For full date formats, converts the display string to an ISO date string.
@@ -16505,6 +16767,13 @@ class AuroInputUtilities {
16505
16767
  * @returns {string | undefined}
16506
16768
  */
16507
16769
  toFormattedValue(valueObject, format) {
16770
+ // Exposed publicly via `AuroInputUtil.toFormattedValue`, so a missing
16771
+ // `format` argument has to fail gracefully rather than throw on
16772
+ // `format.toLowerCase()`. Treat any falsy value (undefined, null, '')
16773
+ // as "no format → no display value".
16774
+ if (!format) {
16775
+ return undefined;
16776
+ }
16508
16777
  const normalizedFormat = format.toLowerCase();
16509
16778
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
16510
16779
 
@@ -16643,9 +16912,10 @@ class DomHandler {
16643
16912
  class BaseInput extends AuroElement$1 {
16644
16913
 
16645
16914
  // Delegate focus to the native <input> inside the shadow root so that
16646
- // showModal()'s dialog focusing steps reach the input element.
16647
- // This keeps the mobile virtual keyboard open when the fullscreen dialog
16648
- // opens, because the browser sees an input-to-input focus transfer.
16915
+ // clicking anywhere on the input wrapper focuses the native input, and
16916
+ // programmatic .focus() calls on elements in the shadow DOM work correctly.
16917
+ // The initial-load focus theft was caused by the static `autofocus`
16918
+ // attribute on the bib input (now conditional), not by delegatesFocus.
16649
16919
  static get shadowRootOptions() {
16650
16920
  return {
16651
16921
  ...AuroElement$1.shadowRootOptions,
@@ -16656,79 +16926,19 @@ class BaseInput extends AuroElement$1 {
16656
16926
  constructor() {
16657
16927
  super();
16658
16928
 
16659
- this.appearance = "default";
16660
- this.disabled = false;
16661
- this.layout = 'classic';
16662
- this.locale = 'en-US';
16663
- this.max = undefined;
16664
- this._maxObject = undefined;
16665
- this.maxLength = undefined;
16666
- this.min = undefined;
16667
- this._minObject = undefined;
16668
- this.minLength = undefined;
16669
- this.required = false;
16670
- this.onDark = false;
16671
- this.setCustomValidityForType = undefined;
16672
- this.size = 'lg';
16673
- this.shape = 'classic';
16674
- this.value = undefined;
16675
- this._valueObject = undefined;
16676
-
16677
- this._initializePrivateDefaults();
16678
- }
16679
-
16680
- /**
16681
- * Internal Defaults.
16682
- * @private
16683
- * @returns {void}
16684
- */
16685
- _initializePrivateDefaults() {
16929
+ // Single-source initialization. Alphabetized so duplicate or stale
16930
+ // defaults are immediately obvious on a diff. Every field is assigned
16931
+ // exactly once here (previously the constructor + the old
16932
+ // `_initializePrivateDefaults` overlapped — both wrote ~14 of the same
16933
+ // fields and double-allocated `util` and `validation`, discarding the
16934
+ // first instance). `validation` is now allocated exactly once; `util`
16935
+ // is seeded here with an en-US default and then rebuilt in
16936
+ // `connectedCallback` once the consumer-resolved locale is available,
16937
+ // so a parent (datepicker/combobox) calling `validate()` synchronously
16938
+ // during its own update cycle sees a populated util instance.
16686
16939
  this.activeLabel = false;
16687
- this.appearance = "default";
16688
- this.icon = false;
16689
- this.disabled = false;
16690
- this.dvInputOnly = false;
16691
- this.hideLabelVisually = false;
16692
- this.max = undefined;
16693
- this.maxLength = undefined;
16694
- this.min = undefined;
16695
- this.minLength = undefined;
16696
- this.noValidate = false;
16697
- this.onDark = false;
16698
- this.required = false;
16699
- this.setCustomValidityForType = undefined;
16700
-
16701
- // Used for storing raw values returned from input mask.
16702
- this._rawMaskValue = undefined;
16703
-
16704
- /**
16705
- * @private
16706
- */
16707
- this.layout = 'classic';
16708
-
16709
- /**
16710
- * @private
16711
- */
16712
- this.shape = 'classic';
16713
-
16714
- /**
16715
- * @private
16716
- */
16717
- this.size = 'lg';
16718
-
16719
- this.touched = false;
16720
- this.util = new AuroInputUtilities({
16721
- locale: "en-US",
16722
- format: this.format
16723
- });
16724
- this.validation = new AuroFormValidation();
16725
- this.inputIconName = undefined;
16726
- this.showPassword = false;
16727
- this.validationCCLength = undefined;
16728
- this.hasValue = false;
16729
- this.label = 'Input label is undefined';
16730
-
16731
16940
 
16941
+ /** @private */
16732
16942
  this.allowedInputTypes = [
16733
16943
  "text",
16734
16944
  "number",
@@ -16737,7 +16947,9 @@ class BaseInput extends AuroElement$1 {
16737
16947
  "credit-card",
16738
16948
  "tel"
16739
16949
  ];
16740
- this.icon = false;
16950
+ this.appearance = "default";
16951
+
16952
+ /** @private */
16741
16953
  this.dateFormatMap = {
16742
16954
  'mm/dd/yyyy': 'dateMMDDYYYY',
16743
16955
  'dd/mm/yyyy': 'dateDDMMYYYY',
@@ -16754,27 +16966,64 @@ class BaseInput extends AuroElement$1 {
16754
16966
  'dd/mm': 'dateDDMM',
16755
16967
  'mm/dd': 'dateMMDD'
16756
16968
  };
16969
+ this.disabled = false;
16970
+
16971
+ /** @private */
16757
16972
  this.domHandler = new DomHandler();
16758
16973
  this.dvInputOnly = false;
16759
16974
  this.hasValue = false;
16975
+ this.hideLabelVisually = false;
16976
+ this.icon = false;
16977
+
16978
+ /** @private */
16760
16979
  this.inputIconName = undefined;
16980
+
16981
+ /** @private */
16761
16982
  this.label = 'Input label is undefined';
16983
+ this.layout = 'classic';
16984
+ this.locale = 'en-US';
16985
+ this._format = undefined;
16986
+
16987
+ /** @private */
16988
+ this._userSetFormat = false;
16989
+ this.max = undefined;
16990
+ this.maxLength = undefined;
16991
+ this.min = undefined;
16992
+ this.minLength = undefined;
16762
16993
  this.noValidate = false;
16763
- this._rawMaskValue = undefined; // Used for storing raw values returned from input mask.
16994
+ this.onDark = false;
16995
+ this.required = false;
16996
+ this.setCustomValidityForType = undefined;
16997
+ // Credit Card is intentionally excluded — its mask manages the cursor
16998
+ // itself, and listing it here caused cursor placement issues in Safari.
16999
+ /** @private */
16764
17000
  this.setSelectionInputTypes = [
16765
17001
  "text",
16766
17002
  "password",
16767
17003
  "email"
16768
- ]; // Credit Card is not included as this caused cursor placement issues in Safari.
17004
+ ];
17005
+ this.shape = 'classic';
17006
+
17007
+ /** @private */
16769
17008
  this.showPassword = false;
17009
+ this.size = 'lg';
16770
17010
  this.touched = false;
17011
+
17012
+ /** @private */
16771
17013
  this.uniqueId = new UniqueId().create();
17014
+
17015
+ /** @private */
16772
17016
  this.util = new AuroInputUtilities({
16773
17017
  locale: this.locale,
16774
17018
  format: this.format
16775
17019
  });
17020
+
17021
+ /** @private */
16776
17022
  this.validation = new AuroFormValidation();
17023
+
17024
+ /** @private */
16777
17025
  this.validationCCLength = undefined;
17026
+ this.value = undefined;
16778
17027
  }
16779
17028
 
16780
17029
  // function to define props used within the scope of this component
@@ -16900,7 +17149,8 @@ class BaseInput extends AuroElement$1 {
16900
17149
  */
16901
17150
  format: {
16902
17151
  type: String,
16903
- reflect: true
17152
+ reflect: true,
17153
+ noAccessor: true
16904
17154
  },
16905
17155
 
16906
17156
  /**
@@ -17104,6 +17354,13 @@ class BaseInput extends AuroElement$1 {
17104
17354
  type: String
17105
17355
  },
17106
17356
 
17357
+ /**
17358
+ * Custom help text message to display when validity = `patternMismatch`.
17359
+ */
17360
+ setCustomValidityPatternMismatch: {
17361
+ type: String
17362
+ },
17363
+
17107
17364
  /**
17108
17365
  * Custom help text message to display when validity = `rangeOverflow`.
17109
17366
  */
@@ -17174,7 +17431,7 @@ class BaseInput extends AuroElement$1 {
17174
17431
 
17175
17432
  /**
17176
17433
  * Populates the `type` attribute on the input.
17177
- * @type {'text' | 'password' | 'email' | 'credit-card' | 'tel' | 'number'}
17434
+ * @type {'text' | 'password' | 'email' | 'credit-card' | 'tel' | 'number' | 'date'}
17178
17435
  * @default 'text'
17179
17436
  */
17180
17437
  type: {
@@ -17199,7 +17456,7 @@ class BaseInput extends AuroElement$1 {
17199
17456
 
17200
17457
  /**
17201
17458
  * Populates the `value` attribute on the input. Can also be read to retrieve the current value of the input.
17202
- * The format for this property should be ISO for `date` type inputs.
17459
+ * For `date` type inputs using a full date format (year/month/day), the `value` should be ISO (YYYY-MM-DD). Partial date formats use the display format.
17203
17460
  */
17204
17461
  value: {
17205
17462
  type: String
@@ -17212,7 +17469,7 @@ class BaseInput extends AuroElement$1 {
17212
17469
  * @returns {Date|undefined}
17213
17470
  */
17214
17471
  get valueObject() {
17215
- return this._valueObject;
17472
+ return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
17216
17473
  }
17217
17474
 
17218
17475
  /**
@@ -17220,7 +17477,7 @@ class BaseInput extends AuroElement$1 {
17220
17477
  * @returns {Date|undefined}
17221
17478
  */
17222
17479
  get minObject() {
17223
- return this._minObject;
17480
+ return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
17224
17481
  }
17225
17482
 
17226
17483
  /**
@@ -17228,33 +17485,63 @@ class BaseInput extends AuroElement$1 {
17228
17485
  * @returns {Date|undefined}
17229
17486
  */
17230
17487
  get maxObject() {
17231
- return this._maxObject;
17488
+ return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
17489
+ }
17490
+
17491
+ get format() {
17492
+ return this._format;
17232
17493
  }
17233
17494
 
17234
17495
  /**
17235
- * Internal setter for readonly date object properties.
17236
- * @private
17237
- * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
17238
- * @param {Date|undefined} propertyValue - Value to assign.
17239
- * @returns {void}
17496
+ * Overrides LitElement's generated accessor so we can track whether the
17497
+ * consumer explicitly set `format`. Locale-derived updates use
17498
+ * `_setFormatFromLocale` instead, which skips this flag.
17240
17499
  */
17241
- setDateObjectProperty(propertyName, propertyValue) {
17242
- const internalPropertyName = `_${propertyName}`;
17243
- const previousValue = this[internalPropertyName];
17244
-
17245
- if (previousValue === propertyValue) {
17246
- return;
17247
- }
17500
+ set format(value) {
17501
+ const oldValue = this._format;
17502
+ this._format = value ? value.toLowerCase() : value;
17503
+ this._userSetFormat = Boolean(value);
17504
+ this.requestUpdate('format', oldValue);
17505
+ }
17248
17506
 
17249
- this[internalPropertyName] = propertyValue;
17250
- this.requestUpdate(propertyName, previousValue);
17507
+ /**
17508
+ * Sets format without marking it as user-set. Used by locale auto-derive
17509
+ * so that a subsequent locale change can still update the format.
17510
+ * @private
17511
+ * @param {string} value
17512
+ */
17513
+ _setFormatFromLocale(value) {
17514
+ const oldValue = this._format;
17515
+ this._format = value ? value.toLowerCase() : value;
17516
+ this.requestUpdate('format', oldValue);
17251
17517
  }
17252
17518
 
17253
17519
  connectedCallback() {
17254
17520
  super.connectedCallback();
17255
17521
 
17522
+ // Mark for query selectors when registered under a versioned tag (e.g. inside
17523
+ // a datepicker/combobox). Must run before parent components call validate() on
17524
+ // their inner inputs — the validation framework matches via tag-or-attribute,
17525
+ // and parents trigger validation during their own updated() cycle, which can
17526
+ // precede this input's firstUpdated().
17527
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
17528
+ this.setAttribute('auro-input', '');
17529
+ }
17530
+
17256
17531
  this.locale = this.domHandler.getLocale(this);
17257
17532
  notifyOnLangChange(this);
17533
+
17534
+ // Pre-compute lengthForType and date-object fields so a parent (e.g. datepicker)
17535
+ // calling our validate() synchronously during its own updated() cycle sees
17536
+ // populated values. Without this, range validation silently no-ops because
17537
+ // `max.length === lengthForType` fails when lengthForType is still undefined.
17538
+ // Rebuild util here (constructor seeded with en-US default) so configureDataForType's
17539
+ // locale-derived format lookup uses the actual locale just resolved above.
17540
+ this.util = new AuroInputUtilities({
17541
+ locale: this.locale,
17542
+ format: this.format
17543
+ });
17544
+ this.configureDataForType();
17258
17545
  }
17259
17546
 
17260
17547
  disconnectedCallback() {
@@ -17274,10 +17561,6 @@ class BaseInput extends AuroElement$1 {
17274
17561
  this.wrapperElement.addEventListener('click', this.handleClick);
17275
17562
  }
17276
17563
 
17277
- // add attribute for query selectors when auro-input is registered under a custom name
17278
- if (this.tagName.toLowerCase() !== 'auro-input') {
17279
- this.setAttribute('auro-input', true);
17280
- }
17281
17564
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
17282
17565
 
17283
17566
  // use validity message override if declared when initializing the component
@@ -17288,7 +17571,6 @@ class BaseInput extends AuroElement$1 {
17288
17571
  this.setCustomHelpTextMessage();
17289
17572
  this.configureAutoFormatting();
17290
17573
  this.configureDataForType();
17291
- this.syncDateValues();
17292
17574
  }
17293
17575
 
17294
17576
  /**
@@ -17324,6 +17606,12 @@ class BaseInput extends AuroElement$1 {
17324
17606
  return;
17325
17607
  }
17326
17608
 
17609
+ // While configureAutoFormatting is running, imask's internal updateControl
17610
+ // writes el.value to align display with the masked value. A synthetic input
17611
+ // event from that write would re-enter handleInput → processCreditCard mid-setup
17612
+ // and clobber a Lit value that was just pushed by the parent combobox.
17613
+ if (component._configuringMask) return;
17614
+
17327
17615
  // If all guard clauses are passed, dispatch the event
17328
17616
  const inputEvent = new InputEvent('input', {
17329
17617
  bubbles: true,
@@ -17340,7 +17628,7 @@ class BaseInput extends AuroElement$1 {
17340
17628
 
17341
17629
  /**
17342
17630
  * @private
17343
- * @deprecated https://dev.azure.com/itsals/E_Retain_Content/_workitems/edit/1557296
17631
+ * @deprecated See https://dev.azure.com/itsals/E_Retain_Content/_workitems/edit/1557296.
17344
17632
  * @returns {void} Sets the default help text for the input.
17345
17633
  */
17346
17634
  setCustomHelpTextMessage() {
@@ -17358,6 +17646,13 @@ class BaseInput extends AuroElement$1 {
17358
17646
 
17359
17647
  if (typeToI18n.includes(this.type)) {
17360
17648
  this.setCustomValidityForType = i18n(this.lang, this.type);
17649
+ // COVERAGE: this `else if` branch is unreachable in WTR. connectedCallback
17650
+ // (L682) calls configureDataForType, which at L1266-1268 assigns
17651
+ // `this.format = this.util.getDateMaskFromLocale().toLowerCase()` for any
17652
+ // type=date input whose `format` attribute is unset. That runs before
17653
+ // firstUpdated invokes setCustomHelpTextMessage, so `!this.format` is
17654
+ // always false here. Retained as a defensive fallback; the whole function
17655
+ // is @deprecated per AB#1557296 and slated for removal.
17361
17656
  } else if (!this.format && this.type === 'date') {
17362
17657
  this.setCustomValidityForType = i18n(this.lang, 'dateMMDDYYYY');
17363
17658
  } else if (this.dateFormatMap[this.format]) {
@@ -17374,12 +17669,10 @@ class BaseInput extends AuroElement$1 {
17374
17669
  updated(changedProperties) {
17375
17670
  super.updated(changedProperties);
17376
17671
 
17377
- // When locale changes without an explicit format override, derive format from the new locale.
17378
- // Only runs if the current format is still the previous locale's default (not user-overridden).
17672
+ // When locale changes, auto-derive format unless the consumer explicitly set one.
17379
17673
  if (changedProperties.has('locale') && !changedProperties.has('format') && this.type === 'date') {
17380
- const previousLocaleFormat = getDateFormatFromLocale(changedProperties.get('locale'));
17381
- if (!this.format || this.format.toLowerCase() === previousLocaleFormat) {
17382
- this.format = getDateFormatFromLocale(this.locale);
17674
+ if (!this._userSetFormat) {
17675
+ this._setFormatFromLocale(getDateFormatFromLocale(this.locale));
17383
17676
  }
17384
17677
  }
17385
17678
 
@@ -17419,8 +17712,6 @@ class BaseInput extends AuroElement$1 {
17419
17712
  this.configureDataForType();
17420
17713
  }
17421
17714
 
17422
- this.syncDateValues(changedProperties);
17423
-
17424
17715
  if (changedProperties.has('value')) {
17425
17716
  if (this.value && this.value.length > 0) {
17426
17717
  this.hasValue = true;
@@ -17442,7 +17733,16 @@ class BaseInput extends AuroElement$1 {
17442
17733
 
17443
17734
  if (formattedValue !== this.inputElement.value) {
17444
17735
  this.skipNextProgrammaticInputEvent = true;
17445
- if (formattedValue) {
17736
+ if (this.maskInstance && this.type !== 'date') {
17737
+ // Route through the mask so its _value and el.value stay in lock-step
17738
+ // (set value calls updateControl which writes el.value = displayValue).
17739
+ // Writing el.value directly leaves the mask thinking displayValue is
17740
+ // stale; _saveSelection on the next focus/click then warns. Date is
17741
+ // excluded because its formattedValue can be raw ISO when the calendar
17742
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
17743
+ // flip validity from patternMismatch to tooShort.
17744
+ this.maskInstance.value = formattedValue || '';
17745
+ } else if (formattedValue) {
17446
17746
  this.inputElement.value = formattedValue;
17447
17747
  } else {
17448
17748
  this.inputElement.value = '';
@@ -17462,87 +17762,24 @@ class BaseInput extends AuroElement$1 {
17462
17762
  }
17463
17763
 
17464
17764
  if (changedProperties.has('error')) {
17465
- this.validate(true);
17466
- }
17467
-
17468
- if (changedProperties.has('validity')) {
17469
- this.notifyValidityChange();
17470
- }
17471
- }
17472
-
17473
- /**
17474
- * @private
17475
- * @returns {void} Notify validity state changed via event.
17476
- */
17477
- notifyValidityChange() {
17478
- this.dispatchEvent(new CustomEvent('auroInput-validityChange', {
17479
- bubbles: true,
17480
- cancelable: false,
17481
- composed: true,
17482
- }));
17483
- }
17484
-
17485
-
17486
- /**
17487
- * Synchronizes the ISO string values and Date object representations for date-related properties.
17488
- * This keeps the model and display values aligned when either side changes.
17489
- *
17490
- * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
17491
- * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
17492
- * represents a full year/month/day date format.
17493
- *
17494
- * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
17495
- * @returns {void}
17496
- * @private
17497
- */
17498
- syncDateValues(changedProperties = undefined) {
17499
- if (!this.util.isFullDateFormat(this.type, this.format)) {
17500
- return;
17501
- }
17502
-
17503
- this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
17504
- this.syncSingleDateValue(changedProperties, 'minObject', 'min');
17505
- this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
17506
- }
17507
-
17508
- /**
17509
- * Synchronizes one date object/string property pair.
17510
- * @private
17511
- * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
17512
- * @param {string} objectProperty - Date object property name.
17513
- * @param {string} valueProperty - ISO string property name.
17514
- * @returns {void}
17515
- */
17516
- syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
17517
- const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
17518
-
17519
- // objectProperty wins over valueProperty when both changed
17520
- if (objectPropertyChanged && this[objectProperty]) {
17521
- this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
17522
- return;
17523
- }
17524
-
17525
- const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
17526
- if (!valuePropertyChanged) {
17527
- return;
17765
+ this.validate(true);
17528
17766
  }
17529
17767
 
17530
- // when value is newly set to the same ISO string that corresponds to the existing Date object, do not clear the Date object (avoid unnecessary updates)
17531
- if (
17532
- changedProperties &&
17533
- valueProperty === 'value' &&
17534
- changedProperties.get('value') === undefined &&
17535
- this[objectProperty] instanceof Date &&
17536
- this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
17537
- ) {
17538
- return;
17768
+ if (changedProperties.has('validity')) {
17769
+ this.notifyValidityChange();
17539
17770
  }
17771
+ }
17540
17772
 
17541
- if (dateFormatter.isValidDate(this[valueProperty])) {
17542
- this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
17543
- } else {
17544
- this.setDateObjectProperty(objectProperty, undefined);
17545
- }
17773
+ /**
17774
+ * @private
17775
+ * @returns {void} Notify validity state changed via event.
17776
+ */
17777
+ notifyValidityChange() {
17778
+ this.dispatchEvent(new CustomEvent('auroInput-validityChange', {
17779
+ bubbles: true,
17780
+ cancelable: false,
17781
+ composed: true,
17782
+ }));
17546
17783
  }
17547
17784
 
17548
17785
  /**
@@ -17551,58 +17788,79 @@ class BaseInput extends AuroElement$1 {
17551
17788
  * @returns {void}
17552
17789
  */
17553
17790
  configureAutoFormatting() {
17554
- if (this.maskInstance) {
17555
- this.maskInstance.destroy();
17556
- }
17791
+ // _configuringMask gates two things: external re-entry into this method
17792
+ // while setup is mid-flight (from property changes that call back here),
17793
+ // and the accept/complete listeners below — both need to ignore the mask
17794
+ // events fired by our own value-restore step.
17795
+ if (this._configuringMask) return;
17796
+ this._configuringMask = true;
17797
+ try {
17798
+ // Destroy any prior mask so IMask can attach fresh under the new format.
17799
+ // Null the reference too — if the new maskOptions.mask is falsy (e.g.
17800
+ // type switched from credit-card to text) the IMask() reassignment
17801
+ // below is skipped, and downstream writes at line ~823 would otherwise
17802
+ // route through the destroyed instance.
17803
+ if (this.maskInstance) {
17804
+ this.maskInstance.destroy();
17805
+ this.maskInstance = null;
17806
+ }
17557
17807
 
17558
- // Pass new format to util
17559
- this.util.updateFormat(this.format);
17808
+ this.util.updateFormat(this.format);
17560
17809
 
17561
- const maskOptions = this.util.getMaskOptions(this.type, this.format);
17810
+ const maskOptions = this.util.getMaskOptions(this.type, this.format);
17562
17811
 
17563
- if (this.inputElement && maskOptions.mask) {
17812
+ if (this.inputElement && maskOptions.mask) {
17813
+ // Capture the current display so it can be re-applied after IMask
17814
+ // attaches. The restore at the bottom goes through maskInstance.value
17815
+ // (not inputElement.value directly) so the mask's internal state and
17816
+ // the input's displayed text stay in lock-step.
17817
+ let existingValue = this.inputElement.value;
17564
17818
 
17565
- // Stash and clear any existing value before IMask init.
17566
- // IMask's constructor processes the current input value which requires
17567
- // selection state clearing first avoids that scenario entirely.
17568
- // When the format changes (e.g. locale switch) and we have a valid ISO
17569
- // model value, compute the display string for the NEW format instead of
17570
- // re-using the old display string, which may be invalid in the new mask.
17571
- let existingValue = this.inputElement.value;
17572
- if (
17573
- this.util.isFullDateFormat(this.type, this.format) &&
17574
- this.value &&
17575
- dateFormatter.isValidDate(this.value) &&
17576
- this.valueObject instanceof Date &&
17577
- !Number.isNaN(this.valueObject.getTime()) &&
17578
- typeof maskOptions.format === 'function'
17579
- ) {
17580
- existingValue = maskOptions.format(this.valueObject);
17581
- }
17819
+ // Format-change case (e.g. locale switch): existingValue is the OLD
17820
+ // mask's display string and may not parse under the new mask. When
17821
+ // we have a valid date model, rebuild the display from valueObject
17822
+ // (the canonical source) using the new mask's format function.
17823
+ if (
17824
+ this.util.isFullDateFormat(this.type, this.format) &&
17825
+ this.value &&
17826
+ this.valueObject &&
17827
+ typeof maskOptions.format === 'function'
17828
+ ) {
17829
+ existingValue = maskOptions.format(this.valueObject);
17830
+ }
17582
17831
 
17583
- this.skipNextProgrammaticInputEvent = true;
17584
- this.inputElement.value = '';
17832
+ // Clear before IMask attaches so the constructor seeds an empty
17833
+ // internal value. Otherwise IMask reads the stale unmasked string
17834
+ // and emits a spurious 'accept' before the restore below runs.
17835
+ this.skipNextProgrammaticInputEvent = true;
17836
+ this.inputElement.value = '';
17585
17837
 
17586
- this.maskInstance = IMask(this.inputElement, maskOptions);
17838
+ this.maskInstance = IMask(this.inputElement, maskOptions);
17587
17839
 
17588
- this.maskInstance.on('accept', () => {
17589
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
17590
- if (this.type === "date") {
17591
- this._rawMaskValue = this.maskInstance.value;
17592
- }
17593
- });
17840
+ // Mask fires 'accept' on every value change, including the restore
17841
+ // step below. Skip events fired during configureAutoFormatting so
17842
+ // we don't overwrite a value the parent just pushed.
17843
+ this.maskInstance.on('accept', () => {
17844
+ if (this._configuringMask) return;
17845
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
17846
+ });
17594
17847
 
17595
- this.maskInstance.on('complete', () => {
17596
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
17597
- if (this.type === "date") {
17598
- this._rawMaskValue = this.maskInstance.value;
17599
- }
17600
- });
17848
+ // Mask fires 'complete' on the restore step below for any value that
17849
+ // happens to be a complete match. Same setup-suppression as 'accept'.
17850
+ this.maskInstance.on('complete', () => {
17851
+ if (this._configuringMask) return;
17852
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
17853
+ });
17601
17854
 
17602
- // Restore the stashed value through IMask so it's properly masked
17603
- if (existingValue) {
17604
- this.maskInstance.value = existingValue;
17855
+ // Write existingValue through the mask (not the input directly) so
17856
+ // the mask reformats it under the new rules and keeps its internal
17857
+ // _value aligned with the input's displayed text.
17858
+ if (existingValue) {
17859
+ this.maskInstance.value = existingValue;
17860
+ }
17605
17861
  }
17862
+ } finally {
17863
+ this._configuringMask = false;
17606
17864
  }
17607
17865
  }
17608
17866
 
@@ -17612,14 +17870,17 @@ class BaseInput extends AuroElement$1 {
17612
17870
  * @returns {void}
17613
17871
  */
17614
17872
  notifyValueChanged() {
17615
- let inputEvent = null;
17616
-
17617
- inputEvent = new Event('input', {
17873
+ // This echoes Lit's reactive value update (and handleClickClear's
17874
+ // programmatic clear) — it is NOT a fresh user-input event. Mark it
17875
+ // `isProgrammatic` so consumers (e.g. auro-combobox) can distinguish
17876
+ // it from genuine user typing and skip clobbering their own
17877
+ // programmatic state during init-time renders.
17878
+ const inputEvent = new Event('input', {
17618
17879
  bubbles: true,
17619
17880
  composed: true,
17620
17881
  });
17882
+ inputEvent.isProgrammatic = true;
17621
17883
 
17622
- // Dispatched event to alert outside shadow DOM context of event firing.
17623
17884
  this.dispatchEvent(inputEvent);
17624
17885
  }
17625
17886
 
@@ -17676,6 +17937,38 @@ class BaseInput extends AuroElement$1 {
17676
17937
  this.touched = true;
17677
17938
  this.validation.validate(this);
17678
17939
  }
17940
+
17941
+ // Prevents cursor jumping in Safari. Setting `this.value` triggers a Lit
17942
+ // update that can re-render the input and reset the native cursor; we
17943
+ // capture the caret position before that update commits and restore it
17944
+ // via `setSelectionRange` once the update has flushed. Gated on
17945
+ // `setSelectionInputTypes` so credit-card (and other masked types whose
17946
+ // formatter manages the cursor itself) doesn't get a competing write.
17947
+ // Capture the caret position INSIDE the gate — reading `selectionStart`
17948
+ // on input types that don't support text selection (number, email in
17949
+ // some browsers) throws InvalidStateError, which would crash all input
17950
+ // handling. Wrap the read in try/catch belt-and-suspenders even though
17951
+ // the gated types currently support it, since the list is a public
17952
+ // property a consumer could mutate.
17953
+ if (this.setSelectionInputTypes.includes(this.type)) {
17954
+ let selectionStart = null;
17955
+ try {
17956
+ ({ selectionStart } = this.inputElement);
17957
+ } catch {
17958
+ return;
17959
+ }
17960
+ if (typeof selectionStart !== 'number') {
17961
+ return;
17962
+ }
17963
+ this.updateComplete.then(() => {
17964
+ try {
17965
+ this.inputElement.setSelectionRange(selectionStart, selectionStart);
17966
+ } catch {
17967
+ // Some input types (number/email in certain UAs) throw on
17968
+ // setSelectionRange; swallow and let the native cursor stand.
17969
+ }
17970
+ });
17971
+ }
17679
17972
  }
17680
17973
 
17681
17974
  /**
@@ -17764,7 +18057,6 @@ class BaseInput extends AuroElement$1 {
17764
18057
  */
17765
18058
  reset() {
17766
18059
  this.value = undefined;
17767
- this.setDateObjectProperty('valueObject', undefined);
17768
18060
  this.validation.reset(this);
17769
18061
  }
17770
18062
 
@@ -17773,7 +18065,6 @@ class BaseInput extends AuroElement$1 {
17773
18065
  */
17774
18066
  clear() {
17775
18067
  this.value = undefined;
17776
- this.setDateObjectProperty('valueObject', undefined);
17777
18068
  }
17778
18069
 
17779
18070
  /**
@@ -17798,8 +18089,7 @@ class BaseInput extends AuroElement$1 {
17798
18089
 
17799
18090
  // Set default date format if type=date and no format is defined
17800
18091
  if (this.type === "date" && !this.format) {
17801
- // Use locale to determine default date format
17802
- this.format = this.util.getDateMaskFromLocale().toLowerCase();
18092
+ this._setFormatFromLocale(this.util.getDateMaskFromLocale().toLowerCase());
17803
18093
  this.util.updateFormat(this.format);
17804
18094
  }
17805
18095
  }
@@ -17811,7 +18101,7 @@ class BaseInput extends AuroElement$1 {
17811
18101
  */
17812
18102
  get placeholderStr() {
17813
18103
  if (!this.placeholder && this.type === 'date') {
17814
- return this.format ? this.format.toUpperCase() : 'MM/DD/YYYY';
18104
+ return this.format.toUpperCase();
17815
18105
  }
17816
18106
  return this.placeholder || "";
17817
18107
  }
@@ -17828,7 +18118,7 @@ class BaseInput extends AuroElement$1 {
17828
18118
  const creditCard = this.matchInputValueToCreditCard();
17829
18119
  const previousFormat = this.format;
17830
18120
 
17831
- this.format = creditCard.maskFormat;
18121
+ this._setFormatFromLocale(creditCard.maskFormat);
17832
18122
 
17833
18123
  this.maxLength = creditCard.formatLength;
17834
18124
  this.minLength = creditCard.formatMinLength;
@@ -17848,7 +18138,7 @@ class BaseInput extends AuroElement$1 {
17848
18138
  /**
17849
18139
  * Function to support credit-card feature type.
17850
18140
  * @private
17851
- * @deprecated https://dev.azure.com/itsals/E_Retain_Content/_workitems/edit/1557296
18141
+ * @deprecated See https://dev.azure.com/itsals/E_Retain_Content/_workitems/edit/1557296.
17852
18142
  * @returns {object} JSON with data for credit card formatting.
17853
18143
  */
17854
18144
  matchInputValueToCreditCard() {
@@ -17889,10 +18179,10 @@ class BaseInput extends AuroElement$1 {
17889
18179
  },
17890
18180
  {
17891
18181
  name: 'Diners club',
17892
- regex: /^(?<num>36|38)\d{0}/u,
18182
+ regex: /^(?<num>30[0-5]|36|38)\d{0}/u,
17893
18183
  formatLength: 16,
17894
18184
  errorMessage: CreditCardValidationMessage,
17895
- cardIcon: 'credit-card',
18185
+ cardIcon: 'cc-dinersclub',
17896
18186
  maskFormat: "0000 000000 0000"
17897
18187
  },
17898
18188
  {
@@ -18302,7 +18592,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
18302
18592
  }
18303
18593
  };
18304
18594
 
18305
- var formkitVersion$1 = '202606191810';
18595
+ var formkitVersion$1 = '202607221815';
18306
18596
 
18307
18597
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18308
18598
  // See LICENSE in the project root for license information.
@@ -18588,20 +18878,50 @@ class AuroInput extends BaseInput {
18588
18878
  * @returns {void}
18589
18879
  */
18590
18880
  checkDisplayValueSlotChange() {
18591
- let nodes = this.shadowRoot.querySelector('slot[name="displayValue"]').assignedNodes();
18592
-
18593
- // Handle when DisplayValue is multi-level slot content (e.g. combobox passing displayValue to input)
18594
- if (nodes && nodes[0] && nodes[0].tagName === 'SLOT') {
18595
- nodes = nodes[0].assignedNodes();
18881
+ const slot = this.shadowRoot?.querySelector('slot[name="displayValue"]');
18882
+ if (!slot) {
18883
+ // Shadow slot not yet rendered fall back to checking light-DOM
18884
+ // children so hasDisplayValueContent isn't stuck false when called
18885
+ // before firstUpdated (e.g. from auro-combobox during mount).
18886
+ // Check for any element (including custom elements like <auro-icon>
18887
+ // that render via shadow DOM with no text content).
18888
+ const lightDomNodes = Array.from(this.querySelectorAll('[slot="displayValue"]:not(slot)'));
18889
+ const hasContent = lightDomNodes.some((node) => (
18890
+ node.textContent.trim().length > 0 ||
18891
+ node.children.length > 0 ||
18892
+ node.shadowRoot !== null
18893
+ ));
18894
+ if (this.hasDisplayValueContent !== hasContent) {
18895
+ this.hasDisplayValueContent = hasContent;
18896
+ this.requestUpdate();
18897
+ }
18898
+ return;
18596
18899
  }
18597
-
18598
- let hasContent = false;
18599
-
18600
- if (nodes.length > 0) {
18601
- hasContent = true;
18900
+ const nodes = slot.assignedNodes({ flatten: true });
18901
+
18902
+ // Check for actual visible content, not just node existence.
18903
+ // An empty <span slot="displayValue"></span> forwarded from the
18904
+ // consumer should not be treated as "has content" — it causes the
18905
+ // displayValue wrapper to render (with hasContent class) but show
18906
+ // nothing, blocking the combobox's synthetic displayValue from
18907
+ // being visible on preset/deeplink load.
18908
+ // Custom elements (e.g. <auro-icon>) that render via shadow DOM are
18909
+ // treated as content even when they have no text or light-DOM children.
18910
+ const hasContent = nodes.some((node) => {
18911
+ if (node.nodeType === Node.TEXT_NODE) {
18912
+ return node.textContent.trim().length > 0;
18913
+ }
18914
+ if (node.nodeType === Node.ELEMENT_NODE) {
18915
+ return node.textContent.trim().length > 0 ||
18916
+ node.children.length > 0 ||
18917
+ node.shadowRoot !== null;
18918
+ }
18919
+ return false;
18920
+ });
18921
+ if (this.hasDisplayValueContent !== hasContent) {
18922
+ this.hasDisplayValueContent = hasContent;
18923
+ this.requestUpdate();
18602
18924
  }
18603
-
18604
- this.hasDisplayValueContent = hasContent;
18605
18925
  }
18606
18926
 
18607
18927
  firstUpdated() {
@@ -18625,7 +18945,7 @@ class AuroInput extends BaseInput {
18625
18945
  /**
18626
18946
  * Determines default help text string.
18627
18947
  * @private
18628
- * @deprecated https://dev.azure.com/itsals/E_Retain_Content/_workitems/edit/1557296
18948
+ * @deprecated See https://dev.azure.com/itsals/E_Retain_Content/_workitems/edit/1557296.
18629
18949
  * @returns {string} Evaluates pre-determined help text.
18630
18950
  */
18631
18951
  getHelpText() {
@@ -18805,6 +19125,7 @@ class AuroInput extends BaseInput {
18805
19125
  <${this.buttonTag}
18806
19126
  @click="${this.handleClickShowPassword}"
18807
19127
  appearance="${this.onDark ? 'inverse' : this.appearance}"
19128
+ aria-pressed="${this.showPassword ? 'true' : 'false'}"
18808
19129
  class="notificationBtn passwordBtn"
18809
19130
  shape="circle"
18810
19131
  size="sm"
@@ -18878,25 +19199,29 @@ class AuroInput extends BaseInput {
18878
19199
  * @returns {html} - Returns HTML for the help text and error message.
18879
19200
  */
18880
19201
  renderHtmlHelpText() {
19202
+ // Single `<p>` with stable identity across validity transitions —
19203
+ // previously two distinct templates (valid vs invalid) caused Lit to
19204
+ // replace the node entirely on a flip, and VoiceOver wouldn't
19205
+ // re-announce because the live-region element it was watching had been
19206
+ // removed and a new one inserted. Keeping one node means the `role`,
19207
+ // `aria-live`, and text content all change in-place, which AT does
19208
+ // observe and announce.
19209
+ const isError = this.validity && this.validity !== 'valid';
18881
19210
  return html`
18882
- ${!this.validity || this.validity === undefined || this.validity === 'valid'
18883
- ? html`
18884
- <${this.helpTextTag}
18885
- appearance="${this.onDark ? 'inverse' : this.appearance}">
18886
- <p id="${this.uniqueId}" part="helpText">
18887
- <slot name="helpText">${this.getHelpText()}</slot>
18888
- </p>
18889
- </${this.helpTextTag}>
18890
- `
18891
- : html`
18892
- <${this.helpTextTag} error
18893
- appearance="${this.onDark ? 'inverse' : this.appearance}">
18894
- <p id="${this.uniqueId}" role="alert" aria-live="assertive" part="helpText">
18895
- ${this.errorMessage}
18896
- </p>
18897
- </${this.helpTextTag}>
18898
- `
19211
+ <${this.helpTextTag}
19212
+ appearance="${this.onDark ? 'inverse' : this.appearance}"
19213
+ ?error=${isError}>
19214
+ <p
19215
+ id="${this.uniqueId}"
19216
+ part="helpText"
19217
+ role="${ifDefined(isError ? 'alert' : undefined)}"
19218
+ aria-live="${ifDefined(isError ? 'assertive' : undefined)}">
19219
+ ${isError
19220
+ ? this.errorMessage
19221
+ : html`<slot name="helpText">${this.getHelpText()}</slot>`
18899
19222
  }
19223
+ </p>
19224
+ </${this.helpTextTag}>
18900
19225
  `;
18901
19226
  }
18902
19227
 
@@ -19028,7 +19353,7 @@ class AuroInput extends BaseInput {
19028
19353
 
19029
19354
  var colorCss$1 = css`#headerContainer{box-shadow:var(--ds-auro-dropdownbib-header-boxshadow-color)}#footerContainer{background-color:var(--ds-auro-dropdownbib-footer-container-color)}`;
19030
19355
 
19031
- var styleCss$2 = css`#bibTemplate{display:flex;overflow:hidden;height:100%;max-height:100%;flex-direction:column}#headerContainer{display:flex;flex-direction:column;padding:var(--ds-size-200, 1rem);padding-top:var(--ds-size-400, 2rem);padding-bottom:0}#headerContainer .titleRow{display:flex;flex-direction:row;justify-content:space-between;gap:var(--ds-size-100, 0.5rem)}#headerContainer .header{max-width:calc(100vw - var(--ds-size-900, 4.5rem));padding-top:var(--ds-size-200, 1rem)}#headerContainer .subheader{width:100%}#closeButton [auro-icon]{--ds-auro-icon-size: var(--ds-size-500, 2.5rem)}#bodyContainer{position:relative;overflow:auto;flex:1 1 100%}:host([isfullscreen]) #bibTemplate{max-height:100dvh}:host([isfullscreen]) #bodyContainer{margin-top:var(--ds-size-100, 0.5rem)}#footerContainer{position:absolute;right:0;bottom:0;left:0;padding:var(--ds-size-200, 1rem)}`;
19356
+ var styleCss$2 = css`#bibTemplate{display:flex;overflow:hidden;height:100%;max-height:100%;flex-direction:column}#headerContainer{display:flex;flex-direction:column;padding:var(--ds-size-200, 1rem);padding-top:var(--ds-size-400, 2rem);padding-bottom:0}#headerContainer .titleRow{display:flex;flex-direction:row;justify-content:space-between;padding-bottom:var(--ds-size-200, 1rem);gap:var(--ds-size-100, 0.5rem)}#headerContainer .header{max-width:calc(100vw - var(--ds-size-900, 4.5rem))}#headerContainer .subheader{width:100%}#closeButton [auro-icon]{--ds-auro-icon-size: var(--ds-size-500, 2.5rem)}#bodyContainer{position:relative;overflow:auto;flex:1}:host([isfullscreen]) #bibTemplate{max-height:100dvh}:host([isfullscreen]) #bodyContainer{margin-top:var(--ds-size-100, 0.5rem)}#footerContainer{padding:var(--ds-size-200, 1rem)}`;
19032
19357
 
19033
19358
  var tokenCss = css`:host{--ds-auro-dropdownbib-header-boxshadow-color: var(--ds-elevation-200, 0px 0px 10px rgba(0, 0, 0, 0.15));--ds-auro-dropdownbib-footer-container-color: var(--ds-basic-color-surface-default, #ffffff)}`;
19034
19359
 
@@ -19423,7 +19748,7 @@ class AuroBibtemplate extends LitElement {
19423
19748
  }
19424
19749
  }
19425
19750
 
19426
- var formkitVersion = '202606191810';
19751
+ var formkitVersion = '202607221815';
19427
19752
 
19428
19753
  var styleCss$1 = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
19429
19754
 
@@ -19780,6 +20105,36 @@ function normalizeFilterValue(value) {
19780
20105
  return raw.trim() ? raw.trimEnd() : raw;
19781
20106
  }
19782
20107
 
20108
+ /**
20109
+ * Returns the option's plain-text label suitable for input.value, with
20110
+ * HTML-indentation whitespace collapsed and the displayValue slot text
20111
+ * removed (the slot renders separately in the trigger).
20112
+ * @param {HTMLElement} option - An auro-menuoption element.
20113
+ * @returns {string} Normalized label.
20114
+ */
20115
+ function getOptionLabel(option) {
20116
+ if (!option) {
20117
+ return '';
20118
+ }
20119
+
20120
+ // Consumer-provided override: short-circuit the DOM walk entirely.
20121
+ if (option.dataset && option.dataset.label) {
20122
+ return option.dataset.label;
20123
+ }
20124
+
20125
+ // Walk direct children — the `slot` attribute only applies to direct children
20126
+ // of the slot host, so a shallow filter is sufficient. Avoids the cloneNode +
20127
+ // querySelector allocation that ran on every keystroke via syncValuesAndStates.
20128
+ let text = '';
20129
+ for (const node of option.childNodes) {
20130
+ const isDisplayValueSlot = node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === 'displayValue';
20131
+ if (!isDisplayValueSlot) {
20132
+ text += node.textContent || '';
20133
+ }
20134
+ }
20135
+ return text.replace(/\s+/gu, ' ').trim();
20136
+ }
20137
+
19783
20138
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
19784
20139
  /**
19785
20140
  * The `auro-combobox` element provides users with a way to select an option from a list of filtered or suggested options based on user input.
@@ -19852,8 +20207,6 @@ class AuroCombobox extends AuroElement {
19852
20207
  this.availableOptions = [];
19853
20208
  this.dropdownId = undefined;
19854
20209
  this.dropdownOpen = false;
19855
- this.triggerExpandedState = false;
19856
- this._expandedTimeout = null;
19857
20210
  this._inFullscreenTransition = false;
19858
20211
  this.errorMessage = null;
19859
20212
  this.isHiddenWhileLoading = false;
@@ -19864,6 +20217,30 @@ class AuroCombobox extends AuroElement {
19864
20217
  this.touched = false;
19865
20218
  this.validation = new AuroFormValidation$1();
19866
20219
  this.validity = undefined;
20220
+ this._userTyped = false;
20221
+ // Tracks every setTimeout scheduled via _scheduleTimer so
20222
+ // disconnectedCallback can cancel them. Without this, a detached
20223
+ // combobox's pending timers still fire — most are no-ops, but
20224
+ // configureMenu's racing-condition retry would otherwise loop.
20225
+ this._pendingTimers = new Set();
20226
+ }
20227
+
20228
+ /**
20229
+ * Wraps setTimeout and records the timer id so disconnectedCallback
20230
+ * can cancel any outstanding callbacks. The id is removed from the set
20231
+ * once the callback fires so the set doesn't grow unbounded.
20232
+ * @param {Function} fn - Callback to run.
20233
+ * @param {number} ms - Delay in milliseconds.
20234
+ * @returns {number} The timer id.
20235
+ * @private
20236
+ */
20237
+ _scheduleTimer(fn, ms) {
20238
+ const id = setTimeout(() => {
20239
+ this._pendingTimers.delete(id);
20240
+ fn();
20241
+ }, ms);
20242
+ this._pendingTimers.add(id);
20243
+ return id;
19867
20244
  }
19868
20245
 
19869
20246
  // This function is to define props used within the scope of this component
@@ -19907,7 +20284,19 @@ class AuroCombobox extends AuroElement {
19907
20284
  availableOptions: {
19908
20285
  state: true,
19909
20286
  type: Array,
19910
- reflect: false
20287
+ reflect: false,
20288
+ hasChanged(newVal, oldVal) {
20289
+ if (!oldVal && !newVal) {
20290
+ return false;
20291
+ }
20292
+ if (!oldVal || !newVal) {
20293
+ return true;
20294
+ }
20295
+ if (newVal.length !== oldVal.length) {
20296
+ return true;
20297
+ }
20298
+ return newVal.some((opt, idx) => opt !== oldVal[idx]);
20299
+ }
19911
20300
  },
19912
20301
 
19913
20302
  /**
@@ -20142,6 +20531,8 @@ class AuroCombobox extends AuroElement {
20142
20531
 
20143
20532
  /**
20144
20533
  * If set, the `icon` attribute will be applied to the trigger `auro-input` element.
20534
+ * Icon rendering is currently limited to `type="credit-card"`. Setting `triggerIcon`
20535
+ * without a supported `type` propagates the attribute but does not render a visible icon.
20145
20536
  */
20146
20537
  triggerIcon: {
20147
20538
  type: Boolean,
@@ -20173,7 +20564,7 @@ class AuroCombobox extends AuroElement {
20173
20564
  },
20174
20565
 
20175
20566
  /**
20176
- * Value selected for the dropdown menu.
20567
+ * Value selected for the dropdown menu. When set programmatically or as a preset attribute, the value must match a selectable option. If it matches an option marked `disabled` or `static`, the selection is rejected: `value` and `optionSelected` are cleared to `undefined`. `hidden` options (including those filtered out by type-ahead) remain selectable by value.
20177
20568
  */
20178
20569
  value: {
20179
20570
  type: String
@@ -20213,17 +20604,6 @@ class AuroCombobox extends AuroElement {
20213
20604
  attribute: false
20214
20605
  },
20215
20606
 
20216
- /**
20217
- * Deferred aria-expanded state for the trigger input.
20218
- * Delays the "true" transition so VoiceOver finishes its character echo
20219
- * before announcing "expanded".
20220
- * @private
20221
- */
20222
- triggerExpandedState: {
20223
- type: Boolean,
20224
- reflect: false,
20225
- attribute: false
20226
- },
20227
20607
  };
20228
20608
  }
20229
20609
 
@@ -20246,13 +20626,6 @@ class AuroCombobox extends AuroElement {
20246
20626
  return this.input.value;
20247
20627
  }
20248
20628
 
20249
- // /**
20250
- // * Sets the value of the input element within the combobox.
20251
- // */
20252
- // set inputValue(value) {
20253
- // this.input.value = value;
20254
- // }
20255
-
20256
20629
  /**
20257
20630
  * Checks if the element is valid.
20258
20631
  * @returns {boolean} - Returns true if the element is valid, false otherwise.
@@ -20338,18 +20711,15 @@ class AuroCombobox extends AuroElement {
20338
20711
  matchString = `${matchString} ${option.getAttribute('suggest')}`.toLowerCase();
20339
20712
  }
20340
20713
 
20341
- // If input is empty, show all options (except static ones)
20342
- if (!filterValue) {
20343
- if (!option.hasAttribute('static')) {
20344
- option.removeAttribute('hidden');
20345
- this.availableOptions.push(option);
20346
- }
20347
- } else if (matchString.includes(filterValue) && !option.hasAttribute('static')) {
20348
- // only count options that match the typed input value AND are not static
20714
+ if (!filterValue && option.hasAttribute('static')) {
20715
+ // Static options are shown when the input is empty so consumers can
20716
+ // surface headers / "add new" rows when no filter is active.
20717
+ option.removeAttribute('hidden');
20718
+ this.availableOptions.push(option);
20719
+ } else if (filterValue && matchString.includes(filterValue) && !option.hasAttribute('static')) {
20349
20720
  option.removeAttribute('hidden');
20350
20721
  this.availableOptions.push(option);
20351
20722
  } else if (!option.hasAttribute('persistent')) {
20352
- // Hide all other non-persistent options
20353
20723
  option.setAttribute('hidden', '');
20354
20724
  option.removeAttribute('aria-setsize');
20355
20725
  option.removeAttribute('aria-posinset');
@@ -20357,7 +20727,9 @@ class AuroCombobox extends AuroElement {
20357
20727
  });
20358
20728
 
20359
20729
  if (this.availableOptions.length === 0) {
20360
- if (this.noMatchOption) {
20730
+ // Only surface the nomatch option once the user has actually typed
20731
+ // something. Empty input is handled by the bib being closed.
20732
+ if (this.noMatchOption && filterValue) {
20361
20733
  this.noMatchOption.removeAttribute('hidden');
20362
20734
  } else if (!this.menu.loading || this.isHiddenWhileLoading) {
20363
20735
  this.hideBib();
@@ -20373,11 +20745,14 @@ class AuroCombobox extends AuroElement {
20373
20745
  * @returns {void}
20374
20746
  */
20375
20747
  syncValuesAndStates() {
20376
- // Only sync matchWord, don't set menu.value here since setMenuValue should handle that
20748
+ // Do NOT call setMenuValue here the caller (handleMenuOptions) already
20749
+ // guards setMenuValue with an option-match check. Calling it unconditionally
20750
+ // sets free-form values on the menu, triggering clearSelection which resets
20751
+ // _index and breaks subsequent makeSelection calls.
20377
20752
  if (this.menu) {
20378
20753
  this.menu.matchWord = normalizeFilterValue(this.input.value);
20379
20754
  }
20380
- const label = this.menu.currentLabel;
20755
+ const label = getOptionLabel(this.menu.optionSelected);
20381
20756
  this.updateTriggerTextDisplay(label || this.value);
20382
20757
  }
20383
20758
 
@@ -20391,12 +20766,16 @@ class AuroCombobox extends AuroElement {
20391
20766
  // in suggestion mode, do not override input value if no selection has been made and the input currently has focus
20392
20767
  const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
20393
20768
 
20394
- if (!this.persistInput && !(this.behavior === 'suggestion' && isInputFocusedWithNoSelection)) {
20395
- this.input.value = label || this.value;
20769
+ const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
20770
+ if (!suppressed) {
20771
+ this.syncInputValuesAcrossTriggerAndBib(label || this.value);
20396
20772
  }
20397
20773
 
20398
- // update the displayValue in the trigger if displayValue slot content is present
20399
- const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]');
20774
+ // Replace any previously appended displayValue clone in the trigger.
20775
+ // :not(slot) excludes the template's <slot name="displayValue"
20776
+ // slot="displayValue"> forwarder, which also has slot="displayValue"
20777
+ // and would otherwise be matched first and removed.
20778
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
20400
20779
 
20401
20780
  if (displayValueInTrigger) {
20402
20781
  displayValueInTrigger.remove();
@@ -20407,19 +20786,111 @@ class AuroCombobox extends AuroElement {
20407
20786
  if (displayValueEl) {
20408
20787
  this.input.appendChild(displayValueEl.cloneNode(true));
20409
20788
  }
20789
+
20790
+ // auro-input's hasDisplayValueContent is non-reactive; nudge it to
20791
+ // re-evaluate the slot and re-render so the displayValue wrapper
20792
+ // gets its hasContent class. Without this, the apple/icon stays
20793
+ // hidden when input.value is set in the same tick as the append.
20794
+ if (typeof this.input.checkDisplayValueSlotChange === 'function') {
20795
+ this.input.checkDisplayValueSlotChange();
20796
+ }
20797
+ } else if (this.value) {
20798
+ // No optionSelected yet (e.g. setMenuValue was called but the menu's
20799
+ // auroMenu-selectedOption event hasn't fired yet, or we're on remount
20800
+ // with no options loaded). Synthesize a displayValue from the value so
20801
+ // the overlay isn't blank.
20802
+ //
20803
+ // NOTE: This renders the raw machine value (e.g. "SEA"), not the
20804
+ // human-readable label (e.g. "Seattle, WA"). For consumers where
20805
+ // value ≠ label, this produces a brief flash of the raw value until
20806
+ // the option elements load and updateTriggerTextDisplay re-runs with
20807
+ // the correct label. This is intentionally preferred over a blank
20808
+ // state — the displayValueInTrigger.remove() cleanup above ensures
20809
+ // this placeholder is replaced once the real option is available.
20810
+ const syntheticDV = document.createElement('span');
20811
+ syntheticDV.setAttribute('slot', 'displayValue');
20812
+ syntheticDV.textContent = this.value;
20813
+ this.input.appendChild(syntheticDV);
20814
+ if (typeof this.input.checkDisplayValueSlotChange === 'function') {
20815
+ this.input.checkDisplayValueSlotChange();
20816
+ }
20410
20817
  }
20411
20818
 
20412
20819
  this.requestUpdate();
20413
20820
  }
20414
20821
 
20822
+ /**
20823
+ * Writes nextValue to the trigger input and the bib input when their current
20824
+ * value differs, then re-asserts imask after Lit's update flushes.
20825
+ * @param {string} nextValue - The value to write to both inputs.
20826
+ * @returns {Promise<void>} Resolves after both inputs flush and imask
20827
+ * re-asserts; resolves immediately when no sync is needed.
20828
+ * @private
20829
+ */
20830
+ async syncInputValuesAcrossTriggerAndBib(nextValue) {
20831
+ // Only set the flag when there's an actual write to suppress —
20832
+ // syncValuesAndStates re-enters here during typing when both inputs
20833
+ // already match, and a no-op flag flip would make the bib branch's
20834
+ // bail eat legitimate user-input events.
20835
+ const triggerNeedsSync = this.input.value !== nextValue;
20836
+ const bibNeedsSync = Boolean(this.inputInBib) && this.inputInBib.value !== nextValue;
20837
+ if (!triggerNeedsSync && !bibNeedsSync) {
20838
+ return;
20839
+ }
20840
+
20841
+ this._syncingDisplayValue = true;
20842
+
20843
+ const pending = [];
20844
+ if (triggerNeedsSync) {
20845
+ this.input.value = nextValue;
20846
+ pending.push(this.input.updateComplete);
20847
+ }
20848
+ if (bibNeedsSync) {
20849
+ this.inputInBib.value = nextValue;
20850
+ pending.push(this.inputInBib.updateComplete);
20851
+ }
20852
+ // finally — not a bare .then — so that an imask throw (see commit
20853
+ // d1857401c: imask can throw on credit-card format change) doesn't strand
20854
+ // _syncingDisplayValue=true and silently swallow every subsequent input.
20855
+ try {
20856
+ await Promise.all(pending);
20857
+ // imask reasserts otherwise, and handleBlur reverts to its stale value.
20858
+ if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
20859
+ this.input.maskInstance.updateValue();
20860
+ }
20861
+ if (bibNeedsSync && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20862
+ this.inputInBib.maskInstance.updateValue();
20863
+ }
20864
+ } finally {
20865
+ this._syncingDisplayValue = false;
20866
+ }
20867
+ }
20868
+
20415
20869
  /**
20416
20870
  * Processes hidden state of all menu options and determines if there are any available options not hidden.
20417
20871
  * @private
20872
+ * @param {object} [options={}] - Optional flag bag.
20873
+ * @param {boolean} [options.preferComboboxValue=false] - When true,
20874
+ * handleMenuOptions matches the selected option against `this.value`
20875
+ * first instead of `this.input.value`. Needed on mount and re-mount
20876
+ * because under `persistInput` the consumer's typedValue prop can drift
20877
+ * from the framework value (Svelte `{#key}` re-mount after a swap, or
20878
+ * SPA preselect after route change) and the old input-first match would
20879
+ * then pick the stale text. Only handleSlotChange passes this; typing
20880
+ * and clearing paths keep the input-first match so user clears aren't
20881
+ * undone.
20418
20882
  * @returns {void}
20419
20883
  */
20420
- handleMenuOptions() {
20884
+ handleMenuOptions({ preferComboboxValue = false } = {}) {
20421
20885
  this.generateOptionsArray();
20422
20886
  this.availableOptions = [];
20887
+ // Single source of truth for the menu's filter/highlight token per call.
20888
+ // syncValuesAndStates re-writes the same value on exact-match keystrokes —
20889
+ // Lit's hasChanged makes that a no-op — and the prior duplicate set inside
20890
+ // handleTriggerInputValueChange is gone.
20891
+ if (this.menu) {
20892
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
20893
+ }
20423
20894
  this.updateFilter();
20424
20895
 
20425
20896
  // Set aria-setsize/aria-posinset on each visible option so screen readers
@@ -20431,15 +20902,28 @@ class AuroCombobox extends AuroElement {
20431
20902
  option.setAttribute('aria-posinset', index + 1);
20432
20903
  });
20433
20904
 
20434
- if (this.value && this.input.value && !this.menu.value) {
20435
- if (this.behavior === 'suggestion' && this.menu.options && this.menu.options.some((opt) => opt.value === this.value)) {
20436
- this.setMenuValue(this.value);
20437
- }
20438
-
20905
+ // On mount/re-mount (via handleSlotChange), prefer the framework-set
20906
+ // `this.value` when it matches a menu option. Covers SPA preselect and
20907
+ // consumer swap patterns (e.g. Svelte `{#key}` re-mount) where
20908
+ // `this.value` is authoritative but `this.input.value` may be empty or
20909
+ // carry stale typed text from another field. Event-driven callers do
20910
+ // not pass the flag, otherwise user clears would be undone by
20911
+ // re-syncing to the previous combobox.value.
20912
+ if (preferComboboxValue &&
20913
+ this.value &&
20914
+ this.menu.options?.some((opt) => opt.value === this.value)) {
20915
+ this.setMenuValue(this.value);
20916
+ this.syncValuesAndStates();
20917
+ } else if (this.input.value &&
20918
+ this.menu.options?.some((opt) => opt.value === this.input.value)) {
20919
+ this.setMenuValue(this.input.value);
20439
20920
  this.syncValuesAndStates();
20440
20921
  }
20441
20922
 
20442
- if (!this.availableOptions.includes(this.menu.optionActive)) {
20923
+ // Re-activate when optionActive is not in the current scrollable viewport,
20924
+ // or when _index has been reset (e.g. by clearSelection in an async update)
20925
+ // so that makeSelection() can find the correct option.
20926
+ if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
20443
20927
  this.activateFirstEnabledAvailableOption();
20444
20928
  }
20445
20929
  }
@@ -20501,6 +20985,10 @@ class AuroCombobox extends AuroElement {
20501
20985
  // from combobox's light DOM and won't be detected by dropdown's contains() check.
20502
20986
  this.dropdown.noHideOnThisFocusLoss = true;
20503
20987
 
20988
+ // Suppress the dropdown's generic focus restoration on close — the combobox
20989
+ // manages its own focus via setClearBtnFocus / setInputFocus / keyboard strategy.
20990
+ this.dropdown.noFocusRestoreOnClose = true;
20991
+
20504
20992
  // Listen for the ID to be added to the dropdown so we can capture it and use it for accessibility.
20505
20993
  this.dropdown.addEventListener('auroDropdown-idAdded', (event) => {
20506
20994
  this.dropdownId = event.detail.id;
@@ -20511,18 +20999,6 @@ class AuroCombobox extends AuroElement {
20511
20999
  this.dropdownOpen = ev.detail.expanded;
20512
21000
  this.updateMenuShapeSize();
20513
21001
 
20514
- // Defer aria-expanded "true" so VoiceOver finishes character echo
20515
- // before announcing "expanded". Set "false" immediately on close.
20516
- clearTimeout(this._expandedTimeout);
20517
- if (this.dropdownOpen) {
20518
- const expandedDelay = 150;
20519
- this._expandedTimeout = setTimeout(() => {
20520
- this.triggerExpandedState = true;
20521
- }, expandedDelay);
20522
- } else {
20523
- this.triggerExpandedState = false;
20524
- }
20525
-
20526
21002
  // Clear aria-activedescendant when dropdown closes
20527
21003
  if (!this.dropdownOpen && this.input) {
20528
21004
  this.input.setActiveDescendant(null);
@@ -20535,6 +21011,14 @@ class AuroCombobox extends AuroElement {
20535
21011
  // keyboard navigation continues from the correct place in the page
20536
21012
  if (this.dropdown.isBibFullscreen) {
20537
21013
  restoreTriggerAfterClose(this.dropdown, this.input);
21014
+
21015
+ // After the rAF inside restoreTriggerAfterClose() refocuses the
21016
+ // trigger, park the caret at end-of-text. Without this, the trigger
21017
+ // shows the new value (e.g. "Peaches") but its caret sits at [0, 0]
21018
+ // because nothing wrote to the native input between close and focus.
21019
+ doubleRaf(() => {
21020
+ this.setInputFocus();
21021
+ });
20538
21022
  }
20539
21023
  }
20540
21024
 
@@ -20564,13 +21048,29 @@ class AuroCombobox extends AuroElement {
20564
21048
 
20565
21049
  guardTouchPassthrough(this.menu);
20566
21050
 
20567
- // Wait for the bibtemplate to fully render across
20568
- // multiple Lit update cycles before moving focus into the bib
20569
- doubleRaf(() => {
21051
+ // showModal() takes focus away from the trigger. Early focus once
21052
+ // the dialog has painted; the shared doubleRaf below is the fallback
21053
+ // for when the dialog needs an extra frame and also clears the
21054
+ // validation guard.
21055
+ requestAnimationFrame(() => {
20570
21056
  this.setInputFocus();
20571
- this._inFullscreenTransition = false;
20572
21057
  });
20573
21058
  }
21059
+ // else (desktop popover-open): Chrome resets the trigger caret to
21060
+ // [0, 0] when its floating <label for="…"> overlay receives focus.
21061
+ // The shared doubleRaf below parks the caret back at end-of-text
21062
+ // after the dropdown layout settles.
21063
+
21064
+ doubleRaf(() => {
21065
+ // Guard: skip if the dropdown closed before this rAF fired
21066
+ // (e.g. user selected an option immediately after the bib opened).
21067
+ if (this.dropdownOpen) {
21068
+ this.setInputFocus();
21069
+ }
21070
+ if (this._inFullscreenTransition) {
21071
+ this._inFullscreenTransition = false;
21072
+ }
21073
+ });
20574
21074
  }
20575
21075
  });
20576
21076
 
@@ -20588,7 +21088,12 @@ class AuroCombobox extends AuroElement {
20588
21088
  this.dropdown.bibDialogLabel = labelElement.textContent.trim() || undefined;
20589
21089
  }
20590
21090
 
20591
- // Exposes the CSS parts from the bibtemplate for styling
21091
+ if (this.customBibWidth) {
21092
+ this.dropdown.dropdownWidth = this.customBibWidth;
21093
+ }
21094
+
21095
+ // Exposes the CSS parts from the dropdown and bibtemplate for styling
21096
+ this.dropdown.exposeCssParts();
20592
21097
  this.bibtemplate.exposeCssParts();
20593
21098
 
20594
21099
  this.hideBib = this.hideBib.bind(this);
@@ -20607,8 +21112,10 @@ class AuroCombobox extends AuroElement {
20607
21112
  this.dropdown.trigger.inert = false;
20608
21113
  }
20609
21114
 
20610
- setTimeout(() => {
20611
- this.setInputFocus();
21115
+ this._scheduleTimer(() => {
21116
+ if (this.dropdown.isPopoverVisible) {
21117
+ this.setInputFocus();
21118
+ }
20612
21119
  }, 0);
20613
21120
  });
20614
21121
  }
@@ -20617,14 +21124,19 @@ class AuroCombobox extends AuroElement {
20617
21124
  * @private
20618
21125
  */
20619
21126
  setClearBtnFocus() {
20620
- const clearBtn = this.input.shadowRoot.querySelector('.clearBtn');
20621
- if (clearBtn) {
20622
- // Wait for the element to fully render across
20623
- // multiple Lit update cycles before moving focus
20624
- doubleRaf(() => {
21127
+ doubleRaf(() => {
21128
+ // First establish focus inside the input's shadow root.
21129
+ // Without this, clearBtn.focus() silently fails when focus is on
21130
+ // document.body (e.g. after a click selection closes the bib dialog).
21131
+ // delegatesFocus on BaseInput routes this to the native <input>.
21132
+ this.input.focus();
21133
+
21134
+ // Now move focus to the clear button within the same shadow root.
21135
+ const clearBtn = this.input.shadowRoot.querySelector('.clearBtn');
21136
+ if (clearBtn) {
20625
21137
  clearBtn.focus();
20626
- });
20627
- }
21138
+ }
21139
+ });
20628
21140
  }
20629
21141
 
20630
21142
  /**
@@ -20646,10 +21158,30 @@ class AuroCombobox extends AuroElement {
20646
21158
  */
20647
21159
  setInputFocus() {
20648
21160
  if (this.dropdown.isBibFullscreen && this.dropdown.isPopoverVisible) {
20649
- this.inputInBib.focus();
21161
+ // Sync the native input value synchronously before focusing.
21162
+ // Lit's property-to-attribute sync is async (microtask), so the
21163
+ // native <input> inside inputInBib may still hold a stale value
21164
+ // when focus moves here during the fullscreen transition. Without
21165
+ // this, keystrokes (e.g. Backspace) operate on the wrong content.
21166
+ const nativeInput = this.inputInBib.inputElement;
21167
+ const triggerNativeInput = this.input.inputElement;
21168
+ if (nativeInput && triggerNativeInput && nativeInput.value !== triggerNativeInput.value) {
21169
+ this.inputInBib.skipNextProgrammaticInputEvent = true;
21170
+ nativeInput.value = triggerNativeInput.value || '';
21171
+ }
21172
+
21173
+ // Focus the native input directly to ensure it receives keystrokes
21174
+ // after the fullscreen dialog opens. The dialog's showModal() may
21175
+ // have moved focus to the dialog element itself; focusing the
21176
+ // auro-input custom element doesn't always reach the native input
21177
+ // when the dialog DOM isn't fully painted.
21178
+ if (nativeInput) {
21179
+ nativeInput.focus();
21180
+ } else {
21181
+ this.inputInBib.focus();
21182
+ }
20650
21183
 
20651
21184
  // Place cursor at end of existing text so the user can continue editing
20652
- const nativeInput = this.inputInBib.inputElement;
20653
21185
  if (nativeInput && nativeInput.value) {
20654
21186
  const len = nativeInput.value.length;
20655
21187
  nativeInput.setSelectionRange(len, len);
@@ -20731,7 +21263,7 @@ class AuroCombobox extends AuroElement {
20731
21263
 
20732
21264
  // racing condition on custom-combobox with custom-menu
20733
21265
  if (!this.menu) {
20734
- setTimeout(() => {
21266
+ this._scheduleTimer(() => {
20735
21267
  this.configureMenu();
20736
21268
  }, 0);
20737
21269
  return;
@@ -20751,46 +21283,73 @@ class AuroCombobox extends AuroElement {
20751
21283
  this.menu.setAttribute('nocheckmark', '');
20752
21284
  }
20753
21285
 
20754
- this.menu.addEventListener("auroMenu-deselectPrevented", () => {
20755
- this.hideBib();
20756
- });
21286
+ // Handle menu option selection
21287
+ this.menu.addEventListener('auroMenu-selectedOption', (evt) => {
21288
+ // Capture before the consumption below — true means this event is the
21289
+ // echo of our own setMenuValue (programmatic value sync), false means
21290
+ // a fresh user selection.
21291
+ const isEcho = this._pendingMenuValueSync === true;
21292
+
21293
+ if (this.menu.optionSelected) {
21294
+ const selected = this.menu.optionSelected;
20757
21295
 
20758
- // Handle menu option selection like select does
20759
- this.menu.addEventListener('auroMenu-selectedOption', (event) => {
20760
- // Update the optionSelected from the event details, not manually
20761
- [this.optionSelected] = event.detail.options;
21296
+ if (this.optionSelected !== selected) {
21297
+ this.optionSelected = selected;
21298
+ }
20762
21299
 
20763
- // Update the internal value to match the menu's value
20764
- this.value = event.detail.stringValue;
21300
+ // Skip writeback when this event is the echo of our own setMenuValue —
21301
+ // otherwise it cascades against handleInputValueChange in suggestion mode.
21302
+ if (this._pendingMenuValueSync) {
21303
+ this._pendingMenuValueSync = false;
21304
+ } else if (!this.value || this.value !== this.optionSelected.value) {
21305
+ this.value = this.optionSelected.value;
21306
+ }
20765
21307
 
20766
- // Update display
20767
- this.updateTriggerTextDisplay(event.detail.label || event.detail.value);
21308
+ // Update display
21309
+ this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
20768
21310
 
20769
- // Update match word for filtering
20770
- const trimmedInput = normalizeFilterValue(this.input.value);
20771
- if (this.menu.matchWord !== trimmedInput) {
20772
- this.menu.matchWord = trimmedInput;
21311
+ // Update match word for filtering
21312
+ const trimmedInput = normalizeFilterValue(this.input.value);
21313
+ if (this.menu.matchWord !== trimmedInput) {
21314
+ this.menu.matchWord = trimmedInput;
21315
+ }
20773
21316
  }
20774
21317
 
20775
- // Update available options based on selection
20776
- this.handleMenuOptions();
20777
-
20778
21318
  // Hide dropdown on selection (except during slot changes)
20779
- if (event.detail && event.detail.source !== 'slotchange') {
20780
- setTimeout(() => {
20781
- // do not close while typing in suggestion mode with no value selected, to allow freeform input
20782
- if (this.menu.value || this.behavior !== 'suggestion') {
20783
- this.hideBib();
20784
- }
20785
- }, 0);
21319
+ if (evt.detail && evt.detail.source !== 'slotchange') {
21320
+ // do not close while typing in suggestion mode with no value selected, to allow freeform input
21321
+ this.hideBib();
21322
+
21323
+ // Move focus to the clear button when the user makes a selection,
21324
+ // unless the Shift+Tab handler opted this selection out.
21325
+ if (this._suppressClearBtnFocusOnSelection) {
21326
+ this._suppressClearBtnFocusOnSelection = false;
21327
+ } else if (!isEcho && this.menu.value !== undefined) {
21328
+ this.setClearBtnFocus();
21329
+ }
20786
21330
 
20787
21331
  // Announce the selection after the dropdown closes so it isn't
20788
21332
  // overridden by VoiceOver's "collapsed" announcement from aria-expanded.
20789
- const selectedValue = event.detail.stringValue;
20790
- const announcementDelay = 300;
20791
- setTimeout(() => {
20792
- announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
20793
- }, announcementDelay);
21333
+ // Skip when there's no selected value (e.g. menu.clearSelection() from
21334
+ // the unmatched-value path), otherwise VoiceOver reads "undefined".
21335
+ const selectedValue = this.menu.value;
21336
+ if (selectedValue) {
21337
+ const announcementDelay = 300;
21338
+ this._scheduleTimer(() => {
21339
+ announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
21340
+ }, announcementDelay);
21341
+ }
21342
+ }
21343
+
21344
+ // Programmatic value syncs leave availableOptions stale because
21345
+ // updateTriggerTextDisplay sets input.value with _syncingDisplayValue
21346
+ // and handleInputValueChange bails. Refresh the filter on echo events
21347
+ // only — fresh user selections take the existing hideBib path.
21348
+ if (isEcho && this.menu.optionSelected) {
21349
+ this._programmaticFilterRefresh = true;
21350
+ this.updateComplete.then(() => {
21351
+ this._programmaticFilterRefresh = false;
21352
+ });
20794
21353
  }
20795
21354
  });
20796
21355
 
@@ -20798,17 +21357,55 @@ class AuroCombobox extends AuroElement {
20798
21357
  this.hideBib();
20799
21358
  });
20800
21359
 
21360
+ // When the menu cannot match a programmatic value to any option,
21361
+ // clear the combobox selection state so it doesn't reference a
21362
+ // stale option. Safe from re-entrancy because any resulting
21363
+ // input.value changes dispatch isProgrammatic events.
21364
+ this.menu.addEventListener('auroMenu-selectValueFailure', () => {
21365
+ // Announce the rejection BEFORE we clear `this.value` so the live
21366
+ // region carries the attempted value — without this the bib closes
21367
+ // silently and screen-reader users get no signal that their request
21368
+ // (e.g. a direct setMenuValue() call) was dropped.
21369
+ const attemptedValue = this.value;
21370
+ if (attemptedValue) {
21371
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${attemptedValue}`);
21372
+ }
21373
+ this.value = undefined;
21374
+ this.optionSelected = undefined;
21375
+ });
21376
+
20801
21377
  this.menu.addEventListener('auroMenu-activatedOption', (evt) => {
20802
21378
  this.optionActive = evt.detail;
20803
21379
 
20804
21380
  if (this.input) {
20805
- this.input.setActiveDescendant(this.optionActive);
21381
+ // setActiveDescendant runs after Lit's update finishes because the
21382
+ // template's `.a11yActivedescendant` binding calls setAttribute on
21383
+ // the internal <input> in auro-input.updated(), which clears the
21384
+ // reflected ariaActiveDescendantElement property. Setting the ref
21385
+ // synchronously here would get overwritten by that attribute write.
21386
+ // Awaiting updateComplete lets the attribute write finish first,
21387
+ // then the ref sticks and screen readers can cross the shadow-DOM
21388
+ // boundary to the auro-menuoption in light DOM.
21389
+ //
21390
+ // The equality check covers the async gap: this.optionActive can
21391
+ // change before the callback runs (user arrowed elsewhere, or the
21392
+ // menu closed and cleared it to null), so we bail if the target
21393
+ // no longer matches to avoid reinstating a stale ref.
21394
+ const targetOption = this.optionActive;
21395
+ this.updateComplete.then(() => {
21396
+ if (this.input && this.optionActive === targetOption) {
21397
+ this.input.setActiveDescendant(targetOption);
21398
+ }
21399
+ });
20806
21400
  }
20807
21401
 
20808
- // Announce the active option for screen readers including position,
20809
- // since shadow DOM boundaries prevent native reading of
20810
- // aria-setsize/aria-posinset via aria-activedescendant.
20811
- if (this.optionActive) {
21402
+ // In fullscreen mode the menu sits inside a nested <dialog> shadow root,
21403
+ // and aria-activedescendant references across that boundary are lost —
21404
+ // VoiceOver/NVDA don't read the active option natively, so we mirror it
21405
+ // into the polite live region. In popover mode aria-activedescendant on
21406
+ // the trigger input is read natively; double-announcing would flood the
21407
+ // queue on arrow-key repeat.
21408
+ if (this.optionActive && this.dropdown.isBibFullscreen) {
20812
21409
  const optionText = this.optionActive.textContent.trim();
20813
21410
  const selectedState = this.optionActive.hasAttribute('selected') ? ', selected' : ', not selected';
20814
21411
  const optionIndex = this.availableOptions.indexOf(this.optionActive) + 1;
@@ -20841,7 +21438,12 @@ class AuroCombobox extends AuroElement {
20841
21438
  * Validate every time we remove focus from the combo box.
20842
21439
  */
20843
21440
  this.addEventListener('focusout', () => {
20844
- if (!this.componentHasFocus && !this._inFullscreenTransition) {
21441
+ // Skip while the dropdown is open — focus transits out briefly on
21442
+ // mousedown of a menu option (popover top-layer breaks :focus-within),
21443
+ // and validating against the pre-selection value flashes a stale error
21444
+ // between mousedown and mouseup. The next focusout fires after the
21445
+ // dropdown closes and validates against the post-selection value.
21446
+ if (!this.componentHasFocus && !this._inFullscreenTransition && !this.dropdownOpen) {
20845
21447
  this.validate();
20846
21448
  }
20847
21449
  });
@@ -20898,43 +21500,65 @@ class AuroCombobox extends AuroElement {
20898
21500
  // event from the trigger. The _syncingBibValue guard persists across the
20899
21501
  // async boundary and prevents that re-entrant event from running the
20900
21502
  // non-fullscreen path (which would call clear() → hideBib()).
20901
- // When the event comes from the fullscreen bib input, sync the value to
20902
- // the trigger and run filtering, but suppress the re-entrant input event
20903
- // that the trigger fires (via Lit updated() → notifyValueChanged()) so
20904
- // the non-fullscreen hide/clear logic doesn't close the dialog.
20905
21503
  if (event.target === this.inputInBib) {
20906
- this._syncingBibValue = true;
20907
- this.input.value = this.inputInBib.value;
20908
- this.input.updateComplete.then(() => {
20909
- this._syncingBibValue = false;
20910
- });
21504
+ // Mirror the trigger-branch bail below — programmatic inputInBib writes
21505
+ // notify back into this branch via Lit's notifyValueChanged.
21506
+ if (this._syncingDisplayValue) {
21507
+ return;
21508
+ }
21509
+
21510
+ // Filtering runs via re-entrance: writing this.input.value below
21511
+ // dispatches an 'input' event that the trigger's listener routes to
21512
+ // handleTriggerInputValueChange, which refreshes the menu filter.
21513
+ if (this.input.value !== this.inputInBib.value) {
21514
+ this._syncingBibValue = true;
21515
+ this.input.value = this.inputInBib.value;
21516
+ this.input.updateComplete.then(() => {
21517
+ this._syncingBibValue = false;
21518
+ });
21519
+ }
20911
21520
 
20912
- // Run filtering inline — the re-entrant event won't reach this code.
20913
- this.menu.matchWord = normalizeFilterValue(this.inputInBib.value);
20914
- this.optionActive = null;
20915
- this.handleMenuOptions();
20916
21521
  this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
20917
- return;
21522
+ } else if (event.target === this.input) {
21523
+
21524
+ // Also sync the native input immediately so keystrokes arriving
21525
+ // before Lit's async update cycle (e.g. rapid Backspaces during a
21526
+ // fullscreen transition) operate on the correct content.
21527
+ // Skip the next programmatic input event to prevent the patched setter
21528
+ // from re-entering handleInputValueChange via the bib path.
21529
+ const bibNativeInput = this.inputInBib.inputElement;
21530
+ if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21531
+ this.inputInBib.skipNextProgrammaticInputEvent = true;
21532
+ bibNativeInput.value = this.input.value || '';
21533
+ }
20918
21534
  }
20919
21535
 
20920
- // Ignore re-entrant input events caused by the bib→trigger sync above.
20921
- if (this._syncingBibValue) {
21536
+ // SPA preselect guard: on init render, auro-input's notifyValueChanged
21537
+ // echoes an empty value through as an isProgrammatic input event before
21538
+ // the framework-set combobox.value has propagated down to the input.
21539
+ // Without this bail, the `this.value = this.input.value` below clobbers
21540
+ // the framework value with '' and clear() wipes optionSelected. Scoped
21541
+ // to the exact shape of that echo (isProgrammatic + non-empty combobox
21542
+ // value + empty input.value) so it doesn't affect the swap/typed paths
21543
+ // where value/input already track each other through the sync helpers.
21544
+ if (event && event.isProgrammatic && this.value && !this.input.value) {
20922
21545
  return;
20923
21546
  }
20924
21547
 
20925
- this.inputInBib.value = this.input.value;
21548
+ this.value = this.input.value;
20926
21549
 
20927
- this.menu.matchWord = normalizeFilterValue(this.input.value);
20928
- this.optionActive = null;
21550
+ // Ignore re-entrant input events caused by programmatic value sets.
21551
+ if (this._syncingBibValue || this._syncingDisplayValue) {
21552
+ return;
21553
+ }
20929
21554
 
20930
21555
  if (this.behavior === 'suggestion') {
20931
21556
  this.value = this.input.value;
20932
21557
  }
20933
21558
 
20934
- if (!this.input.value) {
21559
+ if (!this.input.value && !this._clearing) {
20935
21560
  this.clear();
20936
21561
  }
20937
- this.handleMenuOptions();
20938
21562
 
20939
21563
  // Validate only if the value was set programmatically (not during user
20940
21564
  // interaction). In fullscreen dialog mode, componentHasFocus returns false
@@ -20944,34 +21568,40 @@ class AuroCombobox extends AuroElement {
20944
21568
  this.validate();
20945
21569
  }
20946
21570
 
20947
- if (this.input.value && this.input.value.length === 0) {
20948
- // Hide menu if value is empty, otherwise show if there are available suggestions
20949
- this.hideBib();
20950
- } else if (this.menu.loading) {
20951
- // if input has value but menu is loading, show bib immediately
20952
- this.showBib();
20953
- } else if (this.availableOptions.length === 0 && !this.dropdown.isBibFullscreen) {
20954
- // Force dropdown bib to hide if input value has no matching suggestions
20955
- this.hideBib();
21571
+ this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21572
+ }
21573
+
21574
+ /**
21575
+ * Handles input value changes originating from the trigger input.
21576
+ * Refreshes menu options and filtering, delegates to handleInputValueChange
21577
+ * for value synchronization, and manages fullscreen bib focus.
21578
+ * @private
21579
+ * @param {Event} event - The input event from the trigger input element.
21580
+ * @returns {void}
21581
+ */
21582
+ handleTriggerInputValueChange(event) {
21583
+ // 'input' fires for every user-initiated value change — typing, paste,
21584
+ // IME composition end, dead-key composition, drag-drop. Flip _userTyped
21585
+ // here so updated('availableOptions') auto-opens the bib for sources
21586
+ // that keydown alone misses: paste fires no keydown, IME uses
21587
+ // key='Process', and dead keys produce multi-char keys (all bypass the
21588
+ // prior keydown.key.length===1 gate). Skip programmatic syncs.
21589
+ if (!this._syncingDisplayValue && !this._syncingBibValue && !this._programmaticFilterRefresh) {
21590
+ this._userTyped = true;
20956
21591
  }
20957
21592
 
20958
- // iOS virtual keyboard retention: when in fullscreen mode, ensure the
20959
- // dialog opens and the bib input is focused synchronously within the
20960
- // input event (user gesture) chain. Without this, Lit's async update
20961
- // cycle delays showModal() past the user activation window, causing
20962
- // iOS Safari to dismiss the virtual keyboard when the fullscreen
20963
- // dialog opens — the user then has to tap the input again to resume
20964
- // typing.
20965
- if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
20966
- if (!this.dropdown.isPopoverVisible) {
20967
- this.showBib();
20968
- }
20969
- if (this.dropdown.isPopoverVisible) {
20970
- this.setInputFocus();
20971
- }
21593
+ this.handleMenuOptions();
21594
+ this.optionActive = null;
21595
+
21596
+ if (this.value === this.input.value) {
21597
+ return;
20972
21598
  }
20973
21599
 
20974
- this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21600
+ this.handleInputValueChange(event);
21601
+
21602
+ if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
21603
+ this.setInputFocus();
21604
+ }
20975
21605
  }
20976
21606
 
20977
21607
  /**
@@ -20985,10 +21615,12 @@ class AuroCombobox extends AuroElement {
20985
21615
  inputResolver: (comp, ctx) => (ctx.isModal && comp.inputInBib ? comp.inputInBib : comp.input),
20986
21616
  });
20987
21617
 
20988
- this.addEventListener('focusin', () => {
21618
+ this.addEventListener('focusin', (event) => {
20989
21619
  this.touched = true;
20990
21620
 
20991
- this.focus();
21621
+ if (event.composedPath()[0] === this) {
21622
+ this.focus();
21623
+ }
20992
21624
  });
20993
21625
 
20994
21626
  this.addEventListener('auroFormElement-validated', (evt) => {
@@ -21000,6 +21632,16 @@ class AuroCombobox extends AuroElement {
21000
21632
  return;
21001
21633
  }
21002
21634
 
21635
+ // Ignore dispatches from the bib (fullscreen) input. It's re-validated
21636
+ // inside this.validate()'s auroInputElements loop with its own
21637
+ // (often undefined) validity, and the event is composed/bubbles up to
21638
+ // this listener with `target` retargeted to the combobox. Letting it
21639
+ // through would overwrite the trigger input's correct validity with
21640
+ // the bib input's stale one (e.g. wiping `tooShort` during typing).
21641
+ if (this.inputInBib && evt.composedPath()[0] === this.inputInBib && this.inputInBib !== this.input) {
21642
+ return;
21643
+ }
21644
+
21003
21645
  this.input.validity = evt.detail.validity;
21004
21646
  this.input.errorMessage = evt.detail.message;
21005
21647
  this.validity = evt.detail.validity;
@@ -21029,6 +21671,11 @@ class AuroCombobox extends AuroElement {
21029
21671
  disconnectedCallback() {
21030
21672
  super.disconnectedCallback();
21031
21673
  this._inFullscreenTransition = false;
21674
+ // Cancel any outstanding timers so detached callbacks don't fire on
21675
+ // disposed DOM — most are no-ops, but configureMenu's racing-condition
21676
+ // retry would otherwise keep rescheduling itself indefinitely.
21677
+ this._pendingTimers.forEach((id) => clearTimeout(id));
21678
+ this._pendingTimers.clear();
21032
21679
  }
21033
21680
 
21034
21681
  firstUpdated() {
@@ -21061,10 +21708,18 @@ class AuroCombobox extends AuroElement {
21061
21708
  * @returns {void}
21062
21709
  */
21063
21710
  setMenuValue(value) {
21064
- if (!this.menu) {
21711
+ if (!this.menu || this.menu.value === value) {
21065
21712
  return;
21066
21713
  }
21714
+ // One-shot flag consumed by the auroMenu-selectedOption listener so the
21715
+ // echo of this sync doesn't propagate back to this.value.
21716
+ this._pendingMenuValueSync = true;
21067
21717
  this.menu.value = value;
21718
+ // Backup clear: if menu.value === menu.optionSelected.value already, the
21719
+ // listener won't fire and the flag would swallow the next user click.
21720
+ this._scheduleTimer(() => {
21721
+ this._pendingMenuValueSync = false;
21722
+ }, 0);
21068
21723
  }
21069
21724
 
21070
21725
  /**
@@ -21076,9 +21731,16 @@ class AuroCombobox extends AuroElement {
21076
21731
  this.value = undefined;
21077
21732
  this.typedValue = undefined;
21078
21733
  this.input.value = undefined;
21734
+ // Fullscreen bib mounts a second auro-input that holds the user-facing
21735
+ // native <input> on mobile; clearing only the trigger leaves stale text
21736
+ // there when the bib is reopened after reset.
21737
+ if (this.inputInBib) {
21738
+ this.inputInBib.value = undefined;
21739
+ }
21079
21740
  this.menu.value = undefined;
21080
21741
  this.validation.reset(this);
21081
21742
  this.touched = false;
21743
+ this.validity = undefined;
21082
21744
  }
21083
21745
 
21084
21746
  /**
@@ -21086,16 +21748,35 @@ class AuroCombobox extends AuroElement {
21086
21748
  * @returns {void}
21087
21749
  */
21088
21750
  clear() {
21089
- // Clear combobox state first
21090
- this.optionSelected = undefined;
21091
- this.value = undefined;
21092
-
21093
- // Then clear input and menu
21094
- if (this.input.value) {
21095
- this.input.clear();
21751
+ // input.clear() fires an input event that re-enters handleInputValueChange
21752
+ // → which calls clear() again on empty input. Guard against that loop.
21753
+ if (this._clearing) {
21754
+ return;
21096
21755
  }
21097
- if (this.menu.value || this.menu.optionSelected) {
21098
- this.menu.reset();
21756
+ this._clearing = true;
21757
+ try {
21758
+ this.optionSelected = undefined;
21759
+ this.value = undefined;
21760
+
21761
+ // Clear the appended displayValue clone in the trigger if present.
21762
+ // :not(slot) excludes the template's <slot name="displayValue"
21763
+ // slot="displayValue"> forwarder (line 1816), which also has
21764
+ // slot="displayValue" and would otherwise be matched first and removed,
21765
+ // permanently breaking the consumer-provided displayValue slot.
21766
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
21767
+
21768
+ if (displayValueInTrigger) {
21769
+ displayValueInTrigger.remove();
21770
+ }
21771
+
21772
+ if (this.input.value) {
21773
+ this.input.clear();
21774
+ }
21775
+ if (this.menu.value || this.menu.optionSelected) {
21776
+ this.menu.reset();
21777
+ }
21778
+ } finally {
21779
+ this._clearing = false;
21099
21780
  }
21100
21781
  }
21101
21782
 
@@ -21111,45 +21792,55 @@ class AuroCombobox extends AuroElement {
21111
21792
  }
21112
21793
 
21113
21794
  updated(changedProperties) {
21114
- // After the component is ready, send direct value changes to auro-menu.
21795
+ // After the component is ready, propagate direct changes down to child components.
21115
21796
  if (changedProperties.has('value')) {
21116
- if (this.value && this.value.length > 0) {
21117
- this.hasValue = true;
21118
- } else {
21119
- this.hasValue = false;
21120
- }
21121
-
21122
- if (this.hasValue && !this.input.value && (!this.menu.options || this.menu.options.length === 0)) {
21123
- this.input.value = this.value;
21124
- }
21125
-
21126
- if (this.menu && this.hasValue && this.menu.options) {
21127
- this.menu.options.forEach((opt) => {
21128
- if (!opt.hasAttribute('static')) {
21129
- opt.removeAttribute('hidden');
21130
- }
21797
+ // Only flag programmatic refreshes — user-typed value changes must not
21798
+ // suppress the availableOptions branch's showBib(). Firefox batches
21799
+ // 'value' and 'availableOptions' into the same updated() call, so
21800
+ // setting the flag unconditionally here masks the user-typed open path.
21801
+ if (!this._userTyped) {
21802
+ this._programmaticFilterRefresh = true;
21803
+ // Self-clear after this update cycle completes. This collapses
21804
+ // the previous scattered clear-points into one and prevents the
21805
+ // flag from surviving into the next user interaction.
21806
+ this.updateComplete.then(() => {
21807
+ this._programmaticFilterRefresh = false;
21131
21808
  });
21132
21809
  }
21133
21810
 
21134
- if (this.behavior === 'suggestion') {
21135
- if (!this.menu.options || this.menu.options.length === 0) ; else if (this.menu.options.filter((opt) => opt.value === this.value).length > 0) {
21136
- this.setMenuValue(this.value);
21137
- } else {
21138
- this.menu.value = undefined;
21139
- // Sync the input display for freeform values that don't match any option
21140
- this.input.value = this.value;
21811
+ if (this.input.value !== this.value) {
21812
+ // Clear menu.value AND menu.optionSelected together. Clearing only
21813
+ // menu.value leaves the previously-selected option element pinned
21814
+ // as menu.optionSelected; a later auroMenu-selectedOption event
21815
+ // would then write its stale .value back into combobox.value
21816
+ // (e.g. Tab-after-Backspace re-selecting the prior option).
21817
+ if (this.menu.value || this.menu.optionSelected) {
21818
+ this.menu.clearSelection();
21141
21819
  }
21142
- } else {
21143
- // Use setMenuValue like select does instead of direct assignment
21144
- this.setMenuValue(this.value);
21145
- if (!this.value) {
21146
- this.clear();
21820
+
21821
+ if (!this.persistInput) {
21822
+ this.syncInputValuesAcrossTriggerAndBib(this.value || '');
21823
+ }
21824
+
21825
+ // Programmatic value with no matching option: updateFilter will close
21826
+ // the bib silently (see line 648 — no noMatchOption + 0 results
21827
+ // hides). Announce so screen-reader users hear the request was
21828
+ // dropped. Gated on `input.value !== this.value` so this never fires
21829
+ // for user typing — that path always reconciles input.value to
21830
+ // this.value before updated() runs.
21831
+ if (
21832
+ this.value &&
21833
+ this.menu &&
21834
+ this.menu.options &&
21835
+ this.menu.options.length > 0 &&
21836
+ !this.menu.options.some((opt) => opt.value === this.value)
21837
+ ) {
21838
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${this.value}`);
21147
21839
  }
21148
21840
  }
21149
- if (this.value && !this.componentHasFocus) {
21150
- // If the value got set programmatically make sure we hide the bib
21151
- // when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
21152
- this.hideBib();
21841
+
21842
+ if (!this.value) {
21843
+ this.clear();
21153
21844
  }
21154
21845
 
21155
21846
  // Sync the input and match word, but don't directly set menu.value again
@@ -21163,7 +21854,7 @@ class AuroCombobox extends AuroElement {
21163
21854
  composed: true,
21164
21855
  detail: {
21165
21856
  optionSelected: this.menu.optionSelected,
21166
- value: this.menu.value
21857
+ value: this.value
21167
21858
  }
21168
21859
  }));
21169
21860
 
@@ -21182,12 +21873,26 @@ class AuroCombobox extends AuroElement {
21182
21873
  // branch from calling hideBib() when the dropdown was just opened but
21183
21874
  // :focus-within hasn't propagated through the top-layer dialog's nested
21184
21875
  // shadow DOM boundaries.
21185
- if ((this.availableOptions.length > 0 && (this.componentHasFocus || this.dropdownOpen)) || (this.menu && this.menu.loading) || (this.availableOptions.length === 0 && this.noMatchOption)) {
21186
- this.showBib();
21876
+ // Skip the show/hide side effects when the availableOptions change came
21877
+ // from a programmatic filter refresh after a value swap — the user
21878
+ // didn't interact, so we shouldn't auto-open the bib (especially for
21879
+ // the no-match path where the existing condition unconditionally fires).
21880
+ if ((this.menu && !this._programmaticFilterRefresh)) {
21881
+ if (
21882
+ this.availableOptions.length > 0 ||
21883
+ this.menu.loading ||
21884
+ this.noMatchOption
21885
+ ) {
21886
+ if (this._userTyped && !this.dropdownOpen) {
21887
+ this.showBib();
21888
+ }
21889
+ this._userTyped = false;
21890
+ }
21891
+
21187
21892
  if (!this.availableOptions.includes(this.menu.optionActive)) {
21188
21893
  this.activateFirstEnabledAvailableOption();
21189
21894
  }
21190
- } else {
21895
+ } else if (!this.dropdown.isBibFullscreen) {
21191
21896
  this.hideBib();
21192
21897
  }
21193
21898
  }
@@ -21278,7 +21983,11 @@ class AuroCombobox extends AuroElement {
21278
21983
  }
21279
21984
  });
21280
21985
 
21281
- this.handleMenuOptions();
21986
+ // Slot change (mount/re-mount): pass `preferComboboxValue` so that a
21987
+ // framework-set `this.value` wins over any stale `input.value` when
21988
+ // matching against the newly-available options. Fixes the display
21989
+ // for SPA preselect and Svelte `{#key}` re-mount swap patterns.
21990
+ this.handleMenuOptions({ preferComboboxValue: true });
21282
21991
 
21283
21992
  break;
21284
21993
 
@@ -21331,10 +22040,10 @@ class AuroCombobox extends AuroElement {
21331
22040
  shape="${this.shape}"
21332
22041
  size="${this.size}">
21333
22042
  <${this.inputTag}
21334
- @input="${this.handleInputValueChange}"
22043
+ @input="${this.handleTriggerInputValueChange}"
21335
22044
  appearance="${this.onDark ? 'inverse' : this.appearance}"
21336
22045
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
21337
- .a11yExpanded="${this.triggerExpandedState}"
22046
+ .a11yExpanded="${this.dropdownOpen}"
21338
22047
  .a11yControls="${this.dropdownId}"
21339
22048
  .autocomplete="${this.autocomplete}"
21340
22049
  .inputmode="${this.inputmode}"
@@ -21367,7 +22076,7 @@ class AuroCombobox extends AuroElement {
21367
22076
  <slot @slotchange="${this.handleSlotChange}"></slot>
21368
22077
  <${this.inputTag}
21369
22078
  id="inputInBib"
21370
- autofocus
22079
+ ?autofocus="${this.dropdownOpen && this.dropdown?.isBibFullscreen}"
21371
22080
  @input="${this.handleInputValueChange}"
21372
22081
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
21373
22082
  .a11yControls=${`${this.dropdownId}-floater-bib`}