focus-trap 7.6.5 → 7.7.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 +12 -1
- package/README.md +244 -54
- package/dist/focus-trap.esm.js +236 -36
- 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 +236 -36
- 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 +236 -36
- 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 +10 -6
- package/index.js +194 -42
- package/package.json +43 -33
package/dist/focus-trap.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* focus-trap 7.
|
|
2
|
+
* focus-trap 7.7.0
|
|
3
3
|
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
|
4
4
|
*/
|
|
5
5
|
import { tabbable, focusable, isTabbable, getTabIndex, isFocusable } from 'tabbable';
|
|
@@ -12,6 +12,54 @@ function _arrayLikeToArray(r, a) {
|
|
|
12
12
|
function _arrayWithoutHoles(r) {
|
|
13
13
|
if (Array.isArray(r)) return _arrayLikeToArray(r);
|
|
14
14
|
}
|
|
15
|
+
function _createForOfIteratorHelper(r, e) {
|
|
16
|
+
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
17
|
+
if (!t) {
|
|
18
|
+
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
|
|
19
|
+
t && (r = t);
|
|
20
|
+
var n = 0,
|
|
21
|
+
F = function () {};
|
|
22
|
+
return {
|
|
23
|
+
s: F,
|
|
24
|
+
n: function () {
|
|
25
|
+
return n >= r.length ? {
|
|
26
|
+
done: true
|
|
27
|
+
} : {
|
|
28
|
+
done: false,
|
|
29
|
+
value: r[n++]
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
e: function (r) {
|
|
33
|
+
throw r;
|
|
34
|
+
},
|
|
35
|
+
f: F
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
39
|
+
}
|
|
40
|
+
var o,
|
|
41
|
+
a = true,
|
|
42
|
+
u = false;
|
|
43
|
+
return {
|
|
44
|
+
s: function () {
|
|
45
|
+
t = t.call(r);
|
|
46
|
+
},
|
|
47
|
+
n: function () {
|
|
48
|
+
var r = t.next();
|
|
49
|
+
return a = r.done, r;
|
|
50
|
+
},
|
|
51
|
+
e: function (r) {
|
|
52
|
+
u = true, o = r;
|
|
53
|
+
},
|
|
54
|
+
f: function () {
|
|
55
|
+
try {
|
|
56
|
+
a || null == t.return || t.return();
|
|
57
|
+
} finally {
|
|
58
|
+
if (u) throw o;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
15
63
|
function _defineProperty(e, r, t) {
|
|
16
64
|
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
17
65
|
value: t,
|
|
@@ -73,12 +121,18 @@ function _unsupportedIterableToArray(r, a) {
|
|
|
73
121
|
}
|
|
74
122
|
|
|
75
123
|
var activeFocusTraps = {
|
|
124
|
+
// Returns the trap from the top of the stack.
|
|
125
|
+
getActiveTrap: function getActiveTrap(trapStack) {
|
|
126
|
+
if ((trapStack === null || trapStack === void 0 ? void 0 : trapStack.length) > 0) {
|
|
127
|
+
return trapStack[trapStack.length - 1];
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
},
|
|
131
|
+
// Pauses the currently active trap, then adds a new trap to the stack.
|
|
76
132
|
activateTrap: function activateTrap(trapStack, trap) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
activeTrap._setPausedState(true);
|
|
81
|
-
}
|
|
133
|
+
var activeTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
134
|
+
if (trap !== activeTrap) {
|
|
135
|
+
activeFocusTraps.pauseTrap(trapStack);
|
|
82
136
|
}
|
|
83
137
|
var trapIndex = trapStack.indexOf(trap);
|
|
84
138
|
if (trapIndex === -1) {
|
|
@@ -89,13 +143,24 @@ var activeFocusTraps = {
|
|
|
89
143
|
trapStack.push(trap);
|
|
90
144
|
}
|
|
91
145
|
},
|
|
146
|
+
// Removes the trap from the top of the stack, then unpauses the next trap down.
|
|
92
147
|
deactivateTrap: function deactivateTrap(trapStack, trap) {
|
|
93
148
|
var trapIndex = trapStack.indexOf(trap);
|
|
94
149
|
if (trapIndex !== -1) {
|
|
95
150
|
trapStack.splice(trapIndex, 1);
|
|
96
151
|
}
|
|
97
|
-
|
|
98
|
-
|
|
152
|
+
activeFocusTraps.unpauseTrap(trapStack);
|
|
153
|
+
},
|
|
154
|
+
// Pauses the trap at the top of the stack.
|
|
155
|
+
pauseTrap: function pauseTrap(trapStack) {
|
|
156
|
+
var activeTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
157
|
+
activeTrap === null || activeTrap === void 0 || activeTrap._setPausedState(true);
|
|
158
|
+
},
|
|
159
|
+
// Unpauses the trap at the top of the stack.
|
|
160
|
+
unpauseTrap: function unpauseTrap(trapStack) {
|
|
161
|
+
var activeTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
162
|
+
if (activeTrap && !activeTrap._isManuallyPaused()) {
|
|
163
|
+
activeTrap._setPausedState(false);
|
|
99
164
|
}
|
|
100
165
|
}
|
|
101
166
|
};
|
|
@@ -158,29 +223,31 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
158
223
|
returnFocusOnDeactivate: true,
|
|
159
224
|
escapeDeactivates: true,
|
|
160
225
|
delayInitialFocus: true,
|
|
226
|
+
isolateSubtrees: false,
|
|
161
227
|
isKeyForward: isKeyForward,
|
|
162
228
|
isKeyBackward: isKeyBackward
|
|
163
229
|
}, userOptions);
|
|
164
230
|
var state = {
|
|
165
231
|
// containers given to createFocusTrap()
|
|
166
|
-
|
|
232
|
+
/** @type {Array<HTMLElement>} */
|
|
167
233
|
containers: [],
|
|
168
234
|
// list of objects identifying tabbable nodes in `containers` in the trap
|
|
169
235
|
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap
|
|
170
236
|
// is active, but the trap should never get to a state where there isn't at least one group
|
|
171
237
|
// with at least one tabbable node in it (that would lead to an error condition that would
|
|
172
238
|
// result in an error being thrown)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
239
|
+
/** @type {Array<{
|
|
240
|
+
* container: HTMLElement,
|
|
241
|
+
* tabbableNodes: Array<HTMLElement>, // empty if none
|
|
242
|
+
* focusableNodes: Array<HTMLElement>, // empty if none
|
|
243
|
+
* posTabIndexesFound: boolean,
|
|
244
|
+
* firstTabbableNode: HTMLElement|undefined,
|
|
245
|
+
* lastTabbableNode: HTMLElement|undefined,
|
|
246
|
+
* firstDomTabbableNode: HTMLElement|undefined,
|
|
247
|
+
* lastDomTabbableNode: HTMLElement|undefined,
|
|
248
|
+
* nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
|
|
249
|
+
* }>}
|
|
250
|
+
*/
|
|
184
251
|
containerGroups: [],
|
|
185
252
|
// same order/length as `containers` list
|
|
186
253
|
|
|
@@ -189,6 +256,12 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
189
256
|
// NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
|
|
190
257
|
// the same length
|
|
191
258
|
tabbableGroups: [],
|
|
259
|
+
// references to nodes that are siblings to the ancestors of this trap's containers.
|
|
260
|
+
/** @type {Set<HTMLElement>} */
|
|
261
|
+
adjacentElements: new Set(),
|
|
262
|
+
// references to nodes that were inert before the trap was activated.
|
|
263
|
+
/** @type {Set<HTMLElement>} */
|
|
264
|
+
alreadyInert: new Set(),
|
|
192
265
|
nodeFocusedBeforeActivation: null,
|
|
193
266
|
mostRecentlyFocusedNode: null,
|
|
194
267
|
active: false,
|
|
@@ -792,6 +865,74 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
792
865
|
doc.addEventListener('keydown', checkEscapeKey);
|
|
793
866
|
return trap;
|
|
794
867
|
};
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Traverses up the DOM from each of `containers`, collecting references to
|
|
871
|
+
* the elements that are siblings to `container` or an ancestor of `container`.
|
|
872
|
+
* @param {Array<HTMLElement>} containers
|
|
873
|
+
*/
|
|
874
|
+
var collectAdjacentElements = function collectAdjacentElements(containers) {
|
|
875
|
+
// Re-activate all adjacent elements & clear previous collection.
|
|
876
|
+
if (state.active && !state.paused) {
|
|
877
|
+
trap._setSubtreeIsolation(false);
|
|
878
|
+
}
|
|
879
|
+
state.adjacentElements.clear();
|
|
880
|
+
state.alreadyInert.clear();
|
|
881
|
+
|
|
882
|
+
// Collect all ancestors of all containers to avoid redundant processing.
|
|
883
|
+
var containerAncestors = new Set();
|
|
884
|
+
var adjacentElements = new Set();
|
|
885
|
+
|
|
886
|
+
// Compile all elements adjacent to the focus trap containers & lineage.
|
|
887
|
+
var _iterator = _createForOfIteratorHelper(containers),
|
|
888
|
+
_step;
|
|
889
|
+
try {
|
|
890
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
891
|
+
var container = _step.value;
|
|
892
|
+
containerAncestors.add(container);
|
|
893
|
+
var insideShadowRoot = typeof ShadowRoot !== 'undefined' && container.getRootNode() instanceof ShadowRoot;
|
|
894
|
+
var current = container;
|
|
895
|
+
while (current) {
|
|
896
|
+
containerAncestors.add(current);
|
|
897
|
+
var parent = current.parentElement;
|
|
898
|
+
var siblings = [];
|
|
899
|
+
if (parent) {
|
|
900
|
+
siblings = parent.children;
|
|
901
|
+
} else if (!parent && insideShadowRoot) {
|
|
902
|
+
siblings = current.getRootNode().children;
|
|
903
|
+
parent = current.getRootNode().host;
|
|
904
|
+
insideShadowRoot = typeof ShadowRoot !== 'undefined' && parent.getRootNode() instanceof ShadowRoot;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Add all the children, we'll remove container lineage later.
|
|
908
|
+
var _iterator2 = _createForOfIteratorHelper(siblings),
|
|
909
|
+
_step2;
|
|
910
|
+
try {
|
|
911
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
912
|
+
var child = _step2.value;
|
|
913
|
+
adjacentElements.add(child);
|
|
914
|
+
}
|
|
915
|
+
} catch (err) {
|
|
916
|
+
_iterator2.e(err);
|
|
917
|
+
} finally {
|
|
918
|
+
_iterator2.f();
|
|
919
|
+
}
|
|
920
|
+
current = parent;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Multi-container traps may overlap.
|
|
925
|
+
// Remove elements within container lineages.
|
|
926
|
+
} catch (err) {
|
|
927
|
+
_iterator.e(err);
|
|
928
|
+
} finally {
|
|
929
|
+
_iterator.f();
|
|
930
|
+
}
|
|
931
|
+
containerAncestors.forEach(function (el) {
|
|
932
|
+
adjacentElements["delete"](el);
|
|
933
|
+
});
|
|
934
|
+
state.adjacentElements = adjacentElements;
|
|
935
|
+
};
|
|
795
936
|
var removeListeners = function removeListeners() {
|
|
796
937
|
if (!state.active) {
|
|
797
938
|
return;
|
|
@@ -860,26 +1001,47 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
860
1001
|
var onActivate = getOption(activateOptions, 'onActivate');
|
|
861
1002
|
var onPostActivate = getOption(activateOptions, 'onPostActivate');
|
|
862
1003
|
var checkCanFocusTrap = getOption(activateOptions, 'checkCanFocusTrap');
|
|
863
|
-
|
|
864
|
-
|
|
1004
|
+
|
|
1005
|
+
// If a currently-active trap is isolating its subtree, we need to remove
|
|
1006
|
+
// that isolation to allow the new trap to find tabbable nodes.
|
|
1007
|
+
var preexistingTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
1008
|
+
var revertState = false;
|
|
1009
|
+
if (preexistingTrap && !preexistingTrap.paused) {
|
|
1010
|
+
preexistingTrap._setSubtreeIsolation(false);
|
|
1011
|
+
revertState = true;
|
|
865
1012
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
state.nodeFocusedBeforeActivation = _getActiveElement(doc);
|
|
869
|
-
onActivate === null || onActivate === void 0 || onActivate();
|
|
870
|
-
var finishActivation = function finishActivation() {
|
|
871
|
-
if (checkCanFocusTrap) {
|
|
1013
|
+
try {
|
|
1014
|
+
if (!checkCanFocusTrap) {
|
|
872
1015
|
updateTabbableNodes();
|
|
873
1016
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1017
|
+
state.active = true;
|
|
1018
|
+
state.paused = false;
|
|
1019
|
+
state.nodeFocusedBeforeActivation = _getActiveElement(doc);
|
|
1020
|
+
onActivate === null || onActivate === void 0 || onActivate();
|
|
1021
|
+
var finishActivation = function finishActivation() {
|
|
1022
|
+
if (checkCanFocusTrap) {
|
|
1023
|
+
updateTabbableNodes();
|
|
1024
|
+
}
|
|
1025
|
+
addListeners();
|
|
1026
|
+
updateObservedNodes();
|
|
1027
|
+
if (config.isolateSubtrees) {
|
|
1028
|
+
trap._setSubtreeIsolation(true);
|
|
1029
|
+
}
|
|
1030
|
+
onPostActivate === null || onPostActivate === void 0 || onPostActivate();
|
|
1031
|
+
};
|
|
1032
|
+
if (checkCanFocusTrap) {
|
|
1033
|
+
checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);
|
|
1034
|
+
return this;
|
|
1035
|
+
}
|
|
1036
|
+
finishActivation();
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
// If our activation throws an exception and the stack hasn't changed,
|
|
1039
|
+
// we need to re-enable the prior trap's subtree isolation.
|
|
1040
|
+
if (preexistingTrap === activeFocusTraps.getActiveTrap(trapStack) && revertState) {
|
|
1041
|
+
preexistingTrap._setSubtreeIsolation(true);
|
|
1042
|
+
}
|
|
1043
|
+
throw error;
|
|
881
1044
|
}
|
|
882
|
-
finishActivation();
|
|
883
1045
|
return this;
|
|
884
1046
|
},
|
|
885
1047
|
deactivate: function deactivate(deactivateOptions) {
|
|
@@ -893,6 +1055,15 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
893
1055
|
}, deactivateOptions);
|
|
894
1056
|
clearTimeout(state.delayInitialFocusTimer); // noop if undefined
|
|
895
1057
|
state.delayInitialFocusTimer = undefined;
|
|
1058
|
+
|
|
1059
|
+
// Prior to removing this trap from the trapStack, we need to remove any applications of `inert`.
|
|
1060
|
+
// This allows the next trap down to update its tabbable nodes properly.
|
|
1061
|
+
//
|
|
1062
|
+
// If this trap is not top of the stack, don't change any current isolation.
|
|
1063
|
+
if (!state.paused) {
|
|
1064
|
+
trap._setSubtreeIsolation(false);
|
|
1065
|
+
}
|
|
1066
|
+
state.alreadyInert.clear();
|
|
896
1067
|
removeListeners();
|
|
897
1068
|
state.active = false;
|
|
898
1069
|
state.paused = false;
|
|
@@ -940,8 +1111,14 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
940
1111
|
state.containers = elementsAsArray.map(function (element) {
|
|
941
1112
|
return typeof element === 'string' ? doc.querySelector(element) : element;
|
|
942
1113
|
});
|
|
1114
|
+
if (config.isolateSubtrees) {
|
|
1115
|
+
collectAdjacentElements(state.containers);
|
|
1116
|
+
}
|
|
943
1117
|
if (state.active) {
|
|
944
1118
|
updateTabbableNodes();
|
|
1119
|
+
if (config.isolateSubtrees && !state.paused) {
|
|
1120
|
+
trap._setSubtreeIsolation(true);
|
|
1121
|
+
}
|
|
945
1122
|
}
|
|
946
1123
|
updateObservedNodes();
|
|
947
1124
|
return this;
|
|
@@ -965,11 +1142,13 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
965
1142
|
onPause === null || onPause === void 0 || onPause();
|
|
966
1143
|
removeListeners();
|
|
967
1144
|
updateObservedNodes();
|
|
1145
|
+
trap._setSubtreeIsolation(false);
|
|
968
1146
|
onPostPause === null || onPostPause === void 0 || onPostPause();
|
|
969
1147
|
} else {
|
|
970
1148
|
var onUnpause = getOption(options, 'onUnpause');
|
|
971
1149
|
var onPostUnpause = getOption(options, 'onPostUnpause');
|
|
972
1150
|
onUnpause === null || onUnpause === void 0 || onUnpause();
|
|
1151
|
+
trap._setSubtreeIsolation(true);
|
|
973
1152
|
updateTabbableNodes();
|
|
974
1153
|
addListeners();
|
|
975
1154
|
updateObservedNodes();
|
|
@@ -977,6 +1156,27 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
|
|
|
977
1156
|
}
|
|
978
1157
|
return this;
|
|
979
1158
|
}
|
|
1159
|
+
},
|
|
1160
|
+
_setSubtreeIsolation: {
|
|
1161
|
+
value: function value(isEnabled) {
|
|
1162
|
+
if (config.isolateSubtrees) {
|
|
1163
|
+
state.adjacentElements.forEach(function (el) {
|
|
1164
|
+
if (isEnabled) {
|
|
1165
|
+
// check both attribute and property to ensure initial state is captured
|
|
1166
|
+
// correctly across different browsers and test environments (like JSDOM)
|
|
1167
|
+
var isInitiallyInert = el.inert || el.hasAttribute('inert');
|
|
1168
|
+
if (isInitiallyInert) {
|
|
1169
|
+
state.alreadyInert.add(el);
|
|
1170
|
+
}
|
|
1171
|
+
el.inert = true;
|
|
1172
|
+
} else {
|
|
1173
|
+
if (state.alreadyInert.has(el)) ; else {
|
|
1174
|
+
el.inert = false;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
980
1180
|
}
|
|
981
1181
|
});
|
|
982
1182
|
|