@react-aria/focus 3.6.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.js CHANGED
@@ -3,18 +3,6 @@ import {useLayoutEffect as $6nfFC$useLayoutEffect, runAfterTransition as $6nfFC$
3
3
  import {getInteractionModality as $6nfFC$getInteractionModality, isFocusVisible as $6nfFC$isFocusVisible, useFocusVisibleListener as $6nfFC$useFocusVisibleListener, useFocus as $6nfFC$useFocus, useFocusWithin as $6nfFC$useFocusWithin, useKeyboard as $6nfFC$useKeyboard} from "@react-aria/interactions";
4
4
  import $6nfFC$clsx from "clsx";
5
5
 
6
- function $parcel$export(e, n, v, s) {
7
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
8
- }
9
- var $9bf71ea28793e738$exports = {};
10
-
11
- $parcel$export($9bf71ea28793e738$exports, "FocusScope", () => $9bf71ea28793e738$export$20e40289641fbbb6);
12
- $parcel$export($9bf71ea28793e738$exports, "useFocusManager", () => $9bf71ea28793e738$export$10c5169755ce7bd7);
13
- $parcel$export($9bf71ea28793e738$exports, "getFocusableTreeWalker", () => $9bf71ea28793e738$export$2d6ec8fc375ceafa);
14
- $parcel$export($9bf71ea28793e738$exports, "createFocusManager", () => $9bf71ea28793e738$export$c5251b9e124bf29);
15
- var $6a99195332edec8b$exports = {};
16
-
17
- $parcel$export($6a99195332edec8b$exports, "focusSafely", () => $6a99195332edec8b$export$80f3e147d781571c);
18
6
 
19
7
 
20
8
  function $6a99195332edec8b$export$80f3e147d781571c(element) {
@@ -66,14 +54,15 @@ function $645f2e67b85a24c9$export$e989c0fffaa6b27a(element, childElement) {
66
54
 
67
55
  const $9bf71ea28793e738$var$FocusContext = /*#__PURE__*/ $6nfFC$react.createContext(null);
68
56
  let $9bf71ea28793e738$var$activeScope = null;
69
- let $9bf71ea28793e738$var$scopes = new Map();
70
57
  function $9bf71ea28793e738$export$20e40289641fbbb6(props) {
71
58
  let { children: children , contain: contain , restoreFocus: restoreFocus , autoFocus: autoFocus } = props;
72
59
  let startRef = $6nfFC$useRef();
73
60
  let endRef = $6nfFC$useRef();
74
61
  let scopeRef = $6nfFC$useRef([]);
75
62
  let ctx = $6nfFC$useContext($9bf71ea28793e738$var$FocusContext);
76
- let parentScope = ctx === null || ctx === void 0 ? void 0 : ctx.scopeRef;
63
+ var ref;
64
+ // if there is no scopeRef on the context, then the parent is the focusScopeTree's root, represented by null
65
+ let parentScope = (ref = ctx === null || ctx === void 0 ? void 0 : ctx.scopeRef) !== null && ref !== void 0 ? ref : null;
77
66
  $6nfFC$useLayoutEffect(()=>{
78
67
  // Find all rendered nodes between the sentinels and add them to the scope.
79
68
  let node = startRef.current.nextSibling;
@@ -87,22 +76,27 @@ function $9bf71ea28793e738$export$20e40289641fbbb6(props) {
87
76
  children,
88
77
  parentScope
89
78
  ]);
79
+ // add to the focus scope tree in render order because useEffects/useLayoutEffects run children first whereas render runs parent first
80
+ // which matters when constructing a tree
81
+ if ($9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(parentScope) && !$9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scopeRef)) $9bf71ea28793e738$export$d06fae2ee68b101e.addTreeNode(scopeRef, parentScope);
82
+ let node1 = $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scopeRef);
83
+ node1.contain = contain;
84
+ $9bf71ea28793e738$var$useFocusContainment(scopeRef, contain);
85
+ $9bf71ea28793e738$var$useRestoreFocus(scopeRef, restoreFocus, contain);
86
+ $9bf71ea28793e738$var$useAutoFocus(scopeRef, autoFocus);
87
+ // this layout effect needs to run last so that focusScopeTree cleanup happens at the last moment possible
90
88
  $6nfFC$useLayoutEffect(()=>{
91
- $9bf71ea28793e738$var$scopes.set(scopeRef, parentScope);
92
- return ()=>{
89
+ if (scopeRef && (parentScope || parentScope == null)) return ()=>{
93
90
  // Restore the active scope on unmount if this scope or a descendant scope is active.
94
91
  // Parent effect cleanups run before children, so we need to check if the
95
92
  // parent scope actually still exists before restoring the active scope to it.
96
- if ((scopeRef === $9bf71ea28793e738$var$activeScope || $9bf71ea28793e738$var$isAncestorScope(scopeRef, $9bf71ea28793e738$var$activeScope)) && (!parentScope || $9bf71ea28793e738$var$scopes.has(parentScope))) $9bf71ea28793e738$var$activeScope = parentScope;
97
- $9bf71ea28793e738$var$scopes.delete(scopeRef);
93
+ if ((scopeRef === $9bf71ea28793e738$var$activeScope || $9bf71ea28793e738$var$isAncestorScope(scopeRef, $9bf71ea28793e738$var$activeScope)) && (!parentScope || $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(parentScope))) $9bf71ea28793e738$var$activeScope = parentScope;
94
+ $9bf71ea28793e738$export$d06fae2ee68b101e.removeTreeNode(scopeRef);
98
95
  };
99
96
  }, [
100
97
  scopeRef,
101
98
  parentScope
102
99
  ]);
103
- $9bf71ea28793e738$var$useFocusContainment(scopeRef, contain);
104
- $9bf71ea28793e738$var$useRestoreFocus(scopeRef, restoreFocus, contain);
105
- $9bf71ea28793e738$var$useAutoFocus(scopeRef, autoFocus);
106
100
  let focusManager = $9bf71ea28793e738$var$createFocusManagerForScope(scopeRef);
107
101
  return(/*#__PURE__*/ $6nfFC$react.createElement($9bf71ea28793e738$var$FocusContext.Provider, {
108
102
  value: {
@@ -128,11 +122,12 @@ function $9bf71ea28793e738$var$createFocusManagerForScope(scopeRef) {
128
122
  focusNext (opts = {
129
123
  }) {
130
124
  let scope = scopeRef.current;
131
- let { from: from , tabbable: tabbable , wrap: wrap } = opts;
125
+ let { from: from , tabbable: tabbable , wrap: wrap , accept: accept } = opts;
132
126
  let node = from || document.activeElement;
133
127
  let sentinel = scope[0].previousElementSibling;
134
128
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa($9bf71ea28793e738$var$getScopeRoot(scope), {
135
- tabbable: tabbable
129
+ tabbable: tabbable,
130
+ accept: accept
136
131
  }, scope);
137
132
  walker.currentNode = $9bf71ea28793e738$var$isElementInScope(node, scope) ? node : sentinel;
138
133
  let nextNode = walker.nextNode();
@@ -146,11 +141,12 @@ function $9bf71ea28793e738$var$createFocusManagerForScope(scopeRef) {
146
141
  focusPrevious (opts = {
147
142
  }) {
148
143
  let scope = scopeRef.current;
149
- let { from: from , tabbable: tabbable , wrap: wrap } = opts;
144
+ let { from: from , tabbable: tabbable , wrap: wrap , accept: accept } = opts;
150
145
  let node = from || document.activeElement;
151
146
  let sentinel = scope[scope.length - 1].nextElementSibling;
152
147
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa($9bf71ea28793e738$var$getScopeRoot(scope), {
153
- tabbable: tabbable
148
+ tabbable: tabbable,
149
+ accept: accept
154
150
  }, scope);
155
151
  walker.currentNode = $9bf71ea28793e738$var$isElementInScope(node, scope) ? node : sentinel;
156
152
  let previousNode = walker.previousNode();
@@ -164,9 +160,10 @@ function $9bf71ea28793e738$var$createFocusManagerForScope(scopeRef) {
164
160
  focusFirst (opts = {
165
161
  }) {
166
162
  let scope = scopeRef.current;
167
- let { tabbable: tabbable } = opts;
163
+ let { tabbable: tabbable , accept: accept } = opts;
168
164
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa($9bf71ea28793e738$var$getScopeRoot(scope), {
169
- tabbable: tabbable
165
+ tabbable: tabbable,
166
+ accept: accept
170
167
  }, scope);
171
168
  walker.currentNode = scope[0].previousElementSibling;
172
169
  let nextNode = walker.nextNode();
@@ -176,9 +173,10 @@ function $9bf71ea28793e738$var$createFocusManagerForScope(scopeRef) {
176
173
  focusLast (opts = {
177
174
  }) {
178
175
  let scope = scopeRef.current;
179
- let { tabbable: tabbable } = opts;
176
+ let { tabbable: tabbable , accept: accept } = opts;
180
177
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa($9bf71ea28793e738$var$getScopeRoot(scope), {
181
- tabbable: tabbable
178
+ tabbable: tabbable,
179
+ accept: accept
182
180
  }, scope);
183
181
  walker.currentNode = scope[scope.length - 1].nextElementSibling;
184
182
  let previousNode = walker.previousNode();
@@ -208,15 +206,30 @@ const $9bf71ea28793e738$var$TABBABLE_ELEMENT_SELECTOR = $9bf71ea28793e738$var$fo
208
206
  function $9bf71ea28793e738$var$getScopeRoot(scope) {
209
207
  return scope[0].parentElement;
210
208
  }
209
+ function $9bf71ea28793e738$var$shouldContainFocus(scopeRef) {
210
+ let scope = $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode($9bf71ea28793e738$var$activeScope);
211
+ while(scope && scope.scopeRef !== scopeRef){
212
+ if (scope.contain) return false;
213
+ scope = scope.parent;
214
+ }
215
+ return true;
216
+ }
211
217
  function $9bf71ea28793e738$var$useFocusContainment(scopeRef, contain) {
212
218
  let focusedNode = $6nfFC$useRef();
213
219
  let raf = $6nfFC$useRef(null);
214
220
  $6nfFC$useLayoutEffect(()=>{
215
221
  let scope1 = scopeRef.current;
216
- if (!contain) return;
222
+ if (!contain) {
223
+ // if contain was changed, then we should cancel any ongoing waits to pull focus back into containment
224
+ if (raf.current) {
225
+ cancelAnimationFrame(raf.current);
226
+ raf.current = null;
227
+ }
228
+ return;
229
+ }
217
230
  // Handle the Tab key to contain focus within the scope
218
231
  let onKeyDown = (e)=>{
219
- if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey || scopeRef !== $9bf71ea28793e738$var$activeScope) return;
232
+ if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey || !$9bf71ea28793e738$var$shouldContainFocus(scopeRef)) return;
220
233
  let focusedElement = document.activeElement;
221
234
  let scope = scopeRef.current;
222
235
  if (!$9bf71ea28793e738$var$isElementInScope(focusedElement, scope)) return;
@@ -238,21 +251,23 @@ function $9bf71ea28793e738$var$useFocusContainment(scopeRef, contain) {
238
251
  if (!$9bf71ea28793e738$var$activeScope || $9bf71ea28793e738$var$isAncestorScope($9bf71ea28793e738$var$activeScope, scopeRef)) {
239
252
  $9bf71ea28793e738$var$activeScope = scopeRef;
240
253
  focusedNode.current = e.target;
241
- } else if (scopeRef === $9bf71ea28793e738$var$activeScope && !$9bf71ea28793e738$var$isElementInChildScope(e.target, scopeRef)) {
254
+ } else if ($9bf71ea28793e738$var$shouldContainFocus(scopeRef) && !$9bf71ea28793e738$var$isElementInChildScope(e.target, scopeRef)) {
242
255
  // If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
243
256
  // restore focus to the previously focused node or the first tabbable element in the active scope.
244
257
  if (focusedNode.current) focusedNode.current.focus();
245
258
  else if ($9bf71ea28793e738$var$activeScope) $9bf71ea28793e738$var$focusFirstInScope($9bf71ea28793e738$var$activeScope.current);
246
- } else if (scopeRef === $9bf71ea28793e738$var$activeScope) focusedNode.current = e.target;
259
+ } else if ($9bf71ea28793e738$var$shouldContainFocus(scopeRef)) focusedNode.current = e.target;
247
260
  };
248
261
  let onBlur = (e)=>{
249
262
  // Firefox doesn't shift focus back to the Dialog properly without this
250
263
  raf.current = requestAnimationFrame(()=>{
251
264
  // Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe
252
- if (scopeRef === $9bf71ea28793e738$var$activeScope && !$9bf71ea28793e738$var$isElementInChildScope(document.activeElement, scopeRef)) {
265
+ if ($9bf71ea28793e738$var$shouldContainFocus(scopeRef) && !$9bf71ea28793e738$var$isElementInChildScope(document.activeElement, scopeRef)) {
253
266
  $9bf71ea28793e738$var$activeScope = scopeRef;
254
- focusedNode.current = e.target;
255
- focusedNode.current.focus();
267
+ if (document.body.contains(e.target)) {
268
+ focusedNode.current = e.target;
269
+ focusedNode.current.focus();
270
+ } else if ($9bf71ea28793e738$var$activeScope) $9bf71ea28793e738$var$focusFirstInScope($9bf71ea28793e738$var$activeScope.current);
256
271
  }
257
272
  });
258
273
  };
@@ -276,35 +291,36 @@ function $9bf71ea28793e738$var$useFocusContainment(scopeRef, contain) {
276
291
  ]);
277
292
  // eslint-disable-next-line arrow-body-style
278
293
  $6nfFC$useEffect(()=>{
279
- return ()=>cancelAnimationFrame(raf.current)
280
- ;
294
+ return ()=>{
295
+ if (raf.current) cancelAnimationFrame(raf.current);
296
+ };
281
297
  }, [
282
298
  raf
283
299
  ]);
284
300
  }
285
301
  function $9bf71ea28793e738$var$isElementInAnyScope(element) {
286
- for (let scope of $9bf71ea28793e738$var$scopes.keys()){
287
- if ($9bf71ea28793e738$var$isElementInScope(element, scope.current)) return true;
288
- }
289
- return false;
302
+ return $9bf71ea28793e738$var$isElementInChildScope(element);
290
303
  }
291
304
  function $9bf71ea28793e738$var$isElementInScope(element, scope) {
292
305
  return scope.some((node)=>node.contains(element)
293
306
  );
294
307
  }
295
- function $9bf71ea28793e738$var$isElementInChildScope(element, scope) {
308
+ function $9bf71ea28793e738$var$isElementInChildScope(element, scope = null) {
296
309
  // node.contains in isElementInScope covers child scopes that are also DOM children,
297
310
  // but does not cover child scopes in portals.
298
- for (let s of $9bf71ea28793e738$var$scopes.keys()){
299
- if ((s === scope || $9bf71ea28793e738$var$isAncestorScope(scope, s)) && $9bf71ea28793e738$var$isElementInScope(element, s.current)) return true;
311
+ for (let { scopeRef: s } of $9bf71ea28793e738$export$d06fae2ee68b101e.traverse($9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scope))){
312
+ if ($9bf71ea28793e738$var$isElementInScope(element, s.current)) return true;
300
313
  }
301
314
  return false;
302
315
  }
303
316
  function $9bf71ea28793e738$var$isAncestorScope(ancestor, scope) {
304
- let parent = $9bf71ea28793e738$var$scopes.get(scope);
305
- if (!parent) return false;
306
- if (parent === ancestor) return true;
307
- return $9bf71ea28793e738$var$isAncestorScope(ancestor, parent);
317
+ var ref;
318
+ let parent = (ref = $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scope)) === null || ref === void 0 ? void 0 : ref.parent;
319
+ while(parent){
320
+ if (parent.scopeRef === ancestor) return true;
321
+ parent = parent.parent;
322
+ }
323
+ return false;
308
324
  }
309
325
  function $9bf71ea28793e738$var$focusElement(element, scroll = false) {
310
326
  if (element != null && !scroll) try {
@@ -318,13 +334,22 @@ function $9bf71ea28793e738$var$focusElement(element, scroll = false) {
318
334
  // ignore
319
335
  }
320
336
  }
321
- function $9bf71ea28793e738$var$focusFirstInScope(scope) {
337
+ function $9bf71ea28793e738$var$focusFirstInScope(scope, tabbable = true) {
322
338
  let sentinel = scope[0].previousElementSibling;
323
339
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa($9bf71ea28793e738$var$getScopeRoot(scope), {
324
- tabbable: true
340
+ tabbable: tabbable
325
341
  }, scope);
326
342
  walker.currentNode = sentinel;
327
- $9bf71ea28793e738$var$focusElement(walker.nextNode());
343
+ let nextNode = walker.nextNode();
344
+ // If the scope does not contain a tabbable element, use the first focusable element.
345
+ if (tabbable && !nextNode) {
346
+ walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa($9bf71ea28793e738$var$getScopeRoot(scope), {
347
+ tabbable: false
348
+ }, scope);
349
+ walker.currentNode = sentinel;
350
+ nextNode = walker.nextNode();
351
+ }
352
+ $9bf71ea28793e738$var$focusElement(nextNode);
328
353
  }
329
354
  function $9bf71ea28793e738$var$useAutoFocus(scopeRef, autoFocus) {
330
355
  const autoFocusRef = $6nfFC$react.useRef(autoFocus);
@@ -334,14 +359,38 @@ function $9bf71ea28793e738$var$useAutoFocus(scopeRef, autoFocus) {
334
359
  if (!$9bf71ea28793e738$var$isElementInScope(document.activeElement, $9bf71ea28793e738$var$activeScope.current)) $9bf71ea28793e738$var$focusFirstInScope(scopeRef.current);
335
360
  }
336
361
  autoFocusRef.current = false;
337
- }, []);
362
+ }, [
363
+ scopeRef
364
+ ]);
338
365
  }
339
366
  function $9bf71ea28793e738$var$useRestoreFocus(scopeRef, restoreFocus, contain) {
340
367
  // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
341
368
  const nodeToRestoreRef = $6nfFC$useRef(typeof document !== 'undefined' ? document.activeElement : null);
369
+ // restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
370
+ // restoring-non-containing scopes should only care if they become active so they can perform the restore
371
+ $6nfFC$useLayoutEffect(()=>{
372
+ let scope = scopeRef.current;
373
+ if (!restoreFocus || contain) return;
374
+ let onFocus = ()=>{
375
+ // If focusing an element in a child scope of the currently active scope, the child becomes active.
376
+ // Moving out of the active scope to an ancestor is not allowed.
377
+ if (!$9bf71ea28793e738$var$activeScope || $9bf71ea28793e738$var$isAncestorScope($9bf71ea28793e738$var$activeScope, scopeRef)) $9bf71ea28793e738$var$activeScope = scopeRef;
378
+ };
379
+ document.addEventListener('focusin', onFocus, false);
380
+ scope.forEach((element)=>element.addEventListener('focusin', onFocus, false)
381
+ );
382
+ return ()=>{
383
+ document.removeEventListener('focusin', onFocus, false);
384
+ scope.forEach((element)=>element.removeEventListener('focusin', onFocus, false)
385
+ );
386
+ };
387
+ }, [
388
+ scopeRef,
389
+ contain
390
+ ]);
342
391
  // useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
343
392
  $6nfFC$useLayoutEffect(()=>{
344
- let nodeToRestore = nodeToRestoreRef.current;
393
+ $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = nodeToRestoreRef.current;
345
394
  if (!restoreFocus) return;
346
395
  // Handle the Tab key so that tabbing out of the scope goes to the next element
347
396
  // after the node that had focus when the scope mounted. This is important when
@@ -351,6 +400,7 @@ function $9bf71ea28793e738$var$useRestoreFocus(scopeRef, restoreFocus, contain)
351
400
  if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey) return;
352
401
  let focusedElement = document.activeElement;
353
402
  if (!$9bf71ea28793e738$var$isElementInScope(focusedElement, scopeRef.current)) return;
403
+ let nodeToRestore = $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
354
404
  // Create a DOM tree walker that matches all tabbable elements
355
405
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa(document.body, {
356
406
  tabbable: true
@@ -358,7 +408,10 @@ function $9bf71ea28793e738$var$useRestoreFocus(scopeRef, restoreFocus, contain)
358
408
  // Find the next tabbable element after the currently focused element
359
409
  walker.currentNode = focusedElement;
360
410
  let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
361
- if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) nodeToRestore = null;
411
+ if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) {
412
+ nodeToRestore = null;
413
+ $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = null;
414
+ }
362
415
  // If there is no next element, or it is outside the current scope, move focus to the
363
416
  // next element after the node to restore to instead.
364
417
  if ((!nextElement || !$9bf71ea28793e738$var$isElementInScope(nextElement, scopeRef.current)) && nodeToRestore) {
@@ -379,9 +432,26 @@ function $9bf71ea28793e738$var$useRestoreFocus(scopeRef, restoreFocus, contain)
379
432
  if (!contain) document.addEventListener('keydown', onKeyDown, true);
380
433
  return ()=>{
381
434
  if (!contain) document.removeEventListener('keydown', onKeyDown, true);
382
- if (restoreFocus && nodeToRestore && $9bf71ea28793e738$var$isElementInScope(document.activeElement, scopeRef.current)) requestAnimationFrame(()=>{
383
- if (document.body.contains(nodeToRestore)) $9bf71ea28793e738$var$focusElement(nodeToRestore);
384
- });
435
+ let nodeToRestore = $9bf71ea28793e738$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
436
+ // if we already lost focus to the body and this was the active scope, then we should attempt to restore
437
+ if (restoreFocus && nodeToRestore && ($9bf71ea28793e738$var$isElementInScope(document.activeElement, scopeRef.current) || document.activeElement === document.body && $9bf71ea28793e738$var$activeScope === scopeRef)) {
438
+ // freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
439
+ let clonedTree = $9bf71ea28793e738$export$d06fae2ee68b101e.clone();
440
+ requestAnimationFrame(()=>{
441
+ // Only restore focus if we've lost focus to the body, the alternative is that focus has been purposefully moved elsewhere
442
+ if (document.activeElement === document.body) {
443
+ // look up the tree starting with our scope to find a nodeToRestore still in the DOM
444
+ let treeNode = clonedTree.getTreeNode(scopeRef);
445
+ while(treeNode){
446
+ if (treeNode.nodeToRestore && document.body.contains(treeNode.nodeToRestore)) {
447
+ $9bf71ea28793e738$var$focusElement(treeNode.nodeToRestore);
448
+ return;
449
+ }
450
+ treeNode = treeNode.parent;
451
+ }
452
+ }
453
+ });
454
+ }
385
455
  };
386
456
  }, [
387
457
  scopeRef,
@@ -409,6 +479,7 @@ function $9bf71ea28793e738$export$c5251b9e124bf29(ref, defaultOptions = {
409
479
  focusNext (opts = {
410
480
  }) {
411
481
  let root = ref.current;
482
+ if (!root) return;
412
483
  let { from: from , tabbable: tabbable = defaultOptions.tabbable , wrap: wrap = defaultOptions.wrap , accept: accept = defaultOptions.accept } = opts;
413
484
  let node = from || document.activeElement;
414
485
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa(root, {
@@ -426,6 +497,7 @@ function $9bf71ea28793e738$export$c5251b9e124bf29(ref, defaultOptions = {
426
497
  },
427
498
  focusPrevious (opts = defaultOptions) {
428
499
  let root = ref.current;
500
+ if (!root) return;
429
501
  let { from: from , tabbable: tabbable = defaultOptions.tabbable , wrap: wrap = defaultOptions.wrap , accept: accept = defaultOptions.accept } = opts;
430
502
  let node = from || document.activeElement;
431
503
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa(root, {
@@ -448,6 +520,7 @@ function $9bf71ea28793e738$export$c5251b9e124bf29(ref, defaultOptions = {
448
520
  },
449
521
  focusFirst (opts = defaultOptions) {
450
522
  let root = ref.current;
523
+ if (!root) return;
451
524
  let { tabbable: tabbable = defaultOptions.tabbable , accept: accept = defaultOptions.accept } = opts;
452
525
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa(root, {
453
526
  tabbable: tabbable,
@@ -459,6 +532,7 @@ function $9bf71ea28793e738$export$c5251b9e124bf29(ref, defaultOptions = {
459
532
  },
460
533
  focusLast (opts = defaultOptions) {
461
534
  let root = ref.current;
535
+ if (!root) return;
462
536
  let { tabbable: tabbable = defaultOptions.tabbable , accept: accept = defaultOptions.accept } = opts;
463
537
  let walker = $9bf71ea28793e738$export$2d6ec8fc375ceafa(root, {
464
538
  tabbable: tabbable,
@@ -479,17 +553,76 @@ function $9bf71ea28793e738$var$last(walker) {
479
553
  }while (last)
480
554
  return next;
481
555
  }
556
+ class $9bf71ea28793e738$var$Tree {
557
+ get size() {
558
+ return this.fastMap.size;
559
+ }
560
+ getTreeNode(data) {
561
+ return this.fastMap.get(data);
562
+ }
563
+ addTreeNode(scopeRef, parent, nodeToRestore) {
564
+ let parentNode = this.fastMap.get(parent !== null && parent !== void 0 ? parent : null);
565
+ let node = new $9bf71ea28793e738$var$TreeNode({
566
+ scopeRef: scopeRef
567
+ });
568
+ parentNode.addChild(node);
569
+ node.parent = parentNode;
570
+ this.fastMap.set(scopeRef, node);
571
+ if (nodeToRestore) node.nodeToRestore = nodeToRestore;
572
+ }
573
+ removeTreeNode(scopeRef) {
574
+ // never remove the root
575
+ if (scopeRef === null) return;
576
+ let node = this.fastMap.get(scopeRef);
577
+ let parentNode = node.parent;
578
+ // when we remove a scope, check if any sibling scopes are trying to restore focus to something inside the scope we're removing
579
+ // if we are, then replace the siblings restore with the restore from the scope we're removing
580
+ for (let current of this.traverse())if (current !== node && node.nodeToRestore && current.nodeToRestore && node.scopeRef.current && $9bf71ea28793e738$var$isElementInScope(current.nodeToRestore, node.scopeRef.current)) current.nodeToRestore = node.nodeToRestore;
581
+ let children = node.children;
582
+ parentNode.removeChild(node);
583
+ if (children.length > 0) children.forEach((child)=>parentNode.addChild(child)
584
+ );
585
+ this.fastMap.delete(node.scopeRef);
586
+ }
587
+ // Pre Order Depth First
588
+ *traverse(node = this.root) {
589
+ if (node.scopeRef != null) yield node;
590
+ if (node.children.length > 0) for (let child of node.children)yield* this.traverse(child);
591
+ }
592
+ clone() {
593
+ let newTree = new $9bf71ea28793e738$var$Tree();
594
+ for (let node of this.traverse())newTree.addTreeNode(node.scopeRef, node.parent.scopeRef, node.nodeToRestore);
595
+ return newTree;
596
+ }
597
+ constructor(){
598
+ this.fastMap = new Map();
599
+ this.root = new $9bf71ea28793e738$var$TreeNode({
600
+ scopeRef: null
601
+ });
602
+ this.fastMap.set(null, this.root);
603
+ }
604
+ }
605
+ class $9bf71ea28793e738$var$TreeNode {
606
+ addChild(node) {
607
+ this.children.push(node);
608
+ node.parent = this;
609
+ }
610
+ removeChild(node) {
611
+ this.children.splice(this.children.indexOf(node), 1);
612
+ node.parent = undefined;
613
+ }
614
+ constructor(props){
615
+ this.children = [];
616
+ this.contain = false;
617
+ this.scopeRef = props.scopeRef;
618
+ }
619
+ }
620
+ let $9bf71ea28793e738$export$d06fae2ee68b101e = new $9bf71ea28793e738$var$Tree();
482
621
 
483
622
 
484
- var $907718708eab68af$exports = {};
485
-
486
- $parcel$export($907718708eab68af$exports, "FocusRing", () => $907718708eab68af$export$1a38b4ad7f578e1d);
487
-
488
623
 
489
624
 
490
- var $f7dceffc5ad7768b$exports = {};
491
625
 
492
- $parcel$export($f7dceffc5ad7768b$exports, "useFocusRing", () => $f7dceffc5ad7768b$export$4e328f61c538687f);
493
626
 
494
627
 
495
628
 
@@ -548,10 +681,6 @@ function $907718708eab68af$export$1a38b4ad7f578e1d(props) {
548
681
  }
549
682
 
550
683
 
551
- var $e6afbd83fe6ebbd2$exports = {};
552
-
553
- $parcel$export($e6afbd83fe6ebbd2$exports, "FocusableProvider", () => $e6afbd83fe6ebbd2$export$13f3202a3e5ddd5);
554
- $parcel$export($e6afbd83fe6ebbd2$exports, "useFocusable", () => $e6afbd83fe6ebbd2$export$4c014de7c8940b4c);
555
684
 
556
685
 
557
686