@primer/behaviors 0.0.0-20251215125247 → 0.0.0-20251215200522
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 +21 -28
- package/dist/cjs/focus-zone.js +42 -82
- package/dist/cjs/utils/iterate-focusable-elements.js +13 -21
- package/dist/esm/anchored-position.mjs +17 -17
- package/dist/esm/focus-trap.mjs +21 -28
- package/dist/esm/focus-zone.mjs +42 -82
- package/dist/esm/utils/iterate-focusable-elements.mjs +13 -21
- package/package.json +1 -1
- package/dist/cjs/utils/indexed-set.d.ts +0 -13
- package/dist/cjs/utils/indexed-set.js +0 -53
- package/dist/esm/utils/indexed-set.d.ts +0 -13
- package/dist/esm/utils/indexed-set.mjs +0 -51
|
@@ -27,11 +27,9 @@ 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
|
-
|
|
33
|
-
return parentNode;
|
|
34
|
-
}
|
|
30
|
+
while (parentNode !== null) {
|
|
31
|
+
if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
|
|
32
|
+
return parentNode;
|
|
35
33
|
}
|
|
36
34
|
parentNode = parentNode.parentNode;
|
|
37
35
|
}
|
|
@@ -53,24 +51,26 @@ function isOnTopLayer(element) {
|
|
|
53
51
|
return false;
|
|
54
52
|
}
|
|
55
53
|
function getClippingRect(element) {
|
|
56
|
-
let clippingNode = document.body;
|
|
57
54
|
let parentNode = element;
|
|
58
|
-
while (parentNode !== null
|
|
59
|
-
if (parentNode instanceof
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
while (parentNode !== null) {
|
|
56
|
+
if (!(parentNode instanceof Element)) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
const parentNodeStyle = getComputedStyle(parentNode);
|
|
60
|
+
if (parentNodeStyle.overflow !== 'visible') {
|
|
61
|
+
break;
|
|
65
62
|
}
|
|
66
63
|
parentNode = parentNode.parentNode;
|
|
67
64
|
}
|
|
65
|
+
const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
|
|
68
66
|
const elemRect = clippingNode.getBoundingClientRect();
|
|
69
67
|
const elemStyle = getComputedStyle(clippingNode);
|
|
70
|
-
const borderTop
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
const [borderTop, borderLeft, borderRight, borderBottom] = [
|
|
69
|
+
elemStyle.borderTopWidth,
|
|
70
|
+
elemStyle.borderLeftWidth,
|
|
71
|
+
elemStyle.borderRightWidth,
|
|
72
|
+
elemStyle.borderBottomWidth,
|
|
73
|
+
].map(v => parseInt(v, 10) || 0);
|
|
74
74
|
return {
|
|
75
75
|
top: elemRect.top + borderTop,
|
|
76
76
|
left: elemRect.left + borderLeft,
|
package/dist/cjs/focus-trap.js
CHANGED
|
@@ -6,16 +6,6 @@ var eventListenerSignal = require('./polyfills/event-listener-signal.js');
|
|
|
6
6
|
eventListenerSignal.polyfill();
|
|
7
7
|
const suspendedTrapStack = [];
|
|
8
8
|
let activeTrap = undefined;
|
|
9
|
-
const SR_ONLY_STYLES = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
|
|
10
|
-
function createSentinel({ onFocus }) {
|
|
11
|
-
const sentinel = document.createElement('span');
|
|
12
|
-
sentinel.setAttribute('class', 'sentinel');
|
|
13
|
-
sentinel.setAttribute('tabindex', '0');
|
|
14
|
-
sentinel.setAttribute('aria-hidden', 'true');
|
|
15
|
-
sentinel.style.cssText = SR_ONLY_STYLES;
|
|
16
|
-
sentinel.onfocus = onFocus;
|
|
17
|
-
return sentinel;
|
|
18
|
-
}
|
|
19
9
|
function tryReactivate() {
|
|
20
10
|
const trapToReactivate = suspendedTrapStack.pop();
|
|
21
11
|
if (trapToReactivate) {
|
|
@@ -33,10 +23,9 @@ function observeFocusTrap(container, sentinels) {
|
|
|
33
23
|
const observer = new MutationObserver(mutations => {
|
|
34
24
|
for (const mutation of mutations) {
|
|
35
25
|
if (mutation.type === 'childList' && mutation.addedNodes.length) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
26
|
+
const sentinelChildren = Array.from(mutation.addedNodes).filter(e => e instanceof HTMLElement && e.classList.contains('sentinel') && e.tagName === 'SPAN');
|
|
27
|
+
if (sentinelChildren.length) {
|
|
28
|
+
return;
|
|
40
29
|
}
|
|
41
30
|
const firstChild = container.firstElementChild;
|
|
42
31
|
const lastChild = container.lastElementChild;
|
|
@@ -57,20 +46,24 @@ function focusTrap(container, initialFocus, abortSignal) {
|
|
|
57
46
|
const controller = new AbortController();
|
|
58
47
|
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
59
48
|
container.setAttribute('data-focus-trap', 'active');
|
|
60
|
-
const sentinelStart =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
49
|
+
const sentinelStart = document.createElement('span');
|
|
50
|
+
sentinelStart.setAttribute('class', 'sentinel');
|
|
51
|
+
sentinelStart.setAttribute('tabindex', '0');
|
|
52
|
+
sentinelStart.setAttribute('aria-hidden', 'true');
|
|
53
|
+
sentinelStart.onfocus = () => {
|
|
54
|
+
const lastFocusableChild = iterateFocusableElements.getFocusableChild(container, true);
|
|
55
|
+
lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
|
|
56
|
+
};
|
|
57
|
+
const sentinelEnd = document.createElement('span');
|
|
58
|
+
sentinelEnd.setAttribute('class', 'sentinel');
|
|
59
|
+
sentinelEnd.setAttribute('tabindex', '0');
|
|
60
|
+
sentinelEnd.setAttribute('aria-hidden', 'true');
|
|
61
|
+
sentinelEnd.onfocus = () => {
|
|
62
|
+
const firstFocusableChild = iterateFocusableElements.getFocusableChild(container);
|
|
63
|
+
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
64
|
+
};
|
|
65
|
+
const existingSentinels = Array.from(container.children).filter(e => e.classList.contains('sentinel') && e.tagName === 'SPAN');
|
|
66
|
+
if (!existingSentinels.length) {
|
|
74
67
|
container.prepend(sentinelStart);
|
|
75
68
|
container.append(sentinelEnd);
|
|
76
69
|
}
|
package/dist/cjs/focus-zone.js
CHANGED
|
@@ -5,7 +5,6 @@ var userAgent = require('./utils/user-agent.js');
|
|
|
5
5
|
var iterateFocusableElements = require('./utils/iterate-focusable-elements.js');
|
|
6
6
|
var uniqueId = require('./utils/unique-id.js');
|
|
7
7
|
var isEditableElement = require('./utils/is-editable-element.js');
|
|
8
|
-
var indexedSet = require('./utils/indexed-set.js');
|
|
9
8
|
|
|
10
9
|
eventListenerSignal.polyfill();
|
|
11
10
|
exports.FocusKeys = void 0;
|
|
@@ -83,18 +82,17 @@ function getDirection(keyboardEvent) {
|
|
|
83
82
|
}
|
|
84
83
|
function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
|
|
85
84
|
const key = keyboardEvent.key;
|
|
86
|
-
const
|
|
85
|
+
const keyLength = [...key].length;
|
|
87
86
|
const isEditable = isEditableElement.isEditableElement(activeElement);
|
|
88
87
|
const isSelect = activeElement instanceof HTMLSelectElement;
|
|
89
|
-
if (isEditable && (
|
|
88
|
+
if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
|
|
90
89
|
return true;
|
|
91
90
|
}
|
|
92
91
|
if (isSelect) {
|
|
93
|
-
|
|
94
|
-
if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
|
|
92
|
+
if (key === 'ArrowDown' && userAgent.isMacOS() && !keyboardEvent.metaKey) {
|
|
95
93
|
return true;
|
|
96
94
|
}
|
|
97
|
-
if (key === 'ArrowDown' && !
|
|
95
|
+
if (key === 'ArrowDown' && !userAgent.isMacOS() && keyboardEvent.altKey) {
|
|
98
96
|
return true;
|
|
99
97
|
}
|
|
100
98
|
return false;
|
|
@@ -132,7 +130,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
|
132
130
|
const hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
133
131
|
function focusZone(container, settings) {
|
|
134
132
|
var _a, _b, _c, _d, _e, _f;
|
|
135
|
-
const focusableElements =
|
|
133
|
+
const focusableElements = [];
|
|
136
134
|
const savedTabIndex = new WeakMap();
|
|
137
135
|
const bindKeys = (_a = settings === null || settings === void 0 ? void 0 : settings.bindKeys) !== null && _a !== void 0 ? _a : ((settings === null || settings === void 0 ? void 0 : settings.getNextFocusable) ? exports.FocusKeys.ArrowAll : exports.FocusKeys.ArrowVertical) | exports.FocusKeys.HomeAndEnd;
|
|
138
136
|
const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
|
|
@@ -144,7 +142,7 @@ function focusZone(container, settings) {
|
|
|
144
142
|
const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
|
|
145
143
|
const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
|
|
146
144
|
function getFirstFocusableElement() {
|
|
147
|
-
return focusableElements
|
|
145
|
+
return focusableElements[0];
|
|
148
146
|
}
|
|
149
147
|
function isActiveDescendantInputFocused() {
|
|
150
148
|
return document.activeElement === activeDescendantControl;
|
|
@@ -189,20 +187,17 @@ function focusZone(container, settings) {
|
|
|
189
187
|
activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
|
|
190
188
|
container.removeAttribute(hasActiveDescendantAttribute);
|
|
191
189
|
previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
items[i].removeAttribute(isActiveDescendantAttribute);
|
|
190
|
+
for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
|
|
191
|
+
item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
|
|
195
192
|
}
|
|
196
193
|
activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
|
|
197
194
|
}
|
|
198
195
|
function beginFocusManagement(...elements) {
|
|
199
|
-
const filteredElements = (settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter)
|
|
200
|
-
? elements.filter(e => settings.focusableElementFilter(e))
|
|
201
|
-
: elements;
|
|
196
|
+
const filteredElements = elements.filter(e => { var _a, _b; return (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter) === null || _a === void 0 ? void 0 : _a.call(settings, e)) !== null && _b !== void 0 ? _b : true; });
|
|
202
197
|
if (filteredElements.length === 0) {
|
|
203
198
|
return;
|
|
204
199
|
}
|
|
205
|
-
focusableElements.
|
|
200
|
+
focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
|
|
206
201
|
for (const element of filteredElements) {
|
|
207
202
|
if (!savedTabIndex.has(element)) {
|
|
208
203
|
savedTabIndex.set(element, element.getAttribute('tabindex'));
|
|
@@ -215,13 +210,13 @@ function focusZone(container, settings) {
|
|
|
215
210
|
}
|
|
216
211
|
function findInsertionIndex(elementsToInsert) {
|
|
217
212
|
const firstElementToInsert = elementsToInsert[0];
|
|
218
|
-
if (focusableElements.
|
|
213
|
+
if (focusableElements.length === 0)
|
|
219
214
|
return 0;
|
|
220
215
|
let iMin = 0;
|
|
221
|
-
let iMax = focusableElements.
|
|
216
|
+
let iMax = focusableElements.length - 1;
|
|
222
217
|
while (iMin <= iMax) {
|
|
223
218
|
const i = Math.floor((iMin + iMax) / 2);
|
|
224
|
-
const element = focusableElements
|
|
219
|
+
const element = focusableElements[i];
|
|
225
220
|
if (followsInDocument(firstElementToInsert, element)) {
|
|
226
221
|
iMax = i - 1;
|
|
227
222
|
}
|
|
@@ -236,7 +231,10 @@ function focusZone(container, settings) {
|
|
|
236
231
|
}
|
|
237
232
|
function endFocusManagement(...elements) {
|
|
238
233
|
for (const element of elements) {
|
|
239
|
-
focusableElements.
|
|
234
|
+
const focusableElementIndex = focusableElements.indexOf(element);
|
|
235
|
+
if (focusableElementIndex >= 0) {
|
|
236
|
+
focusableElements.splice(focusableElementIndex, 1);
|
|
237
|
+
}
|
|
240
238
|
const savedIndex = savedTabIndex.get(element);
|
|
241
239
|
if (savedIndex !== undefined) {
|
|
242
240
|
if (savedIndex === null) {
|
|
@@ -263,61 +261,29 @@ function focusZone(container, settings) {
|
|
|
263
261
|
if (!preventInitialFocus)
|
|
264
262
|
updateFocusedElement(initialElement);
|
|
265
263
|
const observer = new MutationObserver(mutations => {
|
|
266
|
-
const elementsToRemove = new Set();
|
|
267
|
-
const elementsToAdd = new Set();
|
|
268
|
-
const attributeRemovals = new Set();
|
|
269
|
-
const attributeAdditions = new Set();
|
|
270
264
|
for (const mutation of mutations) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
elementsToRemove.add(removedNode);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
for (const addedNode of mutation.addedNodes) {
|
|
278
|
-
if (addedNode instanceof HTMLElement) {
|
|
279
|
-
elementsToAdd.add(addedNode);
|
|
280
|
-
}
|
|
265
|
+
for (const removedNode of mutation.removedNodes) {
|
|
266
|
+
if (removedNode instanceof HTMLElement) {
|
|
267
|
+
endFocusManagement(...iterateFocusableElements.iterateFocusableElements(removedNode));
|
|
281
268
|
}
|
|
282
269
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (hasAttribute) {
|
|
287
|
-
attributeRemovals.add(mutation.target);
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
attributeAdditions.add(mutation.target);
|
|
270
|
+
if (mutation.type === 'attributes' && mutation.oldValue === null) {
|
|
271
|
+
if (mutation.target instanceof HTMLElement) {
|
|
272
|
+
endFocusManagement(mutation.target);
|
|
291
273
|
}
|
|
292
274
|
}
|
|
293
275
|
}
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
toRemove.push(el);
|
|
276
|
+
for (const mutation of mutations) {
|
|
277
|
+
for (const addedNode of mutation.addedNodes) {
|
|
278
|
+
if (addedNode instanceof HTMLElement) {
|
|
279
|
+
beginFocusManagement(...iterateFocusableElements.iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
|
|
299
280
|
}
|
|
300
281
|
}
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
if (attributeRemovals.size > 0) {
|
|
306
|
-
endFocusManagement(...attributeRemovals);
|
|
307
|
-
}
|
|
308
|
-
if (elementsToAdd.size > 0) {
|
|
309
|
-
const toAdd = [];
|
|
310
|
-
for (const node of elementsToAdd) {
|
|
311
|
-
for (const el of iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions)) {
|
|
312
|
-
toAdd.push(el);
|
|
282
|
+
if (mutation.type === 'attributes' && mutation.oldValue !== null) {
|
|
283
|
+
if (mutation.target instanceof HTMLElement) {
|
|
284
|
+
beginFocusManagement(mutation.target);
|
|
313
285
|
}
|
|
314
286
|
}
|
|
315
|
-
if (toAdd.length > 0) {
|
|
316
|
-
beginFocusManagement(...toAdd);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
if (attributeAdditions.size > 0) {
|
|
320
|
-
beginFocusManagement(...attributeAdditions);
|
|
321
287
|
}
|
|
322
288
|
});
|
|
323
289
|
observer.observe(container, {
|
|
@@ -329,9 +295,7 @@ function focusZone(container, settings) {
|
|
|
329
295
|
const controller = new AbortController();
|
|
330
296
|
const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
|
|
331
297
|
signal.addEventListener('abort', () => {
|
|
332
|
-
observer.disconnect();
|
|
333
298
|
endFocusManagement(...focusableElements);
|
|
334
|
-
focusableElements.clear();
|
|
335
299
|
});
|
|
336
300
|
let elementIndexFocusedByClick = undefined;
|
|
337
301
|
container.addEventListener('mousedown', event => {
|
|
@@ -341,7 +305,7 @@ function focusZone(container, settings) {
|
|
|
341
305
|
}, { signal });
|
|
342
306
|
if (activeDescendantControl) {
|
|
343
307
|
container.addEventListener('focusin', event => {
|
|
344
|
-
if (event.target instanceof HTMLElement && focusableElements.
|
|
308
|
+
if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
|
|
345
309
|
activeDescendantControl.focus({ preventScroll });
|
|
346
310
|
updateFocusedElement(event.target);
|
|
347
311
|
}
|
|
@@ -351,10 +315,6 @@ function focusZone(container, settings) {
|
|
|
351
315
|
if (!(target instanceof Node)) {
|
|
352
316
|
return;
|
|
353
317
|
}
|
|
354
|
-
if (target instanceof HTMLElement && focusableElements.has(target)) {
|
|
355
|
-
updateFocusedElement(target);
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
318
|
const focusableElement = focusableElements.find(element => element.contains(target));
|
|
359
319
|
if (focusableElement) {
|
|
360
320
|
updateFocusedElement(focusableElement);
|
|
@@ -379,9 +339,8 @@ function focusZone(container, settings) {
|
|
|
379
339
|
if (event.target instanceof HTMLElement) {
|
|
380
340
|
if (elementIndexFocusedByClick !== undefined) {
|
|
381
341
|
if (elementIndexFocusedByClick >= 0) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
updateFocusedElement(clickedElement);
|
|
342
|
+
if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
|
|
343
|
+
updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
|
|
385
344
|
}
|
|
386
345
|
}
|
|
387
346
|
elementIndexFocusedByClick = undefined;
|
|
@@ -392,8 +351,8 @@ function focusZone(container, settings) {
|
|
|
392
351
|
}
|
|
393
352
|
else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
|
|
394
353
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
395
|
-
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.
|
|
396
|
-
const targetElement = focusableElements
|
|
354
|
+
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
|
|
355
|
+
const targetElement = focusableElements[targetElementIndex];
|
|
397
356
|
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
|
|
398
357
|
return;
|
|
399
358
|
}
|
|
@@ -404,7 +363,8 @@ function focusZone(container, settings) {
|
|
|
404
363
|
else if (typeof focusInStrategy === 'function') {
|
|
405
364
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
406
365
|
const elementToFocus = focusInStrategy(event.relatedTarget);
|
|
407
|
-
|
|
366
|
+
const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
|
|
367
|
+
if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
|
|
408
368
|
elementToFocus.focus({ preventScroll });
|
|
409
369
|
return;
|
|
410
370
|
}
|
|
@@ -463,26 +423,26 @@ function focusZone(container, settings) {
|
|
|
463
423
|
nextFocusedIndex += 1;
|
|
464
424
|
}
|
|
465
425
|
else {
|
|
466
|
-
nextFocusedIndex = focusableElements.
|
|
426
|
+
nextFocusedIndex = focusableElements.length - 1;
|
|
467
427
|
}
|
|
468
428
|
if (nextFocusedIndex < 0) {
|
|
469
429
|
if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
|
|
470
|
-
nextFocusedIndex = focusableElements.
|
|
430
|
+
nextFocusedIndex = focusableElements.length - 1;
|
|
471
431
|
}
|
|
472
432
|
else {
|
|
473
433
|
nextFocusedIndex = 0;
|
|
474
434
|
}
|
|
475
435
|
}
|
|
476
|
-
if (nextFocusedIndex >= focusableElements.
|
|
436
|
+
if (nextFocusedIndex >= focusableElements.length) {
|
|
477
437
|
if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
|
|
478
438
|
nextFocusedIndex = 0;
|
|
479
439
|
}
|
|
480
440
|
else {
|
|
481
|
-
nextFocusedIndex = focusableElements.
|
|
441
|
+
nextFocusedIndex = focusableElements.length - 1;
|
|
482
442
|
}
|
|
483
443
|
}
|
|
484
444
|
if (lastFocusedIndex !== nextFocusedIndex) {
|
|
485
|
-
nextElementToFocus = focusableElements
|
|
445
|
+
nextElementToFocus = focusableElements[nextFocusedIndex];
|
|
486
446
|
}
|
|
487
447
|
}
|
|
488
448
|
if (activeDescendantControl) {
|
|
@@ -33,32 +33,24 @@ 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']);
|
|
37
36
|
function isFocusable(elem, strict = false) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (DISABLEABLE_TAGS.has(elem.tagName) && elem.disabled)
|
|
37
|
+
const disabledAttrInert = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET'].includes(elem.tagName) &&
|
|
38
|
+
elem.disabled;
|
|
39
|
+
const hiddenInert = elem.hidden;
|
|
40
|
+
const hiddenInputInert = elem instanceof HTMLInputElement && elem.type === 'hidden';
|
|
41
|
+
const sentinelInert = elem.classList.contains('sentinel');
|
|
42
|
+
if (disabledAttrInert || hiddenInert || hiddenInputInert || sentinelInert) {
|
|
45
43
|
return false;
|
|
44
|
+
}
|
|
46
45
|
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
46
|
const style = getComputedStyle(elem);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!offsetParent && position !== 'fixed' && position !== 'sticky')
|
|
59
|
-
return false;
|
|
60
|
-
if (elem.getClientRects().length === 0)
|
|
47
|
+
const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
|
|
48
|
+
const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
|
|
49
|
+
const displayInert = style.display === 'none' || !elem.offsetParent;
|
|
50
|
+
const clientRectsInert = elem.getClientRects().length === 0;
|
|
51
|
+
if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
|
|
61
52
|
return false;
|
|
53
|
+
}
|
|
62
54
|
}
|
|
63
55
|
if (elem.getAttribute('tabindex') != null) {
|
|
64
56
|
return true;
|
|
@@ -25,11 +25,9 @@ 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
|
-
|
|
31
|
-
return parentNode;
|
|
32
|
-
}
|
|
28
|
+
while (parentNode !== null) {
|
|
29
|
+
if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
|
|
30
|
+
return parentNode;
|
|
33
31
|
}
|
|
34
32
|
parentNode = parentNode.parentNode;
|
|
35
33
|
}
|
|
@@ -51,24 +49,26 @@ function isOnTopLayer(element) {
|
|
|
51
49
|
return false;
|
|
52
50
|
}
|
|
53
51
|
function getClippingRect(element) {
|
|
54
|
-
let clippingNode = document.body;
|
|
55
52
|
let parentNode = element;
|
|
56
|
-
while (parentNode !== null
|
|
57
|
-
if (parentNode instanceof
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
while (parentNode !== null) {
|
|
54
|
+
if (!(parentNode instanceof Element)) {
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const parentNodeStyle = getComputedStyle(parentNode);
|
|
58
|
+
if (parentNodeStyle.overflow !== 'visible') {
|
|
59
|
+
break;
|
|
63
60
|
}
|
|
64
61
|
parentNode = parentNode.parentNode;
|
|
65
62
|
}
|
|
63
|
+
const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
|
|
66
64
|
const elemRect = clippingNode.getBoundingClientRect();
|
|
67
65
|
const elemStyle = getComputedStyle(clippingNode);
|
|
68
|
-
const borderTop
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const [borderTop, borderLeft, borderRight, borderBottom] = [
|
|
67
|
+
elemStyle.borderTopWidth,
|
|
68
|
+
elemStyle.borderLeftWidth,
|
|
69
|
+
elemStyle.borderRightWidth,
|
|
70
|
+
elemStyle.borderBottomWidth,
|
|
71
|
+
].map(v => parseInt(v, 10) || 0);
|
|
72
72
|
return {
|
|
73
73
|
top: elemRect.top + borderTop,
|
|
74
74
|
left: elemRect.left + borderLeft,
|
package/dist/esm/focus-trap.mjs
CHANGED
|
@@ -4,16 +4,6 @@ import { polyfill } from './polyfills/event-listener-signal.mjs';
|
|
|
4
4
|
polyfill();
|
|
5
5
|
const suspendedTrapStack = [];
|
|
6
6
|
let activeTrap = undefined;
|
|
7
|
-
const SR_ONLY_STYLES = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
|
|
8
|
-
function createSentinel({ onFocus }) {
|
|
9
|
-
const sentinel = document.createElement('span');
|
|
10
|
-
sentinel.setAttribute('class', 'sentinel');
|
|
11
|
-
sentinel.setAttribute('tabindex', '0');
|
|
12
|
-
sentinel.setAttribute('aria-hidden', 'true');
|
|
13
|
-
sentinel.style.cssText = SR_ONLY_STYLES;
|
|
14
|
-
sentinel.onfocus = onFocus;
|
|
15
|
-
return sentinel;
|
|
16
|
-
}
|
|
17
7
|
function tryReactivate() {
|
|
18
8
|
const trapToReactivate = suspendedTrapStack.pop();
|
|
19
9
|
if (trapToReactivate) {
|
|
@@ -31,10 +21,9 @@ function observeFocusTrap(container, sentinels) {
|
|
|
31
21
|
const observer = new MutationObserver(mutations => {
|
|
32
22
|
for (const mutation of mutations) {
|
|
33
23
|
if (mutation.type === 'childList' && mutation.addedNodes.length) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
24
|
+
const sentinelChildren = Array.from(mutation.addedNodes).filter(e => e instanceof HTMLElement && e.classList.contains('sentinel') && e.tagName === 'SPAN');
|
|
25
|
+
if (sentinelChildren.length) {
|
|
26
|
+
return;
|
|
38
27
|
}
|
|
39
28
|
const firstChild = container.firstElementChild;
|
|
40
29
|
const lastChild = container.lastElementChild;
|
|
@@ -55,20 +44,24 @@ function focusTrap(container, initialFocus, abortSignal) {
|
|
|
55
44
|
const controller = new AbortController();
|
|
56
45
|
const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
|
|
57
46
|
container.setAttribute('data-focus-trap', 'active');
|
|
58
|
-
const sentinelStart =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
47
|
+
const sentinelStart = document.createElement('span');
|
|
48
|
+
sentinelStart.setAttribute('class', 'sentinel');
|
|
49
|
+
sentinelStart.setAttribute('tabindex', '0');
|
|
50
|
+
sentinelStart.setAttribute('aria-hidden', 'true');
|
|
51
|
+
sentinelStart.onfocus = () => {
|
|
52
|
+
const lastFocusableChild = getFocusableChild(container, true);
|
|
53
|
+
lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
|
|
54
|
+
};
|
|
55
|
+
const sentinelEnd = document.createElement('span');
|
|
56
|
+
sentinelEnd.setAttribute('class', 'sentinel');
|
|
57
|
+
sentinelEnd.setAttribute('tabindex', '0');
|
|
58
|
+
sentinelEnd.setAttribute('aria-hidden', 'true');
|
|
59
|
+
sentinelEnd.onfocus = () => {
|
|
60
|
+
const firstFocusableChild = getFocusableChild(container);
|
|
61
|
+
firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
|
|
62
|
+
};
|
|
63
|
+
const existingSentinels = Array.from(container.children).filter(e => e.classList.contains('sentinel') && e.tagName === 'SPAN');
|
|
64
|
+
if (!existingSentinels.length) {
|
|
72
65
|
container.prepend(sentinelStart);
|
|
73
66
|
container.append(sentinelEnd);
|
|
74
67
|
}
|
package/dist/esm/focus-zone.mjs
CHANGED
|
@@ -3,7 +3,6 @@ import { isMacOS } from './utils/user-agent.mjs';
|
|
|
3
3
|
import { iterateFocusableElements } from './utils/iterate-focusable-elements.mjs';
|
|
4
4
|
import { uniqueId } from './utils/unique-id.mjs';
|
|
5
5
|
import { isEditableElement } from './utils/is-editable-element.mjs';
|
|
6
|
-
import { IndexedSet } from './utils/indexed-set.mjs';
|
|
7
6
|
|
|
8
7
|
polyfill();
|
|
9
8
|
var FocusKeys;
|
|
@@ -81,18 +80,17 @@ function getDirection(keyboardEvent) {
|
|
|
81
80
|
}
|
|
82
81
|
function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
|
|
83
82
|
const key = keyboardEvent.key;
|
|
84
|
-
const
|
|
83
|
+
const keyLength = [...key].length;
|
|
85
84
|
const isEditable = isEditableElement(activeElement);
|
|
86
85
|
const isSelect = activeElement instanceof HTMLSelectElement;
|
|
87
|
-
if (isEditable && (
|
|
86
|
+
if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
|
|
88
87
|
return true;
|
|
89
88
|
}
|
|
90
89
|
if (isSelect) {
|
|
91
|
-
|
|
92
|
-
if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
|
|
90
|
+
if (key === 'ArrowDown' && isMacOS() && !keyboardEvent.metaKey) {
|
|
93
91
|
return true;
|
|
94
92
|
}
|
|
95
|
-
if (key === 'ArrowDown' && !
|
|
93
|
+
if (key === 'ArrowDown' && !isMacOS() && keyboardEvent.altKey) {
|
|
96
94
|
return true;
|
|
97
95
|
}
|
|
98
96
|
return false;
|
|
@@ -130,7 +128,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
|
|
|
130
128
|
const hasActiveDescendantAttribute = 'data-has-active-descendant';
|
|
131
129
|
function focusZone(container, settings) {
|
|
132
130
|
var _a, _b, _c, _d, _e, _f;
|
|
133
|
-
const focusableElements =
|
|
131
|
+
const focusableElements = [];
|
|
134
132
|
const savedTabIndex = new WeakMap();
|
|
135
133
|
const bindKeys = (_a = settings === null || settings === void 0 ? void 0 : settings.bindKeys) !== null && _a !== void 0 ? _a : ((settings === null || settings === void 0 ? void 0 : settings.getNextFocusable) ? FocusKeys.ArrowAll : FocusKeys.ArrowVertical) | FocusKeys.HomeAndEnd;
|
|
136
134
|
const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
|
|
@@ -142,7 +140,7 @@ function focusZone(container, settings) {
|
|
|
142
140
|
const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
|
|
143
141
|
const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
|
|
144
142
|
function getFirstFocusableElement() {
|
|
145
|
-
return focusableElements
|
|
143
|
+
return focusableElements[0];
|
|
146
144
|
}
|
|
147
145
|
function isActiveDescendantInputFocused() {
|
|
148
146
|
return document.activeElement === activeDescendantControl;
|
|
@@ -187,20 +185,17 @@ function focusZone(container, settings) {
|
|
|
187
185
|
activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
|
|
188
186
|
container.removeAttribute(hasActiveDescendantAttribute);
|
|
189
187
|
previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
items[i].removeAttribute(isActiveDescendantAttribute);
|
|
188
|
+
for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
|
|
189
|
+
item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
|
|
193
190
|
}
|
|
194
191
|
activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
|
|
195
192
|
}
|
|
196
193
|
function beginFocusManagement(...elements) {
|
|
197
|
-
const filteredElements = (settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter)
|
|
198
|
-
? elements.filter(e => settings.focusableElementFilter(e))
|
|
199
|
-
: elements;
|
|
194
|
+
const filteredElements = elements.filter(e => { var _a, _b; return (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter) === null || _a === void 0 ? void 0 : _a.call(settings, e)) !== null && _b !== void 0 ? _b : true; });
|
|
200
195
|
if (filteredElements.length === 0) {
|
|
201
196
|
return;
|
|
202
197
|
}
|
|
203
|
-
focusableElements.
|
|
198
|
+
focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
|
|
204
199
|
for (const element of filteredElements) {
|
|
205
200
|
if (!savedTabIndex.has(element)) {
|
|
206
201
|
savedTabIndex.set(element, element.getAttribute('tabindex'));
|
|
@@ -213,13 +208,13 @@ function focusZone(container, settings) {
|
|
|
213
208
|
}
|
|
214
209
|
function findInsertionIndex(elementsToInsert) {
|
|
215
210
|
const firstElementToInsert = elementsToInsert[0];
|
|
216
|
-
if (focusableElements.
|
|
211
|
+
if (focusableElements.length === 0)
|
|
217
212
|
return 0;
|
|
218
213
|
let iMin = 0;
|
|
219
|
-
let iMax = focusableElements.
|
|
214
|
+
let iMax = focusableElements.length - 1;
|
|
220
215
|
while (iMin <= iMax) {
|
|
221
216
|
const i = Math.floor((iMin + iMax) / 2);
|
|
222
|
-
const element = focusableElements
|
|
217
|
+
const element = focusableElements[i];
|
|
223
218
|
if (followsInDocument(firstElementToInsert, element)) {
|
|
224
219
|
iMax = i - 1;
|
|
225
220
|
}
|
|
@@ -234,7 +229,10 @@ function focusZone(container, settings) {
|
|
|
234
229
|
}
|
|
235
230
|
function endFocusManagement(...elements) {
|
|
236
231
|
for (const element of elements) {
|
|
237
|
-
focusableElements.
|
|
232
|
+
const focusableElementIndex = focusableElements.indexOf(element);
|
|
233
|
+
if (focusableElementIndex >= 0) {
|
|
234
|
+
focusableElements.splice(focusableElementIndex, 1);
|
|
235
|
+
}
|
|
238
236
|
const savedIndex = savedTabIndex.get(element);
|
|
239
237
|
if (savedIndex !== undefined) {
|
|
240
238
|
if (savedIndex === null) {
|
|
@@ -261,61 +259,29 @@ function focusZone(container, settings) {
|
|
|
261
259
|
if (!preventInitialFocus)
|
|
262
260
|
updateFocusedElement(initialElement);
|
|
263
261
|
const observer = new MutationObserver(mutations => {
|
|
264
|
-
const elementsToRemove = new Set();
|
|
265
|
-
const elementsToAdd = new Set();
|
|
266
|
-
const attributeRemovals = new Set();
|
|
267
|
-
const attributeAdditions = new Set();
|
|
268
262
|
for (const mutation of mutations) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
elementsToRemove.add(removedNode);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
for (const addedNode of mutation.addedNodes) {
|
|
276
|
-
if (addedNode instanceof HTMLElement) {
|
|
277
|
-
elementsToAdd.add(addedNode);
|
|
278
|
-
}
|
|
263
|
+
for (const removedNode of mutation.removedNodes) {
|
|
264
|
+
if (removedNode instanceof HTMLElement) {
|
|
265
|
+
endFocusManagement(...iterateFocusableElements(removedNode));
|
|
279
266
|
}
|
|
280
267
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (hasAttribute) {
|
|
285
|
-
attributeRemovals.add(mutation.target);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
attributeAdditions.add(mutation.target);
|
|
268
|
+
if (mutation.type === 'attributes' && mutation.oldValue === null) {
|
|
269
|
+
if (mutation.target instanceof HTMLElement) {
|
|
270
|
+
endFocusManagement(mutation.target);
|
|
289
271
|
}
|
|
290
272
|
}
|
|
291
273
|
}
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
toRemove.push(el);
|
|
274
|
+
for (const mutation of mutations) {
|
|
275
|
+
for (const addedNode of mutation.addedNodes) {
|
|
276
|
+
if (addedNode instanceof HTMLElement) {
|
|
277
|
+
beginFocusManagement(...iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
|
|
297
278
|
}
|
|
298
279
|
}
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
if (attributeRemovals.size > 0) {
|
|
304
|
-
endFocusManagement(...attributeRemovals);
|
|
305
|
-
}
|
|
306
|
-
if (elementsToAdd.size > 0) {
|
|
307
|
-
const toAdd = [];
|
|
308
|
-
for (const node of elementsToAdd) {
|
|
309
|
-
for (const el of iterateFocusableElements(node, iterateFocusableElementsOptions)) {
|
|
310
|
-
toAdd.push(el);
|
|
280
|
+
if (mutation.type === 'attributes' && mutation.oldValue !== null) {
|
|
281
|
+
if (mutation.target instanceof HTMLElement) {
|
|
282
|
+
beginFocusManagement(mutation.target);
|
|
311
283
|
}
|
|
312
284
|
}
|
|
313
|
-
if (toAdd.length > 0) {
|
|
314
|
-
beginFocusManagement(...toAdd);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (attributeAdditions.size > 0) {
|
|
318
|
-
beginFocusManagement(...attributeAdditions);
|
|
319
285
|
}
|
|
320
286
|
});
|
|
321
287
|
observer.observe(container, {
|
|
@@ -327,9 +293,7 @@ function focusZone(container, settings) {
|
|
|
327
293
|
const controller = new AbortController();
|
|
328
294
|
const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
|
|
329
295
|
signal.addEventListener('abort', () => {
|
|
330
|
-
observer.disconnect();
|
|
331
296
|
endFocusManagement(...focusableElements);
|
|
332
|
-
focusableElements.clear();
|
|
333
297
|
});
|
|
334
298
|
let elementIndexFocusedByClick = undefined;
|
|
335
299
|
container.addEventListener('mousedown', event => {
|
|
@@ -339,7 +303,7 @@ function focusZone(container, settings) {
|
|
|
339
303
|
}, { signal });
|
|
340
304
|
if (activeDescendantControl) {
|
|
341
305
|
container.addEventListener('focusin', event => {
|
|
342
|
-
if (event.target instanceof HTMLElement && focusableElements.
|
|
306
|
+
if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
|
|
343
307
|
activeDescendantControl.focus({ preventScroll });
|
|
344
308
|
updateFocusedElement(event.target);
|
|
345
309
|
}
|
|
@@ -349,10 +313,6 @@ function focusZone(container, settings) {
|
|
|
349
313
|
if (!(target instanceof Node)) {
|
|
350
314
|
return;
|
|
351
315
|
}
|
|
352
|
-
if (target instanceof HTMLElement && focusableElements.has(target)) {
|
|
353
|
-
updateFocusedElement(target);
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
316
|
const focusableElement = focusableElements.find(element => element.contains(target));
|
|
357
317
|
if (focusableElement) {
|
|
358
318
|
updateFocusedElement(focusableElement);
|
|
@@ -377,9 +337,8 @@ function focusZone(container, settings) {
|
|
|
377
337
|
if (event.target instanceof HTMLElement) {
|
|
378
338
|
if (elementIndexFocusedByClick !== undefined) {
|
|
379
339
|
if (elementIndexFocusedByClick >= 0) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
updateFocusedElement(clickedElement);
|
|
340
|
+
if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
|
|
341
|
+
updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
|
|
383
342
|
}
|
|
384
343
|
}
|
|
385
344
|
elementIndexFocusedByClick = undefined;
|
|
@@ -390,8 +349,8 @@ function focusZone(container, settings) {
|
|
|
390
349
|
}
|
|
391
350
|
else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
|
|
392
351
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
393
|
-
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.
|
|
394
|
-
const targetElement = focusableElements
|
|
352
|
+
const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
|
|
353
|
+
const targetElement = focusableElements[targetElementIndex];
|
|
395
354
|
targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
|
|
396
355
|
return;
|
|
397
356
|
}
|
|
@@ -402,7 +361,8 @@ function focusZone(container, settings) {
|
|
|
402
361
|
else if (typeof focusInStrategy === 'function') {
|
|
403
362
|
if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
|
|
404
363
|
const elementToFocus = focusInStrategy(event.relatedTarget);
|
|
405
|
-
|
|
364
|
+
const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
|
|
365
|
+
if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
|
|
406
366
|
elementToFocus.focus({ preventScroll });
|
|
407
367
|
return;
|
|
408
368
|
}
|
|
@@ -461,26 +421,26 @@ function focusZone(container, settings) {
|
|
|
461
421
|
nextFocusedIndex += 1;
|
|
462
422
|
}
|
|
463
423
|
else {
|
|
464
|
-
nextFocusedIndex = focusableElements.
|
|
424
|
+
nextFocusedIndex = focusableElements.length - 1;
|
|
465
425
|
}
|
|
466
426
|
if (nextFocusedIndex < 0) {
|
|
467
427
|
if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
|
|
468
|
-
nextFocusedIndex = focusableElements.
|
|
428
|
+
nextFocusedIndex = focusableElements.length - 1;
|
|
469
429
|
}
|
|
470
430
|
else {
|
|
471
431
|
nextFocusedIndex = 0;
|
|
472
432
|
}
|
|
473
433
|
}
|
|
474
|
-
if (nextFocusedIndex >= focusableElements.
|
|
434
|
+
if (nextFocusedIndex >= focusableElements.length) {
|
|
475
435
|
if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
|
|
476
436
|
nextFocusedIndex = 0;
|
|
477
437
|
}
|
|
478
438
|
else {
|
|
479
|
-
nextFocusedIndex = focusableElements.
|
|
439
|
+
nextFocusedIndex = focusableElements.length - 1;
|
|
480
440
|
}
|
|
481
441
|
}
|
|
482
442
|
if (lastFocusedIndex !== nextFocusedIndex) {
|
|
483
|
-
nextElementToFocus = focusableElements
|
|
443
|
+
nextElementToFocus = focusableElements[nextFocusedIndex];
|
|
484
444
|
}
|
|
485
445
|
}
|
|
486
446
|
if (activeDescendantControl) {
|
|
@@ -31,32 +31,24 @@ 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']);
|
|
35
34
|
function isFocusable(elem, strict = false) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (DISABLEABLE_TAGS.has(elem.tagName) && elem.disabled)
|
|
35
|
+
const disabledAttrInert = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET'].includes(elem.tagName) &&
|
|
36
|
+
elem.disabled;
|
|
37
|
+
const hiddenInert = elem.hidden;
|
|
38
|
+
const hiddenInputInert = elem instanceof HTMLInputElement && elem.type === 'hidden';
|
|
39
|
+
const sentinelInert = elem.classList.contains('sentinel');
|
|
40
|
+
if (disabledAttrInert || hiddenInert || hiddenInputInert || sentinelInert) {
|
|
43
41
|
return false;
|
|
42
|
+
}
|
|
44
43
|
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
44
|
const style = getComputedStyle(elem);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!offsetParent && position !== 'fixed' && position !== 'sticky')
|
|
57
|
-
return false;
|
|
58
|
-
if (elem.getClientRects().length === 0)
|
|
45
|
+
const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
|
|
46
|
+
const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
|
|
47
|
+
const displayInert = style.display === 'none' || !elem.offsetParent;
|
|
48
|
+
const clientRectsInert = elem.getClientRects().length === 0;
|
|
49
|
+
if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
|
|
59
50
|
return false;
|
|
51
|
+
}
|
|
60
52
|
}
|
|
61
53
|
if (elem.getAttribute('tabindex') != null) {
|
|
62
54
|
return true;
|
package/package.json
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare class IndexedSet<T> {
|
|
2
|
-
private _items;
|
|
3
|
-
private _itemSet;
|
|
4
|
-
insertAt(index: number, ...elements: T[]): void;
|
|
5
|
-
delete(element: T): boolean;
|
|
6
|
-
has(element: T): boolean;
|
|
7
|
-
indexOf(element: T): number;
|
|
8
|
-
get(index: number): T | undefined;
|
|
9
|
-
get size(): number;
|
|
10
|
-
[Symbol.iterator](): Iterator<T>;
|
|
11
|
-
clear(): void;
|
|
12
|
-
find(predicate: (element: T) => boolean): T | undefined;
|
|
13
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
class IndexedSet {
|
|
4
|
-
constructor() {
|
|
5
|
-
this._items = [];
|
|
6
|
-
this._itemSet = new Set();
|
|
7
|
-
}
|
|
8
|
-
insertAt(index, ...elements) {
|
|
9
|
-
const newElements = elements.filter(e => !this._itemSet.has(e));
|
|
10
|
-
if (newElements.length === 0)
|
|
11
|
-
return;
|
|
12
|
-
this._items.splice(index, 0, ...newElements);
|
|
13
|
-
for (const element of newElements) {
|
|
14
|
-
this._itemSet.add(element);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
delete(element) {
|
|
18
|
-
if (!this._itemSet.has(element))
|
|
19
|
-
return false;
|
|
20
|
-
const index = this._items.indexOf(element);
|
|
21
|
-
if (index >= 0) {
|
|
22
|
-
this._items.splice(index, 1);
|
|
23
|
-
}
|
|
24
|
-
this._itemSet.delete(element);
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
has(element) {
|
|
28
|
-
return this._itemSet.has(element);
|
|
29
|
-
}
|
|
30
|
-
indexOf(element) {
|
|
31
|
-
if (!this._itemSet.has(element))
|
|
32
|
-
return -1;
|
|
33
|
-
return this._items.indexOf(element);
|
|
34
|
-
}
|
|
35
|
-
get(index) {
|
|
36
|
-
return this._items[index];
|
|
37
|
-
}
|
|
38
|
-
get size() {
|
|
39
|
-
return this._items.length;
|
|
40
|
-
}
|
|
41
|
-
[Symbol.iterator]() {
|
|
42
|
-
return this._items[Symbol.iterator]();
|
|
43
|
-
}
|
|
44
|
-
clear() {
|
|
45
|
-
this._items = [];
|
|
46
|
-
this._itemSet.clear();
|
|
47
|
-
}
|
|
48
|
-
find(predicate) {
|
|
49
|
-
return this._items.find(predicate);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
exports.IndexedSet = IndexedSet;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare class IndexedSet<T> {
|
|
2
|
-
private _items;
|
|
3
|
-
private _itemSet;
|
|
4
|
-
insertAt(index: number, ...elements: T[]): void;
|
|
5
|
-
delete(element: T): boolean;
|
|
6
|
-
has(element: T): boolean;
|
|
7
|
-
indexOf(element: T): number;
|
|
8
|
-
get(index: number): T | undefined;
|
|
9
|
-
get size(): number;
|
|
10
|
-
[Symbol.iterator](): Iterator<T>;
|
|
11
|
-
clear(): void;
|
|
12
|
-
find(predicate: (element: T) => boolean): T | undefined;
|
|
13
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
class IndexedSet {
|
|
2
|
-
constructor() {
|
|
3
|
-
this._items = [];
|
|
4
|
-
this._itemSet = new Set();
|
|
5
|
-
}
|
|
6
|
-
insertAt(index, ...elements) {
|
|
7
|
-
const newElements = elements.filter(e => !this._itemSet.has(e));
|
|
8
|
-
if (newElements.length === 0)
|
|
9
|
-
return;
|
|
10
|
-
this._items.splice(index, 0, ...newElements);
|
|
11
|
-
for (const element of newElements) {
|
|
12
|
-
this._itemSet.add(element);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
delete(element) {
|
|
16
|
-
if (!this._itemSet.has(element))
|
|
17
|
-
return false;
|
|
18
|
-
const index = this._items.indexOf(element);
|
|
19
|
-
if (index >= 0) {
|
|
20
|
-
this._items.splice(index, 1);
|
|
21
|
-
}
|
|
22
|
-
this._itemSet.delete(element);
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
has(element) {
|
|
26
|
-
return this._itemSet.has(element);
|
|
27
|
-
}
|
|
28
|
-
indexOf(element) {
|
|
29
|
-
if (!this._itemSet.has(element))
|
|
30
|
-
return -1;
|
|
31
|
-
return this._items.indexOf(element);
|
|
32
|
-
}
|
|
33
|
-
get(index) {
|
|
34
|
-
return this._items[index];
|
|
35
|
-
}
|
|
36
|
-
get size() {
|
|
37
|
-
return this._items.length;
|
|
38
|
-
}
|
|
39
|
-
[Symbol.iterator]() {
|
|
40
|
-
return this._items[Symbol.iterator]();
|
|
41
|
-
}
|
|
42
|
-
clear() {
|
|
43
|
-
this._items = [];
|
|
44
|
-
this._itemSet.clear();
|
|
45
|
-
}
|
|
46
|
-
find(predicate) {
|
|
47
|
-
return this._items.find(predicate);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export { IndexedSet };
|