@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
@@ -485,15 +485,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
485
485
  );
486
486
  }
487
487
 
488
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
489
- if (this.auroInputElements?.length === 2) {
490
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
488
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
489
+
490
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
491
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
492
+ // field (datepicker is the intended consumer — start/end are independently required).
493
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
494
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
491
495
  hasValue = false;
492
496
  }
493
497
  }
494
498
 
495
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
496
-
497
499
  if (isCombobox) {
498
500
 
499
501
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -810,6 +812,8 @@ function navigateArrow(component, direction, options = {}) {
810
812
  }
811
813
  }
812
814
 
815
+ /* eslint-disable no-underscore-dangle */
816
+
813
817
  /**
814
818
  * Returns the clear button element from the active input's shadow
815
819
  * DOM, if available.
@@ -845,11 +849,9 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
845
849
  * @param {Object} menu - The menu component.
846
850
  */
847
851
  function reconcileMenuIndex(menu) {
848
- // eslint-disable-next-line no-underscore-dangle
849
852
  if (menu._index < 0 && menu.optionActive && menu.items) {
850
853
  const idx = menu.items.indexOf(menu.optionActive);
851
854
  if (idx >= 0) {
852
- // eslint-disable-next-line no-underscore-dangle
853
855
  menu._index = idx;
854
856
  }
855
857
  }
@@ -868,11 +870,7 @@ const comboboxKeyboardStrategy = {
868
870
 
869
871
  // navigate if bib is open otherwise open it
870
872
  if (component.dropdown.isPopoverVisible) {
871
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
872
- component.activateLastEnabledAvailableOption();
873
- } else {
874
- navigateArrow(component, 'down');
875
- }
873
+ navigateArrow(component, 'down');
876
874
  } else {
877
875
  component.showBib();
878
876
  }
@@ -891,11 +889,7 @@ const comboboxKeyboardStrategy = {
891
889
 
892
890
  // navigate if bib is open otherwise open it
893
891
  if (component.dropdown.isPopoverVisible) {
894
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
895
- component.activateFirstEnabledAvailableOption();
896
- } else {
897
- navigateArrow(component, 'up');
898
- }
892
+ navigateArrow(component, 'up');
899
893
  } else {
900
894
  component.showBib();
901
895
  }
@@ -911,13 +905,18 @@ const comboboxKeyboardStrategy = {
911
905
  },
912
906
 
913
907
  Enter(component, evt, ctx) {
908
+ // Forms should not submit on Enter from a combobox, regardless of which
909
+ // child element (input, clear button, menu) is focused.
910
+ evt.stopPropagation();
911
+
914
912
  if (isClearBtnFocused(ctx)) {
915
- // If the clear button has focus, let the browser activate it normally.
916
- // stopPropagation prevents parent containers (e.g., forms) from treating
917
- // Enter as a submit, but we must NOT call preventDefault — that would
918
- // block the browser's built-in "Enter activates focused button" behavior.
919
- evt.stopPropagation();
920
- } else if (ctx.isExpanded && component.menu.optionActive) {
913
+ // Let the browser dispatch Enter to the focused clear button so its
914
+ // built-in activation fires and clears the selection. Do NOT call
915
+ // preventDefault — that would block the activation.
916
+ return;
917
+ }
918
+
919
+ if (ctx.isExpanded && component.menu.optionActive) {
921
920
  reconcileMenuIndex(component.menu);
922
921
  component.menu.makeSelection();
923
922
 
@@ -926,14 +925,8 @@ const comboboxKeyboardStrategy = {
926
925
  }
927
926
 
928
927
  evt.preventDefault();
929
- evt.stopPropagation();
930
928
  } else {
931
- // Prevent the keypress from bubbling to parent containers (e.g., forms)
932
- // which could interpret Enter as a submit or trigger other unintended behavior.
933
- // This is safe because showBib() opens the dialog programmatically,
934
- // not via event propagation.
935
929
  evt.preventDefault();
936
- evt.stopPropagation();
937
930
  component.showBib();
938
931
  }
939
932
  },
@@ -978,7 +971,7 @@ const comboboxKeyboardStrategy = {
978
971
  component.setClearBtnFocus();
979
972
  }
980
973
  }
981
- },
974
+ }
982
975
  };
983
976
 
984
977
  /**
@@ -4873,7 +4866,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
4873
4866
  }
4874
4867
  };
4875
4868
 
4876
- var formkitVersion$2 = '202606192121';
4869
+ var formkitVersion$2 = '202607010100';
4877
4870
 
4878
4871
  let AuroElement$2 = class AuroElement extends i$3 {
4879
4872
  static get properties() {
@@ -5824,6 +5817,14 @@ class AuroDropdown extends AuroElement$2 {
5824
5817
  };
5825
5818
  this.addEventListener('keydown', this._bibTabHandler);
5826
5819
 
5820
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
5821
+ // desktopModal trap owns focus management. Without this, the very
5822
+ // first focus move into the bib (from the RAF below) triggers
5823
+ // handleFocusLoss → hideBib, tearing down the trap before the
5824
+ // user can press Tab.
5825
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
5826
+ this.noHideOnThisFocusLoss = true;
5827
+
5827
5828
  // Move initial focus into the bib content, matching FocusTrap behavior
5828
5829
  requestAnimationFrame(() => {
5829
5830
  const focusables = getFocusableElements(this.bibContent);
@@ -6137,6 +6138,7 @@ class AuroDropdown extends AuroElement$2 {
6137
6138
  role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
6138
6139
  aria-expanded="${o(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
6139
6140
  aria-controls="${o(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.dropdownId)}"
6141
+ aria-haspopup="${o(this.a11yRole === 'combobox' && !this.triggerContentFocusable ? 'listbox' : undefined)}"
6140
6142
  aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
6141
6143
  aria-disabled="${o(this.disabled ? 'true' : undefined)}"
6142
6144
  @focusin="${this.handleFocusin}"
@@ -10646,15 +10648,17 @@ class AuroFormValidation {
10646
10648
  );
10647
10649
  }
10648
10650
 
10649
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
10650
- if (this.auroInputElements?.length === 2) {
10651
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
10651
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10652
+
10653
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
10654
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
10655
+ // field (datepicker is the intended consumer — start/end are independently required).
10656
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
10657
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
10652
10658
  hasValue = false;
10653
10659
  }
10654
10660
  }
10655
10661
 
10656
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10657
-
10658
10662
  if (isCombobox) {
10659
10663
 
10660
10664
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -16590,10 +16594,18 @@ class AuroInputUtilities {
16590
16594
  * @returns {string | undefined}
16591
16595
  */
16592
16596
  toFormattedValue(valueObject, format) {
16597
+ // Exposed publicly via `AuroInputUtil.toFormattedValue`, so a missing
16598
+ // `format` argument has to fail gracefully rather than throw on
16599
+ // `format.toLowerCase()`. Treat any falsy value (undefined, null, '')
16600
+ // as "no format → no display value".
16601
+ if (!format) {
16602
+ return undefined;
16603
+ }
16593
16604
  const normalizedFormat = format.toLowerCase();
16594
16605
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
16595
16606
 
16596
16607
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
16608
+ console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
16597
16609
  return undefined;
16598
16610
  }
16599
16611
 
@@ -16740,79 +16752,19 @@ class BaseInput extends AuroElement$1 {
16740
16752
  constructor() {
16741
16753
  super();
16742
16754
 
16743
- this.appearance = "default";
16744
- this.disabled = false;
16745
- this.layout = 'classic';
16746
- this.locale = 'en-US';
16747
- this.max = undefined;
16748
- this._maxObject = undefined;
16749
- this.maxLength = undefined;
16750
- this.min = undefined;
16751
- this._minObject = undefined;
16752
- this.minLength = undefined;
16753
- this.required = false;
16754
- this.onDark = false;
16755
- this.setCustomValidityForType = undefined;
16756
- this.size = 'lg';
16757
- this.shape = 'classic';
16758
- this.value = undefined;
16759
- this._valueObject = undefined;
16760
-
16761
- this._initializePrivateDefaults();
16762
- }
16763
-
16764
- /**
16765
- * Internal Defaults.
16766
- * @private
16767
- * @returns {void}
16768
- */
16769
- _initializePrivateDefaults() {
16755
+ // Single-source initialization. Alphabetized so duplicate or stale
16756
+ // defaults are immediately obvious on a diff. Every field is assigned
16757
+ // exactly once here (previously the constructor + the old
16758
+ // `_initializePrivateDefaults` overlapped — both wrote ~14 of the same
16759
+ // fields and double-allocated `util` and `validation`, discarding the
16760
+ // first instance). `validation` is now allocated exactly once; `util`
16761
+ // is seeded here with an en-US default and then rebuilt in
16762
+ // `connectedCallback` once the consumer-resolved locale is available,
16763
+ // so a parent (datepicker/combobox) calling `validate()` synchronously
16764
+ // during its own update cycle sees a populated util instance.
16770
16765
  this.activeLabel = false;
16771
- this.appearance = "default";
16772
- this.icon = false;
16773
- this.disabled = false;
16774
- this.dvInputOnly = false;
16775
- this.hideLabelVisually = false;
16776
- this.max = undefined;
16777
- this.maxLength = undefined;
16778
- this.min = undefined;
16779
- this.minLength = undefined;
16780
- this.noValidate = false;
16781
- this.onDark = false;
16782
- this.required = false;
16783
- this.setCustomValidityForType = undefined;
16784
-
16785
- // Used for storing raw values returned from input mask.
16786
- this._rawMaskValue = undefined;
16787
-
16788
- /**
16789
- * @private
16790
- */
16791
- this.layout = 'classic';
16792
-
16793
- /**
16794
- * @private
16795
- */
16796
- this.shape = 'classic';
16797
-
16798
- /**
16799
- * @private
16800
- */
16801
- this.size = 'lg';
16802
-
16803
- this.touched = false;
16804
- this.util = new AuroInputUtilities({
16805
- locale: "en-US",
16806
- format: this.format
16807
- });
16808
- this.validation = new AuroFormValidation();
16809
- this.inputIconName = undefined;
16810
- this.showPassword = false;
16811
- this.validationCCLength = undefined;
16812
- this.hasValue = false;
16813
- this.label = 'Input label is undefined';
16814
-
16815
16766
 
16767
+ /** @private */
16816
16768
  this.allowedInputTypes = [
16817
16769
  "text",
16818
16770
  "number",
@@ -16821,7 +16773,9 @@ class BaseInput extends AuroElement$1 {
16821
16773
  "credit-card",
16822
16774
  "tel"
16823
16775
  ];
16824
- this.icon = false;
16776
+ this.appearance = "default";
16777
+
16778
+ /** @private */
16825
16779
  this.dateFormatMap = {
16826
16780
  'mm/dd/yyyy': 'dateMMDDYYYY',
16827
16781
  'dd/mm/yyyy': 'dateDDMMYYYY',
@@ -16838,27 +16792,62 @@ class BaseInput extends AuroElement$1 {
16838
16792
  'dd/mm': 'dateDDMM',
16839
16793
  'mm/dd': 'dateMMDD'
16840
16794
  };
16795
+ this.disabled = false;
16796
+
16797
+ /** @private */
16841
16798
  this.domHandler = new DomHandler();
16842
16799
  this.dvInputOnly = false;
16843
16800
  this.hasValue = false;
16801
+ this.hideLabelVisually = false;
16802
+ this.icon = false;
16803
+
16804
+ /** @private */
16844
16805
  this.inputIconName = undefined;
16806
+
16807
+ /** @private */
16845
16808
  this.label = 'Input label is undefined';
16809
+ this.layout = 'classic';
16810
+ this.locale = 'en-US';
16811
+ this.max = undefined;
16812
+ this.maxLength = undefined;
16813
+ this.min = undefined;
16814
+ this.minLength = undefined;
16846
16815
  this.noValidate = false;
16847
- this._rawMaskValue = undefined; // Used for storing raw values returned from input mask.
16816
+ this.onDark = false;
16817
+ // Raw values returned from the input mask before model normalization.
16818
+ this._rawMaskValue = undefined;
16819
+ this.required = false;
16820
+ this.setCustomValidityForType = undefined;
16821
+ // Credit Card is intentionally excluded — its mask manages the cursor
16822
+ // itself, and listing it here caused cursor placement issues in Safari.
16823
+ /** @private */
16848
16824
  this.setSelectionInputTypes = [
16849
16825
  "text",
16850
16826
  "password",
16851
16827
  "email"
16852
- ]; // Credit Card is not included as this caused cursor placement issues in Safari.
16828
+ ];
16829
+ this.shape = 'classic';
16830
+
16831
+ /** @private */
16853
16832
  this.showPassword = false;
16833
+ this.size = 'lg';
16854
16834
  this.touched = false;
16835
+
16836
+ /** @private */
16855
16837
  this.uniqueId = new UniqueId().create();
16838
+
16839
+ /** @private */
16856
16840
  this.util = new AuroInputUtilities({
16857
16841
  locale: this.locale,
16858
16842
  format: this.format
16859
16843
  });
16844
+
16845
+ /** @private */
16860
16846
  this.validation = new AuroFormValidation();
16847
+
16848
+ /** @private */
16861
16849
  this.validationCCLength = undefined;
16850
+ this.value = undefined;
16862
16851
  }
16863
16852
 
16864
16853
  // function to define props used within the scope of this component
@@ -17296,7 +17285,7 @@ class BaseInput extends AuroElement$1 {
17296
17285
  * @returns {Date|undefined}
17297
17286
  */
17298
17287
  get valueObject() {
17299
- return this._valueObject || this._computeDateObjectFallback(this.value);
17288
+ return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
17300
17289
  }
17301
17290
 
17302
17291
  /**
@@ -17304,7 +17293,7 @@ class BaseInput extends AuroElement$1 {
17304
17293
  * @returns {Date|undefined}
17305
17294
  */
17306
17295
  get minObject() {
17307
- return this._minObject || this._computeDateObjectFallback(this.min);
17296
+ return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
17308
17297
  }
17309
17298
 
17310
17299
  /**
@@ -17312,50 +17301,7 @@ class BaseInput extends AuroElement$1 {
17312
17301
  * @returns {Date|undefined}
17313
17302
  */
17314
17303
  get maxObject() {
17315
- return this._maxObject || this._computeDateObjectFallback(this.max);
17316
- }
17317
-
17318
- /**
17319
- * Parses a date string into a Date object when the corresponding `_*Object`
17320
- * field hasn't been synced yet by `updated()`. Returns undefined when the
17321
- * input type/format isn't a full date or the string is not a valid date.
17322
- *
17323
- * Why this exists: a parent (datepicker) can call `inputN.validate()` from
17324
- * inside its own `updated()` before this input's `updated()` has run
17325
- * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
17326
- * and range checks would otherwise silently no-op (flipping the result to
17327
- * `valid` or `patternMismatch`).
17328
- * @private
17329
- * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
17330
- * @returns {Date|undefined}
17331
- */
17332
- _computeDateObjectFallback(dateStr) {
17333
- if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
17334
- return undefined;
17335
- }
17336
- if (!dateFormatter.isValidDate(dateStr)) {
17337
- return undefined;
17338
- }
17339
- return dateFormatter.stringToDateInstance(dateStr);
17340
- }
17341
-
17342
- /**
17343
- * Internal setter for readonly date object properties.
17344
- * @private
17345
- * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
17346
- * @param {Date|undefined} propertyValue - Value to assign.
17347
- * @returns {void}
17348
- */
17349
- setDateObjectProperty(propertyName, propertyValue) {
17350
- const internalPropertyName = `_${propertyName}`;
17351
- const previousValue = this[internalPropertyName];
17352
-
17353
- if (previousValue === propertyValue) {
17354
- return;
17355
- }
17356
-
17357
- this[internalPropertyName] = propertyValue;
17358
- this.requestUpdate(propertyName, previousValue);
17304
+ return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
17359
17305
  }
17360
17306
 
17361
17307
  connectedCallback() {
@@ -17384,7 +17330,6 @@ class BaseInput extends AuroElement$1 {
17384
17330
  format: this.format
17385
17331
  });
17386
17332
  this.configureDataForType();
17387
- this.syncDateValues();
17388
17333
  }
17389
17334
 
17390
17335
  disconnectedCallback() {
@@ -17404,12 +17349,17 @@ class BaseInput extends AuroElement$1 {
17404
17349
  this.wrapperElement.addEventListener('click', this.handleClick);
17405
17350
  }
17406
17351
 
17407
- // add attribute for query selectors when auro-input is registered under a custom name
17408
- if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
17409
- this.setAttribute('auro-input', '');
17410
- }
17411
17352
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
17412
17353
 
17354
+ // Normalize the format token to lowercase so case-mixed values supplied
17355
+ // via attribute (e.g. `format="MM/DD/YYYY"`) hit the `dateFormatMap`
17356
+ // lookup inside `setCustomHelpTextMessage`. Without this, an uppercase
17357
+ // format silently misses the map and leaves `setCustomValidityForType`
17358
+ // unset.
17359
+ if (this.format) {
17360
+ this.format = this.format.toLowerCase();
17361
+ }
17362
+
17413
17363
  // use validity message override if declared when initializing the component
17414
17364
  if (this.hasAttribute('setCustomValidity')) {
17415
17365
  this.ValidityMessageOverride = this.setCustomValidity;
@@ -17418,7 +17368,6 @@ class BaseInput extends AuroElement$1 {
17418
17368
  this.setCustomHelpTextMessage();
17419
17369
  this.configureAutoFormatting();
17420
17370
  this.configureDataForType();
17421
- this.syncDateValues();
17422
17371
  }
17423
17372
 
17424
17373
  /**
@@ -17494,6 +17443,13 @@ class BaseInput extends AuroElement$1 {
17494
17443
 
17495
17444
  if (typeToI18n.includes(this.type)) {
17496
17445
  this.setCustomValidityForType = i18n(this.lang, this.type);
17446
+ // COVERAGE: this `else if` branch is unreachable in WTR. connectedCallback
17447
+ // (L682) calls configureDataForType, which at L1266-1268 assigns
17448
+ // `this.format = this.util.getDateMaskFromLocale().toLowerCase()` for any
17449
+ // type=date input whose `format` attribute is unset. That runs before
17450
+ // firstUpdated invokes setCustomHelpTextMessage, so `!this.format` is
17451
+ // always false here. Retained as a defensive fallback; the whole function
17452
+ // is @deprecated per AB#1557296 and slated for removal.
17497
17453
  } else if (!this.format && this.type === 'date') {
17498
17454
  this.setCustomValidityForType = i18n(this.lang, 'dateMMDDYYYY');
17499
17455
  } else if (this.dateFormatMap[this.format]) {
@@ -17555,8 +17511,6 @@ class BaseInput extends AuroElement$1 {
17555
17511
  this.configureDataForType();
17556
17512
  }
17557
17513
 
17558
- this.syncDateValues(changedProperties);
17559
-
17560
17514
  if (changedProperties.has('value')) {
17561
17515
  if (this.value && this.value.length > 0) {
17562
17516
  this.hasValue = true;
@@ -17578,14 +17532,14 @@ class BaseInput extends AuroElement$1 {
17578
17532
 
17579
17533
  if (formattedValue !== this.inputElement.value) {
17580
17534
  this.skipNextProgrammaticInputEvent = true;
17581
- if (this.maskInstance && this.type === 'credit-card') {
17535
+ if (this.maskInstance && this.type !== 'date') {
17582
17536
  // Route through the mask so its _value and el.value stay in lock-step
17583
17537
  // (set value calls updateControl which writes el.value = displayValue).
17584
17538
  // Writing el.value directly leaves the mask thinking displayValue is
17585
- // stale; _saveSelection on the next focus/click then warns. Scoped to
17586
- // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
17587
- // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
17588
- // and flip validity from patternMismatch to tooShort.
17539
+ // stale; _saveSelection on the next focus/click then warns. Date is
17540
+ // excluded because its formattedValue can be raw ISO when the calendar
17541
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
17542
+ // flip validity from patternMismatch to tooShort.
17589
17543
  this.maskInstance.value = formattedValue || '';
17590
17544
  } else if (formattedValue) {
17591
17545
  this.inputElement.value = formattedValue;
@@ -17627,120 +17581,65 @@ class BaseInput extends AuroElement$1 {
17627
17581
  }));
17628
17582
  }
17629
17583
 
17630
-
17631
- /**
17632
- * Synchronizes the ISO string values and Date object representations for date-related properties.
17633
- * This keeps the model and display values aligned when either side changes.
17634
- *
17635
- * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
17636
- * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
17637
- * represents a full year/month/day date format.
17638
- *
17639
- * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
17640
- * @returns {void}
17641
- * @private
17642
- */
17643
- syncDateValues(changedProperties = undefined) {
17644
- if (!this.util.isFullDateFormat(this.type, this.format)) {
17645
- return;
17646
- }
17647
-
17648
- this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
17649
- this.syncSingleDateValue(changedProperties, 'minObject', 'min');
17650
- this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
17651
- }
17652
-
17653
- /**
17654
- * Synchronizes one date object/string property pair.
17655
- * @private
17656
- * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
17657
- * @param {string} objectProperty - Date object property name.
17658
- * @param {string} valueProperty - ISO string property name.
17659
- * @returns {void}
17660
- */
17661
- syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
17662
- const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
17663
-
17664
- // objectProperty wins over valueProperty when both changed
17665
- if (objectPropertyChanged && this[objectProperty]) {
17666
- this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
17667
- return;
17668
- }
17669
-
17670
- const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
17671
- if (!valuePropertyChanged) {
17672
- return;
17673
- }
17674
-
17675
- // 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)
17676
- if (
17677
- changedProperties &&
17678
- valueProperty === 'value' &&
17679
- changedProperties.get('value') === undefined &&
17680
- this[objectProperty] instanceof Date &&
17681
- this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
17682
- ) {
17683
- return;
17684
- }
17685
-
17686
- if (dateFormatter.isValidDate(this[valueProperty])) {
17687
- this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
17688
- } else {
17689
- this.setDateObjectProperty(objectProperty, undefined);
17690
- }
17691
- }
17692
-
17693
17584
  /**
17694
17585
  * Sets up IMasks and logic based on auto-formatting requirements.
17695
17586
  * @private
17696
17587
  * @returns {void}
17697
17588
  */
17698
17589
  configureAutoFormatting() {
17699
- // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
17700
- // by _configuringMask above) could otherwise trigger handleInput
17701
- // processCreditCard configureAutoFormatting before the outer call's
17702
- // set value has finished its alignCursor pass.
17590
+ // _configuringMask gates two things: external re-entry into this method
17591
+ // while setup is mid-flight (from property changes that call back here),
17592
+ // and the accept/complete listeners below — both need to ignore the mask
17593
+ // events fired by our own value-restore step.
17703
17594
  if (this._configuringMask) return;
17704
17595
  this._configuringMask = true;
17705
17596
  try {
17597
+ // Destroy any prior mask so IMask can attach fresh under the new format.
17598
+ // Null the reference too — if the new maskOptions.mask is falsy (e.g.
17599
+ // type switched from credit-card to text) the IMask() reassignment
17600
+ // below is skipped, and downstream writes at line ~823 would otherwise
17601
+ // route through the destroyed instance.
17706
17602
  if (this.maskInstance) {
17707
17603
  this.maskInstance.destroy();
17604
+ this.maskInstance = null;
17708
17605
  }
17709
17606
 
17710
- // Pass new format to util
17711
17607
  this.util.updateFormat(this.format);
17712
17608
 
17713
17609
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
17714
17610
 
17715
17611
  if (this.inputElement && maskOptions.mask) {
17716
-
17717
- // Stash and clear any existing value before IMask init.
17718
- // IMask's constructor processes the current input value which requires
17719
- // selection state clearing first avoids that scenario entirely.
17720
- // When the format changes (e.g. locale switch) and we have a valid ISO
17721
- // model value, compute the display string for the NEW format instead of
17722
- // re-using the old display string, which may be invalid in the new mask.
17612
+ // Capture the current display so it can be re-applied after IMask
17613
+ // attaches. The restore at the bottom goes through maskInstance.value
17614
+ // (not inputElement.value directly) so the mask's internal state and
17615
+ // the input's displayed text stay in lock-step.
17723
17616
  let existingValue = this.inputElement.value;
17617
+
17618
+ // Format-change case (e.g. locale switch): existingValue is the OLD
17619
+ // mask's display string and may not parse under the new mask. When
17620
+ // we have a valid date model, rebuild the display from valueObject
17621
+ // (the canonical source) using the new mask's format function.
17724
17622
  if (
17725
17623
  this.util.isFullDateFormat(this.type, this.format) &&
17726
17624
  this.value &&
17727
- dateFormatter.isValidDate(this.value) &&
17728
- this.valueObject instanceof Date &&
17729
- !Number.isNaN(this.valueObject.getTime()) &&
17625
+ this.valueObject &&
17730
17626
  typeof maskOptions.format === 'function'
17731
17627
  ) {
17732
17628
  existingValue = maskOptions.format(this.valueObject);
17733
17629
  }
17734
17630
 
17631
+ // Clear before IMask attaches so the constructor seeds an empty
17632
+ // internal value. Otherwise IMask reads the stale unmasked string
17633
+ // and emits a spurious 'accept' before the restore below runs.
17735
17634
  this.skipNextProgrammaticInputEvent = true;
17736
17635
  this.inputElement.value = '';
17737
17636
 
17738
17637
  this.maskInstance = IMask(this.inputElement, maskOptions);
17739
17638
 
17639
+ // Mask fires 'accept' on every value change, including the restore
17640
+ // step below. Skip events fired during configureAutoFormatting so
17641
+ // we don't overwrite a value the parent just pushed.
17740
17642
  this.maskInstance.on('accept', () => {
17741
- // Suppress propagation during configureAutoFormatting's own value-restoration
17742
- // (line below) — the mask emits 'accept' on every value-set, including ours,
17743
- // and we don't want to overwrite a value the parent just pushed.
17744
17643
  if (this._configuringMask) return;
17745
17644
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
17746
17645
  if (this.type === "date") {
@@ -17748,6 +17647,8 @@ class BaseInput extends AuroElement$1 {
17748
17647
  }
17749
17648
  });
17750
17649
 
17650
+ // Mask fires 'complete' on the restore step below for any value that
17651
+ // happens to be a complete match. Same setup-suppression as 'accept'.
17751
17652
  this.maskInstance.on('complete', () => {
17752
17653
  if (this._configuringMask) return;
17753
17654
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -17756,7 +17657,9 @@ class BaseInput extends AuroElement$1 {
17756
17657
  }
17757
17658
  });
17758
17659
 
17759
- // Restore the stashed value through IMask so it's properly masked
17660
+ // Write existingValue through the mask (not the input directly) so
17661
+ // the mask reformats it under the new rules and keeps its internal
17662
+ // _value aligned with the input's displayed text.
17760
17663
  if (existingValue) {
17761
17664
  this.maskInstance.value = existingValue;
17762
17665
  }
@@ -17836,6 +17739,38 @@ class BaseInput extends AuroElement$1 {
17836
17739
  this.touched = true;
17837
17740
  this.validation.validate(this);
17838
17741
  }
17742
+
17743
+ // Prevents cursor jumping in Safari. Setting `this.value` triggers a Lit
17744
+ // update that can re-render the input and reset the native cursor; we
17745
+ // capture the caret position before that update commits and restore it
17746
+ // via `setSelectionRange` once the update has flushed. Gated on
17747
+ // `setSelectionInputTypes` so credit-card (and other masked types whose
17748
+ // formatter manages the cursor itself) doesn't get a competing write.
17749
+ // Capture the caret position INSIDE the gate — reading `selectionStart`
17750
+ // on input types that don't support text selection (number, email in
17751
+ // some browsers) throws InvalidStateError, which would crash all input
17752
+ // handling. Wrap the read in try/catch belt-and-suspenders even though
17753
+ // the gated types currently support it, since the list is a public
17754
+ // property a consumer could mutate.
17755
+ if (this.setSelectionInputTypes.includes(this.type)) {
17756
+ let selectionStart = null;
17757
+ try {
17758
+ ({ selectionStart } = this.inputElement);
17759
+ } catch (error) { // eslint-disable-line no-unused-vars
17760
+ return;
17761
+ }
17762
+ if (typeof selectionStart !== 'number') {
17763
+ return;
17764
+ }
17765
+ this.updateComplete.then(() => {
17766
+ try {
17767
+ this.inputElement.setSelectionRange(selectionStart, selectionStart);
17768
+ } catch (error) { // eslint-disable-line no-unused-vars
17769
+ // Some input types (number/email in certain UAs) throw on
17770
+ // setSelectionRange; swallow and let the native cursor stand.
17771
+ }
17772
+ });
17773
+ }
17839
17774
  }
17840
17775
 
17841
17776
  /**
@@ -17924,7 +17859,6 @@ class BaseInput extends AuroElement$1 {
17924
17859
  */
17925
17860
  reset() {
17926
17861
  this.value = undefined;
17927
- this.setDateObjectProperty('valueObject', undefined);
17928
17862
  this.validation.reset(this);
17929
17863
  }
17930
17864
 
@@ -17933,7 +17867,6 @@ class BaseInput extends AuroElement$1 {
17933
17867
  */
17934
17868
  clear() {
17935
17869
  this.value = undefined;
17936
- this.setDateObjectProperty('valueObject', undefined);
17937
17870
  }
17938
17871
 
17939
17872
  /**
@@ -17971,7 +17904,7 @@ class BaseInput extends AuroElement$1 {
17971
17904
  */
17972
17905
  get placeholderStr() {
17973
17906
  if (!this.placeholder && this.type === 'date') {
17974
- return this.format ? this.format.toUpperCase() : 'MM/DD/YYYY';
17907
+ return this.format.toUpperCase();
17975
17908
  }
17976
17909
  return this.placeholder || "";
17977
17910
  }
@@ -18462,7 +18395,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18462
18395
  }
18463
18396
  };
18464
18397
 
18465
- var formkitVersion$1 = '202606192121';
18398
+ var formkitVersion$1 = '202607010100';
18466
18399
 
18467
18400
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18468
18401
  // See LICENSE in the project root for license information.
@@ -18965,6 +18898,7 @@ class AuroInput extends BaseInput {
18965
18898
  <${this.buttonTag}
18966
18899
  @click="${this.handleClickShowPassword}"
18967
18900
  appearance="${this.onDark ? 'inverse' : this.appearance}"
18901
+ aria-pressed="${this.showPassword ? 'true' : 'false'}"
18968
18902
  class="notificationBtn passwordBtn"
18969
18903
  shape="circle"
18970
18904
  size="sm"
@@ -19038,25 +18972,29 @@ class AuroInput extends BaseInput {
19038
18972
  * @returns {html} - Returns HTML for the help text and error message.
19039
18973
  */
19040
18974
  renderHtmlHelpText() {
18975
+ // Single `<p>` with stable identity across validity transitions —
18976
+ // previously two distinct templates (valid vs invalid) caused Lit to
18977
+ // replace the node entirely on a flip, and VoiceOver wouldn't
18978
+ // re-announce because the live-region element it was watching had been
18979
+ // removed and a new one inserted. Keeping one node means the `role`,
18980
+ // `aria-live`, and text content all change in-place, which AT does
18981
+ // observe and announce.
18982
+ const isError = this.validity && this.validity !== 'valid';
19041
18983
  return u$7`
19042
- ${!this.validity || this.validity === undefined || this.validity === 'valid'
19043
- ? u$7`
19044
- <${this.helpTextTag}
19045
- appearance="${this.onDark ? 'inverse' : this.appearance}">
19046
- <p id="${this.uniqueId}" part="helpText">
19047
- <slot name="helpText">${this.getHelpText()}</slot>
19048
- </p>
19049
- </${this.helpTextTag}>
19050
- `
19051
- : u$7`
19052
- <${this.helpTextTag} error
19053
- appearance="${this.onDark ? 'inverse' : this.appearance}">
19054
- <p id="${this.uniqueId}" role="alert" aria-live="assertive" part="helpText">
19055
- ${this.errorMessage}
19056
- </p>
19057
- </${this.helpTextTag}>
19058
- `
19059
- }
18984
+ <${this.helpTextTag}
18985
+ appearance="${this.onDark ? 'inverse' : this.appearance}"
18986
+ ?error=${isError}>
18987
+ <p
18988
+ id="${this.uniqueId}"
18989
+ part="helpText"
18990
+ role="${o(isError ? 'alert' : undefined)}"
18991
+ aria-live="${o(isError ? 'assertive' : undefined)}">
18992
+ ${isError
18993
+ ? this.errorMessage
18994
+ : u$7`<slot name="helpText">${this.getHelpText()}</slot>`
18995
+ }
18996
+ </p>
18997
+ </${this.helpTextTag}>
19060
18998
  `;
19061
18999
  }
19062
19000
 
@@ -19188,7 +19126,7 @@ class AuroInput extends BaseInput {
19188
19126
 
19189
19127
  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)}`;
19190
19128
 
19191
- 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)}`;
19129
+ 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)}`;
19192
19130
 
19193
19131
  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)}`;
19194
19132
 
@@ -19583,7 +19521,7 @@ class AuroBibtemplate extends i$3 {
19583
19521
  }
19584
19522
  }
19585
19523
 
19586
- var formkitVersion = '202606192121';
19524
+ var formkitVersion = '202607010100';
19587
19525
 
19588
19526
  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}`;
19589
19527
 
@@ -19951,12 +19889,23 @@ function getOptionLabel(option) {
19951
19889
  if (!option) {
19952
19890
  return '';
19953
19891
  }
19954
- const clone = option.cloneNode(true);
19955
- const displayValueEl = clone.querySelector('[slot="displayValue"]');
19956
- if (displayValueEl) {
19957
- displayValueEl.remove();
19892
+
19893
+ // Consumer-provided override: short-circuit the DOM walk entirely.
19894
+ if (option.dataset && option.dataset.label) {
19895
+ return option.dataset.label;
19896
+ }
19897
+
19898
+ // Walk direct children — the `slot` attribute only applies to direct children
19899
+ // of the slot host, so a shallow filter is sufficient. Avoids the cloneNode +
19900
+ // querySelector allocation that ran on every keystroke via syncValuesAndStates.
19901
+ let text = '';
19902
+ for (const node of option.childNodes) {
19903
+ const isDisplayValueSlot = node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === 'displayValue';
19904
+ if (!isDisplayValueSlot) {
19905
+ text += node.textContent || '';
19906
+ }
19958
19907
  }
19959
- return (clone.textContent || '').replace(/\s+/gu, ' ').trim();
19908
+ return text.replace(/\s+/gu, ' ').trim();
19960
19909
  }
19961
19910
 
19962
19911
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
@@ -20031,8 +19980,6 @@ class AuroCombobox extends AuroElement {
20031
19980
  this.availableOptions = [];
20032
19981
  this.dropdownId = undefined;
20033
19982
  this.dropdownOpen = false;
20034
- this.triggerExpandedState = false;
20035
- this._expandedTimeout = null;
20036
19983
  this._inFullscreenTransition = false;
20037
19984
  this.errorMessage = null;
20038
19985
  this.isHiddenWhileLoading = false;
@@ -20043,6 +19990,30 @@ class AuroCombobox extends AuroElement {
20043
19990
  this.touched = false;
20044
19991
  this.validation = new AuroFormValidation$1();
20045
19992
  this.validity = undefined;
19993
+ this._userTyped = false;
19994
+ // Tracks every setTimeout scheduled via _scheduleTimer so
19995
+ // disconnectedCallback can cancel them. Without this, a detached
19996
+ // combobox's pending timers still fire — most are no-ops, but
19997
+ // configureMenu's racing-condition retry would otherwise loop.
19998
+ this._pendingTimers = new Set();
19999
+ }
20000
+
20001
+ /**
20002
+ * setTimeout wrapper that records the timer id so disconnectedCallback
20003
+ * can cancel any outstanding callbacks. The id is removed from the set
20004
+ * once the callback fires so the set doesn't grow unbounded.
20005
+ * @param {Function} fn - Callback to run.
20006
+ * @param {number} ms - Delay in milliseconds.
20007
+ * @returns {number} The timer id.
20008
+ * @private
20009
+ */
20010
+ _scheduleTimer(fn, ms) {
20011
+ const id = setTimeout(() => {
20012
+ this._pendingTimers.delete(id);
20013
+ fn();
20014
+ }, ms);
20015
+ this._pendingTimers.add(id);
20016
+ return id;
20046
20017
  }
20047
20018
 
20048
20019
  // This function is to define props used within the scope of this component
@@ -20404,17 +20375,6 @@ class AuroCombobox extends AuroElement {
20404
20375
  attribute: false
20405
20376
  },
20406
20377
 
20407
- /**
20408
- * Deferred aria-expanded state for the trigger input.
20409
- * Delays the "true" transition so VoiceOver finishes its character echo
20410
- * before announcing "expanded".
20411
- * @private
20412
- */
20413
- triggerExpandedState: {
20414
- type: Boolean,
20415
- reflect: false,
20416
- attribute: false
20417
- },
20418
20378
  };
20419
20379
  }
20420
20380
 
@@ -20437,13 +20397,6 @@ class AuroCombobox extends AuroElement {
20437
20397
  return this.input.value;
20438
20398
  }
20439
20399
 
20440
- // /**
20441
- // * Sets the value of the input element within the combobox.
20442
- // */
20443
- // set inputValue(value) {
20444
- // this.input.value = value;
20445
- // }
20446
-
20447
20400
  /**
20448
20401
  * Checks if the element is valid.
20449
20402
  * @returns {boolean} - Returns true if the element is valid, false otherwise.
@@ -20570,7 +20523,7 @@ class AuroCombobox extends AuroElement {
20570
20523
  if (this.menu) {
20571
20524
  this.menu.matchWord = normalizeFilterValue(this.input.value);
20572
20525
  }
20573
- const label = getOptionLabel(this.menu.optionSelected) || this.menu.currentLabel;
20526
+ const label = getOptionLabel(this.menu.optionSelected);
20574
20527
  this.updateTriggerTextDisplay(label || this.value);
20575
20528
  }
20576
20529
 
@@ -20584,44 +20537,16 @@ class AuroCombobox extends AuroElement {
20584
20537
  // in suggestion mode, do not override input value if no selection has been made and the input currently has focus
20585
20538
  const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
20586
20539
 
20587
- if (!this.persistInput && !(this.behavior === 'suggestion' && isInputFocusedWithNoSelection)) {
20588
- const nextValue = label || this.value;
20589
- // Only set the flag when there's an actual write to suppress —
20590
- // syncValuesAndStates re-enters here during typing when both inputs
20591
- // already match, and a no-op flag flip would make the bib branch's
20592
- // bail eat legitimate user-input events.
20593
- const triggerNeedsSync = this.input.value !== nextValue;
20594
- const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
20595
- if (triggerNeedsSync || bibNeedsSync) {
20596
- this._syncingDisplayValue = true;
20597
- if (triggerNeedsSync) {
20598
- this.input.value = nextValue;
20599
- }
20600
- if (bibNeedsSync) {
20601
- this.inputInBib.value = nextValue;
20602
- }
20603
- const pending = [];
20604
- if (triggerNeedsSync) {
20605
- pending.push(this.input.updateComplete);
20606
- }
20607
- if (bibNeedsSync) {
20608
- pending.push(this.inputInBib.updateComplete);
20609
- }
20610
- Promise.all(pending).then(() => {
20611
- // imask reasserts otherwise, and handleBlur reverts to its stale value.
20612
- if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
20613
- this.input.maskInstance.updateValue();
20614
- }
20615
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20616
- this.inputInBib.maskInstance.updateValue();
20617
- }
20618
- this._syncingDisplayValue = false;
20619
- });
20620
- }
20540
+ const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
20541
+ if (!suppressed) {
20542
+ this.syncInputValuesAcrossTriggerAndBib(label || this.value);
20621
20543
  }
20622
20544
 
20623
- // update the displayValue in the trigger if displayValue slot content is present
20624
- const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]');
20545
+ // Replace any previously appended displayValue clone in the trigger.
20546
+ // :not(slot) excludes the template's <slot name="displayValue"
20547
+ // slot="displayValue"> forwarder, which also has slot="displayValue"
20548
+ // and would otherwise be matched first and removed.
20549
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
20625
20550
 
20626
20551
  if (displayValueInTrigger) {
20627
20552
  displayValueInTrigger.remove();
@@ -20645,6 +20570,53 @@ class AuroCombobox extends AuroElement {
20645
20570
  this.requestUpdate();
20646
20571
  }
20647
20572
 
20573
+ /**
20574
+ * Writes nextValue to the trigger input and the bib input when their current
20575
+ * value differs, then re-asserts imask after Lit's update flushes.
20576
+ * @param {string} nextValue - The value to write to both inputs.
20577
+ * @returns {Promise<void>} Resolves after both inputs flush and imask
20578
+ * re-asserts; resolves immediately when no sync is needed.
20579
+ * @private
20580
+ */
20581
+ async syncInputValuesAcrossTriggerAndBib(nextValue) {
20582
+ // Only set the flag when there's an actual write to suppress —
20583
+ // syncValuesAndStates re-enters here during typing when both inputs
20584
+ // already match, and a no-op flag flip would make the bib branch's
20585
+ // bail eat legitimate user-input events.
20586
+ const triggerNeedsSync = this.input.value !== nextValue;
20587
+ const bibNeedsSync = Boolean(this.inputInBib) && this.inputInBib.value !== nextValue;
20588
+ if (!triggerNeedsSync && !bibNeedsSync) {
20589
+ return;
20590
+ }
20591
+
20592
+ this._syncingDisplayValue = true;
20593
+
20594
+ const pending = [];
20595
+ if (triggerNeedsSync) {
20596
+ this.input.value = nextValue;
20597
+ pending.push(this.input.updateComplete);
20598
+ }
20599
+ if (bibNeedsSync) {
20600
+ this.inputInBib.value = nextValue;
20601
+ pending.push(this.inputInBib.updateComplete);
20602
+ }
20603
+ // finally — not a bare .then — so that an imask throw (see commit
20604
+ // d1857401c: imask can throw on credit-card format change) doesn't strand
20605
+ // _syncingDisplayValue=true and silently swallow every subsequent input.
20606
+ try {
20607
+ await Promise.all(pending);
20608
+ // imask reasserts otherwise, and handleBlur reverts to its stale value.
20609
+ if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
20610
+ this.input.maskInstance.updateValue();
20611
+ }
20612
+ if (bibNeedsSync && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20613
+ this.inputInBib.maskInstance.updateValue();
20614
+ }
20615
+ } finally {
20616
+ this._syncingDisplayValue = false;
20617
+ }
20618
+ }
20619
+
20648
20620
  /**
20649
20621
  * Processes hidden state of all menu options and determines if there are any available options not hidden.
20650
20622
  * @private
@@ -20653,6 +20625,13 @@ class AuroCombobox extends AuroElement {
20653
20625
  handleMenuOptions() {
20654
20626
  this.generateOptionsArray();
20655
20627
  this.availableOptions = [];
20628
+ // Single source of truth for the menu's filter/highlight token per call.
20629
+ // syncValuesAndStates re-writes the same value on exact-match keystrokes —
20630
+ // Lit's hasChanged makes that a no-op — and the prior duplicate set inside
20631
+ // handleTriggerInputValueChange is gone.
20632
+ if (this.menu) {
20633
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
20634
+ }
20656
20635
  this.updateFilter();
20657
20636
 
20658
20637
  // Set aria-setsize/aria-posinset on each visible option so screen readers
@@ -20664,17 +20643,14 @@ class AuroCombobox extends AuroElement {
20664
20643
  option.setAttribute('aria-posinset', index + 1);
20665
20644
  });
20666
20645
 
20667
- if (this.value && this.input.value && !this.menu.value) {
20668
- if (this.behavior === 'suggestion' && this.menu.options && this.menu.options.some((opt) => opt.value === this.value)) {
20669
- this.setMenuValue(this.value);
20670
- }
20671
-
20646
+ if (this.input.value && this.menu.options && this.menu.options.some((opt) => opt.value === this.input.value)) {
20647
+ this.setMenuValue(this.input.value);
20672
20648
  this.syncValuesAndStates();
20673
20649
  }
20674
20650
 
20675
- // Re-activate when optionActive is no longer visible, or when _index has
20676
- // been reset (e.g. by clearSelection in an async update) so that
20677
- // makeSelection() can find the correct option.
20651
+ // Re-activate when optionActive is not in the current scrollable viewport,
20652
+ // or when _index has been reset (e.g. by clearSelection in an async update)
20653
+ // so that makeSelection() can find the correct option.
20678
20654
  if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
20679
20655
  this.activateFirstEnabledAvailableOption();
20680
20656
  }
@@ -20747,18 +20723,6 @@ class AuroCombobox extends AuroElement {
20747
20723
  this.dropdownOpen = ev.detail.expanded;
20748
20724
  this.updateMenuShapeSize();
20749
20725
 
20750
- // Defer aria-expanded "true" so VoiceOver finishes character echo
20751
- // before announcing "expanded". Set "false" immediately on close.
20752
- clearTimeout(this._expandedTimeout);
20753
- if (this.dropdownOpen) {
20754
- const expandedDelay = 150;
20755
- this._expandedTimeout = setTimeout(() => {
20756
- this.triggerExpandedState = true;
20757
- }, expandedDelay);
20758
- } else {
20759
- this.triggerExpandedState = false;
20760
- }
20761
-
20762
20726
  // Clear aria-activedescendant when dropdown closes
20763
20727
  if (!this.dropdownOpen && this.input) {
20764
20728
  this.input.setActiveDescendant(null);
@@ -20808,28 +20772,25 @@ class AuroCombobox extends AuroElement {
20808
20772
 
20809
20773
  guardTouchPassthrough(this.menu);
20810
20774
 
20811
- // The dialog's showModal() steals focus from the trigger.
20812
- // A single rAF is enough for the dialog DOM to be painted and
20813
- // focusable, then doubleRaf finalizes the transition.
20775
+ // showModal() takes focus away from the trigger. Early focus once
20776
+ // the dialog has painted; the shared doubleRaf below is the fallback
20777
+ // for when the dialog needs an extra frame and also clears the
20778
+ // validation guard.
20814
20779
  requestAnimationFrame(() => {
20815
20780
  this.setInputFocus();
20816
20781
  });
20817
-
20818
- doubleRaf(() => {
20819
- this.setInputFocus();
20820
- this._inFullscreenTransition = false;
20821
- });
20822
- } else {
20823
- // Desktop popover-open: restore the trigger caret to end-of-text.
20824
- // Clicking the trigger lands on auro-input's floating <label for="…">
20825
- // overlay; Chrome resets the native input's selection to [0, 0] on
20826
- // label-focus before any JS runs. setInputFocus()'s non-fullscreen
20827
- // branch parks the caret at end. doubleRaf lets the dropdown layout
20828
- // settle first, matching the fullscreen branch timing.
20829
- doubleRaf(() => {
20830
- this.setInputFocus();
20831
- });
20832
20782
  }
20783
+ // else (desktop popover-open): Chrome resets the trigger caret to
20784
+ // [0, 0] when its floating <label for="…"> overlay receives focus.
20785
+ // The shared doubleRaf below parks the caret back at end-of-text
20786
+ // after the dropdown layout settles.
20787
+
20788
+ doubleRaf(() => {
20789
+ this.setInputFocus();
20790
+ if (this._inFullscreenTransition) {
20791
+ this._inFullscreenTransition = false;
20792
+ }
20793
+ });
20833
20794
  }
20834
20795
  });
20835
20796
 
@@ -20866,7 +20827,7 @@ class AuroCombobox extends AuroElement {
20866
20827
  this.dropdown.trigger.inert = false;
20867
20828
  }
20868
20829
 
20869
- setTimeout(() => {
20830
+ this._scheduleTimer(() => {
20870
20831
  this.setInputFocus();
20871
20832
  }, 0);
20872
20833
  });
@@ -20934,24 +20895,7 @@ class AuroCombobox extends AuroElement {
20934
20895
  nativeInput.setSelectionRange(len, len);
20935
20896
  }
20936
20897
  } else {
20937
- // Safety net for the strategy-change setTimeout: a viewport-crossing
20938
- // fullscreen→floating switch can close the dialog and leave focus on
20939
- // body. Every other Branch 2 callsite already has focus on the input
20940
- // via the focusin → this.focus() redirect (L1448-1452), so this is a
20941
- // no-op in the common case.
20942
- if (!this.input.componentHasFocus) {
20943
- this.input.focus();
20944
- }
20945
-
20946
- // Park the trigger native input's caret at end-of-text every time the
20947
- // popover opens. Chrome focuses the native via the floating <label for="…">
20948
- // overlay on click and resets selection to [0, 0]; no JS fires between
20949
- // mousedown and selectionchange, so we restore the caret here.
20950
- const triggerNativeInput = this.input.inputElement;
20951
- if (triggerNativeInput && triggerNativeInput.value) {
20952
- const len = triggerNativeInput.value.length;
20953
- triggerNativeInput.setSelectionRange(len, len);
20954
- }
20898
+ this.input.focus();
20955
20899
  }
20956
20900
  }
20957
20901
 
@@ -21027,7 +20971,7 @@ class AuroCombobox extends AuroElement {
21027
20971
 
21028
20972
  // racing condition on custom-combobox with custom-menu
21029
20973
  if (!this.menu) {
21030
- setTimeout(() => {
20974
+ this._scheduleTimer(() => {
21031
20975
  this.configureMenu();
21032
20976
  }, 0);
21033
20977
  return;
@@ -21057,7 +21001,7 @@ class AuroCombobox extends AuroElement {
21057
21001
  if (this.menu.optionSelected) {
21058
21002
  const selected = this.menu.optionSelected;
21059
21003
 
21060
- if (!this.optionSelected || this.optionSelected !== selected) {
21004
+ if (this.optionSelected !== selected) {
21061
21005
  this.optionSelected = selected;
21062
21006
  }
21063
21007
 
@@ -21070,7 +21014,7 @@ class AuroCombobox extends AuroElement {
21070
21014
  }
21071
21015
 
21072
21016
  // Update display
21073
- this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected) || this.menu.value);
21017
+ this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
21074
21018
 
21075
21019
  // Update match word for filtering
21076
21020
  const trimmedInput = normalizeFilterValue(this.input.value);
@@ -21082,17 +21026,24 @@ class AuroCombobox extends AuroElement {
21082
21026
  // Hide dropdown on selection (except during slot changes)
21083
21027
  if (evt.detail && evt.detail.source !== 'slotchange') {
21084
21028
  // do not close while typing in suggestion mode with no value selected, to allow freeform input
21085
- if (this.menu.value || this.behavior !== 'suggestion') {
21086
- this.hideBib();
21029
+ this.hideBib();
21030
+
21031
+ // Move focus to the clear button when the user makes a selection.
21032
+ if (!isEcho && this.menu.value !== undefined) {
21033
+ this.setClearBtnFocus();
21087
21034
  }
21088
21035
 
21089
21036
  // Announce the selection after the dropdown closes so it isn't
21090
21037
  // overridden by VoiceOver's "collapsed" announcement from aria-expanded.
21038
+ // Skip when there's no selected value (e.g. menu.clearSelection() from
21039
+ // the unmatched-value path), otherwise VoiceOver reads "undefined".
21091
21040
  const selectedValue = this.menu.value;
21092
- const announcementDelay = 300;
21093
- setTimeout(() => {
21094
- announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
21095
- }, announcementDelay);
21041
+ if (selectedValue) {
21042
+ const announcementDelay = 300;
21043
+ this._scheduleTimer(() => {
21044
+ announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
21045
+ }, announcementDelay);
21046
+ }
21096
21047
  }
21097
21048
 
21098
21049
  // Programmatic value syncs leave availableOptions stale because
@@ -21101,22 +21052,6 @@ class AuroCombobox extends AuroElement {
21101
21052
  // only — fresh user selections take the existing hideBib path.
21102
21053
  if (isEcho && this.menu.optionSelected) {
21103
21054
  this._programmaticFilterRefresh = true;
21104
- this.handleMenuOptions();
21105
- setTimeout(() => {
21106
- this._programmaticFilterRefresh = false;
21107
- }, 0);
21108
- }
21109
-
21110
- // base-input skips auto-validate when focus is inside its own shadow,
21111
- // which it is after setTriggerInputFocus — re-run so prior tooShort
21112
- // clears. processCreditCard reasserts errorMessage on every render.
21113
- if (!isEcho && this.menu.optionSelected && this.input.validate) {
21114
- this.input.updateComplete.then(() => {
21115
- this.input.validate(true);
21116
- if (this.input.validity === 'valid') {
21117
- this.input.errorMessage = '';
21118
- }
21119
- });
21120
21055
  }
21121
21056
  });
21122
21057
 
@@ -21129,6 +21064,14 @@ class AuroCombobox extends AuroElement {
21129
21064
  // stale option. Safe from re-entrancy because any resulting
21130
21065
  // input.value changes dispatch isProgrammatic events.
21131
21066
  this.menu.addEventListener('auroMenu-selectValueFailure', () => {
21067
+ // Announce the rejection BEFORE we clear `this.value` so the live
21068
+ // region carries the attempted value — without this the bib closes
21069
+ // silently and screen-reader users get no signal that their request
21070
+ // (e.g. a direct setMenuValue() call) was dropped.
21071
+ const attemptedValue = this.value;
21072
+ if (attemptedValue) {
21073
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${attemptedValue}`);
21074
+ }
21132
21075
  this.value = undefined;
21133
21076
  this.optionSelected = undefined;
21134
21077
  });
@@ -21140,10 +21083,13 @@ class AuroCombobox extends AuroElement {
21140
21083
  this.input.setActiveDescendant(this.optionActive);
21141
21084
  }
21142
21085
 
21143
- // Announce the active option for screen readers including position,
21144
- // since shadow DOM boundaries prevent native reading of
21145
- // aria-setsize/aria-posinset via aria-activedescendant.
21146
- if (this.optionActive) {
21086
+ // In fullscreen mode the menu sits inside a nested <dialog> shadow root,
21087
+ // and aria-activedescendant references across that boundary are lost —
21088
+ // VoiceOver/NVDA don't read the active option natively, so we mirror it
21089
+ // into the polite live region. In popover mode aria-activedescendant on
21090
+ // the trigger input is read natively; double-announcing would flood the
21091
+ // queue on arrow-key repeat.
21092
+ if (this.optionActive && this.dropdown.isBibFullscreen) {
21147
21093
  const optionText = this.optionActive.textContent.trim();
21148
21094
  const selectedState = this.optionActive.hasAttribute('selected') ? ', selected' : ', not selected';
21149
21095
  const optionIndex = this.availableOptions.indexOf(this.optionActive) + 1;
@@ -21176,7 +21122,12 @@ class AuroCombobox extends AuroElement {
21176
21122
  * Validate every time we remove focus from the combo box.
21177
21123
  */
21178
21124
  this.addEventListener('focusout', () => {
21179
- if (!this.componentHasFocus && !this._inFullscreenTransition) {
21125
+ // Skip while the dropdown is open — focus transits out briefly on
21126
+ // mousedown of a menu option (popover top-layer breaks :focus-within),
21127
+ // and validating against the pre-selection value flashes a stale error
21128
+ // between mousedown and mouseup. The next focusout fires after the
21129
+ // dropdown closes and validates against the post-selection value.
21130
+ if (!this.componentHasFocus && !this._inFullscreenTransition && !this.dropdownOpen) {
21180
21131
  this.validate();
21181
21132
  }
21182
21133
  });
@@ -21239,49 +21190,40 @@ class AuroCombobox extends AuroElement {
21239
21190
  if (this._syncingDisplayValue) {
21240
21191
  return;
21241
21192
  }
21242
- this._syncingBibValue = true;
21243
- this.input.value = this.inputInBib.value;
21244
- this.input.updateComplete.then(() => {
21245
- this._syncingBibValue = false;
21246
- });
21247
21193
 
21248
- // Run filtering inline — the re-entrant event won't reach this code.
21249
- this.menu.matchWord = normalizeFilterValue(this.inputInBib.value);
21250
- this.optionActive = null;
21251
-
21252
- // In suggestion mode, keep the combobox value in sync with the typed
21253
- // text so that freeform values are captured (mirroring the non-fullscreen
21254
- // path). Clear the selection when the input is emptied.
21255
- if (this.behavior === 'suggestion') {
21256
- this.value = this.inputInBib.value || undefined;
21194
+ // Filtering runs via re-entrance: writing this.input.value below
21195
+ // dispatches an 'input' event that the trigger's listener routes to
21196
+ // handleTriggerInputValueChange, which refreshes the menu filter.
21197
+ if (this.input.value !== this.inputInBib.value) {
21198
+ this._syncingBibValue = true;
21199
+ this.input.value = this.inputInBib.value;
21200
+ this.input.updateComplete.then(() => {
21201
+ this._syncingBibValue = false;
21202
+ });
21257
21203
  }
21258
21204
 
21259
- this.handleMenuOptions();
21260
21205
  this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21261
- return;
21206
+ } else if (event.target === this.input) {
21207
+
21208
+ // Also sync the native input immediately so keystrokes arriving
21209
+ // before Lit's async update cycle (e.g. rapid Backspaces during a
21210
+ // fullscreen transition) operate on the correct content.
21211
+ // Skip the next programmatic input event to prevent the patched setter
21212
+ // from re-entering handleInputValueChange via the bib path.
21213
+ const bibNativeInput = this.inputInBib.inputElement;
21214
+ if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21215
+ this.inputInBib.skipNextProgrammaticInputEvent = true;
21216
+ bibNativeInput.value = this.input.value || '';
21217
+ }
21262
21218
  }
21263
21219
 
21220
+ this.value = this.input.value;
21221
+
21264
21222
  // Ignore re-entrant input events caused by programmatic value sets.
21265
21223
  if (this._syncingBibValue || this._syncingDisplayValue) {
21266
21224
  return;
21267
21225
  }
21268
21226
 
21269
- this.inputInBib.value = this.input.value;
21270
-
21271
- // Also sync the native input immediately so keystrokes arriving
21272
- // before Lit's async update cycle (e.g. rapid Backspaces during a
21273
- // fullscreen transition) operate on the correct content.
21274
- // Skip the next programmatic input event to prevent the patched setter
21275
- // from re-entering handleInputValueChange via the bib path.
21276
- const bibNativeInput = this.inputInBib.inputElement;
21277
- if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21278
- this.inputInBib.skipNextProgrammaticInputEvent = true;
21279
- bibNativeInput.value = this.input.value || '';
21280
- }
21281
-
21282
- this.menu.matchWord = normalizeFilterValue(this.input.value);
21283
- this.optionActive = null;
21284
-
21285
21227
  if (this.behavior === 'suggestion') {
21286
21228
  this.value = this.input.value;
21287
21229
  }
@@ -21289,7 +21231,6 @@ class AuroCombobox extends AuroElement {
21289
21231
  if (!this.input.value && !this._clearing) {
21290
21232
  this.clear();
21291
21233
  }
21292
- this.handleMenuOptions();
21293
21234
 
21294
21235
  // Validate only if the value was set programmatically (not during user
21295
21236
  // interaction). In fullscreen dialog mode, componentHasFocus returns false
@@ -21299,34 +21240,44 @@ class AuroCombobox extends AuroElement {
21299
21240
  this.validate();
21300
21241
  }
21301
21242
 
21302
- if (this.input.value && this.input.value.length === 0) {
21303
- // Hide menu if value is empty, otherwise show if there are available suggestions
21304
- this.hideBib();
21305
- } else if (this.menu.loading) {
21306
- // if input has value but menu is loading, show bib immediately
21307
- this.showBib();
21308
- } else if (this.availableOptions.length === 0 && !this.dropdown.isBibFullscreen) {
21309
- // Force dropdown bib to hide if input value has no matching suggestions
21310
- this.hideBib();
21243
+ this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21244
+ }
21245
+
21246
+ /**
21247
+ * Handles input value changes originating from the trigger input.
21248
+ * Refreshes menu options and filtering, delegates to handleInputValueChange
21249
+ * for value synchronization, and manages fullscreen bib focus.
21250
+ * @private
21251
+ * @param {Event} event - The input event from the trigger input element.
21252
+ * @returns {void}
21253
+ */
21254
+ handleTriggerInputValueChange(event) {
21255
+ // 'input' fires for every user-initiated value change — typing, paste,
21256
+ // IME composition end, dead-key composition, drag-drop. Flip _userTyped
21257
+ // here so updated('availableOptions') auto-opens the bib for sources
21258
+ // that keydown alone misses: paste fires no keydown, IME uses
21259
+ // key='Process', and dead keys produce multi-char keys (all bypass the
21260
+ // prior keydown.key.length===1 gate). Skip programmatic syncs.
21261
+ if (!this._syncingDisplayValue && !this._syncingBibValue && !this._programmaticFilterRefresh) {
21262
+ this._userTyped = true;
21311
21263
  }
21312
21264
 
21313
- // iOS virtual keyboard retention: when in fullscreen mode, ensure the
21314
- // dialog opens and the bib input is focused synchronously within the
21315
- // input event (user gesture) chain. Without this, Lit's async update
21316
- // cycle delays showModal() past the user activation window, causing
21317
- // iOS Safari to dismiss the virtual keyboard when the fullscreen
21318
- // dialog opens — the user then has to tap the input again to resume
21319
- // typing.
21320
- if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
21321
- if (!this.dropdown.isPopoverVisible) {
21322
- this.showBib();
21323
- }
21324
- if (this.dropdown.isPopoverVisible) {
21325
- this.setInputFocus();
21326
- }
21265
+ this.handleMenuOptions();
21266
+ this.optionActive = null;
21267
+
21268
+ if (this.value === this.input.value) {
21269
+ return;
21327
21270
  }
21328
21271
 
21329
- this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21272
+ this.handleInputValueChange(event);
21273
+
21274
+ if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
21275
+ this.setInputFocus();
21276
+ }
21277
+
21278
+ if (this._programmaticFilterRefresh) {
21279
+ this._programmaticFilterRefresh = false;
21280
+ }
21330
21281
  }
21331
21282
 
21332
21283
  /**
@@ -21394,6 +21345,11 @@ class AuroCombobox extends AuroElement {
21394
21345
  disconnectedCallback() {
21395
21346
  super.disconnectedCallback();
21396
21347
  this._inFullscreenTransition = false;
21348
+ // Cancel any outstanding timers so detached callbacks don't fire on
21349
+ // disposed DOM — most are no-ops, but configureMenu's racing-condition
21350
+ // retry would otherwise keep rescheduling itself indefinitely.
21351
+ this._pendingTimers.forEach((id) => clearTimeout(id));
21352
+ this._pendingTimers.clear();
21397
21353
  }
21398
21354
 
21399
21355
  firstUpdated() {
@@ -21435,7 +21391,7 @@ class AuroCombobox extends AuroElement {
21435
21391
  this.menu.value = value;
21436
21392
  // Backup clear: if menu.value === menu.optionSelected.value already, the
21437
21393
  // listener won't fire and the flag would swallow the next user click.
21438
- setTimeout(() => {
21394
+ this._scheduleTimer(() => {
21439
21395
  this._pendingMenuValueSync = false;
21440
21396
  }, 0);
21441
21397
  }
@@ -21452,15 +21408,7 @@ class AuroCombobox extends AuroElement {
21452
21408
  this.menu.value = undefined;
21453
21409
  this.validation.reset(this);
21454
21410
  this.touched = false;
21455
- // Force validity back to the cleared state. validation.reset() calls
21456
- // validate() at the end, and (post-credit-card-fix) the trigger input's
21457
- // residual validity may still be copied into the combobox by the
21458
- // auroInputElements loop. Explicitly clear here so reset() actually
21459
- // leaves the component in a "no validity" state.
21460
21411
  this.validity = undefined;
21461
- this.errorMessage = '';
21462
- this.input.validity = undefined;
21463
- this.input.errorMessage = '';
21464
21412
  }
21465
21413
 
21466
21414
  /**
@@ -21478,6 +21426,17 @@ class AuroCombobox extends AuroElement {
21478
21426
  this.optionSelected = undefined;
21479
21427
  this.value = undefined;
21480
21428
 
21429
+ // Clear the appended displayValue clone in the trigger if present.
21430
+ // :not(slot) excludes the template's <slot name="displayValue"
21431
+ // slot="displayValue"> forwarder (line 1816), which also has
21432
+ // slot="displayValue" and would otherwise be matched first and removed,
21433
+ // permanently breaking the consumer-provided displayValue slot.
21434
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
21435
+
21436
+ if (displayValueInTrigger) {
21437
+ displayValueInTrigger.remove();
21438
+ }
21439
+
21481
21440
  if (this.input.value) {
21482
21441
  this.input.clear();
21483
21442
  }
@@ -21501,81 +21460,50 @@ class AuroCombobox extends AuroElement {
21501
21460
  }
21502
21461
 
21503
21462
  updated(changedProperties) {
21504
- // After the component is ready, send direct value changes to auro-menu.
21463
+ // After the component is ready, propagate direct changes down to child components.
21505
21464
  if (changedProperties.has('value')) {
21506
- if (this.value && this.value.length > 0) {
21507
- this.hasValue = true;
21508
- } else {
21509
- this.hasValue = false;
21465
+ // Only flag programmatic refreshes — user-typed value changes must not
21466
+ // suppress the availableOptions branch's showBib(). Firefox batches
21467
+ // 'value' and 'availableOptions' into the same updated() call, so
21468
+ // setting the flag unconditionally here masks the user-typed open path.
21469
+ if (!this._userTyped) {
21470
+ this._programmaticFilterRefresh = true;
21510
21471
  }
21511
21472
 
21512
- if (this.hasValue && !this.input.value && (!this.menu.options || this.menu.options.length === 0)) {
21513
- this.input.value = this.value;
21514
- }
21473
+ if (this.input.value !== this.value) {
21474
+ // Clear menu.value AND menu.optionSelected together. Clearing only
21475
+ // menu.value leaves the previously-selected option element pinned
21476
+ // as menu.optionSelected; a later auroMenu-selectedOption event
21477
+ // would then write its stale .value back into combobox.value
21478
+ // (e.g. Tab-after-Backspace re-selecting the prior option).
21479
+ if (this.menu.value || this.menu.optionSelected) {
21480
+ this.menu.clearSelection();
21481
+ }
21515
21482
 
21516
- // Sync menu.value only when an option actually matches; otherwise clear it.
21517
- if (this.menu.options && this.menu.options.length > 0) {
21518
- if (this.menu.options.some((opt) => opt.value === this.value)) {
21519
- this.setMenuValue(this.value);
21520
- } else if (this.behavior === 'filter') {
21521
- // In filter mode, freeform values aren't allowed. Push the unmatched
21522
- // value through setMenuValue so menu dispatches auroMenu-selectValueFailure
21523
- // and the listener at line 1206 clears combobox.value (mirrors select's
21524
- // pattern). Bypassing setMenuValue here would skip the failure cascade.
21525
- this.setMenuValue(this.value);
21526
- } else {
21527
- if (this.menu.value) {
21528
- this.menu.value = undefined;
21529
- }
21530
- // Sync the display for programmatic freeform value changes (e.g. the
21531
- // swap-values pattern). Guard with _syncingDisplayValue so that the
21532
- // re-entrant input event from base-input.notifyValueChanged() is
21533
- // ignored by handleInputValueChange. Skip the sync when input already
21534
- // matches to avoid spurious events during the normal typing path.
21535
- const nextValue = this.value || '';
21536
- const triggerNeedsSync = this.input.value !== nextValue;
21537
- const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
21538
- if (triggerNeedsSync || bibNeedsSync) {
21539
- this._syncingDisplayValue = true;
21540
- if (triggerNeedsSync) {
21541
- this.input.value = nextValue;
21542
- }
21543
- if (bibNeedsSync) {
21544
- this.inputInBib.value = nextValue;
21545
- }
21546
- const pending = [];
21547
- if (triggerNeedsSync) {
21548
- pending.push(this.input.updateComplete);
21549
- }
21550
- if (bibNeedsSync) {
21551
- pending.push(this.inputInBib.updateComplete);
21552
- }
21553
- Promise.all(pending).then(() => {
21554
- if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
21555
- this.input.maskInstance.updateValue();
21556
- }
21557
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
21558
- this.inputInBib.maskInstance.updateValue();
21559
- }
21560
- this._syncingDisplayValue = false;
21561
- // handleInputValueChange bailed on the flag — refresh the filter.
21562
- this._programmaticFilterRefresh = true;
21563
- this.handleMenuOptions();
21564
- setTimeout(() => {
21565
- this._programmaticFilterRefresh = false;
21566
- }, 0);
21567
- });
21568
- }
21483
+ if (!this.persistInput) {
21484
+ this.syncInputValuesAcrossTriggerAndBib(this.value || '');
21485
+ }
21486
+
21487
+ // Programmatic value with no matching option: updateFilter will close
21488
+ // the bib silently (see line 648 no noMatchOption + 0 results
21489
+ // hides). Announce so screen-reader users hear the request was
21490
+ // dropped. Gated on `input.value !== this.value` so this never fires
21491
+ // for user typing that path always reconciles input.value to
21492
+ // this.value before updated() runs.
21493
+ if (
21494
+ this.value &&
21495
+ this.menu &&
21496
+ this.menu.options &&
21497
+ this.menu.options.length > 0 &&
21498
+ !this.menu.options.some((opt) => opt.value === this.value)
21499
+ ) {
21500
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${this.value}`);
21569
21501
  }
21570
21502
  }
21503
+
21571
21504
  if (!this.value) {
21572
21505
  this.clear();
21573
21506
  }
21574
- if (this.value && !this.componentHasFocus) {
21575
- // If the value got set programmatically make sure we hide the bib
21576
- // when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
21577
- this.hideBib();
21578
- }
21579
21507
 
21580
21508
  // Sync the input and match word, but don't directly set menu.value again
21581
21509
  if (this.menu) {
@@ -21588,7 +21516,7 @@ class AuroCombobox extends AuroElement {
21588
21516
  composed: true,
21589
21517
  detail: {
21590
21518
  optionSelected: this.menu.optionSelected,
21591
- value: this.menu.value
21519
+ value: this.value
21592
21520
  }
21593
21521
  }));
21594
21522
 
@@ -21611,16 +21539,30 @@ class AuroCombobox extends AuroElement {
21611
21539
  // from a programmatic filter refresh after a value swap — the user
21612
21540
  // didn't interact, so we shouldn't auto-open the bib (especially for
21613
21541
  // the no-match path where the existing condition unconditionally fires).
21614
- if (this._programmaticFilterRefresh) {
21615
- this._programmaticFilterRefresh = false;
21616
- } else if ((this.availableOptions.length > 0 && (this.componentHasFocus || this.dropdownOpen)) || (this.menu && this.menu.loading) || (this.availableOptions.length === 0 && this.noMatchOption)) {
21617
- this.showBib();
21542
+ if ((this.menu && !this._programmaticFilterRefresh)) {
21543
+ if (
21544
+ this.availableOptions.length > 0 ||
21545
+ this.menu.loading ||
21546
+ this.noMatchOption
21547
+ ) {
21548
+ if (this._userTyped) {
21549
+ if (!this.dropdownOpen) {
21550
+ this.showBib();
21551
+ }
21552
+ this._userTyped = false;
21553
+ }
21554
+ }
21555
+
21618
21556
  if (!this.availableOptions.includes(this.menu.optionActive)) {
21619
21557
  this.activateFirstEnabledAvailableOption();
21620
21558
  }
21621
- } else if (this.dropdown && this.dropdown.isPopoverVisible) {
21559
+ } else if (!this.dropdown.isBibFullscreen) {
21622
21560
  this.hideBib();
21623
21561
  }
21562
+
21563
+ if (this._programmaticFilterRefresh) {
21564
+ this._programmaticFilterRefresh = false;
21565
+ }
21624
21566
  }
21625
21567
 
21626
21568
  if (changedProperties.has('error')) {
@@ -21762,10 +21704,10 @@ class AuroCombobox extends AuroElement {
21762
21704
  shape="${this.shape}"
21763
21705
  size="${this.size}">
21764
21706
  <${this.inputTag}
21765
- @input="${this.handleInputValueChange}"
21707
+ @input="${this.handleTriggerInputValueChange}"
21766
21708
  appearance="${this.onDark ? 'inverse' : this.appearance}"
21767
21709
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
21768
- .a11yExpanded="${this.triggerExpandedState}"
21710
+ .a11yExpanded="${this.dropdownOpen}"
21769
21711
  .a11yControls="${this.dropdownId}"
21770
21712
  .autocomplete="${this.autocomplete}"
21771
21713
  .inputmode="${this.inputmode}"
@@ -23370,14 +23312,10 @@ async function dynamicMenuExample() {
23370
23312
  // Helper function that generates HTML for menuoptions
23371
23313
  function generateMenuOptionHtml(menu, label, value) {
23372
23314
  const option = document.createElement('auro-menuoption');
23373
- const displayValue = document.createElement('div');
23374
- displayValue.setAttribute("slot", "displayValue");
23375
- displayValue.innerHTML = value;
23376
23315
 
23377
23316
  option.value = value;
23378
23317
  option.innerHTML = label;
23379
23318
  menu.appendChild(option);
23380
- option.appendChild(displayValue);
23381
23319
  }
23382
23320
 
23383
23321
  // Main javascript that runs all JS to create example