@react-aria/focus 3.19.0 → 3.20.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/FocusScope.main.js +39 -46
- package/dist/FocusScope.main.js.map +1 -1
- package/dist/FocusScope.mjs +41 -47
- package/dist/FocusScope.module.js +41 -47
- package/dist/FocusScope.module.js.map +1 -1
- package/dist/import.mjs +6 -4
- package/dist/main.js +13 -6
- package/dist/main.js.map +1 -1
- package/dist/module.js +6 -4
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +10 -25
- package/dist/types.d.ts.map +1 -1
- package/dist/virtualFocus.main.js +46 -0
- package/dist/virtualFocus.main.js.map +1 -0
- package/dist/virtualFocus.mjs +38 -0
- package/dist/virtualFocus.module.js +38 -0
- package/dist/virtualFocus.module.js.map +1 -0
- package/package.json +7 -6
- package/src/FocusScope.tsx +66 -62
- package/src/index.ts +6 -4
- package/src/virtualFocus.ts +33 -0
- package/dist/focusSafely.main.js +0 -39
- package/dist/focusSafely.main.js.map +0 -1
- package/dist/focusSafely.mjs +0 -34
- package/dist/focusSafely.module.js +0 -34
- package/dist/focusSafely.module.js.map +0 -1
- package/dist/useFocusable.main.js +0 -75
- package/dist/useFocusable.main.js.map +0 -1
- package/dist/useFocusable.mjs +0 -65
- package/dist/useFocusable.module.js +0 -65
- package/dist/useFocusable.module.js.map +0 -1
- package/src/focusSafely.ts +0 -39
- package/src/useFocusable.tsx +0 -97
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":";;;AAEO,SAAS,0CAAiB,EAAkB;IACjD,IAAI,OAAO,0CAA2B,CAAA,GAAA,uBAAe,EAAE;IACvD,IAAI,SAAS,IAAI;QACf,IAAI,MACF,0CAAoB,MAAM;QAE5B,IAAI,IACF,0CAAqB,IAAI;IAE7B;AACF;AAEO,SAAS,0CAAoB,IAAa,EAAE,EAAkB;IACnE,KAAK,aAAa,CAAC,IAAI,WAAW,QAAQ;QAAC,eAAe;IAAE;IAC5D,KAAK,aAAa,CAAC,IAAI,WAAW,YAAY;QAAC,SAAS;QAAM,eAAe;IAAE;AACjF;AAEO,SAAS,0CAAqB,EAAW,EAAE,IAAoB;IACpE,GAAG,aAAa,CAAC,IAAI,WAAW,SAAS;QAAC,eAAe;IAAI;IAC7D,GAAG,aAAa,CAAC,IAAI,WAAW,WAAW;QAAC,SAAS;QAAM,eAAe;IAAI;AAChF;AAEO,SAAS,0CAA2B,QAAkB;IAC3D,IAAI,gBAAgB,CAAA,GAAA,uBAAe,EAAE;IACrC,IAAI,mBAAmB,0BAAA,oCAAA,cAAe,YAAY,CAAC;IACnD,IAAI,kBACF,OAAO,SAAS,cAAc,CAAC,qBAAqB;IAGtD,OAAO;AACT","sources":["packages/@react-aria/focus/src/virtualFocus.ts"],"sourcesContent":["import {getActiveElement, getOwnerDocument} from '@react-aria/utils';\n\nexport function moveVirtualFocus(to: Element | null) {\n let from = getVirtuallyFocusedElement(getOwnerDocument(to));\n if (from !== to) {\n if (from) {\n dispatchVirtualBlur(from, to);\n }\n if (to) {\n dispatchVirtualFocus(to, from);\n }\n }\n}\n\nexport function dispatchVirtualBlur(from: Element, to: Element | null) {\n from.dispatchEvent(new FocusEvent('blur', {relatedTarget: to}));\n from.dispatchEvent(new FocusEvent('focusout', {bubbles: true, relatedTarget: to}));\n}\n\nexport function dispatchVirtualFocus(to: Element, from: Element | null) {\n to.dispatchEvent(new FocusEvent('focus', {relatedTarget: from}));\n to.dispatchEvent(new FocusEvent('focusin', {bubbles: true, relatedTarget: from}));\n}\n\nexport function getVirtuallyFocusedElement(document: Document) {\n let activeElement = getActiveElement(document);\n let activeDescendant = activeElement?.getAttribute('aria-activedescendant');\n if (activeDescendant) {\n return document.getElementById(activeDescendant) || activeElement;\n }\n\n return activeElement;\n}\n"],"names":[],"version":3,"file":"virtualFocus.module.js.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/focus",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.0",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -22,17 +22,18 @@
|
|
|
22
22
|
"url": "https://github.com/adobe/react-spectrum"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@react-aria/interactions": "^3.
|
|
26
|
-
"@react-aria/utils": "^3.
|
|
27
|
-
"@react-types/shared": "^3.
|
|
25
|
+
"@react-aria/interactions": "^3.24.0",
|
|
26
|
+
"@react-aria/utils": "^3.28.0",
|
|
27
|
+
"@react-types/shared": "^3.28.0",
|
|
28
28
|
"@swc/helpers": "^0.5.0",
|
|
29
29
|
"clsx": "^2.0.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
|
32
|
+
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
|
33
|
+
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
|
33
34
|
},
|
|
34
35
|
"publishConfig": {
|
|
35
36
|
"access": "public"
|
|
36
37
|
},
|
|
37
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "4d3c72c94eea2d72eb3a0e7d56000c6ef7e39726"
|
|
38
39
|
}
|
package/src/FocusScope.tsx
CHANGED
|
@@ -10,9 +10,20 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import {
|
|
14
|
+
createShadowTreeWalker,
|
|
15
|
+
getActiveElement,
|
|
16
|
+
getEventTarget,
|
|
17
|
+
getOwnerDocument,
|
|
18
|
+
isAndroid,
|
|
19
|
+
isChrome,
|
|
20
|
+
isFocusable,
|
|
21
|
+
isTabbable,
|
|
22
|
+
ShadowTreeWalker,
|
|
23
|
+
useLayoutEffect
|
|
24
|
+
} from '@react-aria/utils';
|
|
13
25
|
import {FocusableElement, RefObject} from '@react-types/shared';
|
|
14
|
-
import {focusSafely} from '
|
|
15
|
-
import {getOwnerDocument, useLayoutEffect} from '@react-aria/utils';
|
|
26
|
+
import {focusSafely, getInteractionModality} from '@react-aria/interactions';
|
|
16
27
|
import {isElementVisible} from './isElementVisible';
|
|
17
28
|
import React, {ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
|
|
18
29
|
|
|
@@ -54,7 +65,7 @@ export interface FocusManager {
|
|
|
54
65
|
focusPrevious(opts?: FocusManagerOptions): FocusableElement | null,
|
|
55
66
|
/** Moves focus to the first focusable or tabbable element in the focus scope. */
|
|
56
67
|
focusFirst(opts?: FocusManagerOptions): FocusableElement | null,
|
|
57
|
-
|
|
68
|
+
/** Moves focus to the last focusable or tabbable element in the focus scope. */
|
|
58
69
|
focusLast(opts?: FocusManagerOptions): FocusableElement | null
|
|
59
70
|
}
|
|
60
71
|
|
|
@@ -143,7 +154,7 @@ export function FocusScope(props: FocusScopeProps) {
|
|
|
143
154
|
// This needs to be an effect so that activeScope is updated after the FocusScope tree is complete.
|
|
144
155
|
// It cannot be a useLayoutEffect because the parent of this node hasn't been attached in the tree yet.
|
|
145
156
|
useEffect(() => {
|
|
146
|
-
const activeElement = getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined)
|
|
157
|
+
const activeElement = getActiveElement(getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined));
|
|
147
158
|
let scope: TreeNode | null = null;
|
|
148
159
|
|
|
149
160
|
if (isElementInScope(activeElement, scopeRef.current)) {
|
|
@@ -207,7 +218,7 @@ function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>)
|
|
|
207
218
|
focusNext(opts: FocusManagerOptions = {}) {
|
|
208
219
|
let scope = scopeRef.current!;
|
|
209
220
|
let {from, tabbable, wrap, accept} = opts;
|
|
210
|
-
let node = from || getOwnerDocument(scope[0])
|
|
221
|
+
let node = from || getActiveElement(getOwnerDocument(scope[0] ?? undefined))!;
|
|
211
222
|
let sentinel = scope[0].previousElementSibling!;
|
|
212
223
|
let scopeRoot = getScopeRoot(scope);
|
|
213
224
|
let walker = getFocusableTreeWalker(scopeRoot, {tabbable, accept}, scope);
|
|
@@ -225,11 +236,11 @@ function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>)
|
|
|
225
236
|
focusPrevious(opts: FocusManagerOptions = {}) {
|
|
226
237
|
let scope = scopeRef.current!;
|
|
227
238
|
let {from, tabbable, wrap, accept} = opts;
|
|
228
|
-
let node = from || getOwnerDocument(scope[0])
|
|
239
|
+
let node = from || getActiveElement(getOwnerDocument(scope[0] ?? undefined))!;
|
|
229
240
|
let sentinel = scope[scope.length - 1].nextElementSibling!;
|
|
230
241
|
let scopeRoot = getScopeRoot(scope);
|
|
231
242
|
let walker = getFocusableTreeWalker(scopeRoot, {tabbable, accept}, scope);
|
|
232
|
-
walker.currentNode = isElementInScope(node, scope) ? node
|
|
243
|
+
walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
|
|
233
244
|
let previousNode = walker.previousNode() as FocusableElement;
|
|
234
245
|
if (!previousNode && wrap) {
|
|
235
246
|
walker.currentNode = sentinel;
|
|
@@ -267,31 +278,6 @@ function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>)
|
|
|
267
278
|
};
|
|
268
279
|
}
|
|
269
280
|
|
|
270
|
-
const focusableElements = [
|
|
271
|
-
'input:not([disabled]):not([type=hidden])',
|
|
272
|
-
'select:not([disabled])',
|
|
273
|
-
'textarea:not([disabled])',
|
|
274
|
-
'button:not([disabled])',
|
|
275
|
-
'a[href]',
|
|
276
|
-
'area[href]',
|
|
277
|
-
'summary',
|
|
278
|
-
'iframe',
|
|
279
|
-
'object',
|
|
280
|
-
'embed',
|
|
281
|
-
'audio[controls]',
|
|
282
|
-
'video[controls]',
|
|
283
|
-
'[contenteditable]'
|
|
284
|
-
];
|
|
285
|
-
|
|
286
|
-
const FOCUSABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]),') + ',[tabindex]:not([disabled]):not([hidden])';
|
|
287
|
-
|
|
288
|
-
focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])');
|
|
289
|
-
const TABBABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]):not([tabindex="-1"]),');
|
|
290
|
-
|
|
291
|
-
export function isFocusable(element: HTMLElement) {
|
|
292
|
-
return element.matches(FOCUSABLE_ELEMENT_SELECTOR);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
281
|
function getScopeRoot(scope: Element[]) {
|
|
296
282
|
return scope[0].parentElement!;
|
|
297
283
|
}
|
|
@@ -332,7 +318,7 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
|
|
|
332
318
|
return;
|
|
333
319
|
}
|
|
334
320
|
|
|
335
|
-
let focusedElement = ownerDocument
|
|
321
|
+
let focusedElement = getActiveElement(ownerDocument);
|
|
336
322
|
let scope = scopeRef.current;
|
|
337
323
|
if (!scope || !isElementInScope(focusedElement, scope)) {
|
|
338
324
|
return;
|
|
@@ -356,13 +342,13 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
|
|
|
356
342
|
}
|
|
357
343
|
};
|
|
358
344
|
|
|
359
|
-
let onFocus = (e) => {
|
|
345
|
+
let onFocus: EventListener = (e) => {
|
|
360
346
|
// If focusing an element in a child scope of the currently active scope, the child becomes active.
|
|
361
347
|
// Moving out of the active scope to an ancestor is not allowed.
|
|
362
|
-
if ((!activeScope || isAncestorScope(activeScope, scopeRef)) && isElementInScope(e
|
|
348
|
+
if ((!activeScope || isAncestorScope(activeScope, scopeRef)) && isElementInScope(getEventTarget(e) as Element, scopeRef.current)) {
|
|
363
349
|
activeScope = scopeRef;
|
|
364
|
-
focusedNode.current = e
|
|
365
|
-
} else if (shouldContainFocus(scopeRef) && !isElementInChildScope(e
|
|
350
|
+
focusedNode.current = getEventTarget(e) as FocusableElement;
|
|
351
|
+
} else if (shouldContainFocus(scopeRef) && !isElementInChildScope(getEventTarget(e) as Element, scopeRef)) {
|
|
366
352
|
// If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
|
|
367
353
|
// restore focus to the previously focused node or the first tabbable element in the active scope.
|
|
368
354
|
if (focusedNode.current) {
|
|
@@ -371,21 +357,29 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
|
|
|
371
357
|
focusFirstInScope(activeScope.current);
|
|
372
358
|
}
|
|
373
359
|
} else if (shouldContainFocus(scopeRef)) {
|
|
374
|
-
focusedNode.current = e
|
|
360
|
+
focusedNode.current = getEventTarget(e) as FocusableElement;
|
|
375
361
|
}
|
|
376
362
|
};
|
|
377
363
|
|
|
378
|
-
let onBlur = (e) => {
|
|
364
|
+
let onBlur: EventListener = (e) => {
|
|
379
365
|
// Firefox doesn't shift focus back to the Dialog properly without this
|
|
380
366
|
if (raf.current) {
|
|
381
367
|
cancelAnimationFrame(raf.current);
|
|
382
368
|
}
|
|
383
369
|
raf.current = requestAnimationFrame(() => {
|
|
370
|
+
// Patches infinite focus coersion loop for Android Talkback where the user isn't able to move the virtual cursor
|
|
371
|
+
// if within a containing focus scope. Bug filed against Chrome: https://issuetracker.google.com/issues/384844019.
|
|
372
|
+
// Note that this means focus can leave focus containing modals due to this, but it is isolated to Chrome Talkback.
|
|
373
|
+
let modality = getInteractionModality();
|
|
374
|
+
let shouldSkipFocusRestore = (modality === 'virtual' || modality === null) && isAndroid() && isChrome();
|
|
375
|
+
|
|
384
376
|
// Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe
|
|
385
|
-
|
|
377
|
+
let activeElement = getActiveElement(ownerDocument);
|
|
378
|
+
if (!shouldSkipFocusRestore && activeElement && shouldContainFocus(scopeRef) && !isElementInChildScope(activeElement, scopeRef)) {
|
|
386
379
|
activeScope = scopeRef;
|
|
387
|
-
|
|
388
|
-
|
|
380
|
+
let target = getEventTarget(e) as FocusableElement;
|
|
381
|
+
if (target && target.isConnected) {
|
|
382
|
+
focusedNode.current = target;
|
|
389
383
|
focusedNode.current?.focus();
|
|
390
384
|
} else if (activeScope.current) {
|
|
391
385
|
focusFirstInScope(activeScope.current);
|
|
@@ -508,7 +502,7 @@ function useAutoFocus(scopeRef: RefObject<Element[] | null>, autoFocus?: boolean
|
|
|
508
502
|
if (autoFocusRef.current) {
|
|
509
503
|
activeScope = scopeRef;
|
|
510
504
|
const ownerDocument = getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined);
|
|
511
|
-
if (!isElementInScope(ownerDocument
|
|
505
|
+
if (!isElementInScope(getActiveElement(ownerDocument), activeScope.current) && scopeRef.current) {
|
|
512
506
|
focusFirstInScope(scopeRef.current);
|
|
513
507
|
}
|
|
514
508
|
}
|
|
@@ -528,7 +522,7 @@ function useActiveScopeTracker(scopeRef: RefObject<Element[] | null>, restore?:
|
|
|
528
522
|
const ownerDocument = getOwnerDocument(scope ? scope[0] : undefined);
|
|
529
523
|
|
|
530
524
|
let onFocus = (e) => {
|
|
531
|
-
let target = e
|
|
525
|
+
let target = getEventTarget(e) as Element;
|
|
532
526
|
if (isElementInScope(target, scopeRef.current)) {
|
|
533
527
|
activeScope = scopeRef;
|
|
534
528
|
} else if (!isElementInAnyScope(target)) {
|
|
@@ -561,7 +555,7 @@ function shouldRestoreFocus(scopeRef: ScopeRef) {
|
|
|
561
555
|
function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: boolean, contain?: boolean) {
|
|
562
556
|
// create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
|
|
563
557
|
// eslint-disable-next-line no-restricted-globals
|
|
564
|
-
const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined)
|
|
558
|
+
const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? getActiveElement(getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined)) as FocusableElement : null);
|
|
565
559
|
|
|
566
560
|
// restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
|
|
567
561
|
// restoring-non-containing scopes should only care if they become active so they can perform the restore
|
|
@@ -576,7 +570,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
|
|
|
576
570
|
// If focusing an element in a child scope of the currently active scope, the child becomes active.
|
|
577
571
|
// Moving out of the active scope to an ancestor is not allowed.
|
|
578
572
|
if ((!activeScope || isAncestorScope(activeScope, scopeRef)) &&
|
|
579
|
-
|
|
573
|
+
isElementInScope(getActiveElement(ownerDocument), scopeRef.current)
|
|
580
574
|
) {
|
|
581
575
|
activeScope = scopeRef;
|
|
582
576
|
}
|
|
@@ -588,7 +582,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
|
|
|
588
582
|
ownerDocument.removeEventListener('focusin', onFocus, false);
|
|
589
583
|
scope?.forEach(element => element.removeEventListener('focusin', onFocus, false));
|
|
590
584
|
};
|
|
591
|
-
|
|
585
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
592
586
|
}, [scopeRef, contain]);
|
|
593
587
|
|
|
594
588
|
useLayoutEffect(() => {
|
|
@@ -624,7 +618,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
|
|
|
624
618
|
walker.currentNode = focusedElement;
|
|
625
619
|
let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
|
|
626
620
|
|
|
627
|
-
if (!nodeToRestore || !
|
|
621
|
+
if (!nodeToRestore || !nodeToRestore.isConnected || nodeToRestore === ownerDocument.body) {
|
|
628
622
|
nodeToRestore = undefined;
|
|
629
623
|
treeNode.nodeToRestore = undefined;
|
|
630
624
|
}
|
|
@@ -644,9 +638,9 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
|
|
|
644
638
|
if (nextElement) {
|
|
645
639
|
focusElement(nextElement, true);
|
|
646
640
|
} else {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
641
|
+
// If there is no next element and the nodeToRestore isn't within a FocusScope (i.e. we are leaving the top level focus scope)
|
|
642
|
+
// then move focus to the body.
|
|
643
|
+
// Otherwise restore focus to the nodeToRestore (e.g menu within a popover -> tabbing to close the menu should move focus to menu trigger)
|
|
650
644
|
if (!isElementInAnyScope(nodeToRestore)) {
|
|
651
645
|
focusedElement.blur();
|
|
652
646
|
} else {
|
|
@@ -657,12 +651,12 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
|
|
|
657
651
|
};
|
|
658
652
|
|
|
659
653
|
if (!contain) {
|
|
660
|
-
ownerDocument.addEventListener('keydown', onKeyDown, true);
|
|
654
|
+
ownerDocument.addEventListener('keydown', onKeyDown as EventListener, true);
|
|
661
655
|
}
|
|
662
656
|
|
|
663
657
|
return () => {
|
|
664
658
|
if (!contain) {
|
|
665
|
-
ownerDocument.removeEventListener('keydown', onKeyDown, true);
|
|
659
|
+
ownerDocument.removeEventListener('keydown', onKeyDown as EventListener, true);
|
|
666
660
|
}
|
|
667
661
|
};
|
|
668
662
|
}, [scopeRef, restoreFocus, contain]);
|
|
@@ -688,11 +682,12 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
|
|
|
688
682
|
let nodeToRestore = treeNode.nodeToRestore;
|
|
689
683
|
|
|
690
684
|
// if we already lost focus to the body and this was the active scope, then we should attempt to restore
|
|
685
|
+
let activeElement = getActiveElement(ownerDocument);
|
|
691
686
|
if (
|
|
692
687
|
restoreFocus
|
|
693
688
|
&& nodeToRestore
|
|
694
689
|
&& (
|
|
695
|
-
((
|
|
690
|
+
((activeElement && isElementInChildScope(activeElement, scopeRef)) || (activeElement === ownerDocument.body && shouldRestoreFocus(scopeRef)))
|
|
696
691
|
)
|
|
697
692
|
) {
|
|
698
693
|
// freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
|
|
@@ -741,10 +736,19 @@ function restoreFocusToElement(node: FocusableElement) {
|
|
|
741
736
|
* Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker}
|
|
742
737
|
* that matches all focusable/tabbable elements.
|
|
743
738
|
*/
|
|
744
|
-
export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]) {
|
|
745
|
-
let
|
|
746
|
-
|
|
747
|
-
|
|
739
|
+
export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]): ShadowTreeWalker | TreeWalker {
|
|
740
|
+
let filter = opts?.tabbable ? isTabbable : isFocusable;
|
|
741
|
+
|
|
742
|
+
// Ensure that root is an Element or fall back appropriately
|
|
743
|
+
let rootElement = root?.nodeType === Node.ELEMENT_NODE ? (root as Element) : null;
|
|
744
|
+
|
|
745
|
+
// Determine the document to use
|
|
746
|
+
let doc = getOwnerDocument(rootElement);
|
|
747
|
+
|
|
748
|
+
// Create a TreeWalker, ensuring the root is an Element or Document
|
|
749
|
+
let walker = createShadowTreeWalker(
|
|
750
|
+
doc,
|
|
751
|
+
root || doc,
|
|
748
752
|
NodeFilter.SHOW_ELEMENT,
|
|
749
753
|
{
|
|
750
754
|
acceptNode(node) {
|
|
@@ -753,7 +757,7 @@ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions
|
|
|
753
757
|
return NodeFilter.FILTER_REJECT;
|
|
754
758
|
}
|
|
755
759
|
|
|
756
|
-
if ((node as Element)
|
|
760
|
+
if (filter(node as Element)
|
|
757
761
|
&& isElementVisible(node as Element)
|
|
758
762
|
&& (!scope || isElementInScope(node as Element, scope))
|
|
759
763
|
&& (!opts?.accept || opts.accept(node as Element))
|
|
@@ -784,7 +788,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
|
|
|
784
788
|
return null;
|
|
785
789
|
}
|
|
786
790
|
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
|
|
787
|
-
let node = from || getOwnerDocument(root)
|
|
791
|
+
let node = from || getActiveElement(getOwnerDocument(root));
|
|
788
792
|
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
789
793
|
if (root.contains(node)) {
|
|
790
794
|
walker.currentNode = node!;
|
|
@@ -805,7 +809,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
|
|
|
805
809
|
return null;
|
|
806
810
|
}
|
|
807
811
|
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
|
|
808
|
-
let node = from || getOwnerDocument(root)
|
|
812
|
+
let node = from || getActiveElement(getOwnerDocument(root));
|
|
809
813
|
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
810
814
|
if (root.contains(node)) {
|
|
811
815
|
walker.currentNode = node!;
|
|
@@ -860,7 +864,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
|
|
|
860
864
|
};
|
|
861
865
|
}
|
|
862
866
|
|
|
863
|
-
function last(walker: TreeWalker) {
|
|
867
|
+
function last(walker: ShadowTreeWalker | TreeWalker) {
|
|
864
868
|
let next: FocusableElement | undefined = undefined;
|
|
865
869
|
let last: FocusableElement;
|
|
866
870
|
do {
|
package/src/index.ts
CHANGED
|
@@ -10,14 +10,16 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
export {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager, isElementInChildOfActiveScope
|
|
13
|
+
export {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager, isElementInChildOfActiveScope} from './FocusScope';
|
|
14
14
|
export {FocusRing} from './FocusRing';
|
|
15
|
-
export {FocusableProvider, useFocusable} from './useFocusable';
|
|
16
15
|
export {useFocusRing} from './useFocusRing';
|
|
17
|
-
export {focusSafely} from './focusSafely';
|
|
18
16
|
export {useHasTabbableChild} from './useHasTabbableChild';
|
|
17
|
+
export {moveVirtualFocus, dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement} from './virtualFocus';
|
|
18
|
+
// For backward compatibility.
|
|
19
|
+
export {isFocusable} from '@react-aria/utils';
|
|
20
|
+
export {FocusableProvider, Focusable, useFocusable, focusSafely} from '@react-aria/interactions';
|
|
19
21
|
|
|
20
22
|
export type {FocusScopeProps, FocusManager, FocusManagerOptions} from './FocusScope';
|
|
21
23
|
export type {FocusRingProps} from './FocusRing';
|
|
22
|
-
export type {FocusableAria, FocusableOptions, FocusableProviderProps} from '
|
|
24
|
+
export type {FocusableAria, FocusableOptions, FocusableProviderProps} from '@react-aria/interactions';
|
|
23
25
|
export type {AriaFocusRingProps, FocusRingAria} from './useFocusRing';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {getActiveElement, getOwnerDocument} from '@react-aria/utils';
|
|
2
|
+
|
|
3
|
+
export function moveVirtualFocus(to: Element | null) {
|
|
4
|
+
let from = getVirtuallyFocusedElement(getOwnerDocument(to));
|
|
5
|
+
if (from !== to) {
|
|
6
|
+
if (from) {
|
|
7
|
+
dispatchVirtualBlur(from, to);
|
|
8
|
+
}
|
|
9
|
+
if (to) {
|
|
10
|
+
dispatchVirtualFocus(to, from);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function dispatchVirtualBlur(from: Element, to: Element | null) {
|
|
16
|
+
from.dispatchEvent(new FocusEvent('blur', {relatedTarget: to}));
|
|
17
|
+
from.dispatchEvent(new FocusEvent('focusout', {bubbles: true, relatedTarget: to}));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function dispatchVirtualFocus(to: Element, from: Element | null) {
|
|
21
|
+
to.dispatchEvent(new FocusEvent('focus', {relatedTarget: from}));
|
|
22
|
+
to.dispatchEvent(new FocusEvent('focusin', {bubbles: true, relatedTarget: from}));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getVirtuallyFocusedElement(document: Document) {
|
|
26
|
+
let activeElement = getActiveElement(document);
|
|
27
|
+
let activeDescendant = activeElement?.getAttribute('aria-activedescendant');
|
|
28
|
+
if (activeDescendant) {
|
|
29
|
+
return document.getElementById(activeDescendant) || activeElement;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return activeElement;
|
|
33
|
+
}
|
package/dist/focusSafely.main.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
var $cBYLt$reactariautils = require("@react-aria/utils");
|
|
2
|
-
var $cBYLt$reactariainteractions = require("@react-aria/interactions");
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function $parcel$export(e, n, v, s) {
|
|
6
|
-
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
$parcel$export(module.exports, "focusSafely", () => $1c7f9157d722357d$export$80f3e147d781571c);
|
|
10
|
-
/*
|
|
11
|
-
* Copyright 2020 Adobe. All rights reserved.
|
|
12
|
-
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
|
|
13
|
-
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
14
|
-
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
15
|
-
*
|
|
16
|
-
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
17
|
-
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
18
|
-
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
19
|
-
* governing permissions and limitations under the License.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
function $1c7f9157d722357d$export$80f3e147d781571c(element) {
|
|
23
|
-
// If the user is interacting with a virtual cursor, e.g. screen reader, then
|
|
24
|
-
// wait until after any animated transitions that are currently occurring on
|
|
25
|
-
// the page before shifting focus. This avoids issues with VoiceOver on iOS
|
|
26
|
-
// causing the page to scroll when moving focus if the element is transitioning
|
|
27
|
-
// from off the screen.
|
|
28
|
-
const ownerDocument = (0, $cBYLt$reactariautils.getOwnerDocument)(element);
|
|
29
|
-
if ((0, $cBYLt$reactariainteractions.getInteractionModality)() === 'virtual') {
|
|
30
|
-
let lastFocusedElement = ownerDocument.activeElement;
|
|
31
|
-
(0, $cBYLt$reactariautils.runAfterTransition)(()=>{
|
|
32
|
-
// If focus did not move and the element is still in the document, focus it.
|
|
33
|
-
if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $cBYLt$reactariautils.focusWithoutScrolling)(element);
|
|
34
|
-
});
|
|
35
|
-
} else (0, $cBYLt$reactariautils.focusWithoutScrolling)(element);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
//# sourceMappingURL=focusSafely.main.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":";;;;;;;;;AAAA;;;;;;;;;;CAUC;;AAUM,SAAS,0CAAY,OAAyB;IACnD,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,+EAA+E;IAC/E,uBAAuB;IACvB,MAAM,gBAAgB,CAAA,GAAA,sCAAe,EAAE;IACvC,IAAI,CAAA,GAAA,mDAAqB,QAAQ,WAAW;QAC1C,IAAI,qBAAqB,cAAc,aAAa;QACpD,CAAA,GAAA,wCAAiB,EAAE;YACjB,4EAA4E;YAC5E,IAAI,cAAc,aAAa,KAAK,sBAAsB,QAAQ,WAAW,EAC3E,CAAA,GAAA,2CAAoB,EAAE;QAE1B;IACF,OACE,CAAA,GAAA,2CAAoB,EAAE;AAE1B","sources":["packages/@react-aria/focus/src/focusSafely.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {FocusableElement} from '@react-types/shared';\nimport {focusWithoutScrolling, getOwnerDocument, runAfterTransition} from '@react-aria/utils';\nimport {getInteractionModality} from '@react-aria/interactions';\n\n/**\n * A utility function that focuses an element while avoiding undesired side effects such\n * as page scrolling and screen reader issues with CSS transitions.\n */\nexport function focusSafely(element: FocusableElement) {\n // If the user is interacting with a virtual cursor, e.g. screen reader, then\n // wait until after any animated transitions that are currently occurring on\n // the page before shifting focus. This avoids issues with VoiceOver on iOS\n // causing the page to scroll when moving focus if the element is transitioning\n // from off the screen.\n const ownerDocument = getOwnerDocument(element);\n if (getInteractionModality() === 'virtual') {\n let lastFocusedElement = ownerDocument.activeElement;\n runAfterTransition(() => {\n // If focus did not move and the element is still in the document, focus it.\n if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) {\n focusWithoutScrolling(element);\n }\n });\n } else {\n focusWithoutScrolling(element);\n }\n}\n"],"names":[],"version":3,"file":"focusSafely.main.js.map"}
|
package/dist/focusSafely.mjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {getOwnerDocument as $jlOai$getOwnerDocument, runAfterTransition as $jlOai$runAfterTransition, focusWithoutScrolling as $jlOai$focusWithoutScrolling} from "@react-aria/utils";
|
|
2
|
-
import {getInteractionModality as $jlOai$getInteractionModality} from "@react-aria/interactions";
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
* Copyright 2020 Adobe. All rights reserved.
|
|
6
|
-
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
|
|
7
|
-
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
8
|
-
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
11
|
-
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
12
|
-
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
13
|
-
* governing permissions and limitations under the License.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
function $6a99195332edec8b$export$80f3e147d781571c(element) {
|
|
17
|
-
// If the user is interacting with a virtual cursor, e.g. screen reader, then
|
|
18
|
-
// wait until after any animated transitions that are currently occurring on
|
|
19
|
-
// the page before shifting focus. This avoids issues with VoiceOver on iOS
|
|
20
|
-
// causing the page to scroll when moving focus if the element is transitioning
|
|
21
|
-
// from off the screen.
|
|
22
|
-
const ownerDocument = (0, $jlOai$getOwnerDocument)(element);
|
|
23
|
-
if ((0, $jlOai$getInteractionModality)() === 'virtual') {
|
|
24
|
-
let lastFocusedElement = ownerDocument.activeElement;
|
|
25
|
-
(0, $jlOai$runAfterTransition)(()=>{
|
|
26
|
-
// If focus did not move and the element is still in the document, focus it.
|
|
27
|
-
if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $jlOai$focusWithoutScrolling)(element);
|
|
28
|
-
});
|
|
29
|
-
} else (0, $jlOai$focusWithoutScrolling)(element);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export {$6a99195332edec8b$export$80f3e147d781571c as focusSafely};
|
|
34
|
-
//# sourceMappingURL=focusSafely.module.js.map
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {getOwnerDocument as $jlOai$getOwnerDocument, runAfterTransition as $jlOai$runAfterTransition, focusWithoutScrolling as $jlOai$focusWithoutScrolling} from "@react-aria/utils";
|
|
2
|
-
import {getInteractionModality as $jlOai$getInteractionModality} from "@react-aria/interactions";
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
* Copyright 2020 Adobe. All rights reserved.
|
|
6
|
-
* This file is licensed to you under the Apache License, Version 2.0 (the 'License');
|
|
7
|
-
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
8
|
-
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
11
|
-
* the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
12
|
-
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
13
|
-
* governing permissions and limitations under the License.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
function $6a99195332edec8b$export$80f3e147d781571c(element) {
|
|
17
|
-
// If the user is interacting with a virtual cursor, e.g. screen reader, then
|
|
18
|
-
// wait until after any animated transitions that are currently occurring on
|
|
19
|
-
// the page before shifting focus. This avoids issues with VoiceOver on iOS
|
|
20
|
-
// causing the page to scroll when moving focus if the element is transitioning
|
|
21
|
-
// from off the screen.
|
|
22
|
-
const ownerDocument = (0, $jlOai$getOwnerDocument)(element);
|
|
23
|
-
if ((0, $jlOai$getInteractionModality)() === 'virtual') {
|
|
24
|
-
let lastFocusedElement = ownerDocument.activeElement;
|
|
25
|
-
(0, $jlOai$runAfterTransition)(()=>{
|
|
26
|
-
// If focus did not move and the element is still in the document, focus it.
|
|
27
|
-
if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $jlOai$focusWithoutScrolling)(element);
|
|
28
|
-
});
|
|
29
|
-
} else (0, $jlOai$focusWithoutScrolling)(element);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export {$6a99195332edec8b$export$80f3e147d781571c as focusSafely};
|
|
34
|
-
//# sourceMappingURL=focusSafely.module.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":";;;AAAA;;;;;;;;;;CAUC;;AAUM,SAAS,0CAAY,OAAyB;IACnD,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,+EAA+E;IAC/E,uBAAuB;IACvB,MAAM,gBAAgB,CAAA,GAAA,uBAAe,EAAE;IACvC,IAAI,CAAA,GAAA,6BAAqB,QAAQ,WAAW;QAC1C,IAAI,qBAAqB,cAAc,aAAa;QACpD,CAAA,GAAA,yBAAiB,EAAE;YACjB,4EAA4E;YAC5E,IAAI,cAAc,aAAa,KAAK,sBAAsB,QAAQ,WAAW,EAC3E,CAAA,GAAA,4BAAoB,EAAE;QAE1B;IACF,OACE,CAAA,GAAA,4BAAoB,EAAE;AAE1B","sources":["packages/@react-aria/focus/src/focusSafely.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {FocusableElement} from '@react-types/shared';\nimport {focusWithoutScrolling, getOwnerDocument, runAfterTransition} from '@react-aria/utils';\nimport {getInteractionModality} from '@react-aria/interactions';\n\n/**\n * A utility function that focuses an element while avoiding undesired side effects such\n * as page scrolling and screen reader issues with CSS transitions.\n */\nexport function focusSafely(element: FocusableElement) {\n // If the user is interacting with a virtual cursor, e.g. screen reader, then\n // wait until after any animated transitions that are currently occurring on\n // the page before shifting focus. This avoids issues with VoiceOver on iOS\n // causing the page to scroll when moving focus if the element is transitioning\n // from off the screen.\n const ownerDocument = getOwnerDocument(element);\n if (getInteractionModality() === 'virtual') {\n let lastFocusedElement = ownerDocument.activeElement;\n runAfterTransition(() => {\n // If focus did not move and the element is still in the document, focus it.\n if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) {\n focusWithoutScrolling(element);\n }\n });\n } else {\n focusWithoutScrolling(element);\n }\n}\n"],"names":[],"version":3,"file":"focusSafely.module.js.map"}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
var $1c7f9157d722357d$exports = require("./focusSafely.main.js");
|
|
2
|
-
var $ggOO2$reactariautils = require("@react-aria/utils");
|
|
3
|
-
var $ggOO2$react = require("react");
|
|
4
|
-
var $ggOO2$reactariainteractions = require("@react-aria/interactions");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function $parcel$interopDefault(a) {
|
|
8
|
-
return a && a.__esModule ? a.default : a;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function $parcel$export(e, n, v, s) {
|
|
12
|
-
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
$parcel$export(module.exports, "FocusableProvider", () => $fb504d83237fd6ac$export$13f3202a3e5ddd5);
|
|
16
|
-
$parcel$export(module.exports, "useFocusable", () => $fb504d83237fd6ac$export$4c014de7c8940b4c);
|
|
17
|
-
/*
|
|
18
|
-
* Copyright 2020 Adobe. All rights reserved.
|
|
19
|
-
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
20
|
-
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
21
|
-
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
22
|
-
*
|
|
23
|
-
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
24
|
-
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
25
|
-
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
26
|
-
* governing permissions and limitations under the License.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let $fb504d83237fd6ac$var$FocusableContext = /*#__PURE__*/ (0, ($parcel$interopDefault($ggOO2$react))).createContext(null);
|
|
32
|
-
function $fb504d83237fd6ac$var$useFocusableContext(ref) {
|
|
33
|
-
let context = (0, $ggOO2$react.useContext)($fb504d83237fd6ac$var$FocusableContext) || {};
|
|
34
|
-
(0, $ggOO2$reactariautils.useSyncRef)(context, ref);
|
|
35
|
-
// eslint-disable-next-line
|
|
36
|
-
let { ref: _, ...otherProps } = context;
|
|
37
|
-
return otherProps;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Provides DOM props to the nearest focusable child.
|
|
41
|
-
*/ function $fb504d83237fd6ac$var$FocusableProvider(props, ref) {
|
|
42
|
-
let { children: children, ...otherProps } = props;
|
|
43
|
-
let objRef = (0, $ggOO2$reactariautils.useObjectRef)(ref);
|
|
44
|
-
let context = {
|
|
45
|
-
...otherProps,
|
|
46
|
-
ref: objRef
|
|
47
|
-
};
|
|
48
|
-
return /*#__PURE__*/ (0, ($parcel$interopDefault($ggOO2$react))).createElement($fb504d83237fd6ac$var$FocusableContext.Provider, {
|
|
49
|
-
value: context
|
|
50
|
-
}, children);
|
|
51
|
-
}
|
|
52
|
-
let $fb504d83237fd6ac$export$13f3202a3e5ddd5 = /*#__PURE__*/ (0, ($parcel$interopDefault($ggOO2$react))).forwardRef($fb504d83237fd6ac$var$FocusableProvider);
|
|
53
|
-
function $fb504d83237fd6ac$export$4c014de7c8940b4c(props, domRef) {
|
|
54
|
-
let { focusProps: focusProps } = (0, $ggOO2$reactariainteractions.useFocus)(props);
|
|
55
|
-
let { keyboardProps: keyboardProps } = (0, $ggOO2$reactariainteractions.useKeyboard)(props);
|
|
56
|
-
let interactions = (0, $ggOO2$reactariautils.mergeProps)(focusProps, keyboardProps);
|
|
57
|
-
let domProps = $fb504d83237fd6ac$var$useFocusableContext(domRef);
|
|
58
|
-
let interactionProps = props.isDisabled ? {} : domProps;
|
|
59
|
-
let autoFocusRef = (0, $ggOO2$react.useRef)(props.autoFocus);
|
|
60
|
-
(0, $ggOO2$react.useEffect)(()=>{
|
|
61
|
-
if (autoFocusRef.current && domRef.current) (0, $1c7f9157d722357d$exports.focusSafely)(domRef.current);
|
|
62
|
-
autoFocusRef.current = false;
|
|
63
|
-
}, [
|
|
64
|
-
domRef
|
|
65
|
-
]);
|
|
66
|
-
return {
|
|
67
|
-
focusableProps: (0, $ggOO2$reactariautils.mergeProps)({
|
|
68
|
-
...interactions,
|
|
69
|
-
tabIndex: props.excludeFromTabOrder && !props.isDisabled ? -1 : undefined
|
|
70
|
-
}, interactionProps)
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//# sourceMappingURL=useFocusable.main.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;CAUC;;;;AAsBD,IAAI,uDAAmB,CAAA,GAAA,sCAAI,EAAE,aAAa,CAA+B;AAEzE,SAAS,0CAAoB,GAAuC;IAClE,IAAI,UAAU,CAAA,GAAA,uBAAS,EAAE,2CAAqB,CAAC;IAC/C,CAAA,GAAA,gCAAS,EAAE,SAAS;IAEpB,2BAA2B;IAC3B,IAAI,EAAC,KAAK,CAAC,EAAE,GAAG,YAAW,GAAG;IAC9B,OAAO;AACT;AAEA;;CAEC,GACD,SAAS,wCAAkB,KAA6B,EAAE,GAAmC;IAC3F,IAAI,YAAC,QAAQ,EAAE,GAAG,YAAW,GAAG;IAChC,IAAI,SAAS,CAAA,GAAA,kCAAW,EAAE;IAC1B,IAAI,UAAU;QACZ,GAAG,UAAU;QACb,KAAK;IACP;IAEA,qBACE,0DAAC,uCAAiB,QAAQ;QAAC,OAAO;OAC/B;AAGP;AAEA,IAAI,yDAAqB,CAAA,GAAA,sCAAI,EAAE,UAAU,CAAC;AAWnC,SAAS,0CAA4D,KAA0B,EAAE,MAA0C;IAChJ,IAAI,cAAC,UAAU,EAAC,GAAG,CAAA,GAAA,qCAAO,EAAE;IAC5B,IAAI,iBAAC,aAAa,EAAC,GAAG,CAAA,GAAA,wCAAU,EAAE;IAClC,IAAI,eAAe,CAAA,GAAA,gCAAS,EAAE,YAAY;IAC1C,IAAI,WAAW,0CAAoB;IACnC,IAAI,mBAAmB,MAAM,UAAU,GAAG,CAAC,IAAI;IAC/C,IAAI,eAAe,CAAA,GAAA,mBAAK,EAAE,MAAM,SAAS;IAEzC,CAAA,GAAA,sBAAQ,EAAE;QACR,IAAI,aAAa,OAAO,IAAI,OAAO,OAAO,EACxC,CAAA,GAAA,qCAAU,EAAE,OAAO,OAAO;QAE5B,aAAa,OAAO,GAAG;IACzB,GAAG;QAAC;KAAO;IAEX,OAAO;QACL,gBAAgB,CAAA,GAAA,gCAAS,EACvB;YACE,GAAG,YAAY;YACf,UAAU,MAAM,mBAAmB,IAAI,CAAC,MAAM,UAAU,GAAG,KAAK;QAClE,GACA;IAEJ;AACF","sources":["packages/@react-aria/focus/src/useFocusable.tsx"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {DOMAttributes, FocusableDOMProps, FocusableElement, FocusableProps, RefObject} from '@react-types/shared';\nimport {focusSafely} from './';\nimport {mergeProps, useObjectRef, useSyncRef} from '@react-aria/utils';\nimport React, {ForwardedRef, MutableRefObject, ReactNode, useContext, useEffect, useRef} from 'react';\nimport {useFocus, useKeyboard} from '@react-aria/interactions';\n\nexport interface FocusableOptions<T = FocusableElement> extends FocusableProps<T>, FocusableDOMProps {\n /** Whether focus should be disabled. */\n isDisabled?: boolean\n}\n\nexport interface FocusableProviderProps extends DOMAttributes {\n /** The child element to provide DOM props to. */\n children?: ReactNode\n}\n\ninterface FocusableContextValue extends FocusableProviderProps {\n ref?: MutableRefObject<FocusableElement | null>\n}\n\nlet FocusableContext = React.createContext<FocusableContextValue | null>(null);\n\nfunction useFocusableContext(ref: RefObject<FocusableElement | null>): FocusableContextValue {\n let context = useContext(FocusableContext) || {};\n useSyncRef(context, ref);\n\n // eslint-disable-next-line\n let {ref: _, ...otherProps} = context;\n return otherProps;\n}\n\n/**\n * Provides DOM props to the nearest focusable child.\n */\nfunction FocusableProvider(props: FocusableProviderProps, ref: ForwardedRef<FocusableElement>) {\n let {children, ...otherProps} = props;\n let objRef = useObjectRef(ref);\n let context = {\n ...otherProps,\n ref: objRef\n };\n\n return (\n <FocusableContext.Provider value={context}>\n {children}\n </FocusableContext.Provider>\n );\n}\n\nlet _FocusableProvider = React.forwardRef(FocusableProvider);\nexport {_FocusableProvider as FocusableProvider};\n\nexport interface FocusableAria {\n /** Props for the focusable element. */\n focusableProps: DOMAttributes\n}\n\n/**\n * Used to make an element focusable and capable of auto focus.\n */\nexport function useFocusable<T extends FocusableElement = FocusableElement>(props: FocusableOptions<T>, domRef: RefObject<FocusableElement | null>): FocusableAria {\n let {focusProps} = useFocus(props);\n let {keyboardProps} = useKeyboard(props);\n let interactions = mergeProps(focusProps, keyboardProps);\n let domProps = useFocusableContext(domRef);\n let interactionProps = props.isDisabled ? {} : domProps;\n let autoFocusRef = useRef(props.autoFocus);\n\n useEffect(() => {\n if (autoFocusRef.current && domRef.current) {\n focusSafely(domRef.current);\n }\n autoFocusRef.current = false;\n }, [domRef]);\n\n return {\n focusableProps: mergeProps(\n {\n ...interactions,\n tabIndex: props.excludeFromTabOrder && !props.isDisabled ? -1 : undefined\n },\n interactionProps\n )\n };\n}\n"],"names":[],"version":3,"file":"useFocusable.main.js.map"}
|