@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 +208 -87
- package/dist/main.js.map +1 -1
- package/dist/module.js +197 -68
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +24 -24
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/FocusRing.tsx +1 -1
- package/src/FocusScope.tsx +298 -104
- package/src/focusSafely.ts +2 -1
- package/src/index.ts +10 -5
- package/src/useFocusRing.ts +6 -5
- package/src/useFocusable.tsx +10 -10
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
|
-
|
|
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
|
-
|
|
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$
|
|
97
|
-
$9bf71ea28793e738$
|
|
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)
|
|
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 ||
|
|
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 (
|
|
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 (
|
|
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 (
|
|
265
|
+
if ($9bf71ea28793e738$var$shouldContainFocus(scopeRef) && !$9bf71ea28793e738$var$isElementInChildScope(document.activeElement, scopeRef)) {
|
|
253
266
|
$9bf71ea28793e738$var$activeScope = scopeRef;
|
|
254
|
-
|
|
255
|
-
|
|
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 ()=>
|
|
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
|
-
|
|
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$
|
|
299
|
-
if (
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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:
|
|
340
|
+
tabbable: tabbable
|
|
325
341
|
}, scope);
|
|
326
342
|
walker.currentNode = sentinel;
|
|
327
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
383
|
-
|
|
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
|
|