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.
@@ -1,5 +1,5 @@
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
5
  (function (global, factory) {
@@ -21,31 +21,6 @@
21
21
  function _arrayWithoutHoles(r) {
22
22
  if (Array.isArray(r)) return _arrayLikeToArray(r);
23
23
  }
24
- function asyncGeneratorStep(n, t, e, r, o, a, c) {
25
- try {
26
- var i = n[a](c),
27
- u = i.value;
28
- } catch (n) {
29
- return void e(n);
30
- }
31
- i.done ? t(u) : Promise.resolve(u).then(r, o);
32
- }
33
- function _asyncToGenerator(n) {
34
- return function () {
35
- var t = this,
36
- e = arguments;
37
- return new Promise(function (r, o) {
38
- var a = n.apply(t, e);
39
- function _next(n) {
40
- asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
41
- }
42
- function _throw(n) {
43
- asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
44
- }
45
- _next(void 0);
46
- });
47
- };
48
- }
49
24
  function _createForOfIteratorHelper(r, e) {
50
25
  var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
51
26
  if (!t) {
@@ -129,114 +104,6 @@
129
104
  }
130
105
  return e;
131
106
  }
132
- function _regenerator() {
133
- /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
134
- var e,
135
- t,
136
- r = "function" == typeof Symbol ? Symbol : {},
137
- n = r.iterator || "@@iterator",
138
- o = r.toStringTag || "@@toStringTag";
139
- function i(r, n, o, i) {
140
- var c = n && n.prototype instanceof Generator ? n : Generator,
141
- u = Object.create(c.prototype);
142
- return _regeneratorDefine(u, "_invoke", function (r, n, o) {
143
- var i,
144
- c,
145
- u,
146
- f = 0,
147
- p = o || [],
148
- y = false,
149
- G = {
150
- p: 0,
151
- n: 0,
152
- v: e,
153
- a: d,
154
- f: d.bind(e, 4),
155
- d: function (t, r) {
156
- return i = t, c = 0, u = e, G.n = r, a;
157
- }
158
- };
159
- function d(r, n) {
160
- for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) {
161
- var o,
162
- i = p[t],
163
- d = G.p,
164
- l = i[2];
165
- 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));
166
- }
167
- if (o || r > 1) return a;
168
- throw y = true, n;
169
- }
170
- return function (o, p, l) {
171
- if (f > 1) throw TypeError("Generator is already running");
172
- for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) {
173
- i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u);
174
- try {
175
- if (f = 2, i) {
176
- if (c || (o = "next"), t = i[o]) {
177
- if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object");
178
- if (!t.done) return t;
179
- u = t.value, c < 2 && (c = 0);
180
- } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1);
181
- i = e;
182
- } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break;
183
- } catch (t) {
184
- i = e, c = 1, u = t;
185
- } finally {
186
- f = 1;
187
- }
188
- }
189
- return {
190
- value: t,
191
- done: y
192
- };
193
- };
194
- }(r, o, i), true), u;
195
- }
196
- var a = {};
197
- function Generator() {}
198
- function GeneratorFunction() {}
199
- function GeneratorFunctionPrototype() {}
200
- t = Object.getPrototypeOf;
201
- var c = [][n] ? t(t([][n]())) : (_regeneratorDefine(t = {}, n, function () {
202
- return this;
203
- }), t),
204
- u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c);
205
- function f(e) {
206
- return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e;
207
- }
208
- 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 () {
209
- return this;
210
- }), _regeneratorDefine(u, "toString", function () {
211
- return "[object Generator]";
212
- }), (_regenerator = function () {
213
- return {
214
- w: i,
215
- m: f
216
- };
217
- })();
218
- }
219
- function _regeneratorDefine(e, r, n, t) {
220
- var i = Object.defineProperty;
221
- try {
222
- i({}, "", {});
223
- } catch (e) {
224
- i = 0;
225
- }
226
- _regeneratorDefine = function (e, r, n, t) {
227
- function o(r, n) {
228
- _regeneratorDefine(e, r, function (e) {
229
- return this._invoke(r, n, e);
230
- });
231
- }
232
- r ? i ? i(e, r, {
233
- value: n,
234
- enumerable: !t,
235
- configurable: !t,
236
- writable: !t
237
- }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2));
238
- }, _regeneratorDefine(e, r, n, t);
239
- }
240
107
  function _toConsumableArray(r) {
241
108
  return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
242
109
  }
@@ -365,6 +232,7 @@
365
232
  returnFocusOnDeactivate: true,
366
233
  escapeDeactivates: true,
367
234
  delayInitialFocus: true,
235
+ delayReturnFocus: true,
368
236
  isolateSubtrees: false,
369
237
  isKeyForward: isKeyForward,
370
238
  isKeyBackward: isKeyBackward
@@ -514,6 +382,25 @@
514
382
  }
515
383
  return node;
516
384
  };
385
+
386
+ /**
387
+ * Gets the current activeElement. If it's a web-component and has open shadow-root
388
+ * it will recursively search inside shadow roots for the "true" activeElement.
389
+ *
390
+ * @param {Document | ShadowRoot} el
391
+ *
392
+ * @returns {HTMLElement|null} The element that currently has the focus. `null` if a focused element isn't found.
393
+ **/
394
+ var _getActiveElement = function getActiveElement(el) {
395
+ var activeElement = el.activeElement;
396
+ if (!activeElement) {
397
+ return null;
398
+ }
399
+ if (activeElement.shadowRoot && activeElement.shadowRoot.activeElement !== null) {
400
+ return _getActiveElement(activeElement.shadowRoot);
401
+ }
402
+ return activeElement;
403
+ };
517
404
  var getInitialFocusNode = function getInitialFocusNode() {
518
405
  var node = getNodeForOption('initialFocus', {
519
406
  hasFallback: true
@@ -524,9 +411,11 @@
524
411
  return false;
525
412
  }
526
413
  if (node === undefined || node && !tabbable.isFocusable(node, config.tabbableOptions)) {
414
+ var activeElement = _getActiveElement(doc);
415
+
527
416
  // option not specified nor focusable: use fallback options
528
- if (findContainerIndex(doc.activeElement) >= 0) {
529
- node = doc.activeElement;
417
+ if (findContainerIndex(activeElement) >= 0) {
418
+ node = activeElement;
530
419
  } else {
531
420
  var firstTabbableGroup = state.tabbableGroups[0];
532
421
  var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
@@ -638,25 +527,6 @@
638
527
  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.");
639
528
  }
640
529
  };
641
-
642
- /**
643
- * Gets the current activeElement. If it's a web-component and has open shadow-root
644
- * it will recursively search inside shadow roots for the "true" activeElement.
645
- *
646
- * @param {Document | ShadowRoot} el
647
- *
648
- * @returns {HTMLElement} The element that currently has the focus
649
- **/
650
- var _getActiveElement = function getActiveElement(el) {
651
- var activeElement = el.activeElement;
652
- if (!activeElement) {
653
- return;
654
- }
655
- if (activeElement.shadowRoot && activeElement.shadowRoot.activeElement !== null) {
656
- return _getActiveElement(activeElement.shadowRoot);
657
- }
658
- return activeElement;
659
- };
660
530
  var _tryFocus = function tryFocus(node) {
661
531
  if (node === false) {
662
532
  return;
@@ -977,12 +847,13 @@
977
847
  /**
978
848
  * Adds listeners to the document necessary for trapping focus and attempts to set focus
979
849
  * to the configured initial focus node. Does nothing if the trap isn't active.
980
- * @returns {Promise<void>} Resolved (always) once the initial focus node has been focused.
981
- * Also resolved if the trap isn't active.
850
+ * @returns {Promise<void> | undefined} A promise resolved once the initial focus node has
851
+ * been focused when `delayInitialFocus=true`; `undefined` when focus is set synchronously
852
+ * or the trap isn't active.
982
853
  */
983
854
  var addListeners = function addListeners() {
984
855
  if (!state.active) {
985
- return Promise.resolve();
856
+ return;
986
857
  }
987
858
 
988
859
  // There can be only one listening focus trap at a time
@@ -990,7 +861,7 @@
990
861
 
991
862
  // Delay ensures that the focused element doesn't capture the event
992
863
  // that caused the focus trap activation.
993
- /** @type {Promise<void>} */
864
+ /** @type {Promise<void> | undefined} */
994
865
  var promise;
995
866
  if (config.delayInitialFocus) {
996
867
  // NOTE: Promise constructor callback is called synchronously, which is what we want
@@ -1002,7 +873,6 @@
1002
873
  });
1003
874
  });
1004
875
  } else {
1005
- promise = Promise.resolve();
1006
876
  _tryFocus(getInitialFocusNode());
1007
877
  }
1008
878
  doc.addEventListener('focusin', checkFocusIn, true);
@@ -1111,17 +981,27 @@
1111
981
  //
1112
982
 
1113
983
  var checkDomRemoval = function checkDomRemoval(mutations) {
984
+ var focusedNode = state.mostRecentlyFocusedNode;
985
+ if (!focusedNode) {
986
+ return;
987
+ }
1114
988
  var isFocusedNodeRemoved = mutations.some(function (mutation) {
1115
989
  var removedNodes = Array.from(mutation.removedNodes);
1116
990
  return removedNodes.some(function (node) {
1117
- return node === state.mostRecentlyFocusedNode;
991
+ return node === focusedNode || typeof node.contains === 'function' && node.contains(focusedNode);
1118
992
  });
1119
993
  });
1120
994
 
1121
- // If the currently focused is removed then browsers will move focus to the
995
+ // If the currently focused node is removed then browsers will move focus to the
1122
996
  // <body> element. If this happens, try to move focus back into the trap.
1123
- if (isFocusedNodeRemoved) {
1124
- _tryFocus(getInitialFocusNode());
997
+ if (isFocusedNodeRemoved && state.containers.some(function (container) {
998
+ return container === null || container === void 0 ? void 0 : container.isConnected;
999
+ })) {
1000
+ // Refresh tabbable state before resolving initial focus because
1001
+ // getInitialFocusNode() may fall back to the first tabbable node in the trap.
1002
+ updateTabbableNodes();
1003
+ var initialFocusNode = getInitialFocusNode();
1004
+ _tryFocus(initialFocusNode);
1125
1005
  }
1126
1006
  };
1127
1007
 
@@ -1184,37 +1064,30 @@
1184
1064
  onActivate === null || onActivate === void 0 || onActivate({
1185
1065
  trap: trap
1186
1066
  });
1187
- var finishActivation = /*#__PURE__*/function () {
1188
- var _ref6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
1189
- return _regenerator().w(function (_context) {
1190
- while (1) switch (_context.n) {
1191
- case 0:
1192
- if (checkCanFocusTrap) {
1193
- updateTabbableNodes();
1194
- }
1195
-
1196
- // NOTE: wait for initial focus node to get focused before we potentially isolate
1197
- // the subtrees with aria-hidden while focus is still in some other subtree and
1198
- // not yet in the trap, resulting in some browsers (e.g. Chrome) logging to the
1199
- // console that they, "Blocked aria-hidden on an element because its descendant
1200
- // retained focus..."
1201
- _context.n = 1;
1202
- return addListeners();
1203
- case 1:
1204
- trap._setSubtreeIsolation(true);
1205
- updateObservedNodes();
1206
- onPostActivate === null || onPostActivate === void 0 || onPostActivate({
1207
- trap: trap
1208
- });
1209
- case 2:
1210
- return _context.a(2);
1211
- }
1212
- }, _callee);
1213
- }));
1214
- return function finishActivation() {
1215
- return _ref6.apply(this, arguments);
1067
+ var finishActivation = function finishActivation() {
1068
+ if (checkCanFocusTrap) {
1069
+ updateTabbableNodes();
1070
+ }
1071
+ var afterListeners = function afterListeners() {
1072
+ trap._setSubtreeIsolation(true);
1073
+ updateObservedNodes();
1074
+ onPostActivate === null || onPostActivate === void 0 || onPostActivate({
1075
+ trap: trap
1076
+ });
1216
1077
  };
1217
- }();
1078
+
1079
+ // NOTE: wait for initial focus node to get focused (whether activation is fully,
1080
+ // partially, or not asynchronous) before we potentially isolate the subtrees
1081
+ // with aria-hidden while focus is still in some other subtree and not yet in
1082
+ // the trap, resulting in some browsers (e.g. Chrome) logging to the console that
1083
+ // they, "Blocked aria-hidden on an element because its descendant retained focus..."
1084
+ var listenersPromise = addListeners();
1085
+ if (listenersPromise) {
1086
+ listenersPromise.then(afterListeners);
1087
+ } else {
1088
+ afterListeners();
1089
+ }
1090
+ };
1218
1091
  if (checkCanFocusTrap) {
1219
1092
  checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);
1220
1093
  return this;
@@ -1262,20 +1135,26 @@
1262
1135
  var onDeactivate = getOption(options, 'onDeactivate');
1263
1136
  var onPostDeactivate = getOption(options, 'onPostDeactivate');
1264
1137
  var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
1138
+ var delayReturnFocus = getOption(options, 'delayReturnFocus');
1265
1139
  var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
1266
1140
  onDeactivate === null || onDeactivate === void 0 || onDeactivate({
1267
1141
  trap: trap
1268
1142
  });
1269
- var finishDeactivation = function finishDeactivation() {
1270
- delay(function () {
1271
- if (returnFocus) {
1272
- _tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
1273
- }
1274
- onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate({
1275
- trap: trap
1276
- });
1143
+ var completeDeactivation = function completeDeactivation() {
1144
+ if (returnFocus) {
1145
+ _tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
1146
+ }
1147
+ onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate({
1148
+ trap: trap
1277
1149
  });
1278
1150
  };
1151
+ var finishDeactivation = function finishDeactivation() {
1152
+ if (delayReturnFocus && returnFocus) {
1153
+ delay(completeDeactivation);
1154
+ } else {
1155
+ completeDeactivation();
1156
+ }
1157
+ };
1279
1158
  if (returnFocus && checkCanReturnFocus) {
1280
1159
  checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);
1281
1160
  return this;
@@ -1348,35 +1227,28 @@
1348
1227
  onUnpause === null || onUnpause === void 0 || onUnpause({
1349
1228
  trap: trap
1350
1229
  });
1351
- var finishUnpause = /*#__PURE__*/function () {
1352
- var _ref7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
1353
- return _regenerator().w(function (_context2) {
1354
- while (1) switch (_context2.n) {
1355
- case 0:
1356
- updateTabbableNodes();
1357
-
1358
- // NOTE: wait for initial focus node to get focused before we potentially isolate
1359
- // the subtrees with aria-hidden while focus is still in some other subtree and
1360
- // not yet in the trap, resulting in some browsers (e.g. Chrome) logging to the
1361
- // console that they, "Blocked aria-hidden on an element because its descendant
1362
- // retained focus..."
1363
- _context2.n = 1;
1364
- return addListeners();
1365
- case 1:
1366
- trap._setSubtreeIsolation(true);
1367
- updateObservedNodes();
1368
- onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause({
1369
- trap: trap
1370
- });
1371
- case 2:
1372
- return _context2.a(2);
1373
- }
1374
- }, _callee2);
1375
- }));
1376
- return function finishUnpause() {
1377
- return _ref7.apply(this, arguments);
1230
+ var finishUnpause = function finishUnpause() {
1231
+ updateTabbableNodes();
1232
+ var afterListeners = function afterListeners() {
1233
+ trap._setSubtreeIsolation(true);
1234
+ updateObservedNodes();
1235
+ onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause({
1236
+ trap: trap
1237
+ });
1378
1238
  };
1379
- }();
1239
+
1240
+ // NOTE: wait for initial focus node to get focused (whether activation is fully,
1241
+ // partially, or not asynchronous) before we potentially isolate the subtrees
1242
+ // with aria-hidden while focus is still in some other subtree and not yet in
1243
+ // the trap, resulting in some browsers (e.g. Chrome) logging to the console that
1244
+ // they, "Blocked aria-hidden on an element because its descendant retained focus..."
1245
+ var listenersPromise = addListeners();
1246
+ if (listenersPromise) {
1247
+ listenersPromise.then(afterListeners);
1248
+ } else {
1249
+ afterListeners();
1250
+ }
1251
+ };
1380
1252
  finishUnpause();
1381
1253
  }
1382
1254
  return this;