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