@jsenv/dom 0.12.1 → 0.12.2
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/dist/jsenv_dom.js +34 -4
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -5447,10 +5447,36 @@ const performTabNavigation = (
|
|
|
5447
5447
|
return elementIsFocusable(element, { excludeAriaHidden });
|
|
5448
5448
|
};
|
|
5449
5449
|
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5450
|
+
// A focus group "owns" the activeElement when activeElement is inside it.
|
|
5451
|
+
// From the inside, Tab should exit the group (skip its remaining children).
|
|
5452
|
+
// From the outside, Tab should enter the group normally (first focusable child).
|
|
5453
|
+
const activeFocusGroup =
|
|
5454
|
+
activeElement.closest?.("[navi-focus-group]") || null;
|
|
5455
|
+
const isOwnedByActiveFocusGroup = (el) =>
|
|
5456
|
+
activeFocusGroup && activeFocusGroup.contains(el);
|
|
5457
|
+
|
|
5458
|
+
const predicate = (candidate, skip) => {
|
|
5459
|
+
if (!isFocusableByTab(candidate)) {
|
|
5460
|
+
return false;
|
|
5461
|
+
}
|
|
5462
|
+
// Focus group roots are composite widgets.
|
|
5463
|
+
if (candidate.hasAttribute("navi-focus-group")) {
|
|
5464
|
+
if (isFocusableByTab(candidate)) {
|
|
5465
|
+
// Root has tabindex="0": it is the single Tab stop for the group.
|
|
5466
|
+
// Skip its children — arrow keys handle internal navigation.
|
|
5467
|
+
skip?.();
|
|
5468
|
+
return true;
|
|
5469
|
+
}
|
|
5470
|
+
// Root is not focusable by Tab: descend into children to allow Tab entry.
|
|
5471
|
+
return false;
|
|
5472
|
+
}
|
|
5473
|
+
// If candidate is inside the focus group that currently owns focus, skip
|
|
5474
|
+
// it — Tab should exit the group. (Going *into* a different focus group
|
|
5475
|
+
// is allowed: only one focus group at a time has the activeElement.)
|
|
5476
|
+
if (isOwnedByActiveFocusGroup(candidate)) {
|
|
5477
|
+
return false;
|
|
5478
|
+
}
|
|
5479
|
+
return true;
|
|
5454
5480
|
};
|
|
5455
5481
|
|
|
5456
5482
|
const activeElementIsRoot = activeElement === rootElement;
|
|
@@ -5594,6 +5620,10 @@ const initFocusGroup = (
|
|
|
5594
5620
|
name, // Store undefined as-is for implicit grouping
|
|
5595
5621
|
});
|
|
5596
5622
|
cleanupCallbackSet.add(removeFocusGroup);
|
|
5623
|
+
element.setAttribute("navi-focus-group", "");
|
|
5624
|
+
cleanupCallbackSet.add(() => {
|
|
5625
|
+
element.removeAttribute("navi-focus-group");
|
|
5626
|
+
});
|
|
5597
5627
|
|
|
5598
5628
|
tab: {
|
|
5599
5629
|
if (!skipTab) {
|