@react-aria/focus 3.5.4 → 3.6.1
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 +38 -27
- package/dist/main.js.map +1 -1
- package/dist/module.js +39 -28
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +9 -265
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/FocusScope.tsx +30 -14
- package/src/useFocusRing.ts +9 -9
- package/src/useFocusable.tsx +9 -3
package/src/FocusScope.tsx
CHANGED
|
@@ -44,7 +44,9 @@ interface FocusManagerOptions {
|
|
|
44
44
|
/** Whether to only include tabbable elements, or all focusable elements. */
|
|
45
45
|
tabbable?: boolean,
|
|
46
46
|
/** Whether focus should wrap around when it reaches the end of the scope. */
|
|
47
|
-
wrap?: boolean
|
|
47
|
+
wrap?: boolean,
|
|
48
|
+
/** A callback that determines whether the given element is focused. */
|
|
49
|
+
accept?: (node: Element) => boolean
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
export interface FocusManager {
|
|
@@ -487,7 +489,9 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt
|
|
|
487
489
|
|
|
488
490
|
if ((node as HTMLElement).matches(selector)
|
|
489
491
|
&& isElementVisible(node as HTMLElement)
|
|
490
|
-
&& (!scope || isElementInScope(node as HTMLElement, scope))
|
|
492
|
+
&& (!scope || isElementInScope(node as HTMLElement, scope))
|
|
493
|
+
&& (!opts?.accept || opts.accept(node as Element))
|
|
494
|
+
) {
|
|
491
495
|
return NodeFilter.FILTER_ACCEPT;
|
|
492
496
|
}
|
|
493
497
|
|
|
@@ -506,13 +510,16 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt
|
|
|
506
510
|
/**
|
|
507
511
|
* Creates a FocusManager object that can be used to move focus within an element.
|
|
508
512
|
*/
|
|
509
|
-
export function createFocusManager(ref: RefObject<HTMLElement
|
|
513
|
+
export function createFocusManager(ref: RefObject<HTMLElement>, defaultOptions: FocusManagerOptions = {}): FocusManager {
|
|
510
514
|
return {
|
|
511
515
|
focusNext(opts: FocusManagerOptions = {}) {
|
|
512
516
|
let root = ref.current;
|
|
513
|
-
|
|
517
|
+
if (!root) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
|
|
514
521
|
let node = from || document.activeElement;
|
|
515
|
-
let walker = getFocusableTreeWalker(root, {tabbable});
|
|
522
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
516
523
|
if (root.contains(node)) {
|
|
517
524
|
walker.currentNode = node;
|
|
518
525
|
}
|
|
@@ -526,11 +533,14 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
|
|
|
526
533
|
}
|
|
527
534
|
return nextNode;
|
|
528
535
|
},
|
|
529
|
-
focusPrevious(opts: FocusManagerOptions =
|
|
536
|
+
focusPrevious(opts: FocusManagerOptions = defaultOptions) {
|
|
530
537
|
let root = ref.current;
|
|
531
|
-
|
|
538
|
+
if (!root) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
|
|
532
542
|
let node = from || document.activeElement;
|
|
533
|
-
let walker = getFocusableTreeWalker(root, {tabbable});
|
|
543
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
534
544
|
if (root.contains(node)) {
|
|
535
545
|
walker.currentNode = node;
|
|
536
546
|
} else {
|
|
@@ -550,20 +560,26 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
|
|
|
550
560
|
}
|
|
551
561
|
return previousNode;
|
|
552
562
|
},
|
|
553
|
-
focusFirst(opts =
|
|
563
|
+
focusFirst(opts = defaultOptions) {
|
|
554
564
|
let root = ref.current;
|
|
555
|
-
|
|
556
|
-
|
|
565
|
+
if (!root) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
|
|
569
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
557
570
|
let nextNode = walker.nextNode() as HTMLElement;
|
|
558
571
|
if (nextNode) {
|
|
559
572
|
focusElement(nextNode, true);
|
|
560
573
|
}
|
|
561
574
|
return nextNode;
|
|
562
575
|
},
|
|
563
|
-
focusLast(opts =
|
|
576
|
+
focusLast(opts = defaultOptions) {
|
|
564
577
|
let root = ref.current;
|
|
565
|
-
|
|
566
|
-
|
|
578
|
+
if (!root) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
|
|
582
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
567
583
|
let next = last(walker);
|
|
568
584
|
if (next) {
|
|
569
585
|
focusElement(next, true);
|
package/src/useFocusRing.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {HTMLAttributes, useState} from 'react';
|
|
1
|
+
import {HTMLAttributes, useCallback, useState} from 'react';
|
|
2
2
|
import {isFocusVisible, useFocus, useFocusVisibleListener, useFocusWithin} from '@react-aria/interactions';
|
|
3
3
|
import {useRef} from 'react';
|
|
4
4
|
|
|
@@ -43,20 +43,20 @@ export function useFocusRing(props: FocusRingProps = {}): FocusRingAria {
|
|
|
43
43
|
let state = useRef({
|
|
44
44
|
isFocused: false,
|
|
45
45
|
isFocusVisible: autoFocus || isFocusVisible()
|
|
46
|
-
})
|
|
46
|
+
});
|
|
47
47
|
let [isFocused, setFocused] = useState(false);
|
|
48
|
-
let [isFocusVisibleState, setFocusVisible] = useState(() => state.isFocused && state.isFocusVisible);
|
|
48
|
+
let [isFocusVisibleState, setFocusVisible] = useState(() => state.current.isFocused && state.current.isFocusVisible);
|
|
49
49
|
|
|
50
|
-
let updateState = () => setFocusVisible(state.isFocused && state.isFocusVisible);
|
|
50
|
+
let updateState = useCallback(() => setFocusVisible(state.current.isFocused && state.current.isFocusVisible), []);
|
|
51
51
|
|
|
52
|
-
let onFocusChange = isFocused => {
|
|
53
|
-
state.isFocused = isFocused;
|
|
52
|
+
let onFocusChange = useCallback(isFocused => {
|
|
53
|
+
state.current.isFocused = isFocused;
|
|
54
54
|
setFocused(isFocused);
|
|
55
55
|
updateState();
|
|
56
|
-
};
|
|
56
|
+
}, [updateState]);
|
|
57
57
|
|
|
58
58
|
useFocusVisibleListener((isFocusVisible) => {
|
|
59
|
-
state.isFocusVisible = isFocusVisible;
|
|
59
|
+
state.current.isFocusVisible = isFocusVisible;
|
|
60
60
|
updateState();
|
|
61
61
|
}, [], {isTextInput});
|
|
62
62
|
|
|
@@ -72,7 +72,7 @@ export function useFocusRing(props: FocusRingProps = {}): FocusRingAria {
|
|
|
72
72
|
|
|
73
73
|
return {
|
|
74
74
|
isFocused,
|
|
75
|
-
isFocusVisible: state.isFocused && isFocusVisibleState,
|
|
75
|
+
isFocusVisible: state.current.isFocused && isFocusVisibleState,
|
|
76
76
|
focusProps: within ? focusWithinProps : focusProps
|
|
77
77
|
};
|
|
78
78
|
}
|
package/src/useFocusable.tsx
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {FocusableDOMProps, FocusableProps} from '@react-types/shared';
|
|
14
|
+
import {focusSafely} from './';
|
|
14
15
|
import {mergeProps, useSyncRef} from '@react-aria/utils';
|
|
15
16
|
import React, {HTMLAttributes, MutableRefObject, ReactNode, RefObject, useContext, useEffect, useRef} from 'react';
|
|
16
17
|
import {useFocus, useKeyboard} from '@react-aria/interactions';
|
|
@@ -60,10 +61,15 @@ function FocusableProvider(props: FocusableProviderProps, ref: RefObject<HTMLEle
|
|
|
60
61
|
let _FocusableProvider = React.forwardRef(FocusableProvider);
|
|
61
62
|
export {_FocusableProvider as FocusableProvider};
|
|
62
63
|
|
|
64
|
+
interface FocusableAria {
|
|
65
|
+
/** Props for the focusable element. */
|
|
66
|
+
focusableProps: HTMLAttributes<HTMLElement>
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
/**
|
|
64
70
|
* Used to make an element focusable and capable of auto focus.
|
|
65
71
|
*/
|
|
66
|
-
export function useFocusable(props: FocusableOptions, domRef: RefObject<HTMLElement>) {
|
|
72
|
+
export function useFocusable(props: FocusableOptions, domRef: RefObject<HTMLElement>): FocusableAria {
|
|
67
73
|
let {focusProps} = useFocus(props);
|
|
68
74
|
let {keyboardProps} = useKeyboard(props);
|
|
69
75
|
let interactions = mergeProps(focusProps, keyboardProps);
|
|
@@ -73,10 +79,10 @@ export function useFocusable(props: FocusableOptions, domRef: RefObject<HTMLElem
|
|
|
73
79
|
|
|
74
80
|
useEffect(() => {
|
|
75
81
|
if (autoFocusRef.current && domRef.current) {
|
|
76
|
-
domRef.current
|
|
82
|
+
focusSafely(domRef.current);
|
|
77
83
|
}
|
|
78
84
|
autoFocusRef.current = false;
|
|
79
|
-
}, []);
|
|
85
|
+
}, [domRef]);
|
|
80
86
|
|
|
81
87
|
return {
|
|
82
88
|
focusableProps: mergeProps(
|