@primer/behaviors 0.0.0-20251211200451 → 0.0.0-20251214172224
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/cjs/anchored-position.js +17 -17
- package/dist/cjs/focus-trap.js +8 -6
- package/dist/cjs/focus-zone.js +47 -19
- package/dist/cjs/utils/iterate-focusable-elements.js +20 -13
- package/dist/esm/anchored-position.mjs +17 -17
- package/dist/esm/focus-trap.mjs +8 -6
- package/dist/esm/focus-zone.mjs +47 -19
- package/dist/esm/utils/iterate-focusable-elements.mjs +20 -13
- package/package.json +1 -1
|
@@ -27,9 +27,11 @@ function getPositionedParent(element) {
|
|
|
27
27
|
if (isOnTopLayer(element))
|
|
28
28
|
return document.body;
|
|
29
29
|
let parentNode = element.parentNode;
|
|
30
|
-
while (parentNode !== null) {
|
|
31
|
-
if (parentNode instanceof HTMLElement
|
|
32
|
-
|
|
30
|
+
while (parentNode !== null && parentNode !== document.body) {
|
|
31
|
+
if (parentNode instanceof HTMLElement) {
|
|
32
|
+
if (getComputedStyle(parentNode).position !== 'static') {
|
|
33
|
+
return parentNode;
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
parentNode = parentNode.parentNode;
|
|
35
37
|
}
|
|
@@ -51,26 +53,24 @@ function isOnTopLayer(element) {
|
|
|
51
53
|
return false;
|
|
52
54
|
}
|
|
53
55
|
function getClippingRect(element) {
|
|
56
|
+
let clippingNode = document.body;
|
|
54
57
|
let parentNode = element;
|
|
55
|
-
while (parentNode !== null) {
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
while (parentNode !== null && parentNode !== document.body) {
|
|
59
|
+
if (parentNode instanceof HTMLElement) {
|
|
60
|
+
const overflow = getComputedStyle(parentNode).overflow;
|
|
61
|
+
if (overflow !== 'visible') {
|
|
62
|
+
clippingNode = parentNode;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
62
65
|
}
|
|
63
66
|
parentNode = parentNode.parentNode;
|
|
64
67
|
}
|
|
65
|
-
const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
|
|
66
68
|
const elemRect = clippingNode.getBoundingClientRect();
|
|
67
69
|
const elemStyle = getComputedStyle(clippingNode);
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
elemStyle.borderBottomWidth,
|
|
73
|
-
].map(v => parseInt(v, 10) || 0);
|
|
70
|
+
const borderTop = parseInt(elemStyle.borderTopWidth, 10) || 0;
|
|
71
|
+
const borderLeft = parseInt(elemStyle.borderLeftWidth, 10) || 0;
|
|
72
|
+
const borderRight = parseInt(elemStyle.borderRightWidth, 10) || 0;
|
|
73
|
+
const borderBottom = parseInt(elemStyle.borderBottomWidth, 10) || 0;
|
|
74
74
|
return {
|
|
75
75
|
top: elemRect.top + borderTop,
|
|
76
76
|
left: elemRect.left + borderLeft,
|
package/dist/cjs/focus-trap.js
CHANGED
|
@@ -47,23 +47,25 @@ function focusTrap(container, initialFocus, abortSignal) {
|
|
|
47
47
|
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
48
48
|
container.setAttribute('data-focus-trap', 'active');
|
|
49
49
|
const sentinelStart = document.createElement('span');
|
|
50
|
-
sentinelStart.
|
|
51
|
-
sentinelStart.
|
|
50
|
+
sentinelStart.className = 'sentinel';
|
|
51
|
+
sentinelStart.tabIndex = 0;
|
|
52
52
|
sentinelStart.setAttribute('aria-hidden', 'true');
|
|
53
|
+
sentinelStart.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
|
|
53
54
|
sentinelStart.onfocus = () => {
|
|
54
55
|
const lastFocusableChild = iterateFocusableElements.getFocusableChild(container, true);
|
|
55
56
|
lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
|
|
56
57
|
};
|
|
57
58
|
const sentinelEnd = document.createElement('span');
|
|
58
|
-
sentinelEnd.
|
|
59
|
-
sentinelEnd.
|
|
59
|
+
sentinelEnd.className = 'sentinel';
|
|
60
|
+
sentinelEnd.tabIndex = 0;
|
|
60
61
|
sentinelEnd.setAttribute('aria-hidden', 'true');
|
|
62
|
+
sentinelEnd.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
|
|
61
63
|
sentinelEnd.onfocus = () => {
|
|
62
64
|
const firstFocusableChild = iterateFocusableElements.getFocusableChild(container);
|
|
63
65
|
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
64
66
|
};
|
|
65
|
-
const
|
|
66
|
-
if (!
|
|
67
|
+
const hasExistingSentinels = container.querySelector(':scope > span.sentinel') !== null;
|
|
68
|
+
if (!hasExistingSentinels) {
|
|
67
69
|
container.prepend(sentinelStart);
|
|
68
70
|
container.append(sentinelEnd);
|
|
69
71
|
}
|
package/dist/cjs/focus-zone.js
CHANGED
|
@@ -64,12 +64,19 @@ const KEY_TO_DIRECTION = {
|
|
|
64
64
|
PageDown: 'end',
|
|
65
65
|
Backspace: 'previous',
|
|
66
66
|
};
|
|
67
|
+
let cachedIsMac;
|
|
68
|
+
function getIsMac() {
|
|
69
|
+
if (cachedIsMac === undefined) {
|
|
70
|
+
cachedIsMac = userAgent.isMacOS();
|
|
71
|
+
}
|
|
72
|
+
return cachedIsMac;
|
|
73
|
+
}
|
|
67
74
|
function getDirection(keyboardEvent) {
|
|
68
75
|
const direction = KEY_TO_DIRECTION[keyboardEvent.key];
|
|
69
76
|
if (keyboardEvent.key === 'Tab' && keyboardEvent.shiftKey) {
|
|
70
77
|
return 'previous';
|
|
71
78
|
}
|
|
72
|
-
const isMac =
|
|
79
|
+
const isMac = getIsMac();
|
|
73
80
|
if ((isMac && keyboardEvent.metaKey) || (!isMac && keyboardEvent.ctrlKey)) {
|
|
74
81
|
if (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowUp') {
|
|
75
82
|
return 'start';
|
|
@@ -187,8 +194,9 @@ function focusZone(container, settings) {
|
|
|
187
194
|
activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
|
|
188
195
|
container.removeAttribute(hasActiveDescendantAttribute);
|
|
189
196
|
previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
|
|
190
|
-
|
|
191
|
-
|
|
197
|
+
const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
|
|
198
|
+
for (let i = 0; i < items.length; i++) {
|
|
199
|
+
items[i].removeAttribute(isActiveDescendantAttribute);
|
|
192
200
|
}
|
|
193
201
|
activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
|
|
194
202
|
}
|
|
@@ -261,30 +269,50 @@ function focusZone(container, settings) {
|
|
|
261
269
|
if (!preventInitialFocus)
|
|
262
270
|
updateFocusedElement(initialElement);
|
|
263
271
|
const observer = new MutationObserver(mutations => {
|
|
272
|
+
const elementsToRemove = [];
|
|
273
|
+
const elementsToAdd = [];
|
|
274
|
+
const attributeRemovals = [];
|
|
275
|
+
const attributeAdditions = [];
|
|
264
276
|
for (const mutation of mutations) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
if (mutation.type === 'childList') {
|
|
278
|
+
for (const removedNode of mutation.removedNodes) {
|
|
279
|
+
if (removedNode instanceof HTMLElement) {
|
|
280
|
+
elementsToRemove.push(removedNode);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const addedNode of mutation.addedNodes) {
|
|
284
|
+
if (addedNode instanceof HTMLElement) {
|
|
285
|
+
elementsToAdd.push(addedNode);
|
|
286
|
+
}
|
|
268
287
|
}
|
|
269
288
|
}
|
|
270
|
-
if (mutation.type === 'attributes' && mutation.
|
|
271
|
-
if (mutation.
|
|
272
|
-
|
|
289
|
+
else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
|
|
290
|
+
if (mutation.oldValue === null) {
|
|
291
|
+
attributeRemovals.push(mutation.target);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
attributeAdditions.push(mutation.target);
|
|
273
295
|
}
|
|
274
296
|
}
|
|
275
297
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
298
|
+
if (elementsToRemove.length > 0) {
|
|
299
|
+
const toRemove = elementsToRemove.flatMap(node => [...iterateFocusableElements.iterateFocusableElements(node)]);
|
|
300
|
+
if (toRemove.length > 0) {
|
|
301
|
+
endFocusManagement(...toRemove);
|
|
281
302
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
303
|
+
}
|
|
304
|
+
if (attributeRemovals.length > 0) {
|
|
305
|
+
endFocusManagement(...attributeRemovals);
|
|
306
|
+
}
|
|
307
|
+
if (elementsToAdd.length > 0) {
|
|
308
|
+
const toAdd = elementsToAdd.flatMap(node => [...iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions)]);
|
|
309
|
+
if (toAdd.length > 0) {
|
|
310
|
+
beginFocusManagement(...toAdd);
|
|
286
311
|
}
|
|
287
312
|
}
|
|
313
|
+
if (attributeAdditions.length > 0) {
|
|
314
|
+
beginFocusManagement(...attributeAdditions);
|
|
315
|
+
}
|
|
288
316
|
});
|
|
289
317
|
observer.observe(container, {
|
|
290
318
|
subtree: true,
|
|
@@ -319,7 +347,7 @@ function focusZone(container, settings) {
|
|
|
319
347
|
if (focusableElement) {
|
|
320
348
|
updateFocusedElement(focusableElement);
|
|
321
349
|
}
|
|
322
|
-
}, { signal, capture: true });
|
|
350
|
+
}, { signal, capture: true, passive: true });
|
|
323
351
|
}
|
|
324
352
|
activeDescendantControl.addEventListener('focusin', () => {
|
|
325
353
|
if (!currentFocusedElement) {
|
|
@@ -33,24 +33,31 @@ function* iterateFocusableElements(container, options = {}) {
|
|
|
33
33
|
function getFocusableChild(container, lastChild = false) {
|
|
34
34
|
return iterateFocusableElements(container, { reverse: lastChild, strict: true, onlyTabbable: true }).next().value;
|
|
35
35
|
}
|
|
36
|
+
const DISABLEABLE_TAGS = new Set(['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET']);
|
|
36
37
|
function isFocusable(elem, strict = false) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
if (elem.hidden)
|
|
39
|
+
return false;
|
|
40
|
+
if (elem.classList.contains('sentinel'))
|
|
41
|
+
return false;
|
|
42
|
+
if (elem instanceof HTMLInputElement && elem.type === 'hidden')
|
|
43
|
+
return false;
|
|
44
|
+
if (DISABLEABLE_TAGS.has(elem.tagName) && elem.disabled)
|
|
43
45
|
return false;
|
|
44
|
-
}
|
|
45
46
|
if (strict) {
|
|
47
|
+
const offsetWidth = elem.offsetWidth;
|
|
48
|
+
const offsetHeight = elem.offsetHeight;
|
|
49
|
+
const offsetParent = elem.offsetParent;
|
|
50
|
+
if (offsetWidth === 0 || offsetHeight === 0)
|
|
51
|
+
return false;
|
|
52
|
+
if (!offsetParent)
|
|
53
|
+
return false;
|
|
46
54
|
const style = getComputedStyle(elem);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (
|
|
55
|
+
if (style.display === 'none')
|
|
56
|
+
return false;
|
|
57
|
+
if (style.visibility === 'hidden' || style.visibility === 'collapse')
|
|
58
|
+
return false;
|
|
59
|
+
if (elem.getClientRects().length === 0)
|
|
52
60
|
return false;
|
|
53
|
-
}
|
|
54
61
|
}
|
|
55
62
|
if (elem.getAttribute('tabindex') != null) {
|
|
56
63
|
return true;
|
|
@@ -25,9 +25,11 @@ function getPositionedParent(element) {
|
|
|
25
25
|
if (isOnTopLayer(element))
|
|
26
26
|
return document.body;
|
|
27
27
|
let parentNode = element.parentNode;
|
|
28
|
-
while (parentNode !== null) {
|
|
29
|
-
if (parentNode instanceof HTMLElement
|
|
30
|
-
|
|
28
|
+
while (parentNode !== null && parentNode !== document.body) {
|
|
29
|
+
if (parentNode instanceof HTMLElement) {
|
|
30
|
+
if (getComputedStyle(parentNode).position !== 'static') {
|
|
31
|
+
return parentNode;
|
|
32
|
+
}
|
|
31
33
|
}
|
|
32
34
|
parentNode = parentNode.parentNode;
|
|
33
35
|
}
|
|
@@ -49,26 +51,24 @@ function isOnTopLayer(element) {
|
|
|
49
51
|
return false;
|
|
50
52
|
}
|
|
51
53
|
function getClippingRect(element) {
|
|
54
|
+
let clippingNode = document.body;
|
|
52
55
|
let parentNode = element;
|
|
53
|
-
while (parentNode !== null) {
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
while (parentNode !== null && parentNode !== document.body) {
|
|
57
|
+
if (parentNode instanceof HTMLElement) {
|
|
58
|
+
const overflow = getComputedStyle(parentNode).overflow;
|
|
59
|
+
if (overflow !== 'visible') {
|
|
60
|
+
clippingNode = parentNode;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
60
63
|
}
|
|
61
64
|
parentNode = parentNode.parentNode;
|
|
62
65
|
}
|
|
63
|
-
const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
|
|
64
66
|
const elemRect = clippingNode.getBoundingClientRect();
|
|
65
67
|
const elemStyle = getComputedStyle(clippingNode);
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
elemStyle.borderBottomWidth,
|
|
71
|
-
].map(v => parseInt(v, 10) || 0);
|
|
68
|
+
const borderTop = parseInt(elemStyle.borderTopWidth, 10) || 0;
|
|
69
|
+
const borderLeft = parseInt(elemStyle.borderLeftWidth, 10) || 0;
|
|
70
|
+
const borderRight = parseInt(elemStyle.borderRightWidth, 10) || 0;
|
|
71
|
+
const borderBottom = parseInt(elemStyle.borderBottomWidth, 10) || 0;
|
|
72
72
|
return {
|
|
73
73
|
top: elemRect.top + borderTop,
|
|
74
74
|
left: elemRect.left + borderLeft,
|
package/dist/esm/focus-trap.mjs
CHANGED
|
@@ -45,23 +45,25 @@ function focusTrap(container, initialFocus, abortSignal) {
|
|
|
45
45
|
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
46
46
|
container.setAttribute('data-focus-trap', 'active');
|
|
47
47
|
const sentinelStart = document.createElement('span');
|
|
48
|
-
sentinelStart.
|
|
49
|
-
sentinelStart.
|
|
48
|
+
sentinelStart.className = 'sentinel';
|
|
49
|
+
sentinelStart.tabIndex = 0;
|
|
50
50
|
sentinelStart.setAttribute('aria-hidden', 'true');
|
|
51
|
+
sentinelStart.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
|
|
51
52
|
sentinelStart.onfocus = () => {
|
|
52
53
|
const lastFocusableChild = getFocusableChild(container, true);
|
|
53
54
|
lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
|
|
54
55
|
};
|
|
55
56
|
const sentinelEnd = document.createElement('span');
|
|
56
|
-
sentinelEnd.
|
|
57
|
-
sentinelEnd.
|
|
57
|
+
sentinelEnd.className = 'sentinel';
|
|
58
|
+
sentinelEnd.tabIndex = 0;
|
|
58
59
|
sentinelEnd.setAttribute('aria-hidden', 'true');
|
|
60
|
+
sentinelEnd.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
|
|
59
61
|
sentinelEnd.onfocus = () => {
|
|
60
62
|
const firstFocusableChild = getFocusableChild(container);
|
|
61
63
|
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
62
64
|
};
|
|
63
|
-
const
|
|
64
|
-
if (!
|
|
65
|
+
const hasExistingSentinels = container.querySelector(':scope > span.sentinel') !== null;
|
|
66
|
+
if (!hasExistingSentinels) {
|
|
65
67
|
container.prepend(sentinelStart);
|
|
66
68
|
container.append(sentinelEnd);
|
|
67
69
|
}
|
package/dist/esm/focus-zone.mjs
CHANGED
|
@@ -62,12 +62,19 @@ const KEY_TO_DIRECTION = {
|
|
|
62
62
|
PageDown: 'end',
|
|
63
63
|
Backspace: 'previous',
|
|
64
64
|
};
|
|
65
|
+
let cachedIsMac;
|
|
66
|
+
function getIsMac() {
|
|
67
|
+
if (cachedIsMac === undefined) {
|
|
68
|
+
cachedIsMac = isMacOS();
|
|
69
|
+
}
|
|
70
|
+
return cachedIsMac;
|
|
71
|
+
}
|
|
65
72
|
function getDirection(keyboardEvent) {
|
|
66
73
|
const direction = KEY_TO_DIRECTION[keyboardEvent.key];
|
|
67
74
|
if (keyboardEvent.key === 'Tab' && keyboardEvent.shiftKey) {
|
|
68
75
|
return 'previous';
|
|
69
76
|
}
|
|
70
|
-
const isMac =
|
|
77
|
+
const isMac = getIsMac();
|
|
71
78
|
if ((isMac && keyboardEvent.metaKey) || (!isMac && keyboardEvent.ctrlKey)) {
|
|
72
79
|
if (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowUp') {
|
|
73
80
|
return 'start';
|
|
@@ -185,8 +192,9 @@ function focusZone(container, settings) {
|
|
|
185
192
|
activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
|
|
186
193
|
container.removeAttribute(hasActiveDescendantAttribute);
|
|
187
194
|
previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
|
|
196
|
+
for (let i = 0; i < items.length; i++) {
|
|
197
|
+
items[i].removeAttribute(isActiveDescendantAttribute);
|
|
190
198
|
}
|
|
191
199
|
activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
|
|
192
200
|
}
|
|
@@ -259,30 +267,50 @@ function focusZone(container, settings) {
|
|
|
259
267
|
if (!preventInitialFocus)
|
|
260
268
|
updateFocusedElement(initialElement);
|
|
261
269
|
const observer = new MutationObserver(mutations => {
|
|
270
|
+
const elementsToRemove = [];
|
|
271
|
+
const elementsToAdd = [];
|
|
272
|
+
const attributeRemovals = [];
|
|
273
|
+
const attributeAdditions = [];
|
|
262
274
|
for (const mutation of mutations) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
275
|
+
if (mutation.type === 'childList') {
|
|
276
|
+
for (const removedNode of mutation.removedNodes) {
|
|
277
|
+
if (removedNode instanceof HTMLElement) {
|
|
278
|
+
elementsToRemove.push(removedNode);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
for (const addedNode of mutation.addedNodes) {
|
|
282
|
+
if (addedNode instanceof HTMLElement) {
|
|
283
|
+
elementsToAdd.push(addedNode);
|
|
284
|
+
}
|
|
266
285
|
}
|
|
267
286
|
}
|
|
268
|
-
if (mutation.type === 'attributes' && mutation.
|
|
269
|
-
if (mutation.
|
|
270
|
-
|
|
287
|
+
else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
|
|
288
|
+
if (mutation.oldValue === null) {
|
|
289
|
+
attributeRemovals.push(mutation.target);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
attributeAdditions.push(mutation.target);
|
|
271
293
|
}
|
|
272
294
|
}
|
|
273
295
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
296
|
+
if (elementsToRemove.length > 0) {
|
|
297
|
+
const toRemove = elementsToRemove.flatMap(node => [...iterateFocusableElements(node)]);
|
|
298
|
+
if (toRemove.length > 0) {
|
|
299
|
+
endFocusManagement(...toRemove);
|
|
279
300
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
301
|
+
}
|
|
302
|
+
if (attributeRemovals.length > 0) {
|
|
303
|
+
endFocusManagement(...attributeRemovals);
|
|
304
|
+
}
|
|
305
|
+
if (elementsToAdd.length > 0) {
|
|
306
|
+
const toAdd = elementsToAdd.flatMap(node => [...iterateFocusableElements(node, iterateFocusableElementsOptions)]);
|
|
307
|
+
if (toAdd.length > 0) {
|
|
308
|
+
beginFocusManagement(...toAdd);
|
|
284
309
|
}
|
|
285
310
|
}
|
|
311
|
+
if (attributeAdditions.length > 0) {
|
|
312
|
+
beginFocusManagement(...attributeAdditions);
|
|
313
|
+
}
|
|
286
314
|
});
|
|
287
315
|
observer.observe(container, {
|
|
288
316
|
subtree: true,
|
|
@@ -317,7 +345,7 @@ function focusZone(container, settings) {
|
|
|
317
345
|
if (focusableElement) {
|
|
318
346
|
updateFocusedElement(focusableElement);
|
|
319
347
|
}
|
|
320
|
-
}, { signal, capture: true });
|
|
348
|
+
}, { signal, capture: true, passive: true });
|
|
321
349
|
}
|
|
322
350
|
activeDescendantControl.addEventListener('focusin', () => {
|
|
323
351
|
if (!currentFocusedElement) {
|
|
@@ -31,24 +31,31 @@ function* iterateFocusableElements(container, options = {}) {
|
|
|
31
31
|
function getFocusableChild(container, lastChild = false) {
|
|
32
32
|
return iterateFocusableElements(container, { reverse: lastChild, strict: true, onlyTabbable: true }).next().value;
|
|
33
33
|
}
|
|
34
|
+
const DISABLEABLE_TAGS = new Set(['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET']);
|
|
34
35
|
function isFocusable(elem, strict = false) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
if (elem.hidden)
|
|
37
|
+
return false;
|
|
38
|
+
if (elem.classList.contains('sentinel'))
|
|
39
|
+
return false;
|
|
40
|
+
if (elem instanceof HTMLInputElement && elem.type === 'hidden')
|
|
41
|
+
return false;
|
|
42
|
+
if (DISABLEABLE_TAGS.has(elem.tagName) && elem.disabled)
|
|
41
43
|
return false;
|
|
42
|
-
}
|
|
43
44
|
if (strict) {
|
|
45
|
+
const offsetWidth = elem.offsetWidth;
|
|
46
|
+
const offsetHeight = elem.offsetHeight;
|
|
47
|
+
const offsetParent = elem.offsetParent;
|
|
48
|
+
if (offsetWidth === 0 || offsetHeight === 0)
|
|
49
|
+
return false;
|
|
50
|
+
if (!offsetParent)
|
|
51
|
+
return false;
|
|
44
52
|
const style = getComputedStyle(elem);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
53
|
+
if (style.display === 'none')
|
|
54
|
+
return false;
|
|
55
|
+
if (style.visibility === 'hidden' || style.visibility === 'collapse')
|
|
56
|
+
return false;
|
|
57
|
+
if (elem.getClientRects().length === 0)
|
|
50
58
|
return false;
|
|
51
|
-
}
|
|
52
59
|
}
|
|
53
60
|
if (elem.getAttribute('tabindex') != null) {
|
|
54
61
|
return true;
|