-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathtab_loop.tsx
More file actions
108 lines (96 loc) · 2.93 KB
/
tab_loop.tsx
File metadata and controls
108 lines (96 loc) · 2.93 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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import React, { Component, createRef } from "react";
import type { ReactNode } from "react";
interface TabLoopProps {
enableTabLoop?: boolean;
children?: ReactNode | undefined;
}
const focusableElementsSelector =
"[tabindex], a, button, input, select, textarea";
const focusableFilter = (
node:
| HTMLButtonElement
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement
| HTMLAnchorElement,
) => {
if (node instanceof HTMLAnchorElement) {
return node.tabIndex !== -1;
}
return !node.disabled && node.tabIndex !== -1;
};
/**
* `TabLoop` is a React component that manages tabbing behavior for its children.
*
* TabLoop prevents the user from tabbing outside of the popper
* It creates a tabindex loop so that "Tab" on the last element will focus the first element
* and "Shift Tab" on the first element will focus the last element
*
* @component
* @example
* <TabLoop enableTabLoop={true}>
* <ChildComponent />
* </TabLoop>
*
* @param props - The properties that define the `TabLoop` component.
* @param props.children - The child components.
* @param props.enableTabLoop - Whether to enable the tab loop.
*
* @returns The `TabLoop` component.
*/
export default class TabLoop extends Component<TabLoopProps> {
static defaultProps = {
enableTabLoop: true,
};
constructor(props: TabLoopProps) {
super(props);
this.tabLoopRef = createRef();
}
private tabLoopRef: React.RefObject<HTMLDivElement | null>;
/**
* `getTabChildren` is a method of the `TabLoop` class that retrieves all tabbable children of the component.
*
* This method uses the `tabbable` library to find all tabbable elements within the `TabLoop` component.
* It then filters out any elements that are not visible.
*
* @returns An array of all tabbable and visible children of the `TabLoop` component.
*/
getTabChildren = () =>
Array.prototype.slice
.call(
this.tabLoopRef.current?.querySelectorAll(focusableElementsSelector),
1,
-1,
)
.filter(focusableFilter);
handleFocusStart = () => {
const tabChildren = this.getTabChildren();
tabChildren &&
tabChildren.length > 1 &&
tabChildren[tabChildren.length - 1].focus();
};
handleFocusEnd = () => {
const tabChildren = this.getTabChildren();
tabChildren && tabChildren.length > 1 && tabChildren[0].focus();
};
render(): React.ReactNode {
if (!(this.props.enableTabLoop ?? TabLoop.defaultProps.enableTabLoop)) {
return this.props.children;
}
return (
<div className="react-datepicker__tab-loop" ref={this.tabLoopRef}>
<div
className="react-datepicker__tab-loop__start"
tabIndex={0}
onFocus={this.handleFocusStart}
/>
{this.props.children}
<div
className="react-datepicker__tab-loop__end"
tabIndex={0}
onFocus={this.handleFocusEnd}
/>
</div>
);
}
}