Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

focus-trap

Package Overview
Dependencies
Maintainers
3
Versions
83
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

focus-trap - npm Package Compare versions

Comparing version 6.7.1 to 7.5.2

163

CHANGELOG.md
# Changelog
## 7.5.2
### Patch Changes
- b73eb02: Remove references to `Array.prototype.findLast()` not well supported in Safari ([#996](https://github.com/focus-trap/focus-trap/issues/996))
## 7.5.1
### Patch Changes
- d9e2546: Fix possible exception in new `Tabbable.getTabIndex()` when initializing trap
## 7.5.0
### Minor Changes
- 5e2f913: Adds support for nodes with a positive tabindex in single-container traps only ([#375](https://github.com/focus-trap/focus-trap/issues/375))
## 7.4.3
### Patch Changes
- 134678b: Ensure focus is kept inside the trap when focused element is removed from the DOM ([focus-trap/focus-trap-react#962](https://github.com/focus-trap/focus-trap-react/issues/962))
## 7.4.2
### Patch Changes
- db62ce3: Clicking on open shadowDOM components within a focus trap's container when `clickOutsideDeactivates=true` should not deactivate the focus trap. ([#959](https://github.com/focus-trap/focus-trap/issues/959))
## 7.4.1
### Patch Changes
- 4f720ff: Bump tabbable to v6.1.2 for nwsapi patch
## 7.4.0
### Minor Changes
- da97007: Added new onPost/Pause and onPost/Unpause hooks when un/pausing a trap (also called when auto-un/paused as a result of de/activating a second trap while another is currently active). ([focus-trap-react#948](https://github.com/focus-trap/focus-trap-react/issues/948))
## 7.3.1
### Patch Changes
- a00cfa5: Bump tabbable to v6.1.1 for JSDom fixes
## 7.3.0
### Minor Changes
- ae7129d: Bump tabbable to v6.1.0 adding support for the new HTML `inert` attribute in browsers that support it (NOTE: FireFox does not support it at this time). Also fixes a bug. See tabbable CHANGELOG for more info.
### Patch Changes
- a27ad58: Fix incorrect behavior of `returnFocusOnDeactivate` option when set to true (or defaulted to true) along with `clickOutsideDeactivates=true` and the outside click that deactivates is on a focusable node. Focus was remaining on that node instead of returning to the node focused just prior to activation. ([#893](https://github.com/focus-trap/focus-trap/issues/893))
## 7.2.0
### Minor Changes
- b0482af: Add new `isKeyForward()` and `isKeyBackward()` options ([#612](https://github.com/focus-trap/focus-trap/issues/612))
## 7.1.0
### Minor Changes
- 88cc9bc: Provide new `trapStack` option to make it possible to coordinate auto-activation/pausing between multiple `focus-trap` instances on the same page.
### Patch Changes
- 4f97b38: Bump tabbable to v6.0.1 for a bug fix. See tabbable's changelog for more details.
- f13de76: Mention special Safari setting to enable normal DOM-based tab order in README. [#783](https://github.com/focus-trap/focus-trap/issues/783)
## 7.0.0
### Major Changes
- 5b64423: Revised and clarified official browser support (still as broad and deep as _reasonably_ possible).
- 47f62ac: 🚨 **Breaking:** Tabbable dependency has been updated to v6.0.0 and contains a breaking change related to detached nodes with its default `displayCheck` setting. See tabbable's [changelog](https://github.com/focus-trap/tabbable/blob/master/CHANGELOG.md#600) for more information.
- 5b64423: 🚨 **Breaking:** Dropped support of IE browsers, all versions.
- IE11 was [officially retired](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/) on June 15, 2022 (6 weeks ago). There are no longer any versions of IE that are still maintained or even supported by Microsoft.
## 6.9.4
### Patch Changes
- f68882e: Fix docs and typings to clarify that initialFocus, fallbackFocus, and setReturnFocus options can be functions that also return selector strings.
## 6.9.3
### Patch Changes
- 8a8b1f1: Bump tabbable to v5.3.3 to pick up a small bug fix to web component (shadow DOM) support.
## 6.9.2
### Patch Changes
- ef0ce48: Handle unexpected param (true) passed as the value for the `initialFocus`, `fallbackFocus`, and `setReturnFocus` options: Ignore and perform default behavior.
## 6.9.1
### Patch Changes
- 83262a7: Bumps tabbable to v5.3.2 to pick-up a fix to `displayCheck=full` (default) option behavior that caused issues with detached nodes.
## 6.9.0
### Minor Changes
- 2a57e4b: Add new `trap.active` and `trap.paused` readonly state properties on the trap so that the trap's active/paused state can be queried.
### Patch Changes
- 8fd49df: Fixed bug where `clickOutsideDeactivate` handler would get called on the 'click' event even if the node clicked was in the trap. As with 'mousedown' and 'touchstart' events where this option is also used, the handler should only get called if the target node is _outside_ the trap.
- c32c60a: Fixed: onDeactivate, onPostDeactivate, and checkCanReturnFocus options originally given to createFocusTrap() were not being used by default when calling `trap.deactivate({...})` with an option set even if that option set didn't specify any overrides of these options.
## 6.8.1
### Patch Changes
- 7c86111:
- Bump tabbable to `^5.3.1` (fixing previous update which was incorrectly set to `5.3.0`).
- Fix `tabbableOptions` not being used in all internal uses of tabbable APIs.
- Expose `displayCheck` option in `tabbableOptions` typings and pass it through to tabbable APIs.
- Add info to README about testing traps in JSDom (which is not officially supported).
## 6.8.0
### Minor Changes
- 21458c9: Bumps tabbable to v5.3.0 and includes all changes from the past v6.8.0 beta releases. The big new feature is opt-in Shadow DOM support in tabbable, and a new `getShadowRoot` tabbable option exposed in a new `tabbableOptions` focus-trap config option.
- ⚠️ This will likely break your tests **if you're using JSDom** (e.g. with Jest). See [testing in JSDom](./README.md#testing-in-jsdom) for more info.
## 6.8.0-beta.2
- When updating tabbable nodes, make sure that `getShadowRoot` tabbable option is also passed to `focusable()`.
- Fix bug where having a tabbable node inside a web component in the middle of a tab sequence would cause the tab key to seemingly stop working just before focus should move to it ((#643)[https://github.com/focus-trap/focus-trap/issues/643]).
- Bumps tabbable to `v5.3.0-beta.1`
## 6.8.0-beta.1
- Previous beta didn't include new source. This one does.
## 6.8.0-beta.0
- Adds new `tabbableOptions` configuration option, which allows specifically for the new `getShadowRoot` Tabbable configuration option: `focusTrap.createFocusTrap(rootElement, { tabbableOptions: { getShadowRoot: (node) => closedShadowRoot } })`, for example (where your code has the reference to `closedShadowRoot` previously created on `node` which Tabbable cannot find on its own).
- Bumps tabbable to `v5.3.0-beta.0`
## 6.7.3
### Patch Changes
- ab20d3d: Fix issue with focusing negative tabindex node and then tabbing away when this node is _not_ the last node in the trap's container ((#611)[https://github.com/focus-trap/focus-trap/issues/611])
## 6.7.2
### Patch Changes
- c932330: Fixed bug where tabbing forward from an element with negative tabindex that is last in the trap would result in focus remaining on that element ([565](https://github.com/focus-trap/focus-trap/issues/565))
## 6.7.1

@@ -4,0 +167,0 @@

811

dist/focus-trap.esm.js
/*!
* focus-trap 6.7.1
* focus-trap 7.5.2
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
*/
import { tabbable, isFocusable } from 'tabbable';
import { isFocusable, tabbable, focusable, isTabbable, getTabIndex } from 'tabbable';
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) {
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
}
keys.push.apply(keys, symbols);
enumerableOnly && (symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {

@@ -56,60 +40,71 @@ Object.defineProperty(obj, key, {

}
return obj;
}
function _toPrimitive(input, hint) {
if (typeof input !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (typeof res !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, "string");
return typeof key === "symbol" ? key : String(key);
}
var activeFocusTraps = function () {
var trapQueue = [];
return {
activateTrap: function activateTrap(trap) {
if (trapQueue.length > 0) {
var activeTrap = trapQueue[trapQueue.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
var activeFocusTraps = {
activateTrap: function activateTrap(trapStack, trap) {
if (trapStack.length > 0) {
var activeTrap = trapStack[trapStack.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex === -1) {
trapQueue.push(trap);
} else {
// move this existing trap to the front of the queue
trapQueue.splice(trapIndex, 1);
trapQueue.push(trap);
}
},
deactivateTrap: function deactivateTrap(trap) {
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex !== -1) {
trapQueue.splice(trapIndex, 1);
}
if (trapQueue.length > 0) {
trapQueue[trapQueue.length - 1].unpause();
}
}
};
}();
var trapIndex = trapStack.indexOf(trap);
if (trapIndex === -1) {
trapStack.push(trap);
} else {
// move this existing trap to the front of the queue
trapStack.splice(trapIndex, 1);
trapStack.push(trap);
}
},
deactivateTrap: function deactivateTrap(trapStack, trap) {
var trapIndex = trapStack.indexOf(trap);
if (trapIndex !== -1) {
trapStack.splice(trapIndex, 1);
}
if (trapStack.length > 0) {
trapStack[trapStack.length - 1].unpause();
}
}
};
var isSelectableInput = function isSelectableInput(node) {
return node.tagName && node.tagName.toLowerCase() === 'input' && typeof node.select === 'function';
};
var isEscapeEvent = function isEscapeEvent(e) {
return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
return (e === null || e === void 0 ? void 0 : e.key) === 'Escape' || (e === null || e === void 0 ? void 0 : e.key) === 'Esc' || (e === null || e === void 0 ? void 0 : e.keyCode) === 27;
};
var isTabEvent = function isTabEvent(e) {
return e.key === 'Tab' || e.keyCode === 9;
return (e === null || e === void 0 ? void 0 : e.key) === 'Tab' || (e === null || e === void 0 ? void 0 : e.keyCode) === 9;
};
// checks for TAB by default
var isKeyForward = function isKeyForward(e) {
return isTabEvent(e) && !e.shiftKey;
};
// checks for SHIFT+TAB by default
var isKeyBackward = function isKeyBackward(e) {
return isTabEvent(e) && e.shiftKey;
};
var delay = function delay(fn) {
return setTimeout(fn, 0);
}; // Array.find/findIndex() are not supported on IE; this replicates enough
};
// Array.find/findIndex() are not supported on IE; this replicates enough
// of Array.findIndex() for our needs
var findIndex = function findIndex(arr, fn) {

@@ -125,4 +120,6 @@ var idx = -1;

});
return idx;
};
/**

@@ -135,4 +132,2 @@ * Get an option's value when it could be a plain value, or a handler that provides

*/
var valueOrHandler = function valueOrHandler(value) {

@@ -142,6 +137,4 @@ for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

}
return typeof value === 'function' ? value.apply(void 0, params) : value;
};
var getActualTarget = function getActualTarget(event) {

@@ -158,16 +151,22 @@ // NOTE: If the trap is _inside_ a shadow DOM, event.target will always be the

// NOTE: this must be _outside_ `createFocusTrap()` to make sure all traps in this
// current instance use the same stack if `userOptions.trapStack` isn't specified
var internalTrapStack = [];
var createFocusTrap = function createFocusTrap(elements, userOptions) {
// SSR: a live trap shouldn't be created in this type of environment so this
// should be safe code to execute if the `document` option isn't specified
var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document;
var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || internalTrapStack;
var config = _objectSpread2({
returnFocusOnDeactivate: true,
escapeDeactivates: true,
delayInitialFocus: true
delayInitialFocus: true,
isKeyForward: isKeyForward,
isKeyBackward: isKeyBackward
}, userOptions);
var state = {
// containers given to createFocusTrap()
// @type {Array<HTMLElement>}
containers: [],
// list of objects identifying the first and last tabbable nodes in all containers/groups in
// the trap
// list of objects identifying tabbable nodes in `containers` in the trap
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap

@@ -177,3 +176,20 @@ // is active, but the trap should never get to a state where there isn't at least one group

// result in an error being thrown)
// @type {Array<{ container: HTMLElement, firstTabbableNode: HTMLElement|null, lastTabbableNode: HTMLElement|null }>}
// @type {Array<{
// container: HTMLElement,
// tabbableNodes: Array<HTMLElement>, // empty if none
// focusableNodes: Array<HTMLElement>, // empty if none
// posTabIndexesFound: boolean,
// firstTabbableNode: HTMLElement|undefined,
// lastTabbableNode: HTMLElement|undefined,
// firstDomTabbableNode: HTMLElement|undefined,
// lastDomTabbableNode: HTMLElement|undefined,
// nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
// }>}
containerGroups: [],
// same order/length as `containers` list
// references to objects in `containerGroups`, but only those that actually have
// tabbable nodes in them
// NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
// the same length
tabbableGroups: [],

@@ -186,6 +202,16 @@ nodeFocusedBeforeActivation: null,

// has been delayed during activation
delayInitialFocusTimer: undefined
delayInitialFocusTimer: undefined,
// the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any
recentNavEvent: undefined
};
var trap; // eslint-disable-line prefer-const -- some private functions reference it, and its methods reference private functions, so we must declare here and define later
/**
* Gets a configuration option value.
* @param {Object|undefined} configOverrideOptions If true, and option is defined in this set,
* value will be taken from this object. Otherwise, value will be taken from base configuration.
* @param {string} optionName Name of the option whose value is sought.
* @param {string|undefined} [configOptionName] Name of option to use __instead of__ `optionName`
* IIF `configOverrideOptions` is not defined. Otherwise, `optionName` is used.
*/
var getOption = function getOption(configOverrideOptions, optionName, configOptionName) {

@@ -195,7 +221,30 @@ return configOverrideOptions && configOverrideOptions[optionName] !== undefined ? configOverrideOptions[optionName] : config[configOptionName || optionName];

var containersContain = function containersContain(element) {
return !!(element && state.containers.some(function (container) {
return container.contains(element);
}));
/**
* Finds the index of the container that contains the element.
* @param {HTMLElement} element
* @param {Event} [event] If available, and `element` isn't directly found in any container,
* the event's composed path is used to see if includes any known trap containers in the
* case where the element is inside a Shadow DOM.
* @returns {number} Index of the container in either `state.containers` or
* `state.containerGroups` (the order/length of these lists are the same); -1
* if the element isn't found.
*/
var findContainerIndex = function findContainerIndex(element, event) {
var composedPath = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === 'function' ? event.composedPath() : undefined;
// NOTE: search `containerGroups` because it's possible a group contains no tabbable
// nodes, but still contains focusable nodes (e.g. if they all have `tabindex=-1`)
// and we still need to find the element in there
return state.containerGroups.findIndex(function (_ref) {
var container = _ref.container,
tabbableNodes = _ref.tabbableNodes;
return container.contains(element) || ( // fall back to explicit tabbable search which will take into consideration any
// web components if the `tabbableOptions.getShadowRoot` option was used for
// the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
// look inside web components even if open)
composedPath === null || composedPath === void 0 ? void 0 : composedPath.includes(container)) || tabbableNodes.find(function (node) {
return node === element;
});
});
};
/**

@@ -214,7 +263,4 @@ * Gets the node for the given option, which is expected to be an option that

*/
var getNodeForOption = function getNodeForOption(optionName) {
var optionValue = config[optionName];
if (typeof optionValue === 'function') {

@@ -224,5 +270,7 @@ for (var _len2 = arguments.length, params = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {

}
optionValue = optionValue.apply(void 0, params);
}
if (optionValue === true) {
optionValue = undefined; // use default value
}

@@ -232,8 +280,7 @@ if (!optionValue) {

return optionValue;
} // else, empty string (invalid), null (invalid), 0 (invalid)
}
// else, empty string (invalid), null (invalid), 0 (invalid)
throw new Error("`".concat(optionName, "` was specified but was not a node, or did not return a node"));
}
var node = optionValue; // could be HTMLElement, SVGElement, or non-empty string at this point

@@ -243,3 +290,2 @@

node = doc.querySelector(optionValue); // resolve to node, or null if fails
if (!node) {

@@ -249,50 +295,104 @@ throw new Error("`".concat(optionName, "` as selector refers to no known node"));

}
return node;
};
var getInitialFocusNode = function getInitialFocusNode() {
var node = getNodeForOption('initialFocus'); // false explicitly indicates we want no initialFocus at all
var node = getNodeForOption('initialFocus');
// false explicitly indicates we want no initialFocus at all
if (node === false) {
return false;
}
if (node === undefined) {
// option not specified: use fallback options
if (containersContain(doc.activeElement)) {
if (node === undefined || !isFocusable(node, config.tabbableOptions)) {
// option not specified nor focusable: use fallback options
if (findContainerIndex(doc.activeElement) >= 0) {
node = doc.activeElement;
} else {
var firstTabbableGroup = state.tabbableGroups[0];
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode; // NOTE: `fallbackFocus` option function cannot return `false` (not supported)
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
// NOTE: `fallbackFocus` option function cannot return `false` (not supported)
node = firstTabbableNode || getNodeForOption('fallbackFocus');
}
}
if (!node) {
throw new Error('Your focus-trap needs to have at least one focusable element');
}
return node;
};
var updateTabbableNodes = function updateTabbableNodes() {
state.tabbableGroups = state.containers.map(function (container) {
var tabbableNodes = tabbable(container);
state.containerGroups = state.containers.map(function (container) {
var tabbableNodes = tabbable(container, config.tabbableOptions);
if (tabbableNodes.length > 0) {
return {
container: container,
firstTabbableNode: tabbableNodes[0],
lastTabbableNode: tabbableNodes[tabbableNodes.length - 1]
};
}
// NOTE: if we have tabbable nodes, we must have focusable nodes; focusable nodes
// are a superset of tabbable nodes since nodes with negative `tabindex` attributes
// are focusable but not tabbable
var focusableNodes = focusable(container, config.tabbableOptions);
var firstTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[0] : undefined;
var lastTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : undefined;
var firstDomTabbableNode = focusableNodes.find(function (node) {
return isTabbable(node);
});
var lastDomTabbableNode = focusableNodes.slice().reverse().find(function (node) {
return isTabbable(node);
});
var posTabIndexesFound = !!tabbableNodes.find(function (node) {
return getTabIndex(node) > 0;
});
return {
container: container,
tabbableNodes: tabbableNodes,
focusableNodes: focusableNodes,
/** True if at least one node with positive `tabindex` was found in this container. */
posTabIndexesFound: posTabIndexesFound,
/** First tabbable node in container, __tabindex__ order; `undefined` if none. */
firstTabbableNode: firstTabbableNode,
/** Last tabbable node in container, __tabindex__ order; `undefined` if none. */
lastTabbableNode: lastTabbableNode,
// NOTE: DOM order is NOT NECESSARILY "document position" order, but figuring that out
// would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
// because that API doesn't work with Shadow DOM as well as it should (@see
// https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,
// to address an edge case related to positive tabindex support, this seems like a much easier,
// "close enough most of the time" alternative for positive tabindexes which should generally
// be avoided anyway...
/** First tabbable node in container, __DOM__ order; `undefined` if none. */
firstDomTabbableNode: firstDomTabbableNode,
/** Last tabbable node in container, __DOM__ order; `undefined` if none. */
lastDomTabbableNode: lastDomTabbableNode,
/**
* Finds the __tabbable__ node that follows the given node in the specified direction,
* in this container, if any.
* @param {HTMLElement} node
* @param {boolean} [forward] True if going in forward tab order; false if going
* in reverse.
* @returns {HTMLElement|undefined} The next tabbable node, if any.
*/
nextTabbableNode: function nextTabbableNode(node) {
var forward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var nodeIdx = tabbableNodes.indexOf(node);
if (nodeIdx < 0) {
// either not tabbable nor focusable, or was focused but not tabbable (negative tabindex):
// since `node` should at least have been focusable, we assume that's the case and mimic
// what browsers do, which is set focus to the next node in __document position order__,
// regardless of positive tabindexes, if any -- and for reasons explained in the NOTE
// above related to `firstDomTabbable` and `lastDomTabbable` properties, we fall back to
// basic DOM order
if (forward) {
return focusableNodes.slice(focusableNodes.indexOf(node) + 1).find(function (el) {
return isTabbable(el);
});
}
return focusableNodes.slice(0, focusableNodes.indexOf(node)).reverse().find(function (el) {
return isTabbable(el);
});
}
return tabbableNodes[nodeIdx + (forward ? 1 : -1)];
}
};
});
state.tabbableGroups = state.containerGroups.filter(function (group) {
return group.tabbableNodes.length > 0;
});
return undefined;
}).filter(function (group) {
return !!group;
}); // remove groups with no tabbable nodes
// throw if no groups have tabbable nodes and we don't have a fallback focus node either
if (state.tabbableGroups.length <= 0 && !getNodeForOption('fallbackFocus') // returning false not supported for this option

@@ -302,4 +402,16 @@ ) {

}
// NOTE: Positive tabindexes are only properly supported in single-container traps because
// doing it across multiple containers where tabindexes could be all over the place
// would require Tabbable to support multiple containers, would require additional
// specialized Shadow DOM support, and would require Tabbable's multi-container support
// to look at those containers in document position order rather than user-provided
// order (as they are treated in Focus-trap, for legacy reasons). See discussion on
// https://github.com/focus-trap/focus-trap/issues/375 for more details.
if (state.containerGroups.find(function (g) {
return g.posTabIndexesFound;
}) && state.containerGroups.length > 1) {
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.");
}
};
var tryFocus = function tryFocus(node) {

@@ -309,7 +421,5 @@ if (node === false) {

}
if (node === doc.activeElement) {
return;
}
if (!node || !node.focus) {

@@ -319,8 +429,7 @@ tryFocus(getInitialFocusNode());

}
node.focus({
preventScroll: !!config.preventScroll
});
// NOTE: focus() API does not trigger focusIn event so set MRU node manually
state.mostRecentlyFocusedNode = node;
if (isSelectableInput(node)) {

@@ -330,87 +439,36 @@ node.select();

};
var getReturnFocusNode = function getReturnFocusNode(previousActiveElement) {
var node = getNodeForOption('setReturnFocus', previousActiveElement);
return node ? node : node === false ? false : previousActiveElement;
}; // This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
};
var checkPointerDown = function checkPointerDown(e) {
var target = getActualTarget(e);
if (containersContain(target)) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// if, on deactivation, we should return focus to the node originally-focused
// when the trap was activated (or the configured `setReturnFocus` node),
// then assume it's also OK to return focus to the outside node that was
// just clicked, causing deactivation, as long as that node is focusable;
// if it isn't focusable, then return focus to the original node focused
// on activation (or the configured `setReturnFocus` node)
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked, whether it's focusable or not; by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node)
returnFocus: config.returnFocusOnDeactivate && !isFocusable(target)
});
return;
} // This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
} // otherwise, prevent the click
e.preventDefault();
}; // In case focus escapes the trap for some strange reason, pull it back in.
var checkFocusIn = function checkFocusIn(e) {
var target = getActualTarget(e);
var targetContained = containersContain(target); // In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
e.stopImmediatePropagation();
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}; // Hijack Tab events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
var checkTab = function checkTab(e) {
var target = getActualTarget(e);
/**
* Finds the next node (in either direction) where focus should move according to a
* keyboard focus-in event.
* @param {Object} params
* @param {Node} [params.target] Known target __from which__ to navigate, if any.
* @param {KeyboardEvent|FocusEvent} [params.event] Event to use if `target` isn't known (event
* will be used to determine the `target`). Ignored if `target` is specified.
* @param {boolean} [params.isBackward] True if focus should move backward.
* @returns {Node|undefined} The next node, or `undefined` if a next node couldn't be
* determined given the current state of the trap.
*/
var findNextNavNode = function findNextNavNode(_ref2) {
var target = _ref2.target,
event = _ref2.event,
_ref2$isBackward = _ref2.isBackward,
isBackward = _ref2$isBackward === void 0 ? false : _ref2$isBackward;
target = target || getActualTarget(event);
updateTabbableNodes();
var destinationNode = null;
if (state.tabbableGroups.length > 0) {
// make sure the target is actually contained in a group
// NOTE: the target may also be the container itself if it's tabbable
// NOTE: the target may also be the container itself if it's focusable
// with tabIndex='-1' and was given initial focus
var containerIndex = findIndex(state.tabbableGroups, function (_ref) {
var container = _ref.container;
return container.contains(target);
});
var containerIndex = findContainerIndex(target, event);
var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : undefined;
if (containerIndex < 0) {
// target not found in any group: quite possible focus has escaped the trap,
// so bring it back in to...
if (e.shiftKey) {
// so bring it back into...
if (isBackward) {
// ...the last node in the last group

@@ -422,12 +480,15 @@ destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;

}
} else if (e.shiftKey) {
} else if (isBackward) {
// REVERSE
// is the target the first tabbable node in a group?
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref2) {
var firstTabbableNode = _ref2.firstTabbableNode;
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
var firstTabbableNode = _ref3.firstTabbableNode;
return target === firstTabbableNode;
});
if (startOfGroupIndex < 0 && state.tabbableGroups[containerIndex].container === target) {
// an exception case where the target is the container itself, in which
if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {
// an exception case where the target is either the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle shift+tab as if focus were on the container's

@@ -437,3 +498,2 @@ // first tabbable node, and go to the last tabbable node of the LAST group

}
if (startOfGroupIndex >= 0) {

@@ -445,14 +505,21 @@ // YES: then shift+tab should go to the last tabbable node in the

var destinationGroup = state.tabbableGroups[destinationGroupIndex];
destinationNode = destinationGroup.lastTabbableNode;
destinationNode = getTabIndex(target) >= 0 ? destinationGroup.lastTabbableNode : destinationGroup.lastDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target, false);
}
} else {
// FORWARD
// is the target the last tabbable node in a group?
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
var lastTabbableNode = _ref3.lastTabbableNode;
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref4) {
var lastTabbableNode = _ref4.lastTabbableNode;
return target === lastTabbableNode;
});
if (lastOfGroupIndex < 0 && state.tabbableGroups[containerIndex].container === target) {
// an exception case where the target is the container itself, in which
if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {
// an exception case where the target is the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle tab as if focus were on the container's

@@ -462,3 +529,2 @@ // last tabbable node, and go to the first tabbable node of the FIRST group

}
if (lastOfGroupIndex >= 0) {

@@ -469,63 +535,214 @@ // YES: then tab should go to the first tabbable node in the next

var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
destinationNode = _destinationGroup.firstTabbableNode;
destinationNode = getTabIndex(target) >= 0 ? _destinationGroup.firstTabbableNode : _destinationGroup.firstDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target);
}
}
} else {
// no groups available
// NOTE: the fallbackFocus option does not support returning false to opt-out
destinationNode = getNodeForOption('fallbackFocus');
}
return destinationNode;
};
// This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
var checkPointerDown = function checkPointerDown(e) {
var target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked (and if not focusable, to "nothing"); by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node), whether the
// outside click was on a focusable node or not
returnFocus: config.returnFocusOnDeactivate
});
return;
}
// This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
}
// otherwise, prevent the click
e.preventDefault();
};
// In case focus escapes the trap for some strange reason, pull it back in.
// NOTE: the focusIn event is NOT cancelable, so if focus escapes, it may cause unexpected
// scrolling if the node that got focused was out of view; there's nothing we can do to
// prevent that from happening by the time we discover that focus escaped
var checkFocusIn = function checkFocusIn(event) {
var target = getActualTarget(event);
var targetContained = findContainerIndex(target, event) >= 0;
// In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
event.stopImmediatePropagation();
// focus will escape if the MRU node had a positive tab index and user tried to nav forward;
// it will also escape if the MRU node had a 0 tab index and user tried to nav backward
// toward a node with a positive tab index
var nextNode; // next node to focus, if we find one
var navAcrossContainers = true;
if (state.mostRecentlyFocusedNode) {
if (getTabIndex(state.mostRecentlyFocusedNode) > 0) {
// MRU container index must be >=0 otherwise we wouldn't have it as an MRU node...
var mruContainerIdx = findContainerIndex(state.mostRecentlyFocusedNode);
// there MAY not be any tabbable nodes in the container if there are at least 2 containers
// and the MRU node is focusable but not tabbable (focus-trap requires at least 1 container
// with at least one tabbable node in order to function, so this could be the other container
// with nothing tabbable in it)
var tabbableNodes = state.containerGroups[mruContainerIdx].tabbableNodes;
if (tabbableNodes.length > 0) {
// MRU tab index MAY not be found if the MRU node is focusable but not tabbable
var mruTabIdx = tabbableNodes.findIndex(function (node) {
return node === state.mostRecentlyFocusedNode;
});
if (mruTabIdx >= 0) {
if (config.isKeyForward(state.recentNavEvent)) {
if (mruTabIdx + 1 < tabbableNodes.length) {
nextNode = tabbableNodes[mruTabIdx + 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
} else {
if (mruTabIdx - 1 >= 0) {
nextNode = tabbableNodes[mruTabIdx - 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
}
// else, don't find in container order without considering direction too
}
}
// else, no tabbable nodes in that container (which means we must have at least one other
// container with at least one tabbable node in it, otherwise focus-trap would've thrown
// an error the last time updateTabbableNodes() was run): find next node among all known
// containers
} else {
// check to see if there's at least one tabbable node with a positive tab index inside
// the trap because focus seems to escape when navigating backward from a tabbable node
// with tabindex=0 when this is the case (instead of wrapping to the tabbable node with
// the greatest positive tab index like it should)
if (!state.containerGroups.some(function (g) {
return g.tabbableNodes.some(function (n) {
return getTabIndex(n) > 0;
});
})) {
// no containers with tabbable nodes with positive tab indexes which means the focus
// escaped for some other reason and we should just execute the fallback to the
// MRU node or initial focus node, if any
navAcrossContainers = false;
}
}
} else {
// no MRU node means we're likely in some initial condition when the trap has just
// been activated and initial focus hasn't been given yet, in which case we should
// fall through to trying to focus the initial focus node, which is what should
// happen below at this point in the logic
navAcrossContainers = false;
}
if (navAcrossContainers) {
nextNode = findNextNavNode({
// move FROM the MRU node, not event-related node (which will be the node that is
// outside the trap causing the focus escape we're trying to fix)
target: state.mostRecentlyFocusedNode,
isBackward: config.isKeyBackward(state.recentNavEvent)
});
}
if (nextNode) {
tryFocus(nextNode);
} else {
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}
state.recentNavEvent = undefined; // clear
};
// Hijack key nav events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
var checkKeyNav = function checkKeyNav(event) {
var isBackward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
state.recentNavEvent = event;
var destinationNode = findNextNavNode({
event: event,
isBackward: isBackward
});
if (destinationNode) {
e.preventDefault();
if (isTabEvent(event)) {
// since tab natively moves focus, we wouldn't have a destination node unless we
// were on the edge of a container and had to move to the next/previous edge, in
// which case we want to prevent default to keep the browser from moving focus
// to where it normally would
event.preventDefault();
}
tryFocus(destinationNode);
} // else, let the browser take care of [shift+]tab and move the focus
}
// else, let the browser take care of [shift+]tab and move the focus
};
var checkKey = function checkKey(e) {
if (isEscapeEvent(e) && valueOrHandler(config.escapeDeactivates, e) !== false) {
e.preventDefault();
var checkKey = function checkKey(event) {
if (isEscapeEvent(event) && valueOrHandler(config.escapeDeactivates, event) !== false) {
event.preventDefault();
trap.deactivate();
return;
}
if (isTabEvent(e)) {
checkTab(e);
return;
if (config.isKeyForward(event) || config.isKeyBackward(event)) {
checkKeyNav(event, config.isKeyBackward(event));
}
};
var checkClick = function checkClick(e) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
var target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
return;
}
var target = getActualTarget(e);
if (containersContain(target)) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
return;
}
if (valueOrHandler(config.allowOutsideClick, e)) {
return;
}
e.preventDefault();
e.stopImmediatePropagation();
}; //
};
//
// EVENT LISTENERS
//
var addListeners = function addListeners() {
if (!state.active) {
return;
} // There can be only one listening focus trap at a time
}
// There can be only one listening focus trap at a time
activeFocusTraps.activateTrap(trapStack, trap);
activeFocusTraps.activateTrap(trap); // Delay ensures that the focused element doesn't capture the event
// Delay ensures that the focused element doesn't capture the event
// that caused the focus trap activation.
state.delayInitialFocusTimer = config.delayInitialFocus ? delay(function () {

@@ -553,3 +770,2 @@ tryFocus(getInitialFocusNode());

};
var removeListeners = function removeListeners() {

@@ -559,3 +775,2 @@ if (!state.active) {

}
doc.removeEventListener('focusin', checkFocusIn, true);

@@ -567,8 +782,52 @@ doc.removeEventListener('mousedown', checkPointerDown, true);

return trap;
}; //
};
//
// MUTATION OBSERVER
//
var checkDomRemoval = function checkDomRemoval(mutations) {
var isFocusedNodeRemoved = mutations.some(function (mutation) {
var removedNodes = Array.from(mutation.removedNodes);
return removedNodes.some(function (node) {
return node === state.mostRecentlyFocusedNode;
});
});
// If the currently focused is removed then browsers will move focus to the
// <body> element. If this happens, try to move focus back into the trap.
if (isFocusedNodeRemoved) {
tryFocus(getInitialFocusNode());
}
};
// Use MutationObserver - if supported - to detect if focused node is removed
// from the DOM.
var mutationObserver = typeof window !== 'undefined' && 'MutationObserver' in window ? new MutationObserver(checkDomRemoval) : undefined;
var updateObservedNodes = function updateObservedNodes() {
if (!mutationObserver) {
return;
}
mutationObserver.disconnect();
if (state.active && !state.paused) {
state.containers.map(function (container) {
mutationObserver.observe(container, {
subtree: true,
childList: true
});
});
}
};
//
// TRAP DEFINITION
//
trap = {
get active() {
return state.active;
},
get paused() {
return state.paused;
},
activate: function activate(activateOptions) {

@@ -578,19 +837,12 @@ if (state.active) {

}
var onActivate = getOption(activateOptions, 'onActivate');
var onPostActivate = getOption(activateOptions, 'onPostActivate');
var checkCanFocusTrap = getOption(activateOptions, 'checkCanFocusTrap');
if (!checkCanFocusTrap) {
updateTabbableNodes();
}
state.active = true;
state.paused = false;
state.nodeFocusedBeforeActivation = doc.activeElement;
if (onActivate) {
onActivate();
}
onActivate === null || onActivate === void 0 ? void 0 : onActivate();
var finishActivation = function finishActivation() {

@@ -600,10 +852,6 @@ if (checkCanFocusTrap) {

}
addListeners();
if (onPostActivate) {
onPostActivate();
}
updateObservedNodes();
onPostActivate === null || onPostActivate === void 0 ? void 0 : onPostActivate();
};
if (checkCanFocusTrap) {

@@ -613,3 +861,2 @@ checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);

}
finishActivation();

@@ -622,5 +869,8 @@ return this;

}
var options = _objectSpread2({
onDeactivate: config.onDeactivate,
onPostDeactivate: config.onPostDeactivate,
checkCanReturnFocus: config.checkCanReturnFocus
}, deactivateOptions);
clearTimeout(state.delayInitialFocusTimer); // noop if undefined
state.delayInitialFocusTimer = undefined;

@@ -630,13 +880,9 @@ removeListeners();

state.paused = false;
activeFocusTraps.deactivateTrap(trap);
var onDeactivate = getOption(deactivateOptions, 'onDeactivate');
var onPostDeactivate = getOption(deactivateOptions, 'onPostDeactivate');
var checkCanReturnFocus = getOption(deactivateOptions, 'checkCanReturnFocus');
if (onDeactivate) {
onDeactivate();
}
var returnFocus = getOption(deactivateOptions, 'returnFocus', 'returnFocusOnDeactivate');
updateObservedNodes();
activeFocusTraps.deactivateTrap(trapStack, trap);
var onDeactivate = getOption(options, 'onDeactivate');
var onPostDeactivate = getOption(options, 'onPostDeactivate');
var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
onDeactivate === null || onDeactivate === void 0 ? void 0 : onDeactivate();
var finishDeactivation = function finishDeactivation() {

@@ -647,9 +893,5 @@ delay(function () {

}
if (onPostDeactivate) {
onPostDeactivate();
}
onPostDeactivate === null || onPostDeactivate === void 0 ? void 0 : onPostDeactivate();
});
};
if (returnFocus && checkCanReturnFocus) {

@@ -659,23 +901,30 @@ checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);

}
finishDeactivation();
return this;
},
pause: function pause() {
pause: function pause(pauseOptions) {
if (state.paused || !state.active) {
return this;
}
var onPause = getOption(pauseOptions, 'onPause');
var onPostPause = getOption(pauseOptions, 'onPostPause');
state.paused = true;
onPause === null || onPause === void 0 ? void 0 : onPause();
removeListeners();
updateObservedNodes();
onPostPause === null || onPostPause === void 0 ? void 0 : onPostPause();
return this;
},
unpause: function unpause() {
unpause: function unpause(unpauseOptions) {
if (!state.paused || !state.active) {
return this;
}
var onUnpause = getOption(unpauseOptions, 'onUnpause');
var onPostUnpause = getOption(unpauseOptions, 'onPostUnpause');
state.paused = false;
onUnpause === null || onUnpause === void 0 ? void 0 : onUnpause();
updateTabbableNodes();
addListeners();
updateObservedNodes();
onPostUnpause === null || onPostUnpause === void 0 ? void 0 : onPostUnpause();
return this;

@@ -688,11 +937,11 @@ },

});
if (state.active) {
updateTabbableNodes();
}
updateObservedNodes();
return this;
}
}; // initialize container elements
};
// initialize container elements
trap.updateContainerElements(elements);

@@ -699,0 +948,0 @@ return trap;

/*!
* focus-trap 6.7.1
* focus-trap 7.5.2
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
*/
import{tabbable as e,isFocusable as t}from"tabbable";function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var r,o=(r=[],{activateTrap:function(e){if(r.length>0){var t=r[r.length-1];t!==e&&t.pause()}var n=r.indexOf(e);-1===n||r.splice(n,1),r.push(e)},deactivateTrap:function(e){var t=r.indexOf(e);-1!==t&&r.splice(t,1),r.length>0&&r[r.length-1].unpause()}}),i=function(e){return setTimeout(e,0)},c=function(e,t){var n=-1;return e.every((function(e,a){return!t(e)||(n=a,!1)})),n},u=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),a=1;a<t;a++)n[a-1]=arguments[a];return"function"==typeof e?e.apply(void 0,n):e},s=function(e){return e.target.shadowRoot&&"function"==typeof e.composedPath?e.composedPath()[0]:e.target},l=function(r,l){var f,v=(null==l?void 0:l.document)||document,b=function(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?n(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):n(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}({returnFocusOnDeactivate:!0,escapeDeactivates:!0,delayInitialFocus:!0},l),p={containers:[],tabbableGroups:[],nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1,delayInitialFocusTimer:void 0},d=function(e,t,n){return e&&void 0!==e[t]?e[t]:b[n||t]},h=function(e){return!(!e||!p.containers.some((function(t){return t.contains(e)})))},m=function(e){var t=b[e];if("function"==typeof t){for(var n=arguments.length,a=new Array(n>1?n-1:0),r=1;r<n;r++)a[r-1]=arguments[r];t=t.apply(void 0,a)}if(!t){if(void 0===t||!1===t)return t;throw new Error("`".concat(e,"` was specified but was not a node, or did not return a node"))}var o=t;if("string"==typeof t&&!(o=v.querySelector(t)))throw new Error("`".concat(e,"` as selector refers to no known node"));return o},y=function(){var e=m("initialFocus");if(!1===e)return!1;if(void 0===e)if(h(v.activeElement))e=v.activeElement;else{var t=p.tabbableGroups[0];e=t&&t.firstTabbableNode||m("fallbackFocus")}if(!e)throw new Error("Your focus-trap needs to have at least one focusable element");return e},g=function(){if(p.tabbableGroups=p.containers.map((function(t){var n=e(t);if(n.length>0)return{container:t,firstTabbableNode:n[0],lastTabbableNode:n[n.length-1]}})).filter((function(e){return!!e})),p.tabbableGroups.length<=0&&!m("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times")},w=function e(t){!1!==t&&t!==v.activeElement&&(t&&t.focus?(t.focus({preventScroll:!!b.preventScroll}),p.mostRecentlyFocusedNode=t,function(e){return e.tagName&&"input"===e.tagName.toLowerCase()&&"function"==typeof e.select}(t)&&t.select()):e(y()))},O=function(e){var t=m("setReturnFocus",e);return t||!1!==t&&e},F=function(e){var n=s(e);h(n)||(u(b.clickOutsideDeactivates,e)?f.deactivate({returnFocus:b.returnFocusOnDeactivate&&!t(n)}):u(b.allowOutsideClick,e)||e.preventDefault())},E=function(e){var t=s(e),n=h(t);n||t instanceof Document?n&&(p.mostRecentlyFocusedNode=t):(e.stopImmediatePropagation(),w(p.mostRecentlyFocusedNode||y()))},T=function(e){if(function(e){return"Escape"===e.key||"Esc"===e.key||27===e.keyCode}(e)&&!1!==u(b.escapeDeactivates,e))return e.preventDefault(),void f.deactivate();(function(e){return"Tab"===e.key||9===e.keyCode})(e)&&function(e){var t=s(e);g();var n=null;if(p.tabbableGroups.length>0){var a=c(p.tabbableGroups,(function(e){return e.container.contains(t)}));if(a<0)n=e.shiftKey?p.tabbableGroups[p.tabbableGroups.length-1].lastTabbableNode:p.tabbableGroups[0].firstTabbableNode;else if(e.shiftKey){var r=c(p.tabbableGroups,(function(e){var n=e.firstTabbableNode;return t===n}));if(r<0&&p.tabbableGroups[a].container===t&&(r=a),r>=0){var o=0===r?p.tabbableGroups.length-1:r-1;n=p.tabbableGroups[o].lastTabbableNode}}else{var i=c(p.tabbableGroups,(function(e){var n=e.lastTabbableNode;return t===n}));if(i<0&&p.tabbableGroups[a].container===t&&(i=a),i>=0){var u=i===p.tabbableGroups.length-1?0:i+1;n=p.tabbableGroups[u].firstTabbableNode}}}else n=m("fallbackFocus");n&&(e.preventDefault(),w(n))}(e)},k=function(e){if(!u(b.clickOutsideDeactivates,e)){var t=s(e);h(t)||u(b.allowOutsideClick,e)||(e.preventDefault(),e.stopImmediatePropagation())}},D=function(){if(p.active)return o.activateTrap(f),p.delayInitialFocusTimer=b.delayInitialFocus?i((function(){w(y())})):w(y()),v.addEventListener("focusin",E,!0),v.addEventListener("mousedown",F,{capture:!0,passive:!1}),v.addEventListener("touchstart",F,{capture:!0,passive:!1}),v.addEventListener("click",k,{capture:!0,passive:!1}),v.addEventListener("keydown",T,{capture:!0,passive:!1}),f},G=function(){if(p.active)return v.removeEventListener("focusin",E,!0),v.removeEventListener("mousedown",F,!0),v.removeEventListener("touchstart",F,!0),v.removeEventListener("click",k,!0),v.removeEventListener("keydown",T,!0),f};return(f={activate:function(e){if(p.active)return this;var t=d(e,"onActivate"),n=d(e,"onPostActivate"),a=d(e,"checkCanFocusTrap");a||g(),p.active=!0,p.paused=!1,p.nodeFocusedBeforeActivation=v.activeElement,t&&t();var r=function(){a&&g(),D(),n&&n()};return a?(a(p.containers.concat()).then(r,r),this):(r(),this)},deactivate:function(e){if(!p.active)return this;clearTimeout(p.delayInitialFocusTimer),p.delayInitialFocusTimer=void 0,G(),p.active=!1,p.paused=!1,o.deactivateTrap(f);var t=d(e,"onDeactivate"),n=d(e,"onPostDeactivate"),a=d(e,"checkCanReturnFocus");t&&t();var r=d(e,"returnFocus","returnFocusOnDeactivate"),c=function(){i((function(){r&&w(O(p.nodeFocusedBeforeActivation)),n&&n()}))};return r&&a?(a(O(p.nodeFocusedBeforeActivation)).then(c,c),this):(c(),this)},pause:function(){return p.paused||!p.active||(p.paused=!0,G()),this},unpause:function(){return p.paused&&p.active?(p.paused=!1,g(),D(),this):this},updateContainerElements:function(e){var t=[].concat(e).filter(Boolean);return p.containers=t.map((function(e){return"string"==typeof e?v.querySelector(e):e})),p.active&&g(),this}}).updateContainerElements(r),f};export{l as createFocusTrap};
import{isFocusable as e,tabbable as t,focusable as n,isTabbable as o,getTabIndex as r}from"tabbable";function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){u(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function u(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t||"default");if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var c=function(e,t){if(e.length>0){var n=e[e.length-1];n!==t&&n.pause()}var o=e.indexOf(t);-1===o||e.splice(o,1),e.push(t)},s=function(e,t){var n=e.indexOf(t);-1!==n&&e.splice(n,1),e.length>0&&e[e.length-1].unpause()},l=function(e){return"Tab"===(null==e?void 0:e.key)||9===(null==e?void 0:e.keyCode)},d=function(e){return l(e)&&!e.shiftKey},f=function(e){return l(e)&&e.shiftKey},b=function(e){return setTimeout(e,0)},v=function(e,t){var n=-1;return e.every((function(e,o){return!t(e)||(n=o,!1)})),n},p=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),o=1;o<t;o++)n[o-1]=arguments[o];return"function"==typeof e?e.apply(void 0,n):e},m=function(e){return e.target.shadowRoot&&"function"==typeof e.composedPath?e.composedPath()[0]:e.target},y=[],h=function(a,u){var h,w=(null==u?void 0:u.document)||document,g=(null==u?void 0:u.trapStack)||y,N=i({returnFocusOnDeactivate:!0,escapeDeactivates:!0,delayInitialFocus:!0,isKeyForward:d,isKeyBackward:f},u),F={containers:[],containerGroups:[],tabbableGroups:[],nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1,delayInitialFocusTimer:void 0,recentNavEvent:void 0},O=function(e,t,n){return e&&void 0!==e[t]?e[t]:N[n||t]},E=function(e,t){var n="function"==typeof(null==t?void 0:t.composedPath)?t.composedPath():void 0;return F.containerGroups.findIndex((function(t){var o=t.container,r=t.tabbableNodes;return o.contains(e)||(null==n?void 0:n.includes(o))||r.find((function(t){return t===e}))}))},k=function(e){var t=N[e];if("function"==typeof t){for(var n=arguments.length,o=new Array(n>1?n-1:0),r=1;r<n;r++)o[r-1]=arguments[r];t=t.apply(void 0,o)}if(!0===t&&(t=void 0),!t){if(void 0===t||!1===t)return t;throw new Error("`".concat(e,"` was specified but was not a node, or did not return a node"))}var a=t;if("string"==typeof t&&!(a=w.querySelector(t)))throw new Error("`".concat(e,"` as selector refers to no known node"));return a},T=function(){var t=k("initialFocus");if(!1===t)return!1;if(void 0===t||!e(t,N.tabbableOptions))if(E(w.activeElement)>=0)t=w.activeElement;else{var n=F.tabbableGroups[0];t=n&&n.firstTabbableNode||k("fallbackFocus")}if(!t)throw new Error("Your focus-trap needs to have at least one focusable element");return t},D=function(){if(F.containerGroups=F.containers.map((function(e){var a=t(e,N.tabbableOptions),i=n(e,N.tabbableOptions),u=a.length>0?a[0]:void 0,c=a.length>0?a[a.length-1]:void 0,s=i.find((function(e){return o(e)})),l=i.slice().reverse().find((function(e){return o(e)})),d=!!a.find((function(e){return r(e)>0}));return{container:e,tabbableNodes:a,focusableNodes:i,posTabIndexesFound:d,firstTabbableNode:u,lastTabbableNode:c,firstDomTabbableNode:s,lastDomTabbableNode:l,nextTabbableNode:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],n=a.indexOf(e);return n<0?t?i.slice(i.indexOf(e)+1).find((function(e){return o(e)})):i.slice(0,i.indexOf(e)).reverse().find((function(e){return o(e)})):a[n+(t?1:-1)]}}})),F.tabbableGroups=F.containerGroups.filter((function(e){return e.tabbableNodes.length>0})),F.tabbableGroups.length<=0&&!k("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");if(F.containerGroups.find((function(e){return e.posTabIndexesFound}))&&F.containerGroups.length>1)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.")},P=function e(t){!1!==t&&t!==w.activeElement&&(t&&t.focus?(t.focus({preventScroll:!!N.preventScroll}),F.mostRecentlyFocusedNode=t,function(e){return e.tagName&&"input"===e.tagName.toLowerCase()&&"function"==typeof e.select}(t)&&t.select()):e(T()))},G=function(e){var t=k("setReturnFocus",e);return t||!1!==t&&e},x=function(t){var n=t.target,a=t.event,i=t.isBackward,u=void 0!==i&&i;n=n||m(a),D();var c=null;if(F.tabbableGroups.length>0){var s=E(n,a),d=s>=0?F.containerGroups[s]:void 0;if(s<0)c=u?F.tabbableGroups[F.tabbableGroups.length-1].lastTabbableNode:F.tabbableGroups[0].firstTabbableNode;else if(u){var f=v(F.tabbableGroups,(function(e){var t=e.firstTabbableNode;return n===t}));if(f<0&&(d.container===n||e(n,N.tabbableOptions)&&!o(n,N.tabbableOptions)&&!d.nextTabbableNode(n,!1))&&(f=s),f>=0){var b=0===f?F.tabbableGroups.length-1:f-1,p=F.tabbableGroups[b];c=r(n)>=0?p.lastTabbableNode:p.lastDomTabbableNode}else l(a)||(c=d.nextTabbableNode(n,!1))}else{var y=v(F.tabbableGroups,(function(e){var t=e.lastTabbableNode;return n===t}));if(y<0&&(d.container===n||e(n,N.tabbableOptions)&&!o(n,N.tabbableOptions)&&!d.nextTabbableNode(n))&&(y=s),y>=0){var h=y===F.tabbableGroups.length-1?0:y+1,w=F.tabbableGroups[h];c=r(n)>=0?w.firstTabbableNode:w.firstDomTabbableNode}else l(a)||(c=d.nextTabbableNode(n))}}else c=k("fallbackFocus");return c},R=function(e){var t=m(e);E(t,e)>=0||(p(N.clickOutsideDeactivates,e)?h.deactivate({returnFocus:N.returnFocusOnDeactivate}):p(N.allowOutsideClick,e)||e.preventDefault())},j=function(e){var t=m(e),n=E(t,e)>=0;if(n||t instanceof Document)n&&(F.mostRecentlyFocusedNode=t);else{var o;e.stopImmediatePropagation();var a=!0;if(F.mostRecentlyFocusedNode)if(r(F.mostRecentlyFocusedNode)>0){var i=E(F.mostRecentlyFocusedNode),u=F.containerGroups[i].tabbableNodes;if(u.length>0){var c=u.findIndex((function(e){return e===F.mostRecentlyFocusedNode}));c>=0&&(N.isKeyForward(F.recentNavEvent)?c+1<u.length&&(o=u[c+1],a=!1):c-1>=0&&(o=u[c-1],a=!1))}}else F.containerGroups.some((function(e){return e.tabbableNodes.some((function(e){return r(e)>0}))}))||(a=!1);else a=!1;a&&(o=x({target:F.mostRecentlyFocusedNode,isBackward:N.isKeyBackward(F.recentNavEvent)})),P(o||(F.mostRecentlyFocusedNode||T()))}F.recentNavEvent=void 0},B=function(e){if(!(t=e,"Escape"!==(null==t?void 0:t.key)&&"Esc"!==(null==t?void 0:t.key)&&27!==(null==t?void 0:t.keyCode)||!1===p(N.escapeDeactivates,e)))return e.preventDefault(),void h.deactivate();var t;(N.isKeyForward(e)||N.isKeyBackward(e))&&function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];F.recentNavEvent=e;var n=x({event:e,isBackward:t});n&&(l(e)&&e.preventDefault(),P(n))}(e,N.isKeyBackward(e))},I=function(e){var t=m(e);E(t,e)>=0||p(N.clickOutsideDeactivates,e)||p(N.allowOutsideClick,e)||(e.preventDefault(),e.stopImmediatePropagation())},L=function(){if(F.active)return c(g,h),F.delayInitialFocusTimer=N.delayInitialFocus?b((function(){P(T())})):P(T()),w.addEventListener("focusin",j,!0),w.addEventListener("mousedown",R,{capture:!0,passive:!1}),w.addEventListener("touchstart",R,{capture:!0,passive:!1}),w.addEventListener("click",I,{capture:!0,passive:!1}),w.addEventListener("keydown",B,{capture:!0,passive:!1}),h},C=function(){if(F.active)return w.removeEventListener("focusin",j,!0),w.removeEventListener("mousedown",R,!0),w.removeEventListener("touchstart",R,!0),w.removeEventListener("click",I,!0),w.removeEventListener("keydown",B,!0),h},A="undefined"!=typeof window&&"MutationObserver"in window?new MutationObserver((function(e){e.some((function(e){return Array.from(e.removedNodes).some((function(e){return e===F.mostRecentlyFocusedNode}))}))&&P(T())})):void 0,S=function(){A&&(A.disconnect(),F.active&&!F.paused&&F.containers.map((function(e){A.observe(e,{subtree:!0,childList:!0})})))};return(h={get active(){return F.active},get paused(){return F.paused},activate:function(e){if(F.active)return this;var t=O(e,"onActivate"),n=O(e,"onPostActivate"),o=O(e,"checkCanFocusTrap");o||D(),F.active=!0,F.paused=!1,F.nodeFocusedBeforeActivation=w.activeElement,null==t||t();var r=function(){o&&D(),L(),S(),null==n||n()};return o?(o(F.containers.concat()).then(r,r),this):(r(),this)},deactivate:function(e){if(!F.active)return this;var t=i({onDeactivate:N.onDeactivate,onPostDeactivate:N.onPostDeactivate,checkCanReturnFocus:N.checkCanReturnFocus},e);clearTimeout(F.delayInitialFocusTimer),F.delayInitialFocusTimer=void 0,C(),F.active=!1,F.paused=!1,S(),s(g,h);var n=O(t,"onDeactivate"),o=O(t,"onPostDeactivate"),r=O(t,"checkCanReturnFocus"),a=O(t,"returnFocus","returnFocusOnDeactivate");null==n||n();var u=function(){b((function(){a&&P(G(F.nodeFocusedBeforeActivation)),null==o||o()}))};return a&&r?(r(G(F.nodeFocusedBeforeActivation)).then(u,u),this):(u(),this)},pause:function(e){if(F.paused||!F.active)return this;var t=O(e,"onPause"),n=O(e,"onPostPause");return F.paused=!0,null==t||t(),C(),S(),null==n||n(),this},unpause:function(e){if(!F.paused||!F.active)return this;var t=O(e,"onUnpause"),n=O(e,"onPostUnpause");return F.paused=!1,null==t||t(),D(),L(),S(),null==n||n(),this},updateContainerElements:function(e){var t=[].concat(e).filter(Boolean);return F.containers=t.map((function(e){return"string"==typeof e?w.querySelector(e):e})),F.active&&D(),S(),this}}).updateContainerElements(a),h};export{h as createFocusTrap};
//# sourceMappingURL=focus-trap.esm.min.js.map
/*!
* focus-trap 6.7.1
* focus-trap 7.5.2
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE

@@ -13,39 +13,23 @@ */

var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) {
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
}
keys.push.apply(keys, symbols);
enumerableOnly && (symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {

@@ -61,60 +45,71 @@ Object.defineProperty(obj, key, {

}
return obj;
}
function _toPrimitive(input, hint) {
if (typeof input !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (typeof res !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, "string");
return typeof key === "symbol" ? key : String(key);
}
var activeFocusTraps = function () {
var trapQueue = [];
return {
activateTrap: function activateTrap(trap) {
if (trapQueue.length > 0) {
var activeTrap = trapQueue[trapQueue.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
var activeFocusTraps = {
activateTrap: function activateTrap(trapStack, trap) {
if (trapStack.length > 0) {
var activeTrap = trapStack[trapStack.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex === -1) {
trapQueue.push(trap);
} else {
// move this existing trap to the front of the queue
trapQueue.splice(trapIndex, 1);
trapQueue.push(trap);
}
},
deactivateTrap: function deactivateTrap(trap) {
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex !== -1) {
trapQueue.splice(trapIndex, 1);
}
if (trapQueue.length > 0) {
trapQueue[trapQueue.length - 1].unpause();
}
}
};
}();
var trapIndex = trapStack.indexOf(trap);
if (trapIndex === -1) {
trapStack.push(trap);
} else {
// move this existing trap to the front of the queue
trapStack.splice(trapIndex, 1);
trapStack.push(trap);
}
},
deactivateTrap: function deactivateTrap(trapStack, trap) {
var trapIndex = trapStack.indexOf(trap);
if (trapIndex !== -1) {
trapStack.splice(trapIndex, 1);
}
if (trapStack.length > 0) {
trapStack[trapStack.length - 1].unpause();
}
}
};
var isSelectableInput = function isSelectableInput(node) {
return node.tagName && node.tagName.toLowerCase() === 'input' && typeof node.select === 'function';
};
var isEscapeEvent = function isEscapeEvent(e) {
return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
return (e === null || e === void 0 ? void 0 : e.key) === 'Escape' || (e === null || e === void 0 ? void 0 : e.key) === 'Esc' || (e === null || e === void 0 ? void 0 : e.keyCode) === 27;
};
var isTabEvent = function isTabEvent(e) {
return e.key === 'Tab' || e.keyCode === 9;
return (e === null || e === void 0 ? void 0 : e.key) === 'Tab' || (e === null || e === void 0 ? void 0 : e.keyCode) === 9;
};
// checks for TAB by default
var isKeyForward = function isKeyForward(e) {
return isTabEvent(e) && !e.shiftKey;
};
// checks for SHIFT+TAB by default
var isKeyBackward = function isKeyBackward(e) {
return isTabEvent(e) && e.shiftKey;
};
var delay = function delay(fn) {
return setTimeout(fn, 0);
}; // Array.find/findIndex() are not supported on IE; this replicates enough
};
// Array.find/findIndex() are not supported on IE; this replicates enough
// of Array.findIndex() for our needs
var findIndex = function findIndex(arr, fn) {

@@ -130,4 +125,6 @@ var idx = -1;

});
return idx;
};
/**

@@ -140,4 +137,2 @@ * Get an option's value when it could be a plain value, or a handler that provides

*/
var valueOrHandler = function valueOrHandler(value) {

@@ -147,6 +142,4 @@ for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

}
return typeof value === 'function' ? value.apply(void 0, params) : value;
};
var getActualTarget = function getActualTarget(event) {

@@ -163,16 +156,22 @@ // NOTE: If the trap is _inside_ a shadow DOM, event.target will always be the

// NOTE: this must be _outside_ `createFocusTrap()` to make sure all traps in this
// current instance use the same stack if `userOptions.trapStack` isn't specified
var internalTrapStack = [];
var createFocusTrap = function createFocusTrap(elements, userOptions) {
// SSR: a live trap shouldn't be created in this type of environment so this
// should be safe code to execute if the `document` option isn't specified
var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document;
var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || internalTrapStack;
var config = _objectSpread2({
returnFocusOnDeactivate: true,
escapeDeactivates: true,
delayInitialFocus: true
delayInitialFocus: true,
isKeyForward: isKeyForward,
isKeyBackward: isKeyBackward
}, userOptions);
var state = {
// containers given to createFocusTrap()
// @type {Array<HTMLElement>}
containers: [],
// list of objects identifying the first and last tabbable nodes in all containers/groups in
// the trap
// list of objects identifying tabbable nodes in `containers` in the trap
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap

@@ -182,3 +181,20 @@ // is active, but the trap should never get to a state where there isn't at least one group

// result in an error being thrown)
// @type {Array<{ container: HTMLElement, firstTabbableNode: HTMLElement|null, lastTabbableNode: HTMLElement|null }>}
// @type {Array<{
// container: HTMLElement,
// tabbableNodes: Array<HTMLElement>, // empty if none
// focusableNodes: Array<HTMLElement>, // empty if none
// posTabIndexesFound: boolean,
// firstTabbableNode: HTMLElement|undefined,
// lastTabbableNode: HTMLElement|undefined,
// firstDomTabbableNode: HTMLElement|undefined,
// lastDomTabbableNode: HTMLElement|undefined,
// nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
// }>}
containerGroups: [],
// same order/length as `containers` list
// references to objects in `containerGroups`, but only those that actually have
// tabbable nodes in them
// NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
// the same length
tabbableGroups: [],

@@ -191,6 +207,16 @@ nodeFocusedBeforeActivation: null,

// has been delayed during activation
delayInitialFocusTimer: undefined
delayInitialFocusTimer: undefined,
// the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any
recentNavEvent: undefined
};
var trap; // eslint-disable-line prefer-const -- some private functions reference it, and its methods reference private functions, so we must declare here and define later
/**
* Gets a configuration option value.
* @param {Object|undefined} configOverrideOptions If true, and option is defined in this set,
* value will be taken from this object. Otherwise, value will be taken from base configuration.
* @param {string} optionName Name of the option whose value is sought.
* @param {string|undefined} [configOptionName] Name of option to use __instead of__ `optionName`
* IIF `configOverrideOptions` is not defined. Otherwise, `optionName` is used.
*/
var getOption = function getOption(configOverrideOptions, optionName, configOptionName) {

@@ -200,7 +226,30 @@ return configOverrideOptions && configOverrideOptions[optionName] !== undefined ? configOverrideOptions[optionName] : config[configOptionName || optionName];

var containersContain = function containersContain(element) {
return !!(element && state.containers.some(function (container) {
return container.contains(element);
}));
/**
* Finds the index of the container that contains the element.
* @param {HTMLElement} element
* @param {Event} [event] If available, and `element` isn't directly found in any container,
* the event's composed path is used to see if includes any known trap containers in the
* case where the element is inside a Shadow DOM.
* @returns {number} Index of the container in either `state.containers` or
* `state.containerGroups` (the order/length of these lists are the same); -1
* if the element isn't found.
*/
var findContainerIndex = function findContainerIndex(element, event) {
var composedPath = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === 'function' ? event.composedPath() : undefined;
// NOTE: search `containerGroups` because it's possible a group contains no tabbable
// nodes, but still contains focusable nodes (e.g. if they all have `tabindex=-1`)
// and we still need to find the element in there
return state.containerGroups.findIndex(function (_ref) {
var container = _ref.container,
tabbableNodes = _ref.tabbableNodes;
return container.contains(element) || ( // fall back to explicit tabbable search which will take into consideration any
// web components if the `tabbableOptions.getShadowRoot` option was used for
// the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
// look inside web components even if open)
composedPath === null || composedPath === void 0 ? void 0 : composedPath.includes(container)) || tabbableNodes.find(function (node) {
return node === element;
});
});
};
/**

@@ -219,7 +268,4 @@ * Gets the node for the given option, which is expected to be an option that

*/
var getNodeForOption = function getNodeForOption(optionName) {
var optionValue = config[optionName];
if (typeof optionValue === 'function') {

@@ -229,5 +275,7 @@ for (var _len2 = arguments.length, params = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {

}
optionValue = optionValue.apply(void 0, params);
}
if (optionValue === true) {
optionValue = undefined; // use default value
}

@@ -237,8 +285,7 @@ if (!optionValue) {

return optionValue;
} // else, empty string (invalid), null (invalid), 0 (invalid)
}
// else, empty string (invalid), null (invalid), 0 (invalid)
throw new Error("`".concat(optionName, "` was specified but was not a node, or did not return a node"));
}
var node = optionValue; // could be HTMLElement, SVGElement, or non-empty string at this point

@@ -248,3 +295,2 @@

node = doc.querySelector(optionValue); // resolve to node, or null if fails
if (!node) {

@@ -254,50 +300,104 @@ throw new Error("`".concat(optionName, "` as selector refers to no known node"));

}
return node;
};
var getInitialFocusNode = function getInitialFocusNode() {
var node = getNodeForOption('initialFocus'); // false explicitly indicates we want no initialFocus at all
var node = getNodeForOption('initialFocus');
// false explicitly indicates we want no initialFocus at all
if (node === false) {
return false;
}
if (node === undefined) {
// option not specified: use fallback options
if (containersContain(doc.activeElement)) {
if (node === undefined || !tabbable.isFocusable(node, config.tabbableOptions)) {
// option not specified nor focusable: use fallback options
if (findContainerIndex(doc.activeElement) >= 0) {
node = doc.activeElement;
} else {
var firstTabbableGroup = state.tabbableGroups[0];
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode; // NOTE: `fallbackFocus` option function cannot return `false` (not supported)
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
// NOTE: `fallbackFocus` option function cannot return `false` (not supported)
node = firstTabbableNode || getNodeForOption('fallbackFocus');
}
}
if (!node) {
throw new Error('Your focus-trap needs to have at least one focusable element');
}
return node;
};
var updateTabbableNodes = function updateTabbableNodes() {
state.tabbableGroups = state.containers.map(function (container) {
var tabbableNodes = tabbable.tabbable(container);
state.containerGroups = state.containers.map(function (container) {
var tabbableNodes = tabbable.tabbable(container, config.tabbableOptions);
if (tabbableNodes.length > 0) {
return {
container: container,
firstTabbableNode: tabbableNodes[0],
lastTabbableNode: tabbableNodes[tabbableNodes.length - 1]
};
}
// NOTE: if we have tabbable nodes, we must have focusable nodes; focusable nodes
// are a superset of tabbable nodes since nodes with negative `tabindex` attributes
// are focusable but not tabbable
var focusableNodes = tabbable.focusable(container, config.tabbableOptions);
var firstTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[0] : undefined;
var lastTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : undefined;
var firstDomTabbableNode = focusableNodes.find(function (node) {
return tabbable.isTabbable(node);
});
var lastDomTabbableNode = focusableNodes.slice().reverse().find(function (node) {
return tabbable.isTabbable(node);
});
var posTabIndexesFound = !!tabbableNodes.find(function (node) {
return tabbable.getTabIndex(node) > 0;
});
return {
container: container,
tabbableNodes: tabbableNodes,
focusableNodes: focusableNodes,
/** True if at least one node with positive `tabindex` was found in this container. */
posTabIndexesFound: posTabIndexesFound,
/** First tabbable node in container, __tabindex__ order; `undefined` if none. */
firstTabbableNode: firstTabbableNode,
/** Last tabbable node in container, __tabindex__ order; `undefined` if none. */
lastTabbableNode: lastTabbableNode,
// NOTE: DOM order is NOT NECESSARILY "document position" order, but figuring that out
// would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
// because that API doesn't work with Shadow DOM as well as it should (@see
// https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,
// to address an edge case related to positive tabindex support, this seems like a much easier,
// "close enough most of the time" alternative for positive tabindexes which should generally
// be avoided anyway...
/** First tabbable node in container, __DOM__ order; `undefined` if none. */
firstDomTabbableNode: firstDomTabbableNode,
/** Last tabbable node in container, __DOM__ order; `undefined` if none. */
lastDomTabbableNode: lastDomTabbableNode,
/**
* Finds the __tabbable__ node that follows the given node in the specified direction,
* in this container, if any.
* @param {HTMLElement} node
* @param {boolean} [forward] True if going in forward tab order; false if going
* in reverse.
* @returns {HTMLElement|undefined} The next tabbable node, if any.
*/
nextTabbableNode: function nextTabbableNode(node) {
var forward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var nodeIdx = tabbableNodes.indexOf(node);
if (nodeIdx < 0) {
// either not tabbable nor focusable, or was focused but not tabbable (negative tabindex):
// since `node` should at least have been focusable, we assume that's the case and mimic
// what browsers do, which is set focus to the next node in __document position order__,
// regardless of positive tabindexes, if any -- and for reasons explained in the NOTE
// above related to `firstDomTabbable` and `lastDomTabbable` properties, we fall back to
// basic DOM order
if (forward) {
return focusableNodes.slice(focusableNodes.indexOf(node) + 1).find(function (el) {
return tabbable.isTabbable(el);
});
}
return focusableNodes.slice(0, focusableNodes.indexOf(node)).reverse().find(function (el) {
return tabbable.isTabbable(el);
});
}
return tabbableNodes[nodeIdx + (forward ? 1 : -1)];
}
};
});
state.tabbableGroups = state.containerGroups.filter(function (group) {
return group.tabbableNodes.length > 0;
});
return undefined;
}).filter(function (group) {
return !!group;
}); // remove groups with no tabbable nodes
// throw if no groups have tabbable nodes and we don't have a fallback focus node either
if (state.tabbableGroups.length <= 0 && !getNodeForOption('fallbackFocus') // returning false not supported for this option

@@ -307,4 +407,16 @@ ) {

}
// NOTE: Positive tabindexes are only properly supported in single-container traps because
// doing it across multiple containers where tabindexes could be all over the place
// would require Tabbable to support multiple containers, would require additional
// specialized Shadow DOM support, and would require Tabbable's multi-container support
// to look at those containers in document position order rather than user-provided
// order (as they are treated in Focus-trap, for legacy reasons). See discussion on
// https://github.com/focus-trap/focus-trap/issues/375 for more details.
if (state.containerGroups.find(function (g) {
return g.posTabIndexesFound;
}) && state.containerGroups.length > 1) {
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.");
}
};
var tryFocus = function tryFocus(node) {

@@ -314,7 +426,5 @@ if (node === false) {

}
if (node === doc.activeElement) {
return;
}
if (!node || !node.focus) {

@@ -324,8 +434,7 @@ tryFocus(getInitialFocusNode());

}
node.focus({
preventScroll: !!config.preventScroll
});
// NOTE: focus() API does not trigger focusIn event so set MRU node manually
state.mostRecentlyFocusedNode = node;
if (isSelectableInput(node)) {

@@ -335,87 +444,36 @@ node.select();

};
var getReturnFocusNode = function getReturnFocusNode(previousActiveElement) {
var node = getNodeForOption('setReturnFocus', previousActiveElement);
return node ? node : node === false ? false : previousActiveElement;
}; // This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
};
var checkPointerDown = function checkPointerDown(e) {
var target = getActualTarget(e);
if (containersContain(target)) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// if, on deactivation, we should return focus to the node originally-focused
// when the trap was activated (or the configured `setReturnFocus` node),
// then assume it's also OK to return focus to the outside node that was
// just clicked, causing deactivation, as long as that node is focusable;
// if it isn't focusable, then return focus to the original node focused
// on activation (or the configured `setReturnFocus` node)
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked, whether it's focusable or not; by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node)
returnFocus: config.returnFocusOnDeactivate && !tabbable.isFocusable(target)
});
return;
} // This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
} // otherwise, prevent the click
e.preventDefault();
}; // In case focus escapes the trap for some strange reason, pull it back in.
var checkFocusIn = function checkFocusIn(e) {
var target = getActualTarget(e);
var targetContained = containersContain(target); // In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
e.stopImmediatePropagation();
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}; // Hijack Tab events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
var checkTab = function checkTab(e) {
var target = getActualTarget(e);
/**
* Finds the next node (in either direction) where focus should move according to a
* keyboard focus-in event.
* @param {Object} params
* @param {Node} [params.target] Known target __from which__ to navigate, if any.
* @param {KeyboardEvent|FocusEvent} [params.event] Event to use if `target` isn't known (event
* will be used to determine the `target`). Ignored if `target` is specified.
* @param {boolean} [params.isBackward] True if focus should move backward.
* @returns {Node|undefined} The next node, or `undefined` if a next node couldn't be
* determined given the current state of the trap.
*/
var findNextNavNode = function findNextNavNode(_ref2) {
var target = _ref2.target,
event = _ref2.event,
_ref2$isBackward = _ref2.isBackward,
isBackward = _ref2$isBackward === void 0 ? false : _ref2$isBackward;
target = target || getActualTarget(event);
updateTabbableNodes();
var destinationNode = null;
if (state.tabbableGroups.length > 0) {
// make sure the target is actually contained in a group
// NOTE: the target may also be the container itself if it's tabbable
// NOTE: the target may also be the container itself if it's focusable
// with tabIndex='-1' and was given initial focus
var containerIndex = findIndex(state.tabbableGroups, function (_ref) {
var container = _ref.container;
return container.contains(target);
});
var containerIndex = findContainerIndex(target, event);
var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : undefined;
if (containerIndex < 0) {
// target not found in any group: quite possible focus has escaped the trap,
// so bring it back in to...
if (e.shiftKey) {
// so bring it back into...
if (isBackward) {
// ...the last node in the last group

@@ -427,12 +485,15 @@ destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;

}
} else if (e.shiftKey) {
} else if (isBackward) {
// REVERSE
// is the target the first tabbable node in a group?
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref2) {
var firstTabbableNode = _ref2.firstTabbableNode;
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
var firstTabbableNode = _ref3.firstTabbableNode;
return target === firstTabbableNode;
});
if (startOfGroupIndex < 0 && state.tabbableGroups[containerIndex].container === target) {
// an exception case where the target is the container itself, in which
if (startOfGroupIndex < 0 && (containerGroup.container === target || tabbable.isFocusable(target, config.tabbableOptions) && !tabbable.isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {
// an exception case where the target is either the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle shift+tab as if focus were on the container's

@@ -442,3 +503,2 @@ // first tabbable node, and go to the last tabbable node of the LAST group

}
if (startOfGroupIndex >= 0) {

@@ -450,14 +510,21 @@ // YES: then shift+tab should go to the last tabbable node in the

var destinationGroup = state.tabbableGroups[destinationGroupIndex];
destinationNode = destinationGroup.lastTabbableNode;
destinationNode = tabbable.getTabIndex(target) >= 0 ? destinationGroup.lastTabbableNode : destinationGroup.lastDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target, false);
}
} else {
// FORWARD
// is the target the last tabbable node in a group?
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
var lastTabbableNode = _ref3.lastTabbableNode;
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref4) {
var lastTabbableNode = _ref4.lastTabbableNode;
return target === lastTabbableNode;
});
if (lastOfGroupIndex < 0 && state.tabbableGroups[containerIndex].container === target) {
// an exception case where the target is the container itself, in which
if (lastOfGroupIndex < 0 && (containerGroup.container === target || tabbable.isFocusable(target, config.tabbableOptions) && !tabbable.isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {
// an exception case where the target is the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle tab as if focus were on the container's

@@ -467,3 +534,2 @@ // last tabbable node, and go to the first tabbable node of the FIRST group

}
if (lastOfGroupIndex >= 0) {

@@ -474,63 +540,214 @@ // YES: then tab should go to the first tabbable node in the next

var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
destinationNode = _destinationGroup.firstTabbableNode;
destinationNode = tabbable.getTabIndex(target) >= 0 ? _destinationGroup.firstTabbableNode : _destinationGroup.firstDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target);
}
}
} else {
// no groups available
// NOTE: the fallbackFocus option does not support returning false to opt-out
destinationNode = getNodeForOption('fallbackFocus');
}
return destinationNode;
};
// This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
var checkPointerDown = function checkPointerDown(e) {
var target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked (and if not focusable, to "nothing"); by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node), whether the
// outside click was on a focusable node or not
returnFocus: config.returnFocusOnDeactivate
});
return;
}
// This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
}
// otherwise, prevent the click
e.preventDefault();
};
// In case focus escapes the trap for some strange reason, pull it back in.
// NOTE: the focusIn event is NOT cancelable, so if focus escapes, it may cause unexpected
// scrolling if the node that got focused was out of view; there's nothing we can do to
// prevent that from happening by the time we discover that focus escaped
var checkFocusIn = function checkFocusIn(event) {
var target = getActualTarget(event);
var targetContained = findContainerIndex(target, event) >= 0;
// In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
event.stopImmediatePropagation();
// focus will escape if the MRU node had a positive tab index and user tried to nav forward;
// it will also escape if the MRU node had a 0 tab index and user tried to nav backward
// toward a node with a positive tab index
var nextNode; // next node to focus, if we find one
var navAcrossContainers = true;
if (state.mostRecentlyFocusedNode) {
if (tabbable.getTabIndex(state.mostRecentlyFocusedNode) > 0) {
// MRU container index must be >=0 otherwise we wouldn't have it as an MRU node...
var mruContainerIdx = findContainerIndex(state.mostRecentlyFocusedNode);
// there MAY not be any tabbable nodes in the container if there are at least 2 containers
// and the MRU node is focusable but not tabbable (focus-trap requires at least 1 container
// with at least one tabbable node in order to function, so this could be the other container
// with nothing tabbable in it)
var tabbableNodes = state.containerGroups[mruContainerIdx].tabbableNodes;
if (tabbableNodes.length > 0) {
// MRU tab index MAY not be found if the MRU node is focusable but not tabbable
var mruTabIdx = tabbableNodes.findIndex(function (node) {
return node === state.mostRecentlyFocusedNode;
});
if (mruTabIdx >= 0) {
if (config.isKeyForward(state.recentNavEvent)) {
if (mruTabIdx + 1 < tabbableNodes.length) {
nextNode = tabbableNodes[mruTabIdx + 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
} else {
if (mruTabIdx - 1 >= 0) {
nextNode = tabbableNodes[mruTabIdx - 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
}
// else, don't find in container order without considering direction too
}
}
// else, no tabbable nodes in that container (which means we must have at least one other
// container with at least one tabbable node in it, otherwise focus-trap would've thrown
// an error the last time updateTabbableNodes() was run): find next node among all known
// containers
} else {
// check to see if there's at least one tabbable node with a positive tab index inside
// the trap because focus seems to escape when navigating backward from a tabbable node
// with tabindex=0 when this is the case (instead of wrapping to the tabbable node with
// the greatest positive tab index like it should)
if (!state.containerGroups.some(function (g) {
return g.tabbableNodes.some(function (n) {
return tabbable.getTabIndex(n) > 0;
});
})) {
// no containers with tabbable nodes with positive tab indexes which means the focus
// escaped for some other reason and we should just execute the fallback to the
// MRU node or initial focus node, if any
navAcrossContainers = false;
}
}
} else {
// no MRU node means we're likely in some initial condition when the trap has just
// been activated and initial focus hasn't been given yet, in which case we should
// fall through to trying to focus the initial focus node, which is what should
// happen below at this point in the logic
navAcrossContainers = false;
}
if (navAcrossContainers) {
nextNode = findNextNavNode({
// move FROM the MRU node, not event-related node (which will be the node that is
// outside the trap causing the focus escape we're trying to fix)
target: state.mostRecentlyFocusedNode,
isBackward: config.isKeyBackward(state.recentNavEvent)
});
}
if (nextNode) {
tryFocus(nextNode);
} else {
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}
state.recentNavEvent = undefined; // clear
};
// Hijack key nav events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
var checkKeyNav = function checkKeyNav(event) {
var isBackward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
state.recentNavEvent = event;
var destinationNode = findNextNavNode({
event: event,
isBackward: isBackward
});
if (destinationNode) {
e.preventDefault();
if (isTabEvent(event)) {
// since tab natively moves focus, we wouldn't have a destination node unless we
// were on the edge of a container and had to move to the next/previous edge, in
// which case we want to prevent default to keep the browser from moving focus
// to where it normally would
event.preventDefault();
}
tryFocus(destinationNode);
} // else, let the browser take care of [shift+]tab and move the focus
}
// else, let the browser take care of [shift+]tab and move the focus
};
var checkKey = function checkKey(e) {
if (isEscapeEvent(e) && valueOrHandler(config.escapeDeactivates, e) !== false) {
e.preventDefault();
var checkKey = function checkKey(event) {
if (isEscapeEvent(event) && valueOrHandler(config.escapeDeactivates, event) !== false) {
event.preventDefault();
trap.deactivate();
return;
}
if (isTabEvent(e)) {
checkTab(e);
return;
if (config.isKeyForward(event) || config.isKeyBackward(event)) {
checkKeyNav(event, config.isKeyBackward(event));
}
};
var checkClick = function checkClick(e) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
var target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
return;
}
var target = getActualTarget(e);
if (containersContain(target)) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
return;
}
if (valueOrHandler(config.allowOutsideClick, e)) {
return;
}
e.preventDefault();
e.stopImmediatePropagation();
}; //
};
//
// EVENT LISTENERS
//
var addListeners = function addListeners() {
if (!state.active) {
return;
} // There can be only one listening focus trap at a time
}
// There can be only one listening focus trap at a time
activeFocusTraps.activateTrap(trapStack, trap);
activeFocusTraps.activateTrap(trap); // Delay ensures that the focused element doesn't capture the event
// Delay ensures that the focused element doesn't capture the event
// that caused the focus trap activation.
state.delayInitialFocusTimer = config.delayInitialFocus ? delay(function () {

@@ -558,3 +775,2 @@ tryFocus(getInitialFocusNode());

};
var removeListeners = function removeListeners() {

@@ -564,3 +780,2 @@ if (!state.active) {

}
doc.removeEventListener('focusin', checkFocusIn, true);

@@ -572,8 +787,52 @@ doc.removeEventListener('mousedown', checkPointerDown, true);

return trap;
}; //
};
//
// MUTATION OBSERVER
//
var checkDomRemoval = function checkDomRemoval(mutations) {
var isFocusedNodeRemoved = mutations.some(function (mutation) {
var removedNodes = Array.from(mutation.removedNodes);
return removedNodes.some(function (node) {
return node === state.mostRecentlyFocusedNode;
});
});
// If the currently focused is removed then browsers will move focus to the
// <body> element. If this happens, try to move focus back into the trap.
if (isFocusedNodeRemoved) {
tryFocus(getInitialFocusNode());
}
};
// Use MutationObserver - if supported - to detect if focused node is removed
// from the DOM.
var mutationObserver = typeof window !== 'undefined' && 'MutationObserver' in window ? new MutationObserver(checkDomRemoval) : undefined;
var updateObservedNodes = function updateObservedNodes() {
if (!mutationObserver) {
return;
}
mutationObserver.disconnect();
if (state.active && !state.paused) {
state.containers.map(function (container) {
mutationObserver.observe(container, {
subtree: true,
childList: true
});
});
}
};
//
// TRAP DEFINITION
//
trap = {
get active() {
return state.active;
},
get paused() {
return state.paused;
},
activate: function activate(activateOptions) {

@@ -583,19 +842,12 @@ if (state.active) {

}
var onActivate = getOption(activateOptions, 'onActivate');
var onPostActivate = getOption(activateOptions, 'onPostActivate');
var checkCanFocusTrap = getOption(activateOptions, 'checkCanFocusTrap');
if (!checkCanFocusTrap) {
updateTabbableNodes();
}
state.active = true;
state.paused = false;
state.nodeFocusedBeforeActivation = doc.activeElement;
if (onActivate) {
onActivate();
}
onActivate === null || onActivate === void 0 ? void 0 : onActivate();
var finishActivation = function finishActivation() {

@@ -605,10 +857,6 @@ if (checkCanFocusTrap) {

}
addListeners();
if (onPostActivate) {
onPostActivate();
}
updateObservedNodes();
onPostActivate === null || onPostActivate === void 0 ? void 0 : onPostActivate();
};
if (checkCanFocusTrap) {

@@ -618,3 +866,2 @@ checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);

}
finishActivation();

@@ -627,5 +874,8 @@ return this;

}
var options = _objectSpread2({
onDeactivate: config.onDeactivate,
onPostDeactivate: config.onPostDeactivate,
checkCanReturnFocus: config.checkCanReturnFocus
}, deactivateOptions);
clearTimeout(state.delayInitialFocusTimer); // noop if undefined
state.delayInitialFocusTimer = undefined;

@@ -635,13 +885,9 @@ removeListeners();

state.paused = false;
activeFocusTraps.deactivateTrap(trap);
var onDeactivate = getOption(deactivateOptions, 'onDeactivate');
var onPostDeactivate = getOption(deactivateOptions, 'onPostDeactivate');
var checkCanReturnFocus = getOption(deactivateOptions, 'checkCanReturnFocus');
if (onDeactivate) {
onDeactivate();
}
var returnFocus = getOption(deactivateOptions, 'returnFocus', 'returnFocusOnDeactivate');
updateObservedNodes();
activeFocusTraps.deactivateTrap(trapStack, trap);
var onDeactivate = getOption(options, 'onDeactivate');
var onPostDeactivate = getOption(options, 'onPostDeactivate');
var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
onDeactivate === null || onDeactivate === void 0 ? void 0 : onDeactivate();
var finishDeactivation = function finishDeactivation() {

@@ -652,9 +898,5 @@ delay(function () {

}
if (onPostDeactivate) {
onPostDeactivate();
}
onPostDeactivate === null || onPostDeactivate === void 0 ? void 0 : onPostDeactivate();
});
};
if (returnFocus && checkCanReturnFocus) {

@@ -664,23 +906,30 @@ checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);

}
finishDeactivation();
return this;
},
pause: function pause() {
pause: function pause(pauseOptions) {
if (state.paused || !state.active) {
return this;
}
var onPause = getOption(pauseOptions, 'onPause');
var onPostPause = getOption(pauseOptions, 'onPostPause');
state.paused = true;
onPause === null || onPause === void 0 ? void 0 : onPause();
removeListeners();
updateObservedNodes();
onPostPause === null || onPostPause === void 0 ? void 0 : onPostPause();
return this;
},
unpause: function unpause() {
unpause: function unpause(unpauseOptions) {
if (!state.paused || !state.active) {
return this;
}
var onUnpause = getOption(unpauseOptions, 'onUnpause');
var onPostUnpause = getOption(unpauseOptions, 'onPostUnpause');
state.paused = false;
onUnpause === null || onUnpause === void 0 ? void 0 : onUnpause();
updateTabbableNodes();
addListeners();
updateObservedNodes();
onPostUnpause === null || onPostUnpause === void 0 ? void 0 : onPostUnpause();
return this;

@@ -693,11 +942,11 @@ },

});
if (state.active) {
updateTabbableNodes();
}
updateObservedNodes();
return this;
}
}; // initialize container elements
};
// initialize container elements
trap.updateContainerElements(elements);

@@ -704,0 +953,0 @@ return trap;

/*!
* focus-trap 6.7.1
* focus-trap 7.5.2
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
*/
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("tabbable");function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var a,r=(a=[],{activateTrap:function(e){if(a.length>0){var t=a[a.length-1];t!==e&&t.pause()}var n=a.indexOf(e);-1===n||a.splice(n,1),a.push(e)},deactivateTrap:function(e){var t=a.indexOf(e);-1!==t&&a.splice(t,1),a.length>0&&a[a.length-1].unpause()}}),o=function(e){return setTimeout(e,0)},i=function(e,t){var n=-1;return e.every((function(e,a){return!t(e)||(n=a,!1)})),n},c=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),a=1;a<t;a++)n[a-1]=arguments[a];return"function"==typeof e?e.apply(void 0,n):e},u=function(e){return e.target.shadowRoot&&"function"==typeof e.composedPath?e.composedPath()[0]:e.target};exports.createFocusTrap=function(a,s){var l,f=(null==s?void 0:s.document)||document,b=function(e){for(var a=1;a<arguments.length;a++){var r=null!=arguments[a]?arguments[a]:{};a%2?t(Object(r),!0).forEach((function(t){n(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):t(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}({returnFocusOnDeactivate:!0,escapeDeactivates:!0,delayInitialFocus:!0},s),v={containers:[],tabbableGroups:[],nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1,delayInitialFocusTimer:void 0},p=function(e,t,n){return e&&void 0!==e[t]?e[t]:b[n||t]},d=function(e){return!(!e||!v.containers.some((function(t){return t.contains(e)})))},h=function(e){var t=b[e];if("function"==typeof t){for(var n=arguments.length,a=new Array(n>1?n-1:0),r=1;r<n;r++)a[r-1]=arguments[r];t=t.apply(void 0,a)}if(!t){if(void 0===t||!1===t)return t;throw new Error("`".concat(e,"` was specified but was not a node, or did not return a node"))}var o=t;if("string"==typeof t&&!(o=f.querySelector(t)))throw new Error("`".concat(e,"` as selector refers to no known node"));return o},y=function(){var e=h("initialFocus");if(!1===e)return!1;if(void 0===e)if(d(f.activeElement))e=f.activeElement;else{var t=v.tabbableGroups[0];e=t&&t.firstTabbableNode||h("fallbackFocus")}if(!e)throw new Error("Your focus-trap needs to have at least one focusable element");return e},m=function(){if(v.tabbableGroups=v.containers.map((function(t){var n=e.tabbable(t);if(n.length>0)return{container:t,firstTabbableNode:n[0],lastTabbableNode:n[n.length-1]}})).filter((function(e){return!!e})),v.tabbableGroups.length<=0&&!h("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times")},g=function e(t){!1!==t&&t!==f.activeElement&&(t&&t.focus?(t.focus({preventScroll:!!b.preventScroll}),v.mostRecentlyFocusedNode=t,function(e){return e.tagName&&"input"===e.tagName.toLowerCase()&&"function"==typeof e.select}(t)&&t.select()):e(y()))},w=function(e){var t=h("setReturnFocus",e);return t||!1!==t&&e},F=function(t){var n=u(t);d(n)||(c(b.clickOutsideDeactivates,t)?l.deactivate({returnFocus:b.returnFocusOnDeactivate&&!e.isFocusable(n)}):c(b.allowOutsideClick,t)||t.preventDefault())},O=function(e){var t=u(e),n=d(t);n||t instanceof Document?n&&(v.mostRecentlyFocusedNode=t):(e.stopImmediatePropagation(),g(v.mostRecentlyFocusedNode||y()))},E=function(e){if(function(e){return"Escape"===e.key||"Esc"===e.key||27===e.keyCode}(e)&&!1!==c(b.escapeDeactivates,e))return e.preventDefault(),void l.deactivate();(function(e){return"Tab"===e.key||9===e.keyCode})(e)&&function(e){var t=u(e);m();var n=null;if(v.tabbableGroups.length>0){var a=i(v.tabbableGroups,(function(e){return e.container.contains(t)}));if(a<0)n=e.shiftKey?v.tabbableGroups[v.tabbableGroups.length-1].lastTabbableNode:v.tabbableGroups[0].firstTabbableNode;else if(e.shiftKey){var r=i(v.tabbableGroups,(function(e){var n=e.firstTabbableNode;return t===n}));if(r<0&&v.tabbableGroups[a].container===t&&(r=a),r>=0){var o=0===r?v.tabbableGroups.length-1:r-1;n=v.tabbableGroups[o].lastTabbableNode}}else{var c=i(v.tabbableGroups,(function(e){var n=e.lastTabbableNode;return t===n}));if(c<0&&v.tabbableGroups[a].container===t&&(c=a),c>=0){var s=c===v.tabbableGroups.length-1?0:c+1;n=v.tabbableGroups[s].firstTabbableNode}}}else n=h("fallbackFocus");n&&(e.preventDefault(),g(n))}(e)},T=function(e){if(!c(b.clickOutsideDeactivates,e)){var t=u(e);d(t)||c(b.allowOutsideClick,e)||(e.preventDefault(),e.stopImmediatePropagation())}},k=function(){if(v.active)return r.activateTrap(l),v.delayInitialFocusTimer=b.delayInitialFocus?o((function(){g(y())})):g(y()),f.addEventListener("focusin",O,!0),f.addEventListener("mousedown",F,{capture:!0,passive:!1}),f.addEventListener("touchstart",F,{capture:!0,passive:!1}),f.addEventListener("click",T,{capture:!0,passive:!1}),f.addEventListener("keydown",E,{capture:!0,passive:!1}),l},D=function(){if(v.active)return f.removeEventListener("focusin",O,!0),f.removeEventListener("mousedown",F,!0),f.removeEventListener("touchstart",F,!0),f.removeEventListener("click",T,!0),f.removeEventListener("keydown",E,!0),l};return(l={activate:function(e){if(v.active)return this;var t=p(e,"onActivate"),n=p(e,"onPostActivate"),a=p(e,"checkCanFocusTrap");a||m(),v.active=!0,v.paused=!1,v.nodeFocusedBeforeActivation=f.activeElement,t&&t();var r=function(){a&&m(),k(),n&&n()};return a?(a(v.containers.concat()).then(r,r),this):(r(),this)},deactivate:function(e){if(!v.active)return this;clearTimeout(v.delayInitialFocusTimer),v.delayInitialFocusTimer=void 0,D(),v.active=!1,v.paused=!1,r.deactivateTrap(l);var t=p(e,"onDeactivate"),n=p(e,"onPostDeactivate"),a=p(e,"checkCanReturnFocus");t&&t();var i=p(e,"returnFocus","returnFocusOnDeactivate"),c=function(){o((function(){i&&g(w(v.nodeFocusedBeforeActivation)),n&&n()}))};return i&&a?(a(w(v.nodeFocusedBeforeActivation)).then(c,c),this):(c(),this)},pause:function(){return v.paused||!v.active||(v.paused=!0,D()),this},unpause:function(){return v.paused&&v.active?(v.paused=!1,m(),k(),this):this},updateContainerElements:function(e){var t=[].concat(e).filter(Boolean);return v.containers=t.map((function(e){return"string"==typeof e?f.querySelector(e):e})),v.active&&m(),this}}).updateContainerElements(a),l};
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("tabbable");function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function n(e){for(var n=1;n<arguments.length;n++){var a=null!=arguments[n]?arguments[n]:{};n%2?t(Object(a),!0).forEach((function(t){o(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):t(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function o(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t||"default");if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var a=function(e,t){if(e.length>0){var n=e[e.length-1];n!==t&&n.pause()}var o=e.indexOf(t);-1===o||e.splice(o,1),e.push(t)},r=function(e,t){var n=e.indexOf(t);-1!==n&&e.splice(n,1),e.length>0&&e[e.length-1].unpause()},i=function(e){return"Tab"===(null==e?void 0:e.key)||9===(null==e?void 0:e.keyCode)},u=function(e){return i(e)&&!e.shiftKey},s=function(e){return i(e)&&e.shiftKey},c=function(e){return setTimeout(e,0)},l=function(e,t){var n=-1;return e.every((function(e,o){return!t(e)||(n=o,!1)})),n},b=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),o=1;o<t;o++)n[o-1]=arguments[o];return"function"==typeof e?e.apply(void 0,n):e},d=function(e){return e.target.shadowRoot&&"function"==typeof e.composedPath?e.composedPath()[0]:e.target},f=[];exports.createFocusTrap=function(t,o){var v,p=(null==o?void 0:o.document)||document,m=(null==o?void 0:o.trapStack)||f,y=n({returnFocusOnDeactivate:!0,escapeDeactivates:!0,delayInitialFocus:!0,isKeyForward:u,isKeyBackward:s},o),h={containers:[],containerGroups:[],tabbableGroups:[],nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1,delayInitialFocusTimer:void 0,recentNavEvent:void 0},g=function(e,t,n){return e&&void 0!==e[t]?e[t]:y[n||t]},w=function(e,t){var n="function"==typeof(null==t?void 0:t.composedPath)?t.composedPath():void 0;return h.containerGroups.findIndex((function(t){var o=t.container,a=t.tabbableNodes;return o.contains(e)||(null==n?void 0:n.includes(o))||a.find((function(t){return t===e}))}))},F=function(e){var t=y[e];if("function"==typeof t){for(var n=arguments.length,o=new Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];t=t.apply(void 0,o)}if(!0===t&&(t=void 0),!t){if(void 0===t||!1===t)return t;throw new Error("`".concat(e,"` was specified but was not a node, or did not return a node"))}var r=t;if("string"==typeof t&&!(r=p.querySelector(t)))throw new Error("`".concat(e,"` as selector refers to no known node"));return r},N=function(){var t=F("initialFocus");if(!1===t)return!1;if(void 0===t||!e.isFocusable(t,y.tabbableOptions))if(w(p.activeElement)>=0)t=p.activeElement;else{var n=h.tabbableGroups[0];t=n&&n.firstTabbableNode||F("fallbackFocus")}if(!t)throw new Error("Your focus-trap needs to have at least one focusable element");return t},T=function(){if(h.containerGroups=h.containers.map((function(t){var n=e.tabbable(t,y.tabbableOptions),o=e.focusable(t,y.tabbableOptions),a=n.length>0?n[0]:void 0,r=n.length>0?n[n.length-1]:void 0,i=o.find((function(t){return e.isTabbable(t)})),u=o.slice().reverse().find((function(t){return e.isTabbable(t)})),s=!!n.find((function(t){return e.getTabIndex(t)>0}));return{container:t,tabbableNodes:n,focusableNodes:o,posTabIndexesFound:s,firstTabbableNode:a,lastTabbableNode:r,firstDomTabbableNode:i,lastDomTabbableNode:u,nextTabbableNode:function(t){var a=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=n.indexOf(t);return r<0?a?o.slice(o.indexOf(t)+1).find((function(t){return e.isTabbable(t)})):o.slice(0,o.indexOf(t)).reverse().find((function(t){return e.isTabbable(t)})):n[r+(a?1:-1)]}}})),h.tabbableGroups=h.containerGroups.filter((function(e){return e.tabbableNodes.length>0})),h.tabbableGroups.length<=0&&!F("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");if(h.containerGroups.find((function(e){return e.posTabIndexesFound}))&&h.containerGroups.length>1)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.")},O=function e(t){!1!==t&&t!==p.activeElement&&(t&&t.focus?(t.focus({preventScroll:!!y.preventScroll}),h.mostRecentlyFocusedNode=t,function(e){return e.tagName&&"input"===e.tagName.toLowerCase()&&"function"==typeof e.select}(t)&&t.select()):e(N()))},E=function(e){var t=F("setReturnFocus",e);return t||!1!==t&&e},k=function(t){var n=t.target,o=t.event,a=t.isBackward,r=void 0!==a&&a;n=n||d(o),T();var u=null;if(h.tabbableGroups.length>0){var s=w(n,o),c=s>=0?h.containerGroups[s]:void 0;if(s<0)u=r?h.tabbableGroups[h.tabbableGroups.length-1].lastTabbableNode:h.tabbableGroups[0].firstTabbableNode;else if(r){var b=l(h.tabbableGroups,(function(e){var t=e.firstTabbableNode;return n===t}));if(b<0&&(c.container===n||e.isFocusable(n,y.tabbableOptions)&&!e.isTabbable(n,y.tabbableOptions)&&!c.nextTabbableNode(n,!1))&&(b=s),b>=0){var f=0===b?h.tabbableGroups.length-1:b-1,v=h.tabbableGroups[f];u=e.getTabIndex(n)>=0?v.lastTabbableNode:v.lastDomTabbableNode}else i(o)||(u=c.nextTabbableNode(n,!1))}else{var p=l(h.tabbableGroups,(function(e){var t=e.lastTabbableNode;return n===t}));if(p<0&&(c.container===n||e.isFocusable(n,y.tabbableOptions)&&!e.isTabbable(n,y.tabbableOptions)&&!c.nextTabbableNode(n))&&(p=s),p>=0){var m=p===h.tabbableGroups.length-1?0:p+1,g=h.tabbableGroups[m];u=e.getTabIndex(n)>=0?g.firstTabbableNode:g.firstDomTabbableNode}else i(o)||(u=c.nextTabbableNode(n))}}else u=F("fallbackFocus");return u},P=function(e){var t=d(e);w(t,e)>=0||(b(y.clickOutsideDeactivates,e)?v.deactivate({returnFocus:y.returnFocusOnDeactivate}):b(y.allowOutsideClick,e)||e.preventDefault())},D=function(t){var n=d(t),o=w(n,t)>=0;if(o||n instanceof Document)o&&(h.mostRecentlyFocusedNode=n);else{var a;t.stopImmediatePropagation();var r=!0;if(h.mostRecentlyFocusedNode)if(e.getTabIndex(h.mostRecentlyFocusedNode)>0){var i=w(h.mostRecentlyFocusedNode),u=h.containerGroups[i].tabbableNodes;if(u.length>0){var s=u.findIndex((function(e){return e===h.mostRecentlyFocusedNode}));s>=0&&(y.isKeyForward(h.recentNavEvent)?s+1<u.length&&(a=u[s+1],r=!1):s-1>=0&&(a=u[s-1],r=!1))}}else h.containerGroups.some((function(t){return t.tabbableNodes.some((function(t){return e.getTabIndex(t)>0}))}))||(r=!1);else r=!1;r&&(a=k({target:h.mostRecentlyFocusedNode,isBackward:y.isKeyBackward(h.recentNavEvent)})),O(a||(h.mostRecentlyFocusedNode||N()))}h.recentNavEvent=void 0},x=function(e){if(!(t=e,"Escape"!==(null==t?void 0:t.key)&&"Esc"!==(null==t?void 0:t.key)&&27!==(null==t?void 0:t.keyCode)||!1===b(y.escapeDeactivates,e)))return e.preventDefault(),void v.deactivate();var t;(y.isKeyForward(e)||y.isKeyBackward(e))&&function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];h.recentNavEvent=e;var n=k({event:e,isBackward:t});n&&(i(e)&&e.preventDefault(),O(n))}(e,y.isKeyBackward(e))},G=function(e){var t=d(e);w(t,e)>=0||b(y.clickOutsideDeactivates,e)||b(y.allowOutsideClick,e)||(e.preventDefault(),e.stopImmediatePropagation())},I=function(){if(h.active)return a(m,v),h.delayInitialFocusTimer=y.delayInitialFocus?c((function(){O(N())})):O(N()),p.addEventListener("focusin",D,!0),p.addEventListener("mousedown",P,{capture:!0,passive:!1}),p.addEventListener("touchstart",P,{capture:!0,passive:!1}),p.addEventListener("click",G,{capture:!0,passive:!1}),p.addEventListener("keydown",x,{capture:!0,passive:!1}),v},j=function(){if(h.active)return p.removeEventListener("focusin",D,!0),p.removeEventListener("mousedown",P,!0),p.removeEventListener("touchstart",P,!0),p.removeEventListener("click",G,!0),p.removeEventListener("keydown",x,!0),v},R="undefined"!=typeof window&&"MutationObserver"in window?new MutationObserver((function(e){e.some((function(e){return Array.from(e.removedNodes).some((function(e){return e===h.mostRecentlyFocusedNode}))}))&&O(N())})):void 0,B=function(){R&&(R.disconnect(),h.active&&!h.paused&&h.containers.map((function(e){R.observe(e,{subtree:!0,childList:!0})})))};return(v={get active(){return h.active},get paused(){return h.paused},activate:function(e){if(h.active)return this;var t=g(e,"onActivate"),n=g(e,"onPostActivate"),o=g(e,"checkCanFocusTrap");o||T(),h.active=!0,h.paused=!1,h.nodeFocusedBeforeActivation=p.activeElement,null==t||t();var a=function(){o&&T(),I(),B(),null==n||n()};return o?(o(h.containers.concat()).then(a,a),this):(a(),this)},deactivate:function(e){if(!h.active)return this;var t=n({onDeactivate:y.onDeactivate,onPostDeactivate:y.onPostDeactivate,checkCanReturnFocus:y.checkCanReturnFocus},e);clearTimeout(h.delayInitialFocusTimer),h.delayInitialFocusTimer=void 0,j(),h.active=!1,h.paused=!1,B(),r(m,v);var o=g(t,"onDeactivate"),a=g(t,"onPostDeactivate"),i=g(t,"checkCanReturnFocus"),u=g(t,"returnFocus","returnFocusOnDeactivate");null==o||o();var s=function(){c((function(){u&&O(E(h.nodeFocusedBeforeActivation)),null==a||a()}))};return u&&i?(i(E(h.nodeFocusedBeforeActivation)).then(s,s),this):(s(),this)},pause:function(e){if(h.paused||!h.active)return this;var t=g(e,"onPause"),n=g(e,"onPostPause");return h.paused=!0,null==t||t(),j(),B(),null==n||n(),this},unpause:function(e){if(!h.paused||!h.active)return this;var t=g(e,"onUnpause"),n=g(e,"onPostUnpause");return h.paused=!1,null==t||t(),T(),I(),B(),null==n||n(),this},updateContainerElements:function(e){var t=[].concat(e).filter(Boolean);return h.containers=t.map((function(e){return"string"==typeof e?p.querySelector(e):e})),h.active&&T(),B(),this}}).updateContainerElements(t),v};
//# sourceMappingURL=focus-trap.min.js.map
/*!
* focus-trap 6.7.1
* focus-trap 7.5.2
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE

@@ -18,39 +18,23 @@ */

var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) {
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
}
keys.push.apply(keys, symbols);
enumerableOnly && (symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {

@@ -66,60 +50,71 @@ Object.defineProperty(obj, key, {

}
return obj;
}
function _toPrimitive(input, hint) {
if (typeof input !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (typeof res !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, "string");
return typeof key === "symbol" ? key : String(key);
}
var activeFocusTraps = function () {
var trapQueue = [];
return {
activateTrap: function activateTrap(trap) {
if (trapQueue.length > 0) {
var activeTrap = trapQueue[trapQueue.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
var activeFocusTraps = {
activateTrap: function activateTrap(trapStack, trap) {
if (trapStack.length > 0) {
var activeTrap = trapStack[trapStack.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex === -1) {
trapQueue.push(trap);
} else {
// move this existing trap to the front of the queue
trapQueue.splice(trapIndex, 1);
trapQueue.push(trap);
}
},
deactivateTrap: function deactivateTrap(trap) {
var trapIndex = trapQueue.indexOf(trap);
if (trapIndex !== -1) {
trapQueue.splice(trapIndex, 1);
}
if (trapQueue.length > 0) {
trapQueue[trapQueue.length - 1].unpause();
}
}
};
}();
var trapIndex = trapStack.indexOf(trap);
if (trapIndex === -1) {
trapStack.push(trap);
} else {
// move this existing trap to the front of the queue
trapStack.splice(trapIndex, 1);
trapStack.push(trap);
}
},
deactivateTrap: function deactivateTrap(trapStack, trap) {
var trapIndex = trapStack.indexOf(trap);
if (trapIndex !== -1) {
trapStack.splice(trapIndex, 1);
}
if (trapStack.length > 0) {
trapStack[trapStack.length - 1].unpause();
}
}
};
var isSelectableInput = function isSelectableInput(node) {
return node.tagName && node.tagName.toLowerCase() === 'input' && typeof node.select === 'function';
};
var isEscapeEvent = function isEscapeEvent(e) {
return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
return (e === null || e === void 0 ? void 0 : e.key) === 'Escape' || (e === null || e === void 0 ? void 0 : e.key) === 'Esc' || (e === null || e === void 0 ? void 0 : e.keyCode) === 27;
};
var isTabEvent = function isTabEvent(e) {
return e.key === 'Tab' || e.keyCode === 9;
return (e === null || e === void 0 ? void 0 : e.key) === 'Tab' || (e === null || e === void 0 ? void 0 : e.keyCode) === 9;
};
// checks for TAB by default
var isKeyForward = function isKeyForward(e) {
return isTabEvent(e) && !e.shiftKey;
};
// checks for SHIFT+TAB by default
var isKeyBackward = function isKeyBackward(e) {
return isTabEvent(e) && e.shiftKey;
};
var delay = function delay(fn) {
return setTimeout(fn, 0);
}; // Array.find/findIndex() are not supported on IE; this replicates enough
};
// Array.find/findIndex() are not supported on IE; this replicates enough
// of Array.findIndex() for our needs
var findIndex = function findIndex(arr, fn) {

@@ -135,4 +130,6 @@ var idx = -1;

});
return idx;
};
/**

@@ -145,4 +142,2 @@ * Get an option's value when it could be a plain value, or a handler that provides

*/
var valueOrHandler = function valueOrHandler(value) {

@@ -152,6 +147,4 @@ for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

}
return typeof value === 'function' ? value.apply(void 0, params) : value;
};
var getActualTarget = function getActualTarget(event) {

@@ -168,16 +161,22 @@ // NOTE: If the trap is _inside_ a shadow DOM, event.target will always be the

// NOTE: this must be _outside_ `createFocusTrap()` to make sure all traps in this
// current instance use the same stack if `userOptions.trapStack` isn't specified
var internalTrapStack = [];
var createFocusTrap = function createFocusTrap(elements, userOptions) {
// SSR: a live trap shouldn't be created in this type of environment so this
// should be safe code to execute if the `document` option isn't specified
var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document;
var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || internalTrapStack;
var config = _objectSpread2({
returnFocusOnDeactivate: true,
escapeDeactivates: true,
delayInitialFocus: true
delayInitialFocus: true,
isKeyForward: isKeyForward,
isKeyBackward: isKeyBackward
}, userOptions);
var state = {
// containers given to createFocusTrap()
// @type {Array<HTMLElement>}
containers: [],
// list of objects identifying the first and last tabbable nodes in all containers/groups in
// the trap
// list of objects identifying tabbable nodes in `containers` in the trap
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap

@@ -187,3 +186,20 @@ // is active, but the trap should never get to a state where there isn't at least one group

// result in an error being thrown)
// @type {Array<{ container: HTMLElement, firstTabbableNode: HTMLElement|null, lastTabbableNode: HTMLElement|null }>}
// @type {Array<{
// container: HTMLElement,
// tabbableNodes: Array<HTMLElement>, // empty if none
// focusableNodes: Array<HTMLElement>, // empty if none
// posTabIndexesFound: boolean,
// firstTabbableNode: HTMLElement|undefined,
// lastTabbableNode: HTMLElement|undefined,
// firstDomTabbableNode: HTMLElement|undefined,
// lastDomTabbableNode: HTMLElement|undefined,
// nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
// }>}
containerGroups: [],
// same order/length as `containers` list
// references to objects in `containerGroups`, but only those that actually have
// tabbable nodes in them
// NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
// the same length
tabbableGroups: [],

@@ -196,6 +212,16 @@ nodeFocusedBeforeActivation: null,

// has been delayed during activation
delayInitialFocusTimer: undefined
delayInitialFocusTimer: undefined,
// the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any
recentNavEvent: undefined
};
var trap; // eslint-disable-line prefer-const -- some private functions reference it, and its methods reference private functions, so we must declare here and define later
/**
* Gets a configuration option value.
* @param {Object|undefined} configOverrideOptions If true, and option is defined in this set,
* value will be taken from this object. Otherwise, value will be taken from base configuration.
* @param {string} optionName Name of the option whose value is sought.
* @param {string|undefined} [configOptionName] Name of option to use __instead of__ `optionName`
* IIF `configOverrideOptions` is not defined. Otherwise, `optionName` is used.
*/
var getOption = function getOption(configOverrideOptions, optionName, configOptionName) {

@@ -205,7 +231,30 @@ return configOverrideOptions && configOverrideOptions[optionName] !== undefined ? configOverrideOptions[optionName] : config[configOptionName || optionName];

var containersContain = function containersContain(element) {
return !!(element && state.containers.some(function (container) {
return container.contains(element);
}));
/**
* Finds the index of the container that contains the element.
* @param {HTMLElement} element
* @param {Event} [event] If available, and `element` isn't directly found in any container,
* the event's composed path is used to see if includes any known trap containers in the
* case where the element is inside a Shadow DOM.
* @returns {number} Index of the container in either `state.containers` or
* `state.containerGroups` (the order/length of these lists are the same); -1
* if the element isn't found.
*/
var findContainerIndex = function findContainerIndex(element, event) {
var composedPath = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === 'function' ? event.composedPath() : undefined;
// NOTE: search `containerGroups` because it's possible a group contains no tabbable
// nodes, but still contains focusable nodes (e.g. if they all have `tabindex=-1`)
// and we still need to find the element in there
return state.containerGroups.findIndex(function (_ref) {
var container = _ref.container,
tabbableNodes = _ref.tabbableNodes;
return container.contains(element) || ( // fall back to explicit tabbable search which will take into consideration any
// web components if the `tabbableOptions.getShadowRoot` option was used for
// the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
// look inside web components even if open)
composedPath === null || composedPath === void 0 ? void 0 : composedPath.includes(container)) || tabbableNodes.find(function (node) {
return node === element;
});
});
};
/**

@@ -224,7 +273,4 @@ * Gets the node for the given option, which is expected to be an option that

*/
var getNodeForOption = function getNodeForOption(optionName) {
var optionValue = config[optionName];
if (typeof optionValue === 'function') {

@@ -234,5 +280,7 @@ for (var _len2 = arguments.length, params = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {

}
optionValue = optionValue.apply(void 0, params);
}
if (optionValue === true) {
optionValue = undefined; // use default value
}

@@ -242,8 +290,7 @@ if (!optionValue) {

return optionValue;
} // else, empty string (invalid), null (invalid), 0 (invalid)
}
// else, empty string (invalid), null (invalid), 0 (invalid)
throw new Error("`".concat(optionName, "` was specified but was not a node, or did not return a node"));
}
var node = optionValue; // could be HTMLElement, SVGElement, or non-empty string at this point

@@ -253,3 +300,2 @@

node = doc.querySelector(optionValue); // resolve to node, or null if fails
if (!node) {

@@ -259,50 +305,104 @@ throw new Error("`".concat(optionName, "` as selector refers to no known node"));

}
return node;
};
var getInitialFocusNode = function getInitialFocusNode() {
var node = getNodeForOption('initialFocus'); // false explicitly indicates we want no initialFocus at all
var node = getNodeForOption('initialFocus');
// false explicitly indicates we want no initialFocus at all
if (node === false) {
return false;
}
if (node === undefined) {
// option not specified: use fallback options
if (containersContain(doc.activeElement)) {
if (node === undefined || !tabbable.isFocusable(node, config.tabbableOptions)) {
// option not specified nor focusable: use fallback options
if (findContainerIndex(doc.activeElement) >= 0) {
node = doc.activeElement;
} else {
var firstTabbableGroup = state.tabbableGroups[0];
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode; // NOTE: `fallbackFocus` option function cannot return `false` (not supported)
var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
// NOTE: `fallbackFocus` option function cannot return `false` (not supported)
node = firstTabbableNode || getNodeForOption('fallbackFocus');
}
}
if (!node) {
throw new Error('Your focus-trap needs to have at least one focusable element');
}
return node;
};
var updateTabbableNodes = function updateTabbableNodes() {
state.tabbableGroups = state.containers.map(function (container) {
var tabbableNodes = tabbable.tabbable(container);
state.containerGroups = state.containers.map(function (container) {
var tabbableNodes = tabbable.tabbable(container, config.tabbableOptions);
if (tabbableNodes.length > 0) {
return {
container: container,
firstTabbableNode: tabbableNodes[0],
lastTabbableNode: tabbableNodes[tabbableNodes.length - 1]
};
}
// NOTE: if we have tabbable nodes, we must have focusable nodes; focusable nodes
// are a superset of tabbable nodes since nodes with negative `tabindex` attributes
// are focusable but not tabbable
var focusableNodes = tabbable.focusable(container, config.tabbableOptions);
var firstTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[0] : undefined;
var lastTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : undefined;
var firstDomTabbableNode = focusableNodes.find(function (node) {
return tabbable.isTabbable(node);
});
var lastDomTabbableNode = focusableNodes.slice().reverse().find(function (node) {
return tabbable.isTabbable(node);
});
var posTabIndexesFound = !!tabbableNodes.find(function (node) {
return tabbable.getTabIndex(node) > 0;
});
return {
container: container,
tabbableNodes: tabbableNodes,
focusableNodes: focusableNodes,
/** True if at least one node with positive `tabindex` was found in this container. */
posTabIndexesFound: posTabIndexesFound,
/** First tabbable node in container, __tabindex__ order; `undefined` if none. */
firstTabbableNode: firstTabbableNode,
/** Last tabbable node in container, __tabindex__ order; `undefined` if none. */
lastTabbableNode: lastTabbableNode,
// NOTE: DOM order is NOT NECESSARILY "document position" order, but figuring that out
// would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
// because that API doesn't work with Shadow DOM as well as it should (@see
// https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,
// to address an edge case related to positive tabindex support, this seems like a much easier,
// "close enough most of the time" alternative for positive tabindexes which should generally
// be avoided anyway...
/** First tabbable node in container, __DOM__ order; `undefined` if none. */
firstDomTabbableNode: firstDomTabbableNode,
/** Last tabbable node in container, __DOM__ order; `undefined` if none. */
lastDomTabbableNode: lastDomTabbableNode,
/**
* Finds the __tabbable__ node that follows the given node in the specified direction,
* in this container, if any.
* @param {HTMLElement} node
* @param {boolean} [forward] True if going in forward tab order; false if going
* in reverse.
* @returns {HTMLElement|undefined} The next tabbable node, if any.
*/
nextTabbableNode: function nextTabbableNode(node) {
var forward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var nodeIdx = tabbableNodes.indexOf(node);
if (nodeIdx < 0) {
// either not tabbable nor focusable, or was focused but not tabbable (negative tabindex):
// since `node` should at least have been focusable, we assume that's the case and mimic
// what browsers do, which is set focus to the next node in __document position order__,
// regardless of positive tabindexes, if any -- and for reasons explained in the NOTE
// above related to `firstDomTabbable` and `lastDomTabbable` properties, we fall back to
// basic DOM order
if (forward) {
return focusableNodes.slice(focusableNodes.indexOf(node) + 1).find(function (el) {
return tabbable.isTabbable(el);
});
}
return focusableNodes.slice(0, focusableNodes.indexOf(node)).reverse().find(function (el) {
return tabbable.isTabbable(el);
});
}
return tabbableNodes[nodeIdx + (forward ? 1 : -1)];
}
};
});
state.tabbableGroups = state.containerGroups.filter(function (group) {
return group.tabbableNodes.length > 0;
});
return undefined;
}).filter(function (group) {
return !!group;
}); // remove groups with no tabbable nodes
// throw if no groups have tabbable nodes and we don't have a fallback focus node either
if (state.tabbableGroups.length <= 0 && !getNodeForOption('fallbackFocus') // returning false not supported for this option

@@ -312,4 +412,16 @@ ) {

}
// NOTE: Positive tabindexes are only properly supported in single-container traps because
// doing it across multiple containers where tabindexes could be all over the place
// would require Tabbable to support multiple containers, would require additional
// specialized Shadow DOM support, and would require Tabbable's multi-container support
// to look at those containers in document position order rather than user-provided
// order (as they are treated in Focus-trap, for legacy reasons). See discussion on
// https://github.com/focus-trap/focus-trap/issues/375 for more details.
if (state.containerGroups.find(function (g) {
return g.posTabIndexesFound;
}) && state.containerGroups.length > 1) {
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.");
}
};
var tryFocus = function tryFocus(node) {

@@ -319,7 +431,5 @@ if (node === false) {

}
if (node === doc.activeElement) {
return;
}
if (!node || !node.focus) {

@@ -329,8 +439,7 @@ tryFocus(getInitialFocusNode());

}
node.focus({
preventScroll: !!config.preventScroll
});
// NOTE: focus() API does not trigger focusIn event so set MRU node manually
state.mostRecentlyFocusedNode = node;
if (isSelectableInput(node)) {

@@ -340,87 +449,36 @@ node.select();

};
var getReturnFocusNode = function getReturnFocusNode(previousActiveElement) {
var node = getNodeForOption('setReturnFocus', previousActiveElement);
return node ? node : node === false ? false : previousActiveElement;
}; // This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
};
var checkPointerDown = function checkPointerDown(e) {
var target = getActualTarget(e);
if (containersContain(target)) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// if, on deactivation, we should return focus to the node originally-focused
// when the trap was activated (or the configured `setReturnFocus` node),
// then assume it's also OK to return focus to the outside node that was
// just clicked, causing deactivation, as long as that node is focusable;
// if it isn't focusable, then return focus to the original node focused
// on activation (or the configured `setReturnFocus` node)
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked, whether it's focusable or not; by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node)
returnFocus: config.returnFocusOnDeactivate && !tabbable.isFocusable(target)
});
return;
} // This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
} // otherwise, prevent the click
e.preventDefault();
}; // In case focus escapes the trap for some strange reason, pull it back in.
var checkFocusIn = function checkFocusIn(e) {
var target = getActualTarget(e);
var targetContained = containersContain(target); // In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
e.stopImmediatePropagation();
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}; // Hijack Tab events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
var checkTab = function checkTab(e) {
var target = getActualTarget(e);
/**
* Finds the next node (in either direction) where focus should move according to a
* keyboard focus-in event.
* @param {Object} params
* @param {Node} [params.target] Known target __from which__ to navigate, if any.
* @param {KeyboardEvent|FocusEvent} [params.event] Event to use if `target` isn't known (event
* will be used to determine the `target`). Ignored if `target` is specified.
* @param {boolean} [params.isBackward] True if focus should move backward.
* @returns {Node|undefined} The next node, or `undefined` if a next node couldn't be
* determined given the current state of the trap.
*/
var findNextNavNode = function findNextNavNode(_ref2) {
var target = _ref2.target,
event = _ref2.event,
_ref2$isBackward = _ref2.isBackward,
isBackward = _ref2$isBackward === void 0 ? false : _ref2$isBackward;
target = target || getActualTarget(event);
updateTabbableNodes();
var destinationNode = null;
if (state.tabbableGroups.length > 0) {
// make sure the target is actually contained in a group
// NOTE: the target may also be the container itself if it's tabbable
// NOTE: the target may also be the container itself if it's focusable
// with tabIndex='-1' and was given initial focus
var containerIndex = findIndex(state.tabbableGroups, function (_ref) {
var container = _ref.container;
return container.contains(target);
});
var containerIndex = findContainerIndex(target, event);
var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : undefined;
if (containerIndex < 0) {
// target not found in any group: quite possible focus has escaped the trap,
// so bring it back in to...
if (e.shiftKey) {
// so bring it back into...
if (isBackward) {
// ...the last node in the last group

@@ -432,12 +490,15 @@ destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;

}
} else if (e.shiftKey) {
} else if (isBackward) {
// REVERSE
// is the target the first tabbable node in a group?
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref2) {
var firstTabbableNode = _ref2.firstTabbableNode;
var startOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
var firstTabbableNode = _ref3.firstTabbableNode;
return target === firstTabbableNode;
});
if (startOfGroupIndex < 0 && state.tabbableGroups[containerIndex].container === target) {
// an exception case where the target is the container itself, in which
if (startOfGroupIndex < 0 && (containerGroup.container === target || tabbable.isFocusable(target, config.tabbableOptions) && !tabbable.isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {
// an exception case where the target is either the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle shift+tab as if focus were on the container's

@@ -447,3 +508,2 @@ // first tabbable node, and go to the last tabbable node of the LAST group

}
if (startOfGroupIndex >= 0) {

@@ -455,14 +515,21 @@ // YES: then shift+tab should go to the last tabbable node in the

var destinationGroup = state.tabbableGroups[destinationGroupIndex];
destinationNode = destinationGroup.lastTabbableNode;
destinationNode = tabbable.getTabIndex(target) >= 0 ? destinationGroup.lastTabbableNode : destinationGroup.lastDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target, false);
}
} else {
// FORWARD
// is the target the last tabbable node in a group?
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref3) {
var lastTabbableNode = _ref3.lastTabbableNode;
var lastOfGroupIndex = findIndex(state.tabbableGroups, function (_ref4) {
var lastTabbableNode = _ref4.lastTabbableNode;
return target === lastTabbableNode;
});
if (lastOfGroupIndex < 0 && state.tabbableGroups[containerIndex].container === target) {
// an exception case where the target is the container itself, in which
if (lastOfGroupIndex < 0 && (containerGroup.container === target || tabbable.isFocusable(target, config.tabbableOptions) && !tabbable.isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {
// an exception case where the target is the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle tab as if focus were on the container's

@@ -472,3 +539,2 @@ // last tabbable node, and go to the first tabbable node of the FIRST group

}
if (lastOfGroupIndex >= 0) {

@@ -479,63 +545,214 @@ // YES: then tab should go to the first tabbable node in the next

var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
destinationNode = _destinationGroup.firstTabbableNode;
destinationNode = tabbable.getTabIndex(target) >= 0 ? _destinationGroup.firstTabbableNode : _destinationGroup.firstDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target);
}
}
} else {
// no groups available
// NOTE: the fallbackFocus option does not support returning false to opt-out
destinationNode = getNodeForOption('fallbackFocus');
}
return destinationNode;
};
// This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
var checkPointerDown = function checkPointerDown(e) {
var target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked (and if not focusable, to "nothing"); by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node), whether the
// outside click was on a focusable node or not
returnFocus: config.returnFocusOnDeactivate
});
return;
}
// This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
}
// otherwise, prevent the click
e.preventDefault();
};
// In case focus escapes the trap for some strange reason, pull it back in.
// NOTE: the focusIn event is NOT cancelable, so if focus escapes, it may cause unexpected
// scrolling if the node that got focused was out of view; there's nothing we can do to
// prevent that from happening by the time we discover that focus escaped
var checkFocusIn = function checkFocusIn(event) {
var target = getActualTarget(event);
var targetContained = findContainerIndex(target, event) >= 0;
// In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
event.stopImmediatePropagation();
// focus will escape if the MRU node had a positive tab index and user tried to nav forward;
// it will also escape if the MRU node had a 0 tab index and user tried to nav backward
// toward a node with a positive tab index
var nextNode; // next node to focus, if we find one
var navAcrossContainers = true;
if (state.mostRecentlyFocusedNode) {
if (tabbable.getTabIndex(state.mostRecentlyFocusedNode) > 0) {
// MRU container index must be >=0 otherwise we wouldn't have it as an MRU node...
var mruContainerIdx = findContainerIndex(state.mostRecentlyFocusedNode);
// there MAY not be any tabbable nodes in the container if there are at least 2 containers
// and the MRU node is focusable but not tabbable (focus-trap requires at least 1 container
// with at least one tabbable node in order to function, so this could be the other container
// with nothing tabbable in it)
var tabbableNodes = state.containerGroups[mruContainerIdx].tabbableNodes;
if (tabbableNodes.length > 0) {
// MRU tab index MAY not be found if the MRU node is focusable but not tabbable
var mruTabIdx = tabbableNodes.findIndex(function (node) {
return node === state.mostRecentlyFocusedNode;
});
if (mruTabIdx >= 0) {
if (config.isKeyForward(state.recentNavEvent)) {
if (mruTabIdx + 1 < tabbableNodes.length) {
nextNode = tabbableNodes[mruTabIdx + 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
} else {
if (mruTabIdx - 1 >= 0) {
nextNode = tabbableNodes[mruTabIdx - 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
}
// else, don't find in container order without considering direction too
}
}
// else, no tabbable nodes in that container (which means we must have at least one other
// container with at least one tabbable node in it, otherwise focus-trap would've thrown
// an error the last time updateTabbableNodes() was run): find next node among all known
// containers
} else {
// check to see if there's at least one tabbable node with a positive tab index inside
// the trap because focus seems to escape when navigating backward from a tabbable node
// with tabindex=0 when this is the case (instead of wrapping to the tabbable node with
// the greatest positive tab index like it should)
if (!state.containerGroups.some(function (g) {
return g.tabbableNodes.some(function (n) {
return tabbable.getTabIndex(n) > 0;
});
})) {
// no containers with tabbable nodes with positive tab indexes which means the focus
// escaped for some other reason and we should just execute the fallback to the
// MRU node or initial focus node, if any
navAcrossContainers = false;
}
}
} else {
// no MRU node means we're likely in some initial condition when the trap has just
// been activated and initial focus hasn't been given yet, in which case we should
// fall through to trying to focus the initial focus node, which is what should
// happen below at this point in the logic
navAcrossContainers = false;
}
if (navAcrossContainers) {
nextNode = findNextNavNode({
// move FROM the MRU node, not event-related node (which will be the node that is
// outside the trap causing the focus escape we're trying to fix)
target: state.mostRecentlyFocusedNode,
isBackward: config.isKeyBackward(state.recentNavEvent)
});
}
if (nextNode) {
tryFocus(nextNode);
} else {
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}
state.recentNavEvent = undefined; // clear
};
// Hijack key nav events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
var checkKeyNav = function checkKeyNav(event) {
var isBackward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
state.recentNavEvent = event;
var destinationNode = findNextNavNode({
event: event,
isBackward: isBackward
});
if (destinationNode) {
e.preventDefault();
if (isTabEvent(event)) {
// since tab natively moves focus, we wouldn't have a destination node unless we
// were on the edge of a container and had to move to the next/previous edge, in
// which case we want to prevent default to keep the browser from moving focus
// to where it normally would
event.preventDefault();
}
tryFocus(destinationNode);
} // else, let the browser take care of [shift+]tab and move the focus
}
// else, let the browser take care of [shift+]tab and move the focus
};
var checkKey = function checkKey(e) {
if (isEscapeEvent(e) && valueOrHandler(config.escapeDeactivates, e) !== false) {
e.preventDefault();
var checkKey = function checkKey(event) {
if (isEscapeEvent(event) && valueOrHandler(config.escapeDeactivates, event) !== false) {
event.preventDefault();
trap.deactivate();
return;
}
if (isTabEvent(e)) {
checkTab(e);
return;
if (config.isKeyForward(event) || config.isKeyBackward(event)) {
checkKeyNav(event, config.isKeyBackward(event));
}
};
var checkClick = function checkClick(e) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
var target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
return;
}
var target = getActualTarget(e);
if (containersContain(target)) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
return;
}
if (valueOrHandler(config.allowOutsideClick, e)) {
return;
}
e.preventDefault();
e.stopImmediatePropagation();
}; //
};
//
// EVENT LISTENERS
//
var addListeners = function addListeners() {
if (!state.active) {
return;
} // There can be only one listening focus trap at a time
}
// There can be only one listening focus trap at a time
activeFocusTraps.activateTrap(trapStack, trap);
activeFocusTraps.activateTrap(trap); // Delay ensures that the focused element doesn't capture the event
// Delay ensures that the focused element doesn't capture the event
// that caused the focus trap activation.
state.delayInitialFocusTimer = config.delayInitialFocus ? delay(function () {

@@ -563,3 +780,2 @@ tryFocus(getInitialFocusNode());

};
var removeListeners = function removeListeners() {

@@ -569,3 +785,2 @@ if (!state.active) {

}
doc.removeEventListener('focusin', checkFocusIn, true);

@@ -577,8 +792,52 @@ doc.removeEventListener('mousedown', checkPointerDown, true);

return trap;
}; //
};
//
// MUTATION OBSERVER
//
var checkDomRemoval = function checkDomRemoval(mutations) {
var isFocusedNodeRemoved = mutations.some(function (mutation) {
var removedNodes = Array.from(mutation.removedNodes);
return removedNodes.some(function (node) {
return node === state.mostRecentlyFocusedNode;
});
});
// If the currently focused is removed then browsers will move focus to the
// <body> element. If this happens, try to move focus back into the trap.
if (isFocusedNodeRemoved) {
tryFocus(getInitialFocusNode());
}
};
// Use MutationObserver - if supported - to detect if focused node is removed
// from the DOM.
var mutationObserver = typeof window !== 'undefined' && 'MutationObserver' in window ? new MutationObserver(checkDomRemoval) : undefined;
var updateObservedNodes = function updateObservedNodes() {
if (!mutationObserver) {
return;
}
mutationObserver.disconnect();
if (state.active && !state.paused) {
state.containers.map(function (container) {
mutationObserver.observe(container, {
subtree: true,
childList: true
});
});
}
};
//
// TRAP DEFINITION
//
trap = {
get active() {
return state.active;
},
get paused() {
return state.paused;
},
activate: function activate(activateOptions) {

@@ -588,19 +847,12 @@ if (state.active) {

}
var onActivate = getOption(activateOptions, 'onActivate');
var onPostActivate = getOption(activateOptions, 'onPostActivate');
var checkCanFocusTrap = getOption(activateOptions, 'checkCanFocusTrap');
if (!checkCanFocusTrap) {
updateTabbableNodes();
}
state.active = true;
state.paused = false;
state.nodeFocusedBeforeActivation = doc.activeElement;
if (onActivate) {
onActivate();
}
onActivate === null || onActivate === void 0 ? void 0 : onActivate();
var finishActivation = function finishActivation() {

@@ -610,10 +862,6 @@ if (checkCanFocusTrap) {

}
addListeners();
if (onPostActivate) {
onPostActivate();
}
updateObservedNodes();
onPostActivate === null || onPostActivate === void 0 ? void 0 : onPostActivate();
};
if (checkCanFocusTrap) {

@@ -623,3 +871,2 @@ checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);

}
finishActivation();

@@ -632,5 +879,8 @@ return this;

}
var options = _objectSpread2({
onDeactivate: config.onDeactivate,
onPostDeactivate: config.onPostDeactivate,
checkCanReturnFocus: config.checkCanReturnFocus
}, deactivateOptions);
clearTimeout(state.delayInitialFocusTimer); // noop if undefined
state.delayInitialFocusTimer = undefined;

@@ -640,13 +890,9 @@ removeListeners();

state.paused = false;
activeFocusTraps.deactivateTrap(trap);
var onDeactivate = getOption(deactivateOptions, 'onDeactivate');
var onPostDeactivate = getOption(deactivateOptions, 'onPostDeactivate');
var checkCanReturnFocus = getOption(deactivateOptions, 'checkCanReturnFocus');
if (onDeactivate) {
onDeactivate();
}
var returnFocus = getOption(deactivateOptions, 'returnFocus', 'returnFocusOnDeactivate');
updateObservedNodes();
activeFocusTraps.deactivateTrap(trapStack, trap);
var onDeactivate = getOption(options, 'onDeactivate');
var onPostDeactivate = getOption(options, 'onPostDeactivate');
var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
onDeactivate === null || onDeactivate === void 0 ? void 0 : onDeactivate();
var finishDeactivation = function finishDeactivation() {

@@ -657,9 +903,5 @@ delay(function () {

}
if (onPostDeactivate) {
onPostDeactivate();
}
onPostDeactivate === null || onPostDeactivate === void 0 ? void 0 : onPostDeactivate();
});
};
if (returnFocus && checkCanReturnFocus) {

@@ -669,23 +911,30 @@ checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);

}
finishDeactivation();
return this;
},
pause: function pause() {
pause: function pause(pauseOptions) {
if (state.paused || !state.active) {
return this;
}
var onPause = getOption(pauseOptions, 'onPause');
var onPostPause = getOption(pauseOptions, 'onPostPause');
state.paused = true;
onPause === null || onPause === void 0 ? void 0 : onPause();
removeListeners();
updateObservedNodes();
onPostPause === null || onPostPause === void 0 ? void 0 : onPostPause();
return this;
},
unpause: function unpause() {
unpause: function unpause(unpauseOptions) {
if (!state.paused || !state.active) {
return this;
}
var onUnpause = getOption(unpauseOptions, 'onUnpause');
var onPostUnpause = getOption(unpauseOptions, 'onPostUnpause');
state.paused = false;
onUnpause === null || onUnpause === void 0 ? void 0 : onUnpause();
updateTabbableNodes();
addListeners();
updateObservedNodes();
onPostUnpause === null || onPostUnpause === void 0 ? void 0 : onPostUnpause();
return this;

@@ -698,11 +947,11 @@ },

});
if (state.active) {
updateTabbableNodes();
}
updateObservedNodes();
return this;
}
}; // initialize container elements
};
// initialize container elements
trap.updateContainerElements(elements);

@@ -709,0 +958,0 @@ return trap;

/*!
* focus-trap 6.7.1
* focus-trap 7.5.2
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("tabbable")):"function"==typeof define&&define.amd?define(["exports","tabbable"],t):(e="undefined"!=typeof globalThis?globalThis:e||self,function(){var n=e.focusTrap,a=e.focusTrap={};t(a,e.tabbable),a.noConflict=function(){return e.focusTrap=n,a}}())}(this,(function(e,t){"use strict";function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var r,o=(r=[],{activateTrap:function(e){if(r.length>0){var t=r[r.length-1];t!==e&&t.pause()}var n=r.indexOf(e);-1===n||r.splice(n,1),r.push(e)},deactivateTrap:function(e){var t=r.indexOf(e);-1!==t&&r.splice(t,1),r.length>0&&r[r.length-1].unpause()}}),i=function(e){return setTimeout(e,0)},c=function(e,t){var n=-1;return e.every((function(e,a){return!t(e)||(n=a,!1)})),n},u=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),a=1;a<t;a++)n[a-1]=arguments[a];return"function"==typeof e?e.apply(void 0,n):e},s=function(e){return e.target.shadowRoot&&"function"==typeof e.composedPath?e.composedPath()[0]:e.target};e.createFocusTrap=function(e,r){var l,f=(null==r?void 0:r.document)||document,b=function(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?n(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):n(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}({returnFocusOnDeactivate:!0,escapeDeactivates:!0,delayInitialFocus:!0},r),v={containers:[],tabbableGroups:[],nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1,delayInitialFocusTimer:void 0},d=function(e,t,n){return e&&void 0!==e[t]?e[t]:b[n||t]},p=function(e){return!(!e||!v.containers.some((function(t){return t.contains(e)})))},h=function(e){var t=b[e];if("function"==typeof t){for(var n=arguments.length,a=new Array(n>1?n-1:0),r=1;r<n;r++)a[r-1]=arguments[r];t=t.apply(void 0,a)}if(!t){if(void 0===t||!1===t)return t;throw new Error("`".concat(e,"` was specified but was not a node, or did not return a node"))}var o=t;if("string"==typeof t&&!(o=f.querySelector(t)))throw new Error("`".concat(e,"` as selector refers to no known node"));return o},y=function(){var e=h("initialFocus");if(!1===e)return!1;if(void 0===e)if(p(f.activeElement))e=f.activeElement;else{var t=v.tabbableGroups[0];e=t&&t.firstTabbableNode||h("fallbackFocus")}if(!e)throw new Error("Your focus-trap needs to have at least one focusable element");return e},m=function(){if(v.tabbableGroups=v.containers.map((function(e){var n=t.tabbable(e);if(n.length>0)return{container:e,firstTabbableNode:n[0],lastTabbableNode:n[n.length-1]}})).filter((function(e){return!!e})),v.tabbableGroups.length<=0&&!h("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times")},g=function e(t){!1!==t&&t!==f.activeElement&&(t&&t.focus?(t.focus({preventScroll:!!b.preventScroll}),v.mostRecentlyFocusedNode=t,function(e){return e.tagName&&"input"===e.tagName.toLowerCase()&&"function"==typeof e.select}(t)&&t.select()):e(y()))},w=function(e){var t=h("setReturnFocus",e);return t||!1!==t&&e},F=function(e){var n=s(e);p(n)||(u(b.clickOutsideDeactivates,e)?l.deactivate({returnFocus:b.returnFocusOnDeactivate&&!t.isFocusable(n)}):u(b.allowOutsideClick,e)||e.preventDefault())},O=function(e){var t=s(e),n=p(t);n||t instanceof Document?n&&(v.mostRecentlyFocusedNode=t):(e.stopImmediatePropagation(),g(v.mostRecentlyFocusedNode||y()))},T=function(e){if(function(e){return"Escape"===e.key||"Esc"===e.key||27===e.keyCode}(e)&&!1!==u(b.escapeDeactivates,e))return e.preventDefault(),void l.deactivate();(function(e){return"Tab"===e.key||9===e.keyCode})(e)&&function(e){var t=s(e);m();var n=null;if(v.tabbableGroups.length>0){var a=c(v.tabbableGroups,(function(e){return e.container.contains(t)}));if(a<0)n=e.shiftKey?v.tabbableGroups[v.tabbableGroups.length-1].lastTabbableNode:v.tabbableGroups[0].firstTabbableNode;else if(e.shiftKey){var r=c(v.tabbableGroups,(function(e){var n=e.firstTabbableNode;return t===n}));if(r<0&&v.tabbableGroups[a].container===t&&(r=a),r>=0){var o=0===r?v.tabbableGroups.length-1:r-1;n=v.tabbableGroups[o].lastTabbableNode}}else{var i=c(v.tabbableGroups,(function(e){var n=e.lastTabbableNode;return t===n}));if(i<0&&v.tabbableGroups[a].container===t&&(i=a),i>=0){var u=i===v.tabbableGroups.length-1?0:i+1;n=v.tabbableGroups[u].firstTabbableNode}}}else n=h("fallbackFocus");n&&(e.preventDefault(),g(n))}(e)},E=function(e){if(!u(b.clickOutsideDeactivates,e)){var t=s(e);p(t)||u(b.allowOutsideClick,e)||(e.preventDefault(),e.stopImmediatePropagation())}},k=function(){if(v.active)return o.activateTrap(l),v.delayInitialFocusTimer=b.delayInitialFocus?i((function(){g(y())})):g(y()),f.addEventListener("focusin",O,!0),f.addEventListener("mousedown",F,{capture:!0,passive:!1}),f.addEventListener("touchstart",F,{capture:!0,passive:!1}),f.addEventListener("click",E,{capture:!0,passive:!1}),f.addEventListener("keydown",T,{capture:!0,passive:!1}),l},D=function(){if(v.active)return f.removeEventListener("focusin",O,!0),f.removeEventListener("mousedown",F,!0),f.removeEventListener("touchstart",F,!0),f.removeEventListener("click",E,!0),f.removeEventListener("keydown",T,!0),l};return(l={activate:function(e){if(v.active)return this;var t=d(e,"onActivate"),n=d(e,"onPostActivate"),a=d(e,"checkCanFocusTrap");a||m(),v.active=!0,v.paused=!1,v.nodeFocusedBeforeActivation=f.activeElement,t&&t();var r=function(){a&&m(),k(),n&&n()};return a?(a(v.containers.concat()).then(r,r),this):(r(),this)},deactivate:function(e){if(!v.active)return this;clearTimeout(v.delayInitialFocusTimer),v.delayInitialFocusTimer=void 0,D(),v.active=!1,v.paused=!1,o.deactivateTrap(l);var t=d(e,"onDeactivate"),n=d(e,"onPostDeactivate"),a=d(e,"checkCanReturnFocus");t&&t();var r=d(e,"returnFocus","returnFocusOnDeactivate"),c=function(){i((function(){r&&g(w(v.nodeFocusedBeforeActivation)),n&&n()}))};return r&&a?(a(w(v.nodeFocusedBeforeActivation)).then(c,c),this):(c(),this)},pause:function(){return v.paused||!v.active||(v.paused=!0,D()),this},unpause:function(){return v.paused&&v.active?(v.paused=!1,m(),k(),this):this},updateContainerElements:function(e){var t=[].concat(e).filter(Boolean);return v.containers=t.map((function(e){return"string"==typeof e?f.querySelector(e):e})),v.active&&m(),this}}).updateContainerElements(e),l},Object.defineProperty(e,"__esModule",{value:!0})}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("tabbable")):"function"==typeof define&&define.amd?define(["exports","tabbable"],t):(e="undefined"!=typeof globalThis?globalThis:e||self,function(){var n=e.focusTrap,o=e.focusTrap={};t(o,e.tabbable),o.noConflict=function(){return e.focusTrap=n,o}}())}(this,(function(e,t){"use strict";function n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function o(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?n(Object(o),!0).forEach((function(t){a(e,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):n(Object(o)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))}))}return e}function a(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t||"default");if("object"!=typeof o)return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var r=function(e,t){if(e.length>0){var n=e[e.length-1];n!==t&&n.pause()}var o=e.indexOf(t);-1===o||e.splice(o,1),e.push(t)},i=function(e,t){var n=e.indexOf(t);-1!==n&&e.splice(n,1),e.length>0&&e[e.length-1].unpause()},u=function(e){return"Tab"===(null==e?void 0:e.key)||9===(null==e?void 0:e.keyCode)},c=function(e){return u(e)&&!e.shiftKey},s=function(e){return u(e)&&e.shiftKey},l=function(e){return setTimeout(e,0)},b=function(e,t){var n=-1;return e.every((function(e,o){return!t(e)||(n=o,!1)})),n},d=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),o=1;o<t;o++)n[o-1]=arguments[o];return"function"==typeof e?e.apply(void 0,n):e},f=function(e){return e.target.shadowRoot&&"function"==typeof e.composedPath?e.composedPath()[0]:e.target},v=[];e.createFocusTrap=function(e,n){var a,p=(null==n?void 0:n.document)||document,y=(null==n?void 0:n.trapStack)||v,m=o({returnFocusOnDeactivate:!0,escapeDeactivates:!0,delayInitialFocus:!0,isKeyForward:c,isKeyBackward:s},n),h={containers:[],containerGroups:[],tabbableGroups:[],nodeFocusedBeforeActivation:null,mostRecentlyFocusedNode:null,active:!1,paused:!1,delayInitialFocusTimer:void 0,recentNavEvent:void 0},g=function(e,t,n){return e&&void 0!==e[t]?e[t]:m[n||t]},w=function(e,t){var n="function"==typeof(null==t?void 0:t.composedPath)?t.composedPath():void 0;return h.containerGroups.findIndex((function(t){var o=t.container,a=t.tabbableNodes;return o.contains(e)||(null==n?void 0:n.includes(o))||a.find((function(t){return t===e}))}))},T=function(e){var t=m[e];if("function"==typeof t){for(var n=arguments.length,o=new Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];t=t.apply(void 0,o)}if(!0===t&&(t=void 0),!t){if(void 0===t||!1===t)return t;throw new Error("`".concat(e,"` was specified but was not a node, or did not return a node"))}var r=t;if("string"==typeof t&&!(r=p.querySelector(t)))throw new Error("`".concat(e,"` as selector refers to no known node"));return r},F=function(){var e=T("initialFocus");if(!1===e)return!1;if(void 0===e||!t.isFocusable(e,m.tabbableOptions))if(w(p.activeElement)>=0)e=p.activeElement;else{var n=h.tabbableGroups[0];e=n&&n.firstTabbableNode||T("fallbackFocus")}if(!e)throw new Error("Your focus-trap needs to have at least one focusable element");return e},N=function(){if(h.containerGroups=h.containers.map((function(e){var n=t.tabbable(e,m.tabbableOptions),o=t.focusable(e,m.tabbableOptions),a=n.length>0?n[0]:void 0,r=n.length>0?n[n.length-1]:void 0,i=o.find((function(e){return t.isTabbable(e)})),u=o.slice().reverse().find((function(e){return t.isTabbable(e)})),c=!!n.find((function(e){return t.getTabIndex(e)>0}));return{container:e,tabbableNodes:n,focusableNodes:o,posTabIndexesFound:c,firstTabbableNode:a,lastTabbableNode:r,firstDomTabbableNode:i,lastDomTabbableNode:u,nextTabbableNode:function(e){var a=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=n.indexOf(e);return r<0?a?o.slice(o.indexOf(e)+1).find((function(e){return t.isTabbable(e)})):o.slice(0,o.indexOf(e)).reverse().find((function(e){return t.isTabbable(e)})):n[r+(a?1:-1)]}}})),h.tabbableGroups=h.containerGroups.filter((function(e){return e.tabbableNodes.length>0})),h.tabbableGroups.length<=0&&!T("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");if(h.containerGroups.find((function(e){return e.posTabIndexesFound}))&&h.containerGroups.length>1)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.")},O=function e(t){!1!==t&&t!==p.activeElement&&(t&&t.focus?(t.focus({preventScroll:!!m.preventScroll}),h.mostRecentlyFocusedNode=t,function(e){return e.tagName&&"input"===e.tagName.toLowerCase()&&"function"==typeof e.select}(t)&&t.select()):e(F()))},E=function(e){var t=T("setReturnFocus",e);return t||!1!==t&&e},k=function(e){var n=e.target,o=e.event,a=e.isBackward,r=void 0!==a&&a;n=n||f(o),N();var i=null;if(h.tabbableGroups.length>0){var c=w(n,o),s=c>=0?h.containerGroups[c]:void 0;if(c<0)i=r?h.tabbableGroups[h.tabbableGroups.length-1].lastTabbableNode:h.tabbableGroups[0].firstTabbableNode;else if(r){var l=b(h.tabbableGroups,(function(e){var t=e.firstTabbableNode;return n===t}));if(l<0&&(s.container===n||t.isFocusable(n,m.tabbableOptions)&&!t.isTabbable(n,m.tabbableOptions)&&!s.nextTabbableNode(n,!1))&&(l=c),l>=0){var d=0===l?h.tabbableGroups.length-1:l-1,v=h.tabbableGroups[d];i=t.getTabIndex(n)>=0?v.lastTabbableNode:v.lastDomTabbableNode}else u(o)||(i=s.nextTabbableNode(n,!1))}else{var p=b(h.tabbableGroups,(function(e){var t=e.lastTabbableNode;return n===t}));if(p<0&&(s.container===n||t.isFocusable(n,m.tabbableOptions)&&!t.isTabbable(n,m.tabbableOptions)&&!s.nextTabbableNode(n))&&(p=c),p>=0){var y=p===h.tabbableGroups.length-1?0:p+1,g=h.tabbableGroups[y];i=t.getTabIndex(n)>=0?g.firstTabbableNode:g.firstDomTabbableNode}else u(o)||(i=s.nextTabbableNode(n))}}else i=T("fallbackFocus");return i},P=function(e){var t=f(e);w(t,e)>=0||(d(m.clickOutsideDeactivates,e)?a.deactivate({returnFocus:m.returnFocusOnDeactivate}):d(m.allowOutsideClick,e)||e.preventDefault())},D=function(e){var n=f(e),o=w(n,e)>=0;if(o||n instanceof Document)o&&(h.mostRecentlyFocusedNode=n);else{var a;e.stopImmediatePropagation();var r=!0;if(h.mostRecentlyFocusedNode)if(t.getTabIndex(h.mostRecentlyFocusedNode)>0){var i=w(h.mostRecentlyFocusedNode),u=h.containerGroups[i].tabbableNodes;if(u.length>0){var c=u.findIndex((function(e){return e===h.mostRecentlyFocusedNode}));c>=0&&(m.isKeyForward(h.recentNavEvent)?c+1<u.length&&(a=u[c+1],r=!1):c-1>=0&&(a=u[c-1],r=!1))}}else h.containerGroups.some((function(e){return e.tabbableNodes.some((function(e){return t.getTabIndex(e)>0}))}))||(r=!1);else r=!1;r&&(a=k({target:h.mostRecentlyFocusedNode,isBackward:m.isKeyBackward(h.recentNavEvent)})),O(a||(h.mostRecentlyFocusedNode||F()))}h.recentNavEvent=void 0},x=function(e){if(!(t=e,"Escape"!==(null==t?void 0:t.key)&&"Esc"!==(null==t?void 0:t.key)&&27!==(null==t?void 0:t.keyCode)||!1===d(m.escapeDeactivates,e)))return e.preventDefault(),void a.deactivate();var t;(m.isKeyForward(e)||m.isKeyBackward(e))&&function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];h.recentNavEvent=e;var n=k({event:e,isBackward:t});n&&(u(e)&&e.preventDefault(),O(n))}(e,m.isKeyBackward(e))},G=function(e){var t=f(e);w(t,e)>=0||d(m.clickOutsideDeactivates,e)||d(m.allowOutsideClick,e)||(e.preventDefault(),e.stopImmediatePropagation())},I=function(){if(h.active)return r(y,a),h.delayInitialFocusTimer=m.delayInitialFocus?l((function(){O(F())})):O(F()),p.addEventListener("focusin",D,!0),p.addEventListener("mousedown",P,{capture:!0,passive:!1}),p.addEventListener("touchstart",P,{capture:!0,passive:!1}),p.addEventListener("click",G,{capture:!0,passive:!1}),p.addEventListener("keydown",x,{capture:!0,passive:!1}),a},j=function(){if(h.active)return p.removeEventListener("focusin",D,!0),p.removeEventListener("mousedown",P,!0),p.removeEventListener("touchstart",P,!0),p.removeEventListener("click",G,!0),p.removeEventListener("keydown",x,!0),a},R="undefined"!=typeof window&&"MutationObserver"in window?new MutationObserver((function(e){e.some((function(e){return Array.from(e.removedNodes).some((function(e){return e===h.mostRecentlyFocusedNode}))}))&&O(F())})):void 0,B=function(){R&&(R.disconnect(),h.active&&!h.paused&&h.containers.map((function(e){R.observe(e,{subtree:!0,childList:!0})})))};return(a={get active(){return h.active},get paused(){return h.paused},activate:function(e){if(h.active)return this;var t=g(e,"onActivate"),n=g(e,"onPostActivate"),o=g(e,"checkCanFocusTrap");o||N(),h.active=!0,h.paused=!1,h.nodeFocusedBeforeActivation=p.activeElement,null==t||t();var a=function(){o&&N(),I(),B(),null==n||n()};return o?(o(h.containers.concat()).then(a,a),this):(a(),this)},deactivate:function(e){if(!h.active)return this;var t=o({onDeactivate:m.onDeactivate,onPostDeactivate:m.onPostDeactivate,checkCanReturnFocus:m.checkCanReturnFocus},e);clearTimeout(h.delayInitialFocusTimer),h.delayInitialFocusTimer=void 0,j(),h.active=!1,h.paused=!1,B(),i(y,a);var n=g(t,"onDeactivate"),r=g(t,"onPostDeactivate"),u=g(t,"checkCanReturnFocus"),c=g(t,"returnFocus","returnFocusOnDeactivate");null==n||n();var s=function(){l((function(){c&&O(E(h.nodeFocusedBeforeActivation)),null==r||r()}))};return c&&u?(u(E(h.nodeFocusedBeforeActivation)).then(s,s),this):(s(),this)},pause:function(e){if(h.paused||!h.active)return this;var t=g(e,"onPause"),n=g(e,"onPostPause");return h.paused=!0,null==t||t(),j(),B(),null==n||n(),this},unpause:function(e){if(!h.paused||!h.active)return this;var t=g(e,"onUnpause"),n=g(e,"onPostUnpause");return h.paused=!1,null==t||t(),N(),I(),B(),null==n||n(),this},updateContainerElements:function(e){var t=[].concat(e).filter(Boolean);return h.containers=t.map((function(e){return"string"==typeof e?p.querySelector(e):e})),h.active&&N(),B(),this}}).updateContainerElements(e),a},Object.defineProperty(e,"__esModule",{value:!0})}));
//# sourceMappingURL=focus-trap.umd.min.js.map

@@ -0,2 +1,7 @@

import { CheckOptions as TabbableCheckOptions } from 'tabbable';
declare module 'focus-trap' {
export type FocusTargetValue = HTMLElement | SVGElement | string;
export type FocusTargetValueOrFalse = FocusTargetValue | false;
/**

@@ -7,3 +12,3 @@ * A DOM node, a selector string (which will be passed to

*/
export type FocusTarget = HTMLElement | SVGElement | string | (() => HTMLElement | SVGElement);
export type FocusTarget = FocusTargetValue | (() => FocusTargetValue);

@@ -15,3 +20,3 @@ /**

*/
export type FocusTargetOrFalse = HTMLElement | SVGElement | string | false | (() => HTMLElement | SVGElement | false);
export type FocusTargetOrFalse = FocusTargetValueOrFalse | (() => FocusTargetValueOrFalse);

@@ -21,2 +26,6 @@ type MouseEventToBoolean = (event: MouseEvent | TouchEvent) => boolean;

/** tabbable options supported by focus-trap. */
export interface FocusTrapTabbableOptions extends TabbableCheckOptions {
}
export interface Options {

@@ -33,5 +42,29 @@ /**

*/
onPostActivate?: () => void
onPostActivate?: () => void;
/**
* A function that will be called immediately after the trap's state is updated to be paused.
*/
onPause?: () => void;
/**
* A function that will be called after the trap has been completely paused and is no longer
* managing/trapping focus.
*/
onPostPause?: () => void;
/**
* 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.
*/
onUnpause?: () => void;
/**
* A function that will be called after the trap has been completely unpaused and is once
* again managing/trapping focus.
*/
onPostUnpause?: () => void;
/**
* A function for determining if it is safe to send focus to the focus trap

@@ -47,3 +80,5 @@ * or not.

*/
checkCanFocusTrap?: (containers: Array<HTMLElement | SVGElement>) => Promise<void>
checkCanFocusTrap?: (
containers: Array<HTMLElement | SVGElement>
) => Promise<void>;

@@ -61,3 +96,3 @@ /**

*/
onPostDeactivate?: () => void
onPostDeactivate?: () => void;
/**

@@ -78,3 +113,3 @@ * A function for determining if it is safe to send focus back to the `trigger` element.

*/
checkCanReturnFocus?: (trigger: HTMLElement | SVGElement) => Promise<void>
checkCanReturnFocus?: (trigger: HTMLElement | SVGElement) => Promise<void>;

@@ -116,3 +151,7 @@ /**

*/
setReturnFocus?: HTMLElement | SVGElement | string | false | ((nodeFocusedBeforeActivation: HTMLElement | SVGElement) => HTMLElement | SVGElement | false);
setReturnFocus?:
| FocusTargetValueOrFalse
| ((
nodeFocusedBeforeActivation: HTMLElement | SVGElement
) => FocusTargetValueOrFalse);
/**

@@ -159,6 +198,39 @@ * Default: `true`. If `false` or returns `false`, the `Escape` key will not trigger

*/
document?: Document;
document?: Document;
/**
* Specific tabbable options configurable on focus-trap.
*/
tabbableOptions?: FocusTrapTabbableOptions;
/**
* Define the global trap stack. This makes it possible to share the same stack
* in multiple instances of `focus-trap` in the same page such that
* auto-activation/pausing of traps is properly coordinated among all instances
* as activating a trap when another is already active should result in the other
* being auto-paused. By default, each instance will have its own internal stack,
* leading to conflicts if they each try to trap the focus at the same time.
*/
trapStack?: Array<FocusTrap>;
/**
* Determines if the given keyboard event is a "tab forward" event that will move
* the focus to the next trapped element in tab order. Defaults to the `TAB` key.
* Use this to override the trap's behavior if you want to use arrow keys to control
* keyboard navigation within the trap, for example. Also see `isKeyBackward()` option.
*/
isKeyForward?: KeyboardEventToBoolean;
/**
* Determines if the given keyboard event is a "tab backward" event that will move
* the focus to the previous trapped element in tab order. Defaults to the `SHIFT+TAB` key.
* Use this to override the trap's behavior if you want to use arrow keys to control
* keyboard navigation within the trap, for example. Also see `isKeyForward()` option.
*/
isKeyBackward?: KeyboardEventToBoolean;
}
type ActivateOptions = Pick<Options, 'onActivate' | 'onPostActivate' | 'checkCanFocusTrap'>;
type PauseOptions = Pick<Options, 'onPause' | 'onPostPause'>;
type UnpauseOptions = Pick<Options, 'onUnpause' | 'onPostUnpause'>;

@@ -170,6 +242,8 @@ interface DeactivateOptions extends Pick<Options, 'onDeactivate' | 'onPostDeactivate' | 'checkCanReturnFocus'> {

export interface FocusTrap {
active: boolean,
paused: boolean,
activate(activateOptions?: ActivateOptions): FocusTrap;
deactivate(deactivateOptions?: DeactivateOptions): FocusTrap;
pause(): FocusTrap;
unpause(): FocusTrap;
pause(pauseOptions?: PauseOptions): FocusTrap;
unpause(unpauseOptions?: UnpauseOptions): FocusTrap;
updateContainerElements(containerElements: HTMLElement | SVGElement | string | Array<HTMLElement | SVGElement | string>): FocusTrap;

@@ -176,0 +250,0 @@ }

@@ -1,36 +0,39 @@

import { tabbable, isFocusable } from 'tabbable';
import {
tabbable,
focusable,
isFocusable,
isTabbable,
getTabIndex,
} from 'tabbable';
const activeFocusTraps = (function () {
const trapQueue = [];
return {
activateTrap(trap) {
if (trapQueue.length > 0) {
const activeTrap = trapQueue[trapQueue.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
const activeFocusTraps = {
activateTrap(trapStack, trap) {
if (trapStack.length > 0) {
const activeTrap = trapStack[trapStack.length - 1];
if (activeTrap !== trap) {
activeTrap.pause();
}
}
const trapIndex = trapQueue.indexOf(trap);
if (trapIndex === -1) {
trapQueue.push(trap);
} else {
// move this existing trap to the front of the queue
trapQueue.splice(trapIndex, 1);
trapQueue.push(trap);
}
},
const trapIndex = trapStack.indexOf(trap);
if (trapIndex === -1) {
trapStack.push(trap);
} else {
// move this existing trap to the front of the queue
trapStack.splice(trapIndex, 1);
trapStack.push(trap);
}
},
deactivateTrap(trap) {
const trapIndex = trapQueue.indexOf(trap);
if (trapIndex !== -1) {
trapQueue.splice(trapIndex, 1);
}
deactivateTrap(trapStack, trap) {
const trapIndex = trapStack.indexOf(trap);
if (trapIndex !== -1) {
trapStack.splice(trapIndex, 1);
}
if (trapQueue.length > 0) {
trapQueue[trapQueue.length - 1].unpause();
}
},
};
})();
if (trapStack.length > 0) {
trapStack[trapStack.length - 1].unpause();
}
},
};

@@ -46,9 +49,19 @@ const isSelectableInput = function (node) {

const isEscapeEvent = function (e) {
return e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27;
return e?.key === 'Escape' || e?.key === 'Esc' || e?.keyCode === 27;
};
const isTabEvent = function (e) {
return e.key === 'Tab' || e.keyCode === 9;
return e?.key === 'Tab' || e?.keyCode === 9;
};
// checks for TAB by default
const isKeyForward = function (e) {
return isTabEvent(e) && !e.shiftKey;
};
// checks for SHIFT+TAB by default
const isKeyBackward = function (e) {
return isTabEvent(e) && e.shiftKey;
};
const delay = function (fn) {

@@ -99,5 +112,13 @@ return setTimeout(fn, 0);

// NOTE: this must be _outside_ `createFocusTrap()` to make sure all traps in this
// current instance use the same stack if `userOptions.trapStack` isn't specified
const internalTrapStack = [];
const createFocusTrap = function (elements, userOptions) {
// SSR: a live trap shouldn't be created in this type of environment so this
// should be safe code to execute if the `document` option isn't specified
const doc = userOptions?.document || document;
const trapStack = userOptions?.trapStack || internalTrapStack;
const config = {

@@ -107,2 +128,4 @@ returnFocusOnDeactivate: true,

delayInitialFocus: true,
isKeyForward,
isKeyBackward,
...userOptions,

@@ -112,7 +135,7 @@ };

const state = {
// containers given to createFocusTrap()
// @type {Array<HTMLElement>}
containers: [],
// list of objects identifying the first and last tabbable nodes in all containers/groups in
// the trap
// list of objects identifying tabbable nodes in `containers` in the trap
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap

@@ -122,3 +145,19 @@ // is active, but the trap should never get to a state where there isn't at least one group

// result in an error being thrown)
// @type {Array<{ container: HTMLElement, firstTabbableNode: HTMLElement|null, lastTabbableNode: HTMLElement|null }>}
// @type {Array<{
// container: HTMLElement,
// tabbableNodes: Array<HTMLElement>, // empty if none
// focusableNodes: Array<HTMLElement>, // empty if none
// posTabIndexesFound: boolean,
// firstTabbableNode: HTMLElement|undefined,
// lastTabbableNode: HTMLElement|undefined,
// firstDomTabbableNode: HTMLElement|undefined,
// lastDomTabbableNode: HTMLElement|undefined,
// nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
// }>}
containerGroups: [], // same order/length as `containers` list
// references to objects in `containerGroups`, but only those that actually have
// tabbable nodes in them
// NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
// the same length
tabbableGroups: [],

@@ -134,2 +173,5 @@

delayInitialFocusTimer: undefined,
// the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any
recentNavEvent: undefined,
};

@@ -139,2 +181,10 @@

/**
* Gets a configuration option value.
* @param {Object|undefined} configOverrideOptions If true, and option is defined in this set,
* value will be taken from this object. Otherwise, value will be taken from base configuration.
* @param {string} optionName Name of the option whose value is sought.
* @param {string|undefined} [configOptionName] Name of option to use __instead of__ `optionName`
* IIF `configOverrideOptions` is not defined. Otherwise, `optionName` is used.
*/
const getOption = (configOverrideOptions, optionName, configOptionName) => {

@@ -147,6 +197,29 @@ return configOverrideOptions &&

const containersContain = function (element) {
return !!(
element &&
state.containers.some((container) => container.contains(element))
/**
* Finds the index of the container that contains the element.
* @param {HTMLElement} element
* @param {Event} [event] If available, and `element` isn't directly found in any container,
* the event's composed path is used to see if includes any known trap containers in the
* case where the element is inside a Shadow DOM.
* @returns {number} Index of the container in either `state.containers` or
* `state.containerGroups` (the order/length of these lists are the same); -1
* if the element isn't found.
*/
const findContainerIndex = function (element, event) {
const composedPath =
typeof event?.composedPath === 'function'
? event.composedPath()
: undefined;
// NOTE: search `containerGroups` because it's possible a group contains no tabbable
// nodes, but still contains focusable nodes (e.g. if they all have `tabindex=-1`)
// and we still need to find the element in there
return state.containerGroups.findIndex(
({ container, tabbableNodes }) =>
container.contains(element) ||
// fall back to explicit tabbable search which will take into consideration any
// web components if the `tabbableOptions.getShadowRoot` option was used for
// the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
// look inside web components even if open)
composedPath?.includes(container) ||
tabbableNodes.find((node) => node === element)
);

@@ -175,2 +248,6 @@ };

if (optionValue === true) {
optionValue = undefined; // use default value
}
if (!optionValue) {

@@ -209,5 +286,5 @@ if (optionValue === undefined || optionValue === false) {

if (node === undefined) {
// option not specified: use fallback options
if (containersContain(doc.activeElement)) {
if (node === undefined || !isFocusable(node, config.tabbableOptions)) {
// option not specified nor focusable: use fallback options
if (findContainerIndex(doc.activeElement) >= 0) {
node = doc.activeElement;

@@ -234,18 +311,92 @@ } else {

const updateTabbableNodes = function () {
state.tabbableGroups = state.containers
.map((container) => {
const tabbableNodes = tabbable(container);
state.containerGroups = state.containers.map((container) => {
const tabbableNodes = tabbable(container, config.tabbableOptions);
if (tabbableNodes.length > 0) {
return {
container,
firstTabbableNode: tabbableNodes[0],
lastTabbableNode: tabbableNodes[tabbableNodes.length - 1],
};
}
// NOTE: if we have tabbable nodes, we must have focusable nodes; focusable nodes
// are a superset of tabbable nodes since nodes with negative `tabindex` attributes
// are focusable but not tabbable
const focusableNodes = focusable(container, config.tabbableOptions);
return undefined;
})
.filter((group) => !!group); // remove groups with no tabbable nodes
const firstTabbableNode =
tabbableNodes.length > 0 ? tabbableNodes[0] : undefined;
const lastTabbableNode =
tabbableNodes.length > 0
? tabbableNodes[tabbableNodes.length - 1]
: undefined;
const firstDomTabbableNode = focusableNodes.find((node) =>
isTabbable(node)
);
const lastDomTabbableNode = focusableNodes
.slice()
.reverse()
.find((node) => isTabbable(node));
const posTabIndexesFound = !!tabbableNodes.find(
(node) => getTabIndex(node) > 0
);
return {
container,
tabbableNodes,
focusableNodes,
/** True if at least one node with positive `tabindex` was found in this container. */
posTabIndexesFound,
/** First tabbable node in container, __tabindex__ order; `undefined` if none. */
firstTabbableNode,
/** Last tabbable node in container, __tabindex__ order; `undefined` if none. */
lastTabbableNode,
// NOTE: DOM order is NOT NECESSARILY "document position" order, but figuring that out
// would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
// because that API doesn't work with Shadow DOM as well as it should (@see
// https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,
// to address an edge case related to positive tabindex support, this seems like a much easier,
// "close enough most of the time" alternative for positive tabindexes which should generally
// be avoided anyway...
/** First tabbable node in container, __DOM__ order; `undefined` if none. */
firstDomTabbableNode,
/** Last tabbable node in container, __DOM__ order; `undefined` if none. */
lastDomTabbableNode,
/**
* Finds the __tabbable__ node that follows the given node in the specified direction,
* in this container, if any.
* @param {HTMLElement} node
* @param {boolean} [forward] True if going in forward tab order; false if going
* in reverse.
* @returns {HTMLElement|undefined} The next tabbable node, if any.
*/
nextTabbableNode(node, forward = true) {
const nodeIdx = tabbableNodes.indexOf(node);
if (nodeIdx < 0) {
// either not tabbable nor focusable, or was focused but not tabbable (negative tabindex):
// since `node` should at least have been focusable, we assume that's the case and mimic
// what browsers do, which is set focus to the next node in __document position order__,
// regardless of positive tabindexes, if any -- and for reasons explained in the NOTE
// above related to `firstDomTabbable` and `lastDomTabbable` properties, we fall back to
// basic DOM order
if (forward) {
return focusableNodes
.slice(focusableNodes.indexOf(node) + 1)
.find((el) => isTabbable(el));
}
return focusableNodes
.slice(0, focusableNodes.indexOf(node))
.reverse()
.find((el) => isTabbable(el));
}
return tabbableNodes[nodeIdx + (forward ? 1 : -1)];
},
};
});
state.tabbableGroups = state.containerGroups.filter(
(group) => group.tabbableNodes.length > 0
);
// throw if no groups have tabbable nodes and we don't have a fallback focus node either

@@ -260,2 +411,18 @@ if (

}
// NOTE: Positive tabindexes are only properly supported in single-container traps because
// doing it across multiple containers where tabindexes could be all over the place
// would require Tabbable to support multiple containers, would require additional
// specialized Shadow DOM support, and would require Tabbable's multi-container support
// to look at those containers in document position order rather than user-provided
// order (as they are treated in Focus-trap, for legacy reasons). See discussion on
// https://github.com/focus-trap/focus-trap/issues/375 for more details.
if (
state.containerGroups.find((g) => g.posTabIndexesFound) &&
state.containerGroups.length > 1
) {
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."
);
}
};

@@ -278,2 +445,3 @@

node.focus({ preventScroll: !!config.preventScroll });
// NOTE: focus() API does not trigger focusIn event so set MRU node manually
state.mostRecentlyFocusedNode = node;

@@ -291,66 +459,15 @@

// This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
const checkPointerDown = function (e) {
const target = getActualTarget(e);
if (containersContain(target)) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// if, on deactivation, we should return focus to the node originally-focused
// when the trap was activated (or the configured `setReturnFocus` node),
// then assume it's also OK to return focus to the outside node that was
// just clicked, causing deactivation, as long as that node is focusable;
// if it isn't focusable, then return focus to the original node focused
// on activation (or the configured `setReturnFocus` node)
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked, whether it's focusable or not; by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node)
returnFocus: config.returnFocusOnDeactivate && !isFocusable(target),
});
return;
}
// This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
}
// otherwise, prevent the click
e.preventDefault();
};
// In case focus escapes the trap for some strange reason, pull it back in.
const checkFocusIn = function (e) {
const target = getActualTarget(e);
const targetContained = containersContain(target);
// In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
e.stopImmediatePropagation();
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
};
// Hijack Tab events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
const checkTab = function (e) {
const target = getActualTarget(e);
/**
* Finds the next node (in either direction) where focus should move according to a
* keyboard focus-in event.
* @param {Object} params
* @param {Node} [params.target] Known target __from which__ to navigate, if any.
* @param {KeyboardEvent|FocusEvent} [params.event] Event to use if `target` isn't known (event
* will be used to determine the `target`). Ignored if `target` is specified.
* @param {boolean} [params.isBackward] True if focus should move backward.
* @returns {Node|undefined} The next node, or `undefined` if a next node couldn't be
* determined given the current state of the trap.
*/
const findNextNavNode = function ({ target, event, isBackward = false }) {
target = target || getActualTarget(event);
updateTabbableNodes();

@@ -362,12 +479,12 @@

// make sure the target is actually contained in a group
// NOTE: the target may also be the container itself if it's tabbable
// NOTE: the target may also be the container itself if it's focusable
// with tabIndex='-1' and was given initial focus
const containerIndex = findIndex(state.tabbableGroups, ({ container }) =>
container.contains(target)
);
const containerIndex = findContainerIndex(target, event);
const containerGroup =
containerIndex >= 0 ? state.containerGroups[containerIndex] : undefined;
if (containerIndex < 0) {
// target not found in any group: quite possible focus has escaped the trap,
// so bring it back in to...
if (e.shiftKey) {
// so bring it back into...
if (isBackward) {
// ...the last node in the last group

@@ -381,3 +498,3 @@ destinationNode =

}
} else if (e.shiftKey) {
} else if (isBackward) {
// REVERSE

@@ -393,5 +510,11 @@

startOfGroupIndex < 0 &&
state.tabbableGroups[containerIndex].container === target
(containerGroup.container === target ||
(isFocusable(target, config.tabbableOptions) &&
!isTabbable(target, config.tabbableOptions) &&
!containerGroup.nextTabbableNode(target, false)))
) {
// an exception case where the target is the container itself, in which
// an exception case where the target is either the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle shift+tab as if focus were on the container's

@@ -412,3 +535,11 @@ // first tabbable node, and go to the last tabbable node of the LAST group

const destinationGroup = state.tabbableGroups[destinationGroupIndex];
destinationNode = destinationGroup.lastTabbableNode;
destinationNode =
getTabIndex(target) >= 0
? destinationGroup.lastTabbableNode
: destinationGroup.lastDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target, false);
}

@@ -426,5 +557,11 @@ } else {

lastOfGroupIndex < 0 &&
state.tabbableGroups[containerIndex].container === target
(containerGroup.container === target ||
(isFocusable(target, config.tabbableOptions) &&
!isTabbable(target, config.tabbableOptions) &&
!containerGroup.nextTabbableNode(target)))
) {
// an exception case where the target is the container itself, in which
// an exception case where the target is the container itself, or
// a non-tabbable node that was given focus (i.e. tabindex is negative
// and user clicked on it or node was programmatically given focus)
// and is not followed by any other tabbable node, in which
// case, we should handle tab as if focus were on the container's

@@ -445,6 +582,15 @@ // last tabbable node, and go to the first tabbable node of the FIRST group

const destinationGroup = state.tabbableGroups[destinationGroupIndex];
destinationNode = destinationGroup.firstTabbableNode;
destinationNode =
getTabIndex(target) >= 0
? destinationGroup.firstTabbableNode
: destinationGroup.firstDomTabbableNode;
} else if (!isTabEvent(event)) {
// user must have customized the nav keys so we have to move focus manually _within_
// the active group: do this based on the order determined by tabbable()
destinationNode = containerGroup.nextTabbableNode(target);
}
}
} else {
// no groups available
// NOTE: the fallbackFocus option does not support returning false to opt-out

@@ -454,4 +600,161 @@ destinationNode = getNodeForOption('fallbackFocus');

return destinationNode;
};
// This needs to be done on mousedown and touchstart instead of click
// so that it precedes the focus event.
const checkPointerDown = function (e) {
const target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
// allow the click since it ocurred inside the trap
return;
}
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
// immediately deactivate the trap
trap.deactivate({
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
// which will result in the outside click setting focus to the node
// that was clicked (and if not focusable, to "nothing"); by setting
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
// on activation (or the configured `setReturnFocus` node), whether the
// outside click was on a focusable node or not
returnFocus: config.returnFocusOnDeactivate,
});
return;
}
// This is needed for mobile devices.
// (If we'll only let `click` events through,
// then on mobile they will be blocked anyways if `touchstart` is blocked.)
if (valueOrHandler(config.allowOutsideClick, e)) {
// allow the click outside the trap to take place
return;
}
// otherwise, prevent the click
e.preventDefault();
};
// In case focus escapes the trap for some strange reason, pull it back in.
// NOTE: the focusIn event is NOT cancelable, so if focus escapes, it may cause unexpected
// scrolling if the node that got focused was out of view; there's nothing we can do to
// prevent that from happening by the time we discover that focus escaped
const checkFocusIn = function (event) {
const target = getActualTarget(event);
const targetContained = findContainerIndex(target, event) >= 0;
// In Firefox when you Tab out of an iframe the Document is briefly focused.
if (targetContained || target instanceof Document) {
if (targetContained) {
state.mostRecentlyFocusedNode = target;
}
} else {
// escaped! pull it back in to where it just left
event.stopImmediatePropagation();
// focus will escape if the MRU node had a positive tab index and user tried to nav forward;
// it will also escape if the MRU node had a 0 tab index and user tried to nav backward
// toward a node with a positive tab index
let nextNode; // next node to focus, if we find one
let navAcrossContainers = true;
if (state.mostRecentlyFocusedNode) {
if (getTabIndex(state.mostRecentlyFocusedNode) > 0) {
// MRU container index must be >=0 otherwise we wouldn't have it as an MRU node...
const mruContainerIdx = findContainerIndex(
state.mostRecentlyFocusedNode
);
// there MAY not be any tabbable nodes in the container if there are at least 2 containers
// and the MRU node is focusable but not tabbable (focus-trap requires at least 1 container
// with at least one tabbable node in order to function, so this could be the other container
// with nothing tabbable in it)
const { tabbableNodes } = state.containerGroups[mruContainerIdx];
if (tabbableNodes.length > 0) {
// MRU tab index MAY not be found if the MRU node is focusable but not tabbable
const mruTabIdx = tabbableNodes.findIndex(
(node) => node === state.mostRecentlyFocusedNode
);
if (mruTabIdx >= 0) {
if (config.isKeyForward(state.recentNavEvent)) {
if (mruTabIdx + 1 < tabbableNodes.length) {
nextNode = tabbableNodes[mruTabIdx + 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
} else {
if (mruTabIdx - 1 >= 0) {
nextNode = tabbableNodes[mruTabIdx - 1];
navAcrossContainers = false;
}
// else, don't wrap within the container as focus should move to next/previous
// container
}
// else, don't find in container order without considering direction too
}
}
// else, no tabbable nodes in that container (which means we must have at least one other
// container with at least one tabbable node in it, otherwise focus-trap would've thrown
// an error the last time updateTabbableNodes() was run): find next node among all known
// containers
} else {
// check to see if there's at least one tabbable node with a positive tab index inside
// the trap because focus seems to escape when navigating backward from a tabbable node
// with tabindex=0 when this is the case (instead of wrapping to the tabbable node with
// the greatest positive tab index like it should)
if (
!state.containerGroups.some((g) =>
g.tabbableNodes.some((n) => getTabIndex(n) > 0)
)
) {
// no containers with tabbable nodes with positive tab indexes which means the focus
// escaped for some other reason and we should just execute the fallback to the
// MRU node or initial focus node, if any
navAcrossContainers = false;
}
}
} else {
// no MRU node means we're likely in some initial condition when the trap has just
// been activated and initial focus hasn't been given yet, in which case we should
// fall through to trying to focus the initial focus node, which is what should
// happen below at this point in the logic
navAcrossContainers = false;
}
if (navAcrossContainers) {
nextNode = findNextNavNode({
// move FROM the MRU node, not event-related node (which will be the node that is
// outside the trap causing the focus escape we're trying to fix)
target: state.mostRecentlyFocusedNode,
isBackward: config.isKeyBackward(state.recentNavEvent),
});
}
if (nextNode) {
tryFocus(nextNode);
} else {
tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
}
}
state.recentNavEvent = undefined; // clear
};
// Hijack key nav events on the first and last focusable nodes of the trap,
// in order to prevent focus from escaping. If it escapes for even a
// moment it can end up scrolling the page and causing confusion so we
// kind of need to capture the action at the keydown phase.
const checkKeyNav = function (event, isBackward = false) {
state.recentNavEvent = event;
const destinationNode = findNextNavNode({ event, isBackward });
if (destinationNode) {
e.preventDefault();
if (isTabEvent(event)) {
// since tab natively moves focus, we wouldn't have a destination node unless we
// were on the edge of a container and had to move to the next/previous edge, in
// which case we want to prevent default to keep the browser from moving focus
// to where it normally would
event.preventDefault();
}
tryFocus(destinationNode);

@@ -462,8 +765,8 @@ }

const checkKey = function (e) {
const checkKey = function (event) {
if (
isEscapeEvent(e) &&
valueOrHandler(config.escapeDeactivates, e) !== false
isEscapeEvent(event) &&
valueOrHandler(config.escapeDeactivates, event) !== false
) {
e.preventDefault();
event.preventDefault();
trap.deactivate();

@@ -473,5 +776,4 @@ return;

if (isTabEvent(e)) {
checkTab(e);
return;
if (config.isKeyForward(event) || config.isKeyBackward(event)) {
checkKeyNav(event, config.isKeyBackward(event));
}

@@ -481,9 +783,9 @@ };

const checkClick = function (e) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
const target = getActualTarget(e);
if (findContainerIndex(target, e) >= 0) {
return;
}
const target = getActualTarget(e);
if (containersContain(target)) {
if (valueOrHandler(config.clickOutsideDeactivates, e)) {
return;

@@ -510,3 +812,3 @@ }

// There can be only one listening focus trap at a time
activeFocusTraps.activateTrap(trap);
activeFocusTraps.activateTrap(trapStack, trap);

@@ -557,2 +859,44 @@ // Delay ensures that the focused element doesn't capture the event

//
// MUTATION OBSERVER
//
const checkDomRemoval = function (mutations) {
const isFocusedNodeRemoved = mutations.some(function (mutation) {
const removedNodes = Array.from(mutation.removedNodes);
return removedNodes.some(function (node) {
return node === state.mostRecentlyFocusedNode;
});
});
// If the currently focused is removed then browsers will move focus to the
// <body> element. If this happens, try to move focus back into the trap.
if (isFocusedNodeRemoved) {
tryFocus(getInitialFocusNode());
}
};
// Use MutationObserver - if supported - to detect if focused node is removed
// from the DOM.
const mutationObserver =
typeof window !== 'undefined' && 'MutationObserver' in window
? new MutationObserver(checkDomRemoval)
: undefined;
const updateObservedNodes = function () {
if (!mutationObserver) {
return;
}
mutationObserver.disconnect();
if (state.active && !state.paused) {
state.containers.map(function (container) {
mutationObserver.observe(container, {
subtree: true,
childList: true,
});
});
}
};
//
// TRAP DEFINITION

@@ -562,2 +906,10 @@ //

trap = {
get active() {
return state.active;
},
get paused() {
return state.paused;
},
activate(activateOptions) {

@@ -580,5 +932,3 @@ if (state.active) {

if (onActivate) {
onActivate();
}
onActivate?.();

@@ -590,5 +940,4 @@ const finishActivation = () => {

addListeners();
if (onPostActivate) {
onPostActivate();
}
updateObservedNodes();
onPostActivate?.();
};

@@ -613,2 +962,9 @@

const options = {
onDeactivate: config.onDeactivate,
onPostDeactivate: config.onPostDeactivate,
checkCanReturnFocus: config.checkCanReturnFocus,
...deactivateOptions,
};
clearTimeout(state.delayInitialFocusTimer); // noop if undefined

@@ -620,18 +976,11 @@ state.delayInitialFocusTimer = undefined;

state.paused = false;
updateObservedNodes();
activeFocusTraps.deactivateTrap(trap);
activeFocusTraps.deactivateTrap(trapStack, trap);
const onDeactivate = getOption(deactivateOptions, 'onDeactivate');
const onPostDeactivate = getOption(deactivateOptions, 'onPostDeactivate');
const checkCanReturnFocus = getOption(
deactivateOptions,
'checkCanReturnFocus'
);
if (onDeactivate) {
onDeactivate();
}
const onDeactivate = getOption(options, 'onDeactivate');
const onPostDeactivate = getOption(options, 'onPostDeactivate');
const checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
const returnFocus = getOption(
deactivateOptions,
options,
'returnFocus',

@@ -641,2 +990,4 @@ 'returnFocusOnDeactivate'

onDeactivate?.();
const finishDeactivation = () => {

@@ -647,5 +998,3 @@ delay(() => {

}
if (onPostDeactivate) {
onPostDeactivate();
}
onPostDeactivate?.();
});

@@ -665,3 +1014,3 @@ };

pause() {
pause(pauseOptions) {
if (state.paused || !state.active) {

@@ -671,9 +1020,16 @@ return this;

const onPause = getOption(pauseOptions, 'onPause');
const onPostPause = getOption(pauseOptions, 'onPostPause');
state.paused = true;
onPause?.();
removeListeners();
updateObservedNodes();
onPostPause?.();
return this;
},
unpause() {
unpause(unpauseOptions) {
if (!state.paused || !state.active) {

@@ -683,6 +1039,13 @@ return this;

const onUnpause = getOption(unpauseOptions, 'onUnpause');
const onPostUnpause = getOption(unpauseOptions, 'onPostUnpause');
state.paused = false;
onUnpause?.();
updateTabbableNodes();
addListeners();
updateObservedNodes();
onPostUnpause?.();
return this;

@@ -702,2 +1065,4 @@ },

updateObservedNodes();
return this;

@@ -704,0 +1069,0 @@ },

{
"name": "focus-trap",
"version": "6.7.1",
"version": "7.5.2",
"description": "Trap focus within a DOM node.",

@@ -20,3 +20,3 @@ "main": "dist/focus-trap.js",

"scripts": {
"demo-bundle": "yarn compile:demo",
"demo-bundle": "npm run compile:demo",
"format": "prettier --write \"{*,src/**/*,test/**/*,docs/js/**/*,.github/workflows/*,cypress/**/*}.+(js|yml)\"",

@@ -31,14 +31,15 @@ "format:check": "prettier --check \"{*,src/**/*,test/**/*,docs/js/**/*,.github/workflows/*,cypress/**/*}.+(js|yml)\"",

"compile:demo": "cross-env BUILD_ENV=demo BABEL_ENV=es5 rollup -c",
"compile": "yarn compile:esm && yarn compile:cjs && yarn compile:umd",
"build": "yarn clean && yarn compile",
"start": "yarn compile:demo --watch --environment SERVE,RELOAD,IS_CYPRESS_ENV:''",
"start:cypress": "yarn compile:demo --environment SERVE,IS_CYPRESS_ENV:\"$CYPRESS_BROWSER\"",
"compile": "npm run compile:esm && npm run compile:cjs && npm run compile:umd",
"build": "npm run clean && npm run compile",
"start": "npm run compile:demo -- --watch --environment SERVE,RELOAD",
"start:cypress": "npm run compile:demo -- --environment SERVE,IS_CYPRESS_ENV:\"$CYPRESS_BROWSER\"",
"test:types": "tsc index.d.ts",
"test:unit": "echo \"No unit tests to run!\"",
"test:cypress": "CYPRESS_BROWSER=ANY start-server-and-test start:cypress 9966 'cypress open'",
"test:cypress:ci": "start-server-and-test start:cypress 9966 'cypress run --browser $CYPRESS_BROWSER --headless'",
"test:chrome": "CYPRESS_BROWSER=chrome yarn test:cypress:ci",
"test": "yarn format:check && yarn lint && yarn test:unit && yarn test:types && CYPRESS_BROWSER=chrome yarn test:cypress:ci",
"prepare": "yarn build",
"release": "yarn build && changeset publish"
"test:e2e": "ELECTRON_ENABLE_LOGGING=1 start-server-and-test start:cypress 9966 'cypress run --browser $CYPRESS_BROWSER --headless'",
"test:e2e:chrome": "CYPRESS_BROWSER=chrome npm run test:e2e",
"test:e2e:dev": "ELECTRON_ENABLE_LOGGING=1 start-server-and-test start:cypress 9966 'cypress open'",
"test": "npm run format:check && npm run lint && npm run test:unit && npm run test:types && npm run test:e2e:chrome",
"prepare": "npm run build",
"prepublishOnly": "npm run test && npm run build",
"release": "npm run build && changeset publish"
},

@@ -67,34 +68,35 @@ "repository": {

"dependencies": {
"tabbable": "^5.2.1"
"tabbable": "^6.2.0"
},
"devDependencies": {
"@babel/cli": "^7.15.7",
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@changesets/cli": "^2.17.0",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@testing-library/cypress": "^8.0.1",
"@types/jquery": "^3.5.6",
"all-contributors-cli": "^6.20.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"@babel/cli": "^7.22.5",
"@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@changesets/cli": "^2.26.2",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-node-resolve": "^15.1.0",
"@rollup/plugin-terser": "^0.4.3",
"@testing-library/cypress": "^9.0.0",
"@types/jquery": "^3.5.16",
"all-contributors-cli": "^6.26.0",
"babel-loader": "^9.1.2",
"cross-env": "^7.0.3",
"cypress": "^8.4.1",
"cypress": "^12.16.0",
"cypress-plugin-tab": "^1.0.5",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint": "^8.43.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-cypress": "^2.13.3",
"eslint-plugin-jest": "^27.2.2",
"onchange": "^7.1.0",
"prettier": "^2.4.1",
"rollup": "^2.57.0",
"prettier": "^2.8.8",
"rollup": "^2.79.1",
"rollup-plugin-inject-process-env": "^1.3.1",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^1.1.0",
"rollup-plugin-serve": "^2.0.2",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.1",
"start-server-and-test": "^1.14.0",
"typescript": "^4.4.3"
"start-server-and-test": "^2.0.0",
"typescript": "^5.1.5"
}
}
# focus-trap [![CI](https://github.com/focus-trap/focus-trap/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/focus-trap/focus-trap/actions?query=workflow:CI+branch:master) [![license](https://badgen.now.sh/badge/license/MIT)](./LICENSE)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-19-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-29-orange.svg?style=flat-square)](#contributors)
<!-- ALL-CONTRIBUTORS-BADGE:END -->

@@ -37,3 +37,3 @@

```
```bash
npm install focus-trap

@@ -46,3 +46,3 @@ ```

> NOTE: The UMD build does not bundle the `tabbable` dependency. Therefore you will have to also include that one, and include it _before_ `focus-trap`.
> NOTE: The UMD build does not bundle the `tabbable` dependency. Therefore you will have to also include that one, and include it *before* `focus-trap`.

@@ -58,11 +58,19 @@ ```html

IE9+
As old and as broad as _reasonably_ possible, excluding browsers that are out of support or have nearly no user base.
Why?
Because this module uses [`EventTarget.addEventListener()`](document.createElement('button')).
And its only dependency, tabbable, uses [a couple of IE9+ functions](https://github.com/focus-trap/tabbable#browser-support).
Focused on desktop browsers, particularly Chrome, Edge, FireFox, Safari, and Opera.
Focus-trap is not officially tested on any mobile browsers or devices.
> ❗️ __Safari__: By default, Safari does not tab through all elements on a page, which alters the normal DOM-based tab order expected by focus-trap. If you use or support Safari with this library, make sure you and your users know they __must enable__ the `Preferences > Advanced > Press Tab to highlight each item on a webpage` feature. Otherwise, your traps [will not work the way you expect them to](https://github.com/focus-trap/focus-trap/issues/783).
> ⚠️ Microsoft [no longer supports](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/) any version of IE, so IE is no longer supported by this library.
> 💬 Focus-trap relies on tabbable so its browser support is at least [what tabbable supports](https://github.com/focus-trap/tabbable#browser-support).
> 💬 Keep in mind that performance optimization and old browser support are often at odds, so tabbable may not always be able to use the most optimal (typically modern) APIs in all cases.
## Usage
### createFocusTrap(element[, createOptions])
### createFocusTrap()

@@ -79,3 +87,4 @@ ```javascript

`element` can be
`element` can be:
- a DOM node (the focus trap itself);

@@ -85,3 +94,3 @@ - a selector string (which will be passed to `document.querySelector()` to find the DOM node); or

> A focus trap must have at least one container with at least one tabbable/focusable node in it to be considered valid. While nodes can be added/removed at runtime, with the trap adjusting to added/removed tabbable nodes, __an error will be thrown__ if the trap ever gets into a state where it determines none of its containers have any tabbable nodes in them _and_ the `fallbackFocus` option does not resolve to an alternate node where focus can go.
> A focus trap must have at least one container with at least one tabbable/focusable node in it to be considered valid. While nodes can be added/removed at runtime, with the trap adjusting to added/removed tabbable nodes, **an error will be thrown** if the trap ever gets into a state where it determines none of its containers have any tabbable nodes in them *and* the `fallbackFocus` option does not resolve to an alternate node where focus can go.

@@ -92,34 +101,87 @@ #### createOptions

- **onPostActivate** `{() => void}`: A function that will be called **after** sending focus to the target element upon activation.
- **checkCanFocusTrap** `{(containers: Array<HTMLElement | SVGElement>) => Promise<void>}`: 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). Due to the lack of Promise support, `checkCanFocusTrap` is not supported in IE unless you provide a Promise polyfill.
- **onPause** `{() => void}`: A function that will be called immediately after the trap's state is updated to be paused.
- **onPostPause** `{() => void}`: A function that will be called after the trap has been completely paused and is no longer managing/trapping focus.
- **onUnpause** `{() => void}`: 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.
- **onPostUnpause** `{() => void}`: A function that will be called after the trap has been completely unpaused and is once again managing/trapping focus.
- **checkCanFocusTrap** `{(containers: Array<HTMLElement | SVGElement>) => Promise<void>}`: 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).
- **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.
- **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.
- **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). Due to the lack of Promise support, `checkCanReturnFocus` is not supported in IE unless you provide a Promise polyfill.
- **initialFocus** `{HTMLElement | SVGElement | string | false | (() => HTMLElement | SVGElement | false)}`: 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 a DOM node. 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.
- 💬 Setting this option to `false` (or a function that returns `false`) will prevent the `fallbackFocus` option from being used.
- ⚠️ See warning below about **Shadow DOM** and selector strings.
- **fallbackFocus** `{HTMLElement | SVGElement | string | () => HTMLElement | SVGElement}`: 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 a DOM node.
- 💬 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.
- ⚠️ See warning below about **Shadow DOM** and selector strings.
- **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).
- **initialFocus** `{HTMLElement | SVGElement | string | false | (() => HTMLElement | SVGElement | string | false)}`: 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.
- 💬 Setting this option to `false` (or a function that returns `false`) will prevent the `fallbackFocus` option from being used.
- ⚠️ See warning below about **Shadow DOM** and selector strings.
- **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.
- 💬 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.
- ⚠️ See warning below about **Shadow DOM** and selector strings.
- **escapeDeactivates** `{boolean} | (e: KeyboardEvent) => boolean)`: Default: `true`. If `false` or returns `false`, the `Escape` key will not trigger deactivation of the focus trap. This can be useful if you want to force the user to make a decision instead of allowing an easy way out. Note that if a function is given, it's only called if the ESC key was pressed.
- **clickOutsideDeactivates** `{boolean | (e: MouseEvent | TouchEvent) => boolean}`: If `true` or returns `true`, a click outside the focus trap will deactivate the focus trap and allow the click event to do its thing (i.e. to pass-through to the element that was clicked). This option **takes precedence** over `allowOutsideClick` when it's set to `true`. Default: `false`.
- ⚠️ If you're using a password manager such as 1Password, where the app adds a clickable icon to all fillable fields, you should avoid using this option, and instead use the `allowOutsideClick` option to better control exactly when the focus trap can be deactivated. The clickable icons are usually positioned absolutely, floating on top of the fields, and therefore _not_ part of the container the trap is managing. When using the `clickOutsideDeactivates` option, clicking on a field's 1Password icon will likely cause the trap to be unintentionally deactivated.
- **allowOutsideClick** `{boolean | (e: MouseEvent | TouchEvent) => boolean}`: If set and is or returns `true`, a click outside the focus trap will not be prevented, even when `clickOutsideDeactivates` is `false`. When `clickOutsideDeactivates` is `true`, this option is **ignored** (i.e. if it's a function, it will not be called). Use this option to control if (and even which) clicks are allowed outside the trap in conjunction with `clickOutsideDeactivates: false`. Default: `false`.
- ⚠️ If this is a function, it will be called **twice** on every click: First on `mousedown` (or `touchstart` on mobile), and then on the actual `click` if the function returned `true` on the first event. Be sure to check the event type if the double call is an issue in your code.
- **clickOutsideDeactivates** `{boolean | (e: MouseEvent | TouchEvent) => boolean}`: If `true` or returns `true`, a click outside the focus trap will immediately deactivate the focus trap and allow the click event to do its thing (i.e. to pass-through to the element that was clicked). This option **takes precedence** over `allowOutsideClick` when it's set to `true`. Default: `false`.
- 💬 If a function is provided, it will be called up to **twice** (but only if the click occurs *outside* the trap's containers): First on the `mousedown` (or `touchstart` on mobile) event and, if `true` was returned, again on the `click` event. It will get the same node each time, and it's recommended that the returned value is also the same each time. Be sure to check the event type if the double call is an issue in your code.
- ⚠️ If you're using a password manager such as 1Password, where the app adds a clickable icon to all fillable fields, you should avoid using this option, and instead use the `allowOutsideClick` option to better control exactly when the focus trap can be deactivated. The clickable icons are usually positioned absolutely, floating on top of the fields, and therefore *not* part of the container the trap is managing. When using the `clickOutsideDeactivates` option, clicking on a field's 1Password icon will likely cause the trap to be unintentionally deactivated.
- **allowOutsideClick** `{boolean | (e: MouseEvent | TouchEvent) => boolean}`: If set and is or returns `true`, a click outside the focus trap will not be prevented (letting focus temporarily escape the trap, without deactivating it), even if `clickOutsideDeactivates=false`. Default: `false`.
- 💬 If this is a function, it will be called up to **twice** on every click (but only if the click occurs *outside* the trap's containers): First on `mousedown` (or `touchstart` on mobile), and then on the actual `click` if the function returned `true` on the first event. Be sure to check the event type if the double call is an issue in your code.
- 💡 When `clickOutsideDeactivates=true`, this option is **ignored** (i.e. if it's a function, it will not be called).
- Use this option to control if (and even which) clicks are allowed outside the trap in conjunction with `clickOutsideDeactivates=false`.
- **returnFocusOnDeactivate** `{boolean}`: Default: `true`. If `false`, when the trap is deactivated, focus will *not* return to the element that had focus before activation.
- **setReturnFocus** `{HTMLElement | SVGElement | string | (previousActiveElement: HTMLElement | SVGElement) => HTMLElement | SVGElement | false}`: By default, on **deactivation**, if `returnFocusOnDeactivate=true` (or if `returnFocus=true` in the [deactivation options](#trapdeactivatedeactivateoptions)), focus will be returned to the element that was focused just before activation. With this option, you can specify another element to programmatically receive focus after deactivation. It can be a DOM node, a selector string (which will be passed to `document.querySelector()` to find the DOM node **upon deactivation**), or a function that returns a DOM node to call **upon deactivation** (i.e. the selector and function options are only executed at the time the trap is deactivated), or `false` to leave focus where it is at the time of deactivation.
- 💬 Using the selector or function options is a good way to return focus to a DOM node that may not even exist at the time the trap is activated.
- ⚠️ See warning below about **Shadow DOM** and selector strings.
- 💬 When using this option in conjunction with `clickOutsideDeactivates=true`:
- If `returnFocusOnDeactivate=true` and the outside click causing deactivation is on a focusable element, focus will __not__ return to that element; instead, it will return to the node focused just before activation.
- If `returnFocusOnDeactivate=false` and the outside click is on a focusable node, focus will __remain__ on that node instead of the node focused just before activation. If the outside click is on a non-focusable node, then "nothing" will have focus post-deactivation.
- **setReturnFocus** `{HTMLElement | SVGElement | string | (previousActiveElement: HTMLElement | SVGElement) => HTMLElement | SVGElement | string | false}`: By default, on **deactivation**, if `returnFocusOnDeactivate=true` (or if `returnFocus=true` in the [deactivation options](#trapdeactivate)), focus will be returned to the element that was focused just before activation. With this option, you can specify another element to programmatically receive focus after deactivation. It can be a DOM node, a selector string (which will be passed to `document.querySelector()` to find the DOM node **upon deactivation**), or a function that returns any of these to call **upon deactivation** (i.e. the selector and function options are only executed at the time the trap is deactivated). Can also be `false` (or return `false`) to leave focus where it is at the time of deactivation.
- 💬 Using the selector or function options is a good way to return focus to a DOM node that may not exist at the time the trap is activated.
- ⚠️ See warning below about **Shadow DOM** and selector strings.
- **preventScroll** `{boolean}`: By default, focus() will scroll to the element if not in viewport. It can produce unintended effects like scrolling back to the top of a modal. If set to `true`, no scroll will happen.
- **delayInitialFocus** `{boolean}`: 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.
- **document** {Document}: Default: `window.document`. Document where the focus trap will be active. This allows to use FocusTrap in an iFrame context.
- **document** {Document}: Default: `window.document`. Document where the focus trap will be active. This enables the use of FocusTrap [inside an iFrame](https://focus-trap.github.io/focus-trap/#demo-in-iframe).
- ⚠️ Note that FocusTrap will be unable to trap focus outside the iFrame if you configure this option to be the iFrame's document. It will only trap focus _inside_ of it (as the demo shows). If you want to trap focus _outside_ as well, then your FocusTrap must be configured on an element that [contains the iFrame](https://focus-trap.github.io/focus-trap/#demo-iframe).
- **tabbableOptions**: (optional) [tabbable options](https://github.com/focus-trap/tabbable#common-options) configurable on FocusTrap (all the *common options*).
- ⚠️ See notes about **[testing in JSDom](#testing-in-jsdom)** (e.g. using Jest).
- **trapStack** (optional) `{Array<FocusTrap>}`: Define the global trap stack. This makes it possible to share the same stack in multiple instances of `focus-trap` in the same page such that auto-activation/pausing of traps is properly coordinated among all instances as activating a trap when another is already active should result in the other being auto-paused. By default, each instance will have its own internal stack, leading to conflicts if they each try to trap the focus at the same time.
- **isKeyForward** `{(event: KeyboardEvent) => boolean}`: (optional) Determines if the given keyboard event is a "tab forward" event that will move the focus to the next trapped element in tab order. Defaults to the `TAB` key. Use this to override the trap's behavior if you want to use arrow keys to control keyboard navigation within the trap, for example. Also see `isKeyBackward()` option.
- ⚠️ Using this option will not automatically prevent use of the `TAB` key as the browser will continue to respond to it by moving focus forward because that's what using the `TAB` key does in a browser, but it will no longer respect the trap's container edges as it normally would. You will need to add your own `keydown` handler to call `preventDefault()` on a `TAB` key event if you want to completely suppress the use of the `TAB` key.
- **isKeyBackward** `{(event: KeyboardEvent) => boolean}`: (optional) Determines if the given keyboard event is a "tab backward" event that will move the focus to the previous trapped element in tab order. Defaults to the `SHIFT+TAB` key. Use this to override the trap's behavior if you want to use arrow keys to control keyboard navigation within the trap, for example. Also see `isKeyForward()` option.
- ⚠️ Using this option will not automatically prevent use of the `SHIFT+TAB` key as the browser will continue to respond to it by moving focus backward because that's what using the `SHIFT+TAB` key sequence does in a browser, but it will no longer respect the trap's container edges as it normally would. You will need to add your own `keydown` handler to call `preventDefault()` on a `TAB` key event if you want to completely suppress the use of the `SHIFT+TAB` key sequence.
#### Shadow DOM and selector strings
#### Shadow DOM
⚠️ Beware that putting a focus-trap **inside** an open Shadow DOM means you must either:
##### Selector strings
- **Not use selector strings** for options that support these (because nodes inside Shadow DOMs, even open shadows, are not visible via `document.querySelector()`); OR
- You must **use the `document` option** to configure the focus trap to use your *shadow host* element as its document. The downside of this option is that, while selector queries on nodes inside your trap will now work, the trap will not prevent focus from being set on nodes outside your Shadow DOM, which is the same drawback as putting a focus trap <a href="https://focus-trap.github.io/focus-trap/#demo-in-iframe">inside an iframe</a>.
⚠️ Beware that putting a focus-trap **inside** an open Shadow DOM means you must **not use selector strings** for options that support these (because nodes inside Shadow DOMs, even open shadows, are not visible via `document.querySelector()`).
### trap.activate([activateOptions])
##### Closed shadows
If you have closed shadow roots that you would like considered for tabbable/focusable nodes, use the `tabbableOptions.getShadowRoot` option to provide Tabbable (used internally) with a reference to a given node's shadow root so that it can be searched for candidates.
#### Positive Tabindexes
⚠️ Using positive tab indexes (i.e. `<button tabindex="1">Label</button>`) [is not recommended](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex#accessibility_concerns), primarily for accessibility reasons. Supporting them properly also means a lot of hoops to jump through when Shadow DOM is used as some key DOM APIs like [Node.compareDocumentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition) [do not](https://github.com/whatwg/dom/issues/320) properly support Shadow DOM.
As such, focus-trap considers using positive tabindexes an edge case and only supports them in __single-container__ traps with some caveats for related edge case behavior (see the [demo](https://focus-trap.github.io/focus-trap/#demo-positive-tabindex) for more details).
If you try to create a multi-container trap where at least one container has one node with a positive tabindex, an exception will be thrown:
```
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.
```
### trap.active
```typescript
trap.active: boolean
```
True if the trap is currently active.
### trap.paused
```typescript
trap.paused: boolean
```
True if the trap is currently paused.
### trap.activate()
```typescript
trap.activate([activateOptions]) => FocusTrap
```
Activates the focus trap, adding various event listeners to the document.

@@ -145,4 +207,8 @@

### trap.deactivate([deactivateOptions])
### trap.deactivate()
```typescript
trap.deactivate([deactivateOptions]) => FocusTrap
```
Deactivates the focus trap.

@@ -156,9 +222,13 @@

- **returnFocus** `{boolean}`: Default: whatever you chose for `createOptions.returnFocusOnDeactivate`. If `true`, then the `setReturnFocus` option (specified when the trap was created) is used to determine where focus will be returned.
- **onDeactivate** `{() => void}`: Default: whatever you chose for `createOptions.onDeactivate`. `null` or `false` are the equivalent of a `noop`.
- **onPostDeactivate** `{() => void}`: Default: whatever you chose for `createOptions.onPostDeactivate`. `null` or `false` are the equivalent of a `noop`.
- **checkCanReturnFocus** `{(trigger: HTMLElement | SVGElement) => Promise<void>}`: Default: whatever you chose 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.
- **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.
- **onDeactivate** `{() => void}`: Default: whatever you set for `createOptions.onDeactivate`. `null` or `false` are the equivalent of a `noop`.
- **onPostDeactivate** `{() => void}`: Default: whatever you set for `createOptions.onPostDeactivate`. `null` or `false` are the equivalent of a `noop`.
- **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.
### trap.pause()
```typescript
trap.pause([pauseOptions]) => FocusTrap
```
Pause an active focus trap's event listening without deactivating the trap.

@@ -174,4 +244,15 @@

`pauseOptions`:
These options are used to override the focus trap's default behavior for this particular pausing.
- **onPause** `{() => void}`: Default: whatever you chose for `createOptions.onPause`. `null` or `false` are the equivalent of a `noop`.
- **onPostPause** `{() => void}`: Default: whatever you chose for `createOptions.onPostPause`. `null` or `false` are the equivalent of a `noop`.
### trap.unpause()
```typescript
trap.unpause([unpauseOptions]) => FocusTrap
```
Unpause an active focus trap. (See `pause()`, above.)

@@ -185,7 +266,18 @@

`unpauseOptions`:
These options are used to override the focus trap's default behavior for this particular unpausing.
- **onUnpause** `{() => void}`: Default: whatever you chose for `createOptions.onUnpause`. `null` or `false` are the equivalent of a `noop`.
- **onPostUnpause** `{() => void}`: Default: whatever you chose for `createOptions.onPostUnpause`. `null` or `false` are the equivalent of a `noop`.
### trap.updateContainerElements()
```typescript
trap.updateContainerElements(HTMLElement | SVGElement | string | Array<HTMLElement | SVGElement | string>) => FocusTrap
```
Update the element(s) that are used as containers for the focus trap.
When you call the function `createFocusTrap`, you pass in an element (or selector), or an array of elements (or selectors) to keep the focus within. This method simply allows you to update which elements to keep the focus within.
When you call `createFocusTrap()`, you give it an element (or selector), or an array of elements (or selectors) to keep the focus within. This method simply allows you to update which elements to keep the focus within even while the trap is active.

@@ -246,2 +338,24 @@ A use case for this is found in focus-trap-react, where React `ref`'s may not be initialized yet, but when they are you want to have them be a container element.

## Help
### Testing in JSDom
> ⚠️ JSDom is not officially supported. Your mileage may vary, and tests may break from one release to the next (even a patch or minor release).
>
> This topic is just here to help with what we know may affect your tests.
In general, a focus trap is best tested in a full browser environment such as Cypress, Playwright, or Nightwatch where a full DOM is available.
Sometimes, that's not entirely desirable, and depending on what you're testing, you may be able to get away with using JSDom (e.g. via Jest), but you'll have to configure your traps using the `tabbableOptions.displayCheck: 'none'` option.
See [Testing tabbable in JSDom](https://github.com/focus-trap/tabbable#testing-in-jsdom) for more details.
### ERROR: Your focus-trap must have at least one container with at least one tabbable node in it at all times
This error happens when the containers you specified when you [setup](#createfocustrap) your focus trap do not have -- or no longer have -- any tabbable elements in them, which means that focus will inevitably escape your trap because focus __must__ always go _somewhere_.
You will hit this error if your trap does not have (or no longer has) any [tabbable](https://github.com/focus-trap/tabbable#readme) (and therefore focusable) elements in it, and it was not configured with a backup element (see the `fallbackFocus` [option](#createoptions) -- which must still be in the trap, but does not necessarily have to be tabbable, i.e. it could have `tabindex="-1"`, making it focusable, but not tabbable).
This often happens when traps are related to elements that appear and disappear dynamically. Typically, the error will fire either as the element is being shown (because the trap gets created before the trapped children have been inserted into the DOM), or as it's being hidden (because the trapped children are destroyed before the trap is either destroyed or disabled).
# Contributing

@@ -259,27 +373,43 @@

<table>
<tr>
<td align="center"><a href="https://github.com/andersthorsen"><img src="https://avatars.githubusercontent.com/u/190081?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anders Thorsen</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Aandersthorsen" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/bparish628"><img src="https://avatars1.githubusercontent.com/u/8492971?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benjamin Parish</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Abparish628" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://clintgoodman.com"><img src="https://avatars3.githubusercontent.com/u/5473697?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clint Goodman</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Documentation">📖</a> <a href="#example-cgood92" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/Dan503"><img src="https://avatars.githubusercontent.com/u/10610368?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Tonon</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=Dan503" title="Documentation">📖</a> <a href="#tool-Dan503" title="Tools">🔧</a> <a href="#a11y-Dan503" title="Accessibility">️️️️♿️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=Dan503" title="Code">💻</a></td>
<td align="center"><a href="http://davidtheclark.com/"><img src="https://avatars2.githubusercontent.com/u/628431?v=4?s=100" width="100px;" alt=""/><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>
<td align="center"><a href="https://github.com/features/security"><img src="https://avatars1.githubusercontent.com/u/27347476?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dependabot</b></sub></a><br /><a href="#maintenance-dependabot" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/michael-ar"><img src="https://avatars3.githubusercontent.com/u/18557997?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Reynolds</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Amichael-ar" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/liunate"><img src="https://avatars2.githubusercontent.com/u/38996291?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nate Liu</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=liunate" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/far-fetched"><img src="https://avatars.githubusercontent.com/u/11621383?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Panek</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Afar-fetched" title="Bug reports">🐛</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/randypuro"><img src="https://avatars2.githubusercontent.com/u/2579?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Randy Puro</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Arandypuro" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/sadick254"><img src="https://avatars2.githubusercontent.com/u/5238135?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sadick</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Documentation">📖</a></td>
<td align="center"><a href="https://scottblinch.me/"><img src="https://avatars2.githubusercontent.com/u/4682114?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Scott Blinch</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=scottblinch" title="Documentation">📖</a></td>
<td align="center"><a href="https://seanmcp.com/"><img src="https://avatars1.githubusercontent.com/u/6360367?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sean McPherson</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Documentation">📖</a></td>
<td align="center"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3ASlapbox" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://stefancameron.com/"><img src="https://avatars3.githubusercontent.com/u/2855350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Cameron</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Astefcameron" title="Bug reports">🐛</a> <a href="#infra-stefcameron" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Documentation">📖</a> <a href="#maintenance-stefcameron" title="Maintenance">🚧</a></td>
<td align="center"><a href="http://tylerhawkins.info/201R/"><img src="https://avatars0.githubusercontent.com/u/13806458?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tyler Hawkins</b></sub></a><br /><a href="#tool-thawkin3" title="Tools">🔧</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=thawkin3" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=thawkin3" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/wandroll"><img src="https://avatars.githubusercontent.com/u/4492317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wandrille Verlut</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Documentation">📖</a> <a href="#tool-wandroll" title="Tools">🔧</a></td>
<td align="center"><a href="http://willmruzek.com/"><img src="https://avatars.githubusercontent.com/u/108522?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Will Mruzek</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Documentation">📖</a> <a href="#example-mruzekw" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Tests">⚠️</a> <a href="#question-mruzekw" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://github.com/zioth"><img src="https://avatars3.githubusercontent.com/u/945603?v=4?s=100" width="100px;" alt=""/><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>
</tr>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andersthorsen"><img src="https://avatars.githubusercontent.com/u/190081?v=4?s=100" width="100px;" alt="Anders Thorsen"/><br /><sub><b>Anders Thorsen</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Aandersthorsen" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bparish628"><img src="https://avatars1.githubusercontent.com/u/8492971?v=4?s=100" width="100px;" alt="Benjamin Parish"/><br /><sub><b>Benjamin Parish</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Abparish628" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://clintgoodman.com"><img src="https://avatars3.githubusercontent.com/u/5473697?v=4?s=100" width="100px;" alt="Clint Goodman"/><br /><sub><b>Clint Goodman</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Documentation">📖</a> <a href="#example-cgood92" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Dan503"><img src="https://avatars.githubusercontent.com/u/10610368?v=4?s=100" width="100px;" alt="Daniel Tonon"/><br /><sub><b>Daniel Tonon</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=Dan503" title="Documentation">📖</a> <a href="#tool-Dan503" title="Tools">🔧</a> <a href="#a11y-Dan503" title="Accessibility">️️️️♿️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=Dan503" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DaviDevMod"><img src="https://avatars.githubusercontent.com/u/98312056?v=4?s=100" width="100px;" alt="DaviDevMod"/><br /><sub><b>DaviDevMod</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=DaviDevMod" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=DaviDevMod" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3ADaviDevMod" title="Bug reports">🐛</a></td>
<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>
<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>
</tr>
<tr>
<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>
<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>
<td align="center" valign="top" width="14.28%"><a href="http://reload.dk"><img src="https://avatars.githubusercontent.com/u/73966?v=4?s=100" width="100px;" alt="Kasper Garnæs"/><br /><sub><b>Kasper Garnæs</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=kasperg" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Akasperg" title="Bug reports">🐛</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=kasperg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://blogs.esri.com/esri/arcgis/"><img src="https://avatars.githubusercontent.com/u/1231455?v=4?s=100" width="100px;" alt="Matt Driscoll"/><br /><sub><b>Matt Driscoll</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Adriskull" title="Bug reports">🐛</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=driskull" title="Code">💻</a> <a href="#tutorial-driskull" title="Tutorials">✅</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/msev"><img src="https://avatars.githubusercontent.com/u/1529562?v=4?s=100" width="100px;" alt="Maxime"/><br /><sub><b>Maxime</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Amsev" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/michael-ar"><img src="https://avatars3.githubusercontent.com/u/18557997?v=4?s=100" width="100px;" alt="Michael Reynolds"/><br /><sub><b>Michael Reynolds</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Amichael-ar" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/liunate"><img src="https://avatars2.githubusercontent.com/u/38996291?v=4?s=100" width="100px;" alt="Nate Liu"/><br /><sub><b>Nate Liu</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=liunate" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/far-fetched"><img src="https://avatars.githubusercontent.com/u/11621383?v=4?s=100" width="100px;" alt="Piotr Panek"/><br /><sub><b>Piotr Panek</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Afar-fetched" title="Bug reports">🐛</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/randypuro"><img src="https://avatars2.githubusercontent.com/u/2579?v=4?s=100" width="100px;" alt="Randy Puro"/><br /><sub><b>Randy Puro</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Arandypuro" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sadick254"><img src="https://avatars2.githubusercontent.com/u/5238135?v=4?s=100" width="100px;" alt="Sadick"/><br /><sub><b>Sadick</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://scottblinch.me/"><img src="https://avatars2.githubusercontent.com/u/4682114?v=4?s=100" width="100px;" alt="Scott Blinch"/><br /><sub><b>Scott Blinch</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=scottblinch" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://seanmcp.com/"><img src="https://avatars1.githubusercontent.com/u/6360367?v=4?s=100" width="100px;" alt="Sean McPherson"/><br /><sub><b>Sean McPherson</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/skriems"><img src="https://avatars.githubusercontent.com/u/15573317?v=4?s=100" width="100px;" alt="Sebastian Kriems"/><br /><sub><b>Sebastian Kriems</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Askriems" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4?s=100" width="100px;" alt="Slapbox"/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3ASlapbox" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://stefancameron.com/"><img src="https://avatars3.githubusercontent.com/u/2855350?v=4?s=100" width="100px;" alt="Stefan Cameron"/><br /><sub><b>Stefan Cameron</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Astefcameron" title="Bug reports">🐛</a> <a href="#infra-stefcameron" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Documentation">📖</a> <a href="#maintenance-stefcameron" title="Maintenance">🚧</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://tylerhawkins.info/201R/"><img src="https://avatars0.githubusercontent.com/u/13806458?v=4?s=100" width="100px;" alt="Tyler Hawkins"/><br /><sub><b>Tyler Hawkins</b></sub></a><br /><a href="#tool-thawkin3" title="Tools">🔧</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=thawkin3" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=thawkin3" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vasiliki-b"><img src="https://avatars.githubusercontent.com/u/98032598?v=4?s=100" width="100px;" alt="Vasiliki Boutas"/><br /><sub><b>Vasiliki Boutas</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Avasiliki-b" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://vinicius73.dev/"><img src="https://avatars.githubusercontent.com/u/1561347?v=4?s=100" width="100px;" alt="Vinicius Reis"/><br /><sub><b>Vinicius Reis</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=vinicius73" title="Code">💻</a> <a href="#ideas-vinicius73" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wandroll"><img src="https://avatars.githubusercontent.com/u/4492317?v=4?s=100" width="100px;" alt="Wandrille Verlut"/><br /><sub><b>Wandrille Verlut</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Documentation">📖</a> <a href="#tool-wandroll" title="Tools">🔧</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://willmruzek.com/"><img src="https://avatars.githubusercontent.com/u/108522?v=4?s=100" width="100px;" alt="Will Mruzek"/><br /><sub><b>Will Mruzek</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Documentation">📖</a> <a href="#example-mruzekw" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Tests">⚠️</a> <a href="#question-mruzekw" title="Answering Questions">💬</a></td>
<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>
</tr>
<tr>
<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>
</tr>
</tbody>
</table>

@@ -286,0 +416,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc