focus-trap 8.1.0 β†’ 8.2.1

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 8.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 7e0e518: Fixes lifecycle ordering bug introduced in v8.0.0 that always delays the order even if `delayInitialFocus=false`, `delayReturnFocus=false`, `checkCanFocusTrap=undefined`, `checkCanReturnFocus=undefined` (which would indicate a fully synchronous activation/pause/unpause/deactivation process) [#1862](https://github.com/focus-trap/focus-trap/issues/1862)
8
+ - 2cba31e: Improve shadow DOM focus handling for nested traps by ensuring a parent trap resumes using the innermost active element ([#1885](https://github.com/focus-trap/focus-trap/issues/1885))
9
+
10
+ ## 8.2.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 567dbe1: Add new `delayReturnFocus` option (default true) to control whether return focus and onPostDeactivate are deferred by one frame (#1689).
15
+
16
+ ### Patch Changes
17
+
18
+ - 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).
19
+
3
20
  ## 8.1.0
4
21
 
5
22
  ### Minor Changes
package/README.md CHANGED
@@ -150,6 +150,7 @@ 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
 
@@ -167,6 +168,8 @@ A function that will be called **before** returning focus to the node that had f
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
 
@@ -410,7 +428,7 @@ These options are used to override the focus trap's default behavior for this pa
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
429
  - **onDeactivate** `{(params: LifecycleParameters) => void}`: Default: whatever you set for `createOptions.onDeactivate`. `null` or `false` are the equivalent of a `noop`.
412
430
  - **onPostDeactivate** `{(params: LifecycleParameters) => 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.
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
 
@@ -589,7 +607,7 @@ In alphabetical order:
589
607
  <td align="center" valign="top" width="14.28%"><a href="http://davidtheclark.com/"><img src="https://avatars2.githubusercontent.com/u/628431?v=4?s=100" width="100px;" alt="David Clark"/><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>
590
608
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/features/security"><img src="https://avatars1.githubusercontent.com/u/27347476?v=4?s=100" width="100px;" alt="Dependabot"/><br /><sub><b>Dependabot</b></sub></a><br /><a href="#maintenance-dependabot" title="Maintenance">🚧</a></td>
591
609
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/ShGKme"><img src="https://avatars.githubusercontent.com/u/25978914?v=4?s=100" width="100px;" alt="Grigorii K. Shartsev"/><br /><sub><b>Grigorii K. Shartsev</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3AShGKme" title="Bug reports">πŸ›</a></td>
592
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/jcfranco"><img src="https://avatars.githubusercontent.com/u/197440?v=4?s=100" width="100px;" alt="JC Franco"/><br /><sub><b>JC Franco</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=jcfranco" title="Code">πŸ’»</a></td>
610
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/jcfranco"><img src="https://avatars.githubusercontent.com/u/197440?v=4?s=100" width="100px;" alt="JC Franco"/><br /><sub><b>JC Franco</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=jcfranco" title="Code">πŸ’»</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Ajcfranco" title="Bug reports">πŸ›</a></td>
593
611
  <td align="center" valign="top" width="14.28%"><a href="https://www.schilljs.com/"><img src="https://avatars.githubusercontent.com/u/213943?v=4?s=100" width="100px;" alt="Joas Schilling"/><br /><sub><b>Joas Schilling</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/pulls?q=is%3Apr+reviewed-by%3Anickvergessen" title="Reviewed Pull Requests">πŸ‘€</a></td>
594
612
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/skjnldsv"><img src="https://avatars.githubusercontent.com/u/14975046?v=4?s=100" width="100px;" alt="John MolakvoΓ¦"/><br /><sub><b>John MolakvoΓ¦</b></sub></a><br /><a href="#ideas-skjnldsv" title="Ideas, Planning, & Feedback">πŸ€”</a></td>
595
613
  </tr>
@@ -1,8 +1,8 @@
1
1
  /*!
2
- * focus-trap 8.1.0
2
+ * focus-trap 8.2.1
3
3
  * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
4
4
  */
5
- import { isFocusable, tabbable, focusable, isTabbable, getTabIndex } from 'tabbable';
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);
@@ -12,31 +12,6 @@ function _arrayLikeToArray(r, a) {
12
12
  function _arrayWithoutHoles(r) {
13
13
  if (Array.isArray(r)) return _arrayLikeToArray(r);
14
14
  }
15
- function asyncGeneratorStep(n, t, e, r, o, a, c) {
16
- try {
17
- var i = n[a](c),
18
- u = i.value;
19
- } catch (n) {
20
- return void e(n);
21
- }
22
- i.done ? t(u) : Promise.resolve(u).then(r, o);
23
- }
24
- function _asyncToGenerator(n) {
25
- return function () {
26
- var t = this,
27
- e = arguments;
28
- return new Promise(function (r, o) {
29
- var a = n.apply(t, e);
30
- function _next(n) {
31
- asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
32
- }
33
- function _throw(n) {
34
- asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
35
- }
36
- _next(void 0);
37
- });
38
- };
39
- }
40
15
  function _createForOfIteratorHelper(r, e) {
41
16
  var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
42
17
  if (!t) {
@@ -120,114 +95,6 @@ function _objectSpread2(e) {
120
95
  }
121
96
  return e;
122
97
  }
123
- function _regenerator() {
124
- /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
125
- var e,
126
- t,
127
- r = "function" == typeof Symbol ? Symbol : {},
128
- n = r.iterator || "@@iterator",
129
- o = r.toStringTag || "@@toStringTag";
130
- function i(r, n, o, i) {
131
- var c = n && n.prototype instanceof Generator ? n : Generator,
132
- u = Object.create(c.prototype);
133
- return _regeneratorDefine(u, "_invoke", function (r, n, o) {
134
- var i,
135
- c,
136
- u,
137
- f = 0,
138
- p = o || [],
139
- y = false,
140
- G = {
141
- p: 0,
142
- n: 0,
143
- v: e,
144
- a: d,
145
- f: d.bind(e, 4),
146
- d: function (t, r) {
147
- return i = t, c = 0, u = e, G.n = r, a;
148
- }
149
- };
150
- function d(r, n) {
151
- for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) {
152
- var o,
153
- i = p[t],
154
- d = G.p,
155
- l = i[2];
156
- r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0));
157
- }
158
- if (o || r > 1) return a;
159
- throw y = true, n;
160
- }
161
- return function (o, p, l) {
162
- if (f > 1) throw TypeError("Generator is already running");
163
- for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) {
164
- i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u);
165
- try {
166
- if (f = 2, i) {
167
- if (c || (o = "next"), t = i[o]) {
168
- if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object");
169
- if (!t.done) return t;
170
- u = t.value, c < 2 && (c = 0);
171
- } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1);
172
- i = e;
173
- } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break;
174
- } catch (t) {
175
- i = e, c = 1, u = t;
176
- } finally {
177
- f = 1;
178
- }
179
- }
180
- return {
181
- value: t,
182
- done: y
183
- };
184
- };
185
- }(r, o, i), true), u;
186
- }
187
- var a = {};
188
- function Generator() {}
189
- function GeneratorFunction() {}
190
- function GeneratorFunctionPrototype() {}
191
- t = Object.getPrototypeOf;
192
- var c = [][n] ? t(t([][n]())) : (_regeneratorDefine(t = {}, n, function () {
193
- return this;
194
- }), t),
195
- u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c);
196
- function f(e) {
197
- return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e;
198
- }
199
- return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine(u), _regeneratorDefine(u, o, "Generator"), _regeneratorDefine(u, n, function () {
200
- return this;
201
- }), _regeneratorDefine(u, "toString", function () {
202
- return "[object Generator]";
203
- }), (_regenerator = function () {
204
- return {
205
- w: i,
206
- m: f
207
- };
208
- })();
209
- }
210
- function _regeneratorDefine(e, r, n, t) {
211
- var i = Object.defineProperty;
212
- try {
213
- i({}, "", {});
214
- } catch (e) {
215
- i = 0;
216
- }
217
- _regeneratorDefine = function (e, r, n, t) {
218
- function o(r, n) {
219
- _regeneratorDefine(e, r, function (e) {
220
- return this._invoke(r, n, e);
221
- });
222
- }
223
- r ? i ? i(e, r, {
224
- value: n,
225
- enumerable: !t,
226
- configurable: !t,
227
- writable: !t
228
- }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2));
229
- }, _regeneratorDefine(e, r, n, t);
230
- }
231
98
  function _toConsumableArray(r) {
232
99
  return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
233
100
  }
@@ -356,6 +223,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
356
223
  returnFocusOnDeactivate: true,
357
224
  escapeDeactivates: true,
358
225
  delayInitialFocus: true,
226
+ delayReturnFocus: true,
359
227
  isolateSubtrees: false,
360
228
  isKeyForward: isKeyForward,
361
229
  isKeyBackward: isKeyBackward
@@ -505,6 +373,25 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
505
373
  }
506
374
  return node;
507
375
  };
376
+
377
+ /**
378
+ * Gets the current activeElement. If it's a web-component and has open shadow-root
379
+ * it will recursively search inside shadow roots for the "true" activeElement.
380
+ *
381
+ * @param {Document | ShadowRoot} el
382
+ *
383
+ * @returns {HTMLElement|null} The element that currently has the focus. `null` if a focused element isn't found.
384
+ **/
385
+ var _getActiveElement = function getActiveElement(el) {
386
+ var activeElement = el.activeElement;
387
+ if (!activeElement) {
388
+ return null;
389
+ }
390
+ if (activeElement.shadowRoot && activeElement.shadowRoot.activeElement !== null) {
391
+ return _getActiveElement(activeElement.shadowRoot);
392
+ }
393
+ return activeElement;
394
+ };
508
395
  var getInitialFocusNode = function getInitialFocusNode() {
509
396
  var node = getNodeForOption('initialFocus', {
510
397
  hasFallback: true
@@ -515,9 +402,11 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
515
402
  return false;
516
403
  }
517
404
  if (node === undefined || node && !isFocusable(node, config.tabbableOptions)) {
405
+ var activeElement = _getActiveElement(doc);
406
+
518
407
  // option not specified nor focusable: use fallback options
519
- if (findContainerIndex(doc.activeElement) >= 0) {
520
- node = doc.activeElement;
408
+ if (findContainerIndex(activeElement) >= 0) {
409
+ node = activeElement;
521
410
  } else {
522
411
  var firstTabbableGroup = state.tabbableGroups[0];
523
412
  var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
@@ -629,25 +518,6 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
629
518
  throw new Error("At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.");
630
519
  }
631
520
  };
632
-
633
- /**
634
- * Gets the current activeElement. If it's a web-component and has open shadow-root
635
- * it will recursively search inside shadow roots for the "true" activeElement.
636
- *
637
- * @param {Document | ShadowRoot} el
638
- *
639
- * @returns {HTMLElement} The element that currently has the focus
640
- **/
641
- var _getActiveElement = function getActiveElement(el) {
642
- var activeElement = el.activeElement;
643
- if (!activeElement) {
644
- return;
645
- }
646
- if (activeElement.shadowRoot && activeElement.shadowRoot.activeElement !== null) {
647
- return _getActiveElement(activeElement.shadowRoot);
648
- }
649
- return activeElement;
650
- };
651
521
  var _tryFocus = function tryFocus(node) {
652
522
  if (node === false) {
653
523
  return;
@@ -968,12 +838,13 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
968
838
  /**
969
839
  * Adds listeners to the document necessary for trapping focus and attempts to set focus
970
840
  * to the configured initial focus node. Does nothing if the trap isn't active.
971
- * @returns {Promise<void>} Resolved (always) once the initial focus node has been focused.
972
- * Also resolved if the trap isn't active.
841
+ * @returns {Promise<void> | undefined} A promise resolved once the initial focus node has
842
+ * been focused when `delayInitialFocus=true`; `undefined` when focus is set synchronously
843
+ * or the trap isn't active.
973
844
  */
974
845
  var addListeners = function addListeners() {
975
846
  if (!state.active) {
976
- return Promise.resolve();
847
+ return;
977
848
  }
978
849
 
979
850
  // There can be only one listening focus trap at a time
@@ -981,7 +852,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
981
852
 
982
853
  // Delay ensures that the focused element doesn't capture the event
983
854
  // that caused the focus trap activation.
984
- /** @type {Promise<void>} */
855
+ /** @type {Promise<void> | undefined} */
985
856
  var promise;
986
857
  if (config.delayInitialFocus) {
987
858
  // NOTE: Promise constructor callback is called synchronously, which is what we want
@@ -993,7 +864,6 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
993
864
  });
994
865
  });
995
866
  } else {
996
- promise = Promise.resolve();
997
867
  _tryFocus(getInitialFocusNode());
998
868
  }
999
869
  doc.addEventListener('focusin', checkFocusIn, true);
@@ -1102,17 +972,27 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
1102
972
  //
1103
973
 
1104
974
  var checkDomRemoval = function checkDomRemoval(mutations) {
975
+ var focusedNode = state.mostRecentlyFocusedNode;
976
+ if (!focusedNode) {
977
+ return;
978
+ }
1105
979
  var isFocusedNodeRemoved = mutations.some(function (mutation) {
1106
980
  var removedNodes = Array.from(mutation.removedNodes);
1107
981
  return removedNodes.some(function (node) {
1108
- return node === state.mostRecentlyFocusedNode;
982
+ return node === focusedNode || typeof node.contains === 'function' && node.contains(focusedNode);
1109
983
  });
1110
984
  });
1111
985
 
1112
- // If the currently focused is removed then browsers will move focus to the
986
+ // If the currently focused node is removed then browsers will move focus to the
1113
987
  // <body> element. If this happens, try to move focus back into the trap.
1114
- if (isFocusedNodeRemoved) {
1115
- _tryFocus(getInitialFocusNode());
988
+ if (isFocusedNodeRemoved && state.containers.some(function (container) {
989
+ return container === null || container === void 0 ? void 0 : container.isConnected;
990
+ })) {
991
+ // Refresh tabbable state before resolving initial focus because
992
+ // getInitialFocusNode() may fall back to the first tabbable node in the trap.
993
+ updateTabbableNodes();
994
+ var initialFocusNode = getInitialFocusNode();
995
+ _tryFocus(initialFocusNode);
1116
996
  }
1117
997
  };
1118
998
 
@@ -1175,37 +1055,30 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
1175
1055
  onActivate === null || onActivate === void 0 || onActivate({
1176
1056
  trap: trap
1177
1057
  });
1178
- var finishActivation = /*#__PURE__*/function () {
1179
- var _ref6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
1180
- return _regenerator().w(function (_context) {
1181
- while (1) switch (_context.n) {
1182
- case 0:
1183
- if (checkCanFocusTrap) {
1184
- updateTabbableNodes();
1185
- }
1186
-
1187
- // NOTE: wait for initial focus node to get focused before we potentially isolate
1188
- // the subtrees with aria-hidden while focus is still in some other subtree and
1189
- // not yet in the trap, resulting in some browsers (e.g. Chrome) logging to the
1190
- // console that they, "Blocked aria-hidden on an element because its descendant
1191
- // retained focus..."
1192
- _context.n = 1;
1193
- return addListeners();
1194
- case 1:
1195
- trap._setSubtreeIsolation(true);
1196
- updateObservedNodes();
1197
- onPostActivate === null || onPostActivate === void 0 || onPostActivate({
1198
- trap: trap
1199
- });
1200
- case 2:
1201
- return _context.a(2);
1202
- }
1203
- }, _callee);
1204
- }));
1205
- return function finishActivation() {
1206
- return _ref6.apply(this, arguments);
1058
+ var finishActivation = function finishActivation() {
1059
+ if (checkCanFocusTrap) {
1060
+ updateTabbableNodes();
1061
+ }
1062
+ var afterListeners = function afterListeners() {
1063
+ trap._setSubtreeIsolation(true);
1064
+ updateObservedNodes();
1065
+ onPostActivate === null || onPostActivate === void 0 || onPostActivate({
1066
+ trap: trap
1067
+ });
1207
1068
  };
1208
- }();
1069
+
1070
+ // NOTE: wait for initial focus node to get focused (whether activation is fully,
1071
+ // partially, or not asynchronous) before we potentially isolate the subtrees
1072
+ // with aria-hidden while focus is still in some other subtree and not yet in
1073
+ // the trap, resulting in some browsers (e.g. Chrome) logging to the console that
1074
+ // they, "Blocked aria-hidden on an element because its descendant retained focus..."
1075
+ var listenersPromise = addListeners();
1076
+ if (listenersPromise) {
1077
+ listenersPromise.then(afterListeners);
1078
+ } else {
1079
+ afterListeners();
1080
+ }
1081
+ };
1209
1082
  if (checkCanFocusTrap) {
1210
1083
  checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);
1211
1084
  return this;
@@ -1253,20 +1126,26 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
1253
1126
  var onDeactivate = getOption(options, 'onDeactivate');
1254
1127
  var onPostDeactivate = getOption(options, 'onPostDeactivate');
1255
1128
  var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
1129
+ var delayReturnFocus = getOption(options, 'delayReturnFocus');
1256
1130
  var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
1257
1131
  onDeactivate === null || onDeactivate === void 0 || onDeactivate({
1258
1132
  trap: trap
1259
1133
  });
1260
- var finishDeactivation = function finishDeactivation() {
1261
- delay(function () {
1262
- if (returnFocus) {
1263
- _tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
1264
- }
1265
- onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate({
1266
- trap: trap
1267
- });
1134
+ var completeDeactivation = function completeDeactivation() {
1135
+ if (returnFocus) {
1136
+ _tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
1137
+ }
1138
+ onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate({
1139
+ trap: trap
1268
1140
  });
1269
1141
  };
1142
+ var finishDeactivation = function finishDeactivation() {
1143
+ if (delayReturnFocus && returnFocus) {
1144
+ delay(completeDeactivation);
1145
+ } else {
1146
+ completeDeactivation();
1147
+ }
1148
+ };
1270
1149
  if (returnFocus && checkCanReturnFocus) {
1271
1150
  checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);
1272
1151
  return this;
@@ -1339,35 +1218,28 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
1339
1218
  onUnpause === null || onUnpause === void 0 || onUnpause({
1340
1219
  trap: trap
1341
1220
  });
1342
- var finishUnpause = /*#__PURE__*/function () {
1343
- var _ref7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
1344
- return _regenerator().w(function (_context2) {
1345
- while (1) switch (_context2.n) {
1346
- case 0:
1347
- updateTabbableNodes();
1348
-
1349
- // NOTE: wait for initial focus node to get focused before we potentially isolate
1350
- // the subtrees with aria-hidden while focus is still in some other subtree and
1351
- // not yet in the trap, resulting in some browsers (e.g. Chrome) logging to the
1352
- // console that they, "Blocked aria-hidden on an element because its descendant
1353
- // retained focus..."
1354
- _context2.n = 1;
1355
- return addListeners();
1356
- case 1:
1357
- trap._setSubtreeIsolation(true);
1358
- updateObservedNodes();
1359
- onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause({
1360
- trap: trap
1361
- });
1362
- case 2:
1363
- return _context2.a(2);
1364
- }
1365
- }, _callee2);
1366
- }));
1367
- return function finishUnpause() {
1368
- return _ref7.apply(this, arguments);
1221
+ var finishUnpause = function finishUnpause() {
1222
+ updateTabbableNodes();
1223
+ var afterListeners = function afterListeners() {
1224
+ trap._setSubtreeIsolation(true);
1225
+ updateObservedNodes();
1226
+ onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause({
1227
+ trap: trap
1228
+ });
1369
1229
  };
1370
- }();
1230
+
1231
+ // NOTE: wait for initial focus node to get focused (whether activation is fully,
1232
+ // partially, or not asynchronous) before we potentially isolate the subtrees
1233
+ // with aria-hidden while focus is still in some other subtree and not yet in
1234
+ // the trap, resulting in some browsers (e.g. Chrome) logging to the console that
1235
+ // they, "Blocked aria-hidden on an element because its descendant retained focus..."
1236
+ var listenersPromise = addListeners();
1237
+ if (listenersPromise) {
1238
+ listenersPromise.then(afterListeners);
1239
+ } else {
1240
+ afterListeners();
1241
+ }
1242
+ };
1371
1243
  finishUnpause();
1372
1244
  }
1373
1245
  return this;