@aurodesignsystem-dev/auro-formkit 0.0.0-pr1507.0 → 0.0.0-pr1508.1

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 (148) 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/customize.html +1 -2
  4. package/components/checkbox/demo/customize.min.js +8 -6
  5. package/components/checkbox/demo/getting-started.min.js +8 -6
  6. package/components/checkbox/demo/index.min.js +8 -6
  7. package/components/checkbox/dist/index.js +8 -6
  8. package/components/checkbox/dist/registered.js +8 -6
  9. package/components/combobox/demo/customize.html +1 -2
  10. package/components/combobox/demo/customize.md +105 -137
  11. package/components/combobox/demo/customize.min.js +506 -568
  12. package/components/combobox/demo/getting-started.min.js +506 -564
  13. package/components/combobox/demo/index.min.js +506 -564
  14. package/components/combobox/demo/keyboard-behavior.md +2 -142
  15. package/components/combobox/demo/why-combobox.md +1 -1
  16. package/components/combobox/dist/auro-combobox.d.ts +30 -14
  17. package/components/combobox/dist/index.js +505 -563
  18. package/components/combobox/dist/registered.js +505 -563
  19. package/components/counter/demo/customize.min.js +26 -8
  20. package/components/counter/demo/index.min.js +26 -8
  21. package/components/counter/dist/index.js +26 -8
  22. package/components/counter/dist/registered.js +26 -8
  23. package/components/datepicker/demo/accessibility.md +2 -1
  24. package/components/datepicker/demo/api.md +65 -62
  25. package/components/datepicker/demo/customize.md +150 -39
  26. package/components/datepicker/demo/customize.min.js +988 -500
  27. package/components/datepicker/demo/index.min.js +988 -482
  28. package/components/datepicker/dist/{src/auro-calendar-cell.d.ts → auro-calendar-cell.d.ts} +20 -3
  29. package/components/datepicker/dist/{src/auro-calendar.d.ts → auro-calendar.d.ts} +121 -7
  30. package/components/datepicker/dist/{src/auro-datepicker.d.ts → auro-datepicker.d.ts} +80 -5
  31. package/components/datepicker/dist/blackoutUtils.d.ts +43 -0
  32. package/components/datepicker/dist/index.js +988 -482
  33. package/components/datepicker/dist/registered.js +988 -482
  34. package/components/dropdown/demo/customize.min.js +10 -1
  35. package/components/dropdown/demo/getting-started.min.js +10 -1
  36. package/components/dropdown/demo/index.min.js +10 -1
  37. package/components/dropdown/dist/auro-dropdown.d.ts +1 -0
  38. package/components/dropdown/dist/index.js +10 -1
  39. package/components/dropdown/dist/registered.js +10 -1
  40. package/components/form/demo/customize.html +6 -6
  41. package/components/form/demo/customize.md +203 -51
  42. package/components/form/demo/customize.min.js +2357 -1419
  43. package/components/form/demo/getting-started.min.js +2285 -1419
  44. package/components/form/demo/index.min.js +2285 -1419
  45. package/components/form/demo/registerDemoDeps.min.js +1922 -1382
  46. package/components/form/dist/auro-form.d.ts +122 -4
  47. package/components/form/dist/index.js +363 -37
  48. package/components/form/dist/registered.js +363 -37
  49. package/components/input/demo/api.md +5 -5
  50. package/components/input/demo/customize.html +1 -2
  51. package/components/input/demo/customize.md +62 -61
  52. package/components/input/demo/customize.min.js +177 -241
  53. package/components/input/demo/getting-started.min.js +177 -241
  54. package/components/input/demo/index.min.js +177 -241
  55. package/components/input/dist/base-input.d.ts +34 -93
  56. package/components/input/dist/index.js +190 -242
  57. package/components/input/dist/registered.js +177 -241
  58. package/components/radio/demo/customize.min.js +8 -6
  59. package/components/radio/demo/getting-started.min.js +8 -6
  60. package/components/radio/demo/index.min.js +8 -6
  61. package/components/radio/dist/index.js +8 -6
  62. package/components/radio/dist/registered.js +8 -6
  63. package/components/select/demo/customize.html +1 -2
  64. package/components/select/demo/customize.md +86 -0
  65. package/components/select/demo/customize.min.js +202 -68
  66. package/components/select/demo/getting-started.min.js +204 -115
  67. package/components/select/demo/index.min.js +202 -68
  68. package/components/select/demo/keyboard-behavior.md +18 -4
  69. package/components/select/dist/auro-select.d.ts +20 -8
  70. package/components/select/dist/index.js +202 -68
  71. package/components/select/dist/registered.js +202 -68
  72. package/custom-elements.json +1035 -604
  73. package/package.json +6 -2
  74. package/components/checkbox/demo/customize.js +0 -22
  75. package/components/checkbox/demo/demo-support.js +0 -1
  76. package/components/checkbox/demo/getting-started.js +0 -22
  77. package/components/checkbox/demo/index.js +0 -8
  78. package/components/checkbox/demo/styles.scss +0 -1
  79. package/components/combobox/demo/customize.js +0 -24
  80. package/components/combobox/demo/demo-support.js +0 -1
  81. package/components/combobox/demo/getting-started.js +0 -21
  82. package/components/combobox/demo/index.js +0 -23
  83. package/components/combobox/demo/styles.scss +0 -1
  84. package/components/counter/demo/customize.js +0 -21
  85. package/components/counter/demo/demo-support.js +0 -1
  86. package/components/counter/demo/index.js +0 -21
  87. package/components/counter/demo/styles.scss +0 -1
  88. package/components/datepicker/demo/customize.js +0 -19
  89. package/components/datepicker/demo/demo-support.js +0 -1
  90. package/components/datepicker/demo/index.js +0 -23
  91. package/components/datepicker/demo/styles.scss +0 -1
  92. package/components/dropdown/demo/customize.js +0 -9
  93. package/components/dropdown/demo/demo-support.js +0 -1
  94. package/components/dropdown/demo/getting-started.js +0 -9
  95. package/components/dropdown/demo/index.js +0 -16
  96. package/components/dropdown/demo/styles.scss +0 -1
  97. package/components/form/demo/customize.js +0 -9
  98. package/components/form/demo/demo-support.js +0 -1
  99. package/components/form/demo/getting-started.js +0 -9
  100. package/components/form/demo/index.js +0 -5
  101. package/components/form/demo/registerDemoDeps.js +0 -23
  102. package/components/form/demo/styles.scss +0 -1
  103. package/components/input/demo/customize.js +0 -25
  104. package/components/input/demo/demo-support.js +0 -1
  105. package/components/input/demo/getting-started.js +0 -8
  106. package/components/input/demo/index.js +0 -8
  107. package/components/input/demo/styles.css +0 -974
  108. package/components/input/demo/styles.scss +0 -1
  109. package/components/menu/demo/demo-support.js +0 -1
  110. package/components/menu/demo/index.js +0 -25
  111. package/components/menu/demo/styles.scss +0 -1
  112. package/components/radio/demo/customize.js +0 -22
  113. package/components/radio/demo/demo-support.js +0 -1
  114. package/components/radio/demo/getting-started.js +0 -24
  115. package/components/radio/demo/index.js +0 -8
  116. package/components/radio/demo/styles.scss +0 -1
  117. package/components/select/demo/customize.js +0 -11
  118. package/components/select/demo/demo-support.js +0 -1
  119. package/components/select/demo/getting-started.js +0 -26
  120. package/components/select/demo/index.js +0 -11
  121. package/components/select/demo/styles.scss +0 -1
  122. /package/components/datepicker/dist/{src/auro-calendar-month.d.ts → auro-calendar-month.d.ts} +0 -0
  123. /package/components/datepicker/dist/{src/buttonVersion.d.ts → buttonVersion.d.ts} +0 -0
  124. /package/components/datepicker/dist/{src/datepickerKeyboardStrategy.d.ts → datepickerKeyboardStrategy.d.ts} +0 -0
  125. /package/components/datepicker/dist/{src/iconVersion.d.ts → iconVersion.d.ts} +0 -0
  126. /package/components/datepicker/dist/{src/index.d.ts → index.d.ts} +0 -0
  127. /package/components/datepicker/dist/{src/popoverVersion.d.ts → popoverVersion.d.ts} +0 -0
  128. /package/components/datepicker/dist/{src/styles → styles}/classic/color-css.d.ts +0 -0
  129. /package/components/datepicker/dist/{src/styles → styles}/classic/style-css.d.ts +0 -0
  130. /package/components/datepicker/dist/{src/styles → styles}/color-calendar-css.d.ts +0 -0
  131. /package/components/datepicker/dist/{src/styles → styles}/color-cell-css.d.ts +0 -0
  132. /package/components/datepicker/dist/{src/styles → styles}/color-css.d.ts +0 -0
  133. /package/components/datepicker/dist/{src/styles → styles}/color-month-css.d.ts +0 -0
  134. /package/components/datepicker/dist/{src/styles → styles}/shapeSize-css.d.ts +0 -0
  135. /package/components/datepicker/dist/{src/styles → styles}/snowflake/color-css.d.ts +0 -0
  136. /package/components/datepicker/dist/{src/styles → styles}/snowflake/style-css.d.ts +0 -0
  137. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-cell-css.d.ts +0 -0
  138. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-css.d.ts +0 -0
  139. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-month-css.d.ts +0 -0
  140. /package/components/datepicker/dist/{src/styles → styles}/style-css.d.ts +0 -0
  141. /package/components/datepicker/dist/{src/styles → styles}/tokens-css.d.ts +0 -0
  142. /package/components/datepicker/dist/{src/utilities.d.ts → utilities.d.ts} +0 -0
  143. /package/components/datepicker/dist/{src/utilitiesCalendar.d.ts → utilitiesCalendar.d.ts} +0 -0
  144. /package/components/datepicker/dist/{src/utilitiesCalendarRender.d.ts → utilitiesCalendarRender.d.ts} +0 -0
  145. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/day.d.ts +0 -0
  146. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker-calendar.d.ts +0 -0
  147. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker-cell.d.ts +0 -0
  148. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker.d.ts +0 -0
@@ -500,15 +500,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
500
500
  );
501
501
  }
502
502
 
503
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
504
- if (this.auroInputElements?.length === 2) {
505
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
503
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
504
+
505
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
506
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
507
+ // field (datepicker is the intended consumer — start/end are independently required).
508
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
509
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
506
510
  hasValue = false;
507
511
  }
508
512
  }
509
513
 
510
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
511
-
512
514
  if (isCombobox) {
513
515
 
514
516
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -825,6 +827,8 @@ function navigateArrow(component, direction, options = {}) {
825
827
  }
826
828
  }
827
829
 
830
+ /* eslint-disable no-underscore-dangle */
831
+
828
832
  /**
829
833
  * Returns the clear button element from the active input's shadow
830
834
  * DOM, if available.
@@ -860,11 +864,9 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
860
864
  * @param {Object} menu - The menu component.
861
865
  */
862
866
  function reconcileMenuIndex(menu) {
863
- // eslint-disable-next-line no-underscore-dangle
864
867
  if (menu._index < 0 && menu.optionActive && menu.items) {
865
868
  const idx = menu.items.indexOf(menu.optionActive);
866
869
  if (idx >= 0) {
867
- // eslint-disable-next-line no-underscore-dangle
868
870
  menu._index = idx;
869
871
  }
870
872
  }
@@ -883,11 +885,7 @@ const comboboxKeyboardStrategy = {
883
885
 
884
886
  // navigate if bib is open otherwise open it
885
887
  if (component.dropdown.isPopoverVisible) {
886
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
887
- component.activateLastEnabledAvailableOption();
888
- } else {
889
- navigateArrow(component, 'down');
890
- }
888
+ navigateArrow(component, 'down');
891
889
  } else {
892
890
  component.showBib();
893
891
  }
@@ -906,11 +904,7 @@ const comboboxKeyboardStrategy = {
906
904
 
907
905
  // navigate if bib is open otherwise open it
908
906
  if (component.dropdown.isPopoverVisible) {
909
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
910
- component.activateFirstEnabledAvailableOption();
911
- } else {
912
- navigateArrow(component, 'up');
913
- }
907
+ navigateArrow(component, 'up');
914
908
  } else {
915
909
  component.showBib();
916
910
  }
@@ -926,13 +920,18 @@ const comboboxKeyboardStrategy = {
926
920
  },
927
921
 
928
922
  Enter(component, evt, ctx) {
923
+ // Forms should not submit on Enter from a combobox, regardless of which
924
+ // child element (input, clear button, menu) is focused.
925
+ evt.stopPropagation();
926
+
929
927
  if (isClearBtnFocused(ctx)) {
930
- // If the clear button has focus, let the browser activate it normally.
931
- // stopPropagation prevents parent containers (e.g., forms) from treating
932
- // Enter as a submit, but we must NOT call preventDefault — that would
933
- // block the browser's built-in "Enter activates focused button" behavior.
934
- evt.stopPropagation();
935
- } else if (ctx.isExpanded && component.menu.optionActive) {
928
+ // Let the browser dispatch Enter to the focused clear button so its
929
+ // built-in activation fires and clears the selection. Do NOT call
930
+ // preventDefault — that would block the activation.
931
+ return;
932
+ }
933
+
934
+ if (ctx.isExpanded && component.menu.optionActive) {
936
935
  reconcileMenuIndex(component.menu);
937
936
  component.menu.makeSelection();
938
937
 
@@ -941,14 +940,8 @@ const comboboxKeyboardStrategy = {
941
940
  }
942
941
 
943
942
  evt.preventDefault();
944
- evt.stopPropagation();
945
943
  } else {
946
- // Prevent the keypress from bubbling to parent containers (e.g., forms)
947
- // which could interpret Enter as a submit or trigger other unintended behavior.
948
- // This is safe because showBib() opens the dialog programmatically,
949
- // not via event propagation.
950
944
  evt.preventDefault();
951
- evt.stopPropagation();
952
945
  component.showBib();
953
946
  }
954
947
  },
@@ -993,7 +986,7 @@ const comboboxKeyboardStrategy = {
993
986
  component.setClearBtnFocus();
994
987
  }
995
988
  }
996
- },
989
+ }
997
990
  };
998
991
 
999
992
  /**
@@ -4888,7 +4881,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
4888
4881
  }
4889
4882
  };
4890
4883
 
4891
- var formkitVersion$2 = '202606192121';
4884
+ var formkitVersion$2 = '202607010100';
4892
4885
 
4893
4886
  let AuroElement$2 = class AuroElement extends i$3 {
4894
4887
  static get properties() {
@@ -5839,6 +5832,14 @@ class AuroDropdown extends AuroElement$2 {
5839
5832
  };
5840
5833
  this.addEventListener('keydown', this._bibTabHandler);
5841
5834
 
5835
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
5836
+ // desktopModal trap owns focus management. Without this, the very
5837
+ // first focus move into the bib (from the RAF below) triggers
5838
+ // handleFocusLoss → hideBib, tearing down the trap before the
5839
+ // user can press Tab.
5840
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
5841
+ this.noHideOnThisFocusLoss = true;
5842
+
5842
5843
  // Move initial focus into the bib content, matching FocusTrap behavior
5843
5844
  requestAnimationFrame(() => {
5844
5845
  const focusables = getFocusableElements(this.bibContent);
@@ -6152,6 +6153,7 @@ class AuroDropdown extends AuroElement$2 {
6152
6153
  role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
6153
6154
  aria-expanded="${o(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
6154
6155
  aria-controls="${o(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.dropdownId)}"
6156
+ aria-haspopup="${o(this.a11yRole === 'combobox' && !this.triggerContentFocusable ? 'listbox' : undefined)}"
6155
6157
  aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
6156
6158
  aria-disabled="${o(this.disabled ? 'true' : undefined)}"
6157
6159
  @focusin="${this.handleFocusin}"
@@ -10661,15 +10663,17 @@ class AuroFormValidation {
10661
10663
  );
10662
10664
  }
10663
10665
 
10664
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
10665
- if (this.auroInputElements?.length === 2) {
10666
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
10666
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10667
+
10668
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
10669
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
10670
+ // field (datepicker is the intended consumer — start/end are independently required).
10671
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
10672
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
10667
10673
  hasValue = false;
10668
10674
  }
10669
10675
  }
10670
10676
 
10671
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10672
-
10673
10677
  if (isCombobox) {
10674
10678
 
10675
10679
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -16605,10 +16609,18 @@ class AuroInputUtilities {
16605
16609
  * @returns {string | undefined}
16606
16610
  */
16607
16611
  toFormattedValue(valueObject, format) {
16612
+ // Exposed publicly via `AuroInputUtil.toFormattedValue`, so a missing
16613
+ // `format` argument has to fail gracefully rather than throw on
16614
+ // `format.toLowerCase()`. Treat any falsy value (undefined, null, '')
16615
+ // as "no format → no display value".
16616
+ if (!format) {
16617
+ return undefined;
16618
+ }
16608
16619
  const normalizedFormat = format.toLowerCase();
16609
16620
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
16610
16621
 
16611
16622
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
16623
+ console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
16612
16624
  return undefined;
16613
16625
  }
16614
16626
 
@@ -16755,79 +16767,19 @@ class BaseInput extends AuroElement$1 {
16755
16767
  constructor() {
16756
16768
  super();
16757
16769
 
16758
- this.appearance = "default";
16759
- this.disabled = false;
16760
- this.layout = 'classic';
16761
- this.locale = 'en-US';
16762
- this.max = undefined;
16763
- this._maxObject = undefined;
16764
- this.maxLength = undefined;
16765
- this.min = undefined;
16766
- this._minObject = undefined;
16767
- this.minLength = undefined;
16768
- this.required = false;
16769
- this.onDark = false;
16770
- this.setCustomValidityForType = undefined;
16771
- this.size = 'lg';
16772
- this.shape = 'classic';
16773
- this.value = undefined;
16774
- this._valueObject = undefined;
16775
-
16776
- this._initializePrivateDefaults();
16777
- }
16778
-
16779
- /**
16780
- * Internal Defaults.
16781
- * @private
16782
- * @returns {void}
16783
- */
16784
- _initializePrivateDefaults() {
16770
+ // Single-source initialization. Alphabetized so duplicate or stale
16771
+ // defaults are immediately obvious on a diff. Every field is assigned
16772
+ // exactly once here (previously the constructor + the old
16773
+ // `_initializePrivateDefaults` overlapped — both wrote ~14 of the same
16774
+ // fields and double-allocated `util` and `validation`, discarding the
16775
+ // first instance). `validation` is now allocated exactly once; `util`
16776
+ // is seeded here with an en-US default and then rebuilt in
16777
+ // `connectedCallback` once the consumer-resolved locale is available,
16778
+ // so a parent (datepicker/combobox) calling `validate()` synchronously
16779
+ // during its own update cycle sees a populated util instance.
16785
16780
  this.activeLabel = false;
16786
- this.appearance = "default";
16787
- this.icon = false;
16788
- this.disabled = false;
16789
- this.dvInputOnly = false;
16790
- this.hideLabelVisually = false;
16791
- this.max = undefined;
16792
- this.maxLength = undefined;
16793
- this.min = undefined;
16794
- this.minLength = undefined;
16795
- this.noValidate = false;
16796
- this.onDark = false;
16797
- this.required = false;
16798
- this.setCustomValidityForType = undefined;
16799
-
16800
- // Used for storing raw values returned from input mask.
16801
- this._rawMaskValue = undefined;
16802
-
16803
- /**
16804
- * @private
16805
- */
16806
- this.layout = 'classic';
16807
-
16808
- /**
16809
- * @private
16810
- */
16811
- this.shape = 'classic';
16812
-
16813
- /**
16814
- * @private
16815
- */
16816
- this.size = 'lg';
16817
-
16818
- this.touched = false;
16819
- this.util = new AuroInputUtilities({
16820
- locale: "en-US",
16821
- format: this.format
16822
- });
16823
- this.validation = new AuroFormValidation();
16824
- this.inputIconName = undefined;
16825
- this.showPassword = false;
16826
- this.validationCCLength = undefined;
16827
- this.hasValue = false;
16828
- this.label = 'Input label is undefined';
16829
-
16830
16781
 
16782
+ /** @private */
16831
16783
  this.allowedInputTypes = [
16832
16784
  "text",
16833
16785
  "number",
@@ -16836,7 +16788,9 @@ class BaseInput extends AuroElement$1 {
16836
16788
  "credit-card",
16837
16789
  "tel"
16838
16790
  ];
16839
- this.icon = false;
16791
+ this.appearance = "default";
16792
+
16793
+ /** @private */
16840
16794
  this.dateFormatMap = {
16841
16795
  'mm/dd/yyyy': 'dateMMDDYYYY',
16842
16796
  'dd/mm/yyyy': 'dateDDMMYYYY',
@@ -16853,27 +16807,62 @@ class BaseInput extends AuroElement$1 {
16853
16807
  'dd/mm': 'dateDDMM',
16854
16808
  'mm/dd': 'dateMMDD'
16855
16809
  };
16810
+ this.disabled = false;
16811
+
16812
+ /** @private */
16856
16813
  this.domHandler = new DomHandler();
16857
16814
  this.dvInputOnly = false;
16858
16815
  this.hasValue = false;
16816
+ this.hideLabelVisually = false;
16817
+ this.icon = false;
16818
+
16819
+ /** @private */
16859
16820
  this.inputIconName = undefined;
16821
+
16822
+ /** @private */
16860
16823
  this.label = 'Input label is undefined';
16824
+ this.layout = 'classic';
16825
+ this.locale = 'en-US';
16826
+ this.max = undefined;
16827
+ this.maxLength = undefined;
16828
+ this.min = undefined;
16829
+ this.minLength = undefined;
16861
16830
  this.noValidate = false;
16862
- this._rawMaskValue = undefined; // Used for storing raw values returned from input mask.
16831
+ this.onDark = false;
16832
+ // Raw values returned from the input mask before model normalization.
16833
+ this._rawMaskValue = undefined;
16834
+ this.required = false;
16835
+ this.setCustomValidityForType = undefined;
16836
+ // Credit Card is intentionally excluded — its mask manages the cursor
16837
+ // itself, and listing it here caused cursor placement issues in Safari.
16838
+ /** @private */
16863
16839
  this.setSelectionInputTypes = [
16864
16840
  "text",
16865
16841
  "password",
16866
16842
  "email"
16867
- ]; // Credit Card is not included as this caused cursor placement issues in Safari.
16843
+ ];
16844
+ this.shape = 'classic';
16845
+
16846
+ /** @private */
16868
16847
  this.showPassword = false;
16848
+ this.size = 'lg';
16869
16849
  this.touched = false;
16850
+
16851
+ /** @private */
16870
16852
  this.uniqueId = new UniqueId().create();
16853
+
16854
+ /** @private */
16871
16855
  this.util = new AuroInputUtilities({
16872
16856
  locale: this.locale,
16873
16857
  format: this.format
16874
16858
  });
16859
+
16860
+ /** @private */
16875
16861
  this.validation = new AuroFormValidation();
16862
+
16863
+ /** @private */
16876
16864
  this.validationCCLength = undefined;
16865
+ this.value = undefined;
16877
16866
  }
16878
16867
 
16879
16868
  // function to define props used within the scope of this component
@@ -17311,7 +17300,7 @@ class BaseInput extends AuroElement$1 {
17311
17300
  * @returns {Date|undefined}
17312
17301
  */
17313
17302
  get valueObject() {
17314
- return this._valueObject || this._computeDateObjectFallback(this.value);
17303
+ return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
17315
17304
  }
17316
17305
 
17317
17306
  /**
@@ -17319,7 +17308,7 @@ class BaseInput extends AuroElement$1 {
17319
17308
  * @returns {Date|undefined}
17320
17309
  */
17321
17310
  get minObject() {
17322
- return this._minObject || this._computeDateObjectFallback(this.min);
17311
+ return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
17323
17312
  }
17324
17313
 
17325
17314
  /**
@@ -17327,50 +17316,7 @@ class BaseInput extends AuroElement$1 {
17327
17316
  * @returns {Date|undefined}
17328
17317
  */
17329
17318
  get maxObject() {
17330
- return this._maxObject || this._computeDateObjectFallback(this.max);
17331
- }
17332
-
17333
- /**
17334
- * Parses a date string into a Date object when the corresponding `_*Object`
17335
- * field hasn't been synced yet by `updated()`. Returns undefined when the
17336
- * input type/format isn't a full date or the string is not a valid date.
17337
- *
17338
- * Why this exists: a parent (datepicker) can call `inputN.validate()` from
17339
- * inside its own `updated()` before this input's `updated()` has run
17340
- * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
17341
- * and range checks would otherwise silently no-op (flipping the result to
17342
- * `valid` or `patternMismatch`).
17343
- * @private
17344
- * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
17345
- * @returns {Date|undefined}
17346
- */
17347
- _computeDateObjectFallback(dateStr) {
17348
- if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
17349
- return undefined;
17350
- }
17351
- if (!dateFormatter.isValidDate(dateStr)) {
17352
- return undefined;
17353
- }
17354
- return dateFormatter.stringToDateInstance(dateStr);
17355
- }
17356
-
17357
- /**
17358
- * Internal setter for readonly date object properties.
17359
- * @private
17360
- * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
17361
- * @param {Date|undefined} propertyValue - Value to assign.
17362
- * @returns {void}
17363
- */
17364
- setDateObjectProperty(propertyName, propertyValue) {
17365
- const internalPropertyName = `_${propertyName}`;
17366
- const previousValue = this[internalPropertyName];
17367
-
17368
- if (previousValue === propertyValue) {
17369
- return;
17370
- }
17371
-
17372
- this[internalPropertyName] = propertyValue;
17373
- this.requestUpdate(propertyName, previousValue);
17319
+ return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
17374
17320
  }
17375
17321
 
17376
17322
  connectedCallback() {
@@ -17399,7 +17345,6 @@ class BaseInput extends AuroElement$1 {
17399
17345
  format: this.format
17400
17346
  });
17401
17347
  this.configureDataForType();
17402
- this.syncDateValues();
17403
17348
  }
17404
17349
 
17405
17350
  disconnectedCallback() {
@@ -17419,12 +17364,17 @@ class BaseInput extends AuroElement$1 {
17419
17364
  this.wrapperElement.addEventListener('click', this.handleClick);
17420
17365
  }
17421
17366
 
17422
- // add attribute for query selectors when auro-input is registered under a custom name
17423
- if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
17424
- this.setAttribute('auro-input', '');
17425
- }
17426
17367
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
17427
17368
 
17369
+ // Normalize the format token to lowercase so case-mixed values supplied
17370
+ // via attribute (e.g. `format="MM/DD/YYYY"`) hit the `dateFormatMap`
17371
+ // lookup inside `setCustomHelpTextMessage`. Without this, an uppercase
17372
+ // format silently misses the map and leaves `setCustomValidityForType`
17373
+ // unset.
17374
+ if (this.format) {
17375
+ this.format = this.format.toLowerCase();
17376
+ }
17377
+
17428
17378
  // use validity message override if declared when initializing the component
17429
17379
  if (this.hasAttribute('setCustomValidity')) {
17430
17380
  this.ValidityMessageOverride = this.setCustomValidity;
@@ -17433,7 +17383,6 @@ class BaseInput extends AuroElement$1 {
17433
17383
  this.setCustomHelpTextMessage();
17434
17384
  this.configureAutoFormatting();
17435
17385
  this.configureDataForType();
17436
- this.syncDateValues();
17437
17386
  }
17438
17387
 
17439
17388
  /**
@@ -17509,6 +17458,13 @@ class BaseInput extends AuroElement$1 {
17509
17458
 
17510
17459
  if (typeToI18n.includes(this.type)) {
17511
17460
  this.setCustomValidityForType = i18n(this.lang, this.type);
17461
+ // COVERAGE: this `else if` branch is unreachable in WTR. connectedCallback
17462
+ // (L682) calls configureDataForType, which at L1266-1268 assigns
17463
+ // `this.format = this.util.getDateMaskFromLocale().toLowerCase()` for any
17464
+ // type=date input whose `format` attribute is unset. That runs before
17465
+ // firstUpdated invokes setCustomHelpTextMessage, so `!this.format` is
17466
+ // always false here. Retained as a defensive fallback; the whole function
17467
+ // is @deprecated per AB#1557296 and slated for removal.
17512
17468
  } else if (!this.format && this.type === 'date') {
17513
17469
  this.setCustomValidityForType = i18n(this.lang, 'dateMMDDYYYY');
17514
17470
  } else if (this.dateFormatMap[this.format]) {
@@ -17570,8 +17526,6 @@ class BaseInput extends AuroElement$1 {
17570
17526
  this.configureDataForType();
17571
17527
  }
17572
17528
 
17573
- this.syncDateValues(changedProperties);
17574
-
17575
17529
  if (changedProperties.has('value')) {
17576
17530
  if (this.value && this.value.length > 0) {
17577
17531
  this.hasValue = true;
@@ -17593,14 +17547,14 @@ class BaseInput extends AuroElement$1 {
17593
17547
 
17594
17548
  if (formattedValue !== this.inputElement.value) {
17595
17549
  this.skipNextProgrammaticInputEvent = true;
17596
- if (this.maskInstance && this.type === 'credit-card') {
17550
+ if (this.maskInstance && this.type !== 'date') {
17597
17551
  // Route through the mask so its _value and el.value stay in lock-step
17598
17552
  // (set value calls updateControl which writes el.value = displayValue).
17599
17553
  // Writing el.value directly leaves the mask thinking displayValue is
17600
- // stale; _saveSelection on the next focus/click then warns. Scoped to
17601
- // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
17602
- // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
17603
- // and flip validity from patternMismatch to tooShort.
17554
+ // stale; _saveSelection on the next focus/click then warns. Date is
17555
+ // excluded because its formattedValue can be raw ISO when the calendar
17556
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
17557
+ // flip validity from patternMismatch to tooShort.
17604
17558
  this.maskInstance.value = formattedValue || '';
17605
17559
  } else if (formattedValue) {
17606
17560
  this.inputElement.value = formattedValue;
@@ -17642,120 +17596,65 @@ class BaseInput extends AuroElement$1 {
17642
17596
  }));
17643
17597
  }
17644
17598
 
17645
-
17646
- /**
17647
- * Synchronizes the ISO string values and Date object representations for date-related properties.
17648
- * This keeps the model and display values aligned when either side changes.
17649
- *
17650
- * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
17651
- * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
17652
- * represents a full year/month/day date format.
17653
- *
17654
- * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
17655
- * @returns {void}
17656
- * @private
17657
- */
17658
- syncDateValues(changedProperties = undefined) {
17659
- if (!this.util.isFullDateFormat(this.type, this.format)) {
17660
- return;
17661
- }
17662
-
17663
- this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
17664
- this.syncSingleDateValue(changedProperties, 'minObject', 'min');
17665
- this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
17666
- }
17667
-
17668
- /**
17669
- * Synchronizes one date object/string property pair.
17670
- * @private
17671
- * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
17672
- * @param {string} objectProperty - Date object property name.
17673
- * @param {string} valueProperty - ISO string property name.
17674
- * @returns {void}
17675
- */
17676
- syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
17677
- const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
17678
-
17679
- // objectProperty wins over valueProperty when both changed
17680
- if (objectPropertyChanged && this[objectProperty]) {
17681
- this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
17682
- return;
17683
- }
17684
-
17685
- const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
17686
- if (!valuePropertyChanged) {
17687
- return;
17688
- }
17689
-
17690
- // 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)
17691
- if (
17692
- changedProperties &&
17693
- valueProperty === 'value' &&
17694
- changedProperties.get('value') === undefined &&
17695
- this[objectProperty] instanceof Date &&
17696
- this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
17697
- ) {
17698
- return;
17699
- }
17700
-
17701
- if (dateFormatter.isValidDate(this[valueProperty])) {
17702
- this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
17703
- } else {
17704
- this.setDateObjectProperty(objectProperty, undefined);
17705
- }
17706
- }
17707
-
17708
17599
  /**
17709
17600
  * Sets up IMasks and logic based on auto-formatting requirements.
17710
17601
  * @private
17711
17602
  * @returns {void}
17712
17603
  */
17713
17604
  configureAutoFormatting() {
17714
- // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
17715
- // by _configuringMask above) could otherwise trigger handleInput
17716
- // processCreditCard configureAutoFormatting before the outer call's
17717
- // set value has finished its alignCursor pass.
17605
+ // _configuringMask gates two things: external re-entry into this method
17606
+ // while setup is mid-flight (from property changes that call back here),
17607
+ // and the accept/complete listeners below — both need to ignore the mask
17608
+ // events fired by our own value-restore step.
17718
17609
  if (this._configuringMask) return;
17719
17610
  this._configuringMask = true;
17720
17611
  try {
17612
+ // Destroy any prior mask so IMask can attach fresh under the new format.
17613
+ // Null the reference too — if the new maskOptions.mask is falsy (e.g.
17614
+ // type switched from credit-card to text) the IMask() reassignment
17615
+ // below is skipped, and downstream writes at line ~823 would otherwise
17616
+ // route through the destroyed instance.
17721
17617
  if (this.maskInstance) {
17722
17618
  this.maskInstance.destroy();
17619
+ this.maskInstance = null;
17723
17620
  }
17724
17621
 
17725
- // Pass new format to util
17726
17622
  this.util.updateFormat(this.format);
17727
17623
 
17728
17624
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
17729
17625
 
17730
17626
  if (this.inputElement && maskOptions.mask) {
17731
-
17732
- // Stash and clear any existing value before IMask init.
17733
- // IMask's constructor processes the current input value which requires
17734
- // selection state clearing first avoids that scenario entirely.
17735
- // When the format changes (e.g. locale switch) and we have a valid ISO
17736
- // model value, compute the display string for the NEW format instead of
17737
- // re-using the old display string, which may be invalid in the new mask.
17627
+ // Capture the current display so it can be re-applied after IMask
17628
+ // attaches. The restore at the bottom goes through maskInstance.value
17629
+ // (not inputElement.value directly) so the mask's internal state and
17630
+ // the input's displayed text stay in lock-step.
17738
17631
  let existingValue = this.inputElement.value;
17632
+
17633
+ // Format-change case (e.g. locale switch): existingValue is the OLD
17634
+ // mask's display string and may not parse under the new mask. When
17635
+ // we have a valid date model, rebuild the display from valueObject
17636
+ // (the canonical source) using the new mask's format function.
17739
17637
  if (
17740
17638
  this.util.isFullDateFormat(this.type, this.format) &&
17741
17639
  this.value &&
17742
- dateFormatter.isValidDate(this.value) &&
17743
- this.valueObject instanceof Date &&
17744
- !Number.isNaN(this.valueObject.getTime()) &&
17640
+ this.valueObject &&
17745
17641
  typeof maskOptions.format === 'function'
17746
17642
  ) {
17747
17643
  existingValue = maskOptions.format(this.valueObject);
17748
17644
  }
17749
17645
 
17646
+ // Clear before IMask attaches so the constructor seeds an empty
17647
+ // internal value. Otherwise IMask reads the stale unmasked string
17648
+ // and emits a spurious 'accept' before the restore below runs.
17750
17649
  this.skipNextProgrammaticInputEvent = true;
17751
17650
  this.inputElement.value = '';
17752
17651
 
17753
17652
  this.maskInstance = IMask(this.inputElement, maskOptions);
17754
17653
 
17654
+ // Mask fires 'accept' on every value change, including the restore
17655
+ // step below. Skip events fired during configureAutoFormatting so
17656
+ // we don't overwrite a value the parent just pushed.
17755
17657
  this.maskInstance.on('accept', () => {
17756
- // Suppress propagation during configureAutoFormatting's own value-restoration
17757
- // (line below) — the mask emits 'accept' on every value-set, including ours,
17758
- // and we don't want to overwrite a value the parent just pushed.
17759
17658
  if (this._configuringMask) return;
17760
17659
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
17761
17660
  if (this.type === "date") {
@@ -17763,6 +17662,8 @@ class BaseInput extends AuroElement$1 {
17763
17662
  }
17764
17663
  });
17765
17664
 
17665
+ // Mask fires 'complete' on the restore step below for any value that
17666
+ // happens to be a complete match. Same setup-suppression as 'accept'.
17766
17667
  this.maskInstance.on('complete', () => {
17767
17668
  if (this._configuringMask) return;
17768
17669
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -17771,7 +17672,9 @@ class BaseInput extends AuroElement$1 {
17771
17672
  }
17772
17673
  });
17773
17674
 
17774
- // Restore the stashed value through IMask so it's properly masked
17675
+ // Write existingValue through the mask (not the input directly) so
17676
+ // the mask reformats it under the new rules and keeps its internal
17677
+ // _value aligned with the input's displayed text.
17775
17678
  if (existingValue) {
17776
17679
  this.maskInstance.value = existingValue;
17777
17680
  }
@@ -17851,6 +17754,38 @@ class BaseInput extends AuroElement$1 {
17851
17754
  this.touched = true;
17852
17755
  this.validation.validate(this);
17853
17756
  }
17757
+
17758
+ // Prevents cursor jumping in Safari. Setting `this.value` triggers a Lit
17759
+ // update that can re-render the input and reset the native cursor; we
17760
+ // capture the caret position before that update commits and restore it
17761
+ // via `setSelectionRange` once the update has flushed. Gated on
17762
+ // `setSelectionInputTypes` so credit-card (and other masked types whose
17763
+ // formatter manages the cursor itself) doesn't get a competing write.
17764
+ // Capture the caret position INSIDE the gate — reading `selectionStart`
17765
+ // on input types that don't support text selection (number, email in
17766
+ // some browsers) throws InvalidStateError, which would crash all input
17767
+ // handling. Wrap the read in try/catch belt-and-suspenders even though
17768
+ // the gated types currently support it, since the list is a public
17769
+ // property a consumer could mutate.
17770
+ if (this.setSelectionInputTypes.includes(this.type)) {
17771
+ let selectionStart = null;
17772
+ try {
17773
+ ({ selectionStart } = this.inputElement);
17774
+ } catch (error) { // eslint-disable-line no-unused-vars
17775
+ return;
17776
+ }
17777
+ if (typeof selectionStart !== 'number') {
17778
+ return;
17779
+ }
17780
+ this.updateComplete.then(() => {
17781
+ try {
17782
+ this.inputElement.setSelectionRange(selectionStart, selectionStart);
17783
+ } catch (error) { // eslint-disable-line no-unused-vars
17784
+ // Some input types (number/email in certain UAs) throw on
17785
+ // setSelectionRange; swallow and let the native cursor stand.
17786
+ }
17787
+ });
17788
+ }
17854
17789
  }
17855
17790
 
17856
17791
  /**
@@ -17939,7 +17874,6 @@ class BaseInput extends AuroElement$1 {
17939
17874
  */
17940
17875
  reset() {
17941
17876
  this.value = undefined;
17942
- this.setDateObjectProperty('valueObject', undefined);
17943
17877
  this.validation.reset(this);
17944
17878
  }
17945
17879
 
@@ -17948,7 +17882,6 @@ class BaseInput extends AuroElement$1 {
17948
17882
  */
17949
17883
  clear() {
17950
17884
  this.value = undefined;
17951
- this.setDateObjectProperty('valueObject', undefined);
17952
17885
  }
17953
17886
 
17954
17887
  /**
@@ -17986,7 +17919,7 @@ class BaseInput extends AuroElement$1 {
17986
17919
  */
17987
17920
  get placeholderStr() {
17988
17921
  if (!this.placeholder && this.type === 'date') {
17989
- return this.format ? this.format.toUpperCase() : 'MM/DD/YYYY';
17922
+ return this.format.toUpperCase();
17990
17923
  }
17991
17924
  return this.placeholder || "";
17992
17925
  }
@@ -18477,7 +18410,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18477
18410
  }
18478
18411
  };
18479
18412
 
18480
- var formkitVersion$1 = '202606192121';
18413
+ var formkitVersion$1 = '202607010100';
18481
18414
 
18482
18415
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18483
18416
  // See LICENSE in the project root for license information.
@@ -18980,6 +18913,7 @@ class AuroInput extends BaseInput {
18980
18913
  <${this.buttonTag}
18981
18914
  @click="${this.handleClickShowPassword}"
18982
18915
  appearance="${this.onDark ? 'inverse' : this.appearance}"
18916
+ aria-pressed="${this.showPassword ? 'true' : 'false'}"
18983
18917
  class="notificationBtn passwordBtn"
18984
18918
  shape="circle"
18985
18919
  size="sm"
@@ -19053,25 +18987,29 @@ class AuroInput extends BaseInput {
19053
18987
  * @returns {html} - Returns HTML for the help text and error message.
19054
18988
  */
19055
18989
  renderHtmlHelpText() {
18990
+ // Single `<p>` with stable identity across validity transitions —
18991
+ // previously two distinct templates (valid vs invalid) caused Lit to
18992
+ // replace the node entirely on a flip, and VoiceOver wouldn't
18993
+ // re-announce because the live-region element it was watching had been
18994
+ // removed and a new one inserted. Keeping one node means the `role`,
18995
+ // `aria-live`, and text content all change in-place, which AT does
18996
+ // observe and announce.
18997
+ const isError = this.validity && this.validity !== 'valid';
19056
18998
  return u$7`
19057
- ${!this.validity || this.validity === undefined || this.validity === 'valid'
19058
- ? u$7`
19059
- <${this.helpTextTag}
19060
- appearance="${this.onDark ? 'inverse' : this.appearance}">
19061
- <p id="${this.uniqueId}" part="helpText">
19062
- <slot name="helpText">${this.getHelpText()}</slot>
19063
- </p>
19064
- </${this.helpTextTag}>
19065
- `
19066
- : u$7`
19067
- <${this.helpTextTag} error
19068
- appearance="${this.onDark ? 'inverse' : this.appearance}">
19069
- <p id="${this.uniqueId}" role="alert" aria-live="assertive" part="helpText">
19070
- ${this.errorMessage}
19071
- </p>
19072
- </${this.helpTextTag}>
19073
- `
19074
- }
18999
+ <${this.helpTextTag}
19000
+ appearance="${this.onDark ? 'inverse' : this.appearance}"
19001
+ ?error=${isError}>
19002
+ <p
19003
+ id="${this.uniqueId}"
19004
+ part="helpText"
19005
+ role="${o(isError ? 'alert' : undefined)}"
19006
+ aria-live="${o(isError ? 'assertive' : undefined)}">
19007
+ ${isError
19008
+ ? this.errorMessage
19009
+ : u$7`<slot name="helpText">${this.getHelpText()}</slot>`
19010
+ }
19011
+ </p>
19012
+ </${this.helpTextTag}>
19075
19013
  `;
19076
19014
  }
19077
19015
 
@@ -19203,7 +19141,7 @@ class AuroInput extends BaseInput {
19203
19141
 
19204
19142
  var colorCss$3 = i$6`#headerContainer{box-shadow:var(--ds-auro-dropdownbib-header-boxshadow-color)}#footerContainer{background-color:var(--ds-auro-dropdownbib-footer-container-color)}`;
19205
19143
 
19206
- var styleCss$4 = i$6`#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)}`;
19144
+ var styleCss$4 = i$6`#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}:host([isfullscreen]) #bibTemplate{max-height:100dvh}:host([isfullscreen]) #bodyContainer{margin-top:var(--ds-size-100, 0.5rem)}#footerContainer{padding:var(--ds-size-200, 1rem)}`;
19207
19145
 
19208
19146
  var tokenCss = i$6`: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)}`;
19209
19147
 
@@ -19598,7 +19536,7 @@ class AuroBibtemplate extends i$3 {
19598
19536
  }
19599
19537
  }
19600
19538
 
19601
- var formkitVersion = '202606192121';
19539
+ var formkitVersion = '202607010100';
19602
19540
 
19603
19541
  var styleCss$3 = i$6`.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}`;
19604
19542
 
@@ -19966,12 +19904,23 @@ function getOptionLabel(option) {
19966
19904
  if (!option) {
19967
19905
  return '';
19968
19906
  }
19969
- const clone = option.cloneNode(true);
19970
- const displayValueEl = clone.querySelector('[slot="displayValue"]');
19971
- if (displayValueEl) {
19972
- displayValueEl.remove();
19907
+
19908
+ // Consumer-provided override: short-circuit the DOM walk entirely.
19909
+ if (option.dataset && option.dataset.label) {
19910
+ return option.dataset.label;
19911
+ }
19912
+
19913
+ // Walk direct children — the `slot` attribute only applies to direct children
19914
+ // of the slot host, so a shallow filter is sufficient. Avoids the cloneNode +
19915
+ // querySelector allocation that ran on every keystroke via syncValuesAndStates.
19916
+ let text = '';
19917
+ for (const node of option.childNodes) {
19918
+ const isDisplayValueSlot = node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === 'displayValue';
19919
+ if (!isDisplayValueSlot) {
19920
+ text += node.textContent || '';
19921
+ }
19973
19922
  }
19974
- return (clone.textContent || '').replace(/\s+/gu, ' ').trim();
19923
+ return text.replace(/\s+/gu, ' ').trim();
19975
19924
  }
19976
19925
 
19977
19926
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
@@ -20046,8 +19995,6 @@ class AuroCombobox extends AuroElement {
20046
19995
  this.availableOptions = [];
20047
19996
  this.dropdownId = undefined;
20048
19997
  this.dropdownOpen = false;
20049
- this.triggerExpandedState = false;
20050
- this._expandedTimeout = null;
20051
19998
  this._inFullscreenTransition = false;
20052
19999
  this.errorMessage = null;
20053
20000
  this.isHiddenWhileLoading = false;
@@ -20058,6 +20005,30 @@ class AuroCombobox extends AuroElement {
20058
20005
  this.touched = false;
20059
20006
  this.validation = new AuroFormValidation$1();
20060
20007
  this.validity = undefined;
20008
+ this._userTyped = false;
20009
+ // Tracks every setTimeout scheduled via _scheduleTimer so
20010
+ // disconnectedCallback can cancel them. Without this, a detached
20011
+ // combobox's pending timers still fire — most are no-ops, but
20012
+ // configureMenu's racing-condition retry would otherwise loop.
20013
+ this._pendingTimers = new Set();
20014
+ }
20015
+
20016
+ /**
20017
+ * setTimeout wrapper that records the timer id so disconnectedCallback
20018
+ * can cancel any outstanding callbacks. The id is removed from the set
20019
+ * once the callback fires so the set doesn't grow unbounded.
20020
+ * @param {Function} fn - Callback to run.
20021
+ * @param {number} ms - Delay in milliseconds.
20022
+ * @returns {number} The timer id.
20023
+ * @private
20024
+ */
20025
+ _scheduleTimer(fn, ms) {
20026
+ const id = setTimeout(() => {
20027
+ this._pendingTimers.delete(id);
20028
+ fn();
20029
+ }, ms);
20030
+ this._pendingTimers.add(id);
20031
+ return id;
20061
20032
  }
20062
20033
 
20063
20034
  // This function is to define props used within the scope of this component
@@ -20419,17 +20390,6 @@ class AuroCombobox extends AuroElement {
20419
20390
  attribute: false
20420
20391
  },
20421
20392
 
20422
- /**
20423
- * Deferred aria-expanded state for the trigger input.
20424
- * Delays the "true" transition so VoiceOver finishes its character echo
20425
- * before announcing "expanded".
20426
- * @private
20427
- */
20428
- triggerExpandedState: {
20429
- type: Boolean,
20430
- reflect: false,
20431
- attribute: false
20432
- },
20433
20393
  };
20434
20394
  }
20435
20395
 
@@ -20452,13 +20412,6 @@ class AuroCombobox extends AuroElement {
20452
20412
  return this.input.value;
20453
20413
  }
20454
20414
 
20455
- // /**
20456
- // * Sets the value of the input element within the combobox.
20457
- // */
20458
- // set inputValue(value) {
20459
- // this.input.value = value;
20460
- // }
20461
-
20462
20415
  /**
20463
20416
  * Checks if the element is valid.
20464
20417
  * @returns {boolean} - Returns true if the element is valid, false otherwise.
@@ -20585,7 +20538,7 @@ class AuroCombobox extends AuroElement {
20585
20538
  if (this.menu) {
20586
20539
  this.menu.matchWord = normalizeFilterValue(this.input.value);
20587
20540
  }
20588
- const label = getOptionLabel(this.menu.optionSelected) || this.menu.currentLabel;
20541
+ const label = getOptionLabel(this.menu.optionSelected);
20589
20542
  this.updateTriggerTextDisplay(label || this.value);
20590
20543
  }
20591
20544
 
@@ -20599,44 +20552,16 @@ class AuroCombobox extends AuroElement {
20599
20552
  // in suggestion mode, do not override input value if no selection has been made and the input currently has focus
20600
20553
  const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
20601
20554
 
20602
- if (!this.persistInput && !(this.behavior === 'suggestion' && isInputFocusedWithNoSelection)) {
20603
- const nextValue = label || this.value;
20604
- // Only set the flag when there's an actual write to suppress —
20605
- // syncValuesAndStates re-enters here during typing when both inputs
20606
- // already match, and a no-op flag flip would make the bib branch's
20607
- // bail eat legitimate user-input events.
20608
- const triggerNeedsSync = this.input.value !== nextValue;
20609
- const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
20610
- if (triggerNeedsSync || bibNeedsSync) {
20611
- this._syncingDisplayValue = true;
20612
- if (triggerNeedsSync) {
20613
- this.input.value = nextValue;
20614
- }
20615
- if (bibNeedsSync) {
20616
- this.inputInBib.value = nextValue;
20617
- }
20618
- const pending = [];
20619
- if (triggerNeedsSync) {
20620
- pending.push(this.input.updateComplete);
20621
- }
20622
- if (bibNeedsSync) {
20623
- pending.push(this.inputInBib.updateComplete);
20624
- }
20625
- Promise.all(pending).then(() => {
20626
- // imask reasserts otherwise, and handleBlur reverts to its stale value.
20627
- if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
20628
- this.input.maskInstance.updateValue();
20629
- }
20630
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20631
- this.inputInBib.maskInstance.updateValue();
20632
- }
20633
- this._syncingDisplayValue = false;
20634
- });
20635
- }
20555
+ const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
20556
+ if (!suppressed) {
20557
+ this.syncInputValuesAcrossTriggerAndBib(label || this.value);
20636
20558
  }
20637
20559
 
20638
- // update the displayValue in the trigger if displayValue slot content is present
20639
- const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]');
20560
+ // Replace any previously appended displayValue clone in the trigger.
20561
+ // :not(slot) excludes the template's <slot name="displayValue"
20562
+ // slot="displayValue"> forwarder, which also has slot="displayValue"
20563
+ // and would otherwise be matched first and removed.
20564
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
20640
20565
 
20641
20566
  if (displayValueInTrigger) {
20642
20567
  displayValueInTrigger.remove();
@@ -20660,6 +20585,53 @@ class AuroCombobox extends AuroElement {
20660
20585
  this.requestUpdate();
20661
20586
  }
20662
20587
 
20588
+ /**
20589
+ * Writes nextValue to the trigger input and the bib input when their current
20590
+ * value differs, then re-asserts imask after Lit's update flushes.
20591
+ * @param {string} nextValue - The value to write to both inputs.
20592
+ * @returns {Promise<void>} Resolves after both inputs flush and imask
20593
+ * re-asserts; resolves immediately when no sync is needed.
20594
+ * @private
20595
+ */
20596
+ async syncInputValuesAcrossTriggerAndBib(nextValue) {
20597
+ // Only set the flag when there's an actual write to suppress —
20598
+ // syncValuesAndStates re-enters here during typing when both inputs
20599
+ // already match, and a no-op flag flip would make the bib branch's
20600
+ // bail eat legitimate user-input events.
20601
+ const triggerNeedsSync = this.input.value !== nextValue;
20602
+ const bibNeedsSync = Boolean(this.inputInBib) && this.inputInBib.value !== nextValue;
20603
+ if (!triggerNeedsSync && !bibNeedsSync) {
20604
+ return;
20605
+ }
20606
+
20607
+ this._syncingDisplayValue = true;
20608
+
20609
+ const pending = [];
20610
+ if (triggerNeedsSync) {
20611
+ this.input.value = nextValue;
20612
+ pending.push(this.input.updateComplete);
20613
+ }
20614
+ if (bibNeedsSync) {
20615
+ this.inputInBib.value = nextValue;
20616
+ pending.push(this.inputInBib.updateComplete);
20617
+ }
20618
+ // finally — not a bare .then — so that an imask throw (see commit
20619
+ // d1857401c: imask can throw on credit-card format change) doesn't strand
20620
+ // _syncingDisplayValue=true and silently swallow every subsequent input.
20621
+ try {
20622
+ await Promise.all(pending);
20623
+ // imask reasserts otherwise, and handleBlur reverts to its stale value.
20624
+ if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
20625
+ this.input.maskInstance.updateValue();
20626
+ }
20627
+ if (bibNeedsSync && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20628
+ this.inputInBib.maskInstance.updateValue();
20629
+ }
20630
+ } finally {
20631
+ this._syncingDisplayValue = false;
20632
+ }
20633
+ }
20634
+
20663
20635
  /**
20664
20636
  * Processes hidden state of all menu options and determines if there are any available options not hidden.
20665
20637
  * @private
@@ -20668,6 +20640,13 @@ class AuroCombobox extends AuroElement {
20668
20640
  handleMenuOptions() {
20669
20641
  this.generateOptionsArray();
20670
20642
  this.availableOptions = [];
20643
+ // Single source of truth for the menu's filter/highlight token per call.
20644
+ // syncValuesAndStates re-writes the same value on exact-match keystrokes —
20645
+ // Lit's hasChanged makes that a no-op — and the prior duplicate set inside
20646
+ // handleTriggerInputValueChange is gone.
20647
+ if (this.menu) {
20648
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
20649
+ }
20671
20650
  this.updateFilter();
20672
20651
 
20673
20652
  // Set aria-setsize/aria-posinset on each visible option so screen readers
@@ -20679,17 +20658,14 @@ class AuroCombobox extends AuroElement {
20679
20658
  option.setAttribute('aria-posinset', index + 1);
20680
20659
  });
20681
20660
 
20682
- if (this.value && this.input.value && !this.menu.value) {
20683
- if (this.behavior === 'suggestion' && this.menu.options && this.menu.options.some((opt) => opt.value === this.value)) {
20684
- this.setMenuValue(this.value);
20685
- }
20686
-
20661
+ if (this.input.value && this.menu.options && this.menu.options.some((opt) => opt.value === this.input.value)) {
20662
+ this.setMenuValue(this.input.value);
20687
20663
  this.syncValuesAndStates();
20688
20664
  }
20689
20665
 
20690
- // Re-activate when optionActive is no longer visible, or when _index has
20691
- // been reset (e.g. by clearSelection in an async update) so that
20692
- // makeSelection() can find the correct option.
20666
+ // Re-activate when optionActive is not in the current scrollable viewport,
20667
+ // or when _index has been reset (e.g. by clearSelection in an async update)
20668
+ // so that makeSelection() can find the correct option.
20693
20669
  if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
20694
20670
  this.activateFirstEnabledAvailableOption();
20695
20671
  }
@@ -20762,18 +20738,6 @@ class AuroCombobox extends AuroElement {
20762
20738
  this.dropdownOpen = ev.detail.expanded;
20763
20739
  this.updateMenuShapeSize();
20764
20740
 
20765
- // Defer aria-expanded "true" so VoiceOver finishes character echo
20766
- // before announcing "expanded". Set "false" immediately on close.
20767
- clearTimeout(this._expandedTimeout);
20768
- if (this.dropdownOpen) {
20769
- const expandedDelay = 150;
20770
- this._expandedTimeout = setTimeout(() => {
20771
- this.triggerExpandedState = true;
20772
- }, expandedDelay);
20773
- } else {
20774
- this.triggerExpandedState = false;
20775
- }
20776
-
20777
20741
  // Clear aria-activedescendant when dropdown closes
20778
20742
  if (!this.dropdownOpen && this.input) {
20779
20743
  this.input.setActiveDescendant(null);
@@ -20823,28 +20787,25 @@ class AuroCombobox extends AuroElement {
20823
20787
 
20824
20788
  guardTouchPassthrough(this.menu);
20825
20789
 
20826
- // The dialog's showModal() steals focus from the trigger.
20827
- // A single rAF is enough for the dialog DOM to be painted and
20828
- // focusable, then doubleRaf finalizes the transition.
20790
+ // showModal() takes focus away from the trigger. Early focus once
20791
+ // the dialog has painted; the shared doubleRaf below is the fallback
20792
+ // for when the dialog needs an extra frame and also clears the
20793
+ // validation guard.
20829
20794
  requestAnimationFrame(() => {
20830
20795
  this.setInputFocus();
20831
20796
  });
20832
-
20833
- doubleRaf(() => {
20834
- this.setInputFocus();
20835
- this._inFullscreenTransition = false;
20836
- });
20837
- } else {
20838
- // Desktop popover-open: restore the trigger caret to end-of-text.
20839
- // Clicking the trigger lands on auro-input's floating <label for="…">
20840
- // overlay; Chrome resets the native input's selection to [0, 0] on
20841
- // label-focus before any JS runs. setInputFocus()'s non-fullscreen
20842
- // branch parks the caret at end. doubleRaf lets the dropdown layout
20843
- // settle first, matching the fullscreen branch timing.
20844
- doubleRaf(() => {
20845
- this.setInputFocus();
20846
- });
20847
20797
  }
20798
+ // else (desktop popover-open): Chrome resets the trigger caret to
20799
+ // [0, 0] when its floating <label for="…"> overlay receives focus.
20800
+ // The shared doubleRaf below parks the caret back at end-of-text
20801
+ // after the dropdown layout settles.
20802
+
20803
+ doubleRaf(() => {
20804
+ this.setInputFocus();
20805
+ if (this._inFullscreenTransition) {
20806
+ this._inFullscreenTransition = false;
20807
+ }
20808
+ });
20848
20809
  }
20849
20810
  });
20850
20811
 
@@ -20881,7 +20842,7 @@ class AuroCombobox extends AuroElement {
20881
20842
  this.dropdown.trigger.inert = false;
20882
20843
  }
20883
20844
 
20884
- setTimeout(() => {
20845
+ this._scheduleTimer(() => {
20885
20846
  this.setInputFocus();
20886
20847
  }, 0);
20887
20848
  });
@@ -20949,24 +20910,7 @@ class AuroCombobox extends AuroElement {
20949
20910
  nativeInput.setSelectionRange(len, len);
20950
20911
  }
20951
20912
  } else {
20952
- // Safety net for the strategy-change setTimeout: a viewport-crossing
20953
- // fullscreen→floating switch can close the dialog and leave focus on
20954
- // body. Every other Branch 2 callsite already has focus on the input
20955
- // via the focusin → this.focus() redirect (L1448-1452), so this is a
20956
- // no-op in the common case.
20957
- if (!this.input.componentHasFocus) {
20958
- this.input.focus();
20959
- }
20960
-
20961
- // Park the trigger native input's caret at end-of-text every time the
20962
- // popover opens. Chrome focuses the native via the floating <label for="…">
20963
- // overlay on click and resets selection to [0, 0]; no JS fires between
20964
- // mousedown and selectionchange, so we restore the caret here.
20965
- const triggerNativeInput = this.input.inputElement;
20966
- if (triggerNativeInput && triggerNativeInput.value) {
20967
- const len = triggerNativeInput.value.length;
20968
- triggerNativeInput.setSelectionRange(len, len);
20969
- }
20913
+ this.input.focus();
20970
20914
  }
20971
20915
  }
20972
20916
 
@@ -21042,7 +20986,7 @@ class AuroCombobox extends AuroElement {
21042
20986
 
21043
20987
  // racing condition on custom-combobox with custom-menu
21044
20988
  if (!this.menu) {
21045
- setTimeout(() => {
20989
+ this._scheduleTimer(() => {
21046
20990
  this.configureMenu();
21047
20991
  }, 0);
21048
20992
  return;
@@ -21072,7 +21016,7 @@ class AuroCombobox extends AuroElement {
21072
21016
  if (this.menu.optionSelected) {
21073
21017
  const selected = this.menu.optionSelected;
21074
21018
 
21075
- if (!this.optionSelected || this.optionSelected !== selected) {
21019
+ if (this.optionSelected !== selected) {
21076
21020
  this.optionSelected = selected;
21077
21021
  }
21078
21022
 
@@ -21085,7 +21029,7 @@ class AuroCombobox extends AuroElement {
21085
21029
  }
21086
21030
 
21087
21031
  // Update display
21088
- this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected) || this.menu.value);
21032
+ this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
21089
21033
 
21090
21034
  // Update match word for filtering
21091
21035
  const trimmedInput = normalizeFilterValue(this.input.value);
@@ -21097,17 +21041,24 @@ class AuroCombobox extends AuroElement {
21097
21041
  // Hide dropdown on selection (except during slot changes)
21098
21042
  if (evt.detail && evt.detail.source !== 'slotchange') {
21099
21043
  // do not close while typing in suggestion mode with no value selected, to allow freeform input
21100
- if (this.menu.value || this.behavior !== 'suggestion') {
21101
- this.hideBib();
21044
+ this.hideBib();
21045
+
21046
+ // Move focus to the clear button when the user makes a selection.
21047
+ if (!isEcho && this.menu.value !== undefined) {
21048
+ this.setClearBtnFocus();
21102
21049
  }
21103
21050
 
21104
21051
  // Announce the selection after the dropdown closes so it isn't
21105
21052
  // overridden by VoiceOver's "collapsed" announcement from aria-expanded.
21053
+ // Skip when there's no selected value (e.g. menu.clearSelection() from
21054
+ // the unmatched-value path), otherwise VoiceOver reads "undefined".
21106
21055
  const selectedValue = this.menu.value;
21107
- const announcementDelay = 300;
21108
- setTimeout(() => {
21109
- announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
21110
- }, announcementDelay);
21056
+ if (selectedValue) {
21057
+ const announcementDelay = 300;
21058
+ this._scheduleTimer(() => {
21059
+ announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
21060
+ }, announcementDelay);
21061
+ }
21111
21062
  }
21112
21063
 
21113
21064
  // Programmatic value syncs leave availableOptions stale because
@@ -21116,22 +21067,6 @@ class AuroCombobox extends AuroElement {
21116
21067
  // only — fresh user selections take the existing hideBib path.
21117
21068
  if (isEcho && this.menu.optionSelected) {
21118
21069
  this._programmaticFilterRefresh = true;
21119
- this.handleMenuOptions();
21120
- setTimeout(() => {
21121
- this._programmaticFilterRefresh = false;
21122
- }, 0);
21123
- }
21124
-
21125
- // base-input skips auto-validate when focus is inside its own shadow,
21126
- // which it is after setTriggerInputFocus — re-run so prior tooShort
21127
- // clears. processCreditCard reasserts errorMessage on every render.
21128
- if (!isEcho && this.menu.optionSelected && this.input.validate) {
21129
- this.input.updateComplete.then(() => {
21130
- this.input.validate(true);
21131
- if (this.input.validity === 'valid') {
21132
- this.input.errorMessage = '';
21133
- }
21134
- });
21135
21070
  }
21136
21071
  });
21137
21072
 
@@ -21144,6 +21079,14 @@ class AuroCombobox extends AuroElement {
21144
21079
  // stale option. Safe from re-entrancy because any resulting
21145
21080
  // input.value changes dispatch isProgrammatic events.
21146
21081
  this.menu.addEventListener('auroMenu-selectValueFailure', () => {
21082
+ // Announce the rejection BEFORE we clear `this.value` so the live
21083
+ // region carries the attempted value — without this the bib closes
21084
+ // silently and screen-reader users get no signal that their request
21085
+ // (e.g. a direct setMenuValue() call) was dropped.
21086
+ const attemptedValue = this.value;
21087
+ if (attemptedValue) {
21088
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${attemptedValue}`);
21089
+ }
21147
21090
  this.value = undefined;
21148
21091
  this.optionSelected = undefined;
21149
21092
  });
@@ -21155,10 +21098,13 @@ class AuroCombobox extends AuroElement {
21155
21098
  this.input.setActiveDescendant(this.optionActive);
21156
21099
  }
21157
21100
 
21158
- // Announce the active option for screen readers including position,
21159
- // since shadow DOM boundaries prevent native reading of
21160
- // aria-setsize/aria-posinset via aria-activedescendant.
21161
- if (this.optionActive) {
21101
+ // In fullscreen mode the menu sits inside a nested <dialog> shadow root,
21102
+ // and aria-activedescendant references across that boundary are lost —
21103
+ // VoiceOver/NVDA don't read the active option natively, so we mirror it
21104
+ // into the polite live region. In popover mode aria-activedescendant on
21105
+ // the trigger input is read natively; double-announcing would flood the
21106
+ // queue on arrow-key repeat.
21107
+ if (this.optionActive && this.dropdown.isBibFullscreen) {
21162
21108
  const optionText = this.optionActive.textContent.trim();
21163
21109
  const selectedState = this.optionActive.hasAttribute('selected') ? ', selected' : ', not selected';
21164
21110
  const optionIndex = this.availableOptions.indexOf(this.optionActive) + 1;
@@ -21191,7 +21137,12 @@ class AuroCombobox extends AuroElement {
21191
21137
  * Validate every time we remove focus from the combo box.
21192
21138
  */
21193
21139
  this.addEventListener('focusout', () => {
21194
- if (!this.componentHasFocus && !this._inFullscreenTransition) {
21140
+ // Skip while the dropdown is open — focus transits out briefly on
21141
+ // mousedown of a menu option (popover top-layer breaks :focus-within),
21142
+ // and validating against the pre-selection value flashes a stale error
21143
+ // between mousedown and mouseup. The next focusout fires after the
21144
+ // dropdown closes and validates against the post-selection value.
21145
+ if (!this.componentHasFocus && !this._inFullscreenTransition && !this.dropdownOpen) {
21195
21146
  this.validate();
21196
21147
  }
21197
21148
  });
@@ -21254,49 +21205,40 @@ class AuroCombobox extends AuroElement {
21254
21205
  if (this._syncingDisplayValue) {
21255
21206
  return;
21256
21207
  }
21257
- this._syncingBibValue = true;
21258
- this.input.value = this.inputInBib.value;
21259
- this.input.updateComplete.then(() => {
21260
- this._syncingBibValue = false;
21261
- });
21262
21208
 
21263
- // Run filtering inline — the re-entrant event won't reach this code.
21264
- this.menu.matchWord = normalizeFilterValue(this.inputInBib.value);
21265
- this.optionActive = null;
21266
-
21267
- // In suggestion mode, keep the combobox value in sync with the typed
21268
- // text so that freeform values are captured (mirroring the non-fullscreen
21269
- // path). Clear the selection when the input is emptied.
21270
- if (this.behavior === 'suggestion') {
21271
- this.value = this.inputInBib.value || undefined;
21209
+ // Filtering runs via re-entrance: writing this.input.value below
21210
+ // dispatches an 'input' event that the trigger's listener routes to
21211
+ // handleTriggerInputValueChange, which refreshes the menu filter.
21212
+ if (this.input.value !== this.inputInBib.value) {
21213
+ this._syncingBibValue = true;
21214
+ this.input.value = this.inputInBib.value;
21215
+ this.input.updateComplete.then(() => {
21216
+ this._syncingBibValue = false;
21217
+ });
21272
21218
  }
21273
21219
 
21274
- this.handleMenuOptions();
21275
21220
  this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21276
- return;
21221
+ } else if (event.target === this.input) {
21222
+
21223
+ // Also sync the native input immediately so keystrokes arriving
21224
+ // before Lit's async update cycle (e.g. rapid Backspaces during a
21225
+ // fullscreen transition) operate on the correct content.
21226
+ // Skip the next programmatic input event to prevent the patched setter
21227
+ // from re-entering handleInputValueChange via the bib path.
21228
+ const bibNativeInput = this.inputInBib.inputElement;
21229
+ if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21230
+ this.inputInBib.skipNextProgrammaticInputEvent = true;
21231
+ bibNativeInput.value = this.input.value || '';
21232
+ }
21277
21233
  }
21278
21234
 
21235
+ this.value = this.input.value;
21236
+
21279
21237
  // Ignore re-entrant input events caused by programmatic value sets.
21280
21238
  if (this._syncingBibValue || this._syncingDisplayValue) {
21281
21239
  return;
21282
21240
  }
21283
21241
 
21284
- this.inputInBib.value = this.input.value;
21285
-
21286
- // Also sync the native input immediately so keystrokes arriving
21287
- // before Lit's async update cycle (e.g. rapid Backspaces during a
21288
- // fullscreen transition) operate on the correct content.
21289
- // Skip the next programmatic input event to prevent the patched setter
21290
- // from re-entering handleInputValueChange via the bib path.
21291
- const bibNativeInput = this.inputInBib.inputElement;
21292
- if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21293
- this.inputInBib.skipNextProgrammaticInputEvent = true;
21294
- bibNativeInput.value = this.input.value || '';
21295
- }
21296
-
21297
- this.menu.matchWord = normalizeFilterValue(this.input.value);
21298
- this.optionActive = null;
21299
-
21300
21242
  if (this.behavior === 'suggestion') {
21301
21243
  this.value = this.input.value;
21302
21244
  }
@@ -21304,7 +21246,6 @@ class AuroCombobox extends AuroElement {
21304
21246
  if (!this.input.value && !this._clearing) {
21305
21247
  this.clear();
21306
21248
  }
21307
- this.handleMenuOptions();
21308
21249
 
21309
21250
  // Validate only if the value was set programmatically (not during user
21310
21251
  // interaction). In fullscreen dialog mode, componentHasFocus returns false
@@ -21314,34 +21255,44 @@ class AuroCombobox extends AuroElement {
21314
21255
  this.validate();
21315
21256
  }
21316
21257
 
21317
- if (this.input.value && this.input.value.length === 0) {
21318
- // Hide menu if value is empty, otherwise show if there are available suggestions
21319
- this.hideBib();
21320
- } else if (this.menu.loading) {
21321
- // if input has value but menu is loading, show bib immediately
21322
- this.showBib();
21323
- } else if (this.availableOptions.length === 0 && !this.dropdown.isBibFullscreen) {
21324
- // Force dropdown bib to hide if input value has no matching suggestions
21325
- this.hideBib();
21258
+ this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21259
+ }
21260
+
21261
+ /**
21262
+ * Handles input value changes originating from the trigger input.
21263
+ * Refreshes menu options and filtering, delegates to handleInputValueChange
21264
+ * for value synchronization, and manages fullscreen bib focus.
21265
+ * @private
21266
+ * @param {Event} event - The input event from the trigger input element.
21267
+ * @returns {void}
21268
+ */
21269
+ handleTriggerInputValueChange(event) {
21270
+ // 'input' fires for every user-initiated value change — typing, paste,
21271
+ // IME composition end, dead-key composition, drag-drop. Flip _userTyped
21272
+ // here so updated('availableOptions') auto-opens the bib for sources
21273
+ // that keydown alone misses: paste fires no keydown, IME uses
21274
+ // key='Process', and dead keys produce multi-char keys (all bypass the
21275
+ // prior keydown.key.length===1 gate). Skip programmatic syncs.
21276
+ if (!this._syncingDisplayValue && !this._syncingBibValue && !this._programmaticFilterRefresh) {
21277
+ this._userTyped = true;
21326
21278
  }
21327
21279
 
21328
- // iOS virtual keyboard retention: when in fullscreen mode, ensure the
21329
- // dialog opens and the bib input is focused synchronously within the
21330
- // input event (user gesture) chain. Without this, Lit's async update
21331
- // cycle delays showModal() past the user activation window, causing
21332
- // iOS Safari to dismiss the virtual keyboard when the fullscreen
21333
- // dialog opens — the user then has to tap the input again to resume
21334
- // typing.
21335
- if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
21336
- if (!this.dropdown.isPopoverVisible) {
21337
- this.showBib();
21338
- }
21339
- if (this.dropdown.isPopoverVisible) {
21340
- this.setInputFocus();
21341
- }
21280
+ this.handleMenuOptions();
21281
+ this.optionActive = null;
21282
+
21283
+ if (this.value === this.input.value) {
21284
+ return;
21342
21285
  }
21343
21286
 
21344
- this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21287
+ this.handleInputValueChange(event);
21288
+
21289
+ if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
21290
+ this.setInputFocus();
21291
+ }
21292
+
21293
+ if (this._programmaticFilterRefresh) {
21294
+ this._programmaticFilterRefresh = false;
21295
+ }
21345
21296
  }
21346
21297
 
21347
21298
  /**
@@ -21409,6 +21360,11 @@ class AuroCombobox extends AuroElement {
21409
21360
  disconnectedCallback() {
21410
21361
  super.disconnectedCallback();
21411
21362
  this._inFullscreenTransition = false;
21363
+ // Cancel any outstanding timers so detached callbacks don't fire on
21364
+ // disposed DOM — most are no-ops, but configureMenu's racing-condition
21365
+ // retry would otherwise keep rescheduling itself indefinitely.
21366
+ this._pendingTimers.forEach((id) => clearTimeout(id));
21367
+ this._pendingTimers.clear();
21412
21368
  }
21413
21369
 
21414
21370
  firstUpdated() {
@@ -21450,7 +21406,7 @@ class AuroCombobox extends AuroElement {
21450
21406
  this.menu.value = value;
21451
21407
  // Backup clear: if menu.value === menu.optionSelected.value already, the
21452
21408
  // listener won't fire and the flag would swallow the next user click.
21453
- setTimeout(() => {
21409
+ this._scheduleTimer(() => {
21454
21410
  this._pendingMenuValueSync = false;
21455
21411
  }, 0);
21456
21412
  }
@@ -21467,15 +21423,7 @@ class AuroCombobox extends AuroElement {
21467
21423
  this.menu.value = undefined;
21468
21424
  this.validation.reset(this);
21469
21425
  this.touched = false;
21470
- // Force validity back to the cleared state. validation.reset() calls
21471
- // validate() at the end, and (post-credit-card-fix) the trigger input's
21472
- // residual validity may still be copied into the combobox by the
21473
- // auroInputElements loop. Explicitly clear here so reset() actually
21474
- // leaves the component in a "no validity" state.
21475
21426
  this.validity = undefined;
21476
- this.errorMessage = '';
21477
- this.input.validity = undefined;
21478
- this.input.errorMessage = '';
21479
21427
  }
21480
21428
 
21481
21429
  /**
@@ -21493,6 +21441,17 @@ class AuroCombobox extends AuroElement {
21493
21441
  this.optionSelected = undefined;
21494
21442
  this.value = undefined;
21495
21443
 
21444
+ // Clear the appended displayValue clone in the trigger if present.
21445
+ // :not(slot) excludes the template's <slot name="displayValue"
21446
+ // slot="displayValue"> forwarder (line 1816), which also has
21447
+ // slot="displayValue" and would otherwise be matched first and removed,
21448
+ // permanently breaking the consumer-provided displayValue slot.
21449
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
21450
+
21451
+ if (displayValueInTrigger) {
21452
+ displayValueInTrigger.remove();
21453
+ }
21454
+
21496
21455
  if (this.input.value) {
21497
21456
  this.input.clear();
21498
21457
  }
@@ -21516,81 +21475,50 @@ class AuroCombobox extends AuroElement {
21516
21475
  }
21517
21476
 
21518
21477
  updated(changedProperties) {
21519
- // After the component is ready, send direct value changes to auro-menu.
21478
+ // After the component is ready, propagate direct changes down to child components.
21520
21479
  if (changedProperties.has('value')) {
21521
- if (this.value && this.value.length > 0) {
21522
- this.hasValue = true;
21523
- } else {
21524
- this.hasValue = false;
21480
+ // Only flag programmatic refreshes — user-typed value changes must not
21481
+ // suppress the availableOptions branch's showBib(). Firefox batches
21482
+ // 'value' and 'availableOptions' into the same updated() call, so
21483
+ // setting the flag unconditionally here masks the user-typed open path.
21484
+ if (!this._userTyped) {
21485
+ this._programmaticFilterRefresh = true;
21525
21486
  }
21526
21487
 
21527
- if (this.hasValue && !this.input.value && (!this.menu.options || this.menu.options.length === 0)) {
21528
- this.input.value = this.value;
21529
- }
21488
+ if (this.input.value !== this.value) {
21489
+ // Clear menu.value AND menu.optionSelected together. Clearing only
21490
+ // menu.value leaves the previously-selected option element pinned
21491
+ // as menu.optionSelected; a later auroMenu-selectedOption event
21492
+ // would then write its stale .value back into combobox.value
21493
+ // (e.g. Tab-after-Backspace re-selecting the prior option).
21494
+ if (this.menu.value || this.menu.optionSelected) {
21495
+ this.menu.clearSelection();
21496
+ }
21530
21497
 
21531
- // Sync menu.value only when an option actually matches; otherwise clear it.
21532
- if (this.menu.options && this.menu.options.length > 0) {
21533
- if (this.menu.options.some((opt) => opt.value === this.value)) {
21534
- this.setMenuValue(this.value);
21535
- } else if (this.behavior === 'filter') {
21536
- // In filter mode, freeform values aren't allowed. Push the unmatched
21537
- // value through setMenuValue so menu dispatches auroMenu-selectValueFailure
21538
- // and the listener at line 1206 clears combobox.value (mirrors select's
21539
- // pattern). Bypassing setMenuValue here would skip the failure cascade.
21540
- this.setMenuValue(this.value);
21541
- } else {
21542
- if (this.menu.value) {
21543
- this.menu.value = undefined;
21544
- }
21545
- // Sync the display for programmatic freeform value changes (e.g. the
21546
- // swap-values pattern). Guard with _syncingDisplayValue so that the
21547
- // re-entrant input event from base-input.notifyValueChanged() is
21548
- // ignored by handleInputValueChange. Skip the sync when input already
21549
- // matches to avoid spurious events during the normal typing path.
21550
- const nextValue = this.value || '';
21551
- const triggerNeedsSync = this.input.value !== nextValue;
21552
- const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
21553
- if (triggerNeedsSync || bibNeedsSync) {
21554
- this._syncingDisplayValue = true;
21555
- if (triggerNeedsSync) {
21556
- this.input.value = nextValue;
21557
- }
21558
- if (bibNeedsSync) {
21559
- this.inputInBib.value = nextValue;
21560
- }
21561
- const pending = [];
21562
- if (triggerNeedsSync) {
21563
- pending.push(this.input.updateComplete);
21564
- }
21565
- if (bibNeedsSync) {
21566
- pending.push(this.inputInBib.updateComplete);
21567
- }
21568
- Promise.all(pending).then(() => {
21569
- if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
21570
- this.input.maskInstance.updateValue();
21571
- }
21572
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
21573
- this.inputInBib.maskInstance.updateValue();
21574
- }
21575
- this._syncingDisplayValue = false;
21576
- // handleInputValueChange bailed on the flag — refresh the filter.
21577
- this._programmaticFilterRefresh = true;
21578
- this.handleMenuOptions();
21579
- setTimeout(() => {
21580
- this._programmaticFilterRefresh = false;
21581
- }, 0);
21582
- });
21583
- }
21498
+ if (!this.persistInput) {
21499
+ this.syncInputValuesAcrossTriggerAndBib(this.value || '');
21500
+ }
21501
+
21502
+ // Programmatic value with no matching option: updateFilter will close
21503
+ // the bib silently (see line 648 no noMatchOption + 0 results
21504
+ // hides). Announce so screen-reader users hear the request was
21505
+ // dropped. Gated on `input.value !== this.value` so this never fires
21506
+ // for user typing that path always reconciles input.value to
21507
+ // this.value before updated() runs.
21508
+ if (
21509
+ this.value &&
21510
+ this.menu &&
21511
+ this.menu.options &&
21512
+ this.menu.options.length > 0 &&
21513
+ !this.menu.options.some((opt) => opt.value === this.value)
21514
+ ) {
21515
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${this.value}`);
21584
21516
  }
21585
21517
  }
21518
+
21586
21519
  if (!this.value) {
21587
21520
  this.clear();
21588
21521
  }
21589
- if (this.value && !this.componentHasFocus) {
21590
- // If the value got set programmatically make sure we hide the bib
21591
- // when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
21592
- this.hideBib();
21593
- }
21594
21522
 
21595
21523
  // Sync the input and match word, but don't directly set menu.value again
21596
21524
  if (this.menu) {
@@ -21603,7 +21531,7 @@ class AuroCombobox extends AuroElement {
21603
21531
  composed: true,
21604
21532
  detail: {
21605
21533
  optionSelected: this.menu.optionSelected,
21606
- value: this.menu.value
21534
+ value: this.value
21607
21535
  }
21608
21536
  }));
21609
21537
 
@@ -21626,16 +21554,30 @@ class AuroCombobox extends AuroElement {
21626
21554
  // from a programmatic filter refresh after a value swap — the user
21627
21555
  // didn't interact, so we shouldn't auto-open the bib (especially for
21628
21556
  // the no-match path where the existing condition unconditionally fires).
21629
- if (this._programmaticFilterRefresh) {
21630
- this._programmaticFilterRefresh = false;
21631
- } else if ((this.availableOptions.length > 0 && (this.componentHasFocus || this.dropdownOpen)) || (this.menu && this.menu.loading) || (this.availableOptions.length === 0 && this.noMatchOption)) {
21632
- this.showBib();
21557
+ if ((this.menu && !this._programmaticFilterRefresh)) {
21558
+ if (
21559
+ this.availableOptions.length > 0 ||
21560
+ this.menu.loading ||
21561
+ this.noMatchOption
21562
+ ) {
21563
+ if (this._userTyped) {
21564
+ if (!this.dropdownOpen) {
21565
+ this.showBib();
21566
+ }
21567
+ this._userTyped = false;
21568
+ }
21569
+ }
21570
+
21633
21571
  if (!this.availableOptions.includes(this.menu.optionActive)) {
21634
21572
  this.activateFirstEnabledAvailableOption();
21635
21573
  }
21636
- } else if (this.dropdown && this.dropdown.isPopoverVisible) {
21574
+ } else if (!this.dropdown.isBibFullscreen) {
21637
21575
  this.hideBib();
21638
21576
  }
21577
+
21578
+ if (this._programmaticFilterRefresh) {
21579
+ this._programmaticFilterRefresh = false;
21580
+ }
21639
21581
  }
21640
21582
 
21641
21583
  if (changedProperties.has('error')) {
@@ -21777,10 +21719,10 @@ class AuroCombobox extends AuroElement {
21777
21719
  shape="${this.shape}"
21778
21720
  size="${this.size}">
21779
21721
  <${this.inputTag}
21780
- @input="${this.handleInputValueChange}"
21722
+ @input="${this.handleTriggerInputValueChange}"
21781
21723
  appearance="${this.onDark ? 'inverse' : this.appearance}"
21782
21724
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
21783
- .a11yExpanded="${this.triggerExpandedState}"
21725
+ .a11yExpanded="${this.dropdownOpen}"
21784
21726
  .a11yControls="${this.dropdownId}"
21785
21727
  .autocomplete="${this.autocomplete}"
21786
21728
  .inputmode="${this.inputmode}"