@aurodesignsystem-dev/auro-formkit 0.0.0-pr1609.0 → 0.0.0-pr1610.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 +3 -3
- package/components/combobox/demo/getting-started.min.js +3 -3
- package/components/combobox/demo/index.min.js +3 -3
- package/components/combobox/dist/index.js +3 -3
- package/components/combobox/dist/registered.js +3 -3
- package/components/counter/demo/customize.min.js +296 -22
- package/components/counter/demo/getting-started.min.js +296 -22
- package/components/counter/demo/index.min.js +296 -22
- package/components/counter/dist/counterGroupKeyboardStrategy.d.ts +1 -0
- package/components/counter/dist/index.js +296 -22
- package/components/counter/dist/registered.js +296 -22
- package/components/datepicker/demo/customize.min.js +3 -3
- package/components/datepicker/demo/getting-started.min.js +3 -3
- package/components/datepicker/demo/index.min.js +3 -3
- package/components/datepicker/dist/index.js +3 -3
- package/components/datepicker/dist/registered.js +3 -3
- package/components/dropdown/demo/customize.min.js +1 -1
- package/components/dropdown/demo/getting-started.min.js +2 -2
- package/components/dropdown/demo/index.min.js +1 -1
- package/components/dropdown/dist/index.js +1 -1
- package/components/dropdown/dist/registered.js +1 -1
- package/components/form/demo/customize.min.js +307 -33
- package/components/form/demo/getting-started.min.js +307 -33
- package/components/form/demo/index.min.js +307 -33
- package/components/form/demo/registerDemoDeps.min.js +307 -33
- 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 +2 -2
- package/components/select/demo/getting-started.min.js +2 -2
- package/components/select/demo/index.min.js +2 -2
- package/components/select/dist/index.js +2 -2
- package/components/select/dist/registered.js +2 -2
- package/custom-elements.json +574 -574
- package/package.json +1 -1
|
@@ -1077,7 +1077,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
|
|
|
1077
1077
|
}
|
|
1078
1078
|
};
|
|
1079
1079
|
|
|
1080
|
-
var formkitVersion$1 = '
|
|
1080
|
+
var formkitVersion$1 = '202609042059';
|
|
1081
1081
|
|
|
1082
1082
|
// Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
|
|
1083
1083
|
// See LICENSE in the project root for license information.
|
|
@@ -4458,7 +4458,7 @@ class AuroFloatingUI {
|
|
|
4458
4458
|
}
|
|
4459
4459
|
|
|
4460
4460
|
// Selectors for focusable elements
|
|
4461
|
-
const FOCUSABLE_SELECTORS = [
|
|
4461
|
+
const FOCUSABLE_SELECTORS$1 = [
|
|
4462
4462
|
"a[href]",
|
|
4463
4463
|
"button:not([disabled])",
|
|
4464
4464
|
"textarea:not([disabled])",
|
|
@@ -4472,7 +4472,7 @@ const FOCUSABLE_SELECTORS = [
|
|
|
4472
4472
|
];
|
|
4473
4473
|
|
|
4474
4474
|
// List of custom components that are known to be focusable
|
|
4475
|
-
const FOCUSABLE_COMPONENTS = [
|
|
4475
|
+
const FOCUSABLE_COMPONENTS$1 = [
|
|
4476
4476
|
"auro-checkbox",
|
|
4477
4477
|
"auro-radio",
|
|
4478
4478
|
"auro-dropdown",
|
|
@@ -4494,12 +4494,12 @@ const FOCUSABLE_COMPONENTS = [
|
|
|
4494
4494
|
* @param {HTMLElement} element The element to check for focusability.
|
|
4495
4495
|
* @returns {boolean} True if the element is a focusable custom component, false otherwise.
|
|
4496
4496
|
*/
|
|
4497
|
-
function isFocusableComponent(element) {
|
|
4497
|
+
function isFocusableComponent$1(element) {
|
|
4498
4498
|
const componentName = element.tagName.toLowerCase();
|
|
4499
4499
|
|
|
4500
4500
|
// Guard Clause: Element is a focusable component
|
|
4501
4501
|
if (
|
|
4502
|
-
!FOCUSABLE_COMPONENTS.some(
|
|
4502
|
+
!FOCUSABLE_COMPONENTS$1.some(
|
|
4503
4503
|
(name) => element.hasAttribute(name) || componentName === name,
|
|
4504
4504
|
)
|
|
4505
4505
|
)
|
|
@@ -4523,7 +4523,7 @@ function isFocusableComponent(element) {
|
|
|
4523
4523
|
* @param {HTMLElement} element The element whose tabindex to read.
|
|
4524
4524
|
* @returns {?number} The numeric tabindex or null if missing/invalid.
|
|
4525
4525
|
*/
|
|
4526
|
-
function getNumericTabIndex(element) {
|
|
4526
|
+
function getNumericTabIndex$1(element) {
|
|
4527
4527
|
const raw = element.getAttribute("tabindex");
|
|
4528
4528
|
if (raw == null) return null;
|
|
4529
4529
|
|
|
@@ -4539,7 +4539,7 @@ function getNumericTabIndex(element) {
|
|
|
4539
4539
|
* @param {HTMLElement} container The container to search within
|
|
4540
4540
|
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
4541
4541
|
*/
|
|
4542
|
-
function getFocusableElements(container) {
|
|
4542
|
+
function getFocusableElements$1(container) {
|
|
4543
4543
|
// Get elements in DOM order by walking the tree
|
|
4544
4544
|
const orderedFocusableElements = [];
|
|
4545
4545
|
|
|
@@ -4548,7 +4548,7 @@ function getFocusableElements(container) {
|
|
|
4548
4548
|
// Check if current element is focusable
|
|
4549
4549
|
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
4550
4550
|
// Check if this is a custom component that is focusable
|
|
4551
|
-
const isComponentFocusable = isFocusableComponent(root);
|
|
4551
|
+
const isComponentFocusable = isFocusableComponent$1(root);
|
|
4552
4552
|
|
|
4553
4553
|
if (isComponentFocusable) {
|
|
4554
4554
|
// Add the component itself as a focusable element and don't traverse its shadow DOM
|
|
@@ -4557,7 +4557,7 @@ function getFocusableElements(container) {
|
|
|
4557
4557
|
}
|
|
4558
4558
|
|
|
4559
4559
|
// Check if the element itself matches any selector
|
|
4560
|
-
for (const selector of FOCUSABLE_SELECTORS) {
|
|
4560
|
+
for (const selector of FOCUSABLE_SELECTORS$1) {
|
|
4561
4561
|
if (root.matches?.(selector)) {
|
|
4562
4562
|
orderedFocusableElements.push(root);
|
|
4563
4563
|
break; // Once we know it's focusable, no need to check other selectors
|
|
@@ -4611,20 +4611,20 @@ function getFocusableElements(container) {
|
|
|
4611
4611
|
|
|
4612
4612
|
// First extract elements with valid positive tabindex
|
|
4613
4613
|
const elementsWithTabindex = uniqueElements.filter((el) => {
|
|
4614
|
-
const tabindex = getNumericTabIndex(el);
|
|
4614
|
+
const tabindex = getNumericTabIndex$1(el);
|
|
4615
4615
|
return tabindex !== null && tabindex > 0;
|
|
4616
4616
|
});
|
|
4617
4617
|
|
|
4618
4618
|
// Sort these elements by their tabindex value
|
|
4619
4619
|
elementsWithTabindex.sort((a, b) => {
|
|
4620
|
-
const aIndex = getNumericTabIndex(a) ?? 0;
|
|
4621
|
-
const bIndex = getNumericTabIndex(b) ?? 0;
|
|
4620
|
+
const aIndex = getNumericTabIndex$1(a) ?? 0;
|
|
4621
|
+
const bIndex = getNumericTabIndex$1(b) ?? 0;
|
|
4622
4622
|
return aIndex - bIndex;
|
|
4623
4623
|
});
|
|
4624
4624
|
|
|
4625
4625
|
// Elements without tabindex (preserving their original order)
|
|
4626
4626
|
const elementsWithoutTabindex = uniqueElements.filter((el) => {
|
|
4627
|
-
const tabindex = getNumericTabIndex(el);
|
|
4627
|
+
const tabindex = getNumericTabIndex$1(el);
|
|
4628
4628
|
|
|
4629
4629
|
// Elements without tabindex or with tabindex of 0 stay in DOM order
|
|
4630
4630
|
return tabindex === null || tabindex === 0;
|
|
@@ -5419,7 +5419,7 @@ class AuroHelpText extends LitElement {
|
|
|
5419
5419
|
}
|
|
5420
5420
|
}
|
|
5421
5421
|
|
|
5422
|
-
var formkitVersion = '
|
|
5422
|
+
var formkitVersion = '202609042059';
|
|
5423
5423
|
|
|
5424
5424
|
/**
|
|
5425
5425
|
* AuroElement is the shared base class for layout-aware Auro form components.
|
|
@@ -5719,7 +5719,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
5719
5719
|
*/
|
|
5720
5720
|
focus() {
|
|
5721
5721
|
if (this.isPopoverVisible && this.bibContent) {
|
|
5722
|
-
const focusables = getFocusableElements(this.bibContent);
|
|
5722
|
+
const focusables = getFocusableElements$1(this.bibContent);
|
|
5723
5723
|
if (focusables.length > 0) {
|
|
5724
5724
|
focusables[0].focus();
|
|
5725
5725
|
}
|
|
@@ -5751,7 +5751,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
5751
5751
|
// wrappers like <div> containing a <button>). If none found, try
|
|
5752
5752
|
// focusing the element directly (works for custom elements like
|
|
5753
5753
|
// auro-input that have delegatesFocus or a custom focus() method).
|
|
5754
|
-
const descendants = getFocusableElements(el);
|
|
5754
|
+
const descendants = getFocusableElements$1(el);
|
|
5755
5755
|
if (descendants.length > 0) {
|
|
5756
5756
|
descendants[0].focus();
|
|
5757
5757
|
return;
|
|
@@ -5763,7 +5763,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
5763
5763
|
}
|
|
5764
5764
|
}
|
|
5765
5765
|
// Fallback: try DOM children (non-slotted content)
|
|
5766
|
-
const focusables = getFocusableElements(this.trigger);
|
|
5766
|
+
const focusables = getFocusableElements$1(this.trigger);
|
|
5767
5767
|
if (focusables.length > 0) {
|
|
5768
5768
|
focusables[0].focus();
|
|
5769
5769
|
}
|
|
@@ -6442,7 +6442,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
6442
6442
|
}
|
|
6443
6443
|
|
|
6444
6444
|
// Collect focusable elements from the bib content.
|
|
6445
|
-
const focusables = getFocusableElements(this.bibContent);
|
|
6445
|
+
const focusables = getFocusableElements$1(this.bibContent);
|
|
6446
6446
|
|
|
6447
6447
|
// Fallback: try from slotted content directly
|
|
6448
6448
|
if (!focusables.length) {
|
|
@@ -6451,7 +6451,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
6451
6451
|
|
|
6452
6452
|
for (const node of assignedNodes) {
|
|
6453
6453
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
6454
|
-
focusables.push(...getFocusableElements(node));
|
|
6454
|
+
focusables.push(...getFocusableElements$1(node));
|
|
6455
6455
|
}
|
|
6456
6456
|
}
|
|
6457
6457
|
}
|
|
@@ -6511,7 +6511,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
6511
6511
|
|
|
6512
6512
|
// Move initial focus into the bib content, matching FocusTrap behavior
|
|
6513
6513
|
requestAnimationFrame(() => {
|
|
6514
|
-
const focusables = getFocusableElements(this.bibContent);
|
|
6514
|
+
const focusables = getFocusableElements$1(this.bibContent);
|
|
6515
6515
|
if (focusables.length) {
|
|
6516
6516
|
focusables[0].focus();
|
|
6517
6517
|
}
|
|
@@ -6524,7 +6524,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
6524
6524
|
// popover asynchronously — a synchronous focus() would target
|
|
6525
6525
|
// zero-dimension elements and be silently ignored.
|
|
6526
6526
|
requestAnimationFrame(() => {
|
|
6527
|
-
const focusables = getFocusableElements(this.bibContent);
|
|
6527
|
+
const focusables = getFocusableElements$1(this.bibContent);
|
|
6528
6528
|
if (focusables.length) {
|
|
6529
6529
|
focusables[0].focus();
|
|
6530
6530
|
}
|
|
@@ -6742,7 +6742,7 @@ class AuroDropdown extends AuroElement$1 {
|
|
|
6742
6742
|
if (triggerContentNodes) {
|
|
6743
6743
|
|
|
6744
6744
|
// See if any of them are focusable elements
|
|
6745
|
-
this.triggerContentFocusable = triggerContentNodes.some((node) => getFocusableElements(node).length > 0);
|
|
6745
|
+
this.triggerContentFocusable = triggerContentNodes.some((node) => getFocusableElements$1(node).length > 0);
|
|
6746
6746
|
|
|
6747
6747
|
// If any of them are focusable elements
|
|
6748
6748
|
if (this.triggerContentFocusable) {
|
|
@@ -7504,9 +7504,241 @@ class AuroElement extends LitElement {
|
|
|
7504
7504
|
}
|
|
7505
7505
|
}
|
|
7506
7506
|
|
|
7507
|
+
// Selectors for focusable elements
|
|
7508
|
+
const FOCUSABLE_SELECTORS = [
|
|
7509
|
+
"a[href]",
|
|
7510
|
+
"button:not([disabled])",
|
|
7511
|
+
"textarea:not([disabled])",
|
|
7512
|
+
"input:not([disabled])",
|
|
7513
|
+
"select:not([disabled])",
|
|
7514
|
+
'[role="tab"]:not([disabled])',
|
|
7515
|
+
'[role="link"]:not([disabled])',
|
|
7516
|
+
'[role="button"]:not([disabled])',
|
|
7517
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
7518
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
7519
|
+
];
|
|
7520
|
+
|
|
7521
|
+
// List of custom components that are known to be focusable
|
|
7522
|
+
const FOCUSABLE_COMPONENTS = [
|
|
7523
|
+
"auro-checkbox",
|
|
7524
|
+
"auro-radio",
|
|
7525
|
+
"auro-dropdown",
|
|
7526
|
+
"auro-button",
|
|
7527
|
+
"auro-combobox",
|
|
7528
|
+
"auro-input",
|
|
7529
|
+
"auro-counter",
|
|
7530
|
+
// 'auro-menu', // Auro menu is not focusable by default, it uses a different interaction model
|
|
7531
|
+
"auro-select",
|
|
7532
|
+
"auro-datepicker",
|
|
7533
|
+
"auro-hyperlink",
|
|
7534
|
+
"auro-accordion",
|
|
7535
|
+
];
|
|
7536
|
+
|
|
7537
|
+
/**
|
|
7538
|
+
* Determines if a given element is a custom focusable component.
|
|
7539
|
+
* Returns true if the element matches a known focusable component and is not disabled.
|
|
7540
|
+
*
|
|
7541
|
+
* @param {HTMLElement} element The element to check for focusability.
|
|
7542
|
+
* @returns {boolean} True if the element is a focusable custom component, false otherwise.
|
|
7543
|
+
*/
|
|
7544
|
+
function isFocusableComponent(element) {
|
|
7545
|
+
const componentName = element.tagName.toLowerCase();
|
|
7546
|
+
|
|
7547
|
+
// Guard Clause: Element is a focusable component
|
|
7548
|
+
if (
|
|
7549
|
+
!FOCUSABLE_COMPONENTS.some(
|
|
7550
|
+
(name) => element.hasAttribute(name) || componentName === name,
|
|
7551
|
+
)
|
|
7552
|
+
)
|
|
7553
|
+
return false;
|
|
7554
|
+
|
|
7555
|
+
// Guard Clause: Element is not disabled
|
|
7556
|
+
if (element.hasAttribute("disabled")) return false;
|
|
7557
|
+
|
|
7558
|
+
// Guard Clause: The element is a hyperlink and has no href attribute
|
|
7559
|
+
if (componentName.match("hyperlink") && !element.hasAttribute("href"))
|
|
7560
|
+
return false;
|
|
7561
|
+
|
|
7562
|
+
// If all guard clauses pass, the element is a focusable component
|
|
7563
|
+
return true;
|
|
7564
|
+
}
|
|
7565
|
+
|
|
7566
|
+
/**
|
|
7567
|
+
* Safely get a numeric tabindex for an element.
|
|
7568
|
+
* Returns a number if the tabindex is a valid integer, otherwise null.
|
|
7569
|
+
*
|
|
7570
|
+
* @param {HTMLElement} element The element whose tabindex to read.
|
|
7571
|
+
* @returns {?number} The numeric tabindex or null if missing/invalid.
|
|
7572
|
+
*/
|
|
7573
|
+
function getNumericTabIndex(element) {
|
|
7574
|
+
const raw = element.getAttribute("tabindex");
|
|
7575
|
+
if (raw == null) return null;
|
|
7576
|
+
|
|
7577
|
+
const value = Number.parseInt(raw, 10);
|
|
7578
|
+
return Number.isNaN(value) ? null : value;
|
|
7579
|
+
}
|
|
7580
|
+
|
|
7581
|
+
/**
|
|
7582
|
+
* Retrieves all focusable elements within the container in DOM order, including those in shadow DOM and slots.
|
|
7583
|
+
* Returns a unique, ordered array of elements that can receive focus.
|
|
7584
|
+
* Also sorts elements with tabindex first, preserving their order.
|
|
7585
|
+
*
|
|
7586
|
+
* @param {HTMLElement} container The container to search within
|
|
7587
|
+
* @returns {Array<HTMLElement>} An array of focusable elements within the container.
|
|
7588
|
+
*/
|
|
7589
|
+
function getFocusableElements(container) {
|
|
7590
|
+
// Get elements in DOM order by walking the tree
|
|
7591
|
+
const orderedFocusableElements = [];
|
|
7592
|
+
|
|
7593
|
+
// Define a recursive function to collect focusable elements in DOM order
|
|
7594
|
+
const collectFocusableElements = (root) => {
|
|
7595
|
+
// Check if current element is focusable
|
|
7596
|
+
if (root.nodeType === Node.ELEMENT_NODE) {
|
|
7597
|
+
// Check if this is a custom component that is focusable
|
|
7598
|
+
const isComponentFocusable = isFocusableComponent(root);
|
|
7599
|
+
|
|
7600
|
+
if (isComponentFocusable) {
|
|
7601
|
+
// Add the component itself as a focusable element and don't traverse its shadow DOM
|
|
7602
|
+
orderedFocusableElements.push(root);
|
|
7603
|
+
return; // Skip traversing inside this component
|
|
7604
|
+
}
|
|
7605
|
+
|
|
7606
|
+
// Check if the element itself matches any selector
|
|
7607
|
+
for (const selector of FOCUSABLE_SELECTORS) {
|
|
7608
|
+
if (root.matches?.(selector)) {
|
|
7609
|
+
orderedFocusableElements.push(root);
|
|
7610
|
+
break; // Once we know it's focusable, no need to check other selectors
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
|
|
7614
|
+
// Process shadow DOM only for non-Auro components
|
|
7615
|
+
if (root.shadowRoot) {
|
|
7616
|
+
// Process shadow DOM children in order
|
|
7617
|
+
if (root.shadowRoot.children) {
|
|
7618
|
+
Array.from(root.shadowRoot.children).forEach((child) => {
|
|
7619
|
+
collectFocusableElements(child);
|
|
7620
|
+
});
|
|
7621
|
+
}
|
|
7622
|
+
}
|
|
7623
|
+
|
|
7624
|
+
// Process slots and their assigned nodes in order
|
|
7625
|
+
if (root.tagName === "SLOT") {
|
|
7626
|
+
const assignedNodes = root.assignedNodes({ flatten: true });
|
|
7627
|
+
for (const node of assignedNodes) {
|
|
7628
|
+
collectFocusableElements(node);
|
|
7629
|
+
}
|
|
7630
|
+
} else {
|
|
7631
|
+
// Process light DOM children in order
|
|
7632
|
+
if (root.children) {
|
|
7633
|
+
Array.from(root.children).forEach((child) => {
|
|
7634
|
+
collectFocusableElements(child);
|
|
7635
|
+
});
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
};
|
|
7640
|
+
|
|
7641
|
+
// Start the traversal from the container
|
|
7642
|
+
collectFocusableElements(container);
|
|
7643
|
+
|
|
7644
|
+
// Remove duplicates that might have been collected through different paths
|
|
7645
|
+
// while preserving order
|
|
7646
|
+
const uniqueElements = [];
|
|
7647
|
+
const seen = new Set();
|
|
7648
|
+
|
|
7649
|
+
for (const element of orderedFocusableElements) {
|
|
7650
|
+
if (!seen.has(element)) {
|
|
7651
|
+
seen.add(element);
|
|
7652
|
+
uniqueElements.push(element);
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
// Move tab-indexed elements to the front while preserving their order
|
|
7657
|
+
// This ensures that elements with tabindex are prioritized in the focus order
|
|
7658
|
+
|
|
7659
|
+
// First extract elements with valid positive tabindex
|
|
7660
|
+
const elementsWithTabindex = uniqueElements.filter((el) => {
|
|
7661
|
+
const tabindex = getNumericTabIndex(el);
|
|
7662
|
+
return tabindex !== null && tabindex > 0;
|
|
7663
|
+
});
|
|
7664
|
+
|
|
7665
|
+
// Sort these elements by their tabindex value
|
|
7666
|
+
elementsWithTabindex.sort((a, b) => {
|
|
7667
|
+
const aIndex = getNumericTabIndex(a) ?? 0;
|
|
7668
|
+
const bIndex = getNumericTabIndex(b) ?? 0;
|
|
7669
|
+
return aIndex - bIndex;
|
|
7670
|
+
});
|
|
7671
|
+
|
|
7672
|
+
// Elements without tabindex (preserving their original order)
|
|
7673
|
+
const elementsWithoutTabindex = uniqueElements.filter((el) => {
|
|
7674
|
+
const tabindex = getNumericTabIndex(el);
|
|
7675
|
+
|
|
7676
|
+
// Elements without tabindex or with tabindex of 0 stay in DOM order
|
|
7677
|
+
return tabindex === null || tabindex === 0;
|
|
7678
|
+
});
|
|
7679
|
+
|
|
7680
|
+
// Combine both arrays with tabindex elements first
|
|
7681
|
+
const tabIndexedUniqueElements = [
|
|
7682
|
+
...elementsWithTabindex,
|
|
7683
|
+
...elementsWithoutTabindex,
|
|
7684
|
+
];
|
|
7685
|
+
|
|
7686
|
+
return tabIndexedUniqueElements;
|
|
7687
|
+
}
|
|
7688
|
+
|
|
7507
7689
|
// Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
|
|
7508
7690
|
// See LICENSE in the project root for license information.
|
|
7509
7691
|
|
|
7692
|
+
|
|
7693
|
+
/**
|
|
7694
|
+
* Whether `el` is currently visible enough to receive focus. Prefers the
|
|
7695
|
+
* browser's `checkVisibility()`; otherwise combines a display/visibility check
|
|
7696
|
+
* with an ancestor-hidden probe that keeps position:fixed elements visible.
|
|
7697
|
+
* @param {Element} el - The element to test.
|
|
7698
|
+
* @returns {boolean}
|
|
7699
|
+
*/
|
|
7700
|
+
function isVisibleForFocus(el) {
|
|
7701
|
+
if (typeof el.checkVisibility === 'function') {
|
|
7702
|
+
return el.checkVisibility();
|
|
7703
|
+
}
|
|
7704
|
+
const style = el.ownerDocument.defaultView.getComputedStyle(el);
|
|
7705
|
+
if (style.display === 'none' || style.visibility === 'hidden') {
|
|
7706
|
+
return false;
|
|
7707
|
+
}
|
|
7708
|
+
return el.offsetParent !== null || style.position === 'fixed';
|
|
7709
|
+
}
|
|
7710
|
+
|
|
7711
|
+
/**
|
|
7712
|
+
* Return the tab stop that comes before the counter-group in page tab order,
|
|
7713
|
+
* skipping any hidden entries the walker doesn't filter itself. The group host
|
|
7714
|
+
* itself is not a tab stop; its focusables (the internal dropdown and the
|
|
7715
|
+
* counters) are, so anchor the search on the group's earliest focusable.
|
|
7716
|
+
* @param {Element} component - The auro-counter-group host element.
|
|
7717
|
+
* @returns {Element|null}
|
|
7718
|
+
*/
|
|
7719
|
+
function getPreviousTabStop(component) {
|
|
7720
|
+
const tabStops = getFocusableElements(component.ownerDocument.body);
|
|
7721
|
+
const counters = component.counters || [];
|
|
7722
|
+
const owned = [
|
|
7723
|
+
component,
|
|
7724
|
+
component.dropdown,
|
|
7725
|
+
...counters
|
|
7726
|
+
].filter(Boolean);
|
|
7727
|
+
let groupStart = tabStops.length;
|
|
7728
|
+
owned.forEach((node) => {
|
|
7729
|
+
const index = tabStops.indexOf(node);
|
|
7730
|
+
if (index >= 0 && index < groupStart) {
|
|
7731
|
+
groupStart = index;
|
|
7732
|
+
}
|
|
7733
|
+
});
|
|
7734
|
+
for (let index = groupStart - 1; index >= 0; index -= 1) {
|
|
7735
|
+
if (isVisibleForFocus(tabStops[index])) {
|
|
7736
|
+
return tabStops[index];
|
|
7737
|
+
}
|
|
7738
|
+
}
|
|
7739
|
+
return null;
|
|
7740
|
+
}
|
|
7741
|
+
|
|
7510
7742
|
const counterGroupKeyboardStrategy = {
|
|
7511
7743
|
Escape(component, evt) {
|
|
7512
7744
|
if (!component.dropdown || !component.dropdown.isPopoverVisible) {
|
|
@@ -7520,6 +7752,48 @@ const counterGroupKeyboardStrategy = {
|
|
|
7520
7752
|
|
|
7521
7753
|
component.dropdown.hide();
|
|
7522
7754
|
},
|
|
7755
|
+
|
|
7756
|
+
Tab(component, evt, ctx) {
|
|
7757
|
+
// Forward Tab already exits past the bib content and the focusout handler
|
|
7758
|
+
// closes the bib. Fullscreen relies on the dialog's native focus trapping.
|
|
7759
|
+
if (!ctx.isExpanded || ctx.isModal || !evt.shiftKey) {
|
|
7760
|
+
return;
|
|
7761
|
+
}
|
|
7762
|
+
|
|
7763
|
+
// The trigger precedes the bib content in the flattened tab order, so a
|
|
7764
|
+
// native Shift+Tab off the first counter lands back on the trigger and
|
|
7765
|
+
// leaves the bib open. Only intercept when focus is on the first focusable
|
|
7766
|
+
// in the bib; otherwise let the browser move to the previous counter.
|
|
7767
|
+
// `composedPath()` reports the real event origin through every shadow
|
|
7768
|
+
// boundary, unlike `document.activeElement`, which retargets to the first
|
|
7769
|
+
// slotted counter and cannot tell the counters apart.
|
|
7770
|
+
const focusables = getFocusableElements(component.dropdown.bibContent);
|
|
7771
|
+
if (!focusables.length) {
|
|
7772
|
+
return;
|
|
7773
|
+
}
|
|
7774
|
+
|
|
7775
|
+
const path = evt.composedPath();
|
|
7776
|
+
const activeIndex = focusables.findIndex((el) => path.includes(el));
|
|
7777
|
+
if (activeIndex !== 0) {
|
|
7778
|
+
return;
|
|
7779
|
+
}
|
|
7780
|
+
|
|
7781
|
+
const previousTabStop = getPreviousTabStop(component);
|
|
7782
|
+
|
|
7783
|
+
// Always suppress the native Shift+Tab. Otherwise it fires against the bib
|
|
7784
|
+
// as it is torn down and lands focus on the trigger (which precedes the
|
|
7785
|
+
// first counter), re-opening the exact loop this handler prevents. When
|
|
7786
|
+
// nothing precedes the group, focus falls to the body instead of the
|
|
7787
|
+
// trigger, which reads as exiting the group backward.
|
|
7788
|
+
evt.preventDefault();
|
|
7789
|
+
component.hideBib();
|
|
7790
|
+
|
|
7791
|
+
if (!previousTabStop) {
|
|
7792
|
+
return;
|
|
7793
|
+
}
|
|
7794
|
+
|
|
7795
|
+
doubleRaf(() => previousTabStop.focus());
|
|
7796
|
+
},
|
|
7523
7797
|
};
|
|
7524
7798
|
|
|
7525
7799
|
/* eslint-disable lit/no-invalid-html, lit/binding-positions, max-lines, no-underscore-dangle, arrow-parens, no-confusing-arrow, curly, dot-location, no-inline-comments, line-comment-position,
|