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