@aurodesignsystem/auro-formkit 5.10.0 → 5.11.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 (77) hide show
  1. package/CHANGELOG.md +55 -15
  2. package/components/bibtemplate/dist/auro-bibtemplate.d.ts +6 -0
  3. package/components/bibtemplate/dist/index.js +12 -0
  4. package/components/bibtemplate/dist/registered.js +12 -0
  5. package/components/checkbox/demo/api.min.js +3 -3
  6. package/components/checkbox/demo/index.min.js +3 -3
  7. package/components/checkbox/dist/index.js +3 -3
  8. package/components/checkbox/dist/registered.js +3 -3
  9. package/components/combobox/demo/api.min.js +1140 -305
  10. package/components/combobox/demo/index.min.js +1140 -305
  11. package/components/combobox/dist/auro-combobox.d.ts +24 -1
  12. package/components/combobox/dist/comboboxKeyboardStrategy.d.ts +6 -0
  13. package/components/combobox/dist/index.js +1082 -264
  14. package/components/combobox/dist/registered.js +1082 -264
  15. package/components/counter/demo/api.min.js +583 -172
  16. package/components/counter/demo/index.min.js +583 -172
  17. package/components/counter/dist/auro-counter.d.ts +8 -0
  18. package/components/counter/dist/buttonVersion.d.ts +1 -1
  19. package/components/counter/dist/iconVersion.d.ts +1 -1
  20. package/components/counter/dist/index.js +583 -172
  21. package/components/counter/dist/registered.js +583 -172
  22. package/components/datepicker/demo/api.md +108 -85
  23. package/components/datepicker/demo/api.min.js +872 -257
  24. package/components/datepicker/demo/index.md +18 -12
  25. package/components/datepicker/demo/index.min.js +872 -257
  26. package/components/datepicker/dist/auro-calendar.d.ts +6 -0
  27. package/components/datepicker/dist/auro-datepicker.d.ts +11 -1
  28. package/components/datepicker/dist/index.js +819 -208
  29. package/components/datepicker/dist/registered.js +819 -208
  30. package/components/dropdown/demo/api.md +15 -17
  31. package/components/dropdown/demo/api.min.js +537 -183
  32. package/components/dropdown/demo/index.min.js +537 -183
  33. package/components/dropdown/dist/auro-dropdown.d.ts +27 -1
  34. package/components/dropdown/dist/auro-dropdownBib.d.ts +87 -0
  35. package/components/dropdown/dist/index.js +514 -160
  36. package/components/dropdown/dist/keyboardUtils.d.ts +18 -0
  37. package/components/dropdown/dist/registered.js +514 -160
  38. package/components/form/README.md +47 -2
  39. package/components/form/demo/api.js +2 -0
  40. package/components/form/demo/api.md +303 -30
  41. package/components/form/demo/api.min.js +69256 -62
  42. package/components/form/demo/index.html +0 -1
  43. package/components/form/demo/index.js +1 -0
  44. package/components/form/demo/index.md +1 -275
  45. package/components/form/demo/index.min.js +69255 -62
  46. package/components/form/demo/readme.md +47 -2
  47. package/components/form/demo/working.html +123 -32
  48. package/components/form/dist/auro-form.d.ts +98 -61
  49. package/components/form/dist/index.js +135 -51
  50. package/components/form/dist/registered.js +135 -51
  51. package/components/input/demo/api.md +1 -0
  52. package/components/input/demo/api.min.js +78 -24
  53. package/components/input/demo/index.min.js +78 -24
  54. package/components/input/dist/base-input.d.ts +34 -0
  55. package/components/input/dist/index.js +78 -24
  56. package/components/input/dist/registered.js +78 -24
  57. package/components/menu/demo/api.md +4 -10
  58. package/components/menu/demo/api.min.js +18 -5
  59. package/components/menu/demo/index.min.js +18 -5
  60. package/components/menu/dist/auro-menuoption.d.ts +0 -8
  61. package/components/menu/dist/iconVersion.d.ts +1 -1
  62. package/components/menu/dist/index.js +18 -5
  63. package/components/menu/dist/registered.js +18 -5
  64. package/components/radio/demo/api.min.js +3 -3
  65. package/components/radio/demo/index.min.js +3 -3
  66. package/components/radio/dist/index.js +3 -3
  67. package/components/radio/dist/registered.js +3 -3
  68. package/components/select/demo/api.js +2 -0
  69. package/components/select/demo/api.md +333 -78
  70. package/components/select/demo/api.min.js +945 -282
  71. package/components/select/demo/index.min.js +933 -282
  72. package/components/select/dist/auro-select.d.ts +26 -0
  73. package/components/select/dist/index.js +881 -247
  74. package/components/select/dist/registered.js +881 -247
  75. package/components/select/dist/selectKeyboardStrategy.d.ts +8 -0
  76. package/custom-elements.json +596 -89
  77. package/package.json +7 -5
@@ -256,7 +256,7 @@ let p$3 = class p{registerComponent(t,a){customElements.get(t)||customElements.d
256
256
  </div>
257
257
  `}};
258
258
 
259
- var iconVersion$2 = '9.1.0';
259
+ var iconVersion$2 = '9.1.2';
260
260
 
261
261
  class DateFormatter {
262
262
 
@@ -1014,7 +1014,7 @@ class AuroFormValidation {
1014
1014
  }
1015
1015
  }
1016
1016
 
1017
- if (!hasValue && elem.required && elem.touched) {
1017
+ if (!hasValue && elem.required && (force || elem.touched)) {
1018
1018
  elem.validity = 'valueMissing';
1019
1019
  elem.errorMessage = elem.setCustomValidityValueMissing || elem.setCustomValidity || '';
1020
1020
  } else if (hasValue && this.runtimeUtils.elementMatch(elem, 'auro-input')) {
@@ -1038,7 +1038,7 @@ class AuroFormValidation {
1038
1038
  if (!isCombobox || isCombobox && !elem.persistInput) {
1039
1039
 
1040
1040
  // run validation on all inputs since we're going to use them to set the validity of this component
1041
- this.auroInputElements.forEach(input => input.validate());
1041
+ this.auroInputElements.forEach(input => input.validate(force));
1042
1042
 
1043
1043
  // Reset element validity to the validity of the input
1044
1044
  elem.validity = this.auroInputElements[0].validity;
@@ -1129,6 +1129,34 @@ class AuroFormValidation {
1129
1129
  }
1130
1130
  }
1131
1131
 
1132
+ /**
1133
+ * Announces text to screen readers via an `aria-live` region inside the given shadow root.
1134
+ *
1135
+ * Expects the shadow root to contain an element with `id="srAnnouncement"`.
1136
+ * The text is cleared and re-set inside a `requestAnimationFrame` so that
1137
+ * repeated identical announcements still fire, and is cleared again after
1138
+ * {@link ANNOUNCEMENT_DURATION_MS} so VoiceOver cannot swipe to stale text.
1139
+ *
1140
+ * @param {ShadowRoot} shadowRoot - The shadow root containing the live region.
1141
+ * @param {string} text - The text to announce.
1142
+ */
1143
+
1144
+
1145
+ /**
1146
+ * Schedules a callback after two animation frames.
1147
+ *
1148
+ * Used when opening a fullscreen dialog to wait for the dialog to render
1149
+ * (first frame) and then for a Lit update cycle to complete (second frame)
1150
+ * before performing an action like focusing the close button.
1151
+ *
1152
+ * @param {Function} fn - The callback to execute after two animation frames.
1153
+ */
1154
+ function doubleRaf(fn) {
1155
+ requestAnimationFrame(() => {
1156
+ requestAnimationFrame(fn);
1157
+ });
1158
+ }
1159
+
1132
1160
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
1133
1161
  // See LICENSE in the project root for license information.
1134
1162
 
@@ -1392,7 +1420,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
1392
1420
  }
1393
1421
  };
1394
1422
 
1395
- var formkitVersion$1 = '202602140013';
1423
+ var formkitVersion$1 = '202603102257';
1396
1424
 
1397
1425
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1398
1426
  // See LICENSE in the project root for license information.
@@ -1409,6 +1437,13 @@ var formkitVersion$1 = '202602140013';
1409
1437
  * @slot description - Descriptive content for the counter.
1410
1438
  */
1411
1439
  class AuroCounter extends LitElement {
1440
+ static get shadowRootOptions() {
1441
+ return {
1442
+ ...LitElement.shadowRootOptions,
1443
+ delegatesFocus: true,
1444
+ };
1445
+ }
1446
+
1412
1447
  constructor() {
1413
1448
  super();
1414
1449
 
@@ -1455,14 +1490,6 @@ class AuroCounter extends LitElement {
1455
1490
  */
1456
1491
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
1457
1492
 
1458
- /**
1459
- * @private
1460
- * @property {boolean} delegatesFocus - Whether the shadow root delegates focus.
1461
- */
1462
- this.constructor.shadowRootOptions = {
1463
- ...LitElement.shadowRootOptions,
1464
- delegatesFocus: true,
1465
- };
1466
1493
  }
1467
1494
 
1468
1495
  connectedCallback() {
@@ -1777,7 +1804,7 @@ class AuroCounter extends LitElement {
1777
1804
  aria-valuemax="${this.max}"
1778
1805
  aria-valuemin="${this.min}"
1779
1806
  aria-valuenow="${this.value}"
1780
- aria-valuetext="${this.value !== undefined ? this.value : this.min}"
1807
+ aria-valuetext="'${this.value !== undefined ? this.value : this.min}'"
1781
1808
  role="spinbutton"
1782
1809
  tabindex="${this.disabled ? '-1' : '0'}"
1783
1810
  >
@@ -3544,11 +3571,9 @@ const computePosition = (reference, floating, options) => {
3544
3571
  /* eslint-disable line-comment-position, no-inline-comments */
3545
3572
 
3546
3573
 
3547
-
3548
3574
  const MAX_CONFIGURATION_COUNT = 10;
3549
3575
 
3550
3576
  class AuroFloatingUI {
3551
-
3552
3577
  /**
3553
3578
  * @private
3554
3579
  */
@@ -3563,7 +3588,11 @@ class AuroFloatingUI {
3563
3588
  * @private
3564
3589
  */
3565
3590
  static setupMousePressChecker() {
3566
- if (!AuroFloatingUI.isMousePressHandlerInitialized && window && window.addEventListener) {
3591
+ if (
3592
+ !AuroFloatingUI.isMousePressHandlerInitialized &&
3593
+ window &&
3594
+ window.addEventListener
3595
+ ) {
3567
3596
  AuroFloatingUI.isMousePressHandlerInitialized = true;
3568
3597
 
3569
3598
  // Track timeout for isMousePressed reset to avoid race conditions
@@ -3571,7 +3600,7 @@ class AuroFloatingUI {
3571
3600
  AuroFloatingUI._mousePressedTimeout = null;
3572
3601
  }
3573
3602
  const mouseEventGlobalHandler = (event) => {
3574
- const isPressed = event.type === 'mousedown';
3603
+ const isPressed = event.type === "mousedown";
3575
3604
  if (isPressed) {
3576
3605
  // Clear any pending timeout to prevent race condition
3577
3606
  if (AuroFloatingUI._mousePressedTimeout !== null) {
@@ -3590,8 +3619,8 @@ class AuroFloatingUI {
3590
3619
  }
3591
3620
  };
3592
3621
 
3593
- window.addEventListener('mousedown', mouseEventGlobalHandler);
3594
- window.addEventListener('mouseup', mouseEventGlobalHandler);
3622
+ window.addEventListener("mousedown", mouseEventGlobalHandler);
3623
+ window.addEventListener("mouseup", mouseEventGlobalHandler);
3595
3624
  }
3596
3625
  }
3597
3626
 
@@ -3639,11 +3668,12 @@ class AuroFloatingUI {
3639
3668
  // mirror the boxsize from bibSizer
3640
3669
  if (this.element.bibSizer && this.element.matchWidth) {
3641
3670
  const sizerStyle = window.getComputedStyle(this.element.bibSizer);
3642
- const bibContent = this.element.bib.shadowRoot.querySelector(".container");
3643
- if (sizerStyle.width !== '0px') {
3671
+ const bibContent =
3672
+ this.element.bib.shadowRoot.querySelector(".container");
3673
+ if (sizerStyle.width !== "0px") {
3644
3674
  bibContent.style.width = sizerStyle.width;
3645
3675
  }
3646
- if (sizerStyle.height !== '0px') {
3676
+ if (sizerStyle.height !== "0px") {
3647
3677
  bibContent.style.height = sizerStyle.height;
3648
3678
  }
3649
3679
  bibContent.style.maxWidth = sizerStyle.maxWidth;
@@ -3661,28 +3691,34 @@ class AuroFloatingUI {
3661
3691
  * @returns {String} The positioning strategy, one of 'fullscreen', 'floating', 'cover'.
3662
3692
  */
3663
3693
  getPositioningStrategy() {
3664
- const breakpoint = this.element.bib.mobileFullscreenBreakpoint || this.element.floaterConfig?.fullscreenBreakpoint;
3694
+ const breakpoint =
3695
+ this.element.bib.mobileFullscreenBreakpoint ||
3696
+ this.element.floaterConfig?.fullscreenBreakpoint;
3665
3697
  switch (this.behavior) {
3666
3698
  case "tooltip":
3667
3699
  return "floating";
3668
3700
  case "dialog":
3669
3701
  case "drawer":
3670
3702
  if (breakpoint) {
3671
- const smallerThanBreakpoint = window.matchMedia(`(max-width: ${breakpoint})`).matches;
3703
+ const smallerThanBreakpoint = window.matchMedia(
3704
+ `(max-width: ${breakpoint})`,
3705
+ ).matches;
3672
3706
 
3673
3707
  this.element.expanded = smallerThanBreakpoint;
3674
3708
  }
3675
3709
  if (this.element.nested) {
3676
3710
  return "cover";
3677
3711
  }
3678
- return 'fullscreen';
3712
+ return "fullscreen";
3679
3713
  case "dropdown":
3680
3714
  case undefined:
3681
3715
  case null:
3682
3716
  if (breakpoint) {
3683
- const smallerThanBreakpoint = window.matchMedia(`(max-width: ${breakpoint})`).matches;
3717
+ const smallerThanBreakpoint = window.matchMedia(
3718
+ `(max-width: ${breakpoint})`,
3719
+ ).matches;
3684
3720
  if (smallerThanBreakpoint) {
3685
- return 'fullscreen';
3721
+ return "fullscreen";
3686
3722
  }
3687
3723
  }
3688
3724
  return "floating";
@@ -3703,37 +3739,39 @@ class AuroFloatingUI {
3703
3739
  const strategy = this.getPositioningStrategy();
3704
3740
  this.configureBibStrategy(strategy);
3705
3741
 
3706
- if (strategy === 'floating') {
3742
+ if (strategy === "floating") {
3707
3743
  this.mirrorSize();
3708
3744
  // Define the middlware for the floater configuration
3709
3745
  const middleware = [
3710
3746
  offset(this.element.floaterConfig?.offset || 0),
3711
- ...this.element.floaterConfig?.shift ? [shift()] : [], // Add shift middleware if shift is enabled.
3712
- ...this.element.floaterConfig?.flip ? [flip()] : [], // Add flip middleware if flip is enabled.
3713
- ...this.element.floaterConfig?.autoPlacement ? [autoPlacement()] : [], // Add autoPlacement middleware if autoPlacement is enabled.
3747
+ ...(this.element.floaterConfig?.shift ? [shift()] : []), // Add shift middleware if shift is enabled.
3748
+ ...(this.element.floaterConfig?.flip ? [flip()] : []), // Add flip middleware if flip is enabled.
3749
+ ...(this.element.floaterConfig?.autoPlacement ? [autoPlacement()] : []), // Add autoPlacement middleware if autoPlacement is enabled.
3714
3750
  ];
3715
3751
 
3716
3752
  // Compute the position of the bib
3717
3753
  computePosition(this.element.trigger, this.element.bib, {
3718
- strategy: this.element.floaterConfig?.strategy || 'fixed',
3754
+ strategy: this.element.floaterConfig?.strategy || "fixed",
3719
3755
  placement: this.element.floaterConfig?.placement,
3720
- middleware: middleware || []
3721
- }).then(({ x, y }) => { // eslint-disable-line id-length
3756
+ middleware: middleware || [],
3757
+ }).then(({ x, y }) => {
3758
+ // eslint-disable-line id-length
3722
3759
  Object.assign(this.element.bib.style, {
3723
3760
  left: `${x}px`,
3724
3761
  top: `${y}px`,
3725
3762
  });
3726
3763
  });
3727
- } else if (strategy === 'cover') {
3764
+ } else if (strategy === "cover") {
3728
3765
  // Compute the position of the bib
3729
3766
  computePosition(this.element.parentNode, this.element.bib, {
3730
- placement: 'bottom-start'
3731
- }).then(({ x, y }) => { // eslint-disable-line id-length
3767
+ placement: "bottom-start",
3768
+ }).then(({ x, y }) => {
3769
+ // eslint-disable-line id-length
3732
3770
  Object.assign(this.element.bib.style, {
3733
3771
  left: `${x}px`,
3734
3772
  top: `${y - this.element.parentNode.offsetHeight}px`,
3735
3773
  width: `${this.element.parentNode.offsetWidth}px`,
3736
- height: `${this.element.parentNode.offsetHeight}px`
3774
+ height: `${this.element.parentNode.offsetHeight}px`,
3737
3775
  });
3738
3776
  });
3739
3777
  }
@@ -3746,12 +3784,12 @@ class AuroFloatingUI {
3746
3784
  */
3747
3785
  lockScroll(lock = true) {
3748
3786
  if (lock) {
3749
- document.body.style.overflow = 'hidden'; // hide body's scrollbar
3787
+ document.body.style.overflow = "hidden"; // hide body's scrollbar
3750
3788
 
3751
3789
  // Move `bib` by the amount the viewport is shifted to stay aligned in fullscreen.
3752
3790
  this.element.bib.style.transform = `translateY(${window?.visualViewport?.offsetTop}px)`;
3753
3791
  } else {
3754
- document.body.style.overflow = '';
3792
+ document.body.style.overflow = "";
3755
3793
  }
3756
3794
  }
3757
3795
 
@@ -3765,23 +3803,24 @@ class AuroFloatingUI {
3765
3803
  * @param {string} strategy - The positioning strategy ('fullscreen' or 'floating').
3766
3804
  */
3767
3805
  configureBibStrategy(value) {
3768
- if (value === 'fullscreen') {
3806
+ if (value === "fullscreen") {
3769
3807
  this.element.isBibFullscreen = true;
3770
3808
  // reset the prev position
3771
- this.element.bib.setAttribute('isfullscreen', "");
3772
- this.element.bib.style.position = 'fixed';
3809
+ this.element.bib.setAttribute("isfullscreen", "");
3810
+ this.element.bib.style.position = "fixed";
3773
3811
  this.element.bib.style.top = "0px";
3774
3812
  this.element.bib.style.left = "0px";
3775
- this.element.bib.style.width = '';
3776
- this.element.bib.style.height = '';
3777
- this.element.style.contain = '';
3813
+ this.element.bib.style.width = "";
3814
+ this.element.bib.style.height = "";
3815
+ this.element.style.contain = "";
3778
3816
 
3779
3817
  // reset the size that was mirroring `size` css-part
3780
- const bibContent = this.element.bib.shadowRoot.querySelector(".container");
3818
+ const bibContent =
3819
+ this.element.bib.shadowRoot.querySelector(".container");
3781
3820
  if (bibContent) {
3782
- bibContent.style.width = '';
3783
- bibContent.style.height = '';
3784
- bibContent.style.maxWidth = '';
3821
+ bibContent.style.width = "";
3822
+ bibContent.style.height = "";
3823
+ bibContent.style.maxWidth = "";
3785
3824
  bibContent.style.maxHeight = `${window?.visualViewport?.height}px`;
3786
3825
  this.configureTrial = 0;
3787
3826
  } else if (this.configureTrial < MAX_CONFIGURATION_COUNT) {
@@ -3796,21 +3835,26 @@ class AuroFloatingUI {
3796
3835
  this.lockScroll(true);
3797
3836
  }
3798
3837
  } else {
3799
- this.element.bib.style.position = '';
3800
- this.element.bib.removeAttribute('isfullscreen');
3838
+ this.element.bib.style.position = "";
3839
+ this.element.bib.removeAttribute("isfullscreen");
3801
3840
  this.element.isBibFullscreen = false;
3802
- this.element.style.contain = 'layout';
3841
+ this.element.style.contain = "layout";
3803
3842
  }
3804
3843
 
3805
3844
  const isChanged = this.strategy && this.strategy !== value;
3806
3845
  this.strategy = value;
3807
3846
  if (isChanged) {
3808
- const event = new CustomEvent(this.eventPrefix ? `${this.eventPrefix}-strategy-change` : 'strategy-change', {
3809
- detail: {
3810
- value,
3847
+ const event = new CustomEvent(
3848
+ this.eventPrefix
3849
+ ? `${this.eventPrefix}-strategy-change`
3850
+ : "strategy-change",
3851
+ {
3852
+ detail: {
3853
+ value,
3854
+ },
3855
+ composed: true,
3811
3856
  },
3812
- composed: true
3813
- });
3857
+ );
3814
3858
 
3815
3859
  this.element.dispatchEvent(event);
3816
3860
  }
@@ -3842,19 +3886,24 @@ class AuroFloatingUI {
3842
3886
  return;
3843
3887
  }
3844
3888
 
3845
- if (this.element.noHideOnThisFocusLoss ||
3846
- this.element.hasAttribute('noHideOnThisFocusLoss')) {
3889
+ if (
3890
+ this.element.noHideOnThisFocusLoss ||
3891
+ this.element.hasAttribute("noHideOnThisFocusLoss")
3892
+ ) {
3847
3893
  return;
3848
3894
  }
3849
3895
 
3850
3896
  const { activeElement } = document;
3851
3897
  // if focus is still inside of trigger or bib, do not close
3852
- if (this.element.contains(activeElement) || this.element.bib?.contains(activeElement)) {
3898
+ if (
3899
+ this.element.contains(activeElement) ||
3900
+ this.element.bib?.contains(activeElement)
3901
+ ) {
3853
3902
  return;
3854
3903
  }
3855
3904
 
3856
3905
  // if fullscreen bib is in fullscreen mode, do not close
3857
- if (this.element.bib.hasAttribute('isfullscreen')) {
3906
+ if (this.element.bib.hasAttribute("isfullscreen")) {
3858
3907
  return;
3859
3908
  }
3860
3909
 
@@ -3866,12 +3915,27 @@ class AuroFloatingUI {
3866
3915
  this.focusHandler = () => this.handleFocusLoss();
3867
3916
 
3868
3917
  this.clickHandler = (evt) => {
3869
- if ((!evt.composedPath().includes(this.element.trigger) &&
3870
- !evt.composedPath().includes(this.element.bib)) ||
3871
- (this.element.bib.backdrop && evt.composedPath().includes(this.element.bib.backdrop))) {
3872
- const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
3918
+ // When the bib is fullscreen (modal dialog), don't close on outside
3919
+ // clicks. VoiceOver's synthetic click events inside a top-layer modal
3920
+ // <dialog> may not include the bib in composedPath(), causing false
3921
+ // positives. This mirrors the fullscreen guard in handleFocusLoss().
3922
+ if (this.element.bib && this.element.bib.hasAttribute("isfullscreen")) {
3923
+ return;
3924
+ }
3873
3925
 
3874
- if (existedVisibleFloatingUI && existedVisibleFloatingUI.element.isPopoverVisible) {
3926
+ if (
3927
+ (!evt.composedPath().includes(this.element.trigger) &&
3928
+ !evt.composedPath().includes(this.element.bib)) ||
3929
+ (this.element.bib.backdrop &&
3930
+ evt.composedPath().includes(this.element.bib.backdrop))
3931
+ ) {
3932
+ const existedVisibleFloatingUI =
3933
+ document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
3934
+
3935
+ if (
3936
+ existedVisibleFloatingUI &&
3937
+ existedVisibleFloatingUI.element.isPopoverVisible
3938
+ ) {
3875
3939
  // if something else is open, close that
3876
3940
  existedVisibleFloatingUI.hideBib();
3877
3941
  document.expandedAuroFormkitDropdown = null;
@@ -3884,9 +3948,14 @@ class AuroFloatingUI {
3884
3948
 
3885
3949
  // ESC key handler
3886
3950
  this.keyDownHandler = (evt) => {
3887
- if (evt.key === 'Escape' && this.element.isPopoverVisible) {
3888
- const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
3889
- if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this && existedVisibleFloatingUI.element.isPopoverVisible) {
3951
+ if (evt.key === "Escape" && this.element.isPopoverVisible) {
3952
+ const existedVisibleFloatingUI =
3953
+ document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
3954
+ if (
3955
+ existedVisibleFloatingUI &&
3956
+ existedVisibleFloatingUI !== this &&
3957
+ existedVisibleFloatingUI.element.isPopoverVisible
3958
+ ) {
3890
3959
  // if something else is open, let it handle itself
3891
3960
  return;
3892
3961
  }
@@ -3894,17 +3963,17 @@ class AuroFloatingUI {
3894
3963
  }
3895
3964
  };
3896
3965
 
3897
- if (this.behavior !== 'drawer' && this.behavior !== 'dialog') {
3966
+ if (this.behavior !== "drawer" && this.behavior !== "dialog") {
3898
3967
  // Add event listeners using the stored references
3899
- document.addEventListener('focusin', this.focusHandler);
3968
+ document.addEventListener("focusin", this.focusHandler);
3900
3969
  }
3901
3970
 
3902
- document.addEventListener('keydown', this.keyDownHandler);
3971
+ document.addEventListener("keydown", this.keyDownHandler);
3903
3972
 
3904
3973
  // send this task to the end of queue to prevent conflicting
3905
3974
  // it conflicts if showBib gets call from a button that's not this.element.trigger
3906
3975
  setTimeout(() => {
3907
- window.addEventListener('click', this.clickHandler);
3976
+ window.addEventListener("click", this.clickHandler);
3908
3977
  }, 0);
3909
3978
  }
3910
3979
 
@@ -3912,34 +3981,38 @@ class AuroFloatingUI {
3912
3981
  // Remove event listeners if they exist
3913
3982
 
3914
3983
  if (this.focusHandler) {
3915
- document.removeEventListener('focusin', this.focusHandler);
3984
+ document.removeEventListener("focusin", this.focusHandler);
3916
3985
  this.focusHandler = null;
3917
3986
  }
3918
3987
 
3919
3988
  if (this.clickHandler) {
3920
- window.removeEventListener('click', this.clickHandler);
3989
+ window.removeEventListener("click", this.clickHandler);
3921
3990
  this.clickHandler = null;
3922
3991
  }
3923
3992
 
3924
3993
  if (this.keyDownHandler) {
3925
- document.removeEventListener('keydown', this.keyDownHandler);
3994
+ document.removeEventListener("keydown", this.keyDownHandler);
3926
3995
  this.keyDownHandler = null;
3927
3996
  }
3928
3997
  }
3929
3998
 
3930
3999
  handleUpdate(changedProperties) {
3931
- if (changedProperties.has('isPopoverVisible')) {
4000
+ if (changedProperties.has("isPopoverVisible")) {
3932
4001
  this.updateState();
3933
4002
  }
3934
4003
  }
3935
4004
 
3936
4005
  updateCurrentExpandedDropdown() {
3937
4006
  // Close any other dropdown that is already open
3938
- const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
3939
- if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
4007
+ const existedVisibleFloatingUI =
4008
+ document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
4009
+ if (
4010
+ existedVisibleFloatingUI &&
4011
+ existedVisibleFloatingUI !== this &&
3940
4012
  existedVisibleFloatingUI.element.isPopoverVisible &&
3941
- document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
3942
- document.expandedAuroFloater.hideBib();
4013
+ existedVisibleFloatingUI.eventPrefix === this.eventPrefix
4014
+ ) {
4015
+ existedVisibleFloatingUI.hideBib();
3943
4016
  }
3944
4017
 
3945
4018
  document.expandedAuroFloater = this;
@@ -3948,7 +4021,7 @@ class AuroFloatingUI {
3948
4021
  showBib() {
3949
4022
  if (!this.element.disabled && !this.showing) {
3950
4023
  this.updateCurrentExpandedDropdown();
3951
- this.element.triggerChevron?.setAttribute('data-expanded', true);
4024
+ this.element.triggerChevron?.setAttribute("data-expanded", true);
3952
4025
 
3953
4026
  // prevent double showing: isPopovervisible gets first and showBib gets called later
3954
4027
  if (!this.showing) {
@@ -3962,9 +4035,13 @@ class AuroFloatingUI {
3962
4035
  }
3963
4036
 
3964
4037
  // Setup auto update to handle resize and scroll
3965
- this.element.cleanup = autoUpdate(this.element.trigger || this.element.parentNode, this.element.bib, () => {
3966
- this.position();
3967
- });
4038
+ this.element.cleanup = autoUpdate(
4039
+ this.element.trigger || this.element.parentNode,
4040
+ this.element.bib,
4041
+ () => {
4042
+ this.position();
4043
+ },
4044
+ );
3968
4045
  }
3969
4046
  }
3970
4047
 
@@ -3975,7 +4052,7 @@ class AuroFloatingUI {
3975
4052
  hideBib(eventType = "unknown") {
3976
4053
  if (!this.element.disabled && !this.element.noToggle) {
3977
4054
  this.lockScroll(false);
3978
- this.element.triggerChevron?.removeAttribute('data-expanded');
4055
+ this.element.triggerChevron?.removeAttribute("data-expanded");
3979
4056
 
3980
4057
  if (this.element.isPopoverVisible) {
3981
4058
  this.element.isPopoverVisible = false;
@@ -3995,13 +4072,16 @@ class AuroFloatingUI {
3995
4072
  * @param {String} eventType - The event type that triggered the toggle action.
3996
4073
  */
3997
4074
  dispatchEventDropdownToggle(eventType) {
3998
- const event = new CustomEvent(this.eventPrefix ? `${this.eventPrefix}-toggled` : 'toggled', {
3999
- detail: {
4000
- expanded: this.showing,
4001
- eventType: eventType || "unknown",
4075
+ const event = new CustomEvent(
4076
+ this.eventPrefix ? `${this.eventPrefix}-toggled` : "toggled",
4077
+ {
4078
+ detail: {
4079
+ expanded: this.showing,
4080
+ eventType: eventType || "unknown",
4081
+ },
4082
+ composed: true,
4002
4083
  },
4003
- composed: true
4004
- });
4084
+ );
4005
4085
 
4006
4086
  this.element.dispatchEvent(event);
4007
4087
  }
@@ -4013,12 +4093,15 @@ class AuroFloatingUI {
4013
4093
  this.showBib();
4014
4094
  }
4015
4095
 
4016
- const event = new CustomEvent(this.eventPrefix ? `${this.eventPrefix}-triggerClick` : "triggerClick", {
4017
- composed: true,
4018
- detail: {
4019
- expanded: this.element.isPopoverVisible
4020
- }
4021
- });
4096
+ const event = new CustomEvent(
4097
+ this.eventPrefix ? `${this.eventPrefix}-triggerClick` : "triggerClick",
4098
+ {
4099
+ composed: true,
4100
+ detail: {
4101
+ expanded: this.element.isPopoverVisible,
4102
+ },
4103
+ },
4104
+ );
4022
4105
 
4023
4106
  this.element.dispatchEvent(event);
4024
4107
  }
@@ -4026,30 +4109,32 @@ class AuroFloatingUI {
4026
4109
  handleEvent(event) {
4027
4110
  if (!this.element.disableEventShow) {
4028
4111
  switch (event.type) {
4029
- case 'keydown':
4112
+ case "keydown": {
4030
4113
  // Support both Enter and Space keys for accessibility
4031
4114
  // Space is included as it's expected behavior for interactive elements
4032
4115
 
4033
4116
  const origin = event.composedPath()[0];
4034
- if (event.key === 'Enter' || event.key === ' ' && (!origin || origin.tagName !== "INPUT")) {
4035
-
4117
+ if (
4118
+ event.key === "Enter" ||
4119
+ (event.key === " " && (!origin || origin.tagName !== "INPUT"))
4120
+ ) {
4036
4121
  event.preventDefault();
4037
4122
  this.handleClick();
4038
4123
  }
4039
4124
  break;
4040
- case 'mouseenter':
4125
+ }
4126
+ case "mouseenter":
4041
4127
  if (this.element.hoverToggle) {
4042
4128
  this.showBib();
4043
4129
  }
4044
4130
  break;
4045
- case 'mouseleave':
4131
+ case "mouseleave":
4046
4132
  if (this.element.hoverToggle) {
4047
4133
  this.hideBib("mouseleave");
4048
4134
  }
4049
4135
  break;
4050
- case 'focus':
4136
+ case "focus":
4051
4137
  if (this.element.focusShow) {
4052
-
4053
4138
  /*
4054
4139
  This needs to better handle clicking that gives focus -
4055
4140
  currently it shows and then immediately hides the bib
@@ -4057,12 +4142,12 @@ class AuroFloatingUI {
4057
4142
  this.showBib();
4058
4143
  }
4059
4144
  break;
4060
- case 'blur':
4145
+ case "blur":
4061
4146
  // send this task 100ms later queue to
4062
4147
  // wait a frame in case focus moves within the floating element/bib
4063
4148
  setTimeout(() => this.handleFocusLoss(), 0);
4064
4149
  break;
4065
- case 'click':
4150
+ case "click":
4066
4151
  if (document.activeElement === document.body) {
4067
4152
  event.currentTarget.focus();
4068
4153
  }
@@ -4081,15 +4166,15 @@ class AuroFloatingUI {
4081
4166
  */
4082
4167
  handleTriggerTabIndex() {
4083
4168
  const focusableElementSelectors = [
4084
- 'a',
4085
- 'button',
4169
+ "a",
4170
+ "button",
4086
4171
  'input:not([type="hidden"])',
4087
- 'select',
4088
- 'textarea',
4172
+ "select",
4173
+ "textarea",
4089
4174
  '[tabindex]:not([tabindex="-1"])',
4090
- 'auro-button',
4091
- 'auro-input',
4092
- 'auro-hyperlink'
4175
+ "auro-button",
4176
+ "auro-input",
4177
+ "auro-hyperlink",
4093
4178
  ];
4094
4179
 
4095
4180
  const triggerNode = this.element.querySelectorAll('[slot="trigger"]')[0];
@@ -4117,10 +4202,10 @@ class AuroFloatingUI {
4117
4202
  * @param {*} eventPrefix
4118
4203
  */
4119
4204
  regenerateBibId() {
4120
- this.id = this.element.getAttribute('id');
4205
+ this.id = this.element.getAttribute("id");
4121
4206
  if (!this.id) {
4122
4207
  this.id = window.crypto.randomUUID();
4123
- this.element.setAttribute('id', this.id);
4208
+ this.element.setAttribute("id", this.id);
4124
4209
  }
4125
4210
 
4126
4211
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
@@ -4141,11 +4226,15 @@ class AuroFloatingUI {
4141
4226
  if (this.element.trigger) {
4142
4227
  this.disconnect();
4143
4228
  }
4144
- this.element.trigger = this.element.triggerElement || this.element.shadowRoot.querySelector('#trigger') || this.element.trigger;
4145
- this.element.bib = this.element.shadowRoot.querySelector('#bib') || this.element.bib;
4146
- this.element.bibSizer = this.element.shadowRoot.querySelector('#bibSizer');
4147
- this.element.triggerChevron = this.element.shadowRoot.querySelector('#showStateIcon');
4148
-
4229
+ this.element.trigger =
4230
+ this.element.triggerElement ||
4231
+ this.element.shadowRoot.querySelector("#trigger") ||
4232
+ this.element.trigger;
4233
+ this.element.bib =
4234
+ this.element.shadowRoot.querySelector("#bib") || this.element.bib;
4235
+ this.element.bibSizer = this.element.shadowRoot.querySelector("#bibSizer");
4236
+ this.element.triggerChevron =
4237
+ this.element.shadowRoot.querySelector("#showStateIcon");
4149
4238
 
4150
4239
  if (this.element.floaterConfig) {
4151
4240
  this.element.hoverToggle = this.element.floaterConfig.hoverToggle;
@@ -4156,12 +4245,12 @@ class AuroFloatingUI {
4156
4245
 
4157
4246
  this.handleEvent = this.handleEvent.bind(this);
4158
4247
  if (this.element.trigger) {
4159
- this.element.trigger.addEventListener('keydown', this.handleEvent);
4160
- this.element.trigger.addEventListener('click', this.handleEvent);
4161
- this.element.trigger.addEventListener('mouseenter', this.handleEvent);
4162
- this.element.trigger.addEventListener('mouseleave', this.handleEvent);
4163
- this.element.trigger.addEventListener('focus', this.handleEvent);
4164
- this.element.trigger.addEventListener('blur', this.handleEvent);
4248
+ this.element.trigger.addEventListener("keydown", this.handleEvent);
4249
+ this.element.trigger.addEventListener("click", this.handleEvent);
4250
+ this.element.trigger.addEventListener("mouseenter", this.handleEvent);
4251
+ this.element.trigger.addEventListener("mouseleave", this.handleEvent);
4252
+ this.element.trigger.addEventListener("focus", this.handleEvent);
4253
+ this.element.trigger.addEventListener("blur", this.handleEvent);
4165
4254
  }
4166
4255
  }
4167
4256
 
@@ -4176,12 +4265,18 @@ class AuroFloatingUI {
4176
4265
 
4177
4266
  // Remove event & keyboard listeners
4178
4267
  if (this.element?.trigger) {
4179
- this.element.trigger.removeEventListener('keydown', this.handleEvent);
4180
- this.element.trigger.removeEventListener('click', this.handleEvent);
4181
- this.element.trigger.removeEventListener('mouseenter', this.handleEvent);
4182
- this.element.trigger.removeEventListener('mouseleave', this.handleEvent);
4183
- this.element.trigger.removeEventListener('focus', this.handleEvent);
4184
- this.element.trigger.removeEventListener('blur', this.handleEvent);
4268
+ this.element.trigger.removeEventListener("keydown", this.handleEvent);
4269
+ this.element.trigger.removeEventListener("click", this.handleEvent);
4270
+ this.element.trigger.removeEventListener(
4271
+ "mouseenter",
4272
+ this.handleEvent,
4273
+ );
4274
+ this.element.trigger.removeEventListener(
4275
+ "mouseleave",
4276
+ this.handleEvent,
4277
+ );
4278
+ this.element.trigger.removeEventListener("focus", this.handleEvent);
4279
+ this.element.trigger.removeEventListener("blur", this.handleEvent);
4185
4280
  }
4186
4281
  }
4187
4282
  }
@@ -4628,7 +4723,7 @@ let p$2 = class p{registerComponent(t,a){customElements.get(t)||customElements.d
4628
4723
 
4629
4724
  var iconVersion$1 = '9.1.2';
4630
4725
 
4631
- var styleCss$2 = css`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host(:not([matchWidth])) .container{min-width:fit-content}:host([isfullscreen]){position:fixed;top:0;left:0}:host([isfullscreen]) .container{width:100dvw;max-width:none;height:100dvh;max-height:none;border-radius:unset;margin-top:0;box-shadow:unset;overscroll-behavior:contain}:host([data-show]){display:flex}:host([common]:not([isfullscreen])) .container,:host([rounded]:not([isfullscreen])) .container{border-radius:var(--ds-border-radius, 0.375rem)}:host([common][isfullscreen]) .container,:host([rounded][isfullscreen]) .container{border-radius:unset;box-shadow:unset}:host(:not([isfullscreen])) .container.shape-box{border-radius:unset}:host(:not([isfullscreen])) .container[class*=shape-pill],:host(:not([isfullscreen])) .container[class*=shape-snowflake]{border-radius:30px}:host(:not([isfullscreen])) .container[class*=shape-rounded]{border-radius:16px}.container{display:inline-block;overflow:auto;box-sizing:border-box;border-radius:var(--ds-border-radius, 0.375rem);margin:var(--ds-size-50, 0.25rem) 0}`;
4726
+ var styleCss$2 = css`:host{position:fixed;z-index:var(--depth-tooltip, 400);display:none;isolation:isolate}:host dialog{width:auto;max-width:none;height:auto;max-height:none;padding:0;border:none;margin:0;outline:none;transform:translateZ(0)}:host dialog::backdrop{background:transparent}:host(:not([isfullscreen])) dialog{position:relative;inset:unset}:host(:not([isfullscreen])) .container.shape-box{border-radius:unset}:host(:not([isfullscreen])) .container[class*=shape-pill],:host(:not([isfullscreen])) .container[class*=shape-snowflake]{border-radius:30px}:host(:not([isfullscreen])) .container[class*=shape-rounded]{border-radius:16px}:host(:not([matchWidth])) .container{min-width:fit-content}:host([isfullscreen]){position:fixed;top:0;left:0}:host([isfullscreen]) .container{width:100dvw;max-width:none;height:100dvh;max-height:none;border-radius:unset;margin-top:0;box-shadow:unset;overscroll-behavior:contain}:host([isfullscreen]) .container::backdrop{background:var(--ds-color-background-primary, #fff)}:host([data-show]){display:flex}:host([common]:not([isfullscreen])) .container,:host([rounded]:not([isfullscreen])) .container{border-radius:var(--ds-border-radius, 0.375rem)}:host([common][isfullscreen]) .container,:host([rounded][isfullscreen]) .container{border-radius:unset;box-shadow:unset}.container{display:inline-block;overflow:auto;box-sizing:border-box;border-radius:var(--ds-border-radius, 0.375rem);margin:var(--ds-size-50, 0.25rem) 0}.util_displayHiddenVisually{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip-path:inset(50%);white-space:nowrap}`;
4632
4727
 
4633
4728
  var colorCss$2 = css`.container{background-color:var(--ds-auro-dropdownbib-container-color);box-shadow:var(--ds-auro-dropdownbib-boxshadow-color);color:var(--ds-auro-dropdownbib-text-color)}`;
4634
4729
 
@@ -4636,6 +4731,8 @@ var tokensCss$1 = css`:host(:not([ondark])),:host(:not([appearance=inverse])){--
4636
4731
 
4637
4732
  // Copyright (c) 2020 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
4638
4733
  // See LICENSE in the project root for license information.
4734
+ /* eslint-disable max-lines */
4735
+ // ---------------------------------------------------------------------
4639
4736
 
4640
4737
 
4641
4738
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
@@ -4730,6 +4827,28 @@ class AuroDropdownBib extends LitElement {
4730
4827
  shape: {
4731
4828
  type: String,
4732
4829
  reflect: true
4830
+ },
4831
+
4832
+ /**
4833
+ * Accessible label for the dialog element, used when displayed as a modal.
4834
+ * Applied via aria-labelledby on a visually hidden element rather than
4835
+ * aria-label because iOS VoiceOver does not reliably read aria-label
4836
+ * on <dialog> elements.
4837
+ * @private
4838
+ */
4839
+ dialogLabel: {
4840
+ type: String
4841
+ },
4842
+
4843
+ /**
4844
+ * Overrides the native role of the dialog element.
4845
+ * For example, set to `"presentation"` on desktop combobox to prevent
4846
+ * VoiceOver from announcing "listbox inside of a dialog".
4847
+ * When `undefined`, the dialog keeps its native role.
4848
+ * @private
4849
+ */
4850
+ dialogRole: {
4851
+ type: String
4733
4852
  }
4734
4853
  };
4735
4854
  }
@@ -4797,7 +4916,10 @@ class AuroDropdownBib extends LitElement {
4797
4916
  firstUpdated(changedProperties) {
4798
4917
  super.firstUpdated(changedProperties);
4799
4918
 
4800
- // Dispatch a custom event when the component is connected
4919
+ const dialog = this.shadowRoot.querySelector('dialog');
4920
+ this._setupCancelHandler(dialog);
4921
+ this._setupKeyboardBridge(dialog);
4922
+
4801
4923
  this.dispatchEvent(new CustomEvent('auro-dropdownbib-connected', {
4802
4924
  bubbles: true,
4803
4925
  composed: true,
@@ -4807,6 +4929,189 @@ class AuroDropdownBib extends LitElement {
4807
4929
  }));
4808
4930
  }
4809
4931
 
4932
+ /**
4933
+ * Forwards the dialog's native `cancel` event (fired on ESC) as
4934
+ * an `auro-bib-cancel` custom event so parent components can close.
4935
+ * @param {HTMLDialogElement} dialog
4936
+ * @private
4937
+ */
4938
+ _setupCancelHandler(dialog) {
4939
+ dialog.addEventListener('cancel', (event) => {
4940
+ event.preventDefault();
4941
+ this.dispatchEvent(new CustomEvent('auro-bib-cancel', {
4942
+ bubbles: true,
4943
+ composed: true
4944
+ }));
4945
+ });
4946
+ }
4947
+
4948
+ /**
4949
+ * showModal() creates a closed focus scope — keyboard events inside
4950
+ * the dialog's shadow DOM do NOT bubble out to the combobox/select
4951
+ * keydown handlers in the parent shadow DOM. This handler bridges
4952
+ * that gap by re-dispatching navigation keys so they cross the
4953
+ * shadow boundary and reach the menu navigation logic in the parent
4954
+ * component.
4955
+ *
4956
+ * The trade-off: intercepting these keys means native keyboard
4957
+ * behaviors that would normally "just work" must be manually
4958
+ * re-implemented here:
4959
+ *
4960
+ * - Enter on buttons: Custom elements (auro-button) don't get the
4961
+ * native Enter→click that <button> provides, so we call .click()
4962
+ * directly when Enter is pressed on a button-like element.
4963
+ *
4964
+ * - Tab: Intercepted and re-dispatched so parent components
4965
+ * (select/combobox) can select the active option and close the
4966
+ * dialog. The <dialog> provides containment and isolation
4967
+ * (inert background, VoiceOver focus trapping, top layer), while
4968
+ * the content inside is a role="listbox" navigated via
4969
+ * aria-activedescendant (options are not focusable). Tab keyboard
4970
+ * behavior follows listbox conventions (select + close) because
4971
+ * the dialog's native Tab trap only cycles between the close
4972
+ * button and browser chrome.
4973
+ *
4974
+ * - Escape: The native <dialog> fires a `cancel` event on ESC
4975
+ * (handled by _setupCancelHandler), so the re-dispatched Escape
4976
+ * is a secondary path for parent components that also listen for
4977
+ * Escape keydown.
4978
+ *
4979
+ * @param {HTMLDialogElement} dialog
4980
+ * @private
4981
+ */
4982
+ _setupKeyboardBridge(dialog) {
4983
+ const navKeys = new Set([
4984
+ 'ArrowUp',
4985
+ 'ArrowDown',
4986
+ 'Enter',
4987
+ 'Escape',
4988
+ 'Tab'
4989
+ ]);
4990
+
4991
+ dialog.addEventListener('keydown', (event) => {
4992
+ if (!navKeys.has(event.key)) {
4993
+ return;
4994
+ }
4995
+
4996
+ // Custom elements (auro-button) don't get the native Enter→click
4997
+ // behavior that <button> has. Find the button in the composed path
4998
+ // and click it directly.
4999
+ if (event.key === 'Enter') {
5000
+ const buttonSelector = 'button, [role="button"], auro-button, [auro-button]';
5001
+ const btn = event.composedPath().find((el) => el.matches && el.matches(buttonSelector));
5002
+ if (btn) {
5003
+ event.preventDefault();
5004
+ event.stopPropagation();
5005
+ btn.click();
5006
+ return;
5007
+ }
5008
+ }
5009
+
5010
+ event.preventDefault();
5011
+ event.stopPropagation();
5012
+ const newEvent = new KeyboardEvent('keydown', {
5013
+ key: event.key,
5014
+ code: event.code,
5015
+ shiftKey: event.shiftKey,
5016
+ altKey: event.altKey,
5017
+ ctrlKey: event.ctrlKey,
5018
+ metaKey: event.metaKey,
5019
+ bubbles: true,
5020
+ composed: true,
5021
+ cancelable: true
5022
+ });
5023
+ this.dispatchEvent(newEvent);
5024
+ });
5025
+ }
5026
+
5027
+ /**
5028
+ * Blocks touch-driven page scroll while a fullscreen modal dialog is open.
5029
+ *
5030
+ * The showModal() function places the dialog in the browser's **top layer**,
5031
+ * which is a separate rendering layer above the normal DOM. On mobile, the
5032
+ * compositor processes visual-viewport panning before top-layer touch
5033
+ * handling. This means the entire viewport — including the top-layer dialog
5034
+ * — can be panned by a touch gesture, causing the page behind the dialog to
5035
+ * scroll into view. To prevent this, we add a touchmove listener that cancels
5036
+ * the event if the touch started outside the dialog or any scrollable child within it.
5037
+ *
5038
+ * @private
5039
+ */
5040
+ _lockTouchScroll() {
5041
+ const dialog = this.shadowRoot.querySelector('dialog');
5042
+
5043
+ this._touchMoveHandler = (event) => {
5044
+ // Walk the composed path (which crosses shadow DOM boundaries) to
5045
+ // check whether the touch started inside a scrollable element that
5046
+ // lives within the dialog. If so, allow the scroll.
5047
+ for (const el of event.composedPath()) {
5048
+ if (el === dialog) {
5049
+ // Reached the dialog boundary without finding a scrollable child.
5050
+ break;
5051
+ }
5052
+ if (el instanceof HTMLElement && el.scrollHeight > el.clientHeight) {
5053
+ const { overflowY } = getComputedStyle(el);
5054
+ if (overflowY === 'auto' || overflowY === 'scroll') {
5055
+ return;
5056
+ }
5057
+ }
5058
+ }
5059
+
5060
+ event.preventDefault();
5061
+ };
5062
+
5063
+ document.addEventListener('touchmove', this._touchMoveHandler, { passive: false });
5064
+ }
5065
+
5066
+ /**
5067
+ * Removes the touchmove listener added by _lockTouchScroll().
5068
+ * @private
5069
+ */
5070
+ _unlockTouchScroll() {
5071
+ if (this._touchMoveHandler) {
5072
+ document.removeEventListener('touchmove', this._touchMoveHandler);
5073
+ this._touchMoveHandler = undefined;
5074
+ }
5075
+ }
5076
+
5077
+ open(modal = true) {
5078
+ const dialog = this.shadowRoot.querySelector('dialog');
5079
+ if (dialog && !dialog.open) {
5080
+ if (modal) {
5081
+ // Prevent showModal() from scrolling the page to bring the dialog
5082
+ // into view. Locking overflow on <html> blocks the viewport scroll
5083
+ // that browsers perform natively; we release it immediately after
5084
+ // so it doesn't interfere with the modal's focus management.
5085
+ const { documentElement } = document;
5086
+ const prevOverflow = documentElement.style.overflow;
5087
+ documentElement.style.overflow = 'hidden';
5088
+
5089
+ dialog.showModal();
5090
+
5091
+ documentElement.style.overflow = prevOverflow;
5092
+
5093
+ this._lockTouchScroll();
5094
+
5095
+ } else {
5096
+ // Use setAttribute instead of dialog.show() to avoid the dialog
5097
+ // focusing steps which steal focus from the trigger and cause
5098
+ // the floater's handleFocusLoss() to immediately hide the bib.
5099
+ dialog.setAttribute('open', '');
5100
+ }
5101
+ }
5102
+ }
5103
+
5104
+ /**
5105
+ * Closes the dialog.
5106
+ */
5107
+ close() {
5108
+ const dialog = this.shadowRoot.querySelector('dialog');
5109
+ if (dialog && dialog.open) {
5110
+ this._unlockTouchScroll();
5111
+ dialog.close();
5112
+ }
5113
+ }
5114
+
4810
5115
  // function that renders the HTML and CSS into the scope of the component
4811
5116
  render() {
4812
5117
  const classes = {
@@ -4818,9 +5123,10 @@ class AuroDropdownBib extends LitElement {
4818
5123
  classes[`shape-${this.shape}`] = true;
4819
5124
 
4820
5125
  return html`
4821
- <div class="${classMap(classes)}" part="bibContainer">
5126
+ <dialog class="${classMap(classes)}" part="bibContainer" role="${ifDefined(this.dialogRole)}" aria-labelledby="${ifDefined(this.dialogLabel ? 'dialogLabel' : undefined)}">
5127
+ ${this.dialogLabel ? html`<span id="dialogLabel" class="util_displayHiddenVisually" aria-hidden="true">${this.dialogLabel}</span>` : ''}
4822
5128
  <slot></slot>
4823
- </div>
5129
+ </dialog>
4824
5130
  `;
4825
5131
  }
4826
5132
  }
@@ -4829,7 +5135,7 @@ var shapeSizeCss = css`.shape-classic-xl,.shape-classic-lg,.shape-classic-md,.sh
4829
5135
 
4830
5136
  var colorCss$1$1 = css`:host(:not([layout*=classic])){--ds-auro-dropdown-trigger-border-color: transparent}:host(:not([disabled],[onDark])) .wrapper:focus-within,:host(:not([disabled],[onDark])) .wrapper:active,:host(:not([disabled],[appearance=inverse])) .wrapper:focus-within,:host(:not([disabled],[appearance=inverse])) .wrapper:active{--ds-auro-dropdown-trigger-border-color: var(--ds-advanced-color-state-focused, #01426a);--ds-auro-dropdown-trigger-outline-color: var(--ds-advanced-color-state-focused, #01426a)}:host(:not([disabled],[onDark])) .wrapper:hover,:host(:not([disabled],[appearance=inverse])) .wrapper:hover{--ds-auro-dropdown-trigger-background-color: var(--ds-auro-dropdown-trigger-hover-background-color)}:host(:not([ondark])) .wrapper,:host(:not([appearance=inverse])) .wrapper{border-color:var(--ds-auro-dropdown-trigger-border-color);background-color:var(--ds-auro-dropdown-trigger-background-color);color:var(--ds-auro-dropdown-trigger-text-color)}:host(:not([onDark])[disabled]),:host(:not([appearance=inverse])[disabled]){--ds-auro-dropdown-trigger-text-color: var(--ds-basic-color-texticon-disabled, #d0d0d0);--ds-auro-dropdown-label-text-color: var(--ds-basic-color-texticon-disabled, #d0d0d0);--ds-auro-dropdown-trigger-border-color: var(--ds-basic-color-border-subtle, #dddddd)}:host(:not([onDark])[disabled]) #triggerLabel,:host(:not([appearance=inverse])[disabled]) #triggerLabel{cursor:default}:host(:not([ondark])[error]),:host(:not([appearance=inverse])[error]){--ds-auro-dropdown-trigger-border-color: var(--ds-basic-color-status-error, #e31f26)}:host(:not([disabled])[onDark]) .wrapper:focus-within,:host(:not([disabled])[onDark]) .wrapper:active,:host(:not([disabled])[appearance=inverse]) .wrapper:focus-within,:host(:not([disabled])[appearance=inverse]) .wrapper:active{--ds-auro-dropdown-trigger-border-color: var(--ds-advanced-color-state-focused-inverse, #ffffff);--ds-auro-dropdown-trigger-outline-color: var(--ds-advanced-color-state-focused-inverse, #ffffff)}:host(:not([disabled])[onDark]) .wrapper:hover,:host(:not([disabled])[appearance=inverse]) .wrapper:hover{--ds-auro-dropdown-trigger-background-color: var(--ds-auro-dropdown-trigger-hover-background-color)}:host([onDark]) .label,:host([onDark]) .helpText,:host([appearance=inverse]) .label,:host([appearance=inverse]) .helpText{color:var(--ds-auro-dropdown-label-text-color)}:host([onDark]) .wrapper,:host([appearance=inverse]) .wrapper{border-color:var(--ds-auro-dropdown-trigger-border-color);background-color:var(--ds-auro-dropdown-trigger-background-color);color:var(--ds-auro-dropdown-trigger-text-color)}:host([onDark][disabled]),:host([appearance=inverse][disabled]){--ds-auro-dropdown-trigger-text-color: var(--ds-basic-color-texticon-inverse-disabled, #7e8894);--ds-auro-dropdown-label-text-color: var(--ds-basic-color-texticon-inverse-disabled, #7e8894);--ds-auro-dropdown-trigger-container-color: var(--ds-advanced-color-shared-background-inverse-disabled, rgba(255, 255, 255, 0.1))}:host([onDark][disabled]) #triggerLabel,:host([appearance=inverse][disabled]) #triggerLabel{cursor:default}:host([ondark][error]),:host([appearance=inverse][error]){--ds-auro-dropdown-trigger-border-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8)}`;
4831
5137
 
4832
- var styleCss$1$1 = css`:host{position:relative;display:block;text-align:left}[popover=manual]{overflow:visible;padding:0;border:inherit;margin:0;background:inherit;outline:inherit}:host([open]){z-index:var(--depth-tooltip, 400)}.wrapper{display:flex;flex:1;flex-direction:row;align-items:center;justify-content:center;outline:none}.triggerContentWrapper{display:flex;overflow:hidden;width:100%;flex:1;align-items:center;justify-content:center;text-overflow:ellipsis;white-space:nowrap}:host([matchwidth]) #bibSizer{width:100%}`;
5138
+ var styleCss$1$1 = css`:host{position:relative;display:block;text-align:left}:host([open]){z-index:var(--depth-tooltip, 400)}.wrapper{display:flex;flex:1;flex-direction:row;align-items:center;justify-content:center;outline:none}.triggerContentWrapper{display:flex;overflow:hidden;width:100%;flex:1;align-items:center;justify-content:center;text-overflow:ellipsis;white-space:nowrap}:host([matchwidth]) #bibSizer{width:100%}`;
4833
5139
 
4834
5140
  var classicColorCss = css``;
4835
5141
 
@@ -5067,7 +5373,7 @@ class AuroHelpText extends LitElement {
5067
5373
  }
5068
5374
  }
5069
5375
 
5070
- var formkitVersion = '202602140013';
5376
+ var formkitVersion = '202603102257';
5071
5377
 
5072
5378
  let AuroElement$1 = class AuroElement extends LitElement {
5073
5379
  static get properties() {
@@ -5181,7 +5487,7 @@ let AuroElement$1 = class AuroElement extends LitElement {
5181
5487
  * The `auro-dropdown` element provides a way to place content in a bib that can be toggled.
5182
5488
  * @customElement auro-dropdown
5183
5489
  *
5184
- * @slot - Default slot for the popover content.
5490
+ * @slot - Default slot for the dropdown bib content.
5185
5491
  * @slot helpText - Defines the content of the helpText.
5186
5492
  * @slot trigger - Defines the content of the trigger.
5187
5493
  * @csspart trigger - The trigger content container.
@@ -5193,6 +5499,13 @@ let AuroElement$1 = class AuroElement extends LitElement {
5193
5499
  * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
5194
5500
  */
5195
5501
  class AuroDropdown extends AuroElement$1 {
5502
+ static get shadowRootOptions() {
5503
+ return {
5504
+ ...AuroElement$1.shadowRootOptions,
5505
+ delegatesFocus: true,
5506
+ };
5507
+ }
5508
+
5196
5509
  constructor() {
5197
5510
  super();
5198
5511
 
@@ -5258,15 +5571,6 @@ class AuroDropdown extends AuroElement$1 {
5258
5571
  this.shift = false;
5259
5572
  this.autoPlacement = false;
5260
5573
 
5261
- /**
5262
- * @private
5263
- * @property {boolean} delegatesFocus - Whether the shadow root delegates focus.
5264
- */
5265
- this.constructor.shadowRootOptions = {
5266
- ...LitElement.shadowRootOptions,
5267
- delegatesFocus: true,
5268
- };
5269
-
5270
5574
  /**
5271
5575
  * @private
5272
5576
  */
@@ -5340,6 +5644,18 @@ class AuroDropdown extends AuroElement$1 {
5340
5644
  */
5341
5645
  show() {
5342
5646
  this.floater.showBib();
5647
+
5648
+ // Open dialog synchronously so callers remain in the user gesture
5649
+ // chain. This is critical for mobile browsers (iOS Safari) to keep
5650
+ // the virtual keyboard open when transitioning from the trigger
5651
+ // input to an input inside the fullscreen dialog. Without this,
5652
+ // showModal() fires asynchronously via Lit's update cycle, which
5653
+ // falls outside the user activation window and causes iOS to
5654
+ // dismiss the keyboard.
5655
+ if (this.isBibFullscreen && this.bibElement && this.bibElement.value) {
5656
+ const useModal = !this.disableFocusTrap;
5657
+ this.bibElement.value.open(useModal);
5658
+ }
5343
5659
  }
5344
5660
 
5345
5661
  /**
@@ -5347,13 +5663,37 @@ class AuroDropdown extends AuroElement$1 {
5347
5663
  * If not, trigger element will get focus.
5348
5664
  */
5349
5665
  focus() {
5350
- if (this.isPopoverVisible && this.focusTrap) {
5351
- this.focusTrap.focusFirstElement();
5666
+ if (this.isPopoverVisible && this.bibContent) {
5667
+ const focusables = getFocusableElements(this.bibContent);
5668
+ if (focusables.length > 0) {
5669
+ focusables[0].focus();
5670
+ }
5352
5671
  } else {
5353
5672
  this.trigger.focus();
5354
5673
  }
5355
5674
  }
5356
5675
 
5676
+ /**
5677
+ * Sets the active descendant element for accessibility.
5678
+ * Uses ariaActiveDescendantElement to cross shadow DOM boundaries.
5679
+ * This function is used in components that contain `auro-dropdown` to set the active descendant.
5680
+ * @private
5681
+ * @param {HTMLElement|null} element - The element to set as the active descendant, or null to clear.
5682
+ * @returns {void}
5683
+ */
5684
+ setActiveDescendant(element) {
5685
+ if (!this.trigger) {
5686
+ return;
5687
+ }
5688
+
5689
+ if (element) {
5690
+ this.trigger.ariaActiveDescendantElement = element;
5691
+ } else {
5692
+ this.trigger.ariaActiveDescendantElement = null;
5693
+ this.trigger.removeAttribute('aria-activedescendant');
5694
+ }
5695
+ }
5696
+
5357
5697
  // function to define props used within the scope of this component
5358
5698
  static get properties() {
5359
5699
  return {
@@ -5611,6 +5951,16 @@ class AuroDropdown extends AuroElement$1 {
5611
5951
  */
5612
5952
  tabIndex: {
5613
5953
  type: Number
5954
+ },
5955
+
5956
+ /**
5957
+ * Accessible label for the dropdown bib dialog element.
5958
+ * @private
5959
+ */
5960
+ bibDialogLabel: {
5961
+ type: String,
5962
+ attribute: false,
5963
+ reflect: false
5614
5964
  }
5615
5965
  };
5616
5966
  }
@@ -5662,7 +6012,10 @@ class AuroDropdown extends AuroElement$1 {
5662
6012
 
5663
6013
  disconnectedCallback() {
5664
6014
  super.disconnectedCallback();
5665
- this.floater.disconnect();
6015
+ if (this.floater) {
6016
+ this.floater.hideBib('disconnect');
6017
+ this.floater.disconnect();
6018
+ }
5666
6019
  this.clearTriggerFocusEventBinding();
5667
6020
  }
5668
6021
 
@@ -5684,11 +6037,22 @@ class AuroDropdown extends AuroElement$1 {
5684
6037
 
5685
6038
  if (changedProperties.has('isPopoverVisible') && this.bibElement.value) {
5686
6039
  if (this.isPopoverVisible) {
5687
- this.bibElement.value.showPopover();
6040
+ // Fullscreen: use showModal() for native accessibility (inert outside, focus trap)
6041
+ // Desktop: use show() for Floating UI positioning + FocusTrap for focus management
6042
+ const useModal = this.isBibFullscreen && !this.disableFocusTrap;
6043
+ this.bibElement.value.open(useModal);
5688
6044
  } else {
5689
- this.bibElement.value.hidePopover();
6045
+ this.bibElement.value.close();
5690
6046
  }
5691
6047
  }
6048
+
6049
+ // When fullscreen strategy changes while open, re-open dialog with correct mode
6050
+ // (e.g. resizing from desktop → mobile while dropdown is open)
6051
+ if (changedProperties.has('isBibFullscreen') && this.isPopoverVisible && this.bibElement.value) {
6052
+ const useModal = this.isBibFullscreen && !this.disableFocusTrap;
6053
+ this.bibElement.value.close();
6054
+ this.bibElement.value.open(useModal);
6055
+ }
5692
6056
  }
5693
6057
 
5694
6058
  /**
@@ -5706,11 +6070,28 @@ class AuroDropdown extends AuroElement$1 {
5706
6070
  }
5707
6071
 
5708
6072
  firstUpdated() {
5709
-
5710
6073
  // Configure the floater to, this will generate the ID for the bib
5711
6074
  this.floater.configure(this, 'auroDropdown');
6075
+
6076
+ // Prevent `contain: layout` on the dropdown host. Layout containment
6077
+ // creates a containing block for position:fixed descendants (the bib),
6078
+ // which clips the bib inside ancestor overflow contexts such as a
6079
+ // <dialog> element. Without it, the bib's position:fixed is relative
6080
+ // to the viewport, letting Floating UI's flip middleware detect
6081
+ // viewport boundaries and the bib escape overflow clipping.
6082
+ const origConfigureBibStrategy = this.floater.configureBibStrategy.bind(this.floater);
6083
+ this.floater.configureBibStrategy = (value) => {
6084
+ origConfigureBibStrategy(value);
6085
+ this.style.contain = '';
6086
+ };
6087
+
5712
6088
  this.addEventListener('auroDropdown-toggled', this.handleDropdownToggle);
5713
6089
 
6090
+ // Handle ESC key from dialog's cancel event
6091
+ this.addEventListener('auro-bib-cancel', () => {
6092
+ this.floater.hideBib('keydown');
6093
+ });
6094
+
5714
6095
  /**
5715
6096
  * @description Let subscribers know that the dropdown ID ha been generated and added.
5716
6097
  * @event auroDropdown-idAdded
@@ -5718,9 +6099,9 @@ class AuroDropdown extends AuroElement$1 {
5718
6099
  */
5719
6100
  this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
5720
6101
 
5721
- // Set the bib ID locally if the user hasn't provided a focusable trigger
6102
+ // Set the bib ID locally for aria-controls (must be in the same shadow root as the trigger)
5722
6103
  if (!this.triggerContentFocusable) {
5723
- this.dropdownId = this.floater.element.id;
6104
+ this.dropdownId = this.floater.element.bib.id;
5724
6105
  }
5725
6106
 
5726
6107
  this.bibContent = this.floater.element.bib;
@@ -5780,21 +6161,20 @@ class AuroDropdown extends AuroElement$1 {
5780
6161
  * @private
5781
6162
  */
5782
6163
  updateFocusTrap() {
5783
- // If the dropdown is open, create a focus trap and focus the first element
5784
6164
  if (this.isPopoverVisible && !this.disableFocusTrap) {
5785
- this.focusTrap = new FocusTrap(this.bibContent);
5786
- this.focusTrap.focusFirstElement();
6165
+ if (!this.isBibFullscreen) {
6166
+ // Desktop: show() doesn't trap focus, so use FocusTrap
6167
+ this.focusTrap = new FocusTrap(this.bibContent);
6168
+ this.focusTrap.focusFirstElement();
6169
+ }
6170
+ // Fullscreen: showModal() provides native focus trapping
5787
6171
  return;
5788
6172
  }
5789
6173
 
5790
- // Guard Clause: Ensure there is a focus trap currently active before continuing
5791
- if (!this.focusTrap) {
5792
- return;
6174
+ if (this.focusTrap) {
6175
+ this.focusTrap.disconnect();
6176
+ this.focusTrap = undefined;
5793
6177
  }
5794
-
5795
- // If the dropdown is not open, disconnect the focus trap if it exists
5796
- this.focusTrap.disconnect();
5797
- this.focusTrap = undefined;
5798
6178
  }
5799
6179
 
5800
6180
  /**
@@ -6010,13 +6390,14 @@ class AuroDropdown extends AuroElement$1 {
6010
6390
  <div
6011
6391
  id="showStateIcon"
6012
6392
  class="chevron"
6013
- part="chevron">
6393
+ part="chevron"
6394
+ aria-hidden="true">
6014
6395
  <${this.iconTag}
6015
6396
  category="interface"
6016
6397
  name="${this.isPopoverVisible ? 'chevron-up' : `chevron-down`}"
6017
6398
  appearance="${this.onDark ? 'inverse' : this.appearance}"
6018
- variant="${this.disabled ? 'disabled' : 'muted'}">
6019
- >
6399
+ variant="${this.disabled ? 'disabled' : 'muted'}"
6400
+ ariaHidden="true">
6020
6401
  </${this.iconTag}>
6021
6402
  </div>
6022
6403
  ` : undefined }
@@ -6030,8 +6411,8 @@ class AuroDropdown extends AuroElement$1 {
6030
6411
  shape="${this.shape}"
6031
6412
  ?data-show="${this.isPopoverVisible}"
6032
6413
  ?isfullscreen="${this.isBibFullscreen}"
6414
+ .dialogLabel="${this.bibDialogLabel}"
6033
6415
  ${ref(this.bibElement)}
6034
- popover="manual"
6035
6416
  >
6036
6417
  <div class="slotContent">
6037
6418
  <slot @slotchange="${this.handleDefaultSlot}"></slot>
@@ -6438,6 +6819,18 @@ class AuroBibtemplate extends LitElement {
6438
6819
  this.removeEventListener('touchmove', this.preventBodyScroll, { passive: false });
6439
6820
  }
6440
6821
 
6822
+ /**
6823
+ * Focuses the close button inside the bibtemplate's shadow DOM.
6824
+ * Used by parent components to set initial focus when the fullscreen dialog opens.
6825
+ * @returns {void}
6826
+ */
6827
+ focusCloseButton() {
6828
+ const closeBtn = this.shadowRoot.querySelector('#closeButton');
6829
+ if (closeBtn) {
6830
+ closeBtn.focus();
6831
+ }
6832
+ }
6833
+
6441
6834
  onCloseButtonClick() {
6442
6835
  this.dispatchEvent(new Event("close-click", { bubbles: true,
6443
6836
  composed: true }));
@@ -7114,6 +7507,24 @@ class AuroCounterGroup extends AuroElement {
7114
7507
  this.dropdown.hide();
7115
7508
  }
7116
7509
  });
7510
+
7511
+ // Focus close button when fullscreen dialog opens
7512
+ this.dropdown.addEventListener('auroDropdown-toggled', () => {
7513
+ if (this.dropdown.isPopoverVisible && this.dropdown.isBibFullscreen) {
7514
+ doubleRaf(() => {
7515
+ this.bibtemplate.focusCloseButton();
7516
+ });
7517
+ }
7518
+ });
7519
+
7520
+ // Tab closes the fullscreen dialog
7521
+ // The dialog event bridge intercepts Tab and re-dispatches it as a
7522
+ // composed keydown; this listener catches the re-dispatched event.
7523
+ this.addEventListener('keydown', (evt) => {
7524
+ if (evt.key === 'Tab' && this.dropdown.isPopoverVisible && this.dropdown.isBibFullscreen) {
7525
+ this.dropdown.hide();
7526
+ }
7527
+ });
7117
7528
  }
7118
7529
 
7119
7530
  /**