import type { FocusScope , ScopeChangeEventHandler } from "./useFocusScope" ; import { isNil } from "./assertions" ; import { useLayoutEffect } from "react" ; export interface UseRovingFocusOptions { isDisabled ? : boolean ; } export function useRovingFocus ( scope , { isDisabled = false } = { } ) { useLayoutEffect ( ( ) => { if ( ! isDisabled ) { const handleFocus = ( event ) => { scope . getElements ( ) . forEach ( x => { if ( x . tabIndex === 0 ) { x . tabIndex = - 1 ; } } ) ; ( event . target as HTMLElement ) . tabIndex = 0 ; } ; const registerElement = ( element , isTabbable ) => { element . tabIndex = isTabbable ? 0 : - 1 ; element . addEventListener ( "focusin" , handleFocus , { capture : true } ) ; } ; const disposeElement = ( element ) => { element . removeEventListener ( "focusin" , handleFocus , { capture : true } ) ; } ; const initializeElements = ( ) => { scope . getElements ( ) . forEach ( ( x , index ) => { registerElement ( x , index === 0 ) ; } ) ; } ; initializeElements ( ) ; const onChange = ( newElements , oldElements ) => { oldElements . forEach ( disposeElement ) ; const tabbableIndex = newElements . findIndex ( x => x . tabIndex === 0 ) ; newElements . forEach ( ( x , index ) => { registerElement ( x , ( tabbableIndex === - 1 && index === 0 ) || tabbableIndex === index ) ; } ) ; } ; scope . registerChangeHandler ( onChange ) ; return ( ) => { scope . getElements ( ) . forEach ( disposeElement ) ; scope . removeChangeHandler ( onChange ) ; } ; } } , [ scope , isDisabled ] ) ; } export interface UseKeyedRovingFocusOptions extends UseRovingFocusOptions { keyProp ? : string ; } export function useKeyedRovingFocus ( scope , currentKey , { isDisabled = false , keyProp = "value" } = { } ) { useLayoutEffect ( ( ) => { if ( ! isDisabled ) { const setTabIndexes = ( elements ) => { if ( ! isNil ( currentKey ) ) { const tabbableIndex = ! isNil ( currentKey ) ? elements . findIndex ( x => x . getAttribute ( keyProp ) === currentKey . toString ( ) ) : - 1 ; elements . forEach ( ( x , index ) => { x . tabIndex = tabbableIndex === index ? 0 : - 1 ; } ) ; } else { elements . forEach ( ( x , index ) => { x . tabIndex = index === 0 ? 0 : - 1 ; } ) ; } } ; const initializeElements = ( ) => { setTabIndexes ( scope . getElements ( ) ) ; } ; initializeElements ( ) ; const onChange = ( newElements ) => { setTabIndexes ( newElements ) ; } ; scope . registerChangeHandler ( onChange ) ; return ( ) => { scope . removeChangeHandler ( onChange ) ; } ; } } , [ scope , currentKey , keyProp , isDisabled ] ) ; }