@react-aria/focus 3.7.0 → 3.9.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 +225 -39
- package/dist/main.js.map +1 -1
- package/dist/module.js +226 -40
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/FocusScope.tsx +287 -55
package/dist/main.js
CHANGED
|
@@ -70,14 +70,21 @@ function $d5156037ad898a4d$export$e989c0fffaa6b27a(element, childElement) {
|
|
|
70
70
|
|
|
71
71
|
const $a7a032acae3ddda9$var$FocusContext = /*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createContext(null);
|
|
72
72
|
let $a7a032acae3ddda9$var$activeScope = null;
|
|
73
|
-
let $a7a032acae3ddda9$var$scopes = new Map();
|
|
74
73
|
function $a7a032acae3ddda9$export$20e40289641fbbb6(props) {
|
|
75
74
|
let { children: children , contain: contain , restoreFocus: restoreFocus , autoFocus: autoFocus } = props;
|
|
76
75
|
let startRef = $aB6Cp$react.useRef();
|
|
77
76
|
let endRef = $aB6Cp$react.useRef();
|
|
78
77
|
let scopeRef = $aB6Cp$react.useRef([]);
|
|
79
78
|
let ctx = $aB6Cp$react.useContext($a7a032acae3ddda9$var$FocusContext);
|
|
80
|
-
|
|
79
|
+
var ref;
|
|
80
|
+
// The parent scope is based on the JSX tree, using context.
|
|
81
|
+
// However, if a new scope mounts outside the active scope (e.g. DialogContainer launched from a menu),
|
|
82
|
+
// we want the parent scope to be the active scope instead.
|
|
83
|
+
let ctxParent = (ref = ctx === null || ctx === void 0 ? void 0 : ctx.scopeRef) !== null && ref !== void 0 ? ref : null;
|
|
84
|
+
let parentScope1 = $aB6Cp$react.useMemo(()=>$a7a032acae3ddda9$var$activeScope && $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope) && !$a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, ctxParent) ? $a7a032acae3ddda9$var$activeScope : ctxParent
|
|
85
|
+
, [
|
|
86
|
+
ctxParent
|
|
87
|
+
]);
|
|
81
88
|
$aB6Cp$reactariautils.useLayoutEffect(()=>{
|
|
82
89
|
// Find all rendered nodes between the sentinels and add them to the scope.
|
|
83
90
|
let node = startRef.current.nextSibling;
|
|
@@ -89,24 +96,32 @@ function $a7a032acae3ddda9$export$20e40289641fbbb6(props) {
|
|
|
89
96
|
scopeRef.current = nodes;
|
|
90
97
|
}, [
|
|
91
98
|
children,
|
|
92
|
-
|
|
99
|
+
parentScope1
|
|
93
100
|
]);
|
|
101
|
+
// add to the focus scope tree in render order because useEffects/useLayoutEffects run children first whereas render runs parent first
|
|
102
|
+
// which matters when constructing a tree
|
|
103
|
+
if ($a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(parentScope1) && !$a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef)) $a7a032acae3ddda9$export$d06fae2ee68b101e.addTreeNode(scopeRef, parentScope1);
|
|
104
|
+
let node1 = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef);
|
|
105
|
+
node1.contain = contain;
|
|
106
|
+
$a7a032acae3ddda9$var$useActiveScopeTracker(scopeRef, restoreFocus, contain);
|
|
107
|
+
$a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain);
|
|
108
|
+
$a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain);
|
|
109
|
+
$a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus);
|
|
110
|
+
// this layout effect needs to run last so that focusScopeTree cleanup happens at the last moment possible
|
|
94
111
|
$aB6Cp$reactariautils.useLayoutEffect(()=>{
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
if (scopeRef) return ()=>{
|
|
113
|
+
// Scope may have been re-parented.
|
|
114
|
+
let parentScope = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).parent.scopeRef;
|
|
97
115
|
// Restore the active scope on unmount if this scope or a descendant scope is active.
|
|
98
116
|
// Parent effect cleanups run before children, so we need to check if the
|
|
99
117
|
// parent scope actually still exists before restoring the active scope to it.
|
|
100
|
-
if ((scopeRef === $a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope(scopeRef, $a7a032acae3ddda9$var$activeScope)) && (!parentScope || $a7a032acae3ddda9$
|
|
101
|
-
$a7a032acae3ddda9$
|
|
118
|
+
if ((scopeRef === $a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope(scopeRef, $a7a032acae3ddda9$var$activeScope)) && (!parentScope || $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(parentScope))) $a7a032acae3ddda9$var$activeScope = parentScope;
|
|
119
|
+
$a7a032acae3ddda9$export$d06fae2ee68b101e.removeTreeNode(scopeRef);
|
|
102
120
|
};
|
|
103
121
|
}, [
|
|
104
122
|
scopeRef,
|
|
105
|
-
|
|
123
|
+
parentScope1
|
|
106
124
|
]);
|
|
107
|
-
$a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain);
|
|
108
|
-
$a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain);
|
|
109
|
-
$a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus);
|
|
110
125
|
let focusManager = $a7a032acae3ddda9$var$createFocusManagerForScope(scopeRef);
|
|
111
126
|
return(/*#__PURE__*/ ($parcel$interopDefault($aB6Cp$react)).createElement($a7a032acae3ddda9$var$FocusContext.Provider, {
|
|
112
127
|
value: {
|
|
@@ -216,6 +231,14 @@ const $a7a032acae3ddda9$var$TABBABLE_ELEMENT_SELECTOR = $a7a032acae3ddda9$var$fo
|
|
|
216
231
|
function $a7a032acae3ddda9$var$getScopeRoot(scope) {
|
|
217
232
|
return scope[0].parentElement;
|
|
218
233
|
}
|
|
234
|
+
function $a7a032acae3ddda9$var$shouldContainFocus(scopeRef) {
|
|
235
|
+
let scope = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope);
|
|
236
|
+
while(scope && scope.scopeRef !== scopeRef){
|
|
237
|
+
if (scope.contain) return false;
|
|
238
|
+
scope = scope.parent;
|
|
239
|
+
}
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
219
242
|
function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
|
|
220
243
|
let focusedNode = $aB6Cp$react.useRef();
|
|
221
244
|
let raf = $aB6Cp$react.useRef(null);
|
|
@@ -231,7 +254,7 @@ function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
|
|
|
231
254
|
}
|
|
232
255
|
// Handle the Tab key to contain focus within the scope
|
|
233
256
|
let onKeyDown = (e)=>{
|
|
234
|
-
if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey ||
|
|
257
|
+
if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey || !$a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) return;
|
|
235
258
|
let focusedElement = document.activeElement;
|
|
236
259
|
let scope = scopeRef.current;
|
|
237
260
|
if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scope)) return;
|
|
@@ -250,24 +273,26 @@ function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
|
|
|
250
273
|
let onFocus = (e)=>{
|
|
251
274
|
// If focusing an element in a child scope of the currently active scope, the child becomes active.
|
|
252
275
|
// Moving out of the active scope to an ancestor is not allowed.
|
|
253
|
-
if (!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) {
|
|
276
|
+
if ((!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) && $a7a032acae3ddda9$var$isElementInScope(e.target, scopeRef.current)) {
|
|
254
277
|
$a7a032acae3ddda9$var$activeScope = scopeRef;
|
|
255
278
|
focusedNode.current = e.target;
|
|
256
|
-
} else if (
|
|
279
|
+
} else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(e.target, scopeRef)) {
|
|
257
280
|
// If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
|
|
258
281
|
// restore focus to the previously focused node or the first tabbable element in the active scope.
|
|
259
282
|
if (focusedNode.current) focusedNode.current.focus();
|
|
260
283
|
else if ($a7a032acae3ddda9$var$activeScope) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
|
|
261
|
-
} else if (
|
|
284
|
+
} else if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef)) focusedNode.current = e.target;
|
|
262
285
|
};
|
|
263
286
|
let onBlur = (e)=>{
|
|
264
287
|
// Firefox doesn't shift focus back to the Dialog properly without this
|
|
265
288
|
raf.current = requestAnimationFrame(()=>{
|
|
266
289
|
// Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe
|
|
267
|
-
if (
|
|
290
|
+
if ($a7a032acae3ddda9$var$shouldContainFocus(scopeRef) && !$a7a032acae3ddda9$var$isElementInChildScope(document.activeElement, scopeRef)) {
|
|
268
291
|
$a7a032acae3ddda9$var$activeScope = scopeRef;
|
|
269
|
-
|
|
270
|
-
|
|
292
|
+
if (document.body.contains(e.target)) {
|
|
293
|
+
focusedNode.current = e.target;
|
|
294
|
+
focusedNode.current.focus();
|
|
295
|
+
} else if ($a7a032acae3ddda9$var$activeScope) $a7a032acae3ddda9$var$focusFirstInScope($a7a032acae3ddda9$var$activeScope.current);
|
|
271
296
|
}
|
|
272
297
|
});
|
|
273
298
|
};
|
|
@@ -299,28 +324,28 @@ function $a7a032acae3ddda9$var$useFocusContainment(scopeRef, contain) {
|
|
|
299
324
|
]);
|
|
300
325
|
}
|
|
301
326
|
function $a7a032acae3ddda9$var$isElementInAnyScope(element) {
|
|
302
|
-
|
|
303
|
-
if ($a7a032acae3ddda9$var$isElementInScope(element, scope.current)) return true;
|
|
304
|
-
}
|
|
305
|
-
return false;
|
|
327
|
+
return $a7a032acae3ddda9$var$isElementInChildScope(element);
|
|
306
328
|
}
|
|
307
329
|
function $a7a032acae3ddda9$var$isElementInScope(element, scope) {
|
|
308
330
|
return scope.some((node)=>node.contains(element)
|
|
309
331
|
);
|
|
310
332
|
}
|
|
311
|
-
function $a7a032acae3ddda9$var$isElementInChildScope(element, scope) {
|
|
333
|
+
function $a7a032acae3ddda9$var$isElementInChildScope(element, scope = null) {
|
|
312
334
|
// node.contains in isElementInScope covers child scopes that are also DOM children,
|
|
313
335
|
// but does not cover child scopes in portals.
|
|
314
|
-
for (let s of $a7a032acae3ddda9$
|
|
315
|
-
if (
|
|
336
|
+
for (let { scopeRef: s } of $a7a032acae3ddda9$export$d06fae2ee68b101e.traverse($a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scope))){
|
|
337
|
+
if ($a7a032acae3ddda9$var$isElementInScope(element, s.current)) return true;
|
|
316
338
|
}
|
|
317
339
|
return false;
|
|
318
340
|
}
|
|
319
341
|
function $a7a032acae3ddda9$var$isAncestorScope(ancestor, scope) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
var ref;
|
|
343
|
+
let parent = (ref = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scope)) === null || ref === void 0 ? void 0 : ref.parent;
|
|
344
|
+
while(parent){
|
|
345
|
+
if (parent.scopeRef === ancestor) return true;
|
|
346
|
+
parent = parent.parent;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
324
349
|
}
|
|
325
350
|
function $a7a032acae3ddda9$var$focusElement(element, scroll = false) {
|
|
326
351
|
if (element != null && !scroll) try {
|
|
@@ -334,13 +359,22 @@ function $a7a032acae3ddda9$var$focusElement(element, scroll = false) {
|
|
|
334
359
|
// ignore
|
|
335
360
|
}
|
|
336
361
|
}
|
|
337
|
-
function $a7a032acae3ddda9$var$focusFirstInScope(scope) {
|
|
362
|
+
function $a7a032acae3ddda9$var$focusFirstInScope(scope, tabbable = true) {
|
|
338
363
|
let sentinel = scope[0].previousElementSibling;
|
|
339
364
|
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
|
|
340
|
-
tabbable:
|
|
365
|
+
tabbable: tabbable
|
|
341
366
|
}, scope);
|
|
342
367
|
walker.currentNode = sentinel;
|
|
343
|
-
|
|
368
|
+
let nextNode = walker.nextNode();
|
|
369
|
+
// If the scope does not contain a tabbable element, use the first focusable element.
|
|
370
|
+
if (tabbable && !nextNode) {
|
|
371
|
+
walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa($a7a032acae3ddda9$var$getScopeRoot(scope), {
|
|
372
|
+
tabbable: false
|
|
373
|
+
}, scope);
|
|
374
|
+
walker.currentNode = sentinel;
|
|
375
|
+
nextNode = walker.nextNode();
|
|
376
|
+
}
|
|
377
|
+
$a7a032acae3ddda9$var$focusElement(nextNode);
|
|
344
378
|
}
|
|
345
379
|
function $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus) {
|
|
346
380
|
const autoFocusRef = ($parcel$interopDefault($aB6Cp$react)).useRef(autoFocus);
|
|
@@ -350,15 +384,72 @@ function $a7a032acae3ddda9$var$useAutoFocus(scopeRef, autoFocus) {
|
|
|
350
384
|
if (!$a7a032acae3ddda9$var$isElementInScope(document.activeElement, $a7a032acae3ddda9$var$activeScope.current)) $a7a032acae3ddda9$var$focusFirstInScope(scopeRef.current);
|
|
351
385
|
}
|
|
352
386
|
autoFocusRef.current = false;
|
|
353
|
-
}, [
|
|
387
|
+
}, [
|
|
388
|
+
scopeRef
|
|
389
|
+
]);
|
|
390
|
+
}
|
|
391
|
+
function $a7a032acae3ddda9$var$useActiveScopeTracker(scopeRef, restore, contain) {
|
|
392
|
+
// tracks the active scope, in case restore and contain are both false.
|
|
393
|
+
// if either are true, this is tracked in useRestoreFocus or useFocusContainment.
|
|
394
|
+
$aB6Cp$reactariautils.useLayoutEffect(()=>{
|
|
395
|
+
if (restore || contain) return;
|
|
396
|
+
let scope = scopeRef.current;
|
|
397
|
+
let onFocus = (e)=>{
|
|
398
|
+
let target = e.target;
|
|
399
|
+
if ($a7a032acae3ddda9$var$isElementInScope(target, scopeRef.current)) $a7a032acae3ddda9$var$activeScope = scopeRef;
|
|
400
|
+
else if (!$a7a032acae3ddda9$var$isElementInAnyScope(target)) $a7a032acae3ddda9$var$activeScope = null;
|
|
401
|
+
};
|
|
402
|
+
document.addEventListener('focusin', onFocus, false);
|
|
403
|
+
scope.forEach((element)=>element.addEventListener('focusin', onFocus, false)
|
|
404
|
+
);
|
|
405
|
+
return ()=>{
|
|
406
|
+
document.removeEventListener('focusin', onFocus, false);
|
|
407
|
+
scope.forEach((element)=>element.removeEventListener('focusin', onFocus, false)
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
}, [
|
|
411
|
+
scopeRef,
|
|
412
|
+
restore,
|
|
413
|
+
contain
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
function $a7a032acae3ddda9$var$shouldRestoreFocus(scopeRef) {
|
|
417
|
+
let scope = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode($a7a032acae3ddda9$var$activeScope);
|
|
418
|
+
while(scope && scope.scopeRef !== scopeRef){
|
|
419
|
+
if (scope.nodeToRestore) return false;
|
|
420
|
+
scope = scope.parent;
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
354
423
|
}
|
|
355
424
|
function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain) {
|
|
356
425
|
// create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
|
|
357
426
|
const nodeToRestoreRef = $aB6Cp$react.useRef(typeof document !== 'undefined' ? document.activeElement : null);
|
|
427
|
+
// restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
|
|
428
|
+
// restoring-non-containing scopes should only care if they become active so they can perform the restore
|
|
429
|
+
$aB6Cp$reactariautils.useLayoutEffect(()=>{
|
|
430
|
+
let scope = scopeRef.current;
|
|
431
|
+
if (!restoreFocus || contain) return;
|
|
432
|
+
let onFocus = ()=>{
|
|
433
|
+
// If focusing an element in a child scope of the currently active scope, the child becomes active.
|
|
434
|
+
// Moving out of the active scope to an ancestor is not allowed.
|
|
435
|
+
if (!$a7a032acae3ddda9$var$activeScope || $a7a032acae3ddda9$var$isAncestorScope($a7a032acae3ddda9$var$activeScope, scopeRef)) $a7a032acae3ddda9$var$activeScope = scopeRef;
|
|
436
|
+
};
|
|
437
|
+
document.addEventListener('focusin', onFocus, false);
|
|
438
|
+
scope.forEach((element)=>element.addEventListener('focusin', onFocus, false)
|
|
439
|
+
);
|
|
440
|
+
return ()=>{
|
|
441
|
+
document.removeEventListener('focusin', onFocus, false);
|
|
442
|
+
scope.forEach((element)=>element.removeEventListener('focusin', onFocus, false)
|
|
443
|
+
);
|
|
444
|
+
};
|
|
445
|
+
}, [
|
|
446
|
+
scopeRef,
|
|
447
|
+
contain
|
|
448
|
+
]);
|
|
358
449
|
// useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
|
|
359
450
|
$aB6Cp$reactariautils.useLayoutEffect(()=>{
|
|
360
|
-
let nodeToRestore = nodeToRestoreRef.current;
|
|
361
451
|
if (!restoreFocus) return;
|
|
452
|
+
$a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = nodeToRestoreRef.current;
|
|
362
453
|
// Handle the Tab key so that tabbing out of the scope goes to the next element
|
|
363
454
|
// after the node that had focus when the scope mounted. This is important when
|
|
364
455
|
// using portals for overlays, so that focus goes to the expected element when
|
|
@@ -367,6 +458,7 @@ function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain)
|
|
|
367
458
|
if (e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
368
459
|
let focusedElement = document.activeElement;
|
|
369
460
|
if (!$a7a032acae3ddda9$var$isElementInScope(focusedElement, scopeRef.current)) return;
|
|
461
|
+
let nodeToRestore = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
|
|
370
462
|
// Create a DOM tree walker that matches all tabbable elements
|
|
371
463
|
let walker = $a7a032acae3ddda9$export$2d6ec8fc375ceafa(document.body, {
|
|
372
464
|
tabbable: true
|
|
@@ -374,7 +466,10 @@ function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain)
|
|
|
374
466
|
// Find the next tabbable element after the currently focused element
|
|
375
467
|
walker.currentNode = focusedElement;
|
|
376
468
|
let nextElement = e.shiftKey ? walker.previousNode() : walker.nextNode();
|
|
377
|
-
if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body)
|
|
469
|
+
if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) {
|
|
470
|
+
nodeToRestore = null;
|
|
471
|
+
$a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore = null;
|
|
472
|
+
}
|
|
378
473
|
// If there is no next element, or it is outside the current scope, move focus to the
|
|
379
474
|
// next element after the node to restore to instead.
|
|
380
475
|
if ((!nextElement || !$a7a032acae3ddda9$var$isElementInScope(nextElement, scopeRef.current)) && nodeToRestore) {
|
|
@@ -395,10 +490,36 @@ function $a7a032acae3ddda9$var$useRestoreFocus(scopeRef, restoreFocus, contain)
|
|
|
395
490
|
if (!contain) document.addEventListener('keydown', onKeyDown, true);
|
|
396
491
|
return ()=>{
|
|
397
492
|
if (!contain) document.removeEventListener('keydown', onKeyDown, true);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
493
|
+
let nodeToRestore = $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(scopeRef).nodeToRestore;
|
|
494
|
+
// if we already lost focus to the body and this was the active scope, then we should attempt to restore
|
|
495
|
+
if (restoreFocus && nodeToRestore && ($a7a032acae3ddda9$var$isElementInScope(document.activeElement, scopeRef.current) || document.activeElement === document.body && $a7a032acae3ddda9$var$shouldRestoreFocus(scopeRef))) {
|
|
496
|
+
// freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
|
|
497
|
+
let clonedTree = $a7a032acae3ddda9$export$d06fae2ee68b101e.clone();
|
|
498
|
+
requestAnimationFrame(()=>{
|
|
499
|
+
// Only restore focus if we've lost focus to the body, the alternative is that focus has been purposefully moved elsewhere
|
|
500
|
+
if (document.activeElement === document.body) {
|
|
501
|
+
// look up the tree starting with our scope to find a nodeToRestore still in the DOM
|
|
502
|
+
let treeNode = clonedTree.getTreeNode(scopeRef);
|
|
503
|
+
while(treeNode){
|
|
504
|
+
if (treeNode.nodeToRestore && document.body.contains(treeNode.nodeToRestore)) {
|
|
505
|
+
$a7a032acae3ddda9$var$focusElement(treeNode.nodeToRestore);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
treeNode = treeNode.parent;
|
|
509
|
+
}
|
|
510
|
+
// If no nodeToRestore was found, focus the first element in the nearest
|
|
511
|
+
// ancestor scope that is still in the tree.
|
|
512
|
+
treeNode = clonedTree.getTreeNode(scopeRef);
|
|
513
|
+
while(treeNode){
|
|
514
|
+
if (treeNode.scopeRef && $a7a032acae3ddda9$export$d06fae2ee68b101e.getTreeNode(treeNode.scopeRef)) {
|
|
515
|
+
$a7a032acae3ddda9$var$focusFirstInScope(treeNode.scopeRef.current, true);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
treeNode = treeNode.parent;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
}
|
|
402
523
|
};
|
|
403
524
|
}, [
|
|
404
525
|
scopeRef,
|
|
@@ -500,6 +621,71 @@ function $a7a032acae3ddda9$var$last(walker) {
|
|
|
500
621
|
}while (last)
|
|
501
622
|
return next;
|
|
502
623
|
}
|
|
624
|
+
class $a7a032acae3ddda9$var$Tree {
|
|
625
|
+
get size() {
|
|
626
|
+
return this.fastMap.size;
|
|
627
|
+
}
|
|
628
|
+
getTreeNode(data) {
|
|
629
|
+
return this.fastMap.get(data);
|
|
630
|
+
}
|
|
631
|
+
addTreeNode(scopeRef, parent, nodeToRestore) {
|
|
632
|
+
let parentNode = this.fastMap.get(parent !== null && parent !== void 0 ? parent : null);
|
|
633
|
+
let node = new $a7a032acae3ddda9$var$TreeNode({
|
|
634
|
+
scopeRef: scopeRef
|
|
635
|
+
});
|
|
636
|
+
parentNode.addChild(node);
|
|
637
|
+
node.parent = parentNode;
|
|
638
|
+
this.fastMap.set(scopeRef, node);
|
|
639
|
+
if (nodeToRestore) node.nodeToRestore = nodeToRestore;
|
|
640
|
+
}
|
|
641
|
+
removeTreeNode(scopeRef) {
|
|
642
|
+
// never remove the root
|
|
643
|
+
if (scopeRef === null) return;
|
|
644
|
+
let node = this.fastMap.get(scopeRef);
|
|
645
|
+
let parentNode = node.parent;
|
|
646
|
+
// when we remove a scope, check if any sibling scopes are trying to restore focus to something inside the scope we're removing
|
|
647
|
+
// if we are, then replace the siblings restore with the restore from the scope we're removing
|
|
648
|
+
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;
|
|
649
|
+
let children = node.children;
|
|
650
|
+
parentNode.removeChild(node);
|
|
651
|
+
if (children.length > 0) children.forEach((child)=>parentNode.addChild(child)
|
|
652
|
+
);
|
|
653
|
+
this.fastMap.delete(node.scopeRef);
|
|
654
|
+
}
|
|
655
|
+
// Pre Order Depth First
|
|
656
|
+
*traverse(node = this.root) {
|
|
657
|
+
if (node.scopeRef != null) yield node;
|
|
658
|
+
if (node.children.length > 0) for (let child of node.children)yield* this.traverse(child);
|
|
659
|
+
}
|
|
660
|
+
clone() {
|
|
661
|
+
let newTree = new $a7a032acae3ddda9$var$Tree();
|
|
662
|
+
for (let node of this.traverse())newTree.addTreeNode(node.scopeRef, node.parent.scopeRef, node.nodeToRestore);
|
|
663
|
+
return newTree;
|
|
664
|
+
}
|
|
665
|
+
constructor(){
|
|
666
|
+
this.fastMap = new Map();
|
|
667
|
+
this.root = new $a7a032acae3ddda9$var$TreeNode({
|
|
668
|
+
scopeRef: null
|
|
669
|
+
});
|
|
670
|
+
this.fastMap.set(null, this.root);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
class $a7a032acae3ddda9$var$TreeNode {
|
|
674
|
+
addChild(node) {
|
|
675
|
+
this.children.push(node);
|
|
676
|
+
node.parent = this;
|
|
677
|
+
}
|
|
678
|
+
removeChild(node) {
|
|
679
|
+
this.children.splice(this.children.indexOf(node), 1);
|
|
680
|
+
node.parent = undefined;
|
|
681
|
+
}
|
|
682
|
+
constructor(props){
|
|
683
|
+
this.children = [];
|
|
684
|
+
this.contain = false;
|
|
685
|
+
this.scopeRef = props.scopeRef;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
let $a7a032acae3ddda9$export$d06fae2ee68b101e = new $a7a032acae3ddda9$var$Tree();
|
|
503
689
|
|
|
504
690
|
|
|
505
691
|
|