@react-aria/focus 3.6.1 → 3.7.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.
@@ -10,6 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import {FocusableElement} from '@react-types/shared';
13
14
  import {focusSafely} from './focusSafely';
14
15
  import {isElementVisible} from './isElementVisible';
15
16
  import React, {ReactNode, RefObject, useContext, useEffect, useRef} from 'react';
@@ -18,7 +19,7 @@ import {useLayoutEffect} from '@react-aria/utils';
18
19
  // import {FocusScope, useFocusScope} from 'react-events/focus-scope';
19
20
  // export {FocusScope};
20
21
 
21
- interface FocusScopeProps {
22
+ export interface FocusScopeProps {
22
23
  /** The contents of the focus scope. */
23
24
  children: ReactNode,
24
25
 
@@ -38,9 +39,9 @@ interface FocusScopeProps {
38
39
  autoFocus?: boolean
39
40
  }
40
41
 
41
- interface FocusManagerOptions {
42
+ export interface FocusManagerOptions {
42
43
  /** The element to start searching from. The currently focused element by default. */
43
- from?: HTMLElement,
44
+ from?: Element,
44
45
  /** Whether to only include tabbable elements, or all focusable elements. */
45
46
  tabbable?: boolean,
46
47
  /** Whether focus should wrap around when it reaches the end of the scope. */
@@ -51,16 +52,16 @@ interface FocusManagerOptions {
51
52
 
52
53
  export interface FocusManager {
53
54
  /** Moves focus to the next focusable or tabbable element in the focus scope. */
54
- focusNext(opts?: FocusManagerOptions): HTMLElement,
55
+ focusNext(opts?: FocusManagerOptions): FocusableElement,
55
56
  /** Moves focus to the previous focusable or tabbable element in the focus scope. */
56
- focusPrevious(opts?: FocusManagerOptions): HTMLElement,
57
+ focusPrevious(opts?: FocusManagerOptions): FocusableElement,
57
58
  /** Moves focus to the first focusable or tabbable element in the focus scope. */
58
- focusFirst(opts?: FocusManagerOptions): HTMLElement,
59
+ focusFirst(opts?: FocusManagerOptions): FocusableElement,
59
60
  /** Moves focus to the last focusable or tabbable element in the focus scope. */
60
- focusLast(opts?: FocusManagerOptions): HTMLElement
61
+ focusLast(opts?: FocusManagerOptions): FocusableElement
61
62
  }
62
63
 
63
- type ScopeRef = RefObject<HTMLElement[]>;
64
+ type ScopeRef = RefObject<Element[]>;
64
65
  interface IFocusContext {
65
66
  scopeRef: ScopeRef,
66
67
  focusManager: FocusManager
@@ -87,7 +88,7 @@ export function FocusScope(props: FocusScopeProps) {
87
88
  let {children, contain, restoreFocus, autoFocus} = props;
88
89
  let startRef = useRef<HTMLSpanElement>();
89
90
  let endRef = useRef<HTMLSpanElement>();
90
- let scopeRef = useRef<HTMLElement[]>([]);
91
+ let scopeRef = useRef<Element[]>([]);
91
92
  let ctx = useContext(FocusContext);
92
93
  let parentScope = ctx?.scopeRef;
93
94
 
@@ -143,19 +144,19 @@ export function useFocusManager(): FocusManager {
143
144
  return useContext(FocusContext)?.focusManager;
144
145
  }
145
146
 
146
- function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): FocusManager {
147
+ function createFocusManagerForScope(scopeRef: React.RefObject<Element[]>): FocusManager {
147
148
  return {
148
149
  focusNext(opts: FocusManagerOptions = {}) {
149
150
  let scope = scopeRef.current;
150
- let {from, tabbable, wrap} = opts;
151
+ let {from, tabbable, wrap, accept} = opts;
151
152
  let node = from || document.activeElement;
152
153
  let sentinel = scope[0].previousElementSibling;
153
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
154
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
154
155
  walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
155
- let nextNode = walker.nextNode() as HTMLElement;
156
+ let nextNode = walker.nextNode() as FocusableElement;
156
157
  if (!nextNode && wrap) {
157
158
  walker.currentNode = sentinel;
158
- nextNode = walker.nextNode() as HTMLElement;
159
+ nextNode = walker.nextNode() as FocusableElement;
159
160
  }
160
161
  if (nextNode) {
161
162
  focusElement(nextNode, true);
@@ -164,15 +165,15 @@ function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): F
164
165
  },
165
166
  focusPrevious(opts: FocusManagerOptions = {}) {
166
167
  let scope = scopeRef.current;
167
- let {from, tabbable, wrap} = opts;
168
+ let {from, tabbable, wrap, accept} = opts;
168
169
  let node = from || document.activeElement;
169
170
  let sentinel = scope[scope.length - 1].nextElementSibling;
170
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
171
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
171
172
  walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
172
- let previousNode = walker.previousNode() as HTMLElement;
173
+ let previousNode = walker.previousNode() as FocusableElement;
173
174
  if (!previousNode && wrap) {
174
175
  walker.currentNode = sentinel;
175
- previousNode = walker.previousNode() as HTMLElement;
176
+ previousNode = walker.previousNode() as FocusableElement;
176
177
  }
177
178
  if (previousNode) {
178
179
  focusElement(previousNode, true);
@@ -181,10 +182,10 @@ function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): F
181
182
  },
182
183
  focusFirst(opts = {}) {
183
184
  let scope = scopeRef.current;
184
- let {tabbable} = opts;
185
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
185
+ let {tabbable, accept} = opts;
186
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
186
187
  walker.currentNode = scope[0].previousElementSibling;
187
- let nextNode = walker.nextNode() as HTMLElement;
188
+ let nextNode = walker.nextNode() as FocusableElement;
188
189
  if (nextNode) {
189
190
  focusElement(nextNode, true);
190
191
  }
@@ -192,10 +193,10 @@ function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): F
192
193
  },
193
194
  focusLast(opts = {}) {
194
195
  let scope = scopeRef.current;
195
- let {tabbable} = opts;
196
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
196
+ let {tabbable, accept} = opts;
197
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
197
198
  walker.currentNode = scope[scope.length - 1].nextElementSibling;
198
- let previousNode = walker.previousNode() as HTMLElement;
199
+ let previousNode = walker.previousNode() as FocusableElement;
199
200
  if (previousNode) {
200
201
  focusElement(previousNode, true);
201
202
  }
@@ -225,17 +226,22 @@ const FOCUSABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]),') + '
225
226
  focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])');
226
227
  const TABBABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]):not([tabindex="-1"]),');
227
228
 
228
- function getScopeRoot(scope: HTMLElement[]) {
229
+ function getScopeRoot(scope: Element[]) {
229
230
  return scope[0].parentElement;
230
231
  }
231
232
 
232
- function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolean) {
233
- let focusedNode = useRef<HTMLElement>();
233
+ function useFocusContainment(scopeRef: RefObject<Element[]>, contain: boolean) {
234
+ let focusedNode = useRef<FocusableElement>();
234
235
 
235
236
  let raf = useRef(null);
236
237
  useLayoutEffect(() => {
237
238
  let scope = scopeRef.current;
238
239
  if (!contain) {
240
+ // if contain was changed, then we should cancel any ongoing waits to pull focus back into containment
241
+ if (raf.current) {
242
+ cancelAnimationFrame(raf.current);
243
+ raf.current = null;
244
+ }
239
245
  return;
240
246
  }
241
247
 
@@ -245,7 +251,7 @@ function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolea
245
251
  return;
246
252
  }
247
253
 
248
- let focusedElement = document.activeElement as HTMLElement;
254
+ let focusedElement = document.activeElement;
249
255
  let scope = scopeRef.current;
250
256
  if (!isElementInScope(focusedElement, scope)) {
251
257
  return;
@@ -253,10 +259,10 @@ function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolea
253
259
 
254
260
  let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable: true}, scope);
255
261
  walker.currentNode = focusedElement;
256
- let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
262
+ let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
257
263
  if (!nextElement) {
258
264
  walker.currentNode = e.shiftKey ? scope[scope.length - 1].nextElementSibling : scope[0].previousElementSibling;
259
- nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
265
+ nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
260
266
  }
261
267
 
262
268
  e.preventDefault();
@@ -310,7 +316,11 @@ function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolea
310
316
 
311
317
  // eslint-disable-next-line arrow-body-style
312
318
  useEffect(() => {
313
- return () => cancelAnimationFrame(raf.current);
319
+ return () => {
320
+ if (raf.current) {
321
+ cancelAnimationFrame(raf.current);
322
+ }
323
+ };
314
324
  }, [raf]);
315
325
  }
316
326
 
@@ -323,7 +333,7 @@ function isElementInAnyScope(element: Element) {
323
333
  return false;
324
334
  }
325
335
 
326
- function isElementInScope(element: Element, scope: HTMLElement[]) {
336
+ function isElementInScope(element: Element, scope: Element[]) {
327
337
  return scope.some(node => node.contains(element));
328
338
  }
329
339
 
@@ -352,7 +362,7 @@ function isAncestorScope(ancestor: ScopeRef, scope: ScopeRef) {
352
362
  return isAncestorScope(ancestor, parent);
353
363
  }
354
364
 
355
- function focusElement(element: HTMLElement | null, scroll = false) {
365
+ function focusElement(element: FocusableElement | null, scroll = false) {
356
366
  if (element != null && !scroll) {
357
367
  try {
358
368
  focusSafely(element);
@@ -368,14 +378,14 @@ function focusElement(element: HTMLElement | null, scroll = false) {
368
378
  }
369
379
  }
370
380
 
371
- function focusFirstInScope(scope: HTMLElement[]) {
381
+ function focusFirstInScope(scope: Element[]) {
372
382
  let sentinel = scope[0].previousElementSibling;
373
383
  let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable: true}, scope);
374
384
  walker.currentNode = sentinel;
375
- focusElement(walker.nextNode() as HTMLElement);
385
+ focusElement(walker.nextNode() as FocusableElement);
376
386
  }
377
387
 
378
- function useAutoFocus(scopeRef: RefObject<HTMLElement[]>, autoFocus: boolean) {
388
+ function useAutoFocus(scopeRef: RefObject<Element[]>, autoFocus: boolean) {
379
389
  const autoFocusRef = React.useRef(autoFocus);
380
390
  useEffect(() => {
381
391
  if (autoFocusRef.current) {
@@ -388,9 +398,9 @@ function useAutoFocus(scopeRef: RefObject<HTMLElement[]>, autoFocus: boolean) {
388
398
  }, []);
389
399
  }
390
400
 
391
- function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boolean, contain: boolean) {
401
+ function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus: boolean, contain: boolean) {
392
402
  // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
393
- const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? document.activeElement as HTMLElement : null);
403
+ const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? document.activeElement as FocusableElement : null);
394
404
 
395
405
  // useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
396
406
  useLayoutEffect(() => {
@@ -408,7 +418,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
408
418
  return;
409
419
  }
410
420
 
411
- let focusedElement = document.activeElement as HTMLElement;
421
+ let focusedElement = document.activeElement as FocusableElement;
412
422
  if (!isElementInScope(focusedElement, scopeRef.current)) {
413
423
  return;
414
424
  }
@@ -418,7 +428,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
418
428
 
419
429
  // Find the next tabbable element after the currently focused element
420
430
  walker.currentNode = focusedElement;
421
- let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
431
+ let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
422
432
 
423
433
  if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) {
424
434
  nodeToRestore = null;
@@ -431,7 +441,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
431
441
 
432
442
  // Skip over elements within the scope, in case the scope immediately follows the node to restore.
433
443
  do {
434
- nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
444
+ nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
435
445
  } while (isElementInScope(nextElement, scopeRef.current));
436
446
 
437
447
  e.preventDefault();
@@ -462,7 +472,8 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
462
472
 
463
473
  if (restoreFocus && nodeToRestore && isElementInScope(document.activeElement, scopeRef.current)) {
464
474
  requestAnimationFrame(() => {
465
- if (document.body.contains(nodeToRestore)) {
475
+ // Only restore focus if we've lost focus to the body, the alternative is that focus has been purposefully moved elsewhere
476
+ if (document.body.contains(nodeToRestore) && document.activeElement === document.body) {
466
477
  focusElement(nodeToRestore);
467
478
  }
468
479
  });
@@ -475,7 +486,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
475
486
  * Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker}
476
487
  * that matches all focusable/tabbable elements.
477
488
  */
478
- export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOptions, scope?: HTMLElement[]) {
489
+ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]) {
479
490
  let selector = opts?.tabbable ? TABBABLE_ELEMENT_SELECTOR : FOCUSABLE_ELEMENT_SELECTOR;
480
491
  let walker = document.createTreeWalker(
481
492
  root,
@@ -487,9 +498,9 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt
487
498
  return NodeFilter.FILTER_REJECT;
488
499
  }
489
500
 
490
- if ((node as HTMLElement).matches(selector)
491
- && isElementVisible(node as HTMLElement)
492
- && (!scope || isElementInScope(node as HTMLElement, scope))
501
+ if ((node as Element).matches(selector)
502
+ && isElementVisible(node as Element)
503
+ && (!scope || isElementInScope(node as Element, scope))
493
504
  && (!opts?.accept || opts.accept(node as Element))
494
505
  ) {
495
506
  return NodeFilter.FILTER_ACCEPT;
@@ -510,7 +521,7 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt
510
521
  /**
511
522
  * Creates a FocusManager object that can be used to move focus within an element.
512
523
  */
513
- export function createFocusManager(ref: RefObject<HTMLElement>, defaultOptions: FocusManagerOptions = {}): FocusManager {
524
+ export function createFocusManager(ref: RefObject<Element>, defaultOptions: FocusManagerOptions = {}): FocusManager {
514
525
  return {
515
526
  focusNext(opts: FocusManagerOptions = {}) {
516
527
  let root = ref.current;
@@ -523,10 +534,10 @@ export function createFocusManager(ref: RefObject<HTMLElement>, defaultOptions:
523
534
  if (root.contains(node)) {
524
535
  walker.currentNode = node;
525
536
  }
526
- let nextNode = walker.nextNode() as HTMLElement;
537
+ let nextNode = walker.nextNode() as FocusableElement;
527
538
  if (!nextNode && wrap) {
528
539
  walker.currentNode = root;
529
- nextNode = walker.nextNode() as HTMLElement;
540
+ nextNode = walker.nextNode() as FocusableElement;
530
541
  }
531
542
  if (nextNode) {
532
543
  focusElement(nextNode, true);
@@ -550,7 +561,7 @@ export function createFocusManager(ref: RefObject<HTMLElement>, defaultOptions:
550
561
  }
551
562
  return next;
552
563
  }
553
- let previousNode = walker.previousNode() as HTMLElement;
564
+ let previousNode = walker.previousNode() as FocusableElement;
554
565
  if (!previousNode && wrap) {
555
566
  walker.currentNode = root;
556
567
  previousNode = last(walker);
@@ -567,7 +578,7 @@ export function createFocusManager(ref: RefObject<HTMLElement>, defaultOptions:
567
578
  }
568
579
  let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
569
580
  let walker = getFocusableTreeWalker(root, {tabbable, accept});
570
- let nextNode = walker.nextNode() as HTMLElement;
581
+ let nextNode = walker.nextNode() as FocusableElement;
571
582
  if (nextNode) {
572
583
  focusElement(nextNode, true);
573
584
  }
@@ -590,10 +601,10 @@ export function createFocusManager(ref: RefObject<HTMLElement>, defaultOptions:
590
601
  }
591
602
 
592
603
  function last(walker: TreeWalker) {
593
- let next: HTMLElement;
594
- let last: HTMLElement;
604
+ let next: FocusableElement;
605
+ let last: FocusableElement;
595
606
  do {
596
- last = walker.lastChild() as HTMLElement;
607
+ last = walker.lastChild() as FocusableElement;
597
608
  if (last) {
598
609
  next = last;
599
610
  }
@@ -10,6 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import {FocusableElement} from '@react-types/shared';
13
14
  import {focusWithoutScrolling, runAfterTransition} from '@react-aria/utils';
14
15
  import {getInteractionModality} from '@react-aria/interactions';
15
16
 
@@ -17,7 +18,7 @@ import {getInteractionModality} from '@react-aria/interactions';
17
18
  * A utility function that focuses an element while avoiding undesired side effects such
18
19
  * as page scrolling and screen reader issues with CSS transitions.
19
20
  */
20
- export function focusSafely(element: HTMLElement) {
21
+ export function focusSafely(element: FocusableElement) {
21
22
  // If the user is interacting with a virtual cursor, e.g. screen reader, then
22
23
  // wait until after any animated transitions that are currently occurring on
23
24
  // the page before shifting focus. This avoids issues with VoiceOver on iOS
package/src/index.ts CHANGED
@@ -10,8 +10,13 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- export * from './FocusScope';
14
- export * from './FocusRing';
15
- export * from './useFocusable';
16
- export * from './useFocusRing';
17
- export * from './focusSafely';
13
+ export {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager} from './FocusScope';
14
+ export {FocusRing} from './FocusRing';
15
+ export {FocusableProvider, useFocusable} from './useFocusable';
16
+ export {useFocusRing} from './useFocusRing';
17
+ export {focusSafely} from './focusSafely';
18
+
19
+ export type {FocusScopeProps, FocusManager, FocusManagerOptions} from './FocusScope';
20
+ export type {FocusRingProps} from './FocusRing';
21
+ export type {FocusableAria, FocusableOptions, FocusableProviderProps} from './useFocusable';
22
+ export type {AriaFocusRingProps, FocusRingAria} from './useFocusRing';
@@ -1,8 +1,9 @@
1
- import {HTMLAttributes, useCallback, useState} from 'react';
1
+ import {DOMAttributes} from '@react-types/shared';
2
2
  import {isFocusVisible, useFocus, useFocusVisibleListener, useFocusWithin} from '@react-aria/interactions';
3
+ import {useCallback, useState} from 'react';
3
4
  import {useRef} from 'react';
4
5
 
5
- interface FocusRingProps {
6
+ export interface AriaFocusRingProps {
6
7
  /**
7
8
  * Whether to show the focus ring when something
8
9
  * inside the container element has focus (true), or
@@ -18,7 +19,7 @@ interface FocusRingProps {
18
19
  autoFocus?: boolean
19
20
  }
20
21
 
21
- interface FocusRingAria {
22
+ export interface FocusRingAria {
22
23
  /** Whether the element is currently focused. */
23
24
  isFocused: boolean,
24
25
 
@@ -26,7 +27,7 @@ interface FocusRingAria {
26
27
  isFocusVisible: boolean,
27
28
 
28
29
  /** Props to apply to the container element with the focus ring. */
29
- focusProps: HTMLAttributes<HTMLElement>
30
+ focusProps: DOMAttributes
30
31
  }
31
32
 
32
33
  /**
@@ -34,7 +35,7 @@ interface FocusRingAria {
34
35
  * Focus rings are visible only when the user is interacting with a keyboard,
35
36
  * not with a mouse, touch, or other input methods.
36
37
  */
37
- export function useFocusRing(props: FocusRingProps = {}): FocusRingAria {
38
+ export function useFocusRing(props: AriaFocusRingProps = {}): FocusRingAria {
38
39
  let {
39
40
  autoFocus = false,
40
41
  isTextInput,
@@ -10,29 +10,29 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {FocusableDOMProps, FocusableProps} from '@react-types/shared';
13
+ import {DOMAttributes, FocusableDOMProps, FocusableElement, FocusableProps} from '@react-types/shared';
14
14
  import {focusSafely} from './';
15
15
  import {mergeProps, useSyncRef} from '@react-aria/utils';
16
- import React, {HTMLAttributes, MutableRefObject, ReactNode, RefObject, useContext, useEffect, useRef} from 'react';
16
+ import React, {MutableRefObject, ReactNode, RefObject, useContext, useEffect, useRef} from 'react';
17
17
  import {useFocus, useKeyboard} from '@react-aria/interactions';
18
18
 
19
- interface FocusableOptions extends FocusableProps, FocusableDOMProps {
19
+ export interface FocusableOptions extends FocusableProps, FocusableDOMProps {
20
20
  /** Whether focus should be disabled. */
21
21
  isDisabled?: boolean
22
22
  }
23
23
 
24
- interface FocusableProviderProps extends HTMLAttributes<HTMLElement> {
24
+ export interface FocusableProviderProps extends DOMAttributes {
25
25
  /** The child element to provide DOM props to. */
26
26
  children?: ReactNode
27
27
  }
28
28
 
29
29
  interface FocusableContextValue extends FocusableProviderProps {
30
- ref?: MutableRefObject<HTMLElement>
30
+ ref?: MutableRefObject<FocusableElement>
31
31
  }
32
32
 
33
33
  let FocusableContext = React.createContext<FocusableContextValue>(null);
34
34
 
35
- function useFocusableContext(ref: RefObject<HTMLElement>): FocusableContextValue {
35
+ function useFocusableContext(ref: RefObject<FocusableElement>): FocusableContextValue {
36
36
  let context = useContext(FocusableContext) || {};
37
37
  useSyncRef(context, ref);
38
38
 
@@ -44,7 +44,7 @@ function useFocusableContext(ref: RefObject<HTMLElement>): FocusableContextValue
44
44
  /**
45
45
  * Provides DOM props to the nearest focusable child.
46
46
  */
47
- function FocusableProvider(props: FocusableProviderProps, ref: RefObject<HTMLElement>) {
47
+ function FocusableProvider(props: FocusableProviderProps, ref: RefObject<FocusableElement>) {
48
48
  let {children, ...otherProps} = props;
49
49
  let context = {
50
50
  ...otherProps,
@@ -61,15 +61,15 @@ function FocusableProvider(props: FocusableProviderProps, ref: RefObject<HTMLEle
61
61
  let _FocusableProvider = React.forwardRef(FocusableProvider);
62
62
  export {_FocusableProvider as FocusableProvider};
63
63
 
64
- interface FocusableAria {
64
+ export interface FocusableAria {
65
65
  /** Props for the focusable element. */
66
- focusableProps: HTMLAttributes<HTMLElement>
66
+ focusableProps: DOMAttributes
67
67
  }
68
68
 
69
69
  /**
70
70
  * Used to make an element focusable and capable of auto focus.
71
71
  */
72
- export function useFocusable(props: FocusableOptions, domRef: RefObject<HTMLElement>): FocusableAria {
72
+ export function useFocusable(props: FocusableOptions, domRef: RefObject<FocusableElement>): FocusableAria {
73
73
  let {focusProps} = useFocus(props);
74
74
  let {keyboardProps} = useKeyboard(props);
75
75
  let interactions = mergeProps(focusProps, keyboardProps);