@react-aria/focus
Version:
Spectrum UI components in React
917 lines (893 loc) • 52.4 kB
JavaScript
var $aB6Cp$reactariautils = require("@react-aria/utils");
var $aB6Cp$react = require("react");
var $aB6Cp$reactariainteractions = require("@react-aria/interactions");
var $aB6Cp$clsx = require("clsx");
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
function $parcel$interopDefault(a) {
return a && a.__esModule ? a.default : a;
}
$parcel$export(module.exports, "FocusScope", () => $a7a032acae3ddda9$export$20e40289641fbbb6);
$parcel$export(module.exports, "useFocusManager", () => $a7a032acae3ddda9$export$10c5169755ce7bd7);
$parcel$export(module.exports, "getFocusableTreeWalker", () => $a7a032acae3ddda9$export$2d6ec8fc375ceafa);
$parcel$export(module.exports, "createFocusManager", () => $a7a032acae3ddda9$export$c5251b9e124bf29);
$parcel$export(module.exports, "isElementInChildOfActiveScope", () => $a7a032acae3ddda9$export$1258395f99bf9cbf);
$parcel$export(module.exports, "FocusRing", () => $dfd8c70b928eb1b3$export$1a38b4ad7f578e1d);
$parcel$export(module.exports, "FocusableProvider", () => $fb504d83237fd6ac$export$13f3202a3e5ddd5);
$parcel$export(module.exports, "useFocusable", () => $fb504d83237fd6ac$export$4c014de7c8940b4c);
$parcel$export(module.exports, "useFocusRing", () => $581a96d6eb128c1b$export$4e328f61c538687f);
$parcel$export(module.exports, "focusSafely", () => $1c7f9157d722357d$export$80f3e147d781571c);
$parcel$export(module.exports, "useHasTabbableChild", () => $259c6413a286f2e6$export$eac1895992b9f3d6);
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/ /*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/ /*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
function $1c7f9157d722357d$export$80f3e147d781571c(element) {
// If the user is interacting with a virtual cursor, e.g. screen reader, then
// wait until after any animated transitions that are currently occurring on
// the page before shifting focus. This avoids issues with VoiceOver on iOS
// causing the page to scroll when moving focus if the element is transitioning
// from off the screen.
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(element);
if ((0, $aB6Cp$reactariainteractions.getInteractionModality)() === "virtual") {
let lastFocusedElement = ownerDocument.activeElement;
(0, $aB6Cp$reactariautils.runAfterTransition)(()=>{
// If focus did not move and the element is still in the document, focus it.
if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $aB6Cp$reactariautils.focusWithoutScrolling)(element);
});
} else (0, $aB6Cp$reactariautils.focusWithoutScrolling)(element);
}
/*
* Copyright 2021 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
function $d5156037ad898a4d$var$isStyleVisible(element) {
const windowObject = (0, $aB6Cp$reactariautils.getOwnerWindow)(element);
if (!(element instanceof windowObject.HTMLElement) && !(element instanceof windowObject.SVGElement)) return false;
let { display: display, visibility: visibility } = element.style;
let isVisible = display !== "none" && visibility !== "hidden" && visibility !== "collapse";
if (isVisible) {
const { getComputedStyle: getComputedStyle } = element.ownerDocument.defaultView;
let { display: computedDisplay, visibility: computedVisibility } = getComputedStyle(element);
isVisible = computedDisplay !== "none" && computedVisibility !== "hidden" && computedVisibility !== "collapse";
}
return isVisible;
}
function $d5156037ad898a4d$var$isAttributeVisible(element, childElement) {
return !element.hasAttribute("hidden") && (element.nodeName === "DETAILS" && childElement && childElement.nodeName !== "SUMMARY" ? element.hasAttribute("open") : true);
}
function $d5156037ad898a4d$export$e989c0fffaa6b27a(element, childElement) {
return element.nodeName !== "#comment" && $d5156037ad898a4d$var$isStyleVisible(element) && $d5156037ad898a4d$var$isAttributeVisible(element, childElement) && (!element.parentElement || $d5156037ad898a4d$export$e989c0fffaa6b27a(element.parentElement, element));
}
const $a7a032acae3ddda9$var$FocusContext = /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).createContext(null);
let $a7a032acae3ddda9$var$activeScope = null;
function $a7a032acae3ddda9$export$20e40289641fbbb6(props) {
let { children: children, contain: contain, restoreFocus: restoreFocus, autoFocus: autoFocus } = props;
let startRef = (0, $aB6Cp$react.useRef)(null);
let endRef = (0, $aB6Cp$react.useRef)(null);
let scopeRef = (0, $aB6Cp$react.useRef)([]);
let { parentNode: parentNode } = (0, $aB6Cp$react.useContext)($a7a032acae3ddda9$var$FocusContext) || {};
// Create a tree node here so we can add children to it even before it is added to the tree.
let node = (0, $aB6Cp$react.useMemo)(()=>new $a7a032acae3ddda9$var$TreeNode({
scopeRef: scopeRef
}), [
scopeRef
]);
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
// If a new scope mounts outside the active scope, (e.g. DialogContainer launched from a menu),
// use the active scope as the parent instead of the parent from context. Layout effects run bottom
// up, so if the parent is not yet added to the tree, don't do this. Only the outer-most FocusScope
// that is being added should get the activeScope as its parent.
let parent = parentNode || $a7a032acae3ddda9$export$d06fae2ee68b101e.root;
if ($a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(parent.scopeRef) && $a7a032acae3ddda9$var$activeScope && !$a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, parent.scopeRef)) {
let activeNode = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope);
if (activeNode) parent = activeNode;
}
// Add the node to the parent, and to the tree.
parent.addChild(node);
$a7a032acae3ddda9$export$d06fae2ee68b101e.addNode(node);
}, [
node,
parentNode
]);
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
let node = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef);
if (node) node.contain = !!contain;
}, [
contain
]);
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
var _startRef_current;
// Find all rendered nodes between the sentinels and add them to the scope.
let node = (_startRef_current = startRef.current) === null || _startRef_current === void 0 ? void 0 : _startRef_current.nextSibling;
let nodes = [];
while(node && node !== endRef.current){
nodes.push(node);
node = node.nextSibling;
}
scopeRef.current = nodes;
}, [
children
]);
$a7a032acae3ddda9$var$useActiveScopeTracker(scopeRef, restoreFocus, contain);
$a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain);
$a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain);
$a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus);
// This needs to be an effect so that activeScope is updated after the FocusScope tree is complete.
// It cannot be a useLayoutEffect because the parent of this node hasn't been attached in the tree yet.
(0, $aB6Cp$react.useEffect)(()=>{
const activeElement = (0, $aB6Cp$reactariautils.getOwnerDocument)(scopeRef.current ? scopeRef.current[0] : undefined).activeElement;
let scope = null;
if ($a7a032acae3ddda9$var$isElementInScope(activeElement, scopeRef.current)) {
// We need to traverse the focusScope tree and find the bottom most scope that
// contains the active element and set that as the activeScope.
for (let node of $a7a032acae3ddda9$export$d06fae2ee68b101e.traverse())if (node.scopeRef && $a7a032acae3ddda9$var$isElementInScope(activeElement, node.scopeRef.current)) scope = node;
if (scope === $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef)) $a7a032acae3ddda9$var$activeScope = scope.scopeRef;
}
}, [
scopeRef
]);
// This layout effect cleanup is so that the tree node is removed synchronously with react before the RAF
// in useRestoreFocus cleanup runs.
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
return ()=>{
var _focusScopeTree_getTreeNode_parent, _focusScopeTree_getTreeNode;
var _focusScopeTree_getTreeNode_parent_scopeRef;
// Scope may have been re-parented.
let parentScope = (_focusScopeTree_getTreeNode_parent_scopeRef = (_focusScopeTree_getTreeNode = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef)) === null || _focusScopeTree_getTreeNode === void 0 ? void 0 : (_focusScopeTree_getTreeNode_parent = _focusScopeTree_getTreeNode.parent) === null || _focusScopeTree_getTreeNode_parent === void 0 ? void 0 : _focusScopeTree_getTreeNode_parent.scopeRef) !== null && _focusScopeTree_getTreeNode_parent_scopeRef !== void 0 ? _focusScopeTree_getTreeNode_parent_scopeRef : null;
if ((scopeRef === $a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope(scopeRef, $a7a032acae3ddda9$var$activeScope)) && (!parentScope || $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(parentScope))) $a7a032acae3ddda9$var$activeScope = parentScope;
$a7a032acae3ddda9$export$d06fae2ee68b101e.removeTreeNode(scopeRef);
};
}, [
scopeRef
]);
let focusManager = (0, $aB6Cp$react.useMemo)(()=>$a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef), []);
let value = (0, $aB6Cp$react.useMemo)(()=>({
focusManager: focusManager,
parentNode: node
}), [
node,
focusManager
]);
return /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).createElement($a7a032acae3ddda9$var$FocusContext.Provider, {
value: value
}, /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).createElement("span", {
"data-focus-scope-start": true,
hidden: true,
ref: startRef
}), children, /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).createElement("span", {
"data-focus-scope-end": true,
hidden: true,
ref: endRef
}));
}
function $a7a032acae3ddda9$export$10c5169755ce7bd7() {
var _useContext;
return (_useContext = (0, $aB6Cp$react.useContext)($a7a032acae3ddda9$var$FocusContext)) === null || _useContext === void 0 ? void 0 : _useContext.focusManager;
}
function $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef) {
return {
focusNext (opts = {}) {
let scope = scopeRef.current;
let { from: from, tabbable: tabbable, wrap: wrap, accept: accept } = opts;
let node = from || (0, $aB6Cp$reactariautils.getOwnerDocument)(scope[0]).activeElement;
let sentinel = scope[0].previousElementSibling;
let scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: tabbable,
accept: accept
}, scope);
walker.currentNode = $a7a032acae3ddda9$var$isElementInScope(node, scope) ? node : sentinel;
let nextNode = walker.nextNode();
if (!nextNode && wrap) {
walker.currentNode = sentinel;
nextNode = walker.nextNode();
}
if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
return nextNode;
},
focusPrevious (opts = {}) {
let scope = scopeRef.current;
let { from: from, tabbable: tabbable, wrap: wrap, accept: accept } = opts;
let node = from || (0, $aB6Cp$reactariautils.getOwnerDocument)(scope[0]).activeElement;
let sentinel = scope[scope.length - 1].nextElementSibling;
let scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: tabbable,
accept: accept
}, scope);
walker.currentNode = $a7a032acae3ddda9$var$isElementInScope(node, scope) ? node : sentinel;
let previousNode = walker.previousNode();
if (!previousNode && wrap) {
walker.currentNode = sentinel;
previousNode = walker.previousNode();
}
if (previousNode) $a7a032acae3ddda9$var$focusElement(previousNode, true);
return previousNode;
},
focusFirst (opts = {}) {
let scope = scopeRef.current;
let { tabbable: tabbable, accept: accept } = opts;
let scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: tabbable,
accept: accept
}, scope);
walker.currentNode = scope[0].previousElementSibling;
let nextNode = walker.nextNode();
if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
return nextNode;
},
focusLast (opts = {}) {
let scope = scopeRef.current;
let { tabbable: tabbable, accept: accept } = opts;
let scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: tabbable,
accept: accept
}, scope);
walker.currentNode = scope[scope.length - 1].nextElementSibling;
let previousNode = walker.previousNode();
if (previousNode) $a7a032acae3ddda9$var$focusElement(previousNode, true);
return previousNode;
}
};
}
const $a7a032acae3ddda9$var$focusableElements = [
"input:not([disabled]):not([type=hidden])",
"select:not([disabled])",
"textarea:not([disabled])",
"button:not([disabled])",
"a[href]",
"area[href]",
"summary",
"iframe",
"object",
"embed",
"audio[controls]",
"video[controls]",
"[contenteditable]"
];
const $a7a032acae3ddda9$var$FOCUSABLE_ELEMENT_SELECTOR = $a7a032acae3ddda9$var$focusableElements.join(":not([hidden]),") + ",[tabindex]:not([disabled]):not([hidden])";
$a7a032acae3ddda9$var$focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])');
const $a7a032acae3ddda9$var$TABBABLE_ELEMENT_SELECTOR = $a7a032acae3ddda9$var$focusableElements.join(':not([hidden]):not([tabindex="-1"]),');
function $a7a032acae3ddda9$var$getScopeRoot(scope) {
return scope[0].parentElement;
}
function $a7a032acae3ddda9$var$shouldContainFocus(scopeRef) {
let scope = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope);
while(scope && scope.scopeRef !== scopeRef){
if (scope.contain) return false;
scope = scope.parent;
}
return true;
}
function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
let focusedNode = (0, $aB6Cp$react.useRef)();
let raf = (0, $aB6Cp$react.useRef)();
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
let scope = scopeRef.current;
if (!contain) {
// if contain was changed, then we should cancel any ongoing waits to pull focus back into containment
if (raf.current) {
cancelAnimationFrame(raf.current);
raf.current = undefined;
}
return;
}
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(scope ? scope[0] : undefined);
// Handle the Tab key to contain focus within the scope
let onKeyDown = (e)=>{
if (e.key !== "Tab" || e.altKey || e.ctrlKey || e.metaKey || !$a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) return;
let focusedElement = ownerDocument.activeElement;
let scope = scopeRef.current;
if (!scope || !$a7a032acae3ddda9$var$isElementInScope(focusedElement, scope)) return;
let scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: true
}, scope);
if (!focusedElement) return;
walker.currentNode = focusedElement;
let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
if (!nextElement) {
walker.currentNode = e.shiftKey ? scope[scope.length - 1].nextElementSibling : scope[0].previousElementSibling;
nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
}
e.preventDefault();
if (nextElement) $a7a032acae3ddda9$var$focusElement(nextElement, true);
};
let onFocus = (e)=>{
// If focusing an element in a child scope of the currently active scope, the child becomes active.
// Moving out of the active scope to an ancestor is not allowed.
if ((!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) && $a7a032acae3ddda9$var$isElementInScope(e.target, scopeRef.current)) {
$a7a032acae3ddda9$var$activeScope = scopeRef;
focusedNode.current = e.target;
} else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(e.target, scopeRef)) {
// If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
// restore focus to the previously focused node or the first tabbable element in the active scope.
if (focusedNode.current) focusedNode.current.focus();
else if ($a7a032acae3ddda9$var$activeScope && $a7a032acae3ddda9$var$activeScope.current) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
} else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) focusedNode.current = e.target;
};
let onBlur = (e)=>{
// Firefox doesn't shift focus back to the Dialog properly without this
if (raf.current) cancelAnimationFrame(raf.current);
raf.current = requestAnimationFrame(()=>{
// Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe
if (ownerDocument.activeElement && $a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(ownerDocument.activeElement, scopeRef)) {
$a7a032acae3ddda9$var$activeScope = scopeRef;
if (ownerDocument.body.contains(e.target)) {
var _focusedNode_current;
focusedNode.current = e.target;
(_focusedNode_current = focusedNode.current) === null || _focusedNode_current === void 0 ? void 0 : _focusedNode_current.focus();
} else if ($a7a032acae3ddda9$var$activeScope.current) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
}
});
};
ownerDocument.addEventListener("keydown", onKeyDown, false);
ownerDocument.addEventListener("focusin", onFocus, false);
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.addEventListener("focusin", onFocus, false));
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.addEventListener("focusout", onBlur, false));
return ()=>{
ownerDocument.removeEventListener("keydown", onKeyDown, false);
ownerDocument.removeEventListener("focusin", onFocus, false);
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.removeEventListener("focusin", onFocus, false));
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.removeEventListener("focusout", onBlur, false));
};
}, [
scopeRef,
contain
]);
// This is a useLayoutEffect so it is guaranteed to run before our async synthetic blur
// eslint-disable-next-line arrow-body-style
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
return ()=>{
if (raf.current) cancelAnimationFrame(raf.current);
};
}, [
raf
]);
}
function $a7a032acae3ddda9$var$isElementInAnyScope(element) {
return $a7a032acae3ddda9$var$isElementInChildScope(element);
}
function $a7a032acae3ddda9$var$isElementInScope(element, scope) {
if (!element) return false;
if (!scope) return false;
return scope.some((node)=>node.contains(element));
}
function $a7a032acae3ddda9$var$isElementInChildScope(element, scope = null) {
// If the element is within a top layer element (e.g. toasts), always allow moving focus there.
if (element instanceof Element && element.closest("[data-react-aria-top-layer]")) return true;
// node.contains in isElementInScope covers child scopes that are also DOM children,
// but does not cover child scopes in portals.
for (let { scopeRef: s } of $a7a032acae3ddda9$export$d06fae2ee68b101e.traverse($a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scope))){
if (s && $a7a032acae3ddda9$var$isElementInScope(element, s.current)) return true;
}
return false;
}
function $a7a032acae3ddda9$export$1258395f99bf9cbf(element) {
return $a7a032acae3ddda9$var$isElementInChildScope(element, $a7a032acae3ddda9$var$activeScope);
}
function $a7a032acae3ddda9$var$isAncestorScope(ancestor, scope) {
var _focusScopeTree_getTreeNode;
let parent = (_focusScopeTree_getTreeNode = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scope)) === null || _focusScopeTree_getTreeNode === void 0 ? void 0 : _focusScopeTree_getTreeNode.parent;
while(parent){
if (parent.scopeRef === ancestor) return true;
parent = parent.parent;
}
return false;
}
function $a7a032acae3ddda9$var$focusElement(element, scroll = false) {
if (element != null && !scroll) try {
(0, $1c7f9157d722357d$export$80f3e147d781571c)(element);
} catch (err) {
// ignore
}
else if (element != null) try {
element.focus();
} catch (err) {
// ignore
}
}
function $a7a032acae3ddda9$var$focusFirstInScope(scope, tabbable = true) {
let sentinel = scope[0].previousElementSibling;
let scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: tabbable
}, scope);
walker.currentNode = sentinel;
let nextNode = walker.nextNode();
// If the scope does not contain a tabbable element, use the first focusable element.
if (tabbable && !nextNode) {
scopeRoot = $a7a032acae3ddda9$var$getScopeRoot(scope);
walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(scopeRoot, {
tabbable: false
}, scope);
walker.currentNode = sentinel;
nextNode = walker.nextNode();
}
$a7a032acae3ddda9$var$focusElement(nextNode);
}
function $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus) {
const autoFocusRef = (0, ($parcel$interopDefault($aB6Cp$react))).useRef(autoFocus);
(0, $aB6Cp$react.useEffect)(()=>{
if (autoFocusRef.current) {
$a7a032acae3ddda9$var$activeScope = scopeRef;
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(scopeRef.current ? scopeRef.current[0] : undefined);
if (!$a7a032acae3ddda9$var$isElementInScope(ownerDocument.activeElement, $a7a032acae3ddda9$var$activeScope.current) && scopeRef.current) $a7a032acae3ddda9$var$focusFirstInScope(scopeRef.current);
}
autoFocusRef.current = false;
}, [
scopeRef
]);
}
function $a7a032acae3ddda9$var$useActiveScopeTracker(scopeRef, restore, contain) {
// tracks the active scope, in case restore and contain are both false.
// if either are true, this is tracked in useRestoreFocus or useFocusContainment.
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
if (restore || contain) return;
let scope = scopeRef.current;
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(scope ? scope[0] : undefined);
let onFocus = (e)=>{
let target = e.target;
if ($a7a032acae3ddda9$var$isElementInScope(target, scopeRef.current)) $a7a032acae3ddda9$var$activeScope = scopeRef;
else if (!$a7a032acae3ddda9$var$isElementInAnyScope(target)) $a7a032acae3ddda9$var$activeScope = null;
};
ownerDocument.addEventListener("focusin", onFocus, false);
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.addEventListener("focusin", onFocus, false));
return ()=>{
ownerDocument.removeEventListener("focusin", onFocus, false);
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.removeEventListener("focusin", onFocus, false));
};
}, [
scopeRef,
restore,
contain
]);
}
function $a7a032acae3ddda9$var$shouldRestoreFocus(scopeRef) {
let scope = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope);
while(scope && scope.scopeRef !== scopeRef){
if (scope.nodeToRestore) return false;
scope = scope.parent;
}
return (scope === null || scope === void 0 ? void 0 : scope.scopeRef) === scopeRef;
}
function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain) {
// create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
// eslint-disable-next-line no-restricted-globals
const nodeToRestoreRef = (0, $aB6Cp$react.useRef)(typeof document !== "undefined" ? (0, $aB6Cp$reactariautils.getOwnerDocument)(scopeRef.current ? scopeRef.current[0] : undefined).activeElement : null);
// restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
// restoring-non-containing scopes should only care if they become active so they can perform the restore
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
let scope = scopeRef.current;
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(scope ? scope[0] : undefined);
if (!restoreFocus || contain) return;
let onFocus = ()=>{
// If focusing an element in a child scope of the currently active scope, the child becomes active.
// Moving out of the active scope to an ancestor is not allowed.
if ((!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) && $a7a032acae3ddda9$var$isElementInScope(ownerDocument.activeElement, scopeRef.current)) $a7a032acae3ddda9$var$activeScope = scopeRef;
};
ownerDocument.addEventListener("focusin", onFocus, false);
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.addEventListener("focusin", onFocus, false));
return ()=>{
ownerDocument.removeEventListener("focusin", onFocus, false);
scope === null || scope === void 0 ? void 0 : scope.forEach((element)=>element.removeEventListener("focusin", onFocus, false));
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
scopeRef,
contain
]);
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(scopeRef.current ? scopeRef.current[0] : undefined);
if (!restoreFocus) return;
// Handle the Tab key so that tabbing out of the scope goes to the next element
// after the node that had focus when the scope mounted. This is important when
// using portals for overlays, so that focus goes to the expected element when
// tabbing out of the overlay.
let onKeyDown = (e)=>{
if (e.key !== "Tab" || e.altKey || e.ctrlKey || e.metaKey || !$a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) return;
let focusedElement = ownerDocument.activeElement;
if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scopeRef.current)) return;
let treeNode = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef);
if (!treeNode) return;
let nodeToRestore = treeNode.nodeToRestore;
// Create a DOM tree walker that matches all tabbable elements
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(ownerDocument.body, {
tabbable: true
});
// Find the next tabbable element after the currently focused element
walker.currentNode = focusedElement;
let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
if (!nodeToRestore || !ownerDocument.body.contains(nodeToRestore) || nodeToRestore === ownerDocument.body) {
nodeToRestore = undefined;
treeNode.nodeToRestore = undefined;
}
// If there is no next element, or it is outside the current scope, move focus to the
// next element after the node to restore to instead.
if ((!nextElement || !$a7a032acae3ddda9$var$isElementInScope(nextElement, scopeRef.current)) && nodeToRestore) {
walker.currentNode = nodeToRestore;
// Skip over elements within the scope, in case the scope immediately follows the node to restore.
do nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
while ($a7a032acae3ddda9$var$isElementInScope(nextElement, scopeRef.current));
e.preventDefault();
e.stopPropagation();
if (nextElement) $a7a032acae3ddda9$var$focusElement(nextElement, true);
else // If there is no next element and the nodeToRestore isn't within a FocusScope (i.e. we are leaving the top level focus scope)
// then move focus to the body.
// Otherwise restore focus to the nodeToRestore (e.g menu within a popover -> tabbing to close the menu should move focus to menu trigger)
if (!$a7a032acae3ddda9$var$isElementInAnyScope(nodeToRestore)) focusedElement.blur();
else $a7a032acae3ddda9$var$focusElement(nodeToRestore, true);
}
};
if (!contain) ownerDocument.addEventListener("keydown", onKeyDown, true);
return ()=>{
if (!contain) ownerDocument.removeEventListener("keydown", onKeyDown, true);
};
}, [
scopeRef,
restoreFocus,
contain
]);
// useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
(0, $aB6Cp$reactariautils.useLayoutEffect)(()=>{
const ownerDocument = (0, $aB6Cp$reactariautils.getOwnerDocument)(scopeRef.current ? scopeRef.current[0] : undefined);
if (!restoreFocus) return;
let treeNode = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef);
if (!treeNode) return;
var _nodeToRestoreRef_current;
treeNode.nodeToRestore = (_nodeToRestoreRef_current = nodeToRestoreRef.current) !== null && _nodeToRestoreRef_current !== void 0 ? _nodeToRestoreRef_current : undefined;
return ()=>{
let treeNode = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef);
if (!treeNode) return;
let nodeToRestore = treeNode.nodeToRestore;
// if we already lost focus to the body and this was the active scope, then we should attempt to restore
if (restoreFocus && nodeToRestore && // eslint-disable-next-line react-hooks/exhaustive-deps
($a7a032acae3ddda9$var$isElementInScope(ownerDocument.activeElement, scopeRef.current) || ownerDocument.activeElement === ownerDocument.body && $a7a032acae3ddda9$var$shouldRestoreFocus(scopeRef))) {
// freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
let clonedTree = $a7a032acae3ddda9$export$d06fae2ee68b101e.clone();
requestAnimationFrame(()=>{
// Only restore focus if we've lost focus to the body, the alternative is that focus has been purposefully moved elsewhere
if (ownerDocument.activeElement === ownerDocument.body) {
// look up the tree starting with our scope to find a nodeToRestore still in the DOM
let treeNode = clonedTree.getTreeNode(scopeRef);
while(treeNode){
if (treeNode.nodeToRestore && treeNode.nodeToRestore.isConnected) {
$a7a032acae3ddda9$var$focusElement(treeNode.nodeToRestore);
return;
}
treeNode = treeNode.parent;
}
// If no nodeToRestore was found, focus the first element in the nearest
// ancestor scope that is still in the tree.
treeNode = clonedTree.getTreeNode(scopeRef);
while(treeNode){
if (treeNode.scopeRef && treeNode.scopeRef.current && $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(treeNode.scopeRef)) {
$a7a032acae3ddda9$var$focusFirstInScope(treeNode.scopeRef.current, true);
return;
}
treeNode = treeNode.parent;
}
}
});
}
};
}, [
scopeRef,
restoreFocus
]);
}
function $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, opts, scope) {
let selector = (opts === null || opts === void 0 ? void 0 : opts.tabbable) ? $a7a032acae3ddda9$var$TABBABLE_ELEMENT_SELECTOR : $a7a032acae3ddda9$var$FOCUSABLE_ELEMENT_SELECTOR;
let walker = (0, $aB6Cp$reactariautils.getOwnerDocument)(root).createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
acceptNode (node) {
var _opts_from;
// Skip nodes inside the starting node.
if (opts === null || opts === void 0 ? void 0 : (_opts_from = opts.from) === null || _opts_from === void 0 ? void 0 : _opts_from.contains(node)) return NodeFilter.FILTER_REJECT;
if (node.matches(selector) && (0, $d5156037ad898a4d$export$e989c0fffaa6b27a)(node) && (!scope || $a7a032acae3ddda9$var$isElementInScope(node, scope)) && (!(opts === null || opts === void 0 ? void 0 : opts.accept) || opts.accept(node))) return NodeFilter.FILTER_ACCEPT;
return NodeFilter.FILTER_SKIP;
}
});
if (opts === null || opts === void 0 ? void 0 : opts.from) walker.currentNode = opts.from;
return walker;
}
function $a7a032acae3ddda9$export$c5251b9e124bf29(ref, defaultOptions = {}) {
return {
focusNext (opts = {}) {
let root = ref.current;
if (!root) return null;
let { from: from, tabbable: tabbable = defaultOptions.tabbable, wrap: wrap = defaultOptions.wrap, accept: accept = defaultOptions.accept } = opts;
let node = from || (0, $aB6Cp$reactariautils.getOwnerDocument)(root).activeElement;
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
tabbable: tabbable,
accept: accept
});
if (root.contains(node)) walker.currentNode = node;
let nextNode = walker.nextNode();
if (!nextNode && wrap) {
walker.currentNode = root;
nextNode = walker.nextNode();
}
if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
return nextNode;
},
focusPrevious (opts = defaultOptions) {
let root = ref.current;
if (!root) return null;
let { from: from, tabbable: tabbable = defaultOptions.tabbable, wrap: wrap = defaultOptions.wrap, accept: accept = defaultOptions.accept } = opts;
let node = from || (0, $aB6Cp$reactariautils.getOwnerDocument)(root).activeElement;
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
tabbable: tabbable,
accept: accept
});
if (root.contains(node)) walker.currentNode = node;
else {
let next = $a7a032acae3ddda9$var$last(walker);
if (next) $a7a032acae3ddda9$var$focusElement(next, true);
return next !== null && next !== void 0 ? next : null;
}
let previousNode = walker.previousNode();
if (!previousNode && wrap) {
walker.currentNode = root;
let lastNode = $a7a032acae3ddda9$var$last(walker);
if (!lastNode) // couldn't wrap
return null;
previousNode = lastNode;
}
if (previousNode) $a7a032acae3ddda9$var$focusElement(previousNode, true);
return previousNode !== null && previousNode !== void 0 ? previousNode : null;
},
focusFirst (opts = defaultOptions) {
let root = ref.current;
if (!root) return null;
let { tabbable: tabbable = defaultOptions.tabbable, accept: accept = defaultOptions.accept } = opts;
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
tabbable: tabbable,
accept: accept
});
let nextNode = walker.nextNode();
if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
return nextNode;
},
focusLast (opts = defaultOptions) {
let root = ref.current;
if (!root) return null;
let { tabbable: tabbable = defaultOptions.tabbable, accept: accept = defaultOptions.accept } = opts;
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
tabbable: tabbable,
accept: accept
});
let next = $a7a032acae3ddda9$var$last(walker);
if (next) $a7a032acae3ddda9$var$focusElement(next, true);
return next !== null && next !== void 0 ? next : null;
}
};
}
function $a7a032acae3ddda9$var$last(walker) {
let next = undefined;
let last;
do {
last = walker.lastChild();
if (last) next = last;
}while (last);
return next;
}
class $a7a032acae3ddda9$var$Tree {
get size() {
return this.fastMap.size;
}
getTreeNode(data) {
return this.fastMap.get(data);
}
addTreeNode(scopeRef, parent, nodeToRestore) {
let parentNode = this.fastMap.get(parent !== null && parent !== void 0 ? parent : null);
if (!parentNode) return;
let node = new $a7a032acae3ddda9$var$TreeNode({
scopeRef: scopeRef
});
parentNode.addChild(node);
node.parent = parentNode;
this.fastMap.set(scopeRef, node);
if (nodeToRestore) node.nodeToRestore = nodeToRestore;
}
addNode(node) {
this.fastMap.set(node.scopeRef, node);
}
removeTreeNode(scopeRef) {
// never remove the root
if (scopeRef === null) return;
let node = this.fastMap.get(scopeRef);
if (!node) return;
let parentNode = node.parent;
// when we remove a scope, check if any sibling scopes are trying to restore focus to something inside the scope we're removing
// if we are, then replace the siblings restore with the restore from the scope we're removing
for (let current of this.traverse())if (current !== node && node.nodeToRestore && current.nodeToRestore && node.scopeRef && node.scopeRef.current && $a7a032acae3ddda9$var$isElementInScope(current.nodeToRestore, node.scopeRef.current)) current.nodeToRestore = node.nodeToRestore;
let children = node.children;
if (parentNode) {
parentNode.removeChild(node);
if (children.size > 0) children.forEach((child)=>parentNode && parentNode.addChild(child));
}
this.fastMap.delete(node.scopeRef);
}
// Pre Order Depth First
*traverse(node = this.root) {
if (node.scopeRef != null) yield node;
if (node.children.size > 0) for (let child of node.children)yield* this.traverse(child);
}
clone() {
var _node_parent;
let newTree = new $a7a032acae3ddda9$var$Tree();
var _node_parent_scopeRef;
for (let node of this.traverse())newTree.addTreeNode(node.scopeRef, (_node_parent_scopeRef = (_node_parent = node.parent) === null || _node_parent === void 0 ? void 0 : _node_parent.scopeRef) !== null && _node_parent_scopeRef !== void 0 ? _node_parent_scopeRef : null, node.nodeToRestore);
return newTree;
}
constructor(){
this.fastMap = new Map();
this.root = new $a7a032acae3ddda9$var$TreeNode({
scopeRef: null
});
this.fastMap.set(null, this.root);
}
}
class $a7a032acae3ddda9$var$TreeNode {
addChild(node) {
this.children.add(node);
node.parent = this;
}
removeChild(node) {
this.children.delete(node);
node.parent = undefined;
}
constructor(props){
this.children = new Set();
this.contain = false;
this.scopeRef = props.scopeRef;
}
}
let $a7a032acae3ddda9$export$d06fae2ee68b101e = new $a7a032acae3ddda9$var$Tree();
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
function $581a96d6eb128c1b$export$4e328f61c538687f(props = {}) {
let { autoFocus: autoFocus = false, isTextInput: isTextInput, within: within } = props;
let state = (0, $aB6Cp$react.useRef)({
isFocused: false,
isFocusVisible: autoFocus || (0, $aB6Cp$reactariainteractions.isFocusVisible)()
});
let [isFocused, setFocused] = (0, $aB6Cp$react.useState)(false);
let [isFocusVisibleState, setFocusVisible] = (0, $aB6Cp$react.useState)(()=>state.current.isFocused && state.current.isFocusVisible);
let updateState = (0, $aB6Cp$react.useCallback)(()=>setFocusVisible(state.current.isFocused && state.current.isFocusVisible), []);
let onFocusChange = (0, $aB6Cp$react.useCallback)((isFocused)=>{
state.current.isFocused = isFocused;
setFocused(isFocused);
updateState();
}, [
updateState
]);
(0, $aB6Cp$reactariainteractions.useFocusVisibleListener)((isFocusVisible)=>{
state.current.isFocusVisible = isFocusVisible;
updateState();
}, [], {
isTextInput: isTextInput
});
let { focusProps: focusProps } = (0, $aB6Cp$reactariainteractions.useFocus)({
isDisabled: within,
onFocusChange: onFocusChange
});
let { focusWithinProps: focusWithinProps } = (0, $aB6Cp$reactariainteractions.useFocusWithin)({
isDisabled: !within,
onFocusWithinChange: onFocusChange
});
return {
isFocused: isFocused,
isFocusVisible: isFocusVisibleState,
focusProps: within ? focusWithinProps : focusProps
};
}
function $dfd8c70b928eb1b3$export$1a38b4ad7f578e1d(props) {
let { children: children, focusClass: focusClass, focusRingClass: focusRingClass } = props;
let { isFocused: isFocused, isFocusVisible: isFocusVisible, focusProps: focusProps } = (0, $581a96d6eb128c1b$export$4e328f61c538687f)(props);
let child = (0, ($parcel$interopDefault($aB6Cp$react))).Children.only(children);
return /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).cloneElement(child, (0, $aB6Cp$reactariautils.mergeProps)(child.props, {
...focusProps,
className: (0, ($parcel$interopDefault($aB6Cp$clsx)))({
[focusClass || ""]: isFocused,
[focusRingClass || ""]: isFocusVisible
})
}));
}
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
let $fb504d83237fd6ac$var$FocusableContext = /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).createContext(null);
function $fb504d83237fd6ac$var$useFocusableContext(ref) {
let context = (0, $aB6Cp$react.useContext)($fb504d83237fd6ac$var$FocusableContext) || {};
(0, $aB6Cp$reactariautils.useSyncRef)(context, ref);
// eslint-disable-next-line
let { ref: _, ...otherProps } = context;
return otherProps;
}
/**
* Provides DOM props to the nearest focusable child.
*/ function $fb504d83237fd6ac$var$FocusableProvider(props, ref) {
let { children: children, ...otherProps } = props;
let objRef = (0, $aB6Cp$reactariautils.useObjectRef)(ref);
let context = {
...otherProps,
ref: objRef
};
return /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).createElement($fb504d83237fd6ac$var$FocusableContext.Provider, {
value: context
}, children);
}
let $fb504d83237fd6ac$export$13f3202a3e5ddd5 = /*#__PURE__*/ (0, ($parcel$interopDefault($aB6Cp$react))).forwardRef($fb504d83237fd6ac$var$FocusableProvider);
function $fb504d83237fd6ac$export$4c014de7c8940b4c(props, domRef) {
let { focusProps: focusProps } = (0, $aB6Cp$reactariainteractions.useFocus)(props);
let { keyboardProps: keyboardProps } = (0, $aB6Cp$reactariainteractions.useKeyboard)(props);
let interactions = (0, $aB6Cp$reactariautils.mergeProps)(focusProps, keyboardProps);
let domProps = $fb504d83237fd6ac$var$useFocusableContext(domRef);
let interactionProps = props.isDisabled ? {} : domProps;
let autoFocusRef = (0, $aB6Cp$react.useRef)(props.autoFocus);
(0, $aB6Cp$react.useEffect)(()=>{
if (autoFocusRef.current && domRef.current) (0, $1c7f9157d722357d$export$80f3e147d781571c)(domRef.current);
autoFocusRef.current = false;
}, [
domRef
]);
return {
focusableProps: (0, $aB6Cp$reactariautils.mergeProps)({
...interactions,
tabIndex: props.excludeFromTabOrder && !props.isDisabled ? -1 : undefi