@aurodesignsystem-dev/auro-formkit 0.0.0-pr1604.0 → 0.0.0-pr1605.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/components/checkbox/demo/customize.min.js +1 -1
  2. package/components/checkbox/demo/getting-started.min.js +1 -1
  3. package/components/checkbox/demo/index.min.js +1 -1
  4. package/components/checkbox/dist/index.js +1 -1
  5. package/components/checkbox/dist/registered.js +1 -1
  6. package/components/combobox/demo/customize.min.js +153 -5
  7. package/components/combobox/demo/getting-started.min.js +153 -5
  8. package/components/combobox/demo/index.min.js +153 -5
  9. package/components/combobox/dist/auro-combobox.d.ts +22 -0
  10. package/components/combobox/dist/index.js +137 -5
  11. package/components/combobox/dist/registered.js +137 -5
  12. package/components/counter/demo/customize.min.js +339 -23
  13. package/components/counter/demo/getting-started.min.js +339 -23
  14. package/components/counter/demo/index.min.js +339 -23
  15. package/components/counter/dist/counterGroupKeyboardStrategy.d.ts +1 -0
  16. package/components/counter/dist/index.js +339 -23
  17. package/components/counter/dist/registered.js +339 -23
  18. package/components/datepicker/demo/customize.min.js +94 -5
  19. package/components/datepicker/demo/getting-started.min.js +94 -5
  20. package/components/datepicker/demo/index.min.js +94 -5
  21. package/components/datepicker/dist/auro-datepicker.d.ts +7 -0
  22. package/components/datepicker/dist/index.js +94 -5
  23. package/components/datepicker/dist/registered.js +94 -5
  24. package/components/dropdown/demo/customize.min.js +44 -2
  25. package/components/dropdown/demo/getting-started.min.js +45 -3
  26. package/components/dropdown/demo/index.min.js +44 -2
  27. package/components/dropdown/dist/index.js +44 -2
  28. package/components/dropdown/dist/registered.js +44 -2
  29. package/components/form/demo/customize.min.js +634 -39
  30. package/components/form/demo/getting-started.min.js +634 -39
  31. package/components/form/demo/index.min.js +634 -39
  32. package/components/form/demo/registerDemoDeps.min.js +634 -39
  33. package/components/input/demo/customize.min.js +1 -1
  34. package/components/input/demo/getting-started.min.js +1 -1
  35. package/components/input/demo/index.min.js +1 -1
  36. package/components/input/dist/index.js +1 -1
  37. package/components/input/dist/registered.js +1 -1
  38. package/components/menu/demo/index.min.js +16 -0
  39. package/components/menu/dist/index.js +16 -0
  40. package/components/menu/dist/registered.js +16 -0
  41. package/components/radio/demo/customize.min.js +1 -1
  42. package/components/radio/demo/getting-started.min.js +1 -1
  43. package/components/radio/demo/index.min.js +1 -1
  44. package/components/radio/dist/index.js +1 -1
  45. package/components/radio/dist/registered.js +1 -1
  46. package/components/select/demo/customize.min.js +61 -3
  47. package/components/select/demo/getting-started.min.js +61 -3
  48. package/components/select/demo/index.min.js +61 -3
  49. package/components/select/dist/index.js +45 -3
  50. package/components/select/dist/registered.js +45 -3
  51. package/custom-elements.json +31 -1
  52. package/package.json +3 -3
@@ -3018,6 +3018,48 @@ const computePosition = (reference, floating, options) => {
3018
3018
  });
3019
3019
  };
3020
3020
 
3021
+ /**
3022
+ * Generates a v4 UUID with a three-tier fallback for insecure contexts.
3023
+ *
3024
+ * Tier 1 (secure contexts): crypto.randomUUID() — native, fully random.
3025
+ * Tier 2 (insecure contexts): crypto.getRandomValues() — available on all
3026
+ * modern browsers regardless of secure-context status.
3027
+ * Tier 3 (last resort, no crypto API): new Date().getTime() — lower entropy,
3028
+ * but sufficient for element IDs where collision risk is negligible.
3029
+ *
3030
+ * See: https://w3c.github.io/webcrypto/#dom-crypto-randomuuid (secure-context restriction)
3031
+ */
3032
+ function generateUUID() {
3033
+ let uuid;
3034
+
3035
+ if (
3036
+ typeof crypto !== "undefined" &&
3037
+ typeof crypto.randomUUID === "function"
3038
+ ) {
3039
+ uuid = crypto.randomUUID();
3040
+ } else if (
3041
+ typeof crypto !== "undefined" &&
3042
+ typeof crypto.getRandomValues === "function"
3043
+ ) {
3044
+ const bytes = new Uint8Array(16);
3045
+ crypto.getRandomValues(bytes);
3046
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
3047
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
3048
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0"));
3049
+ uuid = `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
3050
+ } else {
3051
+ let d = new Date().getTime();
3052
+ uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
3053
+ const r = (d % 16) | 0;
3054
+ d = (d - r) / 16 || new Date().getTime();
3055
+ return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
3056
+ });
3057
+ }
3058
+
3059
+ // CSS selectors require IDs starting with a letter; replace a leading digit with 'a'.
3060
+ return /^[0-9]/.test(uuid) ? `a${uuid.slice(1)}` : uuid;
3061
+ }
3062
+
3021
3063
  /* eslint-disable line-comment-position, no-inline-comments */
3022
3064
 
3023
3065
 
@@ -4036,7 +4078,7 @@ class AuroFloatingUI {
4036
4078
 
4037
4079
  this.id = element.getAttribute("id");
4038
4080
  if (!this.id) {
4039
- this.id = window.crypto.randomUUID();
4081
+ this.id = generateUUID();
4040
4082
  element.setAttribute("id", this.id);
4041
4083
  }
4042
4084
 
@@ -5081,7 +5123,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
5081
5123
  }
5082
5124
  };
5083
5125
 
5084
- var formkitVersion$2 = '202609011830';
5126
+ var formkitVersion$2 = '202609022200';
5085
5127
 
5086
5128
  /**
5087
5129
  * AuroElement is the shared base class for layout-aware Auro form components.
@@ -18950,7 +18992,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18950
18992
  }
18951
18993
  };
18952
18994
 
18953
- var formkitVersion$1 = '202609011830';
18995
+ var formkitVersion$1 = '202609022200';
18954
18996
 
18955
18997
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18956
18998
  // See LICENSE in the project root for license information.
@@ -20142,7 +20184,7 @@ class AuroBibtemplate extends i$3 {
20142
20184
  }
20143
20185
  }
20144
20186
 
20145
- var formkitVersion = '202609011830';
20187
+ var formkitVersion = '202609022200';
20146
20188
 
20147
20189
  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}`;
20148
20190
 
@@ -20583,6 +20625,11 @@ class AuroCombobox extends AuroElement {
20583
20625
  this.behavior = "suggestion";
20584
20626
  this.clearBtnFocused = false;
20585
20627
 
20628
+ // One-shot: set when the input's clear button is activated so the
20629
+ // SPA-preselect guard in handleInputValueChange doesn't swallow the
20630
+ // genuine clear (AB#1634257).
20631
+ this._clearBtnActivated = false;
20632
+
20586
20633
  // Defaults that effect the overall layout of the combobox
20587
20634
  this.checkmark = false;
20588
20635
  this.dvInputOnly = false;
@@ -21540,6 +21587,13 @@ class AuroCombobox extends AuroElement {
21540
21587
  this.bibtemplate = this.dropdown.querySelector(this.bibtemplateTag._$litStatic$);
21541
21588
  this.inputInBib = this.bibtemplate.querySelector(this.inputTag._$litStatic$);
21542
21589
 
21590
+ // Mirror the trigger's clear-button tracking onto the fullscreen bib input
21591
+ // so clearing there also fully resets the combobox (AB#1634257). See
21592
+ // trackClearBtnActivation for why this must run in the capture phase.
21593
+ if (this.inputInBib && this.inputInBib !== this.input) {
21594
+ this.trackClearBtnActivation(this.inputInBib);
21595
+ }
21596
+
21543
21597
  // Pass label text to the dropdown bib for accessible dialog naming
21544
21598
  const labelElement = this.querySelector('[slot="label"]');
21545
21599
  if (labelElement) {
@@ -21925,6 +21979,52 @@ class AuroCombobox extends AuroElement {
21925
21979
  this.clearBtnFocused = false;
21926
21980
  }
21927
21981
  });
21982
+
21983
+ this.trackClearBtnActivation(this.input);
21984
+ }
21985
+
21986
+ /**
21987
+ * Flags genuine clear-button activations on an auro-input so the SPA-preselect
21988
+ * guard in handleInputValueChange doesn't swallow them.
21989
+ *
21990
+ * The auro-input clear button fires an `isProgrammatic` input event (via
21991
+ * notifyValueChanged) that is indistinguishable in shape from the
21992
+ * SPA-preselect echo that guard bails on (isProgrammatic + set value + empty
21993
+ * input). Without this one-shot flag, clicking clear would leave
21994
+ * this.value/optionSelected stale (AB#1634257).
21995
+ *
21996
+ * Delegated on the shadow root so it works regardless of when .clearBtn
21997
+ * renders (it only exists after a value is set), and covers both real clicks
21998
+ * and programmatic clearBtn.click() (which no focusin precedes). Registered in
21999
+ * the CAPTURE phase so the flag is set BEFORE the button's own @click
22000
+ * (handleClickClear) fires its synchronous input event — which is when
22001
+ * handleInputValueChange reads the flag.
22002
+ * @private
22003
+ * @param {Element} inputEl - The auro-input whose clear button to track.
22004
+ * @returns {void}
22005
+ */
22006
+ trackClearBtnActivation(inputEl) {
22007
+ if (!inputEl || !inputEl.shadowRoot) {
22008
+ return;
22009
+ }
22010
+ inputEl.shadowRoot.addEventListener('click', (event) => {
22011
+ if (event.target.closest('.clearBtn')) {
22012
+ this._clearBtnActivated = true;
22013
+
22014
+ // Bound the flag's lifetime to this synchronous clear -> input window.
22015
+ // auro-input's handleClickClear fires its input event synchronously
22016
+ // (before any macrotask runs), so handleInputValueChange consumes the
22017
+ // flag first and this reset is a harmless no-op. It only does real work
22018
+ // if that input event never arrives (e.g. auro-input throws mid-clear),
22019
+ // guaranteeing the flag can't survive to be misread as a genuine clear
22020
+ // by a later unrelated event such as an SPA-preselect echo (AB#1634257).
22021
+ // Scheduled via _scheduleTimer so disconnectedCallback cancels it if the
22022
+ // component tears down inside that window.
22023
+ this._scheduleTimer(() => {
22024
+ this._clearBtnActivated = false;
22025
+ }, 0);
22026
+ }
22027
+ }, true);
21928
22028
  }
21929
22029
 
21930
22030
  /**
@@ -21966,6 +22066,17 @@ class AuroCombobox extends AuroElement {
21966
22066
  * @returns {void}
21967
22067
  */
21968
22068
  handleInputValueChange(event) {
22069
+ // Consume the one-shot clear-button flag on EVERY entry, before any branch
22070
+ // below can early-return. trackClearBtnActivation sets it in the capture
22071
+ // phase right before auro-input's clear button fires its synchronous input
22072
+ // event; reading and immediately clearing it here guarantees the flag never
22073
+ // survives a handleInputValueChange call to leak into a later event (e.g.
22074
+ // the bib branch bailing at _syncingDisplayValue, or an SPA-preselect echo)
22075
+ // where it would defeat the guard below. Held in a local so that guard can
22076
+ // still tell a genuine clear from the init-render echo (AB#1634257).
22077
+ const clearBtnActivated = this._clearBtnActivated;
22078
+ this._clearBtnActivated = false;
22079
+
21969
22080
  // When the event comes from the fullscreen bib input, sync the value to
21970
22081
  // the trigger input. Setting trigger.value triggers Lit's updated()
21971
22082
  // (async, microtask) which fires notifyValueChanged() → another 'input'
@@ -22013,7 +22124,12 @@ class AuroCombobox extends AuroElement {
22013
22124
  // to the exact shape of that echo (isProgrammatic + non-empty combobox
22014
22125
  // value + empty input.value) so it doesn't affect the swap/typed paths
22015
22126
  // where value/input already track each other through the sync helpers.
22016
- if (event && event.isProgrammatic && this.value && !this.input.value) {
22127
+ // The clear button's echo has the same shape (isProgrammatic + set value +
22128
+ // empty input) but is a genuine user clear that MUST reset this.value and
22129
+ // optionSelected below. clearBtnActivated (consumed at the top of this
22130
+ // method) is true only for that genuine clear, so only the real
22131
+ // SPA-preselect echo bails (AB#1634257).
22132
+ if (event && event.isProgrammatic && this.value && !this.input.value && !clearBtnActivated) {
22017
22133
  return;
22018
22134
  }
22019
22135
 
@@ -22158,6 +22274,14 @@ class AuroCombobox extends AuroElement {
22158
22274
  // retry would otherwise keep rescheduling itself indefinitely.
22159
22275
  this._pendingTimers.forEach((id) => clearTimeout(id));
22160
22276
  this._pendingTimers.clear();
22277
+
22278
+ // Cancelling the timers above drops the macrotask that would have reset
22279
+ // _clearBtnActivated, so reset it here too. Otherwise a disconnect inside
22280
+ // the clear-click -> macrotask window strands the flag `true`; instance
22281
+ // state survives reconnect, and the first matching input event (including
22282
+ // an SPA-preselect echo) would then bypass the guard in
22283
+ // handleInputValueChange and silently clear value/optionSelected (AB#1634257).
22284
+ this._clearBtnActivated = false;
22161
22285
  }
22162
22286
 
22163
22287
  firstUpdated() {
@@ -22256,6 +22380,14 @@ class AuroCombobox extends AuroElement {
22256
22380
  }
22257
22381
  if (this.menu.value || this.menu.optionSelected) {
22258
22382
  this.menu.reset();
22383
+ } else {
22384
+ // The menu's selection properties are already cleared, but option
22385
+ // elements can still carry a stale `selected` attribute (e.g. the menu
22386
+ // options were rebuilt after the value was set, orphaning the DOM from
22387
+ // the now-null optionSelected). reset() is skipped above in that case,
22388
+ // so re-sync the option DOM directly to guarantee no option renders as
22389
+ // selected once the field is cleared (AB#1634259).
22390
+ this.menu.clearSelection();
22259
22391
  }
22260
22392
  } finally {
22261
22393
  this._clearing = false;
@@ -23707,6 +23839,22 @@ class AuroMenu extends AuroElement {
23707
23839
  this.value = undefined;
23708
23840
  this._selectedKey = undefined;
23709
23841
  this._index = -1;
23842
+
23843
+ // Eagerly sync the option DOM to the now-empty selection. Nulling the
23844
+ // reactive properties above normally schedules updated() → updateItemsState,
23845
+ // but that only runs when a property actually changes. If an option still
23846
+ // carries a stale `selected`/`aria-selected` attribute while these
23847
+ // properties are already undefined (e.g. options were rebuilt after a value
23848
+ // was set), no re-render fires and the option keeps rendering as selected.
23849
+ // Strip that state now so no option looks selected after a clear (AB#1634259).
23850
+ // Intentionally leaves `active`/`optionActive` untouched so a highlighted
23851
+ // option remains keyboard-re-selectable after emptying the value (AB#1606433).
23852
+ this.updateItemsState(new Map([
23853
+ [
23854
+ 'optionSelected',
23855
+ true
23856
+ ]
23857
+ ]));
23710
23858
  }
23711
23859
 
23712
23860
  /**
@@ -351,6 +351,7 @@ export class AuroCombobox extends AuroElement {
351
351
  /** @type {'filter' | 'suggestion'} */
352
352
  behavior: "filter" | "suggestion" | undefined;
353
353
  clearBtnFocused: boolean | undefined;
354
+ _clearBtnActivated: boolean | undefined;
354
355
  checkmark: boolean | undefined;
355
356
  dvInputOnly: boolean | undefined;
356
357
  /** @type {'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'disabled'} */
@@ -538,6 +539,27 @@ export class AuroCombobox extends AuroElement {
538
539
  * @returns {void}
539
540
  */
540
541
  private configureInput;
542
+ /**
543
+ * Flags genuine clear-button activations on an auro-input so the SPA-preselect
544
+ * guard in handleInputValueChange doesn't swallow them.
545
+ *
546
+ * The auro-input clear button fires an `isProgrammatic` input event (via
547
+ * notifyValueChanged) that is indistinguishable in shape from the
548
+ * SPA-preselect echo that guard bails on (isProgrammatic + set value + empty
549
+ * input). Without this one-shot flag, clicking clear would leave
550
+ * this.value/optionSelected stale (AB#1634257).
551
+ *
552
+ * Delegated on the shadow root so it works regardless of when .clearBtn
553
+ * renders (it only exists after a value is set), and covers both real clicks
554
+ * and programmatic clearBtn.click() (which no focusin precedes). Registered in
555
+ * the CAPTURE phase so the flag is set BEFORE the button's own @click
556
+ * (handleClickClear) fires its synchronous input event — which is when
557
+ * handleInputValueChange reads the flag.
558
+ * @private
559
+ * @param {Element} inputEl - The auro-input whose clear button to track.
560
+ * @returns {void}
561
+ */
562
+ private trackClearBtnActivation;
541
563
  /**
542
564
  * Manages the visibility of the dropdown based on loading state changes.
543
565
  *
@@ -2936,6 +2936,48 @@ const computePosition = (reference, floating, options) => {
2936
2936
  });
2937
2937
  };
2938
2938
 
2939
+ /**
2940
+ * Generates a v4 UUID with a three-tier fallback for insecure contexts.
2941
+ *
2942
+ * Tier 1 (secure contexts): crypto.randomUUID() — native, fully random.
2943
+ * Tier 2 (insecure contexts): crypto.getRandomValues() — available on all
2944
+ * modern browsers regardless of secure-context status.
2945
+ * Tier 3 (last resort, no crypto API): new Date().getTime() — lower entropy,
2946
+ * but sufficient for element IDs where collision risk is negligible.
2947
+ *
2948
+ * See: https://w3c.github.io/webcrypto/#dom-crypto-randomuuid (secure-context restriction)
2949
+ */
2950
+ function generateUUID() {
2951
+ let uuid;
2952
+
2953
+ if (
2954
+ typeof crypto !== "undefined" &&
2955
+ typeof crypto.randomUUID === "function"
2956
+ ) {
2957
+ uuid = crypto.randomUUID();
2958
+ } else if (
2959
+ typeof crypto !== "undefined" &&
2960
+ typeof crypto.getRandomValues === "function"
2961
+ ) {
2962
+ const bytes = new Uint8Array(16);
2963
+ crypto.getRandomValues(bytes);
2964
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
2965
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
2966
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0"));
2967
+ uuid = `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
2968
+ } else {
2969
+ let d = new Date().getTime();
2970
+ uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2971
+ const r = (d % 16) | 0;
2972
+ d = (d - r) / 16 || new Date().getTime();
2973
+ return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
2974
+ });
2975
+ }
2976
+
2977
+ // CSS selectors require IDs starting with a letter; replace a leading digit with 'a'.
2978
+ return /^[0-9]/.test(uuid) ? `a${uuid.slice(1)}` : uuid;
2979
+ }
2980
+
2939
2981
  /* eslint-disable line-comment-position, no-inline-comments */
2940
2982
 
2941
2983
 
@@ -3954,7 +3996,7 @@ class AuroFloatingUI {
3954
3996
 
3955
3997
  this.id = element.getAttribute("id");
3956
3998
  if (!this.id) {
3957
- this.id = window.crypto.randomUUID();
3999
+ this.id = generateUUID();
3958
4000
  element.setAttribute("id", this.id);
3959
4001
  }
3960
4002
 
@@ -4999,7 +5041,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
4999
5041
  }
5000
5042
  };
5001
5043
 
5002
- var formkitVersion$2 = '202609011830';
5044
+ var formkitVersion$2 = '202609022200';
5003
5045
 
5004
5046
  /**
5005
5047
  * AuroElement is the shared base class for layout-aware Auro form components.
@@ -18861,7 +18903,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
18861
18903
  }
18862
18904
  };
18863
18905
 
18864
- var formkitVersion$1 = '202609011830';
18906
+ var formkitVersion$1 = '202609022200';
18865
18907
 
18866
18908
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18867
18909
  // See LICENSE in the project root for license information.
@@ -20053,7 +20095,7 @@ class AuroBibtemplate extends LitElement {
20053
20095
  }
20054
20096
  }
20055
20097
 
20056
- var formkitVersion = '202609011830';
20098
+ var formkitVersion = '202609022200';
20057
20099
 
20058
20100
  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}`;
20059
20101
 
@@ -20494,6 +20536,11 @@ class AuroCombobox extends AuroElement {
20494
20536
  this.behavior = "suggestion";
20495
20537
  this.clearBtnFocused = false;
20496
20538
 
20539
+ // One-shot: set when the input's clear button is activated so the
20540
+ // SPA-preselect guard in handleInputValueChange doesn't swallow the
20541
+ // genuine clear (AB#1634257).
20542
+ this._clearBtnActivated = false;
20543
+
20497
20544
  // Defaults that effect the overall layout of the combobox
20498
20545
  this.checkmark = false;
20499
20546
  this.dvInputOnly = false;
@@ -21451,6 +21498,13 @@ class AuroCombobox extends AuroElement {
21451
21498
  this.bibtemplate = this.dropdown.querySelector(this.bibtemplateTag._$litStatic$);
21452
21499
  this.inputInBib = this.bibtemplate.querySelector(this.inputTag._$litStatic$);
21453
21500
 
21501
+ // Mirror the trigger's clear-button tracking onto the fullscreen bib input
21502
+ // so clearing there also fully resets the combobox (AB#1634257). See
21503
+ // trackClearBtnActivation for why this must run in the capture phase.
21504
+ if (this.inputInBib && this.inputInBib !== this.input) {
21505
+ this.trackClearBtnActivation(this.inputInBib);
21506
+ }
21507
+
21454
21508
  // Pass label text to the dropdown bib for accessible dialog naming
21455
21509
  const labelElement = this.querySelector('[slot="label"]');
21456
21510
  if (labelElement) {
@@ -21836,6 +21890,52 @@ class AuroCombobox extends AuroElement {
21836
21890
  this.clearBtnFocused = false;
21837
21891
  }
21838
21892
  });
21893
+
21894
+ this.trackClearBtnActivation(this.input);
21895
+ }
21896
+
21897
+ /**
21898
+ * Flags genuine clear-button activations on an auro-input so the SPA-preselect
21899
+ * guard in handleInputValueChange doesn't swallow them.
21900
+ *
21901
+ * The auro-input clear button fires an `isProgrammatic` input event (via
21902
+ * notifyValueChanged) that is indistinguishable in shape from the
21903
+ * SPA-preselect echo that guard bails on (isProgrammatic + set value + empty
21904
+ * input). Without this one-shot flag, clicking clear would leave
21905
+ * this.value/optionSelected stale (AB#1634257).
21906
+ *
21907
+ * Delegated on the shadow root so it works regardless of when .clearBtn
21908
+ * renders (it only exists after a value is set), and covers both real clicks
21909
+ * and programmatic clearBtn.click() (which no focusin precedes). Registered in
21910
+ * the CAPTURE phase so the flag is set BEFORE the button's own @click
21911
+ * (handleClickClear) fires its synchronous input event — which is when
21912
+ * handleInputValueChange reads the flag.
21913
+ * @private
21914
+ * @param {Element} inputEl - The auro-input whose clear button to track.
21915
+ * @returns {void}
21916
+ */
21917
+ trackClearBtnActivation(inputEl) {
21918
+ if (!inputEl || !inputEl.shadowRoot) {
21919
+ return;
21920
+ }
21921
+ inputEl.shadowRoot.addEventListener('click', (event) => {
21922
+ if (event.target.closest('.clearBtn')) {
21923
+ this._clearBtnActivated = true;
21924
+
21925
+ // Bound the flag's lifetime to this synchronous clear -> input window.
21926
+ // auro-input's handleClickClear fires its input event synchronously
21927
+ // (before any macrotask runs), so handleInputValueChange consumes the
21928
+ // flag first and this reset is a harmless no-op. It only does real work
21929
+ // if that input event never arrives (e.g. auro-input throws mid-clear),
21930
+ // guaranteeing the flag can't survive to be misread as a genuine clear
21931
+ // by a later unrelated event such as an SPA-preselect echo (AB#1634257).
21932
+ // Scheduled via _scheduleTimer so disconnectedCallback cancels it if the
21933
+ // component tears down inside that window.
21934
+ this._scheduleTimer(() => {
21935
+ this._clearBtnActivated = false;
21936
+ }, 0);
21937
+ }
21938
+ }, true);
21839
21939
  }
21840
21940
 
21841
21941
  /**
@@ -21877,6 +21977,17 @@ class AuroCombobox extends AuroElement {
21877
21977
  * @returns {void}
21878
21978
  */
21879
21979
  handleInputValueChange(event) {
21980
+ // Consume the one-shot clear-button flag on EVERY entry, before any branch
21981
+ // below can early-return. trackClearBtnActivation sets it in the capture
21982
+ // phase right before auro-input's clear button fires its synchronous input
21983
+ // event; reading and immediately clearing it here guarantees the flag never
21984
+ // survives a handleInputValueChange call to leak into a later event (e.g.
21985
+ // the bib branch bailing at _syncingDisplayValue, or an SPA-preselect echo)
21986
+ // where it would defeat the guard below. Held in a local so that guard can
21987
+ // still tell a genuine clear from the init-render echo (AB#1634257).
21988
+ const clearBtnActivated = this._clearBtnActivated;
21989
+ this._clearBtnActivated = false;
21990
+
21880
21991
  // When the event comes from the fullscreen bib input, sync the value to
21881
21992
  // the trigger input. Setting trigger.value triggers Lit's updated()
21882
21993
  // (async, microtask) which fires notifyValueChanged() → another 'input'
@@ -21924,7 +22035,12 @@ class AuroCombobox extends AuroElement {
21924
22035
  // to the exact shape of that echo (isProgrammatic + non-empty combobox
21925
22036
  // value + empty input.value) so it doesn't affect the swap/typed paths
21926
22037
  // where value/input already track each other through the sync helpers.
21927
- if (event && event.isProgrammatic && this.value && !this.input.value) {
22038
+ // The clear button's echo has the same shape (isProgrammatic + set value +
22039
+ // empty input) but is a genuine user clear that MUST reset this.value and
22040
+ // optionSelected below. clearBtnActivated (consumed at the top of this
22041
+ // method) is true only for that genuine clear, so only the real
22042
+ // SPA-preselect echo bails (AB#1634257).
22043
+ if (event && event.isProgrammatic && this.value && !this.input.value && !clearBtnActivated) {
21928
22044
  return;
21929
22045
  }
21930
22046
 
@@ -22069,6 +22185,14 @@ class AuroCombobox extends AuroElement {
22069
22185
  // retry would otherwise keep rescheduling itself indefinitely.
22070
22186
  this._pendingTimers.forEach((id) => clearTimeout(id));
22071
22187
  this._pendingTimers.clear();
22188
+
22189
+ // Cancelling the timers above drops the macrotask that would have reset
22190
+ // _clearBtnActivated, so reset it here too. Otherwise a disconnect inside
22191
+ // the clear-click -> macrotask window strands the flag `true`; instance
22192
+ // state survives reconnect, and the first matching input event (including
22193
+ // an SPA-preselect echo) would then bypass the guard in
22194
+ // handleInputValueChange and silently clear value/optionSelected (AB#1634257).
22195
+ this._clearBtnActivated = false;
22072
22196
  }
22073
22197
 
22074
22198
  firstUpdated() {
@@ -22167,6 +22291,14 @@ class AuroCombobox extends AuroElement {
22167
22291
  }
22168
22292
  if (this.menu.value || this.menu.optionSelected) {
22169
22293
  this.menu.reset();
22294
+ } else {
22295
+ // The menu's selection properties are already cleared, but option
22296
+ // elements can still carry a stale `selected` attribute (e.g. the menu
22297
+ // options were rebuilt after the value was set, orphaning the DOM from
22298
+ // the now-null optionSelected). reset() is skipped above in that case,
22299
+ // so re-sync the option DOM directly to guarantee no option renders as
22300
+ // selected once the field is cleared (AB#1634259).
22301
+ this.menu.clearSelection();
22170
22302
  }
22171
22303
  } finally {
22172
22304
  this._clearing = false;