import { createContext , ReactNode , useMemo , useState } from 'react' ; import { useControlled } from '../../hooks' ; import { SelectChangeEvent } from './select' ; interface UseSelectOptions < V > { value ? : V ; options : V [ ] ; isOpen ? : boolean ; onChange ? : ( e : SelectChangeEvent < V > ) => void ; onValue ? : ( value : V ) => void ; onClose ? : ( ) => void ; onOpen ? : ( ) => void ; } interface UseSelectOutput < V > { isOpen : boolean ; focusedIndex : number | null ; selectedIndex : number | null ; open : ( ) => void ; close : ( ) => void ; setFocusedIndex : ( index : number ) => void ; focusPreviousIndex : ( ) => void ; focusNextIndex : ( ) => void ; handleChange : ( e : SelectChangeEvent < V > ) => void ; } const useSelect = < V > ( { value , options , isOpen : controlledOpen , onOpen , onClose , onChange , onValue , } ) : UseSelectOutput < V > => { const [ isOpen , setIsOpen ] = useControlled ( controlledOpen , false ) ; const [ focusedIndex , setFocusedIndex ] = useState < UseSelectOutput < V > [ 'focusedIndex' ] > ( null ) ; const selectedIndex = useMemo ( ( ) => { const index = options . findIndex ( ( option ) => option === value ) ; return index !== - 1 ? index : null ; } , [ options , value ] ) ; const open = ( ) => { onOpen ?. ( ) ; setIsOpen ( true ) ; setFocusedIndex ( selectedIndex || 0 ) ; } ; const close = ( ) => { onClose ?. ( ) ; setIsOpen ( false ) ; } ; const focusPreviousIndex = ( ) => { if ( focusedIndex !== null && focusedIndex !== 0 ) { setFocusedIndex ( focusedIndex - 1 ) ; } } ; const focusNextIndex = ( ) => { if ( focusedIndex !== null && focusedIndex !== options . length - 1 ) { setFocusedIndex ( focusedIndex + 1 ) ; } } ; const handleChange = ( e ) => { onChange ?. ( e ) ; onValue ?. ( e . value ) ; } ; return { open , close , isOpen : isOpen || false , focusedIndex , setFocusedIndex , focusPreviousIndex , focusNextIndex , handleChange , selectedIndex , } ; } ; export default useSelect ; export const SelectContext = createContext ( { } as UseSelectOutput < any > ) ; interface SelectProviderProps < V > extends UseSelectOutput < V > { children : ReactNode ; } export const SelectProvider = < V > ( { children , ... rest } ) => { return < SelectContext . Provider value = { rest } > { children } < /SelectContext.Provider>; } ;