focus-trap 8.0.1 → 8.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 +16 -0
- package/README.md +35 -17
- package/dist/focus-trap.esm.js +52 -19
- package/dist/focus-trap.esm.js.map +1 -1
- package/dist/focus-trap.esm.min.js +3 -3
- package/dist/focus-trap.esm.min.js.map +1 -1
- package/dist/focus-trap.js +51 -18
- 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 +51 -18
- 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 +33 -8
- package/index.js +40 -17
- package/package.json +16 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 8.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 567dbe1: Add new `delayReturnFocus` option (default true) to control whether return focus and onPostDeactivate are deferred by one frame (#1689).
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- b70e8d9: Fix bug where removing the ancestor of a focused node within a trap would result in focus landing on the body instead of remaining in the trap (#1660).
|
|
12
|
+
|
|
13
|
+
## 8.1.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- 642f7f2: Update lifecycle hooks to include the associated focus trap as a parameter.
|
|
18
|
+
|
|
3
19
|
## 8.0.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ Returns a new focus trap on `element` (one or more "containers" of tabbable node
|
|
|
94
94
|
##### onActivate
|
|
95
95
|
|
|
96
96
|
```typescript
|
|
97
|
-
() => void
|
|
97
|
+
(params: LifecycleParameters) => void
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
A function that will be called **before** sending focus to the target element upon activation.
|
|
@@ -102,7 +102,7 @@ A function that will be called **before** sending focus to the target element up
|
|
|
102
102
|
##### onPostActivate
|
|
103
103
|
|
|
104
104
|
```typescript
|
|
105
|
-
() => void
|
|
105
|
+
(params: LifecycleParameters) => void
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
A function that will be called **after** sending focus to the target element upon activation **unless** initial focus is delayed because the [delayInitialFocus](#delayinitialfocus) is true (default).
|
|
@@ -110,7 +110,7 @@ A function that will be called **after** sending focus to the target element upo
|
|
|
110
110
|
##### onPause
|
|
111
111
|
|
|
112
112
|
```typescript
|
|
113
|
-
() => void
|
|
113
|
+
(params: LifecycleParameters) => void
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
A function that will be called immediately after the trap's state is updated to be paused.
|
|
@@ -118,7 +118,7 @@ A function that will be called immediately after the trap's state is updated to
|
|
|
118
118
|
##### onPostPause
|
|
119
119
|
|
|
120
120
|
```typescript
|
|
121
|
-
() => void
|
|
121
|
+
(params: LifecycleParameters) => void
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
A function that will be called after the trap has been completely paused and is no longer managing/trapping focus.
|
|
@@ -126,7 +126,7 @@ A function that will be called after the trap has been completely paused and is
|
|
|
126
126
|
##### onUnpause
|
|
127
127
|
|
|
128
128
|
```typescript
|
|
129
|
-
() => void
|
|
129
|
+
(params: LifecycleParameters) => void
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
A function that will be called immediately after the trap's state is updated to be active again, but prior to updating its knowledge of what nodes are tabbable within its containers, and prior to actively managing/trapping focus.
|
|
@@ -134,7 +134,7 @@ A function that will be called immediately after the trap's state is updated to
|
|
|
134
134
|
##### onPostUnpause
|
|
135
135
|
|
|
136
136
|
```typescript
|
|
137
|
-
() => void
|
|
137
|
+
(params: LifecycleParameters) => void
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
A function that will be called after the trap has been completely unpaused and is once again managing/trapping focus.
|
|
@@ -150,11 +150,12 @@ Note that if [delayInitialFocus](#delayinitialfocus) is true, this handler will
|
|
|
150
150
|
Animated dialogs have a small delay between when `onActivate` is called and when the focus trap is focusable. `checkCanFocusTrap` expects a promise to be returned. When that promise settles (resolves or rejects), focus will be sent to the first tabbable node (in tab order) in the focus trap (or the node configured in the `initialFocus` option).
|
|
151
151
|
|
|
152
152
|
🔺 It does not matter whether the Promise resolves or rejects, only that it settles. A rejected Promise will not result in cancellation of trap activation.
|
|
153
|
+
🔺 This controls **when activation may proceed**. The separate [delayInitialFocus](#delayinitialfocus) option controls whether there is an additional one-frame delay before focus is sent once activation can proceed.
|
|
153
154
|
|
|
154
155
|
##### onDeactivate
|
|
155
156
|
|
|
156
157
|
```typescript
|
|
157
|
-
() => void
|
|
158
|
+
(params: LifecycleParameters) => void
|
|
158
159
|
```
|
|
159
160
|
|
|
160
161
|
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.
|
|
@@ -162,11 +163,13 @@ A function that will be called **before** returning focus to the node that had f
|
|
|
162
163
|
##### onPostDeactivate
|
|
163
164
|
|
|
164
165
|
```typescript
|
|
165
|
-
() => void
|
|
166
|
+
(params: LifecycleParameters) => void
|
|
166
167
|
```
|
|
167
168
|
|
|
168
169
|
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.
|
|
169
170
|
|
|
171
|
+
By default, this callback is delayed by one frame along with return-focus timing; set [delayReturnFocus](#delayreturnfocus) to `false` to remove that extra delay.
|
|
172
|
+
|
|
170
173
|
##### checkCanReturnFocus
|
|
171
174
|
|
|
172
175
|
```typescript
|
|
@@ -175,6 +178,8 @@ A function that will be called after the trap is deactivated, after `onDeactivat
|
|
|
175
178
|
|
|
176
179
|
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).
|
|
177
180
|
|
|
181
|
+
🔺 If [delayReturnFocus](#delayreturnfocus) is `true` (default), there is still an additional one-frame delay after this Promise settles before focus is returned and `onPostDeactivate` is called.
|
|
182
|
+
|
|
178
183
|
##### initialFocus
|
|
179
184
|
|
|
180
185
|
```typescript
|
|
@@ -272,6 +277,19 @@ boolean
|
|
|
272
277
|
Default: `true`. Delays the autofocus to the next execution frame when the focus trap is activated. This prevents elements within the focusable element from capturing the event that triggered the focus trap activation.
|
|
273
278
|
|
|
274
279
|
🔺 Note that when this option is `true` (default), it means the initial element to be focused will not be focused until **after** [onPostActivate](#onpostactivate) or [onPostUnpause](#onpostunpause) are called.
|
|
280
|
+
🔺 This option controls the extra frame delay on activation. Use [checkCanFocusTrap](#checkcanfocustrap) to gate activation readiness, and [delayReturnFocus](#delayreturnfocus) plus [checkCanReturnFocus](#checkcanreturnfocus) for the equivalent deactivation timing controls.
|
|
281
|
+
|
|
282
|
+
##### delayReturnFocus
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
boolean
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Default: `true`. Delays return-focus to the next execution frame when the focus trap is deactivated.
|
|
289
|
+
|
|
290
|
+
- This same delay also applies to [onPostDeactivate](#onpostdeactivate).
|
|
291
|
+
- Set to `false` to skip the extra frame delay.
|
|
292
|
+
- If [checkCanReturnFocus](#checkcanreturnfocus) is configured, that Promise must still settle first.
|
|
275
293
|
|
|
276
294
|
##### isolateSubtrees
|
|
277
295
|
|
|
@@ -389,8 +407,8 @@ Returns the `trap`.
|
|
|
389
407
|
|
|
390
408
|
These options are used to override the focus trap's default behavior for this particular activation.
|
|
391
409
|
|
|
392
|
-
- **onActivate** `{() => void}`: Default: whatever you chose for `createOptions.onActivate`. `null` or `false` are the equivalent of a `noop`.
|
|
393
|
-
- **onPostActivate** `{() => void}`: Default: whatever you chose for `createOptions.onPostActivate`. `null` or `false` are the equivalent of a `noop`.
|
|
410
|
+
- **onActivate** `{(params: LifecycleParameters) => void}`: Default: whatever you chose for `createOptions.onActivate`. `null` or `false` are the equivalent of a `noop`.
|
|
411
|
+
- **onPostActivate** `{(params: LifecycleParameters) => void}`: Default: whatever you chose for `createOptions.onPostActivate`. `null` or `false` are the equivalent of a `noop`.
|
|
394
412
|
- **checkCanFocusTrap** `{(containers: Array<HTMLElement | SVGElement>) => Promise<unknown>}`: Default: whatever you chose for `createOptions.checkCanFocusTrap`.
|
|
395
413
|
|
|
396
414
|
### trap.deactivate()
|
|
@@ -408,9 +426,9 @@ Returns the `trap`.
|
|
|
408
426
|
These options are used to override the focus trap's default behavior for this particular deactivation.
|
|
409
427
|
|
|
410
428
|
- **returnFocus** `{boolean}`: Default: whatever you set for `createOptions.returnFocusOnDeactivate`. If `true`, then the `setReturnFocus` option (specified when the trap was created) is used to determine where focus will be returned.
|
|
411
|
-
- **onDeactivate** `{() => void}`: Default: whatever you set for `createOptions.onDeactivate`. `null` or `false` are the equivalent of a `noop`.
|
|
412
|
-
- **onPostDeactivate** `{() => void}`: Default: whatever you set for `createOptions.onPostDeactivate`. `null` or `false` are the equivalent of a `noop`.
|
|
413
|
-
- **checkCanReturnFocus** `{(trigger: HTMLElement | SVGElement) => Promise<void>}`: Default: whatever you set for `createOptions.checkCanReturnFocus`. Not called if the `returnFocus` option is falsy. `trigger` is either the originally focused node prior to activation, or the result of the `setReturnFocus` configuration option.
|
|
429
|
+
- **onDeactivate** `{(params: LifecycleParameters) => void}`: Default: whatever you set for `createOptions.onDeactivate`. `null` or `false` are the equivalent of a `noop`.
|
|
430
|
+
- **onPostDeactivate** `{(params: LifecycleParameters) => void}`: Default: whatever you set for `createOptions.onPostDeactivate`. `null` or `false` are the equivalent of a `noop`.
|
|
431
|
+
- **checkCanReturnFocus** `{(trigger: HTMLElement | SVGElement) => Promise<void>}`: Default: whatever you set for `createOptions.checkCanReturnFocus`. Not called if the `returnFocus` option is falsy. `trigger` is either the originally focused node prior to activation, or the result of the `setReturnFocus` configuration option. After this Promise settles, `createOptions.delayReturnFocus` determines whether return-focus and `onPostDeactivate` are immediate (`false`) or delayed by one frame (`true`, default).
|
|
414
432
|
|
|
415
433
|
### trap.pause()
|
|
416
434
|
|
|
@@ -436,8 +454,8 @@ This is useful in various cases, one of which is when you want one focus trap wi
|
|
|
436
454
|
|
|
437
455
|
These options are used to override the focus trap's default behavior for this particular pausing.
|
|
438
456
|
|
|
439
|
-
- **onPause** `{() => void}`: Default: whatever you chose for `createOptions.onPause`. `null` or `false` are the equivalent of a `noop`.
|
|
440
|
-
- **onPostPause** `{() => void}`: Default: whatever you chose for `createOptions.onPostPause`. `null` or `false` are the equivalent of a `noop`.
|
|
457
|
+
- **onPause** `{(params: LifecycleParameters) => void}`: Default: whatever you chose for `createOptions.onPause`. `null` or `false` are the equivalent of a `noop`.
|
|
458
|
+
- **onPostPause** `{(params: LifecycleParameters) => void}`: Default: whatever you chose for `createOptions.onPostPause`. `null` or `false` are the equivalent of a `noop`.
|
|
441
459
|
|
|
442
460
|
### trap.unpause()
|
|
443
461
|
|
|
@@ -461,8 +479,8 @@ Returns the `trap`.
|
|
|
461
479
|
|
|
462
480
|
These options are used to override the focus trap's default behavior for this particular unpausing.
|
|
463
481
|
|
|
464
|
-
- **onUnpause** `{() => void}`: Default: whatever you chose for `createOptions.onUnpause`. `null` or `false` are the equivalent of a `noop`.
|
|
465
|
-
- **onPostUnpause** `{() => void}`: Default: whatever you chose for `createOptions.onPostUnpause`. `null` or `false` are the equivalent of a `noop`.
|
|
482
|
+
- **onUnpause** `{(params: LifecycleParameters) => void}`: Default: whatever you chose for `createOptions.onUnpause`. `null` or `false` are the equivalent of a `noop`.
|
|
483
|
+
- **onPostUnpause** `{(params: LifecycleParameters) => void}`: Default: whatever you chose for `createOptions.onPostUnpause`. `null` or `false` are the equivalent of a `noop`.
|
|
466
484
|
|
|
467
485
|
### trap.updateContainerElements()
|
|
468
486
|
|
package/dist/focus-trap.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* focus-trap 8.0
|
|
2
|
+
* focus-trap 8.2.0
|
|
3
3
|
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { tabbable, focusable, isTabbable, getTabIndex, isFocusable } from 'tabbable';
|
|
6
6
|
|
|
7
7
|
function _arrayLikeToArray(r, a) {
|
|
8
8
|
(null == a || a > r.length) && (a = r.length);
|
|
@@ -356,6 +356,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
356
356
|
returnFocusOnDeactivate: true,
|
|
357
357
|
escapeDeactivates: true,
|
|
358
358
|
delayInitialFocus: true,
|
|
359
|
+
delayReturnFocus: true,
|
|
359
360
|
isolateSubtrees: false,
|
|
360
361
|
isKeyForward: isKeyForward,
|
|
361
362
|
isKeyBackward: isKeyBackward
|
|
@@ -1102,17 +1103,27 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
1102
1103
|
//
|
|
1103
1104
|
|
|
1104
1105
|
var checkDomRemoval = function checkDomRemoval(mutations) {
|
|
1106
|
+
var focusedNode = state.mostRecentlyFocusedNode;
|
|
1107
|
+
if (!focusedNode) {
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1105
1110
|
var isFocusedNodeRemoved = mutations.some(function (mutation) {
|
|
1106
1111
|
var removedNodes = Array.from(mutation.removedNodes);
|
|
1107
1112
|
return removedNodes.some(function (node) {
|
|
1108
|
-
return node ===
|
|
1113
|
+
return node === focusedNode || typeof node.contains === 'function' && node.contains(focusedNode);
|
|
1109
1114
|
});
|
|
1110
1115
|
});
|
|
1111
1116
|
|
|
1112
|
-
// If the currently focused is removed then browsers will move focus to the
|
|
1117
|
+
// If the currently focused node is removed then browsers will move focus to the
|
|
1113
1118
|
// <body> element. If this happens, try to move focus back into the trap.
|
|
1114
|
-
if (isFocusedNodeRemoved) {
|
|
1115
|
-
|
|
1119
|
+
if (isFocusedNodeRemoved && state.containers.some(function (container) {
|
|
1120
|
+
return container === null || container === void 0 ? void 0 : container.isConnected;
|
|
1121
|
+
})) {
|
|
1122
|
+
// Refresh tabbable state before resolving initial focus because
|
|
1123
|
+
// getInitialFocusNode() may fall back to the first tabbable node in the trap.
|
|
1124
|
+
updateTabbableNodes();
|
|
1125
|
+
var initialFocusNode = getInitialFocusNode();
|
|
1126
|
+
_tryFocus(initialFocusNode);
|
|
1116
1127
|
}
|
|
1117
1128
|
};
|
|
1118
1129
|
|
|
@@ -1172,7 +1183,9 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
1172
1183
|
state.active = true;
|
|
1173
1184
|
state.paused = false;
|
|
1174
1185
|
state.nodeFocusedBeforeActivation = _getActiveElement(doc);
|
|
1175
|
-
onActivate === null || onActivate === void 0 || onActivate(
|
|
1186
|
+
onActivate === null || onActivate === void 0 || onActivate({
|
|
1187
|
+
trap: trap
|
|
1188
|
+
});
|
|
1176
1189
|
var finishActivation = /*#__PURE__*/function () {
|
|
1177
1190
|
var _ref6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
1178
1191
|
return _regenerator().w(function (_context) {
|
|
@@ -1192,7 +1205,9 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
1192
1205
|
case 1:
|
|
1193
1206
|
trap._setSubtreeIsolation(true);
|
|
1194
1207
|
updateObservedNodes();
|
|
1195
|
-
onPostActivate === null || onPostActivate === void 0 || onPostActivate(
|
|
1208
|
+
onPostActivate === null || onPostActivate === void 0 || onPostActivate({
|
|
1209
|
+
trap: trap
|
|
1210
|
+
});
|
|
1196
1211
|
case 2:
|
|
1197
1212
|
return _context.a(2);
|
|
1198
1213
|
}
|
|
@@ -1249,16 +1264,26 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
1249
1264
|
var onDeactivate = getOption(options, 'onDeactivate');
|
|
1250
1265
|
var onPostDeactivate = getOption(options, 'onPostDeactivate');
|
|
1251
1266
|
var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
|
|
1267
|
+
var delayReturnFocus = getOption(options, 'delayReturnFocus');
|
|
1252
1268
|
var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
|
|
1253
|
-
onDeactivate === null || onDeactivate === void 0 || onDeactivate(
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1269
|
+
onDeactivate === null || onDeactivate === void 0 || onDeactivate({
|
|
1270
|
+
trap: trap
|
|
1271
|
+
});
|
|
1272
|
+
var completeDeactivation = function completeDeactivation() {
|
|
1273
|
+
if (returnFocus) {
|
|
1274
|
+
_tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
|
|
1275
|
+
}
|
|
1276
|
+
onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate({
|
|
1277
|
+
trap: trap
|
|
1260
1278
|
});
|
|
1261
1279
|
};
|
|
1280
|
+
var finishDeactivation = function finishDeactivation() {
|
|
1281
|
+
if (delayReturnFocus && returnFocus) {
|
|
1282
|
+
delay(completeDeactivation);
|
|
1283
|
+
} else {
|
|
1284
|
+
completeDeactivation();
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1262
1287
|
if (returnFocus && checkCanReturnFocus) {
|
|
1263
1288
|
checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);
|
|
1264
1289
|
return this;
|
|
@@ -1316,15 +1341,21 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
1316
1341
|
if (paused) {
|
|
1317
1342
|
var onPause = getOption(options, 'onPause');
|
|
1318
1343
|
var onPostPause = getOption(options, 'onPostPause');
|
|
1319
|
-
onPause === null || onPause === void 0 || onPause(
|
|
1344
|
+
onPause === null || onPause === void 0 || onPause({
|
|
1345
|
+
trap: trap
|
|
1346
|
+
});
|
|
1320
1347
|
removeListeners();
|
|
1321
1348
|
trap._setSubtreeIsolation(false);
|
|
1322
1349
|
updateObservedNodes();
|
|
1323
|
-
onPostPause === null || onPostPause === void 0 || onPostPause(
|
|
1350
|
+
onPostPause === null || onPostPause === void 0 || onPostPause({
|
|
1351
|
+
trap: trap
|
|
1352
|
+
});
|
|
1324
1353
|
} else {
|
|
1325
1354
|
var onUnpause = getOption(options, 'onUnpause');
|
|
1326
1355
|
var onPostUnpause = getOption(options, 'onPostUnpause');
|
|
1327
|
-
onUnpause === null || onUnpause === void 0 || onUnpause(
|
|
1356
|
+
onUnpause === null || onUnpause === void 0 || onUnpause({
|
|
1357
|
+
trap: trap
|
|
1358
|
+
});
|
|
1328
1359
|
var finishUnpause = /*#__PURE__*/function () {
|
|
1329
1360
|
var _ref7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
1330
1361
|
return _regenerator().w(function (_context2) {
|
|
@@ -1342,7 +1373,9 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
1342
1373
|
case 1:
|
|
1343
1374
|
trap._setSubtreeIsolation(true);
|
|
1344
1375
|
updateObservedNodes();
|
|
1345
|
-
onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause(
|
|
1376
|
+
onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause({
|
|
1377
|
+
trap: trap
|
|
1378
|
+
});
|
|
1346
1379
|
case 2:
|
|
1347
1380
|
return _context2.a(2);
|
|
1348
1381
|
}
|