-
-
Notifications
You must be signed in to change notification settings - Fork 243
Expand file tree
/
Copy pathfocusUtils.ts
More file actions
91 lines (81 loc) · 2.23 KB
/
Copy pathfocusUtils.ts
File metadata and controls
91 lines (81 loc) · 2.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import type * as React from 'react';
const TABBABLE_SELECTOR =
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
function isTabbable(el: HTMLElement, win: Window): boolean {
if (el.closest('[aria-hidden="true"]')) {
return false;
}
if ('disabled' in el && (el as HTMLButtonElement).disabled) {
return false;
}
if (el instanceof HTMLInputElement && el.type === 'hidden') {
return false;
}
const ti = el.getAttribute('tabindex');
if (ti !== null && Number(ti) < 0) {
return false;
}
const style = win.getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden') {
return false;
}
return true;
}
/** Visible, tabbable descendants inside `container` (in DOM order). */
export function getTabbableElements(container: HTMLElement): HTMLElement[] {
const doc = container.ownerDocument;
const win = doc.defaultView!;
const nodeList = container.querySelectorAll<HTMLElement>(TABBABLE_SELECTOR);
const list: HTMLElement[] = [];
for (let i = 0; i < nodeList.length; i += 1) {
const el = nodeList[i];
if (isTabbable(el, win)) {
list.push(el);
}
}
return list;
}
export function focusPopupRootOrFirst(
container: HTMLElement,
): HTMLElement | null {
const tabbables = getTabbableElements(container);
if (tabbables.length) {
tabbables[0].focus();
return tabbables[0];
}
if (!container.hasAttribute('tabindex')) {
container.setAttribute('tabindex', '-1');
}
container.focus();
return container;
}
export function handlePopupTabTrap(
e: React.KeyboardEvent,
container: HTMLElement,
): void {
if (e.key !== 'Tab' || e.defaultPrevented) {
return;
}
const list = getTabbableElements(container);
const active = document.activeElement as HTMLElement | null;
if (!active || !container.contains(active)) {
return;
}
if (list.length === 0) {
if (active === container) {
e.preventDefault();
}
return;
}
const first = list[0];
const last = list[list.length - 1];
if (!e.shiftKey) {
if (active === last || active === container) {
e.preventDefault();
first.focus();
}
} else if (active === first || active === container) {
e.preventDefault();
last.focus();
}
}