focus-trap 6.1.1 → 6.2.0
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/CHANGELOG.md +26 -0
- package/README.md +28 -12
- package/dist/focus-trap.esm.js +182 -106
- package/dist/focus-trap.esm.js.map +1 -1
- package/dist/focus-trap.esm.min.js +2 -7
- package/dist/focus-trap.esm.min.js.map +1 -1
- package/dist/focus-trap.js +182 -106
- package/dist/focus-trap.js.map +1 -1
- package/dist/focus-trap.min.js +2 -7
- package/dist/focus-trap.min.js.map +1 -1
- package/dist/focus-trap.umd.js +182 -106
- package/dist/focus-trap.umd.js.map +1 -1
- package/dist/focus-trap.umd.min.js +2 -7
- package/dist/focus-trap.umd.min.js.map +1 -1
- package/index.d.ts +6 -5
- package/index.js +83 -23
- package/package.json +23 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2267d17: Adding support for multiple elements to be passed in #217
|
|
8
|
+
|
|
9
|
+
## 6.1.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 38b6b98: Update tabbable to [5.1.3](https://github.com/focus-trap/tabbable/blob/master/CHANGELOG.md#513) to get bug fixes related to detail and summary elements.
|
|
14
|
+
|
|
15
|
+
## 6.1.3
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 6a39217: Close the gap with #172 and bump `tabbable` to 5.1.2 which has a similar fix.
|
|
20
|
+
- 756c79d: Fix #172 (again): Transpile ESM bundle down to the same browser target used for the CJS and UMD bundles. ESM is just the module system, not the browser target.
|
|
21
|
+
|
|
22
|
+
## 6.1.2
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 00674dd: Fix #172: Transpile non-minified bundles so they are compatible with IE11.
|
|
27
|
+
- 679009b: Update tabbable dependency to 5.1.1 to get transpiled non-minified bundles.
|
|
28
|
+
|
|
3
29
|
## 6.1.1
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# focus-trap [](https://github.com/focus-trap/focus-trap/actions?query=workflow:CI+branch:master) [](./LICENSE)
|
|
2
2
|
|
|
3
3
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
4
|
-
[](#contributors)
|
|
5
5
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
6
6
|
|
|
7
7
|
Trap focus within a DOM node.
|
|
@@ -28,7 +28,7 @@ When the focus trap is deactivated, this is what should happen:
|
|
|
28
28
|
- Focus is passed to *whichever element had focus when the trap was activated* (e.g. the button that opened the modal or menu).
|
|
29
29
|
- Tabbing and clicking behave normally everywhere.
|
|
30
30
|
|
|
31
|
-
[Check out the demos.](http://
|
|
31
|
+
[Check out the demos.](http://focus-trap.github.io/focus-trap/demo/)
|
|
32
32
|
|
|
33
33
|
For more advanced usage (e.g. focus traps within focus traps), you can also pause a focus trap's behavior without deactivating it entirely, then unpause at will.
|
|
34
34
|
|
|
@@ -63,7 +63,8 @@ Returns a new focus trap on `element`.
|
|
|
63
63
|
|
|
64
64
|
`element` can be
|
|
65
65
|
- a DOM node (the focus trap itself) or
|
|
66
|
-
- a selector string (which will be
|
|
66
|
+
- a selector string (which will be passed to `document.querySelector()` to find the DOM node) or
|
|
67
|
+
- an array of DOM nodes or selector strings (where the order determines where the focus will go after the last tabbable element of a DOM node/selector is reached).
|
|
67
68
|
|
|
68
69
|
`createOptions`:
|
|
69
70
|
|
|
@@ -134,6 +135,16 @@ If the focus trap has not been activated or has not been paused, nothing happens
|
|
|
134
135
|
|
|
135
136
|
Returns the `focusTrap`.
|
|
136
137
|
|
|
138
|
+
### focusTrap.updateContainerElements()
|
|
139
|
+
|
|
140
|
+
Update the element(s) that are used as containers for the focus trap.
|
|
141
|
+
|
|
142
|
+
When you call the function `createFocusTrap`, you pass in an element (or selector), or an array of elements (or selectors) to keep the focus within. This method simply allows you to update which elements to keep the focus within.
|
|
143
|
+
|
|
144
|
+
A use case for this is found in focus-trap-react, where React `ref`'s may not be initialized yet, but when they are you want to have them be a container element.
|
|
145
|
+
|
|
146
|
+
Returns the `focusTrap`.
|
|
147
|
+
|
|
137
148
|
## Examples
|
|
138
149
|
|
|
139
150
|
Read code in `demo/` and [see how it works](http://davidtheclark.github.io/focus-trap/demo/).
|
|
@@ -206,17 +217,22 @@ In alphabetical order:
|
|
|
206
217
|
<!-- markdownlint-disable -->
|
|
207
218
|
<table>
|
|
208
219
|
<tr>
|
|
209
|
-
<td align="center"><a href="http://davidtheclark.com/"><img src="https://avatars2.githubusercontent.com/u/628431?v=4
|
|
210
|
-
<td align="center"><a href="https://
|
|
211
|
-
<td align="center"><a href="https://github.com/
|
|
212
|
-
<td align="center"><a href="https://github.com/
|
|
213
|
-
<td align="center"><a href="https://github.com/
|
|
214
|
-
<td align="center"><a href="https://seanmcp.com/"><img src="https://avatars1.githubusercontent.com/u/6360367?v=4
|
|
215
|
-
<td align="center"><a href="https://
|
|
220
|
+
<td align="center"><a href="http://davidtheclark.com/"><img src="https://avatars2.githubusercontent.com/u/628431?v=4" width="100px;" alt=""/><br /><sub><b>David Clark</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=davidtheclark" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Adavidtheclark" title="Bug reports">🐛</a> <a href="#infra-davidtheclark" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=davidtheclark" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=davidtheclark" title="Documentation">📖</a> <a href="#maintenance-davidtheclark" title="Maintenance">🚧</a></td>
|
|
221
|
+
<td align="center"><a href="https://stefancameron.com/"><img src="https://avatars3.githubusercontent.com/u/2855350?v=4" width="100px;" alt=""/><br /><sub><b>Stefan Cameron</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Astefcameron" title="Bug reports">🐛</a> <a href="#infra-stefcameron" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Documentation">📖</a> <a href="#maintenance-stefcameron" title="Maintenance">🚧</a></td>
|
|
222
|
+
<td align="center"><a href="https://github.com/liunate"><img src="https://avatars2.githubusercontent.com/u/38996291?v=4" width="100px;" alt=""/><br /><sub><b>Nate Liu</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=liunate" title="Tests">⚠️</a></td>
|
|
223
|
+
<td align="center"><a href="https://github.com/sadick254"><img src="https://avatars2.githubusercontent.com/u/5238135?v=4" width="100px;" alt=""/><br /><sub><b>Sadick</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Documentation">📖</a></td>
|
|
224
|
+
<td align="center"><a href="https://github.com/michael-ar"><img src="https://avatars3.githubusercontent.com/u/18557997?v=4" width="100px;" alt=""/><br /><sub><b>Michael Reynolds</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Amichael-ar" title="Bug reports">🐛</a></td>
|
|
225
|
+
<td align="center"><a href="https://seanmcp.com/"><img src="https://avatars1.githubusercontent.com/u/6360367?v=4" width="100px;" alt=""/><br /><sub><b>Sean McPherson</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Documentation">📖</a></td>
|
|
226
|
+
<td align="center"><a href="https://github.com/features/security"><img src="https://avatars1.githubusercontent.com/u/27347476?v=4" width="100px;" alt=""/><br /><sub><b>Dependabot</b></sub></a><br /><a href="#maintenance-dependabot" title="Maintenance">🚧</a></td>
|
|
227
|
+
</tr>
|
|
228
|
+
<tr>
|
|
229
|
+
<td align="center"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4" width="100px;" alt=""/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3ASlapbox" title="Bug reports">🐛</a></td>
|
|
230
|
+
<td align="center"><a href="https://github.com/bparish628"><img src="https://avatars1.githubusercontent.com/u/8492971?v=4" width="100px;" alt=""/><br /><sub><b>Benjamin Parish</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Abparish628" title="Bug reports">🐛</a></td>
|
|
231
|
+
<td align="center"><a href="https://scottblinch.me/"><img src="https://avatars2.githubusercontent.com/u/4682114?v=4" width="100px;" alt=""/><br /><sub><b>Scott Blinch</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=scottblinch" title="Documentation">📖</a></td>
|
|
232
|
+
<td align="center"><a href="https://clintgoodman.com"><img src="https://avatars3.githubusercontent.com/u/5473697?v=4" width="100px;" alt=""/><br /><sub><b>Clint Goodman</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Documentation">📖</a> <a href="#example-cgood92" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Tests">⚠️</a></td>
|
|
216
233
|
</tr>
|
|
217
234
|
</table>
|
|
218
235
|
|
|
219
|
-
<!-- markdownlint-
|
|
236
|
+
<!-- markdownlint-enable -->
|
|
220
237
|
<!-- prettier-ignore-end -->
|
|
221
|
-
|
|
222
238
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
package/dist/focus-trap.esm.js
CHANGED
|
@@ -1,23 +1,74 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* focus-trap 6.
|
|
2
|
+
* focus-trap 6.2.0
|
|
3
3
|
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
|
4
4
|
*/
|
|
5
5
|
import { isFocusable, tabbable } from 'tabbable';
|
|
6
6
|
|
|
7
|
+
function _defineProperty(obj, key, value) {
|
|
8
|
+
if (key in obj) {
|
|
9
|
+
Object.defineProperty(obj, key, {
|
|
10
|
+
value: value,
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
obj[key] = value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return obj;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function ownKeys(object, enumerableOnly) {
|
|
23
|
+
var keys = Object.keys(object);
|
|
24
|
+
|
|
25
|
+
if (Object.getOwnPropertySymbols) {
|
|
26
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
27
|
+
if (enumerableOnly) symbols = symbols.filter(function (sym) {
|
|
28
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
29
|
+
});
|
|
30
|
+
keys.push.apply(keys, symbols);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return keys;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _objectSpread2(target) {
|
|
37
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
38
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
39
|
+
|
|
40
|
+
if (i % 2) {
|
|
41
|
+
ownKeys(Object(source), true).forEach(function (key) {
|
|
42
|
+
_defineProperty(target, key, source[key]);
|
|
43
|
+
});
|
|
44
|
+
} else if (Object.getOwnPropertyDescriptors) {
|
|
45
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
46
|
+
} else {
|
|
47
|
+
ownKeys(Object(source)).forEach(function (key) {
|
|
48
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
|
|
7
56
|
var activeFocusDelay;
|
|
8
57
|
|
|
9
|
-
var activeFocusTraps =
|
|
58
|
+
var activeFocusTraps = function () {
|
|
10
59
|
var trapQueue = [];
|
|
11
60
|
return {
|
|
12
|
-
activateTrap: function (trap) {
|
|
61
|
+
activateTrap: function activateTrap(trap) {
|
|
13
62
|
if (trapQueue.length > 0) {
|
|
14
63
|
var activeTrap = trapQueue[trapQueue.length - 1];
|
|
64
|
+
|
|
15
65
|
if (activeTrap !== trap) {
|
|
16
66
|
activeTrap.pause();
|
|
17
67
|
}
|
|
18
68
|
}
|
|
19
69
|
|
|
20
70
|
var trapIndex = trapQueue.indexOf(trap);
|
|
71
|
+
|
|
21
72
|
if (trapIndex === -1) {
|
|
22
73
|
trapQueue.push(trap);
|
|
23
74
|
} else {
|
|
@@ -26,9 +77,9 @@ var activeFocusTraps = (function () {
|
|
|
26
77
|
trapQueue.push(trap);
|
|
27
78
|
}
|
|
28
79
|
},
|
|
29
|
-
|
|
30
|
-
deactivateTrap: function (trap) {
|
|
80
|
+
deactivateTrap: function deactivateTrap(trap) {
|
|
31
81
|
var trapIndex = trapQueue.indexOf(trap);
|
|
82
|
+
|
|
32
83
|
if (trapIndex !== -1) {
|
|
33
84
|
trapQueue.splice(trapIndex, 1);
|
|
34
85
|
}
|
|
@@ -36,53 +87,60 @@ var activeFocusTraps = (function () {
|
|
|
36
87
|
if (trapQueue.length > 0) {
|
|
37
88
|
trapQueue[trapQueue.length - 1].unpause();
|
|
38
89
|
}
|
|
39
|
-
}
|
|
90
|
+
}
|
|
40
91
|
};
|
|
41
|
-
}
|
|
92
|
+
}();
|
|
42
93
|
|
|
43
|
-
function createFocusTrap(
|
|
94
|
+
function createFocusTrap(elements, userOptions) {
|
|
44
95
|
var doc = document;
|
|
45
|
-
var container =
|
|
46
|
-
typeof element === 'string' ? doc.querySelector(element) : element;
|
|
47
96
|
|
|
48
|
-
var config = {
|
|
97
|
+
var config = _objectSpread2({
|
|
49
98
|
returnFocusOnDeactivate: true,
|
|
50
99
|
escapeDeactivates: true,
|
|
51
|
-
delayInitialFocus: true
|
|
52
|
-
|
|
53
|
-
};
|
|
100
|
+
delayInitialFocus: true
|
|
101
|
+
}, userOptions);
|
|
54
102
|
|
|
55
103
|
var state = {
|
|
56
|
-
|
|
57
|
-
|
|
104
|
+
// @type {Array<HTMLElement>}
|
|
105
|
+
containers: [],
|
|
106
|
+
// @type {{ firstTabbableNode: HTMLElement, lastTabbableNode: HTMLElement }}
|
|
107
|
+
tabbableGroups: [],
|
|
58
108
|
nodeFocusedBeforeActivation: null,
|
|
59
109
|
mostRecentlyFocusedNode: null,
|
|
60
110
|
active: false,
|
|
61
|
-
paused: false
|
|
111
|
+
paused: false
|
|
62
112
|
};
|
|
63
|
-
|
|
64
113
|
var trap = {
|
|
65
114
|
activate: activate,
|
|
66
115
|
deactivate: deactivate,
|
|
67
116
|
pause: pause,
|
|
68
117
|
unpause: unpause,
|
|
118
|
+
updateContainerElements: updateContainerElements
|
|
69
119
|
};
|
|
70
|
-
|
|
120
|
+
updateContainerElements(elements);
|
|
71
121
|
return trap;
|
|
72
122
|
|
|
123
|
+
function updateContainerElements(containerElements) {
|
|
124
|
+
var elementsAsArray = [].concat(containerElements).filter(Boolean);
|
|
125
|
+
state.containers = elementsAsArray.map(function (element) {
|
|
126
|
+
return typeof element === 'string' ? doc.querySelector(element) : element;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (state.active) {
|
|
130
|
+
updateTabbableNodes();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return trap;
|
|
134
|
+
}
|
|
135
|
+
|
|
73
136
|
function activate(activateOptions) {
|
|
74
137
|
if (state.active) return;
|
|
75
|
-
|
|
76
138
|
updateTabbableNodes();
|
|
77
|
-
|
|
78
139
|
state.active = true;
|
|
79
140
|
state.paused = false;
|
|
80
141
|
state.nodeFocusedBeforeActivation = doc.activeElement;
|
|
142
|
+
var onActivate = activateOptions && activateOptions.onActivate ? activateOptions.onActivate : config.onActivate;
|
|
81
143
|
|
|
82
|
-
var onActivate =
|
|
83
|
-
activateOptions && activateOptions.onActivate
|
|
84
|
-
? activateOptions.onActivate
|
|
85
|
-
: config.onActivate;
|
|
86
144
|
if (onActivate) {
|
|
87
145
|
onActivate();
|
|
88
146
|
}
|
|
@@ -93,27 +151,19 @@ function createFocusTrap(element, userOptions) {
|
|
|
93
151
|
|
|
94
152
|
function deactivate(deactivateOptions) {
|
|
95
153
|
if (!state.active) return;
|
|
96
|
-
|
|
97
154
|
clearTimeout(activeFocusDelay);
|
|
98
|
-
|
|
99
155
|
removeListeners();
|
|
100
156
|
state.active = false;
|
|
101
157
|
state.paused = false;
|
|
102
|
-
|
|
103
158
|
activeFocusTraps.deactivateTrap(trap);
|
|
159
|
+
var onDeactivate = deactivateOptions && deactivateOptions.onDeactivate !== undefined ? deactivateOptions.onDeactivate : config.onDeactivate;
|
|
104
160
|
|
|
105
|
-
var onDeactivate =
|
|
106
|
-
deactivateOptions && deactivateOptions.onDeactivate !== undefined
|
|
107
|
-
? deactivateOptions.onDeactivate
|
|
108
|
-
: config.onDeactivate;
|
|
109
161
|
if (onDeactivate) {
|
|
110
162
|
onDeactivate();
|
|
111
163
|
}
|
|
112
164
|
|
|
113
|
-
var returnFocus =
|
|
114
|
-
|
|
115
|
-
? deactivateOptions.returnFocus
|
|
116
|
-
: config.returnFocusOnDeactivate;
|
|
165
|
+
var returnFocus = deactivateOptions && deactivateOptions.returnFocus !== undefined ? deactivateOptions.returnFocus : config.returnFocusOnDeactivate;
|
|
166
|
+
|
|
117
167
|
if (returnFocus) {
|
|
118
168
|
delay(function () {
|
|
119
169
|
tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
|
|
@@ -124,100 +174,101 @@ function createFocusTrap(element, userOptions) {
|
|
|
124
174
|
}
|
|
125
175
|
|
|
126
176
|
function pause() {
|
|
127
|
-
if (state.paused || !state.active) return;
|
|
177
|
+
if (state.paused || !state.active) return trap;
|
|
128
178
|
state.paused = true;
|
|
129
179
|
removeListeners();
|
|
180
|
+
return trap;
|
|
130
181
|
}
|
|
131
182
|
|
|
132
183
|
function unpause() {
|
|
133
|
-
if (!state.paused || !state.active) return;
|
|
184
|
+
if (!state.paused || !state.active) return trap;
|
|
134
185
|
state.paused = false;
|
|
135
186
|
updateTabbableNodes();
|
|
136
187
|
addListeners();
|
|
188
|
+
return trap;
|
|
137
189
|
}
|
|
138
190
|
|
|
139
191
|
function addListeners() {
|
|
140
|
-
if (!state.active) return;
|
|
141
|
-
|
|
142
|
-
// There can be only one listening focus trap at a time
|
|
143
|
-
activeFocusTraps.activateTrap(trap);
|
|
192
|
+
if (!state.active) return; // There can be only one listening focus trap at a time
|
|
144
193
|
|
|
145
|
-
// Delay ensures that the focused element doesn't capture the event
|
|
194
|
+
activeFocusTraps.activateTrap(trap); // Delay ensures that the focused element doesn't capture the event
|
|
146
195
|
// that caused the focus trap activation.
|
|
147
|
-
activeFocusDelay = config.delayInitialFocus
|
|
148
|
-
? delay(function () {
|
|
149
|
-
tryFocus(getInitialFocusNode());
|
|
150
|
-
})
|
|
151
|
-
: tryFocus(getInitialFocusNode());
|
|
152
196
|
|
|
197
|
+
activeFocusDelay = config.delayInitialFocus ? delay(function () {
|
|
198
|
+
tryFocus(getInitialFocusNode());
|
|
199
|
+
}) : tryFocus(getInitialFocusNode());
|
|
153
200
|
doc.addEventListener('focusin', checkFocusIn, true);
|
|
154
201
|
doc.addEventListener('mousedown', checkPointerDown, {
|
|
155
202
|
capture: true,
|
|
156
|
-
passive: false
|
|
203
|
+
passive: false
|
|
157
204
|
});
|
|
158
205
|
doc.addEventListener('touchstart', checkPointerDown, {
|
|
159
206
|
capture: true,
|
|
160
|
-
passive: false
|
|
207
|
+
passive: false
|
|
161
208
|
});
|
|
162
209
|
doc.addEventListener('click', checkClick, {
|
|
163
210
|
capture: true,
|
|
164
|
-
passive: false
|
|
211
|
+
passive: false
|
|
165
212
|
});
|
|
166
213
|
doc.addEventListener('keydown', checkKey, {
|
|
167
214
|
capture: true,
|
|
168
|
-
passive: false
|
|
215
|
+
passive: false
|
|
169
216
|
});
|
|
170
|
-
|
|
171
217
|
return trap;
|
|
172
218
|
}
|
|
173
219
|
|
|
174
220
|
function removeListeners() {
|
|
175
221
|
if (!state.active) return;
|
|
176
|
-
|
|
177
222
|
doc.removeEventListener('focusin', checkFocusIn, true);
|
|
178
223
|
doc.removeEventListener('mousedown', checkPointerDown, true);
|
|
179
224
|
doc.removeEventListener('touchstart', checkPointerDown, true);
|
|
180
225
|
doc.removeEventListener('click', checkClick, true);
|
|
181
226
|
doc.removeEventListener('keydown', checkKey, true);
|
|
182
|
-
|
|
183
227
|
return trap;
|
|
184
228
|
}
|
|
185
229
|
|
|
186
230
|
function getNodeForOption(optionName) {
|
|
187
231
|
var optionValue = config[optionName];
|
|
188
232
|
var node = optionValue;
|
|
233
|
+
|
|
189
234
|
if (!optionValue) {
|
|
190
235
|
return null;
|
|
191
236
|
}
|
|
237
|
+
|
|
192
238
|
if (typeof optionValue === 'string') {
|
|
193
239
|
node = doc.querySelector(optionValue);
|
|
240
|
+
|
|
194
241
|
if (!node) {
|
|
195
242
|
throw new Error('`' + optionName + '` refers to no known node');
|
|
196
243
|
}
|
|
197
244
|
}
|
|
245
|
+
|
|
198
246
|
if (typeof optionValue === 'function') {
|
|
199
247
|
node = optionValue();
|
|
248
|
+
|
|
200
249
|
if (!node) {
|
|
201
250
|
throw new Error('`' + optionName + '` did not return a node');
|
|
202
251
|
}
|
|
203
252
|
}
|
|
253
|
+
|
|
204
254
|
return node;
|
|
205
255
|
}
|
|
206
256
|
|
|
207
257
|
function getInitialFocusNode() {
|
|
208
258
|
var node;
|
|
259
|
+
|
|
209
260
|
if (getNodeForOption('initialFocus') !== null) {
|
|
210
261
|
node = getNodeForOption('initialFocus');
|
|
211
|
-
} else if (
|
|
262
|
+
} else if (containersContain(doc.activeElement)) {
|
|
212
263
|
node = doc.activeElement;
|
|
213
264
|
} else {
|
|
214
|
-
|
|
265
|
+
var firstTabbableGroup = state.tabbableGroups[0];
|
|
266
|
+
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
|
|
267
|
+
node = firstTabbableNode || getNodeForOption('fallbackFocus');
|
|
215
268
|
}
|
|
216
269
|
|
|
217
270
|
if (!node) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
'Your focus-trap needs to have at least one focusable element'
|
|
220
|
-
);
|
|
271
|
+
throw new Error('Your focus-trap needs to have at least one focusable element');
|
|
221
272
|
}
|
|
222
273
|
|
|
223
274
|
return node;
|
|
@@ -226,12 +277,12 @@ function createFocusTrap(element, userOptions) {
|
|
|
226
277
|
function getReturnFocusNode(previousActiveElement) {
|
|
227
278
|
var node = getNodeForOption('setReturnFocus');
|
|
228
279
|
return node ? node : previousActiveElement;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// This needs to be done on mousedown and touchstart instead of click
|
|
280
|
+
} // This needs to be done on mousedown and touchstart instead of click
|
|
232
281
|
// so that it precedes the focus event.
|
|
282
|
+
|
|
283
|
+
|
|
233
284
|
function checkPointerDown(e) {
|
|
234
|
-
if (
|
|
285
|
+
if (containersContain(e.target)) {
|
|
235
286
|
// allow the click since it ocurred inside the trap
|
|
236
287
|
return;
|
|
237
288
|
}
|
|
@@ -250,34 +301,30 @@ function createFocusTrap(element, userOptions) {
|
|
|
250
301
|
// that was clicked, whether it's focusable or not; by setting
|
|
251
302
|
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
|
|
252
303
|
// on activation (or the configured `setReturnFocus` node)
|
|
253
|
-
returnFocus: config.returnFocusOnDeactivate && !isFocusable(e.target)
|
|
304
|
+
returnFocus: config.returnFocusOnDeactivate && !isFocusable(e.target)
|
|
254
305
|
});
|
|
255
306
|
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// This is needed for mobile devices.
|
|
307
|
+
} // This is needed for mobile devices.
|
|
259
308
|
// (If we'll only let `click` events through,
|
|
260
309
|
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
? config.allowOutsideClick
|
|
265
|
-
: config.allowOutsideClick(e))
|
|
266
|
-
) {
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
if (config.allowOutsideClick && (typeof config.allowOutsideClick === 'boolean' ? config.allowOutsideClick : config.allowOutsideClick(e))) {
|
|
267
313
|
// allow the click outside the trap to take place
|
|
268
314
|
return;
|
|
269
|
-
}
|
|
315
|
+
} // otherwise, prevent the click
|
|
316
|
+
|
|
270
317
|
|
|
271
|
-
// otherwise, prevent the click
|
|
272
318
|
e.preventDefault();
|
|
273
|
-
}
|
|
319
|
+
} // In case focus escapes the trap for some strange reason, pull it back in.
|
|
320
|
+
|
|
274
321
|
|
|
275
|
-
// In case focus escapes the trap for some strange reason, pull it back in.
|
|
276
322
|
function checkFocusIn(e) {
|
|
277
323
|
// In Firefox when you Tab out of an iframe the Document is briefly focused.
|
|
278
|
-
if (
|
|
324
|
+
if (containersContain(e.target) || e.target instanceof Document) {
|
|
279
325
|
return;
|
|
280
326
|
}
|
|
327
|
+
|
|
281
328
|
e.stopImmediatePropagation();
|
|
282
329
|
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
|
|
283
330
|
}
|
|
@@ -288,72 +335,101 @@ function createFocusTrap(element, userOptions) {
|
|
|
288
335
|
deactivate();
|
|
289
336
|
return;
|
|
290
337
|
}
|
|
338
|
+
|
|
291
339
|
if (isTabEvent(e)) {
|
|
292
340
|
checkTab(e);
|
|
293
341
|
return;
|
|
294
342
|
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Hijack Tab events on the first and last focusable nodes of the trap,
|
|
343
|
+
} // Hijack Tab events on the first and last focusable nodes of the trap,
|
|
298
344
|
// in order to prevent focus from escaping. If it escapes for even a
|
|
299
345
|
// moment it can end up scrolling the page and causing confusion so we
|
|
300
346
|
// kind of need to capture the action at the keydown phase.
|
|
347
|
+
|
|
348
|
+
|
|
301
349
|
function checkTab(e) {
|
|
302
350
|
updateTabbableNodes();
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
351
|
+
var destinationNode = null;
|
|
352
|
+
|
|
353
|
+
if (e.shiftKey) {
|
|
354
|
+
var startOfGroupIndex = state.tabbableGroups.findIndex(function (_ref) {
|
|
355
|
+
var firstTabbableNode = _ref.firstTabbableNode;
|
|
356
|
+
return e.target === firstTabbableNode;
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (startOfGroupIndex >= 0) {
|
|
360
|
+
var destinationGroupIndex = startOfGroupIndex === 0 ? state.tabbableGroups.length - 1 : startOfGroupIndex - 1;
|
|
361
|
+
var destinationGroup = state.tabbableGroups[destinationGroupIndex];
|
|
362
|
+
destinationNode = destinationGroup.lastTabbableNode;
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
var lastOfGroupIndex = state.tabbableGroups.findIndex(function (_ref2) {
|
|
366
|
+
var lastTabbableNode = _ref2.lastTabbableNode;
|
|
367
|
+
return e.target === lastTabbableNode;
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (lastOfGroupIndex >= 0) {
|
|
371
|
+
var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
|
|
372
|
+
|
|
373
|
+
var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
|
|
374
|
+
destinationNode = _destinationGroup.firstTabbableNode;
|
|
375
|
+
}
|
|
307
376
|
}
|
|
308
|
-
|
|
377
|
+
|
|
378
|
+
if (destinationNode) {
|
|
309
379
|
e.preventDefault();
|
|
310
|
-
tryFocus(
|
|
311
|
-
return;
|
|
380
|
+
tryFocus(destinationNode);
|
|
312
381
|
}
|
|
313
382
|
}
|
|
314
383
|
|
|
315
384
|
function checkClick(e) {
|
|
316
385
|
if (config.clickOutsideDeactivates) return;
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
(typeof config.allowOutsideClick === 'boolean'
|
|
321
|
-
? config.allowOutsideClick
|
|
322
|
-
: config.allowOutsideClick(e))
|
|
323
|
-
) {
|
|
386
|
+
if (containersContain(e.target)) return;
|
|
387
|
+
|
|
388
|
+
if (config.allowOutsideClick && (typeof config.allowOutsideClick === 'boolean' ? config.allowOutsideClick : config.allowOutsideClick(e))) {
|
|
324
389
|
return;
|
|
325
390
|
}
|
|
391
|
+
|
|
326
392
|
e.preventDefault();
|
|
327
393
|
e.stopImmediatePropagation();
|
|
328
394
|
}
|
|
329
395
|
|
|
330
396
|
function updateTabbableNodes() {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
397
|
+
state.tabbableGroups = state.containers.map(function (container) {
|
|
398
|
+
var tabbableNodes = tabbable(container);
|
|
399
|
+
return {
|
|
400
|
+
firstTabbableNode: tabbableNodes[0],
|
|
401
|
+
lastTabbableNode: tabbableNodes[tabbableNodes.length - 1]
|
|
402
|
+
};
|
|
403
|
+
});
|
|
335
404
|
}
|
|
336
405
|
|
|
337
406
|
function tryFocus(node) {
|
|
338
407
|
if (node === doc.activeElement) return;
|
|
408
|
+
|
|
339
409
|
if (!node || !node.focus) {
|
|
340
410
|
tryFocus(getInitialFocusNode());
|
|
341
411
|
return;
|
|
342
412
|
}
|
|
343
|
-
|
|
413
|
+
|
|
414
|
+
node.focus({
|
|
415
|
+
preventScroll: !!config.preventScroll
|
|
416
|
+
});
|
|
344
417
|
state.mostRecentlyFocusedNode = node;
|
|
418
|
+
|
|
345
419
|
if (isSelectableInput(node)) {
|
|
346
420
|
node.select();
|
|
347
421
|
}
|
|
348
422
|
}
|
|
423
|
+
|
|
424
|
+
function containersContain(element) {
|
|
425
|
+
return state.containers.some(function (container) {
|
|
426
|
+
return container.contains(element);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
349
429
|
}
|
|
350
430
|
|
|
351
431
|
function isSelectableInput(node) {
|
|
352
|
-
return (
|
|
353
|
-
node.tagName &&
|
|
354
|
-
node.tagName.toLowerCase() === 'input' &&
|
|
355
|
-
typeof node.select === 'function'
|
|
356
|
-
);
|
|
432
|
+
return node.tagName && node.tagName.toLowerCase() === 'input' && typeof node.select === 'function';
|
|
357
433
|
}
|
|
358
434
|
|
|
359
435
|
function isEscapeEvent(e) {
|