@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/main.js CHANGED
@@ -3,37 +3,22 @@ var $aB6Cp$reactariautils = require("@react-aria/utils");
3
3
  var $aB6Cp$reactariainteractions = require("@react-aria/interactions");
4
4
  var $aB6Cp$clsx = require("clsx");
5
5
 
6
- function $parcel$exportWildcard(dest, source) {
7
- Object.keys(source).forEach(function(key) {
8
- if (key === 'default' || key === '__esModule' || dest.hasOwnProperty(key)) {
9
- return;
10
- }
11
-
12
- Object.defineProperty(dest, key, {
13
- enumerable: true,
14
- get: function get() {
15
- return source[key];
16
- }
17
- });
18
- });
19
-
20
- return dest;
6
+ function $parcel$export(e, n, v, s) {
7
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
21
8
  }
22
9
  function $parcel$interopDefault(a) {
23
10
  return a && a.__esModule ? a.default : a;
24
11
  }
25
- function $parcel$export(e, n, v, s) {
26
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
27
- }
28
- var $a7a032acae3ddda9$exports = {};
29
-
30
- $parcel$export($a7a032acae3ddda9$exports, "FocusScope", () => $a7a032acae3ddda9$export$20e40289641fbbb6);
31
- $parcel$export($a7a032acae3ddda9$exports, "useFocusManager", () => $a7a032acae3ddda9$export$10c5169755ce7bd7);
32
- $parcel$export($a7a032acae3ddda9$exports, "getFocusableTreeWalker", () => $a7a032acae3ddda9$export$2d6ec8fc375ceafa);
33
- $parcel$export($a7a032acae3ddda9$exports, "createFocusManager", () => $a7a032acae3ddda9$export$c5251b9e124bf29);
34
- var $1c7f9157d722357d$exports = {};
35
12
 
36
- $parcel$export($1c7f9157d722357d$exports, "focusSafely", () => $1c7f9157d722357d$export$80f3e147d781571c);
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);
37
22
 
38
23
 
39
24
  function $1c7f9157d722357d$export$80f3e147d781571c(element) {
@@ -85,14 +70,15 @@ function $d5156037ad898a4d$export$e989c0fffaa6b27a(element, childElement) {
85
70
 
86
71
  const $a7a032acae3ddda9$var$FocusContext = /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createContext(null);
87
72
  let $a7a032acae3ddda9$var$activeScope = null;
88
- let $a7a032acae3ddda9$var$scopes = new Map();
89
73
  function $a7a032acae3ddda9$export$20e40289641fbbb6(props) {
90
74
  let { children: children , contain: contain , restoreFocus: restoreFocus , autoFocus: autoFocus } = props;
91
75
  let startRef = $aB6Cp$react.useRef();
92
76
  let endRef = $aB6Cp$react.useRef();
93
77
  let scopeRef = $aB6Cp$react.useRef([]);
94
78
  let ctx = $aB6Cp$react.useContext($a7a032acae3ddda9$var$FocusContext);
95
- let parentScope = ctx === null || ctx === void 0 ? void 0 : ctx.scopeRef;
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;
96
82
  $aB6Cp$reactariautils.useLayoutEffect(()=>{
97
83
  // Find all rendered nodes between the sentinels and add them to the scope.
98
84
  let node = startRef.current.nextSibling;
@@ -106,22 +92,27 @@ function $a7a032acae3ddda9$export$20e40289641fbbb6(props) {
106
92
  children,
107
93
  parentScope
108
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
109
104
  $aB6Cp$reactariautils.useLayoutEffect(()=>{
110
- $a7a032acae3ddda9$var$scopes.set(scopeRef, parentScope);
111
- return ()=>{
105
+ if (scopeRef && (parentScope || parentScope == null)) return ()=>{
112
106
  // Restore the active scope on unmount if this scope or a descendant scope is active.
113
107
  // Parent effect cleanups run before children, so we need to check if the
114
108
  // parent scope actually still exists before restoring the active scope to it.
115
- if ((scopeRef === $a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope(scopeRef, $a7a032acae3ddda9$var$activeScope)) && (!parentScope || $a7a032acae3ddda9$var$scopes.has(parentScope))) $a7a032acae3ddda9$var$activeScope = parentScope;
116
- $a7a032acae3ddda9$var$scopes.delete(scopeRef);
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);
117
111
  };
118
112
  }, [
119
113
  scopeRef,
120
114
  parentScope
121
115
  ]);
122
- $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain);
123
- $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain);
124
- $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus);
125
116
  let focusManager = $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef);
126
117
  return(/*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createElement($a7a032acae3ddda9$var$FocusContext.Provider, {
127
118
  value: {
@@ -147,11 +138,12 @@ function $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef) {
147
138
  focusNext (opts = {
148
139
  }) {
149
140
  let scope = scopeRef.current;
150
- let { from: from , tabbable: tabbable , wrap: wrap } = opts;
141
+ let { from: from , tabbable: tabbable , wrap: wrap , accept: accept } = opts;
151
142
  let node = from || document.activeElement;
152
143
  let sentinel = scope[0].previousElementSibling;
153
144
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
154
- tabbable: tabbable
145
+ tabbable: tabbable,
146
+ accept: accept
155
147
  }, scope);
156
148
  walker.currentNode = $a7a032acae3ddda9$var$isElementInScope(node, scope) ? node : sentinel;
157
149
  let nextNode = walker.nextNode();
@@ -165,11 +157,12 @@ function $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef) {
165
157
  focusPrevious (opts = {
166
158
  }) {
167
159
  let scope = scopeRef.current;
168
- let { from: from , tabbable: tabbable , wrap: wrap } = opts;
160
+ let { from: from , tabbable: tabbable , wrap: wrap , accept: accept } = opts;
169
161
  let node = from || document.activeElement;
170
162
  let sentinel = scope[scope.length - 1].nextElementSibling;
171
163
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
172
- tabbable: tabbable
164
+ tabbable: tabbable,
165
+ accept: accept
173
166
  }, scope);
174
167
  walker.currentNode = $a7a032acae3ddda9$var$isElementInScope(node, scope) ? node : sentinel;
175
168
  let previousNode = walker.previousNode();
@@ -183,9 +176,10 @@ function $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef) {
183
176
  focusFirst (opts = {
184
177
  }) {
185
178
  let scope = scopeRef.current;
186
- let { tabbable: tabbable } = opts;
179
+ let { tabbable: tabbable , accept: accept } = opts;
187
180
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
188
- tabbable: tabbable
181
+ tabbable: tabbable,
182
+ accept: accept
189
183
  }, scope);
190
184
  walker.currentNode = scope[0].previousElementSibling;
191
185
  let nextNode = walker.nextNode();
@@ -195,9 +189,10 @@ function $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef) {
195
189
  focusLast (opts = {
196
190
  }) {
197
191
  let scope = scopeRef.current;
198
- let { tabbable: tabbable } = opts;
192
+ let { tabbable: tabbable , accept: accept } = opts;
199
193
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
200
- tabbable: tabbable
194
+ tabbable: tabbable,
195
+ accept: accept
201
196
  }, scope);
202
197
  walker.currentNode = scope[scope.length - 1].nextElementSibling;
203
198
  let previousNode = walker.previousNode();
@@ -227,15 +222,30 @@ const $a7a032acae3ddda9$var$TABBABLE_ELEMENT_SELECTOR = $a7a032acae3ddda9$var$fo
227
222
  function $a7a032acae3ddda9$var$getScopeRoot(scope) {
228
223
  return scope[0].parentElement;
229
224
  }
225
+ function $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
+ }
230
233
  function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
231
234
  let focusedNode = $aB6Cp$react.useRef();
232
235
  let raf = $aB6Cp$react.useRef(null);
233
236
  $aB6Cp$reactariautils.useLayoutEffect(()=>{
234
237
  let scope1 = scopeRef.current;
235
- if (!contain) return;
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
+ }
236
246
  // Handle the Tab key to contain focus within the scope
237
247
  let onKeyDown = (e)=>{
238
- if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey || scopeRef !== $a7a032acae3ddda9$var$activeScope) return;
248
+ if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey || !$a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) return;
239
249
  let focusedElement = document.activeElement;
240
250
  let scope = scopeRef.current;
241
251
  if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scope)) return;
@@ -257,21 +267,23 @@ function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
257
267
  if (!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) {
258
268
  $a7a032acae3ddda9$var$activeScope = scopeRef;
259
269
  focusedNode.current = e.target;
260
- } else if (scopeRef === $a7a032acae3ddda9$var$activeScope && !$a7a032acae3ddda9$var$isElementInChildScope(e.target, scopeRef)) {
270
+ } else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(e.target, scopeRef)) {
261
271
  // If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
262
272
  // restore focus to the previously focused node or the first tabbable element in the active scope.
263
273
  if (focusedNode.current) focusedNode.current.focus();
264
274
  else if ($a7a032acae3ddda9$var$activeScope) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
265
- } else if (scopeRef === $a7a032acae3ddda9$var$activeScope) focusedNode.current = e.target;
275
+ } else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) focusedNode.current = e.target;
266
276
  };
267
277
  let onBlur = (e)=>{
268
278
  // Firefox doesn't shift focus back to the Dialog properly without this
269
279
  raf.current = requestAnimationFrame(()=>{
270
280
  // Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe
271
- if (scopeRef === $a7a032acae3ddda9$var$activeScope && !$a7a032acae3ddda9$var$isElementInChildScope(document.activeElement, scopeRef)) {
281
+ if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(document.activeElement, scopeRef)) {
272
282
  $a7a032acae3ddda9$var$activeScope = scopeRef;
273
- focusedNode.current = e.target;
274
- focusedNode.current.focus();
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);
275
287
  }
276
288
  });
277
289
  };
@@ -295,35 +307,36 @@ function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
295
307
  ]);
296
308
  // eslint-disable-next-line arrow-body-style
297
309
  $aB6Cp$react.useEffect(()=>{
298
- return ()=>cancelAnimationFrame(raf.current)
299
- ;
310
+ return ()=>{
311
+ if (raf.current) cancelAnimationFrame(raf.current);
312
+ };
300
313
  }, [
301
314
  raf
302
315
  ]);
303
316
  }
304
317
  function $a7a032acae3ddda9$var$isElementInAnyScope(element) {
305
- for (let scope of $a7a032acae3ddda9$var$scopes.keys()){
306
- if ($a7a032acae3ddda9$var$isElementInScope(element, scope.current)) return true;
307
- }
308
- return false;
318
+ return $a7a032acae3ddda9$var$isElementInChildScope(element);
309
319
  }
310
320
  function $a7a032acae3ddda9$var$isElementInScope(element, scope) {
311
321
  return scope.some((node)=>node.contains(element)
312
322
  );
313
323
  }
314
- function $a7a032acae3ddda9$var$isElementInChildScope(element, scope) {
324
+ function $a7a032acae3ddda9$var$isElementInChildScope(element, scope = null) {
315
325
  // node.contains in isElementInScope covers child scopes that are also DOM children,
316
326
  // but does not cover child scopes in portals.
317
- for (let s of $a7a032acae3ddda9$var$scopes.keys()){
318
- if ((s === scope || $a7a032acae3ddda9$var$isAncestorScope(scope, s)) && $a7a032acae3ddda9$var$isElementInScope(element, s.current)) return true;
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;
319
329
  }
320
330
  return false;
321
331
  }
322
332
  function $a7a032acae3ddda9$var$isAncestorScope(ancestor, scope) {
323
- let parent = $a7a032acae3ddda9$var$scopes.get(scope);
324
- if (!parent) return false;
325
- if (parent === ancestor) return true;
326
- return $a7a032acae3ddda9$var$isAncestorScope(ancestor, parent);
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;
327
340
  }
328
341
  function $a7a032acae3ddda9$var$focusElement(element, scroll = false) {
329
342
  if (element != null && !scroll) try {
@@ -337,13 +350,22 @@ function $a7a032acae3ddda9$var$focusElement(element, scroll = false) {
337
350
  // ignore
338
351
  }
339
352
  }
340
- function $a7a032acae3ddda9$var$focusFirstInScope(scope) {
353
+ function $a7a032acae3ddda9$var$focusFirstInScope(scope, tabbable = true) {
341
354
  let sentinel = scope[0].previousElementSibling;
342
355
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
343
- tabbable: true
356
+ tabbable: tabbable
344
357
  }, scope);
345
358
  walker.currentNode = sentinel;
346
- $a7a032acae3ddda9$var$focusElement(walker.nextNode());
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);
347
369
  }
348
370
  function $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus) {
349
371
  const autoFocusRef = ($parcel$interopDefault($aB6Cp$react)).useRef(autoFocus);
@@ -353,14 +375,38 @@ function $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus) {
353
375
  if (!$a7a032acae3ddda9$var$isElementInScope(document.activeElement, $a7a032acae3ddda9$var$activeScope.current)) $a7a032acae3ddda9$var$focusFirstInScope(scopeRef.current);
354
376
  }
355
377
  autoFocusRef.current = false;
356
- }, []);
378
+ }, [
379
+ scopeRef
380
+ ]);
357
381
  }
358
382
  function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain) {
359
383
  // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
360
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
+ ]);
361
407
  // useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
362
408
  $aB6Cp$reactariautils.useLayoutEffect(()=>{
363
- let nodeToRestore = nodeToRestoreRef.current;
409
+ $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = nodeToRestoreRef.current;
364
410
  if (!restoreFocus) return;
365
411
  // Handle the Tab key so that tabbing out of the scope goes to the next element
366
412
  // after the node that had focus when the scope mounted. This is important when
@@ -370,6 +416,7 @@ function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain)
370
416
  if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey) return;
371
417
  let focusedElement = document.activeElement;
372
418
  if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scopeRef.current)) return;
419
+ let nodeToRestore = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
373
420
  // Create a DOM tree walker that matches all tabbable elements
374
421
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(document.body, {
375
422
  tabbable: true
@@ -377,7 +424,10 @@ function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain)
377
424
  // Find the next tabbable element after the currently focused element
378
425
  walker.currentNode = focusedElement;
379
426
  let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
380
- if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) nodeToRestore = null;
427
+ if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) {
428
+ nodeToRestore = null;
429
+ $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = null;
430
+ }
381
431
  // If there is no next element, or it is outside the current scope, move focus to the
382
432
  // next element after the node to restore to instead.
383
433
  if ((!nextElement || !$a7a032acae3ddda9$var$isElementInScope(nextElement, scopeRef.current)) && nodeToRestore) {
@@ -398,9 +448,26 @@ function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain)
398
448
  if (!contain) document.addEventListener('keydown', onKeyDown, true);
399
449
  return ()=>{
400
450
  if (!contain) document.removeEventListener('keydown', onKeyDown, true);
401
- if (restoreFocus && nodeToRestore && $a7a032acae3ddda9$var$isElementInScope(document.activeElement, scopeRef.current)) requestAnimationFrame(()=>{
402
- if (document.body.contains(nodeToRestore)) $a7a032acae3ddda9$var$focusElement(nodeToRestore);
403
- });
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
+ }
404
471
  };
405
472
  }, [
406
473
  scopeRef,
@@ -428,6 +495,7 @@ function $a7a032acae3ddda9$export$c5251b9e124bf29(ref, defaultOptions = {
428
495
  focusNext (opts = {
429
496
  }) {
430
497
  let root = ref.current;
498
+ if (!root) return;
431
499
  let { from: from , tabbable: tabbable = defaultOptions.tabbable , wrap: wrap = defaultOptions.wrap , accept: accept = defaultOptions.accept } = opts;
432
500
  let node = from || document.activeElement;
433
501
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
@@ -445,6 +513,7 @@ function $a7a032acae3ddda9$export$c5251b9e124bf29(ref, defaultOptions = {
445
513
  },
446
514
  focusPrevious (opts = defaultOptions) {
447
515
  let root = ref.current;
516
+ if (!root) return;
448
517
  let { from: from , tabbable: tabbable = defaultOptions.tabbable , wrap: wrap = defaultOptions.wrap , accept: accept = defaultOptions.accept } = opts;
449
518
  let node = from || document.activeElement;
450
519
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
@@ -467,6 +536,7 @@ function $a7a032acae3ddda9$export$c5251b9e124bf29(ref, defaultOptions = {
467
536
  },
468
537
  focusFirst (opts = defaultOptions) {
469
538
  let root = ref.current;
539
+ if (!root) return;
470
540
  let { tabbable: tabbable = defaultOptions.tabbable , accept: accept = defaultOptions.accept } = opts;
471
541
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
472
542
  tabbable: tabbable,
@@ -478,6 +548,7 @@ function $a7a032acae3ddda9$export$c5251b9e124bf29(ref, defaultOptions = {
478
548
  },
479
549
  focusLast (opts = defaultOptions) {
480
550
  let root = ref.current;
551
+ if (!root) return;
481
552
  let { tabbable: tabbable = defaultOptions.tabbable , accept: accept = defaultOptions.accept } = opts;
482
553
  let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(root, {
483
554
  tabbable: tabbable,
@@ -498,17 +569,76 @@ function $a7a032acae3ddda9$var$last(walker) {
498
569
  }while (last)
499
570
  return next;
500
571
  }
572
+ class $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
+ }
621
+ class $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
+ }
636
+ let $a7a032acae3ddda9$export$d06fae2ee68b101e = new $a7a032acae3ddda9$var$Tree();
501
637
 
502
638
 
503
- var $dfd8c70b928eb1b3$exports = {};
504
-
505
- $parcel$export($dfd8c70b928eb1b3$exports, "FocusRing", () => $dfd8c70b928eb1b3$export$1a38b4ad7f578e1d);
506
-
507
639
 
508
640
 
509
- var $581a96d6eb128c1b$exports = {};
510
641
 
511
- $parcel$export($581a96d6eb128c1b$exports, "useFocusRing", () => $581a96d6eb128c1b$export$4e328f61c538687f);
512
642
 
513
643
 
514
644
 
@@ -567,10 +697,6 @@ function $dfd8c70b928eb1b3$export$1a38b4ad7f578e1d(props) {
567
697
  }
568
698
 
569
699
 
570
- var $fb504d83237fd6ac$exports = {};
571
-
572
- $parcel$export($fb504d83237fd6ac$exports, "FocusableProvider", () => $fb504d83237fd6ac$export$13f3202a3e5ddd5);
573
- $parcel$export($fb504d83237fd6ac$exports, "useFocusable", () => $fb504d83237fd6ac$export$4c014de7c8940b4c);
574
700
 
575
701
 
576
702
 
@@ -622,11 +748,6 @@ function $fb504d83237fd6ac$export$4c014de7c8940b4c(props, domRef) {
622
748
 
623
749
 
624
750
 
625
- $parcel$exportWildcard(module.exports, $a7a032acae3ddda9$exports);
626
- $parcel$exportWildcard(module.exports, $dfd8c70b928eb1b3$exports);
627
- $parcel$exportWildcard(module.exports, $fb504d83237fd6ac$exports);
628
- $parcel$exportWildcard(module.exports, $581a96d6eb128c1b$exports);
629
- $parcel$exportWildcard(module.exports, $1c7f9157d722357d$exports);
630
751
 
631
752
 
632
753
  //# sourceMappingURL=main.js.map