focus-trap 6.2.0 → 6.3.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 +33 -0
- package/README.md +47 -29
- package/SECURITY.md +37 -0
- package/dist/focus-trap.esm.js +316 -215
- package/dist/focus-trap.esm.js.map +1 -1
- package/dist/focus-trap.esm.min.js +2 -2
- package/dist/focus-trap.esm.min.js.map +1 -1
- package/dist/focus-trap.js +315 -214
- package/dist/focus-trap.js.map +1 -1
- package/dist/focus-trap.min.js +2 -2
- package/dist/focus-trap.min.js.map +1 -1
- package/dist/focus-trap.umd.js +315 -214
- package/dist/focus-trap.umd.js.map +1 -1
- package/dist/focus-trap.umd.min.js +2 -2
- package/dist/focus-trap.umd.min.js.map +1 -1
- package/index.d.ts +7 -6
- package/index.js +381 -271
- package/package.json +35 -30
package/dist/focus-trap.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* focus-trap 6.
|
|
2
|
+
* focus-trap 6.3.0
|
|
3
3
|
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { tabbable, isFocusable } from 'tabbable';
|
|
6
6
|
|
|
7
7
|
function _defineProperty(obj, key, value) {
|
|
8
8
|
if (key in obj) {
|
|
@@ -91,7 +91,54 @@ var activeFocusTraps = function () {
|
|
|
91
91
|
};
|
|
92
92
|
}();
|
|
93
93
|
|
|
94
|
-
function
|
|
94
|
+
var isSelectableInput = function isSelectableInput(node) {
|
|
95
|
+
return node.tagName && node.tagName.toLowerCase() === 'input' && typeof node.select === 'function';
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
var isEscapeEvent = function isEscapeEvent(e) {
|
|
99
|
+
return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
var isTabEvent = function isTabEvent(e) {
|
|
103
|
+
return e.key === 'Tab' || e.keyCode === 9;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
var delay = function delay(fn) {
|
|
107
|
+
return setTimeout(fn, 0);
|
|
108
|
+
}; // Array.find/findIndex() are not supported on IE; this replicates enough
|
|
109
|
+
// of Array.findIndex() for our needs
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
var findIndex = function findIndex(arr, fn) {
|
|
113
|
+
var idx = -1;
|
|
114
|
+
arr.every(function (value, i) {
|
|
115
|
+
if (fn(value)) {
|
|
116
|
+
idx = i;
|
|
117
|
+
return false; // break
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return true; // next
|
|
121
|
+
});
|
|
122
|
+
return idx;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Get an option's value when it could be a plain value, or a handler that provides
|
|
126
|
+
* the value.
|
|
127
|
+
* @param {*} value Option's value to check.
|
|
128
|
+
* @param {...*} [params] Any parameters to pass to the handler, if `value` is a function.
|
|
129
|
+
* @returns {*} The `value`, or the handler's returned value.
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
var valueOrHandler = function valueOrHandler(value) {
|
|
134
|
+
for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
135
|
+
params[_key - 1] = arguments[_key];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return typeof value === 'function' ? value.apply(void 0, params) : value;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
95
142
|
var doc = document;
|
|
96
143
|
|
|
97
144
|
var config = _objectSpread2({
|
|
@@ -103,143 +150,41 @@ function createFocusTrap(elements, userOptions) {
|
|
|
103
150
|
var state = {
|
|
104
151
|
// @type {Array<HTMLElement>}
|
|
105
152
|
containers: [],
|
|
106
|
-
//
|
|
153
|
+
// list of objects identifying the first and last tabbable nodes in all containers/groups in
|
|
154
|
+
// the trap
|
|
155
|
+
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap
|
|
156
|
+
// is active, but the trap should never get to a state where there isn't at least one group
|
|
157
|
+
// with at least one tabbable node in it (that would lead to an error condition that would
|
|
158
|
+
// result in an error being thrown)
|
|
159
|
+
// @type {Array<{ container: HTMLElement, firstTabbableNode: HTMLElement|null, lastTabbableNode: HTMLElement|null }>}
|
|
107
160
|
tabbableGroups: [],
|
|
108
161
|
nodeFocusedBeforeActivation: null,
|
|
109
162
|
mostRecentlyFocusedNode: null,
|
|
110
163
|
active: false,
|
|
111
164
|
paused: false
|
|
112
165
|
};
|
|
113
|
-
var trap
|
|
114
|
-
activate: activate,
|
|
115
|
-
deactivate: deactivate,
|
|
116
|
-
pause: pause,
|
|
117
|
-
unpause: unpause,
|
|
118
|
-
updateContainerElements: updateContainerElements
|
|
119
|
-
};
|
|
120
|
-
updateContainerElements(elements);
|
|
121
|
-
return trap;
|
|
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
|
-
|
|
136
|
-
function activate(activateOptions) {
|
|
137
|
-
if (state.active) return;
|
|
138
|
-
updateTabbableNodes();
|
|
139
|
-
state.active = true;
|
|
140
|
-
state.paused = false;
|
|
141
|
-
state.nodeFocusedBeforeActivation = doc.activeElement;
|
|
142
|
-
var onActivate = activateOptions && activateOptions.onActivate ? activateOptions.onActivate : config.onActivate;
|
|
143
|
-
|
|
144
|
-
if (onActivate) {
|
|
145
|
-
onActivate();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
addListeners();
|
|
149
|
-
return trap;
|
|
150
|
-
}
|
|
166
|
+
var trap; // eslint-disable-line prefer-const -- some private functions reference it, and its methods reference private functions, so we must declare here and define later
|
|
151
167
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
removeListeners();
|
|
156
|
-
state.active = false;
|
|
157
|
-
state.paused = false;
|
|
158
|
-
activeFocusTraps.deactivateTrap(trap);
|
|
159
|
-
var onDeactivate = deactivateOptions && deactivateOptions.onDeactivate !== undefined ? deactivateOptions.onDeactivate : config.onDeactivate;
|
|
160
|
-
|
|
161
|
-
if (onDeactivate) {
|
|
162
|
-
onDeactivate();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
var returnFocus = deactivateOptions && deactivateOptions.returnFocus !== undefined ? deactivateOptions.returnFocus : config.returnFocusOnDeactivate;
|
|
166
|
-
|
|
167
|
-
if (returnFocus) {
|
|
168
|
-
delay(function () {
|
|
169
|
-
tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return trap;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function pause() {
|
|
177
|
-
if (state.paused || !state.active) return trap;
|
|
178
|
-
state.paused = true;
|
|
179
|
-
removeListeners();
|
|
180
|
-
return trap;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function unpause() {
|
|
184
|
-
if (!state.paused || !state.active) return trap;
|
|
185
|
-
state.paused = false;
|
|
186
|
-
updateTabbableNodes();
|
|
187
|
-
addListeners();
|
|
188
|
-
return trap;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function addListeners() {
|
|
192
|
-
if (!state.active) return; // There can be only one listening focus trap at a time
|
|
193
|
-
|
|
194
|
-
activeFocusTraps.activateTrap(trap); // Delay ensures that the focused element doesn't capture the event
|
|
195
|
-
// that caused the focus trap activation.
|
|
196
|
-
|
|
197
|
-
activeFocusDelay = config.delayInitialFocus ? delay(function () {
|
|
198
|
-
tryFocus(getInitialFocusNode());
|
|
199
|
-
}) : tryFocus(getInitialFocusNode());
|
|
200
|
-
doc.addEventListener('focusin', checkFocusIn, true);
|
|
201
|
-
doc.addEventListener('mousedown', checkPointerDown, {
|
|
202
|
-
capture: true,
|
|
203
|
-
passive: false
|
|
204
|
-
});
|
|
205
|
-
doc.addEventListener('touchstart', checkPointerDown, {
|
|
206
|
-
capture: true,
|
|
207
|
-
passive: false
|
|
208
|
-
});
|
|
209
|
-
doc.addEventListener('click', checkClick, {
|
|
210
|
-
capture: true,
|
|
211
|
-
passive: false
|
|
212
|
-
});
|
|
213
|
-
doc.addEventListener('keydown', checkKey, {
|
|
214
|
-
capture: true,
|
|
215
|
-
passive: false
|
|
168
|
+
var containersContain = function containersContain(element) {
|
|
169
|
+
return state.containers.some(function (container) {
|
|
170
|
+
return container.contains(element);
|
|
216
171
|
});
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function removeListeners() {
|
|
221
|
-
if (!state.active) return;
|
|
222
|
-
doc.removeEventListener('focusin', checkFocusIn, true);
|
|
223
|
-
doc.removeEventListener('mousedown', checkPointerDown, true);
|
|
224
|
-
doc.removeEventListener('touchstart', checkPointerDown, true);
|
|
225
|
-
doc.removeEventListener('click', checkClick, true);
|
|
226
|
-
doc.removeEventListener('keydown', checkKey, true);
|
|
227
|
-
return trap;
|
|
228
|
-
}
|
|
172
|
+
};
|
|
229
173
|
|
|
230
|
-
function getNodeForOption(optionName) {
|
|
174
|
+
var getNodeForOption = function getNodeForOption(optionName) {
|
|
231
175
|
var optionValue = config[optionName];
|
|
232
|
-
var node = optionValue;
|
|
233
176
|
|
|
234
177
|
if (!optionValue) {
|
|
235
178
|
return null;
|
|
236
179
|
}
|
|
237
180
|
|
|
181
|
+
var node = optionValue;
|
|
182
|
+
|
|
238
183
|
if (typeof optionValue === 'string') {
|
|
239
184
|
node = doc.querySelector(optionValue);
|
|
240
185
|
|
|
241
186
|
if (!node) {
|
|
242
|
-
throw new Error(
|
|
187
|
+
throw new Error("`".concat(optionName, "` refers to no known node"));
|
|
243
188
|
}
|
|
244
189
|
}
|
|
245
190
|
|
|
@@ -247,14 +192,14 @@ function createFocusTrap(elements, userOptions) {
|
|
|
247
192
|
node = optionValue();
|
|
248
193
|
|
|
249
194
|
if (!node) {
|
|
250
|
-
throw new Error(
|
|
195
|
+
throw new Error("`".concat(optionName, "` did not return a node"));
|
|
251
196
|
}
|
|
252
197
|
}
|
|
253
198
|
|
|
254
199
|
return node;
|
|
255
|
-
}
|
|
200
|
+
};
|
|
256
201
|
|
|
257
|
-
function getInitialFocusNode() {
|
|
202
|
+
var getInitialFocusNode = function getInitialFocusNode() {
|
|
258
203
|
var node;
|
|
259
204
|
|
|
260
205
|
if (getNodeForOption('initialFocus') !== null) {
|
|
@@ -272,24 +217,67 @@ function createFocusTrap(elements, userOptions) {
|
|
|
272
217
|
}
|
|
273
218
|
|
|
274
219
|
return node;
|
|
275
|
-
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
var updateTabbableNodes = function updateTabbableNodes() {
|
|
223
|
+
state.tabbableGroups = state.containers.map(function (container) {
|
|
224
|
+
var tabbableNodes = tabbable(container);
|
|
276
225
|
|
|
277
|
-
|
|
226
|
+
if (tabbableNodes.length > 0) {
|
|
227
|
+
return {
|
|
228
|
+
container: container,
|
|
229
|
+
firstTabbableNode: tabbableNodes[0],
|
|
230
|
+
lastTabbableNode: tabbableNodes[tabbableNodes.length - 1]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return undefined;
|
|
235
|
+
}).filter(function (group) {
|
|
236
|
+
return !!group;
|
|
237
|
+
}); // remove groups with no tabbable nodes
|
|
238
|
+
// throw if no groups have tabbable nodes and we don't have a fallback focus node either
|
|
239
|
+
|
|
240
|
+
if (state.tabbableGroups.length <= 0 && !getNodeForOption('fallbackFocus')) {
|
|
241
|
+
throw new Error('Your focus-trap must have at least one container with at least one tabbable node in it at all times');
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
var tryFocus = function tryFocus(node) {
|
|
246
|
+
if (node === doc.activeElement) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!node || !node.focus) {
|
|
251
|
+
tryFocus(getInitialFocusNode());
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
node.focus({
|
|
256
|
+
preventScroll: !!config.preventScroll
|
|
257
|
+
});
|
|
258
|
+
state.mostRecentlyFocusedNode = node;
|
|
259
|
+
|
|
260
|
+
if (isSelectableInput(node)) {
|
|
261
|
+
node.select();
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
var getReturnFocusNode = function getReturnFocusNode(previousActiveElement) {
|
|
278
266
|
var node = getNodeForOption('setReturnFocus');
|
|
279
267
|
return node ? node : previousActiveElement;
|
|
280
|
-
} // This needs to be done on mousedown and touchstart instead of click
|
|
268
|
+
}; // This needs to be done on mousedown and touchstart instead of click
|
|
281
269
|
// so that it precedes the focus event.
|
|
282
270
|
|
|
283
271
|
|
|
284
|
-
function checkPointerDown(e) {
|
|
272
|
+
var checkPointerDown = function checkPointerDown(e) {
|
|
285
273
|
if (containersContain(e.target)) {
|
|
286
274
|
// allow the click since it ocurred inside the trap
|
|
287
275
|
return;
|
|
288
276
|
}
|
|
289
277
|
|
|
290
|
-
if (config.clickOutsideDeactivates) {
|
|
278
|
+
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
|
|
291
279
|
// immediately deactivate the trap
|
|
292
|
-
deactivate({
|
|
280
|
+
trap.deactivate({
|
|
293
281
|
// if, on deactivation, we should return focus to the node originally-focused
|
|
294
282
|
// when the trap was activated (or the configured `setReturnFocus` node),
|
|
295
283
|
// then assume it's also OK to return focus to the outside node that was
|
|
@@ -309,140 +297,253 @@ function createFocusTrap(elements, userOptions) {
|
|
|
309
297
|
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
|
|
310
298
|
|
|
311
299
|
|
|
312
|
-
if (
|
|
300
|
+
if (valueOrHandler(config.allowOutsideClick, e)) {
|
|
313
301
|
// allow the click outside the trap to take place
|
|
314
302
|
return;
|
|
315
303
|
} // otherwise, prevent the click
|
|
316
304
|
|
|
317
305
|
|
|
318
306
|
e.preventDefault();
|
|
319
|
-
} // In case focus escapes the trap for some strange reason, pull it back in.
|
|
307
|
+
}; // In case focus escapes the trap for some strange reason, pull it back in.
|
|
320
308
|
|
|
321
309
|
|
|
322
|
-
function checkFocusIn(e) {
|
|
323
|
-
// In Firefox when you Tab out of an iframe the Document is briefly focused.
|
|
324
|
-
if (containersContain(e.target) || e.target instanceof Document) {
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
310
|
+
var checkFocusIn = function checkFocusIn(e) {
|
|
311
|
+
var targetContained = containersContain(e.target); // In Firefox when you Tab out of an iframe the Document is briefly focused.
|
|
327
312
|
|
|
328
|
-
e.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
e.
|
|
335
|
-
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (isTabEvent(e)) {
|
|
340
|
-
checkTab(e);
|
|
341
|
-
return;
|
|
313
|
+
if (targetContained || e.target instanceof Document) {
|
|
314
|
+
if (targetContained) {
|
|
315
|
+
state.mostRecentlyFocusedNode = e.target;
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
// escaped! pull it back in to where it just left
|
|
319
|
+
e.stopImmediatePropagation();
|
|
320
|
+
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
|
|
342
321
|
}
|
|
343
|
-
} // Hijack Tab events on the first and last focusable nodes of the trap,
|
|
322
|
+
}; // Hijack Tab events on the first and last focusable nodes of the trap,
|
|
344
323
|
// in order to prevent focus from escaping. If it escapes for even a
|
|
345
324
|
// moment it can end up scrolling the page and causing confusion so we
|
|
346
325
|
// kind of need to capture the action at the keydown phase.
|
|
347
326
|
|
|
348
327
|
|
|
349
|
-
function checkTab(e) {
|
|
328
|
+
var checkTab = function checkTab(e) {
|
|
350
329
|
updateTabbableNodes();
|
|
351
330
|
var destinationNode = null;
|
|
352
331
|
|
|
353
|
-
if (
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
332
|
+
if (state.tabbableGroups.length > 0) {
|
|
333
|
+
// make sure the target is actually contained in a group
|
|
334
|
+
var containerIndex = findIndex(state.tabbableGroups, function (_ref) {
|
|
335
|
+
var container = _ref.container;
|
|
336
|
+
return container.contains(e.target);
|
|
357
337
|
});
|
|
358
338
|
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
339
|
+
if (containerIndex < 0) {
|
|
340
|
+
// target not found in any group: quite possible focus has escaped the trap,
|
|
341
|
+
// so bring it back in to...
|
|
342
|
+
if (e.shiftKey) {
|
|
343
|
+
// ...the last node in the last group
|
|
344
|
+
destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;
|
|
345
|
+
} else {
|
|
346
|
+
// ...the first node in the first group
|
|
347
|
+
destinationNode = state.tabbableGroups[0].firstTabbableNode;
|
|
348
|
+
}
|
|
349
|
+
} else if (e.shiftKey) {
|
|
350
|
+
// REVERSE
|
|
351
|
+
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref2) {
|
|
352
|
+
var firstTabbableNode = _ref2.firstTabbableNode;
|
|
353
|
+
return e.target === firstTabbableNode;
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (startOfGroupIndex >= 0) {
|
|
357
|
+
var destinationGroupIndex = startOfGroupIndex === 0 ? state.tabbableGroups.length - 1 : startOfGroupIndex - 1;
|
|
358
|
+
var destinationGroup = state.tabbableGroups[destinationGroupIndex];
|
|
359
|
+
destinationNode = destinationGroup.lastTabbableNode;
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
// FORWARD
|
|
363
|
+
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
|
|
364
|
+
var lastTabbableNode = _ref3.lastTabbableNode;
|
|
365
|
+
return e.target === lastTabbableNode;
|
|
366
|
+
});
|
|
369
367
|
|
|
370
|
-
|
|
371
|
-
|
|
368
|
+
if (lastOfGroupIndex >= 0) {
|
|
369
|
+
var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
|
|
372
370
|
|
|
373
|
-
|
|
374
|
-
|
|
371
|
+
var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
|
|
372
|
+
destinationNode = _destinationGroup.firstTabbableNode;
|
|
373
|
+
}
|
|
375
374
|
}
|
|
375
|
+
} else {
|
|
376
|
+
destinationNode = getNodeForOption('fallbackFocus');
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
if (destinationNode) {
|
|
379
380
|
e.preventDefault();
|
|
380
381
|
tryFocus(destinationNode);
|
|
381
382
|
}
|
|
382
|
-
}
|
|
383
|
+
};
|
|
383
384
|
|
|
384
|
-
function
|
|
385
|
-
if (config.
|
|
386
|
-
|
|
385
|
+
var checkKey = function checkKey(e) {
|
|
386
|
+
if (config.escapeDeactivates !== false && isEscapeEvent(e)) {
|
|
387
|
+
e.preventDefault();
|
|
388
|
+
trap.deactivate();
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
387
391
|
|
|
388
|
-
if (
|
|
392
|
+
if (isTabEvent(e)) {
|
|
393
|
+
checkTab(e);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
var checkClick = function checkClick(e) {
|
|
399
|
+
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (containersContain(e.target)) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (valueOrHandler(config.allowOutsideClick, e)) {
|
|
389
408
|
return;
|
|
390
409
|
}
|
|
391
410
|
|
|
392
411
|
e.preventDefault();
|
|
393
412
|
e.stopImmediatePropagation();
|
|
394
|
-
}
|
|
413
|
+
}; //
|
|
414
|
+
// EVENT LISTENERS
|
|
415
|
+
//
|
|
395
416
|
|
|
396
|
-
function updateTabbableNodes() {
|
|
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
|
-
});
|
|
404
|
-
}
|
|
405
417
|
|
|
406
|
-
function
|
|
407
|
-
if (
|
|
418
|
+
var addListeners = function addListeners() {
|
|
419
|
+
if (!state.active) {
|
|
420
|
+
return;
|
|
421
|
+
} // There can be only one listening focus trap at a time
|
|
408
422
|
|
|
409
|
-
|
|
423
|
+
|
|
424
|
+
activeFocusTraps.activateTrap(trap); // Delay ensures that the focused element doesn't capture the event
|
|
425
|
+
// that caused the focus trap activation.
|
|
426
|
+
|
|
427
|
+
activeFocusDelay = config.delayInitialFocus ? delay(function () {
|
|
410
428
|
tryFocus(getInitialFocusNode());
|
|
429
|
+
}) : tryFocus(getInitialFocusNode());
|
|
430
|
+
doc.addEventListener('focusin', checkFocusIn, true);
|
|
431
|
+
doc.addEventListener('mousedown', checkPointerDown, {
|
|
432
|
+
capture: true,
|
|
433
|
+
passive: false
|
|
434
|
+
});
|
|
435
|
+
doc.addEventListener('touchstart', checkPointerDown, {
|
|
436
|
+
capture: true,
|
|
437
|
+
passive: false
|
|
438
|
+
});
|
|
439
|
+
doc.addEventListener('click', checkClick, {
|
|
440
|
+
capture: true,
|
|
441
|
+
passive: false
|
|
442
|
+
});
|
|
443
|
+
doc.addEventListener('keydown', checkKey, {
|
|
444
|
+
capture: true,
|
|
445
|
+
passive: false
|
|
446
|
+
});
|
|
447
|
+
return trap;
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
var removeListeners = function removeListeners() {
|
|
451
|
+
if (!state.active) {
|
|
411
452
|
return;
|
|
412
453
|
}
|
|
413
454
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
455
|
+
doc.removeEventListener('focusin', checkFocusIn, true);
|
|
456
|
+
doc.removeEventListener('mousedown', checkPointerDown, true);
|
|
457
|
+
doc.removeEventListener('touchstart', checkPointerDown, true);
|
|
458
|
+
doc.removeEventListener('click', checkClick, true);
|
|
459
|
+
doc.removeEventListener('keydown', checkKey, true);
|
|
460
|
+
return trap;
|
|
461
|
+
}; //
|
|
462
|
+
// TRAP DEFINITION
|
|
463
|
+
//
|
|
418
464
|
|
|
419
|
-
if (isSelectableInput(node)) {
|
|
420
|
-
node.select();
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
465
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
466
|
+
trap = {
|
|
467
|
+
activate: function activate(activateOptions) {
|
|
468
|
+
if (state.active) {
|
|
469
|
+
return this;
|
|
470
|
+
}
|
|
430
471
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
472
|
+
updateTabbableNodes();
|
|
473
|
+
state.active = true;
|
|
474
|
+
state.paused = false;
|
|
475
|
+
state.nodeFocusedBeforeActivation = doc.activeElement;
|
|
476
|
+
var onActivate = activateOptions && activateOptions.onActivate ? activateOptions.onActivate : config.onActivate;
|
|
434
477
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
478
|
+
if (onActivate) {
|
|
479
|
+
onActivate();
|
|
480
|
+
}
|
|
438
481
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
482
|
+
addListeners();
|
|
483
|
+
return this;
|
|
484
|
+
},
|
|
485
|
+
deactivate: function deactivate(deactivateOptions) {
|
|
486
|
+
if (!state.active) {
|
|
487
|
+
return this;
|
|
488
|
+
}
|
|
442
489
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
490
|
+
clearTimeout(activeFocusDelay);
|
|
491
|
+
removeListeners();
|
|
492
|
+
state.active = false;
|
|
493
|
+
state.paused = false;
|
|
494
|
+
activeFocusTraps.deactivateTrap(trap);
|
|
495
|
+
var onDeactivate = deactivateOptions && deactivateOptions.onDeactivate !== undefined ? deactivateOptions.onDeactivate : config.onDeactivate;
|
|
496
|
+
|
|
497
|
+
if (onDeactivate) {
|
|
498
|
+
onDeactivate();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
var returnFocus = deactivateOptions && deactivateOptions.returnFocus !== undefined ? deactivateOptions.returnFocus : config.returnFocusOnDeactivate;
|
|
502
|
+
|
|
503
|
+
if (returnFocus) {
|
|
504
|
+
delay(function () {
|
|
505
|
+
tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return this;
|
|
510
|
+
},
|
|
511
|
+
pause: function pause() {
|
|
512
|
+
if (state.paused || !state.active) {
|
|
513
|
+
return this;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
state.paused = true;
|
|
517
|
+
removeListeners();
|
|
518
|
+
return this;
|
|
519
|
+
},
|
|
520
|
+
unpause: function unpause() {
|
|
521
|
+
if (!state.paused || !state.active) {
|
|
522
|
+
return this;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
state.paused = false;
|
|
526
|
+
updateTabbableNodes();
|
|
527
|
+
addListeners();
|
|
528
|
+
return this;
|
|
529
|
+
},
|
|
530
|
+
updateContainerElements: function updateContainerElements(containerElements) {
|
|
531
|
+
var elementsAsArray = [].concat(containerElements).filter(Boolean);
|
|
532
|
+
state.containers = elementsAsArray.map(function (element) {
|
|
533
|
+
return typeof element === 'string' ? doc.querySelector(element) : element;
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (state.active) {
|
|
537
|
+
updateTabbableNodes();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return this;
|
|
541
|
+
}
|
|
542
|
+
}; // initialize container elements
|
|
543
|
+
|
|
544
|
+
trap.updateContainerElements(elements);
|
|
545
|
+
return trap;
|
|
546
|
+
};
|
|
446
547
|
|
|
447
548
|
export { createFocusTrap };
|
|
448
549
|
//# sourceMappingURL=focus-trap.esm.js.map
|