focus-trap 7.6.0 β 7.6.2
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 +12 -0
- package/README.md +8 -3
- package/dist/focus-trap.esm.js +72 -39
- 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 +72 -41
- 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 +72 -41
- 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.js +42 -33
- package/package.json +17 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.6.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d4a4c34: Replace findIndex with native implementation - [Closes #1305](https://github.com/focus-trap/focus-trap/issues/1305
|
|
8
|
+
|
|
9
|
+
## 7.6.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- fc5910d: Fix fallbackFocus not used when initialFocus is selector to non-existent node ([#1218](https://github.com/focus-trap/focus-trap/issues/1218))
|
|
14
|
+
|
|
3
15
|
## 7.6.0
|
|
4
16
|
|
|
5
17
|
### Minor 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.
|
|
@@ -101,9 +101,12 @@ Returns a new focus trap on `element` (one or more "containers" of tabbable node
|
|
|
101
101
|
- **onDeactivate** `{() => void}`: A function that will be called **before** returning focus to the node that had focus prior to activation (or configured with the `setReturnFocus` option) upon deactivation.
|
|
102
102
|
- **onPostDeactivate** `{() => void}`: A function that will be called after the trap is deactivated, after `onDeactivate`. If the `returnFocus` deactivation option was set, it will be called **after** returning focus to the node that had focus prior to activation (or configured with the `setReturnFocus` option) upon deactivation; otherwise, it will be called after deactivation completes.
|
|
103
103
|
- **checkCanReturnFocus** `{(trigger: HTMLElement | SVGElement) => Promise<void>}`: An animated trigger button will have a small delay between when `onDeactivate` is called and when the focus is able to be sent back to the trigger. `checkCanReturnFocus` expects a promise to be returned. When that promise settles (resolves or rejects), focus will be sent to to the node that had focus prior to the activation of the trap (or the node configured in the `setReturnFocus` option).
|
|
104
|
-
- **initialFocus** `{HTMLElement | SVGElement | string | false | undefined | (() => HTMLElement | SVGElement | string | false | undefined)}`: By default, when a focus trap is activated the first element in the focus trap's tab order will receive focus. With this option you can specify a different element to receive that initial focus. Can be a DOM node, or a selector string (which will be passed to `document.querySelector()` to find the DOM node), or a function that returns any of these. You can also set this option to `false` (or to a function that returns `false`) to prevent any initial focus at all when the trap activates.
|
|
104
|
+
- **initialFocus** `{HTMLElement | SVGElement | string | false | undefined | (() => HTMLElement | SVGElement | string | false | undefined)}`: By default (when `undefined` or the function returns `undefined`), when a focus trap is activated, the active element will receive focus if it's in the trap, otherwise, the first element in the focus trap's tab order will receive focus. With this option you can specify a different element to receive that initial focus. Can be a DOM node, or a selector string (which will be passed to `document.querySelector()` to find the DOM node), or a function that returns any of these. You can also set this option to `false` (or to a function that returns `false`) to prevent any initial focus at all when the trap activates.
|
|
105
105
|
- π¬ Setting this option to `false` (or a function that returns `false`) will prevent the `fallbackFocus` option from being used.
|
|
106
|
-
-
|
|
106
|
+
- π¬ If the option resolves to a non-focusable node (e.g. one that exists, but is hidden), the default behavior will be used (as though the option weren't set at all).
|
|
107
|
+
- π¬ If the option resolves to a non-existent node, an exception will be thrown.
|
|
108
|
+
- π¬ If the option resolves to a valid selector string (directly set, or returned from a function), but the selector doesn't match a node, the trap will fall back to the `fallbackFocus` node option. If that option also fails to yield a node, an exception will be thrown.
|
|
109
|
+
- π¬ If the option resolves to `undefined` (i.e. not set or function returns `undefined`), the default behavior will be used.
|
|
107
110
|
- β οΈ See warning below about **Shadow DOM** and selector strings.
|
|
108
111
|
- **fallbackFocus** `{HTMLElement | SVGElement | string | () => HTMLElement | SVGElement | string}`: By default, an error will be thrown if the focus trap contains no elements in its tab order. With this option you can specify a fallback element to programmatically receive focus if no other tabbable elements are found. For example, you may want a popover's `<div>` to receive focus if the popover's content includes no tabbable elements. *Make sure the fallback element has a negative `tabindex` so it can be programmatically focused.* The option value can be a DOM node, a selector string (which will be passed to `document.querySelector()` to find the DOM node), or a function that returns any of these.
|
|
109
112
|
- π¬ If `initialFocus` is `false` (or a function that returns `false`), this function will not be called when the trap is activated, and no element will be initially focused. This function may still be called while the trap is active if things change such that there are no longer any tabbable nodes in the trap.
|
|
@@ -413,6 +416,8 @@ In alphabetical order:
|
|
|
413
416
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zioth"><img src="https://avatars3.githubusercontent.com/u/945603?v=4?s=100" width="100px;" alt="Zioth"/><br /><sub><b>Zioth</b></sub></a><br /><a href="#ideas-zioth" title="Ideas, Planning, & Feedback">π€</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Azioth" title="Bug reports">π</a></td>
|
|
414
417
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/glushkova91"><img src="https://avatars.githubusercontent.com/u/13402897?v=4?s=100" width="100px;" alt="glushkova91"/><br /><sub><b>glushkova91</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=glushkova91" title="Documentation">π</a></td>
|
|
415
418
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jpveooys"><img src="https://avatars.githubusercontent.com/u/66470099?v=4?s=100" width="100px;" alt="jpveooys"/><br /><sub><b>jpveooys</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Ajpveooys" title="Bug reports">π</a></td>
|
|
419
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/konradr33"><img src="https://avatars.githubusercontent.com/u/32595283?v=4?s=100" width="100px;" alt="konradr33"/><br /><sub><b>konradr33</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Akonradr33" title="Bug reports">π</a></td>
|
|
420
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tomasvn"><img src="https://avatars.githubusercontent.com/u/17225564?v=4?s=100" width="100px;" alt="tomasvn"/><br /><sub><b>tomasvn</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=tomasvn" title="Code">π»</a></td>
|
|
416
421
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/simonxabris"><img src="https://avatars.githubusercontent.com/u/27497229?v=4?s=100" width="100px;" alt="Γbris Simon"/><br /><sub><b>Γbris Simon</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=simonxabris" title="Code">π»</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Asimonxabris" title="Bug reports">π</a></td>
|
|
417
422
|
</tr>
|
|
418
423
|
</tbody>
|
package/dist/focus-trap.esm.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* focus-trap 7.6.
|
|
2
|
+
* focus-trap 7.6.2
|
|
3
3
|
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
|
4
4
|
*/
|
|
5
5
|
import { isFocusable, tabbable, focusable, isTabbable, getTabIndex } from 'tabbable';
|
|
6
6
|
|
|
7
|
+
function _arrayLikeToArray(r, a) {
|
|
8
|
+
(null == a || a > r.length) && (a = r.length);
|
|
9
|
+
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
|
10
|
+
return n;
|
|
11
|
+
}
|
|
12
|
+
function _arrayWithoutHoles(r) {
|
|
13
|
+
if (Array.isArray(r)) return _arrayLikeToArray(r);
|
|
14
|
+
}
|
|
7
15
|
function _defineProperty(e, r, t) {
|
|
8
16
|
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
9
17
|
value: t,
|
|
@@ -12,6 +20,12 @@ function _defineProperty(e, r, t) {
|
|
|
12
20
|
writable: !0
|
|
13
21
|
}) : e[r] = t, e;
|
|
14
22
|
}
|
|
23
|
+
function _iterableToArray(r) {
|
|
24
|
+
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
|
|
25
|
+
}
|
|
26
|
+
function _nonIterableSpread() {
|
|
27
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
28
|
+
}
|
|
15
29
|
function ownKeys(e, r) {
|
|
16
30
|
var t = Object.keys(e);
|
|
17
31
|
if (Object.getOwnPropertySymbols) {
|
|
@@ -33,6 +47,9 @@ function _objectSpread2(e) {
|
|
|
33
47
|
}
|
|
34
48
|
return e;
|
|
35
49
|
}
|
|
50
|
+
function _toConsumableArray(r) {
|
|
51
|
+
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
|
|
52
|
+
}
|
|
36
53
|
function _toPrimitive(t, r) {
|
|
37
54
|
if ("object" != typeof t || !t) return t;
|
|
38
55
|
var e = t[Symbol.toPrimitive];
|
|
@@ -47,6 +64,13 @@ function _toPropertyKey(t) {
|
|
|
47
64
|
var i = _toPrimitive(t, "string");
|
|
48
65
|
return "symbol" == typeof i ? i : i + "";
|
|
49
66
|
}
|
|
67
|
+
function _unsupportedIterableToArray(r, a) {
|
|
68
|
+
if (r) {
|
|
69
|
+
if ("string" == typeof r) return _arrayLikeToArray(r, a);
|
|
70
|
+
var t = {}.toString.call(r).slice(8, -1);
|
|
71
|
+
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
50
74
|
|
|
51
75
|
var activeFocusTraps = {
|
|
52
76
|
activateTrap: function activateTrap(trapStack, trap) {
|
|
@@ -98,20 +122,6 @@ var delay = function delay(fn) {
|
|
|
98
122
|
return setTimeout(fn, 0);
|
|
99
123
|
};
|
|
100
124
|
|
|
101
|
-
// Array.find/findIndex() are not supported on IE; this replicates enough
|
|
102
|
-
// of Array.findIndex() for our needs
|
|
103
|
-
var findIndex = function findIndex(arr, fn) {
|
|
104
|
-
var idx = -1;
|
|
105
|
-
arr.every(function (value, i) {
|
|
106
|
-
if (fn(value)) {
|
|
107
|
-
idx = i;
|
|
108
|
-
return false; // break
|
|
109
|
-
}
|
|
110
|
-
return true; // next
|
|
111
|
-
});
|
|
112
|
-
return idx;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
125
|
/**
|
|
116
126
|
* Get an option's value when it could be a plain value, or a handler that provides
|
|
117
127
|
* the value.
|
|
@@ -221,7 +231,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
221
231
|
return state.containerGroups.findIndex(function (_ref) {
|
|
222
232
|
var container = _ref.container,
|
|
223
233
|
tabbableNodes = _ref.tabbableNodes;
|
|
224
|
-
return container.contains(element) || (
|
|
234
|
+
return container.contains(element) || (// fall back to explicit tabbable search which will take into consideration any
|
|
225
235
|
// web components if the `tabbableOptions.getShadowRoot` option was used for
|
|
226
236
|
// the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
|
|
227
237
|
// look inside web components even if open)
|
|
@@ -237,20 +247,27 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
237
247
|
* (if a node is explicitly NOT given), or a function that returns any of these
|
|
238
248
|
* values.
|
|
239
249
|
* @param {string} optionName
|
|
240
|
-
* @
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
250
|
+
* @param {Object} options
|
|
251
|
+
* @param {boolean} [options.hasFallback] True if the option could be a selector string
|
|
252
|
+
* and the option allows for a fallback scenario in the case where the selector is
|
|
253
|
+
* valid but does not match a node (i.e. the queried node doesn't exist in the DOM).
|
|
254
|
+
* @param {Array} [options.params] Params to pass to the option if it's a function.
|
|
255
|
+
* @returns {undefined | null | false | HTMLElement | SVGElement} Returns
|
|
256
|
+
* `undefined` if the option is not specified; `null` if the option didn't resolve
|
|
257
|
+
* to a node but `options.hasFallback=true`, `false` if the option resolved to `false`
|
|
258
|
+
* (node explicitly not given); otherwise, the resolved DOM node.
|
|
244
259
|
* @throws {Error} If the option is set, not `false`, and is not, or does not
|
|
245
|
-
* resolve to a node.
|
|
260
|
+
* resolve to a node, unless the option is a selector string and `options.hasFallback=true`.
|
|
246
261
|
*/
|
|
247
262
|
var getNodeForOption = function getNodeForOption(optionName) {
|
|
263
|
+
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
264
|
+
_ref2$hasFallback = _ref2.hasFallback,
|
|
265
|
+
hasFallback = _ref2$hasFallback === void 0 ? false : _ref2$hasFallback,
|
|
266
|
+
_ref2$params = _ref2.params,
|
|
267
|
+
params = _ref2$params === void 0 ? [] : _ref2$params;
|
|
248
268
|
var optionValue = config[optionName];
|
|
249
269
|
if (typeof optionValue === 'function') {
|
|
250
|
-
|
|
251
|
-
params[_key2 - 1] = arguments[_key2];
|
|
252
|
-
}
|
|
253
|
-
optionValue = optionValue.apply(void 0, params);
|
|
270
|
+
optionValue = optionValue.apply(void 0, _toConsumableArray(params));
|
|
254
271
|
}
|
|
255
272
|
if (optionValue === true) {
|
|
256
273
|
optionValue = undefined; // use default value
|
|
@@ -266,21 +283,31 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
266
283
|
var node = optionValue; // could be HTMLElement, SVGElement, or non-empty string at this point
|
|
267
284
|
|
|
268
285
|
if (typeof optionValue === 'string') {
|
|
269
|
-
|
|
286
|
+
try {
|
|
287
|
+
node = doc.querySelector(optionValue); // resolve to node, or null if fails
|
|
288
|
+
} catch (err) {
|
|
289
|
+
throw new Error("`".concat(optionName, "` appears to be an invalid selector; error=\"").concat(err.message, "\""));
|
|
290
|
+
}
|
|
270
291
|
if (!node) {
|
|
271
|
-
|
|
292
|
+
if (!hasFallback) {
|
|
293
|
+
throw new Error("`".concat(optionName, "` as selector refers to no known node"));
|
|
294
|
+
}
|
|
295
|
+
// else, `node` MUST be `null` because that's what `Document.querySelector()` returns
|
|
296
|
+
// if the selector is valid but doesn't match anything
|
|
272
297
|
}
|
|
273
298
|
}
|
|
274
299
|
return node;
|
|
275
300
|
};
|
|
276
301
|
var getInitialFocusNode = function getInitialFocusNode() {
|
|
277
|
-
var node = getNodeForOption('initialFocus'
|
|
302
|
+
var node = getNodeForOption('initialFocus', {
|
|
303
|
+
hasFallback: true
|
|
304
|
+
});
|
|
278
305
|
|
|
279
306
|
// false explicitly indicates we want no initialFocus at all
|
|
280
307
|
if (node === false) {
|
|
281
308
|
return false;
|
|
282
309
|
}
|
|
283
|
-
if (node === undefined || !isFocusable(node, config.tabbableOptions)) {
|
|
310
|
+
if (node === undefined || node && !isFocusable(node, config.tabbableOptions)) {
|
|
284
311
|
// option not specified nor focusable: use fallback options
|
|
285
312
|
if (findContainerIndex(doc.activeElement) >= 0) {
|
|
286
313
|
node = doc.activeElement;
|
|
@@ -291,6 +318,10 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
291
318
|
// NOTE: `fallbackFocus` option function cannot return `false` (not supported)
|
|
292
319
|
node = firstTabbableNode || getNodeForOption('fallbackFocus');
|
|
293
320
|
}
|
|
321
|
+
} else if (node === null) {
|
|
322
|
+
// option is a VALID selector string that doesn't yield a node: use the `fallbackFocus`
|
|
323
|
+
// option instead of the default behavior when the option isn't specified at all
|
|
324
|
+
node = getNodeForOption('fallbackFocus');
|
|
294
325
|
}
|
|
295
326
|
if (!node) {
|
|
296
327
|
throw new Error('Your focus-trap needs to have at least one focusable element');
|
|
@@ -431,7 +462,9 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
431
462
|
}
|
|
432
463
|
};
|
|
433
464
|
var getReturnFocusNode = function getReturnFocusNode(previousActiveElement) {
|
|
434
|
-
var node = getNodeForOption('setReturnFocus',
|
|
465
|
+
var node = getNodeForOption('setReturnFocus', {
|
|
466
|
+
params: [previousActiveElement]
|
|
467
|
+
});
|
|
435
468
|
return node ? node : node === false ? false : previousActiveElement;
|
|
436
469
|
};
|
|
437
470
|
|
|
@@ -446,11 +479,11 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
446
479
|
* @returns {Node|undefined} The next node, or `undefined` if a next node couldn't be
|
|
447
480
|
* determined given the current state of the trap.
|
|
448
481
|
*/
|
|
449
|
-
var findNextNavNode = function findNextNavNode(
|
|
450
|
-
var target =
|
|
451
|
-
event =
|
|
452
|
-
|
|
453
|
-
isBackward =
|
|
482
|
+
var findNextNavNode = function findNextNavNode(_ref3) {
|
|
483
|
+
var target = _ref3.target,
|
|
484
|
+
event = _ref3.event,
|
|
485
|
+
_ref3$isBackward = _ref3.isBackward,
|
|
486
|
+
isBackward = _ref3$isBackward === void 0 ? false : _ref3$isBackward;
|
|
454
487
|
target = target || getActualTarget(event);
|
|
455
488
|
updateTabbableNodes();
|
|
456
489
|
var destinationNode = null;
|
|
@@ -474,8 +507,8 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
474
507
|
// REVERSE
|
|
475
508
|
|
|
476
509
|
// is the target the first tabbable node in a group?
|
|
477
|
-
var startOfGroupIndex =
|
|
478
|
-
var firstTabbableNode =
|
|
510
|
+
var startOfGroupIndex = state.tabbableGroups.findIndex(function (_ref4) {
|
|
511
|
+
var firstTabbableNode = _ref4.firstTabbableNode;
|
|
479
512
|
return target === firstTabbableNode;
|
|
480
513
|
});
|
|
481
514
|
if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {
|
|
@@ -503,8 +536,8 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
503
536
|
// FORWARD
|
|
504
537
|
|
|
505
538
|
// is the target the last tabbable node in a group?
|
|
506
|
-
var lastOfGroupIndex =
|
|
507
|
-
var lastTabbableNode =
|
|
539
|
+
var lastOfGroupIndex = state.tabbableGroups.findIndex(function (_ref5) {
|
|
540
|
+
var lastTabbableNode = _ref5.lastTabbableNode;
|
|
508
541
|
return target === lastTabbableNode;
|
|
509
542
|
});
|
|
510
543
|
if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {
|