@react-aria/focus 3.5.3 → 3.6.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 +34 -27
- package/dist/main.js.map +1 -1
- package/dist/module.js +35 -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 +18 -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,13 @@ 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
|
-
let {from, tabbable, wrap} = opts;
|
|
517
|
+
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
|
|
514
518
|
let node = from || document.activeElement;
|
|
515
|
-
let walker = getFocusableTreeWalker(root, {tabbable});
|
|
519
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
516
520
|
if (root.contains(node)) {
|
|
517
521
|
walker.currentNode = node;
|
|
518
522
|
}
|
|
@@ -526,11 +530,11 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
|
|
|
526
530
|
}
|
|
527
531
|
return nextNode;
|
|
528
532
|
},
|
|
529
|
-
focusPrevious(opts: FocusManagerOptions =
|
|
533
|
+
focusPrevious(opts: FocusManagerOptions = defaultOptions) {
|
|
530
534
|
let root = ref.current;
|
|
531
|
-
let {from, tabbable, wrap} = opts;
|
|
535
|
+
let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
|
|
532
536
|
let node = from || document.activeElement;
|
|
533
|
-
let walker = getFocusableTreeWalker(root, {tabbable});
|
|
537
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
534
538
|
if (root.contains(node)) {
|
|
535
539
|
walker.currentNode = node;
|
|
536
540
|
} else {
|
|
@@ -550,20 +554,20 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
|
|
|
550
554
|
}
|
|
551
555
|
return previousNode;
|
|
552
556
|
},
|
|
553
|
-
focusFirst(opts =
|
|
557
|
+
focusFirst(opts = defaultOptions) {
|
|
554
558
|
let root = ref.current;
|
|
555
|
-
let {tabbable} = opts;
|
|
556
|
-
let walker = getFocusableTreeWalker(root, {tabbable});
|
|
559
|
+
let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
|
|
560
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
557
561
|
let nextNode = walker.nextNode() as HTMLElement;
|
|
558
562
|
if (nextNode) {
|
|
559
563
|
focusElement(nextNode, true);
|
|
560
564
|
}
|
|
561
565
|
return nextNode;
|
|
562
566
|
},
|
|
563
|
-
focusLast(opts =
|
|
567
|
+
focusLast(opts = defaultOptions) {
|
|
564
568
|
let root = ref.current;
|
|
565
|
-
let {tabbable} = opts;
|
|
566
|
-
let walker = getFocusableTreeWalker(root, {tabbable});
|
|
569
|
+
let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
|
|
570
|
+
let walker = getFocusableTreeWalker(root, {tabbable, accept});
|
|
567
571
|
let next = last(walker);
|
|
568
572
|
if (next) {
|
|
569
573
|
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(
|