@react-aria/focus 3.17.0 → 3.18.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/FocusRing.main.js +2 -2
- package/dist/FocusRing.main.js.map +1 -1
- package/dist/FocusRing.mjs +3 -3
- package/dist/FocusRing.module.js +2 -2
- package/dist/FocusRing.module.js.map +1 -1
- package/dist/FocusScope.main.js +62 -42
- package/dist/FocusScope.main.js.map +1 -1
- package/dist/FocusScope.mjs +63 -43
- package/dist/FocusScope.module.js +62 -42
- package/dist/FocusScope.module.js.map +1 -1
- package/dist/focusSafely.main.js +1 -1
- package/dist/focusSafely.mjs +2 -2
- package/dist/focusSafely.module.js +1 -1
- package/dist/isElementVisible.main.js +5 -5
- package/dist/isElementVisible.mjs +6 -6
- package/dist/isElementVisible.module.js +5 -5
- package/dist/types.d.ts +6 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/useFocusRing.mjs +1 -1
- package/dist/useFocusable.main.js.map +1 -1
- package/dist/useFocusable.mjs +1 -1
- package/dist/useFocusable.module.js.map +1 -1
- package/dist/useHasTabbableChild.main.js +3 -3
- package/dist/useHasTabbableChild.main.js.map +1 -1
- package/dist/useHasTabbableChild.mjs +4 -4
- package/dist/useHasTabbableChild.module.js +3 -3
- package/dist/useHasTabbableChild.module.js.map +1 -1
- package/package.json +6 -6
- package/src/FocusRing.tsx +1 -1
- package/src/FocusScope.tsx +40 -17
- package/src/useFocusable.tsx +4 -4
- package/src/useHasTabbableChild.ts +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {getFocusableTreeWalker as $9bf71ea28793e738$export$2d6ec8fc375ceafa} from "./FocusScope.module.js";
|
|
2
|
-
import {useState as $hGAaG$useState} from "react";
|
|
3
2
|
import {useLayoutEffect as $hGAaG$useLayoutEffect} from "@react-aria/utils";
|
|
3
|
+
import {useState as $hGAaG$useState} from "react";
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
6
|
* Copyright 2022 Adobe. All rights reserved.
|
|
@@ -36,8 +36,8 @@ function $83013635b024ae3d$export$eac1895992b9f3d6(ref, options) {
|
|
|
36
36
|
childList: true,
|
|
37
37
|
attributes: true,
|
|
38
38
|
attributeFilter: [
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
'tabIndex',
|
|
40
|
+
'disabled'
|
|
41
41
|
]
|
|
42
42
|
});
|
|
43
43
|
return ()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;;;AAAA;;;;;;;;;;CAUC;;;
|
|
1
|
+
{"mappings":";;;;AAAA;;;;;;;;;;CAUC;;;AAoBM,SAAS,0CAAoB,GAA8B,EAAE,OAAqC;IACvG,IAAI,aAAa,oBAAA,8BAAA,QAAS,UAAU;IACpC,IAAI,CAAC,kBAAkB,oBAAoB,GAAG,CAAA,GAAA,eAAO,EAAE;IAEvD,CAAA,GAAA,sBAAc,EAAE;QACd,IAAI,CAAA,gBAAA,0BAAA,IAAK,OAAO,KAAI,CAAC,YAAY;YAC/B,IAAI,SAAS;gBACX,IAAI,IAAI,OAAO,EAAE;oBACf,IAAI,SAAS,CAAA,GAAA,yCAAqB,EAAE,IAAI,OAAO,EAAE;wBAAC,UAAU;oBAAI;oBAChE,oBAAoB,CAAC,CAAC,OAAO,QAAQ;gBACvC;YACF;YAEA;YAEA,qFAAqF;YACrF,IAAI,WAAW,IAAI,iBAAiB;YACpC,SAAS,OAAO,CAAC,IAAI,OAAO,EAAE;gBAC5B,SAAS;gBACT,WAAW;gBACX,YAAY;gBACZ,iBAAiB;oBAAC;oBAAY;iBAAW;YAC3C;YAEA,OAAO;gBACL,qFAAqF;gBACrF,wFAAwF;gBACxF,4FAA4F;gBAC5F,yFAAyF;gBACzF,SAAS,UAAU;YACrB;QACF;IACF;IAEA,OAAO,aAAa,QAAQ;AAC9B","sources":["packages/@react-aria/focus/src/useHasTabbableChild.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {getFocusableTreeWalker} from './FocusScope';\nimport {RefObject} from '@react-types/shared';\nimport {useLayoutEffect} from '@react-aria/utils';\nimport {useState} from 'react';\n\ninterface AriaHasTabbableChildOptions {\n isDisabled?: boolean\n}\n\n// This was created for a special empty case of a component that can have child or\n// be empty, like Collection/Virtualizer/Table/ListView/etc. When these components\n// are empty they can have a message with a tabbable element, which is like them\n// being not empty, when it comes to focus and tab order.\n\n/**\n * Returns whether an element has a tabbable child, and updates as children change.\n * @private\n */\nexport function useHasTabbableChild(ref: RefObject<Element | null>, options?: AriaHasTabbableChildOptions): boolean {\n let isDisabled = options?.isDisabled;\n let [hasTabbableChild, setHasTabbableChild] = useState(false);\n\n useLayoutEffect(() => {\n if (ref?.current && !isDisabled) {\n let update = () => {\n if (ref.current) {\n let walker = getFocusableTreeWalker(ref.current, {tabbable: true});\n setHasTabbableChild(!!walker.nextNode());\n }\n };\n\n update();\n\n // Update when new elements are inserted, or the tabIndex/disabled attribute updates.\n let observer = new MutationObserver(update);\n observer.observe(ref.current, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['tabIndex', 'disabled']\n });\n\n return () => {\n // Disconnect mutation observer when a React update occurs on the top-level component\n // so we update synchronously after re-rendering. Otherwise React will emit act warnings\n // in tests since mutation observers fire asynchronously. The mutation observer is necessary\n // so we also update if a child component re-renders and adds/removes something tabbable.\n observer.disconnect();\n };\n }\n });\n\n return isDisabled ? false : hasTabbableChild;\n}\n"],"names":[],"version":3,"file":"useHasTabbableChild.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.18.0",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -22,17 +22,17 @@
|
|
|
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.22.0",
|
|
26
|
+
"@react-aria/utils": "^3.25.0",
|
|
27
|
+
"@react-types/shared": "^3.24.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"
|
|
32
|
+
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "86d80e3216bc32e75108831cf3a5a720bc849206"
|
|
38
38
|
}
|
package/src/FocusRing.tsx
CHANGED
|
@@ -45,7 +45,7 @@ export function FocusRing(props: FocusRingProps) {
|
|
|
45
45
|
let {isFocused, isFocusVisible, focusProps} = useFocusRing(props);
|
|
46
46
|
let child = React.Children.only(children);
|
|
47
47
|
|
|
48
|
-
return React.cloneElement(child, mergeProps(child.props, {
|
|
48
|
+
return React.cloneElement(child, mergeProps(child.props as any, {
|
|
49
49
|
...focusProps,
|
|
50
50
|
className: clsx({
|
|
51
51
|
[focusClass || '']: isFocused,
|
package/src/FocusScope.tsx
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {FocusableElement} from '@react-types/shared';
|
|
13
|
+
import {FocusableElement, RefObject} from '@react-types/shared';
|
|
14
14
|
import {focusSafely} from './focusSafely';
|
|
15
15
|
import {getOwnerDocument, useLayoutEffect} from '@react-aria/utils';
|
|
16
16
|
import {isElementVisible} from './isElementVisible';
|
|
17
|
-
import React, {ReactNode,
|
|
17
|
+
import React, {ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
|
|
18
18
|
|
|
19
19
|
export interface FocusScopeProps {
|
|
20
20
|
/** The contents of the focus scope. */
|
|
@@ -58,13 +58,14 @@ export interface FocusManager {
|
|
|
58
58
|
focusLast(opts?: FocusManagerOptions): FocusableElement | null
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
type ScopeRef = RefObject<Element[]> | null;
|
|
61
|
+
type ScopeRef = RefObject<Element[] | null> | null;
|
|
62
62
|
interface IFocusContext {
|
|
63
63
|
focusManager: FocusManager,
|
|
64
64
|
parentNode: TreeNode | null
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const FocusContext = React.createContext<IFocusContext | null>(null);
|
|
68
|
+
const RESTORE_FOCUS_EVENT = 'react-aria-focus-scope-restore';
|
|
68
69
|
|
|
69
70
|
let activeScope: ScopeRef = null;
|
|
70
71
|
|
|
@@ -117,12 +118,21 @@ export function FocusScope(props: FocusScopeProps) {
|
|
|
117
118
|
// Find all rendered nodes between the sentinels and add them to the scope.
|
|
118
119
|
let node = startRef.current?.nextSibling!;
|
|
119
120
|
let nodes: Element[] = [];
|
|
121
|
+
let stopPropagation = e => e.stopPropagation();
|
|
120
122
|
while (node && node !== endRef.current) {
|
|
121
123
|
nodes.push(node as Element);
|
|
124
|
+
// Stop custom restore focus event from propagating to parent focus scopes.
|
|
125
|
+
node.addEventListener(RESTORE_FOCUS_EVENT, stopPropagation);
|
|
122
126
|
node = node.nextSibling as Element;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
scopeRef.current = nodes;
|
|
130
|
+
|
|
131
|
+
return () => {
|
|
132
|
+
for (let node of nodes) {
|
|
133
|
+
node.removeEventListener(RESTORE_FOCUS_EVENT, stopPropagation);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
126
136
|
}, [children]);
|
|
127
137
|
|
|
128
138
|
useActiveScopeTracker(scopeRef, restoreFocus, contain);
|
|
@@ -192,7 +202,7 @@ export function useFocusManager(): FocusManager | undefined {
|
|
|
192
202
|
return useContext(FocusContext)?.focusManager;
|
|
193
203
|
}
|
|
194
204
|
|
|
195
|
-
function createFocusManagerForScope(scopeRef: React.RefObject<Element[]>): FocusManager {
|
|
205
|
+
function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>): FocusManager {
|
|
196
206
|
return {
|
|
197
207
|
focusNext(opts: FocusManagerOptions = {}) {
|
|
198
208
|
let scope = scopeRef.current!;
|
|
@@ -299,10 +309,10 @@ function shouldContainFocus(scopeRef: ScopeRef) {
|
|
|
299
309
|
return true;
|
|
300
310
|
}
|
|
301
311
|
|
|
302
|
-
function useFocusContainment(scopeRef: RefObject<Element[]>, contain?: boolean) {
|
|
303
|
-
let focusedNode = useRef<FocusableElement>();
|
|
312
|
+
function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: boolean) {
|
|
313
|
+
let focusedNode = useRef<FocusableElement>(undefined);
|
|
304
314
|
|
|
305
|
-
let raf = useRef<ReturnType<typeof requestAnimationFrame>>();
|
|
315
|
+
let raf = useRef<ReturnType<typeof requestAnimationFrame>>(undefined);
|
|
306
316
|
useLayoutEffect(() => {
|
|
307
317
|
let scope = scopeRef.current;
|
|
308
318
|
if (!contain) {
|
|
@@ -470,7 +480,7 @@ function focusElement(element: FocusableElement | null, scroll = false) {
|
|
|
470
480
|
}
|
|
471
481
|
}
|
|
472
482
|
|
|
473
|
-
function
|
|
483
|
+
function getFirstInScope(scope: Element[], tabbable = true) {
|
|
474
484
|
let sentinel = scope[0].previousElementSibling!;
|
|
475
485
|
let scopeRoot = getScopeRoot(scope);
|
|
476
486
|
let walker = getFocusableTreeWalker(scopeRoot, {tabbable}, scope);
|
|
@@ -485,10 +495,14 @@ function focusFirstInScope(scope: Element[], tabbable:boolean = true) {
|
|
|
485
495
|
nextNode = walker.nextNode();
|
|
486
496
|
}
|
|
487
497
|
|
|
488
|
-
|
|
498
|
+
return nextNode as FocusableElement;
|
|
489
499
|
}
|
|
490
500
|
|
|
491
|
-
function
|
|
501
|
+
function focusFirstInScope(scope: Element[], tabbable:boolean = true) {
|
|
502
|
+
focusElement(getFirstInScope(scope, tabbable));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function useAutoFocus(scopeRef: RefObject<Element[] | null>, autoFocus?: boolean) {
|
|
492
506
|
const autoFocusRef = React.useRef(autoFocus);
|
|
493
507
|
useEffect(() => {
|
|
494
508
|
if (autoFocusRef.current) {
|
|
@@ -502,7 +516,7 @@ function useAutoFocus(scopeRef: RefObject<Element[]>, autoFocus?: boolean) {
|
|
|
502
516
|
}, [scopeRef]);
|
|
503
517
|
}
|
|
504
518
|
|
|
505
|
-
function useActiveScopeTracker(scopeRef: RefObject<Element[]>, restore?: boolean, contain?: boolean) {
|
|
519
|
+
function useActiveScopeTracker(scopeRef: RefObject<Element[] | null>, restore?: boolean, contain?: boolean) {
|
|
506
520
|
// tracks the active scope, in case restore and contain are both false.
|
|
507
521
|
// if either are true, this is tracked in useRestoreFocus or useFocusContainment.
|
|
508
522
|
useLayoutEffect(() => {
|
|
@@ -544,7 +558,7 @@ function shouldRestoreFocus(scopeRef: ScopeRef) {
|
|
|
544
558
|
return scope?.scopeRef === scopeRef;
|
|
545
559
|
}
|
|
546
560
|
|
|
547
|
-
function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus?: boolean, contain?: boolean) {
|
|
561
|
+
function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: boolean, contain?: boolean) {
|
|
548
562
|
// create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
|
|
549
563
|
// eslint-disable-next-line no-restricted-globals
|
|
550
564
|
const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined).activeElement as FocusableElement : null);
|
|
@@ -679,8 +693,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus?: boolean,
|
|
|
679
693
|
&& nodeToRestore
|
|
680
694
|
&& (
|
|
681
695
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
682
|
-
isElementInScope(ownerDocument.activeElement, scopeRef.current)
|
|
683
|
-
|| (ownerDocument.activeElement === ownerDocument.body && shouldRestoreFocus(scopeRef))
|
|
696
|
+
(isElementInScope(ownerDocument.activeElement, scopeRef.current) || (ownerDocument.activeElement === ownerDocument.body && shouldRestoreFocus(scopeRef)))
|
|
684
697
|
)
|
|
685
698
|
) {
|
|
686
699
|
// freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
|
|
@@ -692,7 +705,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus?: boolean,
|
|
|
692
705
|
let treeNode = clonedTree.getTreeNode(scopeRef);
|
|
693
706
|
while (treeNode) {
|
|
694
707
|
if (treeNode.nodeToRestore && treeNode.nodeToRestore.isConnected) {
|
|
695
|
-
|
|
708
|
+
restoreFocusToElement(treeNode.nodeToRestore);
|
|
696
709
|
return;
|
|
697
710
|
}
|
|
698
711
|
treeNode = treeNode.parent;
|
|
@@ -703,7 +716,8 @@ function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus?: boolean,
|
|
|
703
716
|
treeNode = clonedTree.getTreeNode(scopeRef);
|
|
704
717
|
while (treeNode) {
|
|
705
718
|
if (treeNode.scopeRef && treeNode.scopeRef.current && focusScopeTree.getTreeNode(treeNode.scopeRef)) {
|
|
706
|
-
|
|
719
|
+
let node = getFirstInScope(treeNode.scopeRef.current, true);
|
|
720
|
+
restoreFocusToElement(node);
|
|
707
721
|
return;
|
|
708
722
|
}
|
|
709
723
|
treeNode = treeNode.parent;
|
|
@@ -715,6 +729,15 @@ function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus?: boolean,
|
|
|
715
729
|
}, [scopeRef, restoreFocus]);
|
|
716
730
|
}
|
|
717
731
|
|
|
732
|
+
function restoreFocusToElement(node: FocusableElement) {
|
|
733
|
+
// Dispatch a custom event that parent elements can intercept to customize focus restoration.
|
|
734
|
+
// For example, virtualized collection components reuse DOM elements, so the original element
|
|
735
|
+
// might still exist in the DOM but representing a different item.
|
|
736
|
+
if (node.dispatchEvent(new CustomEvent(RESTORE_FOCUS_EVENT, {bubbles: true, cancelable: true}))) {
|
|
737
|
+
focusElement(node);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
718
741
|
/**
|
|
719
742
|
* Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker}
|
|
720
743
|
* that matches all focusable/tabbable elements.
|
|
@@ -754,7 +777,7 @@ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions
|
|
|
754
777
|
/**
|
|
755
778
|
* Creates a FocusManager object that can be used to move focus within an element.
|
|
756
779
|
*/
|
|
757
|
-
export function createFocusManager(ref: RefObject<Element>, defaultOptions: FocusManagerOptions = {}): FocusManager {
|
|
780
|
+
export function createFocusManager(ref: RefObject<Element | null>, defaultOptions: FocusManagerOptions = {}): FocusManager {
|
|
758
781
|
return {
|
|
759
782
|
focusNext(opts: FocusManagerOptions = {}) {
|
|
760
783
|
let root = ref.current;
|
package/src/useFocusable.tsx
CHANGED
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {DOMAttributes, FocusableDOMProps, FocusableElement, FocusableProps} from '@react-types/shared';
|
|
13
|
+
import {DOMAttributes, FocusableDOMProps, FocusableElement, FocusableProps, RefObject} from '@react-types/shared';
|
|
14
14
|
import {focusSafely} from './';
|
|
15
15
|
import {mergeProps, useObjectRef, useSyncRef} from '@react-aria/utils';
|
|
16
|
-
import React, {ForwardedRef, MutableRefObject, ReactNode,
|
|
16
|
+
import React, {ForwardedRef, MutableRefObject, ReactNode, useContext, useEffect, useRef} from 'react';
|
|
17
17
|
import {useFocus, useKeyboard} from '@react-aria/interactions';
|
|
18
18
|
|
|
19
19
|
export interface FocusableOptions extends FocusableProps, FocusableDOMProps {
|
|
@@ -32,7 +32,7 @@ interface FocusableContextValue extends FocusableProviderProps {
|
|
|
32
32
|
|
|
33
33
|
let FocusableContext = React.createContext<FocusableContextValue | null>(null);
|
|
34
34
|
|
|
35
|
-
function useFocusableContext(ref: RefObject<FocusableElement>): FocusableContextValue {
|
|
35
|
+
function useFocusableContext(ref: RefObject<FocusableElement | null>): FocusableContextValue {
|
|
36
36
|
let context = useContext(FocusableContext) || {};
|
|
37
37
|
useSyncRef(context, ref);
|
|
38
38
|
|
|
@@ -70,7 +70,7 @@ export interface FocusableAria {
|
|
|
70
70
|
/**
|
|
71
71
|
* Used to make an element focusable and capable of auto focus.
|
|
72
72
|
*/
|
|
73
|
-
export function useFocusable(props: FocusableOptions, domRef: RefObject<FocusableElement>): FocusableAria {
|
|
73
|
+
export function useFocusable(props: FocusableOptions, domRef: RefObject<FocusableElement | null>): FocusableAria {
|
|
74
74
|
let {focusProps} = useFocus(props);
|
|
75
75
|
let {keyboardProps} = useKeyboard(props);
|
|
76
76
|
let interactions = mergeProps(focusProps, keyboardProps);
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {getFocusableTreeWalker} from './FocusScope';
|
|
14
|
-
import {RefObject
|
|
14
|
+
import {RefObject} from '@react-types/shared';
|
|
15
15
|
import {useLayoutEffect} from '@react-aria/utils';
|
|
16
|
+
import {useState} from 'react';
|
|
16
17
|
|
|
17
18
|
interface AriaHasTabbableChildOptions {
|
|
18
19
|
isDisabled?: boolean
|
|
@@ -27,7 +28,7 @@ interface AriaHasTabbableChildOptions {
|
|
|
27
28
|
* Returns whether an element has a tabbable child, and updates as children change.
|
|
28
29
|
* @private
|
|
29
30
|
*/
|
|
30
|
-
export function useHasTabbableChild(ref: RefObject<Element>, options?: AriaHasTabbableChildOptions): boolean {
|
|
31
|
+
export function useHasTabbableChild(ref: RefObject<Element | null>, options?: AriaHasTabbableChildOptions): boolean {
|
|
31
32
|
let isDisabled = options?.isDisabled;
|
|
32
33
|
let [hasTabbableChild, setHasTabbableChild] = useState(false);
|
|
33
34
|
|