@aurodesignsystem-dev/auro-formkit 0.0.0-pr1508.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 (138) 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.min.js +8 -6
  4. package/components/checkbox/demo/getting-started.min.js +8 -6
  5. package/components/checkbox/demo/index.min.js +8 -6
  6. package/components/checkbox/dist/index.js +8 -6
  7. package/components/checkbox/dist/registered.js +8 -6
  8. package/components/combobox/demo/customize.md +2 -10
  9. package/components/combobox/demo/customize.min.js +386 -305
  10. package/components/combobox/demo/getting-started.min.js +386 -301
  11. package/components/combobox/demo/index.min.js +386 -301
  12. package/components/combobox/demo/keyboard-behavior.md +2 -142
  13. package/components/combobox/demo/why-combobox.md +1 -1
  14. package/components/combobox/dist/auro-combobox.d.ts +23 -16
  15. package/components/combobox/dist/index.js +385 -300
  16. package/components/combobox/dist/registered.js +385 -300
  17. package/components/counter/demo/customize.min.js +18 -8
  18. package/components/counter/demo/index.min.js +18 -8
  19. package/components/counter/dist/index.js +18 -8
  20. package/components/counter/dist/registered.js +18 -8
  21. package/components/datepicker/demo/accessibility.md +2 -1
  22. package/components/datepicker/demo/api.md +63 -62
  23. package/components/datepicker/demo/customize.md +12 -1
  24. package/components/datepicker/demo/customize.min.js +932 -335
  25. package/components/datepicker/demo/index.min.js +932 -335
  26. package/components/datepicker/dist/{src/auro-calendar-cell.d.ts → auro-calendar-cell.d.ts} +20 -3
  27. package/components/datepicker/dist/{src/auro-calendar.d.ts → auro-calendar.d.ts} +121 -7
  28. package/components/datepicker/dist/{src/auro-datepicker.d.ts → auro-datepicker.d.ts} +78 -5
  29. package/components/datepicker/dist/blackoutUtils.d.ts +43 -0
  30. package/components/datepicker/dist/index.js +932 -335
  31. package/components/datepicker/dist/registered.js +932 -335
  32. package/components/dropdown/demo/customize.min.js +2 -1
  33. package/components/dropdown/demo/getting-started.min.js +2 -1
  34. package/components/dropdown/demo/index.min.js +2 -1
  35. package/components/dropdown/dist/index.js +2 -1
  36. package/components/dropdown/dist/registered.js +2 -1
  37. package/components/form/demo/customize.min.js +1690 -826
  38. package/components/form/demo/getting-started.min.js +1690 -826
  39. package/components/form/demo/index.min.js +1690 -826
  40. package/components/form/demo/registerDemoDeps.min.js +1690 -826
  41. package/components/input/demo/api.md +5 -5
  42. package/components/input/demo/customize.md +8 -8
  43. package/components/input/demo/customize.min.js +137 -95
  44. package/components/input/demo/getting-started.min.js +137 -95
  45. package/components/input/demo/index.min.js +137 -95
  46. package/components/input/dist/base-input.d.ts +33 -44
  47. package/components/input/dist/index.js +150 -96
  48. package/components/input/dist/registered.js +137 -95
  49. package/components/radio/demo/customize.min.js +8 -6
  50. package/components/radio/demo/getting-started.min.js +8 -6
  51. package/components/radio/demo/index.min.js +8 -6
  52. package/components/radio/dist/index.js +8 -6
  53. package/components/radio/dist/registered.js +8 -6
  54. package/components/select/demo/customize.md +86 -0
  55. package/components/select/demo/customize.min.js +194 -68
  56. package/components/select/demo/getting-started.min.js +196 -115
  57. package/components/select/demo/index.min.js +194 -68
  58. package/components/select/demo/keyboard-behavior.md +18 -4
  59. package/components/select/dist/auro-select.d.ts +20 -8
  60. package/components/select/dist/index.js +194 -68
  61. package/components/select/dist/registered.js +194 -68
  62. package/custom-elements.json +2365 -1819
  63. package/package.json +6 -2
  64. package/components/checkbox/demo/customize.js +0 -22
  65. package/components/checkbox/demo/demo-support.js +0 -1
  66. package/components/checkbox/demo/getting-started.js +0 -22
  67. package/components/checkbox/demo/index.js +0 -8
  68. package/components/checkbox/demo/styles.scss +0 -1
  69. package/components/combobox/demo/customize.js +0 -24
  70. package/components/combobox/demo/demo-support.js +0 -1
  71. package/components/combobox/demo/getting-started.js +0 -21
  72. package/components/combobox/demo/index.js +0 -23
  73. package/components/combobox/demo/styles.scss +0 -1
  74. package/components/counter/demo/customize.js +0 -21
  75. package/components/counter/demo/demo-support.js +0 -1
  76. package/components/counter/demo/index.js +0 -21
  77. package/components/counter/demo/styles.scss +0 -1
  78. package/components/datepicker/demo/customize.js +0 -17
  79. package/components/datepicker/demo/demo-support.js +0 -1
  80. package/components/datepicker/demo/index.js +0 -23
  81. package/components/datepicker/demo/styles.scss +0 -1
  82. package/components/dropdown/demo/customize.js +0 -9
  83. package/components/dropdown/demo/demo-support.js +0 -1
  84. package/components/dropdown/demo/getting-started.js +0 -9
  85. package/components/dropdown/demo/index.js +0 -16
  86. package/components/dropdown/demo/styles.scss +0 -1
  87. package/components/form/demo/customize.js +0 -28
  88. package/components/form/demo/demo-support.js +0 -1
  89. package/components/form/demo/getting-started.js +0 -9
  90. package/components/form/demo/index.js +0 -5
  91. package/components/form/demo/registerDemoDeps.js +0 -23
  92. package/components/form/demo/styles.scss +0 -1
  93. package/components/input/demo/customize.js +0 -25
  94. package/components/input/demo/demo-support.js +0 -1
  95. package/components/input/demo/getting-started.js +0 -8
  96. package/components/input/demo/index.js +0 -8
  97. package/components/input/demo/styles.css +0 -974
  98. package/components/input/demo/styles.scss +0 -1
  99. package/components/menu/demo/demo-support.js +0 -1
  100. package/components/menu/demo/index.js +0 -25
  101. package/components/menu/demo/styles.scss +0 -1
  102. package/components/radio/demo/customize.js +0 -22
  103. package/components/radio/demo/demo-support.js +0 -1
  104. package/components/radio/demo/getting-started.js +0 -24
  105. package/components/radio/demo/index.js +0 -8
  106. package/components/radio/demo/styles.scss +0 -1
  107. package/components/select/demo/customize.js +0 -11
  108. package/components/select/demo/demo-support.js +0 -1
  109. package/components/select/demo/getting-started.js +0 -26
  110. package/components/select/demo/index.js +0 -11
  111. package/components/select/demo/styles.scss +0 -1
  112. /package/components/datepicker/dist/{src/auro-calendar-month.d.ts → auro-calendar-month.d.ts} +0 -0
  113. /package/components/datepicker/dist/{src/buttonVersion.d.ts → buttonVersion.d.ts} +0 -0
  114. /package/components/datepicker/dist/{src/datepickerKeyboardStrategy.d.ts → datepickerKeyboardStrategy.d.ts} +0 -0
  115. /package/components/datepicker/dist/{src/iconVersion.d.ts → iconVersion.d.ts} +0 -0
  116. /package/components/datepicker/dist/{src/index.d.ts → index.d.ts} +0 -0
  117. /package/components/datepicker/dist/{src/popoverVersion.d.ts → popoverVersion.d.ts} +0 -0
  118. /package/components/datepicker/dist/{src/styles → styles}/classic/color-css.d.ts +0 -0
  119. /package/components/datepicker/dist/{src/styles → styles}/classic/style-css.d.ts +0 -0
  120. /package/components/datepicker/dist/{src/styles → styles}/color-calendar-css.d.ts +0 -0
  121. /package/components/datepicker/dist/{src/styles → styles}/color-cell-css.d.ts +0 -0
  122. /package/components/datepicker/dist/{src/styles → styles}/color-css.d.ts +0 -0
  123. /package/components/datepicker/dist/{src/styles → styles}/color-month-css.d.ts +0 -0
  124. /package/components/datepicker/dist/{src/styles → styles}/shapeSize-css.d.ts +0 -0
  125. /package/components/datepicker/dist/{src/styles → styles}/snowflake/color-css.d.ts +0 -0
  126. /package/components/datepicker/dist/{src/styles → styles}/snowflake/style-css.d.ts +0 -0
  127. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-cell-css.d.ts +0 -0
  128. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-css.d.ts +0 -0
  129. /package/components/datepicker/dist/{src/styles → styles}/style-auro-calendar-month-css.d.ts +0 -0
  130. /package/components/datepicker/dist/{src/styles → styles}/style-css.d.ts +0 -0
  131. /package/components/datepicker/dist/{src/styles → styles}/tokens-css.d.ts +0 -0
  132. /package/components/datepicker/dist/{src/utilities.d.ts → utilities.d.ts} +0 -0
  133. /package/components/datepicker/dist/{src/utilitiesCalendar.d.ts → utilitiesCalendar.d.ts} +0 -0
  134. /package/components/datepicker/dist/{src/utilitiesCalendarRender.d.ts → utilitiesCalendarRender.d.ts} +0 -0
  135. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/day.d.ts +0 -0
  136. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker-calendar.d.ts +0 -0
  137. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker-cell.d.ts +0 -0
  138. /package/components/datepicker/dist/{src/vendor → vendor}/wc-range-datepicker/range-datepicker.d.ts +0 -0
@@ -448,15 +448,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
448
448
  );
449
449
  }
450
450
 
451
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
452
- if (this.auroInputElements?.length === 2) {
453
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
451
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
452
+
453
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
454
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
455
+ // field (datepicker is the intended consumer — start/end are independently required).
456
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
457
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
454
458
  hasValue = false;
455
459
  }
456
460
  }
457
461
 
458
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
459
-
460
462
  if (isCombobox) {
461
463
 
462
464
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -825,11 +827,7 @@ const comboboxKeyboardStrategy = {
825
827
 
826
828
  // navigate if bib is open otherwise open it
827
829
  if (component.dropdown.isPopoverVisible) {
828
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
829
- component.activateLastEnabledAvailableOption();
830
- } else {
831
- navigateArrow(component, 'down');
832
- }
830
+ navigateArrow(component, 'down');
833
831
  } else {
834
832
  component.showBib();
835
833
  }
@@ -848,11 +846,7 @@ const comboboxKeyboardStrategy = {
848
846
 
849
847
  // navigate if bib is open otherwise open it
850
848
  if (component.dropdown.isPopoverVisible) {
851
- if (evt.altKey || evt.ctrlKey || evt.metaKey) {
852
- component.activateFirstEnabledAvailableOption();
853
- } else {
854
- navigateArrow(component, 'up');
855
- }
849
+ navigateArrow(component, 'up');
856
850
  } else {
857
851
  component.showBib();
858
852
  }
@@ -868,13 +862,18 @@ const comboboxKeyboardStrategy = {
868
862
  },
869
863
 
870
864
  Enter(component, evt, ctx) {
865
+ // Forms should not submit on Enter from a combobox, regardless of which
866
+ // child element (input, clear button, menu) is focused.
867
+ evt.stopPropagation();
868
+
871
869
  if (isClearBtnFocused(ctx)) {
872
- // If the clear button has focus, let the browser activate it normally.
873
- // stopPropagation prevents parent containers (e.g., forms) from treating
874
- // Enter as a submit, but we must NOT call preventDefault — that would
875
- // block the browser's built-in "Enter activates focused button" behavior.
876
- evt.stopPropagation();
877
- } else if (ctx.isExpanded && component.menu.optionActive) {
870
+ // Let the browser dispatch Enter to the focused clear button so its
871
+ // built-in activation fires and clears the selection. Do NOT call
872
+ // preventDefault — that would block the activation.
873
+ return;
874
+ }
875
+
876
+ if (ctx.isExpanded && component.menu.optionActive) {
878
877
  reconcileMenuIndex(component.menu);
879
878
  component.menu.makeSelection();
880
879
 
@@ -883,14 +882,8 @@ const comboboxKeyboardStrategy = {
883
882
  }
884
883
 
885
884
  evt.preventDefault();
886
- evt.stopPropagation();
887
885
  } else {
888
- // Prevent the keypress from bubbling to parent containers (e.g., forms)
889
- // which could interpret Enter as a submit or trigger other unintended behavior.
890
- // This is safe because showBib() opens the dialog programmatically,
891
- // not via event propagation.
892
886
  evt.preventDefault();
893
- evt.stopPropagation();
894
887
  component.showBib();
895
888
  }
896
889
  },
@@ -935,7 +928,7 @@ const comboboxKeyboardStrategy = {
935
928
  component.setClearBtnFocus();
936
929
  }
937
930
  }
938
- },
931
+ }
939
932
  };
940
933
 
941
934
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
@@ -4806,7 +4799,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
4806
4799
  }
4807
4800
  };
4808
4801
 
4809
- var formkitVersion$2 = '202606231948';
4802
+ var formkitVersion$2 = '202607010100';
4810
4803
 
4811
4804
  let AuroElement$2 = class AuroElement extends LitElement {
4812
4805
  static get properties() {
@@ -6078,6 +6071,7 @@ class AuroDropdown extends AuroElement$2 {
6078
6071
  role="${ifDefined(this.triggerContentFocusable ? undefined : this.a11yRole)}"
6079
6072
  aria-expanded="${ifDefined(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
6080
6073
  aria-controls="${ifDefined(this.a11yRole === 'button' || this.triggerContentFocusable ? undefined : this.dropdownId)}"
6074
+ aria-haspopup="${ifDefined(this.a11yRole === 'combobox' && !this.triggerContentFocusable ? 'listbox' : undefined)}"
6081
6075
  aria-labelledby="${ifDefined(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
6082
6076
  aria-disabled="${ifDefined(this.disabled ? 'true' : undefined)}"
6083
6077
  @focusin="${this.handleFocusin}"
@@ -10580,15 +10574,17 @@ class AuroFormValidation {
10580
10574
  );
10581
10575
  }
10582
10576
 
10583
- // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false;
10584
- if (this.auroInputElements?.length === 2) {
10585
- if (!this.auroInputElements[1].value || this.auroInputElements[1].length === 0) {
10577
+ const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10578
+
10579
+ // If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
10580
+ // Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
10581
+ // field (datepicker is the intended consumer — start/end are independently required).
10582
+ if (this.auroInputElements?.length === 2 && !isCombobox) {
10583
+ if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
10586
10584
  hasValue = false;
10587
10585
  }
10588
10586
  }
10589
10587
 
10590
- const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
10591
-
10592
10588
  if (isCombobox) {
10593
10589
 
10594
10590
  if (!elem.persistInput || elem.behavior === "filter") {
@@ -16524,6 +16520,13 @@ class AuroInputUtilities {
16524
16520
  * @returns {string | undefined}
16525
16521
  */
16526
16522
  toFormattedValue(valueObject, format) {
16523
+ // Exposed publicly via `AuroInputUtil.toFormattedValue`, so a missing
16524
+ // `format` argument has to fail gracefully rather than throw on
16525
+ // `format.toLowerCase()`. Treat any falsy value (undefined, null, '')
16526
+ // as "no format → no display value".
16527
+ if (!format) {
16528
+ return undefined;
16529
+ }
16527
16530
  const normalizedFormat = format.toLowerCase();
16528
16531
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
16529
16532
 
@@ -16675,76 +16678,19 @@ class BaseInput extends AuroElement$1 {
16675
16678
  constructor() {
16676
16679
  super();
16677
16680
 
16678
- this.appearance = "default";
16679
- this.disabled = false;
16680
- this.layout = 'classic';
16681
- this.locale = 'en-US';
16682
- this.max = undefined;
16683
- this.maxLength = undefined;
16684
- this.min = undefined;
16685
- this.minLength = undefined;
16686
- this.required = false;
16687
- this.onDark = false;
16688
- this.setCustomValidityForType = undefined;
16689
- this.size = 'lg';
16690
- this.shape = 'classic';
16691
- this.value = undefined;
16692
-
16693
- this._initializePrivateDefaults();
16694
- }
16695
-
16696
- /**
16697
- * Internal Defaults.
16698
- * @private
16699
- * @returns {void}
16700
- */
16701
- _initializePrivateDefaults() {
16681
+ // Single-source initialization. Alphabetized so duplicate or stale
16682
+ // defaults are immediately obvious on a diff. Every field is assigned
16683
+ // exactly once here (previously the constructor + the old
16684
+ // `_initializePrivateDefaults` overlapped — both wrote ~14 of the same
16685
+ // fields and double-allocated `util` and `validation`, discarding the
16686
+ // first instance). `validation` is now allocated exactly once; `util`
16687
+ // is seeded here with an en-US default and then rebuilt in
16688
+ // `connectedCallback` once the consumer-resolved locale is available,
16689
+ // so a parent (datepicker/combobox) calling `validate()` synchronously
16690
+ // during its own update cycle sees a populated util instance.
16702
16691
  this.activeLabel = false;
16703
- this.appearance = "default";
16704
- this.icon = false;
16705
- this.disabled = false;
16706
- this.dvInputOnly = false;
16707
- this.hideLabelVisually = false;
16708
- this.max = undefined;
16709
- this.maxLength = undefined;
16710
- this.min = undefined;
16711
- this.minLength = undefined;
16712
- this.noValidate = false;
16713
- this.onDark = false;
16714
- this.required = false;
16715
- this.setCustomValidityForType = undefined;
16716
-
16717
- // Used for storing raw values returned from input mask.
16718
- this._rawMaskValue = undefined;
16719
-
16720
- /**
16721
- * @private
16722
- */
16723
- this.layout = 'classic';
16724
-
16725
- /**
16726
- * @private
16727
- */
16728
- this.shape = 'classic';
16729
-
16730
- /**
16731
- * @private
16732
- */
16733
- this.size = 'lg';
16734
-
16735
- this.touched = false;
16736
- this.util = new AuroInputUtilities({
16737
- locale: "en-US",
16738
- format: this.format
16739
- });
16740
- this.validation = new AuroFormValidation();
16741
- this.inputIconName = undefined;
16742
- this.showPassword = false;
16743
- this.validationCCLength = undefined;
16744
- this.hasValue = false;
16745
- this.label = 'Input label is undefined';
16746
-
16747
16692
 
16693
+ /** @private */
16748
16694
  this.allowedInputTypes = [
16749
16695
  "text",
16750
16696
  "number",
@@ -16753,7 +16699,9 @@ class BaseInput extends AuroElement$1 {
16753
16699
  "credit-card",
16754
16700
  "tel"
16755
16701
  ];
16756
- this.icon = false;
16702
+ this.appearance = "default";
16703
+
16704
+ /** @private */
16757
16705
  this.dateFormatMap = {
16758
16706
  'mm/dd/yyyy': 'dateMMDDYYYY',
16759
16707
  'dd/mm/yyyy': 'dateDDMMYYYY',
@@ -16770,27 +16718,62 @@ class BaseInput extends AuroElement$1 {
16770
16718
  'dd/mm': 'dateDDMM',
16771
16719
  'mm/dd': 'dateMMDD'
16772
16720
  };
16721
+ this.disabled = false;
16722
+
16723
+ /** @private */
16773
16724
  this.domHandler = new DomHandler();
16774
16725
  this.dvInputOnly = false;
16775
16726
  this.hasValue = false;
16727
+ this.hideLabelVisually = false;
16728
+ this.icon = false;
16729
+
16730
+ /** @private */
16776
16731
  this.inputIconName = undefined;
16732
+
16733
+ /** @private */
16777
16734
  this.label = 'Input label is undefined';
16735
+ this.layout = 'classic';
16736
+ this.locale = 'en-US';
16737
+ this.max = undefined;
16738
+ this.maxLength = undefined;
16739
+ this.min = undefined;
16740
+ this.minLength = undefined;
16778
16741
  this.noValidate = false;
16779
- this._rawMaskValue = undefined; // Used for storing raw values returned from input mask.
16742
+ this.onDark = false;
16743
+ // Raw values returned from the input mask before model normalization.
16744
+ this._rawMaskValue = undefined;
16745
+ this.required = false;
16746
+ this.setCustomValidityForType = undefined;
16747
+ // Credit Card is intentionally excluded — its mask manages the cursor
16748
+ // itself, and listing it here caused cursor placement issues in Safari.
16749
+ /** @private */
16780
16750
  this.setSelectionInputTypes = [
16781
16751
  "text",
16782
16752
  "password",
16783
16753
  "email"
16784
- ]; // Credit Card is not included as this caused cursor placement issues in Safari.
16754
+ ];
16755
+ this.shape = 'classic';
16756
+
16757
+ /** @private */
16785
16758
  this.showPassword = false;
16759
+ this.size = 'lg';
16786
16760
  this.touched = false;
16761
+
16762
+ /** @private */
16787
16763
  this.uniqueId = new UniqueId().create();
16764
+
16765
+ /** @private */
16788
16766
  this.util = new AuroInputUtilities({
16789
16767
  locale: this.locale,
16790
16768
  format: this.format
16791
16769
  });
16770
+
16771
+ /** @private */
16792
16772
  this.validation = new AuroFormValidation();
16773
+
16774
+ /** @private */
16793
16775
  this.validationCCLength = undefined;
16776
+ this.value = undefined;
16794
16777
  }
16795
16778
 
16796
16779
  // function to define props used within the scope of this component
@@ -17294,6 +17277,15 @@ class BaseInput extends AuroElement$1 {
17294
17277
 
17295
17278
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
17296
17279
 
17280
+ // Normalize the format token to lowercase so case-mixed values supplied
17281
+ // via attribute (e.g. `format="MM/DD/YYYY"`) hit the `dateFormatMap`
17282
+ // lookup inside `setCustomHelpTextMessage`. Without this, an uppercase
17283
+ // format silently misses the map and leaves `setCustomValidityForType`
17284
+ // unset.
17285
+ if (this.format) {
17286
+ this.format = this.format.toLowerCase();
17287
+ }
17288
+
17297
17289
  // use validity message override if declared when initializing the component
17298
17290
  if (this.hasAttribute('setCustomValidity')) {
17299
17291
  this.ValidityMessageOverride = this.setCustomValidity;
@@ -17377,6 +17369,13 @@ class BaseInput extends AuroElement$1 {
17377
17369
 
17378
17370
  if (typeToI18n.includes(this.type)) {
17379
17371
  this.setCustomValidityForType = i18n(this.lang, this.type);
17372
+ // COVERAGE: this `else if` branch is unreachable in WTR. connectedCallback
17373
+ // (L682) calls configureDataForType, which at L1266-1268 assigns
17374
+ // `this.format = this.util.getDateMaskFromLocale().toLowerCase()` for any
17375
+ // type=date input whose `format` attribute is unset. That runs before
17376
+ // firstUpdated invokes setCustomHelpTextMessage, so `!this.format` is
17377
+ // always false here. Retained as a defensive fallback; the whole function
17378
+ // is @deprecated per AB#1557296 and slated for removal.
17380
17379
  } else if (!this.format && this.type === 'date') {
17381
17380
  this.setCustomValidityForType = i18n(this.lang, 'dateMMDDYYYY');
17382
17381
  } else if (this.dateFormatMap[this.format]) {
@@ -17666,6 +17665,38 @@ class BaseInput extends AuroElement$1 {
17666
17665
  this.touched = true;
17667
17666
  this.validation.validate(this);
17668
17667
  }
17668
+
17669
+ // Prevents cursor jumping in Safari. Setting `this.value` triggers a Lit
17670
+ // update that can re-render the input and reset the native cursor; we
17671
+ // capture the caret position before that update commits and restore it
17672
+ // via `setSelectionRange` once the update has flushed. Gated on
17673
+ // `setSelectionInputTypes` so credit-card (and other masked types whose
17674
+ // formatter manages the cursor itself) doesn't get a competing write.
17675
+ // Capture the caret position INSIDE the gate — reading `selectionStart`
17676
+ // on input types that don't support text selection (number, email in
17677
+ // some browsers) throws InvalidStateError, which would crash all input
17678
+ // handling. Wrap the read in try/catch belt-and-suspenders even though
17679
+ // the gated types currently support it, since the list is a public
17680
+ // property a consumer could mutate.
17681
+ if (this.setSelectionInputTypes.includes(this.type)) {
17682
+ let selectionStart = null;
17683
+ try {
17684
+ ({ selectionStart } = this.inputElement);
17685
+ } catch (error) { // eslint-disable-line no-unused-vars
17686
+ return;
17687
+ }
17688
+ if (typeof selectionStart !== 'number') {
17689
+ return;
17690
+ }
17691
+ this.updateComplete.then(() => {
17692
+ try {
17693
+ this.inputElement.setSelectionRange(selectionStart, selectionStart);
17694
+ } catch (error) { // eslint-disable-line no-unused-vars
17695
+ // Some input types (number/email in certain UAs) throw on
17696
+ // setSelectionRange; swallow and let the native cursor stand.
17697
+ }
17698
+ });
17699
+ }
17669
17700
  }
17670
17701
 
17671
17702
  /**
@@ -17799,7 +17830,7 @@ class BaseInput extends AuroElement$1 {
17799
17830
  */
17800
17831
  get placeholderStr() {
17801
17832
  if (!this.placeholder && this.type === 'date') {
17802
- return this.format ? this.format.toUpperCase() : 'MM/DD/YYYY';
17833
+ return this.format.toUpperCase();
17803
17834
  }
17804
17835
  return this.placeholder || "";
17805
17836
  }
@@ -18290,7 +18321,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
18290
18321
  }
18291
18322
  };
18292
18323
 
18293
- var formkitVersion$1 = '202606231948';
18324
+ var formkitVersion$1 = '202607010100';
18294
18325
 
18295
18326
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18296
18327
  // See LICENSE in the project root for license information.
@@ -18793,6 +18824,7 @@ class AuroInput extends BaseInput {
18793
18824
  <${this.buttonTag}
18794
18825
  @click="${this.handleClickShowPassword}"
18795
18826
  appearance="${this.onDark ? 'inverse' : this.appearance}"
18827
+ aria-pressed="${this.showPassword ? 'true' : 'false'}"
18796
18828
  class="notificationBtn passwordBtn"
18797
18829
  shape="circle"
18798
18830
  size="sm"
@@ -18866,25 +18898,29 @@ class AuroInput extends BaseInput {
18866
18898
  * @returns {html} - Returns HTML for the help text and error message.
18867
18899
  */
18868
18900
  renderHtmlHelpText() {
18901
+ // Single `<p>` with stable identity across validity transitions —
18902
+ // previously two distinct templates (valid vs invalid) caused Lit to
18903
+ // replace the node entirely on a flip, and VoiceOver wouldn't
18904
+ // re-announce because the live-region element it was watching had been
18905
+ // removed and a new one inserted. Keeping one node means the `role`,
18906
+ // `aria-live`, and text content all change in-place, which AT does
18907
+ // observe and announce.
18908
+ const isError = this.validity && this.validity !== 'valid';
18869
18909
  return html`
18870
- ${!this.validity || this.validity === undefined || this.validity === 'valid'
18871
- ? html`
18872
- <${this.helpTextTag}
18873
- appearance="${this.onDark ? 'inverse' : this.appearance}">
18874
- <p id="${this.uniqueId}" part="helpText">
18875
- <slot name="helpText">${this.getHelpText()}</slot>
18876
- </p>
18877
- </${this.helpTextTag}>
18878
- `
18879
- : html`
18880
- <${this.helpTextTag} error
18881
- appearance="${this.onDark ? 'inverse' : this.appearance}">
18882
- <p id="${this.uniqueId}" role="alert" aria-live="assertive" part="helpText">
18883
- ${this.errorMessage}
18884
- </p>
18885
- </${this.helpTextTag}>
18886
- `
18910
+ <${this.helpTextTag}
18911
+ appearance="${this.onDark ? 'inverse' : this.appearance}"
18912
+ ?error=${isError}>
18913
+ <p
18914
+ id="${this.uniqueId}"
18915
+ part="helpText"
18916
+ role="${ifDefined(isError ? 'alert' : undefined)}"
18917
+ aria-live="${ifDefined(isError ? 'assertive' : undefined)}">
18918
+ ${isError
18919
+ ? this.errorMessage
18920
+ : html`<slot name="helpText">${this.getHelpText()}</slot>`
18887
18921
  }
18922
+ </p>
18923
+ </${this.helpTextTag}>
18888
18924
  `;
18889
18925
  }
18890
18926
 
@@ -19016,7 +19052,7 @@ class AuroInput extends BaseInput {
19016
19052
 
19017
19053
  var colorCss$1 = css`#headerContainer{box-shadow:var(--ds-auro-dropdownbib-header-boxshadow-color)}#footerContainer{background-color:var(--ds-auro-dropdownbib-footer-container-color)}`;
19018
19054
 
19019
- var styleCss$2 = css`#bibTemplate{display:flex;overflow:hidden;height:100%;max-height:100%;flex-direction:column}#headerContainer{display:flex;flex-direction:column;padding:var(--ds-size-200, 1rem);padding-top:var(--ds-size-400, 2rem);padding-bottom:0}#headerContainer .titleRow{display:flex;flex-direction:row;justify-content:space-between;gap:var(--ds-size-100, 0.5rem)}#headerContainer .header{max-width:calc(100vw - var(--ds-size-900, 4.5rem));padding-top:var(--ds-size-200, 1rem)}#headerContainer .subheader{width:100%}#closeButton [auro-icon]{--ds-auro-icon-size: var(--ds-size-500, 2.5rem)}#bodyContainer{position:relative;overflow:auto;flex:1 1 100%}:host([isfullscreen]) #bibTemplate{max-height:100dvh}:host([isfullscreen]) #bodyContainer{margin-top:var(--ds-size-100, 0.5rem)}#footerContainer{position:absolute;right:0;bottom:0;left:0;padding:var(--ds-size-200, 1rem)}`;
19055
+ var styleCss$2 = css`#bibTemplate{display:flex;overflow:hidden;height:100%;max-height:100%;flex-direction:column}#headerContainer{display:flex;flex-direction:column;padding:var(--ds-size-200, 1rem);padding-top:var(--ds-size-400, 2rem);padding-bottom:0}#headerContainer .titleRow{display:flex;flex-direction:row;justify-content:space-between;gap:var(--ds-size-100, 0.5rem)}#headerContainer .header{max-width:calc(100vw - var(--ds-size-900, 4.5rem));padding-top:var(--ds-size-200, 1rem)}#headerContainer .subheader{width:100%}#closeButton [auro-icon]{--ds-auro-icon-size: var(--ds-size-500, 2.5rem)}#bodyContainer{position:relative;overflow:auto;flex:1}:host([isfullscreen]) #bibTemplate{max-height:100dvh}:host([isfullscreen]) #bodyContainer{margin-top:var(--ds-size-100, 0.5rem)}#footerContainer{padding:var(--ds-size-200, 1rem)}`;
19020
19056
 
19021
19057
  var tokenCss = css`:host{--ds-auro-dropdownbib-header-boxshadow-color: var(--ds-elevation-200, 0px 0px 10px rgba(0, 0, 0, 0.15));--ds-auro-dropdownbib-footer-container-color: var(--ds-basic-color-surface-default, #ffffff)}`;
19022
19058
 
@@ -19411,7 +19447,7 @@ class AuroBibtemplate extends LitElement {
19411
19447
  }
19412
19448
  }
19413
19449
 
19414
- var formkitVersion = '202606231948';
19450
+ var formkitVersion = '202607010100';
19415
19451
 
19416
19452
  var styleCss$1 = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
19417
19453
 
@@ -19779,12 +19815,23 @@ function getOptionLabel(option) {
19779
19815
  if (!option) {
19780
19816
  return '';
19781
19817
  }
19782
- const clone = option.cloneNode(true);
19783
- const displayValueEl = clone.querySelector('[slot="displayValue"]');
19784
- if (displayValueEl) {
19785
- displayValueEl.remove();
19818
+
19819
+ // Consumer-provided override: short-circuit the DOM walk entirely.
19820
+ if (option.dataset && option.dataset.label) {
19821
+ return option.dataset.label;
19822
+ }
19823
+
19824
+ // Walk direct children — the `slot` attribute only applies to direct children
19825
+ // of the slot host, so a shallow filter is sufficient. Avoids the cloneNode +
19826
+ // querySelector allocation that ran on every keystroke via syncValuesAndStates.
19827
+ let text = '';
19828
+ for (const node of option.childNodes) {
19829
+ const isDisplayValueSlot = node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === 'displayValue';
19830
+ if (!isDisplayValueSlot) {
19831
+ text += node.textContent || '';
19832
+ }
19786
19833
  }
19787
- return (clone.textContent || '').replace(/\s+/gu, ' ').trim();
19834
+ return text.replace(/\s+/gu, ' ').trim();
19788
19835
  }
19789
19836
 
19790
19837
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
@@ -19859,8 +19906,6 @@ class AuroCombobox extends AuroElement {
19859
19906
  this.availableOptions = [];
19860
19907
  this.dropdownId = undefined;
19861
19908
  this.dropdownOpen = false;
19862
- this.triggerExpandedState = false;
19863
- this._expandedTimeout = null;
19864
19909
  this._inFullscreenTransition = false;
19865
19910
  this.errorMessage = null;
19866
19911
  this.isHiddenWhileLoading = false;
@@ -19871,6 +19916,30 @@ class AuroCombobox extends AuroElement {
19871
19916
  this.touched = false;
19872
19917
  this.validation = new AuroFormValidation$1();
19873
19918
  this.validity = undefined;
19919
+ this._userTyped = false;
19920
+ // Tracks every setTimeout scheduled via _scheduleTimer so
19921
+ // disconnectedCallback can cancel them. Without this, a detached
19922
+ // combobox's pending timers still fire — most are no-ops, but
19923
+ // configureMenu's racing-condition retry would otherwise loop.
19924
+ this._pendingTimers = new Set();
19925
+ }
19926
+
19927
+ /**
19928
+ * setTimeout wrapper that records the timer id so disconnectedCallback
19929
+ * can cancel any outstanding callbacks. The id is removed from the set
19930
+ * once the callback fires so the set doesn't grow unbounded.
19931
+ * @param {Function} fn - Callback to run.
19932
+ * @param {number} ms - Delay in milliseconds.
19933
+ * @returns {number} The timer id.
19934
+ * @private
19935
+ */
19936
+ _scheduleTimer(fn, ms) {
19937
+ const id = setTimeout(() => {
19938
+ this._pendingTimers.delete(id);
19939
+ fn();
19940
+ }, ms);
19941
+ this._pendingTimers.add(id);
19942
+ return id;
19874
19943
  }
19875
19944
 
19876
19945
  // This function is to define props used within the scope of this component
@@ -20232,17 +20301,6 @@ class AuroCombobox extends AuroElement {
20232
20301
  attribute: false
20233
20302
  },
20234
20303
 
20235
- /**
20236
- * Deferred aria-expanded state for the trigger input.
20237
- * Delays the "true" transition so VoiceOver finishes its character echo
20238
- * before announcing "expanded".
20239
- * @private
20240
- */
20241
- triggerExpandedState: {
20242
- type: Boolean,
20243
- reflect: false,
20244
- attribute: false
20245
- },
20246
20304
  };
20247
20305
  }
20248
20306
 
@@ -20265,13 +20323,6 @@ class AuroCombobox extends AuroElement {
20265
20323
  return this.input.value;
20266
20324
  }
20267
20325
 
20268
- // /**
20269
- // * Sets the value of the input element within the combobox.
20270
- // */
20271
- // set inputValue(value) {
20272
- // this.input.value = value;
20273
- // }
20274
-
20275
20326
  /**
20276
20327
  * Checks if the element is valid.
20277
20328
  * @returns {boolean} - Returns true if the element is valid, false otherwise.
@@ -20417,8 +20468,11 @@ class AuroCombobox extends AuroElement {
20417
20468
  this.syncInputValuesAcrossTriggerAndBib(label || this.value);
20418
20469
  }
20419
20470
 
20420
- // update the displayValue in the trigger if displayValue slot content is present
20421
- const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]');
20471
+ // Replace any previously appended displayValue clone in the trigger.
20472
+ // :not(slot) excludes the template's <slot name="displayValue"
20473
+ // slot="displayValue"> forwarder, which also has slot="displayValue"
20474
+ // and would otherwise be matched first and removed.
20475
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
20422
20476
 
20423
20477
  if (displayValueInTrigger) {
20424
20478
  displayValueInTrigger.remove();
@@ -20446,19 +20500,19 @@ class AuroCombobox extends AuroElement {
20446
20500
  * Writes nextValue to the trigger input and the bib input when their current
20447
20501
  * value differs, then re-asserts imask after Lit's update flushes.
20448
20502
  * @param {string} nextValue - The value to write to both inputs.
20449
- * @returns {Promise<void> | null} Promise that resolves after the inputs flush,
20450
- * or null when no sync was needed (so callers can skip post-flush work).
20503
+ * @returns {Promise<void>} Resolves after both inputs flush and imask
20504
+ * re-asserts; resolves immediately when no sync is needed.
20451
20505
  * @private
20452
20506
  */
20453
- syncInputValuesAcrossTriggerAndBib(nextValue) {
20507
+ async syncInputValuesAcrossTriggerAndBib(nextValue) {
20454
20508
  // Only set the flag when there's an actual write to suppress —
20455
20509
  // syncValuesAndStates re-enters here during typing when both inputs
20456
20510
  // already match, and a no-op flag flip would make the bib branch's
20457
20511
  // bail eat legitimate user-input events.
20458
20512
  const triggerNeedsSync = this.input.value !== nextValue;
20459
- const bibNeedsSync = this.inputInBib.value !== nextValue;
20513
+ const bibNeedsSync = Boolean(this.inputInBib) && this.inputInBib.value !== nextValue;
20460
20514
  if (!triggerNeedsSync && !bibNeedsSync) {
20461
- return null;
20515
+ return;
20462
20516
  }
20463
20517
 
20464
20518
  this._syncingDisplayValue = true;
@@ -20472,16 +20526,21 @@ class AuroCombobox extends AuroElement {
20472
20526
  this.inputInBib.value = nextValue;
20473
20527
  pending.push(this.inputInBib.updateComplete);
20474
20528
  }
20475
- return Promise.all(pending).then(() => {
20529
+ // finally — not a bare .then — so that an imask throw (see commit
20530
+ // d1857401c: imask can throw on credit-card format change) doesn't strand
20531
+ // _syncingDisplayValue=true and silently swallow every subsequent input.
20532
+ try {
20533
+ await Promise.all(pending);
20476
20534
  // imask reasserts otherwise, and handleBlur reverts to its stale value.
20477
20535
  if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
20478
20536
  this.input.maskInstance.updateValue();
20479
20537
  }
20480
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20538
+ if (bibNeedsSync && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
20481
20539
  this.inputInBib.maskInstance.updateValue();
20482
20540
  }
20541
+ } finally {
20483
20542
  this._syncingDisplayValue = false;
20484
- });
20543
+ }
20485
20544
  }
20486
20545
 
20487
20546
  /**
@@ -20492,6 +20551,13 @@ class AuroCombobox extends AuroElement {
20492
20551
  handleMenuOptions() {
20493
20552
  this.generateOptionsArray();
20494
20553
  this.availableOptions = [];
20554
+ // Single source of truth for the menu's filter/highlight token per call.
20555
+ // syncValuesAndStates re-writes the same value on exact-match keystrokes —
20556
+ // Lit's hasChanged makes that a no-op — and the prior duplicate set inside
20557
+ // handleTriggerInputValueChange is gone.
20558
+ if (this.menu) {
20559
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
20560
+ }
20495
20561
  this.updateFilter();
20496
20562
 
20497
20563
  // Set aria-setsize/aria-posinset on each visible option so screen readers
@@ -20503,11 +20569,8 @@ class AuroCombobox extends AuroElement {
20503
20569
  option.setAttribute('aria-posinset', index + 1);
20504
20570
  });
20505
20571
 
20506
- if (this.value && this.input.value && !this.menu.value) {
20507
- if (this.behavior === 'suggestion' && this.menu.options && this.menu.options.some((opt) => opt.value === this.value)) {
20508
- this.setMenuValue(this.value);
20509
- }
20510
-
20572
+ if (this.input.value && this.menu.options && this.menu.options.some((opt) => opt.value === this.input.value)) {
20573
+ this.setMenuValue(this.input.value);
20511
20574
  this.syncValuesAndStates();
20512
20575
  }
20513
20576
 
@@ -20586,18 +20649,6 @@ class AuroCombobox extends AuroElement {
20586
20649
  this.dropdownOpen = ev.detail.expanded;
20587
20650
  this.updateMenuShapeSize();
20588
20651
 
20589
- // Defer aria-expanded "true" so VoiceOver finishes character echo
20590
- // before announcing "expanded". Set "false" immediately on close.
20591
- clearTimeout(this._expandedTimeout);
20592
- if (this.dropdownOpen) {
20593
- const expandedDelay = 150;
20594
- this._expandedTimeout = setTimeout(() => {
20595
- this.triggerExpandedState = true;
20596
- }, expandedDelay);
20597
- } else {
20598
- this.triggerExpandedState = false;
20599
- }
20600
-
20601
20652
  // Clear aria-activedescendant when dropdown closes
20602
20653
  if (!this.dropdownOpen && this.input) {
20603
20654
  this.input.setActiveDescendant(null);
@@ -20702,7 +20753,7 @@ class AuroCombobox extends AuroElement {
20702
20753
  this.dropdown.trigger.inert = false;
20703
20754
  }
20704
20755
 
20705
- setTimeout(() => {
20756
+ this._scheduleTimer(() => {
20706
20757
  this.setInputFocus();
20707
20758
  }, 0);
20708
20759
  });
@@ -20846,7 +20897,7 @@ class AuroCombobox extends AuroElement {
20846
20897
 
20847
20898
  // racing condition on custom-combobox with custom-menu
20848
20899
  if (!this.menu) {
20849
- setTimeout(() => {
20900
+ this._scheduleTimer(() => {
20850
20901
  this.configureMenu();
20851
20902
  }, 0);
20852
20903
  return;
@@ -20901,9 +20952,7 @@ class AuroCombobox extends AuroElement {
20901
20952
  // Hide dropdown on selection (except during slot changes)
20902
20953
  if (evt.detail && evt.detail.source !== 'slotchange') {
20903
20954
  // do not close while typing in suggestion mode with no value selected, to allow freeform input
20904
- if (this.menu.value || this.behavior !== 'suggestion') {
20905
- this.hideBib();
20906
- }
20955
+ this.hideBib();
20907
20956
 
20908
20957
  // Move focus to the clear button when the user makes a selection.
20909
20958
  if (!isEcho && this.menu.value !== undefined) {
@@ -20917,7 +20966,7 @@ class AuroCombobox extends AuroElement {
20917
20966
  const selectedValue = this.menu.value;
20918
20967
  if (selectedValue) {
20919
20968
  const announcementDelay = 300;
20920
- setTimeout(() => {
20969
+ this._scheduleTimer(() => {
20921
20970
  announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
20922
20971
  }, announcementDelay);
20923
20972
  }
@@ -20929,10 +20978,6 @@ class AuroCombobox extends AuroElement {
20929
20978
  // only — fresh user selections take the existing hideBib path.
20930
20979
  if (isEcho && this.menu.optionSelected) {
20931
20980
  this._programmaticFilterRefresh = true;
20932
- this.handleMenuOptions();
20933
- setTimeout(() => {
20934
- this._programmaticFilterRefresh = false;
20935
- }, 0);
20936
20981
  }
20937
20982
  });
20938
20983
 
@@ -20945,6 +20990,14 @@ class AuroCombobox extends AuroElement {
20945
20990
  // stale option. Safe from re-entrancy because any resulting
20946
20991
  // input.value changes dispatch isProgrammatic events.
20947
20992
  this.menu.addEventListener('auroMenu-selectValueFailure', () => {
20993
+ // Announce the rejection BEFORE we clear `this.value` so the live
20994
+ // region carries the attempted value — without this the bib closes
20995
+ // silently and screen-reader users get no signal that their request
20996
+ // (e.g. a direct setMenuValue() call) was dropped.
20997
+ const attemptedValue = this.value;
20998
+ if (attemptedValue) {
20999
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${attemptedValue}`);
21000
+ }
20948
21001
  this.value = undefined;
20949
21002
  this.optionSelected = undefined;
20950
21003
  });
@@ -20956,10 +21009,13 @@ class AuroCombobox extends AuroElement {
20956
21009
  this.input.setActiveDescendant(this.optionActive);
20957
21010
  }
20958
21011
 
20959
- // Announce the active option for screen readers including position,
20960
- // since shadow DOM boundaries prevent native reading of
20961
- // aria-setsize/aria-posinset via aria-activedescendant.
20962
- if (this.optionActive) {
21012
+ // In fullscreen mode the menu sits inside a nested <dialog> shadow root,
21013
+ // and aria-activedescendant references across that boundary are lost —
21014
+ // VoiceOver/NVDA don't read the active option natively, so we mirror it
21015
+ // into the polite live region. In popover mode aria-activedescendant on
21016
+ // the trigger input is read natively; double-announcing would flood the
21017
+ // queue on arrow-key repeat.
21018
+ if (this.optionActive && this.dropdown.isBibFullscreen) {
20963
21019
  const optionText = this.optionActive.textContent.trim();
20964
21020
  const selectedState = this.optionActive.hasAttribute('selected') ? ', selected' : ', not selected';
20965
21021
  const optionIndex = this.availableOptions.indexOf(this.optionActive) + 1;
@@ -20992,7 +21048,12 @@ class AuroCombobox extends AuroElement {
20992
21048
  * Validate every time we remove focus from the combo box.
20993
21049
  */
20994
21050
  this.addEventListener('focusout', () => {
20995
- if (!this.componentHasFocus && !this._inFullscreenTransition) {
21051
+ // Skip while the dropdown is open — focus transits out briefly on
21052
+ // mousedown of a menu option (popover top-layer breaks :focus-within),
21053
+ // and validating against the pre-selection value flashes a stale error
21054
+ // between mousedown and mouseup. The next focusout fires after the
21055
+ // dropdown closes and validates against the post-selection value.
21056
+ if (!this.componentHasFocus && !this._inFullscreenTransition && !this.dropdownOpen) {
20996
21057
  this.validate();
20997
21058
  }
20998
21059
  });
@@ -21055,49 +21116,40 @@ class AuroCombobox extends AuroElement {
21055
21116
  if (this._syncingDisplayValue) {
21056
21117
  return;
21057
21118
  }
21058
- this._syncingBibValue = true;
21059
- this.input.value = this.inputInBib.value;
21060
- this.input.updateComplete.then(() => {
21061
- this._syncingBibValue = false;
21062
- });
21063
21119
 
21064
- // Run filtering inline — the re-entrant event won't reach this code.
21065
- this.menu.matchWord = normalizeFilterValue(this.inputInBib.value);
21066
- this.optionActive = null;
21067
-
21068
- // In suggestion mode, keep the combobox value in sync with the typed
21069
- // text so that freeform values are captured (mirroring the non-fullscreen
21070
- // path). Clear the selection when the input is emptied.
21071
- if (this.behavior === 'suggestion') {
21072
- this.value = this.inputInBib.value || undefined;
21120
+ // Filtering runs via re-entrance: writing this.input.value below
21121
+ // dispatches an 'input' event that the trigger's listener routes to
21122
+ // handleTriggerInputValueChange, which refreshes the menu filter.
21123
+ if (this.input.value !== this.inputInBib.value) {
21124
+ this._syncingBibValue = true;
21125
+ this.input.value = this.inputInBib.value;
21126
+ this.input.updateComplete.then(() => {
21127
+ this._syncingBibValue = false;
21128
+ });
21073
21129
  }
21074
21130
 
21075
- this.handleMenuOptions();
21076
21131
  this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21077
- return;
21132
+ } else if (event.target === this.input) {
21133
+
21134
+ // Also sync the native input immediately so keystrokes arriving
21135
+ // before Lit's async update cycle (e.g. rapid Backspaces during a
21136
+ // fullscreen transition) operate on the correct content.
21137
+ // Skip the next programmatic input event to prevent the patched setter
21138
+ // from re-entering handleInputValueChange via the bib path.
21139
+ const bibNativeInput = this.inputInBib.inputElement;
21140
+ if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21141
+ this.inputInBib.skipNextProgrammaticInputEvent = true;
21142
+ bibNativeInput.value = this.input.value || '';
21143
+ }
21078
21144
  }
21079
21145
 
21146
+ this.value = this.input.value;
21147
+
21080
21148
  // Ignore re-entrant input events caused by programmatic value sets.
21081
21149
  if (this._syncingBibValue || this._syncingDisplayValue) {
21082
21150
  return;
21083
21151
  }
21084
21152
 
21085
- this.inputInBib.value = this.input.value;
21086
-
21087
- // Also sync the native input immediately so keystrokes arriving
21088
- // before Lit's async update cycle (e.g. rapid Backspaces during a
21089
- // fullscreen transition) operate on the correct content.
21090
- // Skip the next programmatic input event to prevent the patched setter
21091
- // from re-entering handleInputValueChange via the bib path.
21092
- const bibNativeInput = this.inputInBib.inputElement;
21093
- if (bibNativeInput && bibNativeInput.value !== this.input.value) {
21094
- this.inputInBib.skipNextProgrammaticInputEvent = true;
21095
- bibNativeInput.value = this.input.value || '';
21096
- }
21097
-
21098
- this.menu.matchWord = normalizeFilterValue(this.input.value);
21099
- this.optionActive = null;
21100
-
21101
21153
  if (this.behavior === 'suggestion') {
21102
21154
  this.value = this.input.value;
21103
21155
  }
@@ -21105,7 +21157,6 @@ class AuroCombobox extends AuroElement {
21105
21157
  if (!this.input.value && !this._clearing) {
21106
21158
  this.clear();
21107
21159
  }
21108
- this.handleMenuOptions();
21109
21160
 
21110
21161
  // Validate only if the value was set programmatically (not during user
21111
21162
  // interaction). In fullscreen dialog mode, componentHasFocus returns false
@@ -21115,34 +21166,44 @@ class AuroCombobox extends AuroElement {
21115
21166
  this.validate();
21116
21167
  }
21117
21168
 
21118
- if (this.input.value && this.input.value.length === 0) {
21119
- // Hide menu if value is empty, otherwise show if there are available suggestions
21120
- this.hideBib();
21121
- } else if (this.menu.loading) {
21122
- // if input has value but menu is loading, show bib immediately
21123
- this.showBib();
21124
- } else if (this.availableOptions.length === 0 && !this.dropdown.isBibFullscreen) {
21125
- // Force dropdown bib to hide if input value has no matching suggestions
21126
- this.hideBib();
21169
+ this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21170
+ }
21171
+
21172
+ /**
21173
+ * Handles input value changes originating from the trigger input.
21174
+ * Refreshes menu options and filtering, delegates to handleInputValueChange
21175
+ * for value synchronization, and manages fullscreen bib focus.
21176
+ * @private
21177
+ * @param {Event} event - The input event from the trigger input element.
21178
+ * @returns {void}
21179
+ */
21180
+ handleTriggerInputValueChange(event) {
21181
+ // 'input' fires for every user-initiated value change — typing, paste,
21182
+ // IME composition end, dead-key composition, drag-drop. Flip _userTyped
21183
+ // here so updated('availableOptions') auto-opens the bib for sources
21184
+ // that keydown alone misses: paste fires no keydown, IME uses
21185
+ // key='Process', and dead keys produce multi-char keys (all bypass the
21186
+ // prior keydown.key.length===1 gate). Skip programmatic syncs.
21187
+ if (!this._syncingDisplayValue && !this._syncingBibValue && !this._programmaticFilterRefresh) {
21188
+ this._userTyped = true;
21127
21189
  }
21128
21190
 
21129
- // iOS virtual keyboard retention: when in fullscreen mode, ensure the
21130
- // dialog opens and the bib input is focused synchronously within the
21131
- // input event (user gesture) chain. Without this, Lit's async update
21132
- // cycle delays showModal() past the user activation window, causing
21133
- // iOS Safari to dismiss the virtual keyboard when the fullscreen
21134
- // dialog opens — the user then has to tap the input again to resume
21135
- // typing.
21136
- if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
21137
- if (!this.dropdown.isPopoverVisible) {
21138
- this.showBib();
21139
- }
21140
- if (this.dropdown.isPopoverVisible) {
21141
- this.setInputFocus();
21142
- }
21191
+ this.handleMenuOptions();
21192
+ this.optionActive = null;
21193
+
21194
+ if (this.value === this.input.value) {
21195
+ return;
21143
21196
  }
21144
21197
 
21145
- this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
21198
+ this.handleInputValueChange(event);
21199
+
21200
+ if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
21201
+ this.setInputFocus();
21202
+ }
21203
+
21204
+ if (this._programmaticFilterRefresh) {
21205
+ this._programmaticFilterRefresh = false;
21206
+ }
21146
21207
  }
21147
21208
 
21148
21209
  /**
@@ -21210,6 +21271,11 @@ class AuroCombobox extends AuroElement {
21210
21271
  disconnectedCallback() {
21211
21272
  super.disconnectedCallback();
21212
21273
  this._inFullscreenTransition = false;
21274
+ // Cancel any outstanding timers so detached callbacks don't fire on
21275
+ // disposed DOM — most are no-ops, but configureMenu's racing-condition
21276
+ // retry would otherwise keep rescheduling itself indefinitely.
21277
+ this._pendingTimers.forEach((id) => clearTimeout(id));
21278
+ this._pendingTimers.clear();
21213
21279
  }
21214
21280
 
21215
21281
  firstUpdated() {
@@ -21251,7 +21317,7 @@ class AuroCombobox extends AuroElement {
21251
21317
  this.menu.value = value;
21252
21318
  // Backup clear: if menu.value === menu.optionSelected.value already, the
21253
21319
  // listener won't fire and the flag would swallow the next user click.
21254
- setTimeout(() => {
21320
+ this._scheduleTimer(() => {
21255
21321
  this._pendingMenuValueSync = false;
21256
21322
  }, 0);
21257
21323
  }
@@ -21286,6 +21352,17 @@ class AuroCombobox extends AuroElement {
21286
21352
  this.optionSelected = undefined;
21287
21353
  this.value = undefined;
21288
21354
 
21355
+ // Clear the appended displayValue clone in the trigger if present.
21356
+ // :not(slot) excludes the template's <slot name="displayValue"
21357
+ // slot="displayValue"> forwarder (line 1816), which also has
21358
+ // slot="displayValue" and would otherwise be matched first and removed,
21359
+ // permanently breaking the consumer-provided displayValue slot.
21360
+ const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
21361
+
21362
+ if (displayValueInTrigger) {
21363
+ displayValueInTrigger.remove();
21364
+ }
21365
+
21289
21366
  if (this.input.value) {
21290
21367
  this.input.clear();
21291
21368
  }
@@ -21309,56 +21386,50 @@ class AuroCombobox extends AuroElement {
21309
21386
  }
21310
21387
 
21311
21388
  updated(changedProperties) {
21312
- // After the component is ready, send direct value changes to auro-menu.
21389
+ // After the component is ready, propagate direct changes down to child components.
21313
21390
  if (changedProperties.has('value')) {
21314
- if (this.value && this.value.length > 0) {
21315
- this.hasValue = true;
21316
- } else {
21317
- this.hasValue = false;
21391
+ // Only flag programmatic refreshes — user-typed value changes must not
21392
+ // suppress the availableOptions branch's showBib(). Firefox batches
21393
+ // 'value' and 'availableOptions' into the same updated() call, so
21394
+ // setting the flag unconditionally here masks the user-typed open path.
21395
+ if (!this._userTyped) {
21396
+ this._programmaticFilterRefresh = true;
21318
21397
  }
21319
21398
 
21320
- if (this.hasValue && !this.input.value && (!this.menu.options || this.menu.options.length === 0)) {
21321
- this.input.value = this.value;
21322
- }
21399
+ if (this.input.value !== this.value) {
21400
+ // Clear menu.value AND menu.optionSelected together. Clearing only
21401
+ // menu.value leaves the previously-selected option element pinned
21402
+ // as menu.optionSelected; a later auroMenu-selectedOption event
21403
+ // would then write its stale .value back into combobox.value
21404
+ // (e.g. Tab-after-Backspace re-selecting the prior option).
21405
+ if (this.menu.value || this.menu.optionSelected) {
21406
+ this.menu.clearSelection();
21407
+ }
21323
21408
 
21324
- // Sync menu.value only when an option actually matches, or when filter
21325
- // mode needs setMenuValue to dispatch auroMenu-selectValueFailure (the
21326
- // listener at line 1206 clears combobox.value, mirroring select's
21327
- // pattern). Otherwise sync the input display for freeform values.
21328
- if (this.menu.options && this.menu.options.length > 0) {
21329
- if (this.menu.options.some((opt) => opt.value === this.value) || this.behavior === 'filter') {
21330
- this.setMenuValue(this.value);
21331
- } else {
21332
- // Clear menu.value AND menu.optionSelected together. Clearing only
21333
- // menu.value leaves the previously-selected option element pinned
21334
- // as menu.optionSelected; a later auroMenu-selectedOption event
21335
- // would then write its stale .value back into combobox.value.
21336
- if (this.menu.value || this.menu.optionSelected) {
21337
- this.menu.clearSelection();
21338
- }
21339
- // Suggestion-mode freeform value: sync the trigger + bib to show it,
21340
- // then refresh the filter once the inputs flush (handleInputValueChange
21341
- // bailed on _syncingDisplayValue).
21342
- const syncPromise = this.syncInputValuesAcrossTriggerAndBib(this.value || '');
21343
- if (syncPromise) {
21344
- syncPromise.then(() => {
21345
- this._programmaticFilterRefresh = true;
21346
- this.handleMenuOptions();
21347
- setTimeout(() => {
21348
- this._programmaticFilterRefresh = false;
21349
- }, 0);
21350
- });
21351
- }
21409
+ if (!this.persistInput) {
21410
+ this.syncInputValuesAcrossTriggerAndBib(this.value || '');
21411
+ }
21412
+
21413
+ // Programmatic value with no matching option: updateFilter will close
21414
+ // the bib silently (see line 648 no noMatchOption + 0 results
21415
+ // hides). Announce so screen-reader users hear the request was
21416
+ // dropped. Gated on `input.value !== this.value` so this never fires
21417
+ // for user typing that path always reconciles input.value to
21418
+ // this.value before updated() runs.
21419
+ if (
21420
+ this.value &&
21421
+ this.menu &&
21422
+ this.menu.options &&
21423
+ this.menu.options.length > 0 &&
21424
+ !this.menu.options.some((opt) => opt.value === this.value)
21425
+ ) {
21426
+ announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${this.value}`);
21352
21427
  }
21353
21428
  }
21429
+
21354
21430
  if (!this.value) {
21355
21431
  this.clear();
21356
21432
  }
21357
- if (this.value && !this.componentHasFocus) {
21358
- // If the value got set programmatically make sure we hide the bib
21359
- // when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
21360
- this.hideBib();
21361
- }
21362
21433
 
21363
21434
  // Sync the input and match word, but don't directly set menu.value again
21364
21435
  if (this.menu) {
@@ -21371,7 +21442,7 @@ class AuroCombobox extends AuroElement {
21371
21442
  composed: true,
21372
21443
  detail: {
21373
21444
  optionSelected: this.menu.optionSelected,
21374
- value: this.menu.value
21445
+ value: this.value
21375
21446
  }
21376
21447
  }));
21377
21448
 
@@ -21394,16 +21465,30 @@ class AuroCombobox extends AuroElement {
21394
21465
  // from a programmatic filter refresh after a value swap — the user
21395
21466
  // didn't interact, so we shouldn't auto-open the bib (especially for
21396
21467
  // the no-match path where the existing condition unconditionally fires).
21397
- if (this._programmaticFilterRefresh) {
21398
- this._programmaticFilterRefresh = false;
21399
- } else if ((this.availableOptions.length > 0 && (this.componentHasFocus || this.dropdownOpen)) || (this.menu && this.menu.loading) || (this.availableOptions.length === 0 && this.noMatchOption)) {
21400
- this.showBib();
21468
+ if ((this.menu && !this._programmaticFilterRefresh)) {
21469
+ if (
21470
+ this.availableOptions.length > 0 ||
21471
+ this.menu.loading ||
21472
+ this.noMatchOption
21473
+ ) {
21474
+ if (this._userTyped) {
21475
+ if (!this.dropdownOpen) {
21476
+ this.showBib();
21477
+ }
21478
+ this._userTyped = false;
21479
+ }
21480
+ }
21481
+
21401
21482
  if (!this.availableOptions.includes(this.menu.optionActive)) {
21402
21483
  this.activateFirstEnabledAvailableOption();
21403
21484
  }
21404
- } else if (this.dropdown && this.dropdown.isPopoverVisible) {
21485
+ } else if (!this.dropdown.isBibFullscreen) {
21405
21486
  this.hideBib();
21406
21487
  }
21488
+
21489
+ if (this._programmaticFilterRefresh) {
21490
+ this._programmaticFilterRefresh = false;
21491
+ }
21407
21492
  }
21408
21493
 
21409
21494
  if (changedProperties.has('error')) {
@@ -21545,10 +21630,10 @@ class AuroCombobox extends AuroElement {
21545
21630
  shape="${this.shape}"
21546
21631
  size="${this.size}">
21547
21632
  <${this.inputTag}
21548
- @input="${this.handleInputValueChange}"
21633
+ @input="${this.handleTriggerInputValueChange}"
21549
21634
  appearance="${this.onDark ? 'inverse' : this.appearance}"
21550
21635
  .a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
21551
- .a11yExpanded="${this.triggerExpandedState}"
21636
+ .a11yExpanded="${this.dropdownOpen}"
21552
21637
  .a11yControls="${this.dropdownId}"
21553
21638
  .autocomplete="${this.autocomplete}"
21554
21639
  .inputmode="${this.inputmode}"