UNPKG

37.2 kBJavaScriptView Raw
1var $aB6Cp$react = require("react");
2var $aB6Cp$reactariautils = require("@react-aria/utils");
3var $aB6Cp$reactariainteractions = require("@react-aria/interactions");
4var $aB6Cp$clsx = require("clsx");
5
6function $parcel$export(e, n, v, s) {
7 Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
8}
9function $parcel$interopDefault(a) {
10 return a && a.__esModule ? a.default : a;
11}
12
13$parcel$export(module.exports, "FocusScope", () => $a7a032acae3ddda9$export$20e40289641fbbb6);
14$parcel$export(module.exports, "useFocusManager", () => $a7a032acae3ddda9$export$10c5169755ce7bd7);
15$parcel$export(module.exports, "getFocusableTreeWalker", () => $a7a032acae3ddda9$export$2d6ec8fc375ceafa);
16$parcel$export(module.exports, "createFocusManager", () => $a7a032acae3ddda9$export$c5251b9e124bf29);
17$parcel$export(module.exports, "FocusRing", () => $dfd8c70b928eb1b3$export$1a38b4ad7f578e1d);
18$parcel$export(module.exports, "FocusableProvider", () => $fb504d83237fd6ac$export$13f3202a3e5ddd5);
19$parcel$export(module.exports, "useFocusable", () => $fb504d83237fd6ac$export$4c014de7c8940b4c);
20$parcel$export(module.exports, "useFocusRing", () => $581a96d6eb128c1b$export$4e328f61c538687f);
21$parcel$export(module.exports, "focusSafely", () => $1c7f9157d722357d$export$80f3e147d781571c);
22
23
24function $1c7f9157d722357d$export$80f3e147d781571c(element) {
25 // If the user is interacting with a virtual cursor, e.g. screen reader, then
26 // wait until after any animated transitions that are currently occurring on
27 // the page before shifting focus. This avoids issues with VoiceOver on iOS
28 // causing the page to scroll when moving focus if the element is transitioning
29 // from off the screen.
30 if ($aB6Cp$reactariainteractions.getInteractionModality() === 'virtual') {
31 let lastFocusedElement = document.activeElement;
32 $aB6Cp$reactariautils.runAfterTransition(()=>{
33 // If focus did not move and the element is still in the document, focus it.
34 if (document.activeElement === lastFocusedElement && document.contains(element)) $aB6Cp$reactariautils.focusWithoutScrolling(element);
35 });
36 } else $aB6Cp$reactariautils.focusWithoutScrolling(element);
37}
38
39
40/*
41 * Copyright 2021 Adobe. All rights reserved.
42 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
43 * you may not use this file except in compliance with the License. You may obtain a copy
44 * of the License at http://www.apache.org/licenses/LICENSE-2.0
45 *
46 * Unless required by applicable law or agreed to in writing, software distributed under
47 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
48 * OF ANY KIND, either express or implied. See the License for the specific language
49 * governing permissions and limitations under the License.
50 */ function $d5156037ad898a4d$var$isStyleVisible(element) {
51 if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) return false;
52 let { display: display , visibility: visibility } = element.style;
53 let isVisible = display !== 'none' && visibility !== 'hidden' && visibility !== 'collapse';
54 if (isVisible) {
55 const { getComputedStyle: getComputedStyle } = element.ownerDocument.defaultView;
56 let { display: computedDisplay , visibility: computedVisibility } = getComputedStyle(element);
57 isVisible = computedDisplay !== 'none' && computedVisibility !== 'hidden' && computedVisibility !== 'collapse';
58 }
59 return isVisible;
60}
61function $d5156037ad898a4d$var$isAttributeVisible(element, childElement) {
62 return !element.hasAttribute('hidden') && (element.nodeName === 'DETAILS' && childElement && childElement.nodeName !== 'SUMMARY' ? element.hasAttribute('open') : true);
63}
64function $d5156037ad898a4d$export$e989c0fffaa6b27a(element, childElement) {
65 return element.nodeName !== '#comment' && $d5156037ad898a4d$var$isStyleVisible(element) && $d5156037ad898a4d$var$isAttributeVisible(element, childElement) && (!element.parentElement || $d5156037ad898a4d$export$e989c0fffaa6b27a(element.parentElement, element));
66}
67
68
69
70
71const $a7a032acae3ddda9$var$FocusContext = /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createContext(null);
72let $a7a032acae3ddda9$var$activeScope = null;
73function $a7a032acae3ddda9$export$20e40289641fbbb6(props) {
74 let { children: children , contain: contain , restoreFocus: restoreFocus , autoFocus: autoFocus } = props;
75 let startRef = $aB6Cp$react.useRef();
76 let endRef = $aB6Cp$react.useRef();
77 let scopeRef = $aB6Cp$react.useRef([]);
78 let ctx = $aB6Cp$react.useContext($a7a032acae3ddda9$var$FocusContext);
79 var ref;
80 // if there is no scopeRef on the context, then the parent is the focusScopeTree's root, represented by null
81 let parentScope = (ref = ctx === null || ctx === void 0 ? void 0 : ctx.scopeRef) !== null && ref !== void 0 ? ref : null;
82 $aB6Cp$reactariautils.useLayoutEffect(()=>{
83 // Find all rendered nodes between the sentinels and add them to the scope.
84 let node = startRef.current.nextSibling;
85 let nodes = [];
86 while(node && node !== endRef.current){
87 nodes.push(node);
88 node = node.nextSibling;
89 }
90 scopeRef.current = nodes;
91 }, [
92 children,
93 parentScope
94 ]);
95 // add to the focus scope tree in render order because useEffects/useLayoutEffects run children first whereas render runs parent first
96 // which matters when constructing a tree
97 if ($a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(parentScope) && !$a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef)) $a7a032acae3ddda9$export$d06fae2ee68b101e.addTreeNode(scopeRef, parentScope);
98 let node1 = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef);
99 node1.contain = contain;
100 $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain);
101 $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain);
102 $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus);
103 // this layout effect needs to run last so that focusScopeTree cleanup happens at the last moment possible
104 $aB6Cp$reactariautils.useLayoutEffect(()=>{
105 if (scopeRef && (parentScope || parentScope == null)) return ()=>{
106 // Restore the active scope on unmount if this scope or a descendant scope is active.
107 // Parent effect cleanups run before children, so we need to check if the
108 // parent scope actually still exists before restoring the active scope to it.
109 if ((scopeRef === $a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope(scopeRef, $a7a032acae3ddda9$var$activeScope)) && (!parentScope || $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(parentScope))) $a7a032acae3ddda9$var$activeScope = parentScope;
110 $a7a032acae3ddda9$export$d06fae2ee68b101e.removeTreeNode(scopeRef);
111 };
112 }, [
113 scopeRef,
114 parentScope
115 ]);
116 let focusManager = $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef);
117 return(/*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createElement($a7a032acae3ddda9$var$FocusContext.Provider, {
118 value: {
119 scopeRef: scopeRef,
120 focusManager: focusManager
121 }
122 }, /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createElement("span", {
123 "data-focus-scope-start": true,
124 hidden: true,
125 ref: startRef
126 }), children, /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createElement("span", {
127 "data-focus-scope-end": true,
128 hidden: true,
129 ref: endRef
130 })));
131}
132function $a7a032acae3ddda9$export$10c5169755ce7bd7() {
133 var ref;
134 return (ref = $aB6Cp$react.useContext($a7a032acae3ddda9$var$FocusContext)) === null || ref === void 0 ? void 0 : ref.focusManager;
135}
136function $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef) {
137 return {
138 focusNext (opts = {
139 }) {
140 let scope = scopeRef.current;
141 let { from: from , tabbable: tabbable , wrap: wrap , accept: accept } = opts;
142 let node = from || document.activeElement;
143 let sentinel = scope[0].previousElementSibling;
144 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
145 tabbable: tabbable,
146 accept: accept
147 }, scope);
148 walker.currentNode = $a7a032acae3ddda9$var$isElementInScope(node, scope) ? node : sentinel;
149 let nextNode = walker.nextNode();
150 if (!nextNode && wrap) {
151 walker.currentNode = sentinel;
152 nextNode = walker.nextNode();
153 }
154 if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
155 return nextNode;
156 },
157 focusPrevious (opts = {
158 }) {
159 let scope = scopeRef.current;
160 let { from: from , tabbable: tabbable , wrap: wrap , accept: accept } = opts;
161 let node = from || document.activeElement;
162 let sentinel = scope[scope.length - 1].nextElementSibling;
163 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
164 tabbable: tabbable,
165 accept: accept
166 }, scope);
167 walker.currentNode = $a7a032acae3ddda9$var$isElementInScope(node, scope) ? node : sentinel;
168 let previousNode = walker.previousNode();
169 if (!previousNode && wrap) {
170 walker.currentNode = sentinel;
171 previousNode = walker.previousNode();
172 }
173 if (previousNode) $a7a032acae3ddda9$var$focusElement(previousNode, true);
174 return previousNode;
175 },
176 focusFirst (opts = {
177 }) {
178 let scope = scopeRef.current;
179 let { tabbable: tabbable , accept: accept } = opts;
180 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
181 tabbable: tabbable,
182 accept: accept
183 }, scope);
184 walker.currentNode = scope[0].previousElementSibling;
185 let nextNode = walker.nextNode();
186 if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
187 return nextNode;
188 },
189 focusLast (opts = {
190 }) {
191 let scope = scopeRef.current;
192 let { tabbable: tabbable , accept: accept } = opts;
193 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
194 tabbable: tabbable,
195 accept: accept
196 }, scope);
197 walker.currentNode = scope[scope.length - 1].nextElementSibling;
198 let previousNode = walker.previousNode();
199 if (previousNode) $a7a032acae3ddda9$var$focusElement(previousNode, true);
200 return previousNode;
201 }
202 };
203}
204const $a7a032acae3ddda9$var$focusableElements = [
205 'input:not([disabled]):not([type=hidden])',
206 'select:not([disabled])',
207 'textarea:not([disabled])',
208 'button:not([disabled])',
209 'a[href]',
210 'area[href]',
211 'summary',
212 'iframe',
213 'object',
214 'embed',
215 'audio[controls]',
216 'video[controls]',
217 '[contenteditable]'
218];
219const $a7a032acae3ddda9$var$FOCUSABLE_ELEMENT_SELECTOR = $a7a032acae3ddda9$var$focusableElements.join(':not([hidden]),') + ',[tabindex]:not([disabled]):not([hidden])';
220$a7a032acae3ddda9$var$focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])');
221const $a7a032acae3ddda9$var$TABBABLE_ELEMENT_SELECTOR = $a7a032acae3ddda9$var$focusableElements.join(':not([hidden]):not([tabindex="-1"]),');
222function $a7a032acae3ddda9$var$getScopeRoot(scope) {
223 return scope[0].parentElement;
224}
225function $a7a032acae3ddda9$var$shouldContainFocus(scopeRef) {
226 let scope = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope);
227 while(scope && scope.scopeRef !== scopeRef){
228 if (scope.contain) return false;
229 scope = scope.parent;
230 }
231 return true;
232}
233function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
234 let focusedNode = $aB6Cp$react.useRef();
235 let raf = $aB6Cp$react.useRef(null);
236 $aB6Cp$reactariautils.useLayoutEffect(()=>{
237 let scope1 = scopeRef.current;
238 if (!contain) {
239 // if contain was changed, then we should cancel any ongoing waits to pull focus back into containment
240 if (raf.current) {
241 cancelAnimationFrame(raf.current);
242 raf.current = null;
243 }
244 return;
245 }
246 // Handle the Tab key to contain focus within the scope
247 let onKeyDown = (e)=>{
248 if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey || !$a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) return;
249 let focusedElement = document.activeElement;
250 let scope = scopeRef.current;
251 if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scope)) return;
252 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
253 tabbable: true
254 }, scope);
255 walker.currentNode = focusedElement;
256 let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
257 if (!nextElement) {
258 walker.currentNode = e.shiftKey ? scope[scope.length - 1].nextElementSibling : scope[0].previousElementSibling;
259 nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
260 }
261 e.preventDefault();
262 if (nextElement) $a7a032acae3ddda9$var$focusElement(nextElement, true);
263 };
264 let onFocus = (e)=>{
265 // If focusing an element in a child scope of the currently active scope, the child becomes active.
266 // Moving out of the active scope to an ancestor is not allowed.
267 if (!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) {
268 $a7a032acae3ddda9$var$activeScope = scopeRef;
269 focusedNode.current = e.target;
270 } else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(e.target, scopeRef)) {
271 // If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
272 // restore focus to the previously focused node or the first tabbable element in the active scope.
273 if (focusedNode.current) focusedNode.current.focus();
274 else if ($a7a032acae3ddda9$var$activeScope) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
275 } else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) focusedNode.current = e.target;
276 };
277 let onBlur = (e)=>{
278 // Firefox doesn't shift focus back to the Dialog properly without this
279 raf.current = requestAnimationFrame(()=>{
280 // Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe
281 if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(document.activeElement, scopeRef)) {
282 $a7a032acae3ddda9$var$activeScope = scopeRef;
283 if (document.body.contains(e.target)) {
284 focusedNode.current = e.target;
285 focusedNode.current.focus();
286 } else if ($a7a032acae3ddda9$var$activeScope) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
287 }
288 });
289 };
290 document.addEventListener('keydown', onKeyDown, false);
291 document.addEventListener('focusin', onFocus, false);
292 scope1.forEach((element)=>element.addEventListener('focusin', onFocus, false)
293 );
294 scope1.forEach((element)=>element.addEventListener('focusout', onBlur, false)
295 );
296 return ()=>{
297 document.removeEventListener('keydown', onKeyDown, false);
298 document.removeEventListener('focusin', onFocus, false);
299 scope1.forEach((element)=>element.removeEventListener('focusin', onFocus, false)
300 );
301 scope1.forEach((element)=>element.removeEventListener('focusout', onBlur, false)
302 );
303 };
304 }, [
305 scopeRef,
306 contain
307 ]);
308 // eslint-disable-next-line arrow-body-style
309 $aB6Cp$react.useEffect(()=>{
310 return ()=>{
311 if (raf.current) cancelAnimationFrame(raf.current);
312 };
313 }, [
314 raf
315 ]);
316}
317function $a7a032acae3ddda9$var$isElementInAnyScope(element) {
318 return $a7a032acae3ddda9$var$isElementInChildScope(element);
319}
320function $a7a032acae3ddda9$var$isElementInScope(element, scope) {
321 return scope.some((node)=>node.contains(element)
322 );
323}
324function $a7a032acae3ddda9$var$isElementInChildScope(element, scope = null) {
325 // node.contains in isElementInScope covers child scopes that are also DOM children,
326 // but does not cover child scopes in portals.
327 for (let { scopeRef: s } of $a7a032acae3ddda9$export$d06fae2ee68b101e.traverse($a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scope))){
328 if ($a7a032acae3ddda9$var$isElementInScope(element, s.current)) return true;
329 }
330 return false;
331}
332function $a7a032acae3ddda9$var$isAncestorScope(ancestor, scope) {
333 var ref;
334 let parent = (ref = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scope)) === null || ref === void 0 ? void 0 : ref.parent;
335 while(parent){
336 if (parent.scopeRef === ancestor) return true;
337 parent = parent.parent;
338 }
339 return false;
340}
341function $a7a032acae3ddda9$var$focusElement(element, scroll = false) {
342 if (element != null && !scroll) try {
343 $1c7f9157d722357d$export$80f3e147d781571c(element);
344 } catch (err) {
345 // ignore
346 }
347 else if (element != null) try {
348 element.focus();
349 } catch (err1) {
350 // ignore
351 }
352}
353function $a7a032acae3ddda9$var$focusFirstInScope(scope, tabbable = true) {
354 let sentinel = scope[0].previousElementSibling;
355 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
356 tabbable: tabbable
357 }, scope);
358 walker.currentNode = sentinel;
359 let nextNode = walker.nextNode();
360 // If the scope does not contain a tabbable element, use the first focusable element.
361 if (tabbable && !nextNode) {
362 walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
363 tabbable: false
364 }, scope);
365 walker.currentNode = sentinel;
366 nextNode = walker.nextNode();
367 }
368 $a7a032acae3ddda9$var$focusElement(nextNode);
369}
370function $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus) {
371 const autoFocusRef = ($parcel$interopDefault($aB6Cp$react)).useRef(autoFocus);
372 $aB6Cp$react.useEffect(()=>{
373 if (autoFocusRef.current) {
374 $a7a032acae3ddda9$var$activeScope = scopeRef;
375 if (!$a7a032acae3ddda9$var$isElementInScope(document.activeElement, $a7a032acae3ddda9$var$activeScope.current)) $a7a032acae3ddda9$var$focusFirstInScope(scopeRef.current);
376 }
377 autoFocusRef.current = false;
378 }, [
379 scopeRef
380 ]);
381}
382function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain) {
383 // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
384 const nodeToRestoreRef = $aB6Cp$react.useRef(typeof document !== 'undefined' ? document.activeElement : null);
385 // restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
386 // restoring-non-containing scopes should only care if they become active so they can perform the restore
387 $aB6Cp$reactariautils.useLayoutEffect(()=>{
388 let scope = scopeRef.current;
389 if (!restoreFocus || contain) return;
390 let onFocus = ()=>{
391 // If focusing an element in a child scope of the currently active scope, the child becomes active.
392 // Moving out of the active scope to an ancestor is not allowed.
393 if (!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) $a7a032acae3ddda9$var$activeScope = scopeRef;
394 };
395 document.addEventListener('focusin', onFocus, false);
396 scope.forEach((element)=>element.addEventListener('focusin', onFocus, false)
397 );
398 return ()=>{
399 document.removeEventListener('focusin', onFocus, false);
400 scope.forEach((element)=>element.removeEventListener('focusin', onFocus, false)
401 );
402 };
403 }, [
404 scopeRef,
405 contain
406 ]);
407 // useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
408 $aB6Cp$reactariautils.useLayoutEffect(()=>{
409 $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = nodeToRestoreRef.current;
410 if (!restoreFocus) return;
411 // Handle the Tab key so that tabbing out of the scope goes to the next element
412 // after the node that had focus when the scope mounted. This is important when
413 // using portals for overlays, so that focus goes to the expected element when
414 // tabbing out of the overlay.
415 let onKeyDown = (e)=>{
416 if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey) return;
417 let focusedElement = document.activeElement;
418 if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scopeRef.current)) return;
419 let nodeToRestore = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
420 // Create a DOM tree walker that matches all tabbable elements
421 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(document.body, {
422 tabbable: true
423 });
424 // Find the next tabbable element after the currently focused element
425 walker.currentNode = focusedElement;
426 let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
427 if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) {
428 nodeToRestore = null;
429 $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = null;
430 }
431 // If there is no next element, or it is outside the current scope, move focus to the
432 // next element after the node to restore to instead.
433 if ((!nextElement || !$a7a032acae3ddda9$var$isElementInScope(nextElement, scopeRef.current)) && nodeToRestore) {
434 walker.currentNode = nodeToRestore;
435 // Skip over elements within the scope, in case the scope immediately follows the node to restore.
436 do nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
437 while ($a7a032acae3ddda9$var$isElementInScope(nextElement, scopeRef.current))
438 e.preventDefault();
439 e.stopPropagation();
440 if (nextElement) $a7a032acae3ddda9$var$focusElement(nextElement, true);
441 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)
442 // then move focus to the body.
443 // Otherwise restore focus to the nodeToRestore (e.g menu within a popover -> tabbing to close the menu should move focus to menu trigger)
444 if (!$a7a032acae3ddda9$var$isElementInAnyScope(nodeToRestore)) focusedElement.blur();
445 else $a7a032acae3ddda9$var$focusElement(nodeToRestore, true);
446 }
447 };
448 if (!contain) document.addEventListener('keydown', onKeyDown, true);
449 return ()=>{
450 if (!contain) document.removeEventListener('keydown', onKeyDown, true);
451 let nodeToRestore = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
452 // if we already lost focus to the body and this was the active scope, then we should attempt to restore
453 if (restoreFocus && nodeToRestore && ($a7a032acae3ddda9$var$isElementInScope(document.activeElement, scopeRef.current) || document.activeElement === document.body && $a7a032acae3ddda9$var$activeScope === scopeRef)) {
454 // freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
455 let clonedTree = $a7a032acae3ddda9$export$d06fae2ee68b101e.clone();
456 requestAnimationFrame(()=>{
457 // Only restore focus if we've lost focus to the body, the alternative is that focus has been purposefully moved elsewhere
458 if (document.activeElement === document.body) {
459 // look up the tree starting with our scope to find a nodeToRestore still in the DOM
460 let treeNode = clonedTree.getTreeNode(scopeRef);
461 while(treeNode){
462 if (treeNode.nodeToRestore && document.body.contains(treeNode.nodeToRestore)) {
463 $a7a032acae3ddda9$var$focusElement(treeNode.nodeToRestore);
464 return;
465 }
466 treeNode = treeNode.parent;
467 }
468 }
469 });
470 }
471 };
472 }, [
473 scopeRef,
474 restoreFocus,
475 contain
476 ]);
477}
478function $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, opts, scope) {
479 let selector = (opts === null || opts === void 0 ? void 0 : opts.tabbable) ? $a7a032acae3ddda9$var$TABBABLE_ELEMENT_SELECTOR : $a7a032acae3ddda9$var$FOCUSABLE_ELEMENT_SELECTOR;
480 let walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
481 acceptNode (node) {
482 var ref;
483 // Skip nodes inside the starting node.
484 if (opts === null || opts === void 0 ? void 0 : (ref = opts.from) === null || ref === void 0 ? void 0 : ref.contains(node)) return NodeFilter.FILTER_REJECT;
485 if (node.matches(selector) && $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;
486 return NodeFilter.FILTER_SKIP;
487 }
488 });
489 if (opts === null || opts === void 0 ? void 0 : opts.from) walker.currentNode = opts.from;
490 return walker;
491}
492function $a7a032acae3ddda9$export$c5251b9e124bf29(ref, defaultOptions = {
493}) {
494 return {
495 focusNext (opts = {
496 }) {
497 let root = ref.current;
498 if (!root) return;
499 let { from: from , tabbable: tabbable = defaultOptions.tabbable , wrap: wrap = defaultOptions.wrap , accept: accept = defaultOptions.accept } = opts;
500 let node = from || document.activeElement;
501 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
502 tabbable: tabbable,
503 accept: accept
504 });
505 if (root.contains(node)) walker.currentNode = node;
506 let nextNode = walker.nextNode();
507 if (!nextNode && wrap) {
508 walker.currentNode = root;
509 nextNode = walker.nextNode();
510 }
511 if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
512 return nextNode;
513 },
514 focusPrevious (opts = defaultOptions) {
515 let root = ref.current;
516 if (!root) return;
517 let { from: from , tabbable: tabbable = defaultOptions.tabbable , wrap: wrap = defaultOptions.wrap , accept: accept = defaultOptions.accept } = opts;
518 let node = from || document.activeElement;
519 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
520 tabbable: tabbable,
521 accept: accept
522 });
523 if (root.contains(node)) walker.currentNode = node;
524 else {
525 let next = $a7a032acae3ddda9$var$last(walker);
526 if (next) $a7a032acae3ddda9$var$focusElement(next, true);
527 return next;
528 }
529 let previousNode = walker.previousNode();
530 if (!previousNode && wrap) {
531 walker.currentNode = root;
532 previousNode = $a7a032acae3ddda9$var$last(walker);
533 }
534 if (previousNode) $a7a032acae3ddda9$var$focusElement(previousNode, true);
535 return previousNode;
536 },
537 focusFirst (opts = defaultOptions) {
538 let root = ref.current;
539 if (!root) return;
540 let { tabbable: tabbable = defaultOptions.tabbable , accept: accept = defaultOptions.accept } = opts;
541 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
542 tabbable: tabbable,
543 accept: accept
544 });
545 let nextNode = walker.nextNode();
546 if (nextNode) $a7a032acae3ddda9$var$focusElement(nextNode, true);
547 return nextNode;
548 },
549 focusLast (opts = defaultOptions) {
550 let root = ref.current;
551 if (!root) return;
552 let { tabbable: tabbable = defaultOptions.tabbable , accept: accept = defaultOptions.accept } = opts;
553 let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
554 tabbable: tabbable,
555 accept: accept
556 });
557 let next = $a7a032acae3ddda9$var$last(walker);
558 if (next) $a7a032acae3ddda9$var$focusElement(next, true);
559 return next;
560 }
561 };
562}
563function $a7a032acae3ddda9$var$last(walker) {
564 let next;
565 let last;
566 do {
567 last = walker.lastChild();
568 if (last) next = last;
569 }while (last)
570 return next;
571}
572class $a7a032acae3ddda9$var$Tree {
573 get size() {
574 return this.fastMap.size;
575 }
576 getTreeNode(data) {
577 return this.fastMap.get(data);
578 }
579 addTreeNode(scopeRef, parent, nodeToRestore) {
580 let parentNode = this.fastMap.get(parent !== null && parent !== void 0 ? parent : null);
581 let node = new $a7a032acae3ddda9$var$TreeNode({
582 scopeRef: scopeRef
583 });
584 parentNode.addChild(node);
585 node.parent = parentNode;
586 this.fastMap.set(scopeRef, node);
587 if (nodeToRestore) node.nodeToRestore = nodeToRestore;
588 }
589 removeTreeNode(scopeRef) {
590 // never remove the root
591 if (scopeRef === null) return;
592 let node = this.fastMap.get(scopeRef);
593 let parentNode = node.parent;
594 // when we remove a scope, check if any sibling scopes are trying to restore focus to something inside the scope we're removing
595 // if we are, then replace the siblings restore with the restore from the scope we're removing
596 for (let current of this.traverse())if (current !== node && node.nodeToRestore && current.nodeToRestore && node.scopeRef.current && $a7a032acae3ddda9$var$isElementInScope(current.nodeToRestore, node.scopeRef.current)) current.nodeToRestore = node.nodeToRestore;
597 let children = node.children;
598 parentNode.removeChild(node);
599 if (children.length > 0) children.forEach((child)=>parentNode.addChild(child)
600 );
601 this.fastMap.delete(node.scopeRef);
602 }
603 // Pre Order Depth First
604 *traverse(node = this.root) {
605 if (node.scopeRef != null) yield node;
606 if (node.children.length > 0) for (let child of node.children)yield* this.traverse(child);
607 }
608 clone() {
609 let newTree = new $a7a032acae3ddda9$var$Tree();
610 for (let node of this.traverse())newTree.addTreeNode(node.scopeRef, node.parent.scopeRef, node.nodeToRestore);
611 return newTree;
612 }
613 constructor(){
614 this.fastMap = new Map();
615 this.root = new $a7a032acae3ddda9$var$TreeNode({
616 scopeRef: null
617 });
618 this.fastMap.set(null, this.root);
619 }
620}
621class $a7a032acae3ddda9$var$TreeNode {
622 addChild(node) {
623 this.children.push(node);
624 node.parent = this;
625 }
626 removeChild(node) {
627 this.children.splice(this.children.indexOf(node), 1);
628 node.parent = undefined;
629 }
630 constructor(props){
631 this.children = [];
632 this.contain = false;
633 this.scopeRef = props.scopeRef;
634 }
635}
636let $a7a032acae3ddda9$export$d06fae2ee68b101e = new $a7a032acae3ddda9$var$Tree();
637
638
639
640
641
642
643
644
645function $581a96d6eb128c1b$export$4e328f61c538687f(props = {
646}) {
647 let { autoFocus: autoFocus = false , isTextInput: isTextInput , within: within } = props;
648 let state = $aB6Cp$react.useRef({
649 isFocused: false,
650 isFocusVisible: autoFocus || $aB6Cp$reactariainteractions.isFocusVisible()
651 });
652 let [isFocused1, setFocused] = $aB6Cp$react.useState(false);
653 let [isFocusVisibleState, setFocusVisible] = $aB6Cp$react.useState(()=>state.current.isFocused && state.current.isFocusVisible
654 );
655 let updateState = $aB6Cp$react.useCallback(()=>setFocusVisible(state.current.isFocused && state.current.isFocusVisible)
656 , []);
657 let onFocusChange = $aB6Cp$react.useCallback((isFocused)=>{
658 state.current.isFocused = isFocused;
659 setFocused(isFocused);
660 updateState();
661 }, [
662 updateState
663 ]);
664 $aB6Cp$reactariainteractions.useFocusVisibleListener((isFocusVisible)=>{
665 state.current.isFocusVisible = isFocusVisible;
666 updateState();
667 }, [], {
668 isTextInput: isTextInput
669 });
670 let { focusProps: focusProps } = $aB6Cp$reactariainteractions.useFocus({
671 isDisabled: within,
672 onFocusChange: onFocusChange
673 });
674 let { focusWithinProps: focusWithinProps } = $aB6Cp$reactariainteractions.useFocusWithin({
675 isDisabled: !within,
676 onFocusWithinChange: onFocusChange
677 });
678 return {
679 isFocused: isFocused1,
680 isFocusVisible: state.current.isFocused && isFocusVisibleState,
681 focusProps: within ? focusWithinProps : focusProps
682 };
683}
684
685
686function $dfd8c70b928eb1b3$export$1a38b4ad7f578e1d(props) {
687 let { children: children , focusClass: focusClass , focusRingClass: focusRingClass } = props;
688 let { isFocused: isFocused , isFocusVisible: isFocusVisible , focusProps: focusProps } = $581a96d6eb128c1b$export$4e328f61c538687f(props);
689 let child = ($parcel$interopDefault($aB6Cp$react)).Children.only(children);
690 return(/*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).cloneElement(child, $aB6Cp$reactariautils.mergeProps(child.props, {
691 ...focusProps,
692 className: ($parcel$interopDefault($aB6Cp$clsx))({
693 [focusClass || '']: isFocused,
694 [focusRingClass || '']: isFocusVisible
695 })
696 })));
697}
698
699
700
701
702
703
704let $fb504d83237fd6ac$var$FocusableContext = /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createContext(null);
705function $fb504d83237fd6ac$var$useFocusableContext(ref) {
706 let context = $aB6Cp$react.useContext($fb504d83237fd6ac$var$FocusableContext) || {
707 };
708 $aB6Cp$reactariautils.useSyncRef(context, ref);
709 // eslint-disable-next-line
710 let { ref: _ , ...otherProps } = context;
711 return otherProps;
712}
713/**
714 * Provides DOM props to the nearest focusable child.
715 */ function $fb504d83237fd6ac$var$FocusableProvider(props, ref) {
716 let { children: children , ...otherProps } = props;
717 let context = {
718 ...otherProps,
719 ref: ref
720 };
721 return(/*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createElement($fb504d83237fd6ac$var$FocusableContext.Provider, {
722 value: context
723 }, children));
724}
725let $fb504d83237fd6ac$export$13f3202a3e5ddd5 = /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).forwardRef($fb504d83237fd6ac$var$FocusableProvider);
726function $fb504d83237fd6ac$export$4c014de7c8940b4c(props, domRef) {
727 let { focusProps: focusProps } = $aB6Cp$reactariainteractions.useFocus(props);
728 let { keyboardProps: keyboardProps } = $aB6Cp$reactariainteractions.useKeyboard(props);
729 let interactions = $aB6Cp$reactariautils.mergeProps(focusProps, keyboardProps);
730 let domProps = $fb504d83237fd6ac$var$useFocusableContext(domRef);
731 let interactionProps = props.isDisabled ? {
732 } : domProps;
733 let autoFocusRef = $aB6Cp$react.useRef(props.autoFocus);
734 $aB6Cp$react.useEffect(()=>{
735 if (autoFocusRef.current && domRef.current) $1c7f9157d722357d$export$80f3e147d781571c(domRef.current);
736 autoFocusRef.current = false;
737 }, [
738 domRef
739 ]);
740 return {
741 focusableProps: $aB6Cp$reactariautils.mergeProps({
742 ...interactions,
743 tabIndex: props.excludeFromTabOrder && !props.isDisabled ? -1 : undefined
744 }, interactionProps)
745 };
746}
747
748
749
750
751
752
753//# sourceMappingURL=main.js.map