1212
1313import { useRef , useEffect } from 'react' ;
1414
15- const INPUT_TYPES_WHITE_LIST = {
15+ const INPUT_TYPES_WHITE_LIST : Record < string , boolean > = {
1616 text : true ,
1717 search : true ,
1818 url : true ,
@@ -28,27 +28,35 @@ const INPUT_TYPES_WHITE_LIST = {
2828 'datetime-local' : true
2929} ;
3030
31- export function useFocusVisible ( {
32- scope,
33- relativeDocument = document ,
34- className = 'garden-focus-visible' ,
35- dataAttribute = 'data-garden-focus-visible'
36- } = { } ) {
37- // console.log(scope.current)
31+ export interface IUseFocusVisibleProps {
32+ scope : React . RefObject < HTMLElement | null > ;
33+ relativeDocument ?: any ;
34+ className ?: string ;
35+ dataAttribute ?: string ;
36+ }
37+
38+ export function useFocusVisible (
39+ {
40+ scope,
41+ relativeDocument = document ,
42+ className = 'garden-focus-visible' ,
43+ dataAttribute = 'data-garden-focus-visible'
44+ } : IUseFocusVisibleProps = { } as any
45+ ) : void {
3846 if ( ! scope ) {
3947 throw new Error ( 'Error: the useFocusVisible() hook requires a "scope" property' ) ;
4048 }
4149
4250 const hadKeyboardEvent = useRef ( false ) ;
4351 const hadFocusVisibleRecently = useRef ( false ) ;
44- const hadFocusVisibleRecentlyTimeout = useRef ( null ) ;
52+ const hadFocusVisibleRecentlyTimeout = useRef < number | undefined > ( ) ;
4553
4654 useEffect ( ( ) => {
4755 /**
4856 * Helper function for legacy browsers and iframes which sometimes focus
4957 * elements like document, body, and non-interactive SVG.
5058 */
51- const isValidFocusTarget = el => {
59+ const isValidFocusTarget = ( el : Element ) => {
5260 if (
5361 el &&
5462 el !== scope . current &&
@@ -68,15 +76,19 @@ export function useFocusVisible({
6876 * `garden-focus-visible` class being added, i.e. whether it should always match
6977 * `:focus-visible` when focused.
7078 */
71- const focusTriggersKeyboardModality = el => {
72- const type = el . type ;
79+ const focusTriggersKeyboardModality = ( el : HTMLElement ) => {
80+ const type = ( el as HTMLInputElement ) . type ;
7381 const tagName = el . tagName ;
7482
75- if ( tagName === 'INPUT' && INPUT_TYPES_WHITE_LIST [ type ] && ! el . readOnly ) {
83+ if (
84+ tagName === 'INPUT' &&
85+ INPUT_TYPES_WHITE_LIST [ type ] &&
86+ ! ( el as HTMLInputElement ) . readOnly
87+ ) {
7688 return true ;
7789 }
7890
79- if ( tagName === 'TEXTAREA' && ! el . readOnly ) {
91+ if ( tagName === 'TEXTAREA' && ! ( el as HTMLTextAreaElement ) . readOnly ) {
8092 return true ;
8193 }
8294
@@ -92,7 +104,7 @@ export function useFocusVisible({
92104 /**
93105 * Whether the given element is currently :focus-visible
94106 */
95- const isFocused = el => {
107+ const isFocused = ( el : HTMLElement ) => {
96108 if ( el && ( el . classList . contains ( className ) || el . hasAttribute ( dataAttribute ) ) ) {
97109 return true ;
98110 }
@@ -104,19 +116,19 @@ export function useFocusVisible({
104116 * Add the `:focus-visible` class to the given element if it was not added by
105117 * the consumer.
106118 */
107- const addFocusVisibleClass = el => {
119+ const addFocusVisibleClass = ( el : HTMLElement ) => {
108120 if ( isFocused ( el ) ) {
109121 return ;
110122 }
111123
112124 el . classList . add ( className ) ;
113- el . setAttribute ( dataAttribute , true ) ;
125+ el . setAttribute ( dataAttribute , ' true' ) ;
114126 } ;
115127
116128 /**
117129 * Remove the `:focus-visible` class from the given element.
118130 */
119- const removeFocusVisibleClass = el => {
131+ const removeFocusVisibleClass = ( el : HTMLElement ) => {
120132 el . classList . remove ( className ) ;
121133 el . removeAttribute ( dataAttribute ) ;
122134 } ;
@@ -128,7 +140,7 @@ export function useFocusVisible({
128140 * Apply `:focus-visible` to any current active element and keep track
129141 * of our keyboard modality state with `hadKeyboardEvent`.
130142 */
131- const onKeyDown = e => {
143+ const onKeyDown = ( e : KeyboardEvent ) => {
132144 if ( e . metaKey || e . altKey || e . ctrlKey ) {
133145 return ;
134146 }
@@ -158,26 +170,26 @@ export function useFocusVisible({
158170 * via the keyboard (e.g. a text box)
159171 * @param {Event } e
160172 */
161- const onFocus = e => {
173+ const onFocus = ( e : FocusEvent ) => {
162174 // Prevent IE from focusing the document or HTML element.
163- if ( ! isValidFocusTarget ( e . target ) ) {
175+ if ( ! isValidFocusTarget ( e . target as HTMLElement ) ) {
164176 return ;
165177 }
166178
167- if ( hadKeyboardEvent . current || focusTriggersKeyboardModality ( e . target ) ) {
168- addFocusVisibleClass ( e . target ) ;
179+ if ( hadKeyboardEvent . current || focusTriggersKeyboardModality ( e . target as HTMLElement ) ) {
180+ addFocusVisibleClass ( e . target as HTMLElement ) ;
169181 }
170182 } ;
171183
172184 /**
173185 * On `blur`, remove the `:focus-visible` styling from the target.
174186 */
175- const onBlur = e => {
176- if ( ! isValidFocusTarget ( e . target ) ) {
187+ const onBlur = ( e : FocusEvent ) => {
188+ if ( ! isValidFocusTarget ( e . target as HTMLElement ) ) {
177189 return ;
178190 }
179191
180- if ( isFocused ( e . target ) ) {
192+ if ( isFocused ( e . target as HTMLElement ) ) {
181193 /**
182194 * To detect a tab/window switch, we look for a blur event
183195 * followed rapidly by a visibility change. If we don't see
@@ -186,12 +198,15 @@ export function useFocusVisible({
186198 hadFocusVisibleRecently . current = true ;
187199
188200 clearTimeout ( hadFocusVisibleRecentlyTimeout . current ) ;
189- hadFocusVisibleRecentlyTimeout . current = setTimeout ( ( ) => {
201+
202+ const timeoutId = setTimeout ( ( ) => {
190203 hadFocusVisibleRecently . current = false ;
191204 clearTimeout ( hadFocusVisibleRecentlyTimeout . current ) ;
192205 } , 100 ) ;
193206
194- removeFocusVisibleClass ( e . target ) ;
207+ hadFocusVisibleRecentlyTimeout . current = Number ( timeoutId ) ;
208+
209+ removeFocusVisibleClass ( e . target as HTMLElement ) ;
195210 }
196211 } ;
197212
@@ -202,8 +217,10 @@ export function useFocusVisible({
202217 *
203218 * This accounts for situations where focus enters the page from the URL bar.
204219 */
205- const onInitialPointerMove = e => {
206- if ( e . target . nodeName && e . target . nodeName . toLowerCase ( ) === 'html' ) {
220+ const onInitialPointerMove = ( e : MouseEvent | TouchEvent ) => {
221+ const nodeName = ( e . target as HTMLDocument ) . nodeName ;
222+
223+ if ( nodeName && nodeName . toLowerCase ( ) === 'html' ) {
207224 return ;
208225 }
209226
0 commit comments