@react-aria/focus 3.5.5 → 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.
package/dist/types.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import React, { ReactNode, RefObject, HTMLAttributes, ReactElement } from "react";
2
- import { FocusableDOMProps, FocusableProps } from "@react-types/shared";
1
+ import { FocusableElement, DOMAttributes, FocusableDOMProps, FocusableProps } from "@react-types/shared";
2
+ import React, { ReactNode, RefObject, ReactElement } from "react";
3
3
  /**
4
4
  * A utility function that focuses an element while avoiding undesired side effects such
5
5
  * as page scrolling and screen reader issues with CSS transitions.
6
6
  */
7
- export function focusSafely(element: HTMLElement): void;
8
- interface FocusScopeProps {
7
+ export function focusSafely(element: FocusableElement): void;
8
+ export interface FocusScopeProps {
9
9
  /** The contents of the focus scope. */
10
10
  children: ReactNode;
11
11
  /**
@@ -21,23 +21,25 @@ interface FocusScopeProps {
21
21
  /** Whether to auto focus the first focusable element in the focus scope on mount. */
22
22
  autoFocus?: boolean;
23
23
  }
24
- interface FocusManagerOptions {
24
+ export interface FocusManagerOptions {
25
25
  /** The element to start searching from. The currently focused element by default. */
26
- from?: HTMLElement;
26
+ from?: Element;
27
27
  /** Whether to only include tabbable elements, or all focusable elements. */
28
28
  tabbable?: boolean;
29
29
  /** Whether focus should wrap around when it reaches the end of the scope. */
30
30
  wrap?: boolean;
31
+ /** A callback that determines whether the given element is focused. */
32
+ accept?: (node: Element) => boolean;
31
33
  }
32
34
  export interface FocusManager {
33
35
  /** Moves focus to the next focusable or tabbable element in the focus scope. */
34
- focusNext(opts?: FocusManagerOptions): HTMLElement;
36
+ focusNext(opts?: FocusManagerOptions): FocusableElement;
35
37
  /** Moves focus to the previous focusable or tabbable element in the focus scope. */
36
- focusPrevious(opts?: FocusManagerOptions): HTMLElement;
38
+ focusPrevious(opts?: FocusManagerOptions): FocusableElement;
37
39
  /** Moves focus to the first focusable or tabbable element in the focus scope. */
38
- focusFirst(opts?: FocusManagerOptions): HTMLElement;
40
+ focusFirst(opts?: FocusManagerOptions): FocusableElement;
39
41
  /** Moves focus to the last focusable or tabbable element in the focus scope. */
40
- focusLast(opts?: FocusManagerOptions): HTMLElement;
42
+ focusLast(opts?: FocusManagerOptions): FocusableElement;
41
43
  }
42
44
  /**
43
45
  * A FocusScope manages focus for its descendants. It supports containing focus inside
@@ -57,12 +59,12 @@ export function useFocusManager(): FocusManager;
57
59
  * Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker}
58
60
  * that matches all focusable/tabbable elements.
59
61
  */
60
- export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOptions, scope?: HTMLElement[]): TreeWalker;
62
+ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]): TreeWalker;
61
63
  /**
62
64
  * Creates a FocusManager object that can be used to move focus within an element.
63
65
  */
64
- export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager;
65
- interface FocusRingProps {
66
+ export function createFocusManager(ref: RefObject<Element>, defaultOptions?: FocusManagerOptions): FocusManager;
67
+ export interface AriaFocusRingProps {
66
68
  /**
67
69
  * Whether to show the focus ring when something
68
70
  * inside the container element has focus (true), or
@@ -75,21 +77,21 @@ interface FocusRingProps {
75
77
  /** Whether the element will be auto focused. */
76
78
  autoFocus?: boolean;
77
79
  }
78
- interface FocusRingAria {
80
+ export interface FocusRingAria {
79
81
  /** Whether the element is currently focused. */
80
82
  isFocused: boolean;
81
83
  /** Whether keyboard focus should be visible. */
82
84
  isFocusVisible: boolean;
83
85
  /** Props to apply to the container element with the focus ring. */
84
- focusProps: HTMLAttributes<HTMLElement>;
86
+ focusProps: DOMAttributes;
85
87
  }
86
88
  /**
87
89
  * Determines whether a focus ring should be shown to indicate keyboard focus.
88
90
  * Focus rings are visible only when the user is interacting with a keyboard,
89
91
  * not with a mouse, touch, or other input methods.
90
92
  */
91
- export function useFocusRing(props?: FocusRingProps): FocusRingAria;
92
- interface _FocusRingProps1 {
93
+ export function useFocusRing(props?: AriaFocusRingProps): FocusRingAria;
94
+ export interface FocusRingProps {
93
95
  /** Child element to apply CSS classes to. */
94
96
  children: ReactElement;
95
97
  /** CSS class to apply when the element is focused. */
@@ -113,23 +115,23 @@ interface _FocusRingProps1 {
113
115
  * Focus rings are visible only when the user is interacting with a keyboard,
114
116
  * not with a mouse, touch, or other input methods.
115
117
  */
116
- export function FocusRing(props: _FocusRingProps1): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
117
- interface FocusableOptions extends FocusableProps, FocusableDOMProps {
118
+ export function FocusRing(props: FocusRingProps): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
119
+ export interface FocusableOptions extends FocusableProps, FocusableDOMProps {
118
120
  /** Whether focus should be disabled. */
119
121
  isDisabled?: boolean;
120
122
  }
121
- interface FocusableProviderProps extends HTMLAttributes<HTMLElement> {
123
+ export interface FocusableProviderProps extends DOMAttributes {
122
124
  /** The child element to provide DOM props to. */
123
125
  children?: ReactNode;
124
126
  }
125
- export let FocusableProvider: React.ForwardRefExoticComponent<FocusableProviderProps & React.RefAttributes<HTMLElement>>;
126
- interface FocusableAria {
127
+ export let FocusableProvider: React.ForwardRefExoticComponent<FocusableProviderProps & React.RefAttributes<FocusableElement>>;
128
+ export interface FocusableAria {
127
129
  /** Props for the focusable element. */
128
- focusableProps: HTMLAttributes<HTMLElement>;
130
+ focusableProps: DOMAttributes;
129
131
  }
130
132
  /**
131
133
  * Used to make an element focusable and capable of auto focus.
132
134
  */
133
- export function useFocusable(props: FocusableOptions, domRef: RefObject<HTMLElement>): FocusableAria;
135
+ export function useFocusable(props: FocusableOptions, domRef: RefObject<FocusableElement>): FocusableAria;
134
136
 
135
137
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"mappings":";;AAeA;;;GAGG;AACH,4BAA4B,OAAO,EAAE,WAAW,QAiB/C;AEhBD;IACE,uCAAuC;IACvC,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,qFAAqF;IACrF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;IACE,qFAAqF;IACrF,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED;IACE,gFAAgF;IAChF,SAAS,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,WAAW,CAAC;IACnD,oFAAoF;IACpF,aAAa,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,WAAW,CAAC;IACvD,iFAAiF;IACjF,UAAU,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,WAAW,CAAC;IAClD,gFAAgF;IAClF,SAAS,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,WAAW,CAAA;CACnD;AAkBD;;;;;;GAMG;AACH,2BAA2B,KAAK,EAAE,eAAe,eAiDhD;AAED;;;;GAIG;AACH,mCAAmC,YAAY,CAE9C;AA0UD;;;GAGG;AACH,uCAAuC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,WAAW,EAAE,cA4B1G;AAED;;GAEG;AACH,mCAAmC,GAAG,EAAE,UAAU,WAAW,CAAC,GAAG,YAAY,CAiE5E;ACzjBD;IACE;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;IACE,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IAEnB,gDAAgD;IAChD,cAAc,EAAE,OAAO,CAAC;IAExB,mEAAmE;IACnE,UAAU,EAAE,eAAe,WAAW,CAAC,CAAA;CACxC;AAED;;;;GAIG;AACH,6BAA6B,KAAK,GAAE,cAAmB,GAAG,aAAa,CAyCtE;AC5DD;IACE,6CAA6C;IAC7C,QAAQ,EAAE,YAAY,CAAC;IACvB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;;GAIG;AACH,0BAA0B,KAAK,EAAE,gBAAc,sEAY9C;ACpCD,0BAA2B,SAAQ,cAAc,EAAE,iBAAiB;IAClE,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,gCAAiC,SAAQ,eAAe,WAAW,CAAC;IAClE,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAkCD,OAAA,IAAI,6GAAwD,CAAC;AAG7D;IACE,uCAAuC;IACvC,cAAc,EAAE,eAAe,WAAW,CAAC,CAAA;CAC5C;AAED;;GAEG;AACH,6BAA6B,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,WAAW,CAAC,GAAG,aAAa,CAwBnG","sources":["packages/@react-aria/focus/src/packages/@react-aria/focus/src/focusSafely.ts","packages/@react-aria/focus/src/packages/@react-aria/focus/src/isElementVisible.ts","packages/@react-aria/focus/src/packages/@react-aria/focus/src/FocusScope.tsx","packages/@react-aria/focus/src/packages/@react-aria/focus/src/useFocusRing.ts","packages/@react-aria/focus/src/packages/@react-aria/focus/src/FocusRing.tsx","packages/@react-aria/focus/src/packages/@react-aria/focus/src/useFocusable.tsx","packages/@react-aria/focus/src/packages/@react-aria/focus/src/index.ts","packages/@react-aria/focus/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,"/*\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\nexport * from './FocusScope';\nexport * from './FocusRing';\nexport * from './useFocusable';\nexport * from './useFocusRing';\nexport * from './focusSafely';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":";;AAgBA;;;GAGG;AACH,4BAA4B,OAAO,EAAE,gBAAgB,QAiBpD;AEhBD;IACE,uCAAuC;IACvC,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,qFAAqF;IACrF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;IACE,qFAAqF;IACrF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;CACpC;AAED;IACE,gFAAgF;IAChF,SAAS,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,gBAAgB,CAAC;IACxD,oFAAoF;IACpF,aAAa,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,gBAAgB,CAAC;IAC5D,iFAAiF;IACjF,UAAU,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,gBAAgB,CAAC;IACvD,gFAAgF;IAClF,SAAS,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,gBAAgB,CAAA;CACxD;AAkBD;;;;;;GAMG;AACH,2BAA2B,KAAK,EAAE,eAAe,eAiDhD;AAED;;;;GAIG;AACH,mCAAmC,YAAY,CAE9C;AAoVD;;;GAGG;AACH,uCAAuC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,cA8BlG;AAED;;GAEG;AACH,mCAAmC,GAAG,EAAE,UAAU,OAAO,CAAC,EAAE,cAAc,GAAE,mBAAwB,GAAG,YAAY,CA6ElH;ACnlBD;IACE;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;IACE,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IAEnB,gDAAgD;IAChD,cAAc,EAAE,OAAO,CAAC;IAExB,mEAAmE;IACnE,UAAU,EAAE,aAAa,CAAA;CAC1B;AAED;;;;GAIG;AACH,6BAA6B,KAAK,GAAE,kBAAuB,GAAG,aAAa,CAyC1E;AC7DD;IACE,6CAA6C;IAC7C,QAAQ,EAAE,YAAY,CAAC;IACvB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;;GAIG;AACH,0BAA0B,KAAK,EAAE,cAAc,sEAY9C;ACpCD,iCAAkC,SAAQ,cAAc,EAAE,iBAAiB;IACzE,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,uCAAwC,SAAQ,aAAa;IAC3D,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAkCD,OAAA,IAAI,kHAAwD,CAAC;AAG7D;IACE,uCAAuC;IACvC,cAAc,EAAE,aAAa,CAAA;CAC9B;AAED;;GAEG;AACH,6BAA6B,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,gBAAgB,CAAC,GAAG,aAAa,CAwBxG","sources":["packages/@react-aria/focus/src/packages/@react-aria/focus/src/focusSafely.ts","packages/@react-aria/focus/src/packages/@react-aria/focus/src/isElementVisible.ts","packages/@react-aria/focus/src/packages/@react-aria/focus/src/FocusScope.tsx","packages/@react-aria/focus/src/packages/@react-aria/focus/src/useFocusRing.ts","packages/@react-aria/focus/src/packages/@react-aria/focus/src/FocusRing.tsx","packages/@react-aria/focus/src/packages/@react-aria/focus/src/useFocusable.tsx","packages/@react-aria/focus/src/packages/@react-aria/focus/src/index.ts","packages/@react-aria/focus/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,"/*\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\nexport {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager} from './FocusScope';\nexport {FocusRing} from './FocusRing';\nexport {FocusableProvider, useFocusable} from './useFocusable';\nexport {useFocusRing} from './useFocusRing';\nexport {focusSafely} from './focusSafely';\n\nexport type {FocusScopeProps, FocusManager, FocusManagerOptions} from './FocusScope';\nexport type {FocusRingProps} from './FocusRing';\nexport type {FocusableAria, FocusableOptions, FocusableProviderProps} from './useFocusable';\nexport type {AriaFocusRingProps, FocusRingAria} from './useFocusRing';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/focus",
3
- "version": "3.5.5",
3
+ "version": "3.7.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -18,16 +18,16 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@babel/runtime": "^7.6.2",
21
- "@react-aria/interactions": "^3.8.4",
22
- "@react-aria/utils": "^3.12.0",
23
- "@react-types/shared": "^3.12.0",
21
+ "@react-aria/interactions": "^3.10.0",
22
+ "@react-aria/utils": "^3.13.2",
23
+ "@react-types/shared": "^3.14.0",
24
24
  "clsx": "^1.1.1"
25
25
  },
26
26
  "peerDependencies": {
27
- "react": "^16.8.0 || ^17.0.0-rc.1"
27
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"
31
31
  },
32
- "gitHead": "6a503b715e0dbbf92038cd7f08b1bcdde4c78e82"
32
+ "gitHead": "cd7c0ec917122c7612f653c22f8ed558f8b66ecd"
33
33
  }
package/src/FocusRing.tsx CHANGED
@@ -15,7 +15,7 @@ import {mergeProps} from '@react-aria/utils';
15
15
  import React, {ReactElement} from 'react';
16
16
  import {useFocusRing} from './useFocusRing';
17
17
 
18
- interface FocusRingProps {
18
+ export interface FocusRingProps {
19
19
  /** Child element to apply CSS classes to. */
20
20
  children: ReactElement,
21
21
  /** CSS class to apply when the element is focused. */
@@ -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,27 +39,29 @@ 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. */
47
- wrap?: boolean
48
+ wrap?: boolean,
49
+ /** A callback that determines whether the given element is focused. */
50
+ accept?: (node: Element) => boolean
48
51
  }
49
52
 
50
53
  export interface FocusManager {
51
54
  /** Moves focus to the next focusable or tabbable element in the focus scope. */
52
- focusNext(opts?: FocusManagerOptions): HTMLElement,
55
+ focusNext(opts?: FocusManagerOptions): FocusableElement,
53
56
  /** Moves focus to the previous focusable or tabbable element in the focus scope. */
54
- focusPrevious(opts?: FocusManagerOptions): HTMLElement,
57
+ focusPrevious(opts?: FocusManagerOptions): FocusableElement,
55
58
  /** Moves focus to the first focusable or tabbable element in the focus scope. */
56
- focusFirst(opts?: FocusManagerOptions): HTMLElement,
59
+ focusFirst(opts?: FocusManagerOptions): FocusableElement,
57
60
  /** Moves focus to the last focusable or tabbable element in the focus scope. */
58
- focusLast(opts?: FocusManagerOptions): HTMLElement
61
+ focusLast(opts?: FocusManagerOptions): FocusableElement
59
62
  }
60
63
 
61
- type ScopeRef = RefObject<HTMLElement[]>;
64
+ type ScopeRef = RefObject<Element[]>;
62
65
  interface IFocusContext {
63
66
  scopeRef: ScopeRef,
64
67
  focusManager: FocusManager
@@ -85,7 +88,7 @@ export function FocusScope(props: FocusScopeProps) {
85
88
  let {children, contain, restoreFocus, autoFocus} = props;
86
89
  let startRef = useRef<HTMLSpanElement>();
87
90
  let endRef = useRef<HTMLSpanElement>();
88
- let scopeRef = useRef<HTMLElement[]>([]);
91
+ let scopeRef = useRef<Element[]>([]);
89
92
  let ctx = useContext(FocusContext);
90
93
  let parentScope = ctx?.scopeRef;
91
94
 
@@ -141,19 +144,19 @@ export function useFocusManager(): FocusManager {
141
144
  return useContext(FocusContext)?.focusManager;
142
145
  }
143
146
 
144
- function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): FocusManager {
147
+ function createFocusManagerForScope(scopeRef: React.RefObject<Element[]>): FocusManager {
145
148
  return {
146
149
  focusNext(opts: FocusManagerOptions = {}) {
147
150
  let scope = scopeRef.current;
148
- let {from, tabbable, wrap} = opts;
151
+ let {from, tabbable, wrap, accept} = opts;
149
152
  let node = from || document.activeElement;
150
153
  let sentinel = scope[0].previousElementSibling;
151
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
154
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
152
155
  walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
153
- let nextNode = walker.nextNode() as HTMLElement;
156
+ let nextNode = walker.nextNode() as FocusableElement;
154
157
  if (!nextNode && wrap) {
155
158
  walker.currentNode = sentinel;
156
- nextNode = walker.nextNode() as HTMLElement;
159
+ nextNode = walker.nextNode() as FocusableElement;
157
160
  }
158
161
  if (nextNode) {
159
162
  focusElement(nextNode, true);
@@ -162,15 +165,15 @@ function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): F
162
165
  },
163
166
  focusPrevious(opts: FocusManagerOptions = {}) {
164
167
  let scope = scopeRef.current;
165
- let {from, tabbable, wrap} = opts;
168
+ let {from, tabbable, wrap, accept} = opts;
166
169
  let node = from || document.activeElement;
167
170
  let sentinel = scope[scope.length - 1].nextElementSibling;
168
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
171
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
169
172
  walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
170
- let previousNode = walker.previousNode() as HTMLElement;
173
+ let previousNode = walker.previousNode() as FocusableElement;
171
174
  if (!previousNode && wrap) {
172
175
  walker.currentNode = sentinel;
173
- previousNode = walker.previousNode() as HTMLElement;
176
+ previousNode = walker.previousNode() as FocusableElement;
174
177
  }
175
178
  if (previousNode) {
176
179
  focusElement(previousNode, true);
@@ -179,10 +182,10 @@ function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): F
179
182
  },
180
183
  focusFirst(opts = {}) {
181
184
  let scope = scopeRef.current;
182
- let {tabbable} = opts;
183
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
185
+ let {tabbable, accept} = opts;
186
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
184
187
  walker.currentNode = scope[0].previousElementSibling;
185
- let nextNode = walker.nextNode() as HTMLElement;
188
+ let nextNode = walker.nextNode() as FocusableElement;
186
189
  if (nextNode) {
187
190
  focusElement(nextNode, true);
188
191
  }
@@ -190,10 +193,10 @@ function createFocusManagerForScope(scopeRef: React.RefObject<HTMLElement[]>): F
190
193
  },
191
194
  focusLast(opts = {}) {
192
195
  let scope = scopeRef.current;
193
- let {tabbable} = opts;
194
- let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable}, scope);
196
+ let {tabbable, accept} = opts;
197
+ let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope);
195
198
  walker.currentNode = scope[scope.length - 1].nextElementSibling;
196
- let previousNode = walker.previousNode() as HTMLElement;
199
+ let previousNode = walker.previousNode() as FocusableElement;
197
200
  if (previousNode) {
198
201
  focusElement(previousNode, true);
199
202
  }
@@ -223,17 +226,22 @@ const FOCUSABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]),') + '
223
226
  focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])');
224
227
  const TABBABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]):not([tabindex="-1"]),');
225
228
 
226
- function getScopeRoot(scope: HTMLElement[]) {
229
+ function getScopeRoot(scope: Element[]) {
227
230
  return scope[0].parentElement;
228
231
  }
229
232
 
230
- function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolean) {
231
- let focusedNode = useRef<HTMLElement>();
233
+ function useFocusContainment(scopeRef: RefObject<Element[]>, contain: boolean) {
234
+ let focusedNode = useRef<FocusableElement>();
232
235
 
233
236
  let raf = useRef(null);
234
237
  useLayoutEffect(() => {
235
238
  let scope = scopeRef.current;
236
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
+ }
237
245
  return;
238
246
  }
239
247
 
@@ -243,7 +251,7 @@ function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolea
243
251
  return;
244
252
  }
245
253
 
246
- let focusedElement = document.activeElement as HTMLElement;
254
+ let focusedElement = document.activeElement;
247
255
  let scope = scopeRef.current;
248
256
  if (!isElementInScope(focusedElement, scope)) {
249
257
  return;
@@ -251,10 +259,10 @@ function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolea
251
259
 
252
260
  let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable: true}, scope);
253
261
  walker.currentNode = focusedElement;
254
- let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
262
+ let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
255
263
  if (!nextElement) {
256
264
  walker.currentNode = e.shiftKey ? scope[scope.length - 1].nextElementSibling : scope[0].previousElementSibling;
257
- nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
265
+ nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
258
266
  }
259
267
 
260
268
  e.preventDefault();
@@ -308,7 +316,11 @@ function useFocusContainment(scopeRef: RefObject<HTMLElement[]>, contain: boolea
308
316
 
309
317
  // eslint-disable-next-line arrow-body-style
310
318
  useEffect(() => {
311
- return () => cancelAnimationFrame(raf.current);
319
+ return () => {
320
+ if (raf.current) {
321
+ cancelAnimationFrame(raf.current);
322
+ }
323
+ };
312
324
  }, [raf]);
313
325
  }
314
326
 
@@ -321,7 +333,7 @@ function isElementInAnyScope(element: Element) {
321
333
  return false;
322
334
  }
323
335
 
324
- function isElementInScope(element: Element, scope: HTMLElement[]) {
336
+ function isElementInScope(element: Element, scope: Element[]) {
325
337
  return scope.some(node => node.contains(element));
326
338
  }
327
339
 
@@ -350,7 +362,7 @@ function isAncestorScope(ancestor: ScopeRef, scope: ScopeRef) {
350
362
  return isAncestorScope(ancestor, parent);
351
363
  }
352
364
 
353
- function focusElement(element: HTMLElement | null, scroll = false) {
365
+ function focusElement(element: FocusableElement | null, scroll = false) {
354
366
  if (element != null && !scroll) {
355
367
  try {
356
368
  focusSafely(element);
@@ -366,14 +378,14 @@ function focusElement(element: HTMLElement | null, scroll = false) {
366
378
  }
367
379
  }
368
380
 
369
- function focusFirstInScope(scope: HTMLElement[]) {
381
+ function focusFirstInScope(scope: Element[]) {
370
382
  let sentinel = scope[0].previousElementSibling;
371
383
  let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable: true}, scope);
372
384
  walker.currentNode = sentinel;
373
- focusElement(walker.nextNode() as HTMLElement);
385
+ focusElement(walker.nextNode() as FocusableElement);
374
386
  }
375
387
 
376
- function useAutoFocus(scopeRef: RefObject<HTMLElement[]>, autoFocus: boolean) {
388
+ function useAutoFocus(scopeRef: RefObject<Element[]>, autoFocus: boolean) {
377
389
  const autoFocusRef = React.useRef(autoFocus);
378
390
  useEffect(() => {
379
391
  if (autoFocusRef.current) {
@@ -386,9 +398,9 @@ function useAutoFocus(scopeRef: RefObject<HTMLElement[]>, autoFocus: boolean) {
386
398
  }, []);
387
399
  }
388
400
 
389
- function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boolean, contain: boolean) {
401
+ function useRestoreFocus(scopeRef: RefObject<Element[]>, restoreFocus: boolean, contain: boolean) {
390
402
  // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
391
- const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? document.activeElement as HTMLElement : null);
403
+ const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? document.activeElement as FocusableElement : null);
392
404
 
393
405
  // useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously.
394
406
  useLayoutEffect(() => {
@@ -406,7 +418,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
406
418
  return;
407
419
  }
408
420
 
409
- let focusedElement = document.activeElement as HTMLElement;
421
+ let focusedElement = document.activeElement as FocusableElement;
410
422
  if (!isElementInScope(focusedElement, scopeRef.current)) {
411
423
  return;
412
424
  }
@@ -416,7 +428,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
416
428
 
417
429
  // Find the next tabbable element after the currently focused element
418
430
  walker.currentNode = focusedElement;
419
- let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
431
+ let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
420
432
 
421
433
  if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) {
422
434
  nodeToRestore = null;
@@ -429,7 +441,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
429
441
 
430
442
  // Skip over elements within the scope, in case the scope immediately follows the node to restore.
431
443
  do {
432
- nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement;
444
+ nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
433
445
  } while (isElementInScope(nextElement, scopeRef.current));
434
446
 
435
447
  e.preventDefault();
@@ -460,7 +472,8 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
460
472
 
461
473
  if (restoreFocus && nodeToRestore && isElementInScope(document.activeElement, scopeRef.current)) {
462
474
  requestAnimationFrame(() => {
463
- 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) {
464
477
  focusElement(nodeToRestore);
465
478
  }
466
479
  });
@@ -473,7 +486,7 @@ function useRestoreFocus(scopeRef: RefObject<HTMLElement[]>, restoreFocus: boole
473
486
  * Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker}
474
487
  * that matches all focusable/tabbable elements.
475
488
  */
476
- export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOptions, scope?: HTMLElement[]) {
489
+ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]) {
477
490
  let selector = opts?.tabbable ? TABBABLE_ELEMENT_SELECTOR : FOCUSABLE_ELEMENT_SELECTOR;
478
491
  let walker = document.createTreeWalker(
479
492
  root,
@@ -485,9 +498,11 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt
485
498
  return NodeFilter.FILTER_REJECT;
486
499
  }
487
500
 
488
- if ((node as HTMLElement).matches(selector)
489
- && isElementVisible(node as HTMLElement)
490
- && (!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))
504
+ && (!opts?.accept || opts.accept(node as Element))
505
+ ) {
491
506
  return NodeFilter.FILTER_ACCEPT;
492
507
  }
493
508
 
@@ -506,31 +521,37 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt
506
521
  /**
507
522
  * Creates a FocusManager object that can be used to move focus within an element.
508
523
  */
509
- export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
524
+ export function createFocusManager(ref: RefObject<Element>, defaultOptions: FocusManagerOptions = {}): FocusManager {
510
525
  return {
511
526
  focusNext(opts: FocusManagerOptions = {}) {
512
527
  let root = ref.current;
513
- let {from, tabbable, wrap} = opts;
528
+ if (!root) {
529
+ return;
530
+ }
531
+ let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
514
532
  let node = from || document.activeElement;
515
- let walker = getFocusableTreeWalker(root, {tabbable});
533
+ let walker = getFocusableTreeWalker(root, {tabbable, accept});
516
534
  if (root.contains(node)) {
517
535
  walker.currentNode = node;
518
536
  }
519
- let nextNode = walker.nextNode() as HTMLElement;
537
+ let nextNode = walker.nextNode() as FocusableElement;
520
538
  if (!nextNode && wrap) {
521
539
  walker.currentNode = root;
522
- nextNode = walker.nextNode() as HTMLElement;
540
+ nextNode = walker.nextNode() as FocusableElement;
523
541
  }
524
542
  if (nextNode) {
525
543
  focusElement(nextNode, true);
526
544
  }
527
545
  return nextNode;
528
546
  },
529
- focusPrevious(opts: FocusManagerOptions = {}) {
547
+ focusPrevious(opts: FocusManagerOptions = defaultOptions) {
530
548
  let root = ref.current;
531
- let {from, tabbable, wrap} = opts;
549
+ if (!root) {
550
+ return;
551
+ }
552
+ let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
532
553
  let node = from || document.activeElement;
533
- let walker = getFocusableTreeWalker(root, {tabbable});
554
+ let walker = getFocusableTreeWalker(root, {tabbable, accept});
534
555
  if (root.contains(node)) {
535
556
  walker.currentNode = node;
536
557
  } else {
@@ -540,7 +561,7 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
540
561
  }
541
562
  return next;
542
563
  }
543
- let previousNode = walker.previousNode() as HTMLElement;
564
+ let previousNode = walker.previousNode() as FocusableElement;
544
565
  if (!previousNode && wrap) {
545
566
  walker.currentNode = root;
546
567
  previousNode = last(walker);
@@ -550,20 +571,26 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
550
571
  }
551
572
  return previousNode;
552
573
  },
553
- focusFirst(opts = {}) {
574
+ focusFirst(opts = defaultOptions) {
554
575
  let root = ref.current;
555
- let {tabbable} = opts;
556
- let walker = getFocusableTreeWalker(root, {tabbable});
557
- let nextNode = walker.nextNode() as HTMLElement;
576
+ if (!root) {
577
+ return;
578
+ }
579
+ let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
580
+ let walker = getFocusableTreeWalker(root, {tabbable, accept});
581
+ let nextNode = walker.nextNode() as FocusableElement;
558
582
  if (nextNode) {
559
583
  focusElement(nextNode, true);
560
584
  }
561
585
  return nextNode;
562
586
  },
563
- focusLast(opts = {}) {
587
+ focusLast(opts = defaultOptions) {
564
588
  let root = ref.current;
565
- let {tabbable} = opts;
566
- let walker = getFocusableTreeWalker(root, {tabbable});
589
+ if (!root) {
590
+ return;
591
+ }
592
+ let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts;
593
+ let walker = getFocusableTreeWalker(root, {tabbable, accept});
567
594
  let next = last(walker);
568
595
  if (next) {
569
596
  focusElement(next, true);
@@ -574,10 +601,10 @@ export function createFocusManager(ref: RefObject<HTMLElement>): FocusManager {
574
601
  }
575
602
 
576
603
  function last(walker: TreeWalker) {
577
- let next: HTMLElement;
578
- let last: HTMLElement;
604
+ let next: FocusableElement;
605
+ let last: FocusableElement;
579
606
  do {
580
- last = walker.lastChild() as HTMLElement;
607
+ last = walker.lastChild() as FocusableElement;
581
608
  if (last) {
582
609
  next = last;
583
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';