@aurodesignsystem-dev/auro-formkit 0.0.0-pr1551.0 → 0.0.0-pr1552.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 (43) 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 +207 -46
  7. package/components/combobox/demo/getting-started.min.js +207 -46
  8. package/components/combobox/demo/index.min.js +207 -46
  9. package/components/combobox/dist/index.js +207 -46
  10. package/components/combobox/dist/registered.js +207 -46
  11. package/components/counter/demo/customize.min.js +206 -45
  12. package/components/counter/demo/index.min.js +206 -45
  13. package/components/counter/dist/index.js +206 -45
  14. package/components/counter/dist/registered.js +206 -45
  15. package/components/datepicker/demo/customize.min.js +207 -46
  16. package/components/datepicker/demo/index.min.js +207 -46
  17. package/components/datepicker/dist/index.js +207 -46
  18. package/components/datepicker/dist/registered.js +207 -46
  19. package/components/dropdown/demo/customize.min.js +205 -44
  20. package/components/dropdown/demo/getting-started.min.js +205 -44
  21. package/components/dropdown/demo/index.min.js +205 -44
  22. package/components/dropdown/dist/index.js +205 -44
  23. package/components/dropdown/dist/registered.js +205 -44
  24. package/components/form/demo/customize.min.js +829 -185
  25. package/components/form/demo/getting-started.min.js +829 -185
  26. package/components/form/demo/index.min.js +829 -185
  27. package/components/form/demo/registerDemoDeps.min.js +829 -185
  28. package/components/input/demo/customize.min.js +1 -1
  29. package/components/input/demo/getting-started.min.js +1 -1
  30. package/components/input/demo/index.min.js +1 -1
  31. package/components/input/dist/index.js +1 -1
  32. package/components/input/dist/registered.js +1 -1
  33. package/components/radio/demo/customize.min.js +1 -1
  34. package/components/radio/demo/getting-started.min.js +1 -1
  35. package/components/radio/demo/index.min.js +1 -1
  36. package/components/radio/dist/index.js +1 -1
  37. package/components/radio/dist/registered.js +1 -1
  38. package/components/select/demo/customize.min.js +206 -45
  39. package/components/select/demo/getting-started.min.js +206 -45
  40. package/components/select/demo/index.min.js +206 -45
  41. package/components/select/dist/index.js +206 -45
  42. package/components/select/dist/registered.js +206 -45
  43. package/package.json +2 -2
@@ -1275,7 +1275,7 @@ class AuroHelpText extends i$2 {
1275
1275
  }
1276
1276
  }
1277
1277
 
1278
- var formkitVersion = '202607100029';
1278
+ var formkitVersion = '202607092329';
1279
1279
 
1280
1280
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1281
1281
  // See LICENSE in the project root for license information.
@@ -1275,7 +1275,7 @@ class AuroHelpText extends i$2 {
1275
1275
  }
1276
1276
  }
1277
1277
 
1278
- var formkitVersion = '202607100029';
1278
+ var formkitVersion = '202607092329';
1279
1279
 
1280
1280
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1281
1281
  // See LICENSE in the project root for license information.
@@ -1275,7 +1275,7 @@ class AuroHelpText extends i$2 {
1275
1275
  }
1276
1276
  }
1277
1277
 
1278
- var formkitVersion = '202607100029';
1278
+ var formkitVersion = '202607092329';
1279
1279
 
1280
1280
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1281
1281
  // See LICENSE in the project root for license information.
@@ -1228,7 +1228,7 @@ class AuroHelpText extends LitElement {
1228
1228
  }
1229
1229
  }
1230
1230
 
1231
- var formkitVersion = '202607100029';
1231
+ var formkitVersion = '202607092329';
1232
1232
 
1233
1233
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1234
1234
  // See LICENSE in the project root for license information.
@@ -1228,7 +1228,7 @@ class AuroHelpText extends LitElement {
1228
1228
  }
1229
1229
  }
1230
1230
 
1231
- var formkitVersion = '202607100029';
1231
+ var formkitVersion = '202607092329';
1232
1232
 
1233
1233
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
1234
1234
  // See LICENSE in the project root for license information.
@@ -3520,40 +3520,12 @@ class AuroFloatingUI {
3520
3520
  return;
3521
3521
  }
3522
3522
 
3523
- // Chrome-specific: during popover top-layer promotion after a click on a
3524
- // slotted focusable, :focus-within can briefly evaluate false while the
3525
- // active element is still structurally inside the trigger/bib. Fall back
3526
- // to a shadow-piercing ancestry walk from the deep active element before
3527
- // treating this as a real focus loss.
3528
- try {
3529
- let active = document.activeElement;
3530
- while (active && active.shadowRoot && active.shadowRoot.activeElement) {
3531
- active = active.shadowRoot.activeElement;
3532
- }
3533
- const targets = [element, element.trigger, element.bib].filter(Boolean);
3534
- let node = active;
3535
- while (node) {
3536
- if (targets.includes(node)) {
3537
- return;
3538
- }
3539
- node =
3540
- node.parentElement ||
3541
- (node.getRootNode && node.getRootNode().host) ||
3542
- null;
3543
- }
3544
- } catch (e) {
3545
- // Defensive: fall through to the existing close path if traversal fails.
3546
- }
3547
-
3548
3523
  // if fullscreen bib is in fullscreen mode, do not close
3549
3524
  if (element.bib.hasAttribute("isfullscreen")) {
3550
3525
  return;
3551
3526
  }
3552
3527
 
3553
- // eventType "focusloss" distinguishes a Tab/click-driven close from an
3554
- // Escape keydown close. Consumers use this to decide whether to restore
3555
- // focus to the trigger (Escape) or let it advance naturally (Tab).
3556
- this.hideBib("focusloss");
3528
+ this.hideBib("keydown");
3557
3529
  }
3558
3530
 
3559
3531
  setupHideHandlers() {
@@ -4223,6 +4195,199 @@ function getFocusableElements(container) {
4223
4195
  return tabIndexedUniqueElements;
4224
4196
  }
4225
4197
 
4198
+ /**
4199
+ * FocusTrap manages keyboard focus within a specified container element, ensuring that focus does not leave the container when tabbing.
4200
+ * It is commonly used for modal dialogs or overlays to improve accessibility by trapping focus within interactive UI components.
4201
+ */
4202
+ class FocusTrap {
4203
+ /**
4204
+ * Creates a new FocusTrap instance for the given container element.
4205
+ * Initializes event listeners and prepares the container for focus management.
4206
+ *
4207
+ * @param {HTMLElement} container The DOM element to trap focus within.
4208
+ * @param {boolean} [controlTabOrder=false] If true enables manual control of the tab order by the FocusTrap.
4209
+ * @throws {Error} If the provided container is not a valid HTMLElement.
4210
+ */
4211
+ constructor(container, controlTabOrder = false) {
4212
+ if (!container || !(container instanceof HTMLElement)) {
4213
+ throw new Error("FocusTrap requires a valid HTMLElement.");
4214
+ }
4215
+
4216
+ this.container = container;
4217
+ this.tabDirection = "forward"; // or 'backward';
4218
+ this.controlTabOrder = controlTabOrder;
4219
+
4220
+ this._init();
4221
+ }
4222
+
4223
+ /**
4224
+ * Initializes the focus trap by setting up event listeners and attributes on the container.
4225
+ * Prepares the container for focus management, including support for shadow DOM and inert attributes.
4226
+ *
4227
+ * @private
4228
+ */
4229
+ _init() {
4230
+ // Add inert attribute to prevent focusing programmatically as well (if supported)
4231
+ if ("inert" in HTMLElement.prototype) {
4232
+ this.container.inert = false; // Ensure the container isn't inert
4233
+ this.container.setAttribute("data-focus-trap-container", true); // Mark for identification
4234
+ }
4235
+
4236
+ // Track tab direction
4237
+ this.container.addEventListener("keydown", this._onKeydown);
4238
+ }
4239
+
4240
+ /**
4241
+ * Gets an array of currently active (focused) elements in the document and shadow DOM.
4242
+ * @returns {Array<HTMLElement>} An array of focusable elements within the container.
4243
+ * @private
4244
+ */
4245
+ _getActiveElements() {
4246
+ // Get the active element(s) in the document and shadow root
4247
+ // This will include the active element in the shadow DOM if it exists
4248
+ // Active element may be inside the shadow DOM depending on delegatesFocus, so we need to check both
4249
+ let { activeElement } = document;
4250
+ const actives = [activeElement];
4251
+ while (activeElement?.shadowRoot?.activeElement) {
4252
+ actives.push(activeElement.shadowRoot.activeElement);
4253
+ activeElement = activeElement.shadowRoot.activeElement;
4254
+ }
4255
+ return actives;
4256
+ }
4257
+
4258
+ /**
4259
+ * Gets the next focus index based on the current index and focusable elements.
4260
+ * @param {number} currentIndex The current index of the focused element.
4261
+ * @param {Array<HTMLElement>} focusables The array of focusable elements.
4262
+ * @returns {number|null} The next focus index or null if not determined.
4263
+ */
4264
+ _getNextFocusIndex(currentIndex, focusables, actives) {
4265
+ if (this.controlTabOrder) {
4266
+ // Calculate the new index based on the current index and tab direction
4267
+ let newFocusIndex =
4268
+ currentIndex + (this.tabDirection === "forward" ? 1 : -1);
4269
+
4270
+ // Wrap-around logic
4271
+ if (newFocusIndex < 0) newFocusIndex = focusables.length - 1;
4272
+ if (newFocusIndex >= focusables.length) newFocusIndex = 0;
4273
+
4274
+ // Early return with the new index
4275
+ return newFocusIndex;
4276
+ }
4277
+
4278
+ // Determine if we need to wrap
4279
+ const atFirst =
4280
+ actives.includes(focusables[0]) || actives.includes(this.container);
4281
+ const atLast = actives.includes(focusables[focusables.length - 1]);
4282
+
4283
+ // Only wrap if at the ends
4284
+ if (this.tabDirection === "backward" && atFirst) {
4285
+ return focusables.length - 1;
4286
+ }
4287
+
4288
+ if (this.tabDirection === "forward" && atLast) {
4289
+ return 0;
4290
+ }
4291
+
4292
+ // No wrap, so don't change focus, return early
4293
+ return null;
4294
+ }
4295
+
4296
+ /**
4297
+ * Handles the Tab key press event to manage focus within the container.
4298
+ * @param {KeyboardEvent} e The keyboard event triggered by the user.
4299
+ * @returns {void}
4300
+ */
4301
+ _handleTabKey(e) {
4302
+ // Update the focusable elements
4303
+ const focusables = this._getFocusableElements();
4304
+
4305
+ // If there are no focusable elements, exit
4306
+ if (!focusables.length) return;
4307
+
4308
+ // Set the tab direction based on the key pressed
4309
+ this.tabDirection = e.shiftKey ? "backward" : "forward";
4310
+
4311
+ // Get the active elements that are currently focused
4312
+ const actives = this._getActiveElements();
4313
+
4314
+ // If we're at either end of the focusable elements, wrap around to the other end
4315
+ let focusIndex = focusables.findIndex((el) => actives.includes(el));
4316
+
4317
+ // Fallback if we have no focused element
4318
+ if (focusIndex === -1) focusIndex = 0;
4319
+
4320
+ // Get the next focus index based on the current focus index, tab direction, and controlTabOrder setting
4321
+ // Is null if no new focus index is determined
4322
+ const newFocusIndex = this._getNextFocusIndex(
4323
+ focusIndex,
4324
+ focusables,
4325
+ actives,
4326
+ );
4327
+
4328
+ // If we have a new focus index, set focus to that element
4329
+ if (newFocusIndex !== null) {
4330
+ e.preventDefault();
4331
+ focusables[newFocusIndex].focus();
4332
+ }
4333
+ }
4334
+
4335
+ /**
4336
+ * Catches the keydown event
4337
+ * @param {KeyboardEvent} e The keyboard event triggered by user interaction.
4338
+ * @private
4339
+ */
4340
+ _onKeydown = (e) => {
4341
+ // Handle tab
4342
+ if (e.key === "Tab") this._handleTabKey(e);
4343
+ };
4344
+
4345
+ /**
4346
+ * Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
4347
+ * Returns a unique, ordered array of elements that can receive focus.
4348
+ *
4349
+ * @returns {Array<HTMLElement>} An array of focusable elements within the container.
4350
+ * @private
4351
+ */
4352
+ _getFocusableElements() {
4353
+ // Use the imported utility function to get focusable elements
4354
+ const elements = getFocusableElements(this.container);
4355
+
4356
+ // Return the elements found
4357
+ return elements;
4358
+ }
4359
+
4360
+ /**
4361
+ * Moves focus to the first focusable element within the container.
4362
+ * Useful for setting initial focus when activating the focus trap.
4363
+ */
4364
+ focusFirstElement() {
4365
+ const focusables = this._getFocusableElements();
4366
+ if (focusables.length) focusables[0].focus();
4367
+ }
4368
+
4369
+ /**
4370
+ * Moves focus to the last focusable element within the container.
4371
+ * Useful for setting focus when deactivating or cycling focus in reverse.
4372
+ */
4373
+ focusLastElement() {
4374
+ const focusables = this._getFocusableElements();
4375
+ if (focusables.length) focusables[focusables.length - 1].focus();
4376
+ }
4377
+
4378
+ /**
4379
+ * Removes event listeners and attributes added by the focus trap.
4380
+ * Call this method to clean up when the focus trap is no longer needed.
4381
+ */
4382
+ disconnect() {
4383
+ if (this.container.hasAttribute("data-focus-trap-container")) {
4384
+ this.container.removeAttribute("data-focus-trap-container");
4385
+ }
4386
+
4387
+ this.container.removeEventListener("keydown", this._onKeydown);
4388
+ }
4389
+ }
4390
+
4226
4391
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
4227
4392
  // See LICENSE in the project root for license information.
4228
4393
 
@@ -4720,7 +4885,7 @@ class AuroDropdownBib extends i$3 {
4720
4885
  classes[`shape-${this.shape}`] = true;
4721
4886
 
4722
4887
  return u$7`
4723
- <dialog tabindex="-1" class="${e$2(classes)}" part="bibContainer" role="${o(this.dialogRole)}" aria-labelledby="${o(this.dialogLabel ? 'dialogLabel' : undefined)}">
4888
+ <dialog class="${e$2(classes)}" part="bibContainer" role="${o(this.dialogRole)}" aria-labelledby="${o(this.dialogLabel ? 'dialogLabel' : undefined)}">
4724
4889
  ${this.dialogLabel ? u$7`<span id="dialogLabel" class="util_displayHiddenVisually">${this.dialogLabel}</span>` : ''}
4725
4890
  <slot></slot>
4726
4891
  <span id="srAnnouncement" class="util_displayHiddenVisually" aria-live="polite" role="status"></span>
@@ -4971,7 +5136,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
4971
5136
  }
4972
5137
  };
4973
5138
 
4974
- var formkitVersion$2 = '202607100029';
5139
+ var formkitVersion$2 = '202607092329';
4975
5140
 
4976
5141
  let AuroElement$2 = class AuroElement extends i$3 {
4977
5142
  static get properties() {
@@ -5725,10 +5890,7 @@ class AuroDropdown extends AuroElement$2 {
5725
5890
  }
5726
5891
 
5727
5892
 
5728
- // On Tab-driven close (eventType "focusloss"), let focus advance naturally
5729
- // — restoring to the trigger would trap the user on this dropdown, forcing
5730
- // an extra Tab to move on. Escape and outside-click still restore.
5731
- if (!this.isPopoverVisible && eventType !== "focusloss") {
5893
+ if (!this.isPopoverVisible) {
5732
5894
  // wait til the bib gets fully closed and rendered
5733
5895
  setTimeout(() => {
5734
5896
  // Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
@@ -5958,16 +6120,15 @@ class AuroDropdown extends AuroElement$2 {
5958
6120
  }
5959
6121
  });
5960
6122
  } else {
5961
- // Normal desktop (non-modal): move initial focus into the bib but
5962
- // don't trap Tab. Tab should exit the bib and let the floater's
5963
- // handleFocusLoss close it, matching native <select>/<details>
5964
- // behavior. Deferred one frame because Floating UI positions the
5965
- // popover asynchronously a synchronous focus() would target
5966
- // zero-dimension elements and be silently ignored.
6123
+ // Normal desktop: use FocusTrap on the bib element.
6124
+ // Defer focusFirstElement to the next frame because the popover
6125
+ // is positioned asynchronously by Floating UI — calling focus()
6126
+ // synchronously here would target elements with zero dimensions
6127
+ // and the focus call would be silently ignored.
6128
+ this.focusTrap = new FocusTrap(this.bibContent);
5967
6129
  requestAnimationFrame(() => {
5968
- const focusables = getFocusableElements(this.bibContent);
5969
- if (focusables.length) {
5970
- focusables[0].focus();
6130
+ if (this.focusTrap) {
6131
+ this.focusTrap.focusFirstElement();
5971
6132
  }
5972
6133
  });
5973
6134
  }
@@ -18600,7 +18761,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18600
18761
  }
18601
18762
  };
18602
18763
 
18603
- var formkitVersion$1 = '202607100029';
18764
+ var formkitVersion$1 = '202607092329';
18604
18765
 
18605
18766
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18606
18767
  // See LICENSE in the project root for license information.
@@ -19721,7 +19882,7 @@ class AuroBibtemplate extends i$3 {
19721
19882
  }
19722
19883
  }
19723
19884
 
19724
- var formkitVersion = '202607100029';
19885
+ var formkitVersion = '202607092329';
19725
19886
 
19726
19887
  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}`;
19727
19888
 
@@ -3520,40 +3520,12 @@ class AuroFloatingUI {
3520
3520
  return;
3521
3521
  }
3522
3522
 
3523
- // Chrome-specific: during popover top-layer promotion after a click on a
3524
- // slotted focusable, :focus-within can briefly evaluate false while the
3525
- // active element is still structurally inside the trigger/bib. Fall back
3526
- // to a shadow-piercing ancestry walk from the deep active element before
3527
- // treating this as a real focus loss.
3528
- try {
3529
- let active = document.activeElement;
3530
- while (active && active.shadowRoot && active.shadowRoot.activeElement) {
3531
- active = active.shadowRoot.activeElement;
3532
- }
3533
- const targets = [element, element.trigger, element.bib].filter(Boolean);
3534
- let node = active;
3535
- while (node) {
3536
- if (targets.includes(node)) {
3537
- return;
3538
- }
3539
- node =
3540
- node.parentElement ||
3541
- (node.getRootNode && node.getRootNode().host) ||
3542
- null;
3543
- }
3544
- } catch (e) {
3545
- // Defensive: fall through to the existing close path if traversal fails.
3546
- }
3547
-
3548
3523
  // if fullscreen bib is in fullscreen mode, do not close
3549
3524
  if (element.bib.hasAttribute("isfullscreen")) {
3550
3525
  return;
3551
3526
  }
3552
3527
 
3553
- // eventType "focusloss" distinguishes a Tab/click-driven close from an
3554
- // Escape keydown close. Consumers use this to decide whether to restore
3555
- // focus to the trigger (Escape) or let it advance naturally (Tab).
3556
- this.hideBib("focusloss");
3528
+ this.hideBib("keydown");
3557
3529
  }
3558
3530
 
3559
3531
  setupHideHandlers() {
@@ -4223,6 +4195,199 @@ function getFocusableElements(container) {
4223
4195
  return tabIndexedUniqueElements;
4224
4196
  }
4225
4197
 
4198
+ /**
4199
+ * FocusTrap manages keyboard focus within a specified container element, ensuring that focus does not leave the container when tabbing.
4200
+ * It is commonly used for modal dialogs or overlays to improve accessibility by trapping focus within interactive UI components.
4201
+ */
4202
+ class FocusTrap {
4203
+ /**
4204
+ * Creates a new FocusTrap instance for the given container element.
4205
+ * Initializes event listeners and prepares the container for focus management.
4206
+ *
4207
+ * @param {HTMLElement} container The DOM element to trap focus within.
4208
+ * @param {boolean} [controlTabOrder=false] If true enables manual control of the tab order by the FocusTrap.
4209
+ * @throws {Error} If the provided container is not a valid HTMLElement.
4210
+ */
4211
+ constructor(container, controlTabOrder = false) {
4212
+ if (!container || !(container instanceof HTMLElement)) {
4213
+ throw new Error("FocusTrap requires a valid HTMLElement.");
4214
+ }
4215
+
4216
+ this.container = container;
4217
+ this.tabDirection = "forward"; // or 'backward';
4218
+ this.controlTabOrder = controlTabOrder;
4219
+
4220
+ this._init();
4221
+ }
4222
+
4223
+ /**
4224
+ * Initializes the focus trap by setting up event listeners and attributes on the container.
4225
+ * Prepares the container for focus management, including support for shadow DOM and inert attributes.
4226
+ *
4227
+ * @private
4228
+ */
4229
+ _init() {
4230
+ // Add inert attribute to prevent focusing programmatically as well (if supported)
4231
+ if ("inert" in HTMLElement.prototype) {
4232
+ this.container.inert = false; // Ensure the container isn't inert
4233
+ this.container.setAttribute("data-focus-trap-container", true); // Mark for identification
4234
+ }
4235
+
4236
+ // Track tab direction
4237
+ this.container.addEventListener("keydown", this._onKeydown);
4238
+ }
4239
+
4240
+ /**
4241
+ * Gets an array of currently active (focused) elements in the document and shadow DOM.
4242
+ * @returns {Array<HTMLElement>} An array of focusable elements within the container.
4243
+ * @private
4244
+ */
4245
+ _getActiveElements() {
4246
+ // Get the active element(s) in the document and shadow root
4247
+ // This will include the active element in the shadow DOM if it exists
4248
+ // Active element may be inside the shadow DOM depending on delegatesFocus, so we need to check both
4249
+ let { activeElement } = document;
4250
+ const actives = [activeElement];
4251
+ while (activeElement?.shadowRoot?.activeElement) {
4252
+ actives.push(activeElement.shadowRoot.activeElement);
4253
+ activeElement = activeElement.shadowRoot.activeElement;
4254
+ }
4255
+ return actives;
4256
+ }
4257
+
4258
+ /**
4259
+ * Gets the next focus index based on the current index and focusable elements.
4260
+ * @param {number} currentIndex The current index of the focused element.
4261
+ * @param {Array<HTMLElement>} focusables The array of focusable elements.
4262
+ * @returns {number|null} The next focus index or null if not determined.
4263
+ */
4264
+ _getNextFocusIndex(currentIndex, focusables, actives) {
4265
+ if (this.controlTabOrder) {
4266
+ // Calculate the new index based on the current index and tab direction
4267
+ let newFocusIndex =
4268
+ currentIndex + (this.tabDirection === "forward" ? 1 : -1);
4269
+
4270
+ // Wrap-around logic
4271
+ if (newFocusIndex < 0) newFocusIndex = focusables.length - 1;
4272
+ if (newFocusIndex >= focusables.length) newFocusIndex = 0;
4273
+
4274
+ // Early return with the new index
4275
+ return newFocusIndex;
4276
+ }
4277
+
4278
+ // Determine if we need to wrap
4279
+ const atFirst =
4280
+ actives.includes(focusables[0]) || actives.includes(this.container);
4281
+ const atLast = actives.includes(focusables[focusables.length - 1]);
4282
+
4283
+ // Only wrap if at the ends
4284
+ if (this.tabDirection === "backward" && atFirst) {
4285
+ return focusables.length - 1;
4286
+ }
4287
+
4288
+ if (this.tabDirection === "forward" && atLast) {
4289
+ return 0;
4290
+ }
4291
+
4292
+ // No wrap, so don't change focus, return early
4293
+ return null;
4294
+ }
4295
+
4296
+ /**
4297
+ * Handles the Tab key press event to manage focus within the container.
4298
+ * @param {KeyboardEvent} e The keyboard event triggered by the user.
4299
+ * @returns {void}
4300
+ */
4301
+ _handleTabKey(e) {
4302
+ // Update the focusable elements
4303
+ const focusables = this._getFocusableElements();
4304
+
4305
+ // If there are no focusable elements, exit
4306
+ if (!focusables.length) return;
4307
+
4308
+ // Set the tab direction based on the key pressed
4309
+ this.tabDirection = e.shiftKey ? "backward" : "forward";
4310
+
4311
+ // Get the active elements that are currently focused
4312
+ const actives = this._getActiveElements();
4313
+
4314
+ // If we're at either end of the focusable elements, wrap around to the other end
4315
+ let focusIndex = focusables.findIndex((el) => actives.includes(el));
4316
+
4317
+ // Fallback if we have no focused element
4318
+ if (focusIndex === -1) focusIndex = 0;
4319
+
4320
+ // Get the next focus index based on the current focus index, tab direction, and controlTabOrder setting
4321
+ // Is null if no new focus index is determined
4322
+ const newFocusIndex = this._getNextFocusIndex(
4323
+ focusIndex,
4324
+ focusables,
4325
+ actives,
4326
+ );
4327
+
4328
+ // If we have a new focus index, set focus to that element
4329
+ if (newFocusIndex !== null) {
4330
+ e.preventDefault();
4331
+ focusables[newFocusIndex].focus();
4332
+ }
4333
+ }
4334
+
4335
+ /**
4336
+ * Catches the keydown event
4337
+ * @param {KeyboardEvent} e The keyboard event triggered by user interaction.
4338
+ * @private
4339
+ */
4340
+ _onKeydown = (e) => {
4341
+ // Handle tab
4342
+ if (e.key === "Tab") this._handleTabKey(e);
4343
+ };
4344
+
4345
+ /**
4346
+ * Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
4347
+ * Returns a unique, ordered array of elements that can receive focus.
4348
+ *
4349
+ * @returns {Array<HTMLElement>} An array of focusable elements within the container.
4350
+ * @private
4351
+ */
4352
+ _getFocusableElements() {
4353
+ // Use the imported utility function to get focusable elements
4354
+ const elements = getFocusableElements(this.container);
4355
+
4356
+ // Return the elements found
4357
+ return elements;
4358
+ }
4359
+
4360
+ /**
4361
+ * Moves focus to the first focusable element within the container.
4362
+ * Useful for setting initial focus when activating the focus trap.
4363
+ */
4364
+ focusFirstElement() {
4365
+ const focusables = this._getFocusableElements();
4366
+ if (focusables.length) focusables[0].focus();
4367
+ }
4368
+
4369
+ /**
4370
+ * Moves focus to the last focusable element within the container.
4371
+ * Useful for setting focus when deactivating or cycling focus in reverse.
4372
+ */
4373
+ focusLastElement() {
4374
+ const focusables = this._getFocusableElements();
4375
+ if (focusables.length) focusables[focusables.length - 1].focus();
4376
+ }
4377
+
4378
+ /**
4379
+ * Removes event listeners and attributes added by the focus trap.
4380
+ * Call this method to clean up when the focus trap is no longer needed.
4381
+ */
4382
+ disconnect() {
4383
+ if (this.container.hasAttribute("data-focus-trap-container")) {
4384
+ this.container.removeAttribute("data-focus-trap-container");
4385
+ }
4386
+
4387
+ this.container.removeEventListener("keydown", this._onKeydown);
4388
+ }
4389
+ }
4390
+
4226
4391
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
4227
4392
  // See LICENSE in the project root for license information.
4228
4393
 
@@ -4720,7 +4885,7 @@ class AuroDropdownBib extends i$3 {
4720
4885
  classes[`shape-${this.shape}`] = true;
4721
4886
 
4722
4887
  return u$7`
4723
- <dialog tabindex="-1" class="${e$2(classes)}" part="bibContainer" role="${o(this.dialogRole)}" aria-labelledby="${o(this.dialogLabel ? 'dialogLabel' : undefined)}">
4888
+ <dialog class="${e$2(classes)}" part="bibContainer" role="${o(this.dialogRole)}" aria-labelledby="${o(this.dialogLabel ? 'dialogLabel' : undefined)}">
4724
4889
  ${this.dialogLabel ? u$7`<span id="dialogLabel" class="util_displayHiddenVisually">${this.dialogLabel}</span>` : ''}
4725
4890
  <slot></slot>
4726
4891
  <span id="srAnnouncement" class="util_displayHiddenVisually" aria-live="polite" role="status"></span>
@@ -4971,7 +5136,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
4971
5136
  }
4972
5137
  };
4973
5138
 
4974
- var formkitVersion$2 = '202607100029';
5139
+ var formkitVersion$2 = '202607092329';
4975
5140
 
4976
5141
  let AuroElement$2 = class AuroElement extends i$3 {
4977
5142
  static get properties() {
@@ -5725,10 +5890,7 @@ class AuroDropdown extends AuroElement$2 {
5725
5890
  }
5726
5891
 
5727
5892
 
5728
- // On Tab-driven close (eventType "focusloss"), let focus advance naturally
5729
- // — restoring to the trigger would trap the user on this dropdown, forcing
5730
- // an extra Tab to move on. Escape and outside-click still restore.
5731
- if (!this.isPopoverVisible && eventType !== "focusloss") {
5893
+ if (!this.isPopoverVisible) {
5732
5894
  // wait til the bib gets fully closed and rendered
5733
5895
  setTimeout(() => {
5734
5896
  // Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
@@ -5958,16 +6120,15 @@ class AuroDropdown extends AuroElement$2 {
5958
6120
  }
5959
6121
  });
5960
6122
  } else {
5961
- // Normal desktop (non-modal): move initial focus into the bib but
5962
- // don't trap Tab. Tab should exit the bib and let the floater's
5963
- // handleFocusLoss close it, matching native <select>/<details>
5964
- // behavior. Deferred one frame because Floating UI positions the
5965
- // popover asynchronously a synchronous focus() would target
5966
- // zero-dimension elements and be silently ignored.
6123
+ // Normal desktop: use FocusTrap on the bib element.
6124
+ // Defer focusFirstElement to the next frame because the popover
6125
+ // is positioned asynchronously by Floating UI — calling focus()
6126
+ // synchronously here would target elements with zero dimensions
6127
+ // and the focus call would be silently ignored.
6128
+ this.focusTrap = new FocusTrap(this.bibContent);
5967
6129
  requestAnimationFrame(() => {
5968
- const focusables = getFocusableElements(this.bibContent);
5969
- if (focusables.length) {
5970
- focusables[0].focus();
6130
+ if (this.focusTrap) {
6131
+ this.focusTrap.focusFirstElement();
5971
6132
  }
5972
6133
  });
5973
6134
  }
@@ -18600,7 +18761,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
18600
18761
  }
18601
18762
  };
18602
18763
 
18603
- var formkitVersion$1 = '202607100029';
18764
+ var formkitVersion$1 = '202607092329';
18604
18765
 
18605
18766
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
18606
18767
  // See LICENSE in the project root for license information.
@@ -19721,7 +19882,7 @@ class AuroBibtemplate extends i$3 {
19721
19882
  }
19722
19883
  }
19723
19884
 
19724
- var formkitVersion = '202607100029';
19885
+ var formkitVersion = '202607092329';
19725
19886
 
19726
19887
  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}`;
19727
19888