@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.
- package/components/checkbox/demo/customize.min.js +1 -1
- package/components/checkbox/demo/getting-started.min.js +1 -1
- package/components/checkbox/demo/index.min.js +1 -1
- package/components/checkbox/dist/index.js +1 -1
- package/components/checkbox/dist/registered.js +1 -1
- package/components/combobox/demo/customize.min.js +207 -46
- package/components/combobox/demo/getting-started.min.js +207 -46
- package/components/combobox/demo/index.min.js +207 -46
- package/components/combobox/dist/index.js +207 -46
- package/components/combobox/dist/registered.js +207 -46
- package/components/counter/demo/customize.min.js +206 -45
- package/components/counter/demo/index.min.js +206 -45
- package/components/counter/dist/index.js +206 -45
- package/components/counter/dist/registered.js +206 -45
- package/components/datepicker/demo/customize.min.js +207 -46
- package/components/datepicker/demo/index.min.js +207 -46
- package/components/datepicker/dist/index.js +207 -46
- package/components/datepicker/dist/registered.js +207 -46
- package/components/dropdown/demo/customize.min.js +205 -44
- package/components/dropdown/demo/getting-started.min.js +205 -44
- package/components/dropdown/demo/index.min.js +205 -44
- package/components/dropdown/dist/index.js +205 -44
- package/components/dropdown/dist/registered.js +205 -44
- package/components/form/demo/customize.min.js +829 -185
- package/components/form/demo/getting-started.min.js +829 -185
- package/components/form/demo/index.min.js +829 -185
- package/components/form/demo/registerDemoDeps.min.js +829 -185
- package/components/input/demo/customize.min.js +1 -1
- package/components/input/demo/getting-started.min.js +1 -1
- package/components/input/demo/index.min.js +1 -1
- package/components/input/dist/index.js +1 -1
- package/components/input/dist/registered.js +1 -1
- package/components/radio/demo/customize.min.js +1 -1
- package/components/radio/demo/getting-started.min.js +1 -1
- package/components/radio/demo/index.min.js +1 -1
- package/components/radio/dist/index.js +1 -1
- package/components/radio/dist/registered.js +1 -1
- package/components/select/demo/customize.min.js +206 -45
- package/components/select/demo/getting-started.min.js +206 -45
- package/components/select/demo/index.min.js +206 -45
- package/components/select/dist/index.js +206 -45
- package/components/select/dist/registered.js +206 -45
- package/package.json +2 -2
|
@@ -3535,40 +3535,12 @@ class AuroFloatingUI {
|
|
|
3535
3535
|
return;
|
|
3536
3536
|
}
|
|
3537
3537
|
|
|
3538
|
-
// Chrome-specific: during popover top-layer promotion after a click on a
|
|
3539
|
-
// slotted focusable, :focus-within can briefly evaluate false while the
|
|
3540
|
-
// active element is still structurally inside the trigger/bib. Fall back
|
|
3541
|
-
// to a shadow-piercing ancestry walk from the deep active element before
|
|
3542
|
-
// treating this as a real focus loss.
|
|
3543
|
-
try {
|
|
3544
|
-
let active = document.activeElement;
|
|
3545
|
-
while (active && active.shadowRoot && active.shadowRoot.activeElement) {
|
|
3546
|
-
active = active.shadowRoot.activeElement;
|
|
3547
|
-
}
|
|
3548
|
-
const targets = [element, element.trigger, element.bib].filter(Boolean);
|
|
3549
|
-
let node = active;
|
|
3550
|
-
while (node) {
|
|
3551
|
-
if (targets.includes(node)) {
|
|
3552
|
-
return;
|
|
3553
|
-
}
|
|
3554
|
-
node =
|
|
3555
|
-
node.parentElement ||
|
|
3556
|
-
(node.getRootNode && node.getRootNode().host) ||
|
|
3557
|
-
null;
|
|
3558
|
-
}
|
|
3559
|
-
} catch (e) {
|
|
3560
|
-
// Defensive: fall through to the existing close path if traversal fails.
|
|
3561
|
-
}
|
|
3562
|
-
|
|
3563
3538
|
// if fullscreen bib is in fullscreen mode, do not close
|
|
3564
3539
|
if (element.bib.hasAttribute("isfullscreen")) {
|
|
3565
3540
|
return;
|
|
3566
3541
|
}
|
|
3567
3542
|
|
|
3568
|
-
|
|
3569
|
-
// Escape keydown close. Consumers use this to decide whether to restore
|
|
3570
|
-
// focus to the trigger (Escape) or let it advance naturally (Tab).
|
|
3571
|
-
this.hideBib("focusloss");
|
|
3543
|
+
this.hideBib("keydown");
|
|
3572
3544
|
}
|
|
3573
3545
|
|
|
3574
3546
|
setupHideHandlers() {
|
|
@@ -4238,6 +4210,199 @@ function getFocusableElements(container) {
|
|
|
4238
4210
|
return tabIndexedUniqueElements;
|
|
4239
4211
|
}
|
|
4240
4212
|
|
|
4213
|
+
/**
|
|
4214
|
+
* FocusTrap manages keyboard focus within a specified container element, ensuring that focus does not leave the container when tabbing.
|
|
4215
|
+
* It is commonly used for modal dialogs or overlays to improve accessibility by trapping focus within interactive UI components.
|
|
4216
|
+
*/
|
|
4217
|
+
class FocusTrap {
|
|
4218
|
+
/**
|
|
4219
|
+
* Creates a new FocusTrap instance for the given container element.
|
|
4220
|
+
* Initializes event listeners and prepares the container for focus management.
|
|
4221
|
+
*
|
|
4222
|
+
* @param {HTMLElement} container The DOM element to trap focus within.
|
|
4223
|
+
* @param {boolean} [controlTabOrder=false] If true enables manual control of the tab order by the FocusTrap.
|
|
4224
|
+
* @throws {Error} If the provided container is not a valid HTMLElement.
|
|
4225
|
+
*/
|
|
4226
|
+
constructor(container, controlTabOrder = false) {
|
|
4227
|
+
if (!container || !(container instanceof HTMLElement)) {
|
|
4228
|
+
throw new Error("FocusTrap requires a valid HTMLElement.");
|
|
4229
|
+
}
|
|
4230
|
+
|
|
4231
|
+
this.container = container;
|
|
4232
|
+
this.tabDirection = "forward"; // or 'backward';
|
|
4233
|
+
this.controlTabOrder = controlTabOrder;
|
|
4234
|
+
|
|
4235
|
+
this._init();
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
/**
|
|
4239
|
+
* Initializes the focus trap by setting up event listeners and attributes on the container.
|
|
4240
|
+
* Prepares the container for focus management, including support for shadow DOM and inert attributes.
|
|
4241
|
+
*
|
|
4242
|
+
* @private
|
|
4243
|
+
*/
|
|
4244
|
+
_init() {
|
|
4245
|
+
// Add inert attribute to prevent focusing programmatically as well (if supported)
|
|
4246
|
+
if ("inert" in HTMLElement.prototype) {
|
|
4247
|
+
this.container.inert = false; // Ensure the container isn't inert
|
|
4248
|
+
this.container.setAttribute("data-focus-trap-container", true); // Mark for identification
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
// Track tab direction
|
|
4252
|
+
this.container.addEventListener("keydown", this._onKeydown);
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4255
|
+
/**
|
|
4256
|
+
* Gets an array of currently active (focused) elements in the document and shadow DOM.
|
|
4257
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
4258
|
+
* @private
|
|
4259
|
+
*/
|
|
4260
|
+
_getActiveElements() {
|
|
4261
|
+
// Get the active element(s) in the document and shadow root
|
|
4262
|
+
// This will include the active element in the shadow DOM if it exists
|
|
4263
|
+
// Active element may be inside the shadow DOM depending on delegatesFocus, so we need to check both
|
|
4264
|
+
let { activeElement } = document;
|
|
4265
|
+
const actives = [activeElement];
|
|
4266
|
+
while (activeElement?.shadowRoot?.activeElement) {
|
|
4267
|
+
actives.push(activeElement.shadowRoot.activeElement);
|
|
4268
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
4269
|
+
}
|
|
4270
|
+
return actives;
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
/**
|
|
4274
|
+
* Gets the next focus index based on the current index and focusable elements.
|
|
4275
|
+
* @param {number} currentIndex The current index of the focused element.
|
|
4276
|
+
* @param {Array<HTMLElement>} focusables The array of focusable elements.
|
|
4277
|
+
* @returns {number|null} The next focus index or null if not determined.
|
|
4278
|
+
*/
|
|
4279
|
+
_getNextFocusIndex(currentIndex, focusables, actives) {
|
|
4280
|
+
if (this.controlTabOrder) {
|
|
4281
|
+
// Calculate the new index based on the current index and tab direction
|
|
4282
|
+
let newFocusIndex =
|
|
4283
|
+
currentIndex + (this.tabDirection === "forward" ? 1 : -1);
|
|
4284
|
+
|
|
4285
|
+
// Wrap-around logic
|
|
4286
|
+
if (newFocusIndex < 0) newFocusIndex = focusables.length - 1;
|
|
4287
|
+
if (newFocusIndex >= focusables.length) newFocusIndex = 0;
|
|
4288
|
+
|
|
4289
|
+
// Early return with the new index
|
|
4290
|
+
return newFocusIndex;
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
// Determine if we need to wrap
|
|
4294
|
+
const atFirst =
|
|
4295
|
+
actives.includes(focusables[0]) || actives.includes(this.container);
|
|
4296
|
+
const atLast = actives.includes(focusables[focusables.length - 1]);
|
|
4297
|
+
|
|
4298
|
+
// Only wrap if at the ends
|
|
4299
|
+
if (this.tabDirection === "backward" && atFirst) {
|
|
4300
|
+
return focusables.length - 1;
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
if (this.tabDirection === "forward" && atLast) {
|
|
4304
|
+
return 0;
|
|
4305
|
+
}
|
|
4306
|
+
|
|
4307
|
+
// No wrap, so don't change focus, return early
|
|
4308
|
+
return null;
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
/**
|
|
4312
|
+
* Handles the Tab key press event to manage focus within the container.
|
|
4313
|
+
* @param {KeyboardEvent} e The keyboard event triggered by the user.
|
|
4314
|
+
* @returns {void}
|
|
4315
|
+
*/
|
|
4316
|
+
_handleTabKey(e) {
|
|
4317
|
+
// Update the focusable elements
|
|
4318
|
+
const focusables = this._getFocusableElements();
|
|
4319
|
+
|
|
4320
|
+
// If there are no focusable elements, exit
|
|
4321
|
+
if (!focusables.length) return;
|
|
4322
|
+
|
|
4323
|
+
// Set the tab direction based on the key pressed
|
|
4324
|
+
this.tabDirection = e.shiftKey ? "backward" : "forward";
|
|
4325
|
+
|
|
4326
|
+
// Get the active elements that are currently focused
|
|
4327
|
+
const actives = this._getActiveElements();
|
|
4328
|
+
|
|
4329
|
+
// If we're at either end of the focusable elements, wrap around to the other end
|
|
4330
|
+
let focusIndex = focusables.findIndex((el) => actives.includes(el));
|
|
4331
|
+
|
|
4332
|
+
// Fallback if we have no focused element
|
|
4333
|
+
if (focusIndex === -1) focusIndex = 0;
|
|
4334
|
+
|
|
4335
|
+
// Get the next focus index based on the current focus index, tab direction, and controlTabOrder setting
|
|
4336
|
+
// Is null if no new focus index is determined
|
|
4337
|
+
const newFocusIndex = this._getNextFocusIndex(
|
|
4338
|
+
focusIndex,
|
|
4339
|
+
focusables,
|
|
4340
|
+
actives,
|
|
4341
|
+
);
|
|
4342
|
+
|
|
4343
|
+
// If we have a new focus index, set focus to that element
|
|
4344
|
+
if (newFocusIndex !== null) {
|
|
4345
|
+
e.preventDefault();
|
|
4346
|
+
focusables[newFocusIndex].focus();
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4350
|
+
/**
|
|
4351
|
+
* Catches the keydown event
|
|
4352
|
+
* @param {KeyboardEvent} e The keyboard event triggered by user interaction.
|
|
4353
|
+
* @private
|
|
4354
|
+
*/
|
|
4355
|
+
_onKeydown = (e) => {
|
|
4356
|
+
// Handle tab
|
|
4357
|
+
if (e.key === "Tab") this._handleTabKey(e);
|
|
4358
|
+
};
|
|
4359
|
+
|
|
4360
|
+
/**
|
|
4361
|
+
* Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
|
|
4362
|
+
* Returns a unique, ordered array of elements that can receive focus.
|
|
4363
|
+
*
|
|
4364
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
4365
|
+
* @private
|
|
4366
|
+
*/
|
|
4367
|
+
_getFocusableElements() {
|
|
4368
|
+
// Use the imported utility function to get focusable elements
|
|
4369
|
+
const elements = getFocusableElements(this.container);
|
|
4370
|
+
|
|
4371
|
+
// Return the elements found
|
|
4372
|
+
return elements;
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4375
|
+
/**
|
|
4376
|
+
* Moves focus to the first focusable element within the container.
|
|
4377
|
+
* Useful for setting initial focus when activating the focus trap.
|
|
4378
|
+
*/
|
|
4379
|
+
focusFirstElement() {
|
|
4380
|
+
const focusables = this._getFocusableElements();
|
|
4381
|
+
if (focusables.length) focusables[0].focus();
|
|
4382
|
+
}
|
|
4383
|
+
|
|
4384
|
+
/**
|
|
4385
|
+
* Moves focus to the last focusable element within the container.
|
|
4386
|
+
* Useful for setting focus when deactivating or cycling focus in reverse.
|
|
4387
|
+
*/
|
|
4388
|
+
focusLastElement() {
|
|
4389
|
+
const focusables = this._getFocusableElements();
|
|
4390
|
+
if (focusables.length) focusables[focusables.length - 1].focus();
|
|
4391
|
+
}
|
|
4392
|
+
|
|
4393
|
+
/**
|
|
4394
|
+
* Removes event listeners and attributes added by the focus trap.
|
|
4395
|
+
* Call this method to clean up when the focus trap is no longer needed.
|
|
4396
|
+
*/
|
|
4397
|
+
disconnect() {
|
|
4398
|
+
if (this.container.hasAttribute("data-focus-trap-container")) {
|
|
4399
|
+
this.container.removeAttribute("data-focus-trap-container");
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4402
|
+
this.container.removeEventListener("keydown", this._onKeydown);
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4241
4406
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
4242
4407
|
// See LICENSE in the project root for license information.
|
|
4243
4408
|
|
|
@@ -4735,7 +4900,7 @@ class AuroDropdownBib extends i$3 {
|
|
|
4735
4900
|
classes[`shape-${this.shape}`] = true;
|
|
4736
4901
|
|
|
4737
4902
|
return u$7`
|
|
4738
|
-
<dialog
|
|
4903
|
+
<dialog class="${e$2(classes)}" part="bibContainer" role="${o(this.dialogRole)}" aria-labelledby="${o(this.dialogLabel ? 'dialogLabel' : undefined)}">
|
|
4739
4904
|
${this.dialogLabel ? u$7`<span id="dialogLabel" class="util_displayHiddenVisually">${this.dialogLabel}</span>` : ''}
|
|
4740
4905
|
<slot></slot>
|
|
4741
4906
|
<span id="srAnnouncement" class="util_displayHiddenVisually" aria-live="polite" role="status"></span>
|
|
@@ -4986,7 +5151,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
|
|
|
4986
5151
|
}
|
|
4987
5152
|
};
|
|
4988
5153
|
|
|
4989
|
-
var formkitVersion$2 = '
|
|
5154
|
+
var formkitVersion$2 = '202607092329';
|
|
4990
5155
|
|
|
4991
5156
|
let AuroElement$2 = class AuroElement extends i$3 {
|
|
4992
5157
|
static get properties() {
|
|
@@ -5740,10 +5905,7 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5740
5905
|
}
|
|
5741
5906
|
|
|
5742
5907
|
|
|
5743
|
-
|
|
5744
|
-
// — restoring to the trigger would trap the user on this dropdown, forcing
|
|
5745
|
-
// an extra Tab to move on. Escape and outside-click still restore.
|
|
5746
|
-
if (!this.isPopoverVisible && eventType !== "focusloss") {
|
|
5908
|
+
if (!this.isPopoverVisible) {
|
|
5747
5909
|
// wait til the bib gets fully closed and rendered
|
|
5748
5910
|
setTimeout(() => {
|
|
5749
5911
|
// Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
|
|
@@ -5973,16 +6135,15 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5973
6135
|
}
|
|
5974
6136
|
});
|
|
5975
6137
|
} else {
|
|
5976
|
-
// Normal desktop
|
|
5977
|
-
//
|
|
5978
|
-
//
|
|
5979
|
-
//
|
|
5980
|
-
//
|
|
5981
|
-
|
|
6138
|
+
// Normal desktop: use FocusTrap on the bib element.
|
|
6139
|
+
// Defer focusFirstElement to the next frame because the popover
|
|
6140
|
+
// is positioned asynchronously by Floating UI — calling focus()
|
|
6141
|
+
// synchronously here would target elements with zero dimensions
|
|
6142
|
+
// and the focus call would be silently ignored.
|
|
6143
|
+
this.focusTrap = new FocusTrap(this.bibContent);
|
|
5982
6144
|
requestAnimationFrame(() => {
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
focusables[0].focus();
|
|
6145
|
+
if (this.focusTrap) {
|
|
6146
|
+
this.focusTrap.focusFirstElement();
|
|
5986
6147
|
}
|
|
5987
6148
|
});
|
|
5988
6149
|
}
|
|
@@ -18615,7 +18776,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
|
|
|
18615
18776
|
}
|
|
18616
18777
|
};
|
|
18617
18778
|
|
|
18618
|
-
var formkitVersion$1 = '
|
|
18779
|
+
var formkitVersion$1 = '202607092329';
|
|
18619
18780
|
|
|
18620
18781
|
// Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
18621
18782
|
// See LICENSE in the project root for license information.
|
|
@@ -19736,7 +19897,7 @@ class AuroBibtemplate extends i$3 {
|
|
|
19736
19897
|
}
|
|
19737
19898
|
}
|
|
19738
19899
|
|
|
19739
|
-
var formkitVersion = '
|
|
19900
|
+
var formkitVersion = '202607092329';
|
|
19740
19901
|
|
|
19741
19902
|
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}`;
|
|
19742
19903
|
|
|
@@ -3453,40 +3453,12 @@ class AuroFloatingUI {
|
|
|
3453
3453
|
return;
|
|
3454
3454
|
}
|
|
3455
3455
|
|
|
3456
|
-
// Chrome-specific: during popover top-layer promotion after a click on a
|
|
3457
|
-
// slotted focusable, :focus-within can briefly evaluate false while the
|
|
3458
|
-
// active element is still structurally inside the trigger/bib. Fall back
|
|
3459
|
-
// to a shadow-piercing ancestry walk from the deep active element before
|
|
3460
|
-
// treating this as a real focus loss.
|
|
3461
|
-
try {
|
|
3462
|
-
let active = document.activeElement;
|
|
3463
|
-
while (active && active.shadowRoot && active.shadowRoot.activeElement) {
|
|
3464
|
-
active = active.shadowRoot.activeElement;
|
|
3465
|
-
}
|
|
3466
|
-
const targets = [element, element.trigger, element.bib].filter(Boolean);
|
|
3467
|
-
let node = active;
|
|
3468
|
-
while (node) {
|
|
3469
|
-
if (targets.includes(node)) {
|
|
3470
|
-
return;
|
|
3471
|
-
}
|
|
3472
|
-
node =
|
|
3473
|
-
node.parentElement ||
|
|
3474
|
-
(node.getRootNode && node.getRootNode().host) ||
|
|
3475
|
-
null;
|
|
3476
|
-
}
|
|
3477
|
-
} catch (e) {
|
|
3478
|
-
// Defensive: fall through to the existing close path if traversal fails.
|
|
3479
|
-
}
|
|
3480
|
-
|
|
3481
3456
|
// if fullscreen bib is in fullscreen mode, do not close
|
|
3482
3457
|
if (element.bib.hasAttribute("isfullscreen")) {
|
|
3483
3458
|
return;
|
|
3484
3459
|
}
|
|
3485
3460
|
|
|
3486
|
-
|
|
3487
|
-
// Escape keydown close. Consumers use this to decide whether to restore
|
|
3488
|
-
// focus to the trigger (Escape) or let it advance naturally (Tab).
|
|
3489
|
-
this.hideBib("focusloss");
|
|
3461
|
+
this.hideBib("keydown");
|
|
3490
3462
|
}
|
|
3491
3463
|
|
|
3492
3464
|
setupHideHandlers() {
|
|
@@ -4156,6 +4128,199 @@ function getFocusableElements(container) {
|
|
|
4156
4128
|
return tabIndexedUniqueElements;
|
|
4157
4129
|
}
|
|
4158
4130
|
|
|
4131
|
+
/**
|
|
4132
|
+
* FocusTrap manages keyboard focus within a specified container element, ensuring that focus does not leave the container when tabbing.
|
|
4133
|
+
* It is commonly used for modal dialogs or overlays to improve accessibility by trapping focus within interactive UI components.
|
|
4134
|
+
*/
|
|
4135
|
+
class FocusTrap {
|
|
4136
|
+
/**
|
|
4137
|
+
* Creates a new FocusTrap instance for the given container element.
|
|
4138
|
+
* Initializes event listeners and prepares the container for focus management.
|
|
4139
|
+
*
|
|
4140
|
+
* @param {HTMLElement} container The DOM element to trap focus within.
|
|
4141
|
+
* @param {boolean} [controlTabOrder=false] If true enables manual control of the tab order by the FocusTrap.
|
|
4142
|
+
* @throws {Error} If the provided container is not a valid HTMLElement.
|
|
4143
|
+
*/
|
|
4144
|
+
constructor(container, controlTabOrder = false) {
|
|
4145
|
+
if (!container || !(container instanceof HTMLElement)) {
|
|
4146
|
+
throw new Error("FocusTrap requires a valid HTMLElement.");
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
this.container = container;
|
|
4150
|
+
this.tabDirection = "forward"; // or 'backward';
|
|
4151
|
+
this.controlTabOrder = controlTabOrder;
|
|
4152
|
+
|
|
4153
|
+
this._init();
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
/**
|
|
4157
|
+
* Initializes the focus trap by setting up event listeners and attributes on the container.
|
|
4158
|
+
* Prepares the container for focus management, including support for shadow DOM and inert attributes.
|
|
4159
|
+
*
|
|
4160
|
+
* @private
|
|
4161
|
+
*/
|
|
4162
|
+
_init() {
|
|
4163
|
+
// Add inert attribute to prevent focusing programmatically as well (if supported)
|
|
4164
|
+
if ("inert" in HTMLElement.prototype) {
|
|
4165
|
+
this.container.inert = false; // Ensure the container isn't inert
|
|
4166
|
+
this.container.setAttribute("data-focus-trap-container", true); // Mark for identification
|
|
4167
|
+
}
|
|
4168
|
+
|
|
4169
|
+
// Track tab direction
|
|
4170
|
+
this.container.addEventListener("keydown", this._onKeydown);
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
/**
|
|
4174
|
+
* Gets an array of currently active (focused) elements in the document and shadow DOM.
|
|
4175
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
4176
|
+
* @private
|
|
4177
|
+
*/
|
|
4178
|
+
_getActiveElements() {
|
|
4179
|
+
// Get the active element(s) in the document and shadow root
|
|
4180
|
+
// This will include the active element in the shadow DOM if it exists
|
|
4181
|
+
// Active element may be inside the shadow DOM depending on delegatesFocus, so we need to check both
|
|
4182
|
+
let { activeElement } = document;
|
|
4183
|
+
const actives = [activeElement];
|
|
4184
|
+
while (activeElement?.shadowRoot?.activeElement) {
|
|
4185
|
+
actives.push(activeElement.shadowRoot.activeElement);
|
|
4186
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
4187
|
+
}
|
|
4188
|
+
return actives;
|
|
4189
|
+
}
|
|
4190
|
+
|
|
4191
|
+
/**
|
|
4192
|
+
* Gets the next focus index based on the current index and focusable elements.
|
|
4193
|
+
* @param {number} currentIndex The current index of the focused element.
|
|
4194
|
+
* @param {Array<HTMLElement>} focusables The array of focusable elements.
|
|
4195
|
+
* @returns {number|null} The next focus index or null if not determined.
|
|
4196
|
+
*/
|
|
4197
|
+
_getNextFocusIndex(currentIndex, focusables, actives) {
|
|
4198
|
+
if (this.controlTabOrder) {
|
|
4199
|
+
// Calculate the new index based on the current index and tab direction
|
|
4200
|
+
let newFocusIndex =
|
|
4201
|
+
currentIndex + (this.tabDirection === "forward" ? 1 : -1);
|
|
4202
|
+
|
|
4203
|
+
// Wrap-around logic
|
|
4204
|
+
if (newFocusIndex < 0) newFocusIndex = focusables.length - 1;
|
|
4205
|
+
if (newFocusIndex >= focusables.length) newFocusIndex = 0;
|
|
4206
|
+
|
|
4207
|
+
// Early return with the new index
|
|
4208
|
+
return newFocusIndex;
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
// Determine if we need to wrap
|
|
4212
|
+
const atFirst =
|
|
4213
|
+
actives.includes(focusables[0]) || actives.includes(this.container);
|
|
4214
|
+
const atLast = actives.includes(focusables[focusables.length - 1]);
|
|
4215
|
+
|
|
4216
|
+
// Only wrap if at the ends
|
|
4217
|
+
if (this.tabDirection === "backward" && atFirst) {
|
|
4218
|
+
return focusables.length - 1;
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
if (this.tabDirection === "forward" && atLast) {
|
|
4222
|
+
return 0;
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
// No wrap, so don't change focus, return early
|
|
4226
|
+
return null;
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
/**
|
|
4230
|
+
* Handles the Tab key press event to manage focus within the container.
|
|
4231
|
+
* @param {KeyboardEvent} e The keyboard event triggered by the user.
|
|
4232
|
+
* @returns {void}
|
|
4233
|
+
*/
|
|
4234
|
+
_handleTabKey(e) {
|
|
4235
|
+
// Update the focusable elements
|
|
4236
|
+
const focusables = this._getFocusableElements();
|
|
4237
|
+
|
|
4238
|
+
// If there are no focusable elements, exit
|
|
4239
|
+
if (!focusables.length) return;
|
|
4240
|
+
|
|
4241
|
+
// Set the tab direction based on the key pressed
|
|
4242
|
+
this.tabDirection = e.shiftKey ? "backward" : "forward";
|
|
4243
|
+
|
|
4244
|
+
// Get the active elements that are currently focused
|
|
4245
|
+
const actives = this._getActiveElements();
|
|
4246
|
+
|
|
4247
|
+
// If we're at either end of the focusable elements, wrap around to the other end
|
|
4248
|
+
let focusIndex = focusables.findIndex((el) => actives.includes(el));
|
|
4249
|
+
|
|
4250
|
+
// Fallback if we have no focused element
|
|
4251
|
+
if (focusIndex === -1) focusIndex = 0;
|
|
4252
|
+
|
|
4253
|
+
// Get the next focus index based on the current focus index, tab direction, and controlTabOrder setting
|
|
4254
|
+
// Is null if no new focus index is determined
|
|
4255
|
+
const newFocusIndex = this._getNextFocusIndex(
|
|
4256
|
+
focusIndex,
|
|
4257
|
+
focusables,
|
|
4258
|
+
actives,
|
|
4259
|
+
);
|
|
4260
|
+
|
|
4261
|
+
// If we have a new focus index, set focus to that element
|
|
4262
|
+
if (newFocusIndex !== null) {
|
|
4263
|
+
e.preventDefault();
|
|
4264
|
+
focusables[newFocusIndex].focus();
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
|
|
4268
|
+
/**
|
|
4269
|
+
* Catches the keydown event
|
|
4270
|
+
* @param {KeyboardEvent} e The keyboard event triggered by user interaction.
|
|
4271
|
+
* @private
|
|
4272
|
+
*/
|
|
4273
|
+
_onKeydown = (e) => {
|
|
4274
|
+
// Handle tab
|
|
4275
|
+
if (e.key === "Tab") this._handleTabKey(e);
|
|
4276
|
+
};
|
|
4277
|
+
|
|
4278
|
+
/**
|
|
4279
|
+
* Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
|
|
4280
|
+
* Returns a unique, ordered array of elements that can receive focus.
|
|
4281
|
+
*
|
|
4282
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
4283
|
+
* @private
|
|
4284
|
+
*/
|
|
4285
|
+
_getFocusableElements() {
|
|
4286
|
+
// Use the imported utility function to get focusable elements
|
|
4287
|
+
const elements = getFocusableElements(this.container);
|
|
4288
|
+
|
|
4289
|
+
// Return the elements found
|
|
4290
|
+
return elements;
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
/**
|
|
4294
|
+
* Moves focus to the first focusable element within the container.
|
|
4295
|
+
* Useful for setting initial focus when activating the focus trap.
|
|
4296
|
+
*/
|
|
4297
|
+
focusFirstElement() {
|
|
4298
|
+
const focusables = this._getFocusableElements();
|
|
4299
|
+
if (focusables.length) focusables[0].focus();
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
/**
|
|
4303
|
+
* Moves focus to the last focusable element within the container.
|
|
4304
|
+
* Useful for setting focus when deactivating or cycling focus in reverse.
|
|
4305
|
+
*/
|
|
4306
|
+
focusLastElement() {
|
|
4307
|
+
const focusables = this._getFocusableElements();
|
|
4308
|
+
if (focusables.length) focusables[focusables.length - 1].focus();
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
/**
|
|
4312
|
+
* Removes event listeners and attributes added by the focus trap.
|
|
4313
|
+
* Call this method to clean up when the focus trap is no longer needed.
|
|
4314
|
+
*/
|
|
4315
|
+
disconnect() {
|
|
4316
|
+
if (this.container.hasAttribute("data-focus-trap-container")) {
|
|
4317
|
+
this.container.removeAttribute("data-focus-trap-container");
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
this.container.removeEventListener("keydown", this._onKeydown);
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4323
|
+
|
|
4159
4324
|
// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
|
|
4160
4325
|
// See LICENSE in the project root for license information.
|
|
4161
4326
|
|
|
@@ -4653,7 +4818,7 @@ class AuroDropdownBib extends LitElement {
|
|
|
4653
4818
|
classes[`shape-${this.shape}`] = true;
|
|
4654
4819
|
|
|
4655
4820
|
return html`
|
|
4656
|
-
<dialog
|
|
4821
|
+
<dialog class="${classMap(classes)}" part="bibContainer" role="${ifDefined(this.dialogRole)}" aria-labelledby="${ifDefined(this.dialogLabel ? 'dialogLabel' : undefined)}">
|
|
4657
4822
|
${this.dialogLabel ? html`<span id="dialogLabel" class="util_displayHiddenVisually">${this.dialogLabel}</span>` : ''}
|
|
4658
4823
|
<slot></slot>
|
|
4659
4824
|
<span id="srAnnouncement" class="util_displayHiddenVisually" aria-live="polite" role="status"></span>
|
|
@@ -4904,7 +5069,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
|
|
|
4904
5069
|
}
|
|
4905
5070
|
};
|
|
4906
5071
|
|
|
4907
|
-
var formkitVersion$2 = '
|
|
5072
|
+
var formkitVersion$2 = '202607092329';
|
|
4908
5073
|
|
|
4909
5074
|
let AuroElement$2 = class AuroElement extends LitElement {
|
|
4910
5075
|
static get properties() {
|
|
@@ -5658,10 +5823,7 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5658
5823
|
}
|
|
5659
5824
|
|
|
5660
5825
|
|
|
5661
|
-
|
|
5662
|
-
// — restoring to the trigger would trap the user on this dropdown, forcing
|
|
5663
|
-
// an extra Tab to move on. Escape and outside-click still restore.
|
|
5664
|
-
if (!this.isPopoverVisible && eventType !== "focusloss") {
|
|
5826
|
+
if (!this.isPopoverVisible) {
|
|
5665
5827
|
// wait til the bib gets fully closed and rendered
|
|
5666
5828
|
setTimeout(() => {
|
|
5667
5829
|
// Skip if the bib re-opened, or if focus moved intentionally outside the dropdown (not to body).
|
|
@@ -5891,16 +6053,15 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5891
6053
|
}
|
|
5892
6054
|
});
|
|
5893
6055
|
} else {
|
|
5894
|
-
// Normal desktop
|
|
5895
|
-
//
|
|
5896
|
-
//
|
|
5897
|
-
//
|
|
5898
|
-
//
|
|
5899
|
-
|
|
6056
|
+
// Normal desktop: use FocusTrap on the bib element.
|
|
6057
|
+
// Defer focusFirstElement to the next frame because the popover
|
|
6058
|
+
// is positioned asynchronously by Floating UI — calling focus()
|
|
6059
|
+
// synchronously here would target elements with zero dimensions
|
|
6060
|
+
// and the focus call would be silently ignored.
|
|
6061
|
+
this.focusTrap = new FocusTrap(this.bibContent);
|
|
5900
6062
|
requestAnimationFrame(() => {
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
focusables[0].focus();
|
|
6063
|
+
if (this.focusTrap) {
|
|
6064
|
+
this.focusTrap.focusFirstElement();
|
|
5904
6065
|
}
|
|
5905
6066
|
});
|
|
5906
6067
|
}
|
|
@@ -18526,7 +18687,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
|
|
|
18526
18687
|
}
|
|
18527
18688
|
};
|
|
18528
18689
|
|
|
18529
|
-
var formkitVersion$1 = '
|
|
18690
|
+
var formkitVersion$1 = '202607092329';
|
|
18530
18691
|
|
|
18531
18692
|
// Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
18532
18693
|
// See LICENSE in the project root for license information.
|
|
@@ -19647,7 +19808,7 @@ class AuroBibtemplate extends LitElement {
|
|
|
19647
19808
|
}
|
|
19648
19809
|
}
|
|
19649
19810
|
|
|
19650
|
-
var formkitVersion = '
|
|
19811
|
+
var formkitVersion = '202607092329';
|
|
19651
19812
|
|
|
19652
19813
|
var styleCss$1 = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
|
|
19653
19814
|
|