import React from 'react' import type { GridRowId } from '../../types' import { useGridContext, useGridSelector } from '../grid-context' import { getNextSubFocusIndex, getNormalizedIndex, shouldPreventNavigation, } from './utils' import type { SubNavigationContextType } from './types' import type { GridFocusArea } from '../../state' export function useSubFocusHandler( columnId: string, area: GridFocusArea, rowId: GridRowId = '', functionalIndexes = 0 ) { const grid = useGridContext() const hasFocus = useGridSelector((state) => grid.selectors.selectHasFocus(state, columnId, area, rowId) ) const subFocus = useGridSelector((state) => hasFocus ? grid.selectors.selectSubFocus(state) : 'first' ) const [ rendererWantsToDisplayFocusStyles, setRendererWantsToDisplayFocusStyles, ] = React.useState(false) const [registeredIndexes, setRegisteredIndexes] = React.useState(0) const registerIndexes = React.useCallback((indexes: number) => { setRegisteredIndexes(indexes) }, []) const availableIndexes = React.useMemo(() => { //If there are functional indexes we need to activate sub-navigation, so we add one for the renderer in case there are no registered indexes const implicitRendererIndex = !registeredIndexes && functionalIndexes ? 1 : 0 return registeredIndexes + functionalIndexes + implicitRendererIndex }, [functionalIndexes, registeredIndexes]) const indexNormalized = getNormalizedIndex(subFocus, availableIndexes) const onKeyDown: React.KeyboardEventHandler = React.useCallback( (e) => { if (hasFocus && !e.shiftKey) { if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') { const { area, columnId, rowId, subFocus } = grid.selectors.selectCurrentFocus(grid.getState()) const direction = e.key === 'ArrowRight' ? 'right' : 'left' if ( shouldPreventNavigation( direction, subFocus, availableIndexes ) ) { e.stopPropagation() e.preventDefault() grid.dispatch({ type: 'updateFocus', payload: { columnId, rowId, area, subFocus: getNextSubFocusIndex( direction, subFocus, availableIndexes ), }, }) } } } }, [availableIndexes, grid, hasFocus] ) const setCurrentSubFocusIndex = React.useCallback( (index: number | 'first' | 'last') => { const { columnId, rowId, area } = grid.selectors.selectCurrentFocus( grid.getState() ) grid.dispatch({ type: 'updateFocus', payload: { columnId, rowId, area, subFocus: index, }, }) }, [grid] ) const value: SubNavigationContextType = React.useMemo( () => ({ registerIndexes, registeredIndexes, currentSubFocusIndex: indexNormalized - functionalIndexes, setRendererWantsToDisplayFocusStyles, hasFocus, setCurrentSubFocusIndex, }), [ functionalIndexes, hasFocus, indexNormalized, registerIndexes, registeredIndexes, setCurrentSubFocusIndex, ] ) return { /** * function to register the amount of indexes (stops) in the renderer */ registerIndexes, /** * total amount of indexes (stops) in the column */ registeredIndexes, /** * keydown handler to be passed to the cell wrapping element */ onKeyDown, /** * current sub-focus index (including functional level indexes) */ subFocus, /** * if the renderer has called for taking care of the styling */ rendererWantsToDisplayFocusStyles, /** * memoized value to be passed to the `SubNavigationContext` -provider */ value, } }