import { createSelector } from '../utils' import type { StoreSimpleSelectors } from '../selectors' import type { GridState } from '../reducer' import type { GridRowId } from '../../types' import type { GridFocusArea, HeaderHierarchyGroup } from '../types' import { ROW_FOCUS_ID } from '../../constants' import type { GridFocusState } from '../reducer/focus' import { getHeaderRowIndex } from '../../api/focus-utils' import type { SelectColumnSpanByRowId } from './row' const isValidRow = ( rowId: GridRowId, area: GridFocusArea, sortedRowIds: GridRowId[] ) => { const rowIndex = Math.min( sortedRowIds.indexOf(rowId), sortedRowIds.length - 1 ) if (rowIndex < 0 && !(area === 'footer' && rowId === '0')) { return false } return true } const groupHeaderHasFocus = ( rowId: GridRowId, focus: GridFocusState['focus'], group: HeaderHierarchyGroup | undefined, levels: number, headerRowIds: GridRowId[] ) => { if (!group) { return false } const groupRow = levels - group.level const rowIndex = getHeaderRowIndex(headerRowIds, rowId) const focusedRowIndex = getHeaderRowIndex(headerRowIds, focus.rowId) let ret = false if (groupRow === rowIndex && group.columnIds.includes(focus.columnId)) { ret = focusedRowIndex === groupRow || (focusedRowIndex < groupRow && !group.parentIds.length) } return ret } const columnHeaderHasFocus = ( columnId: string, focus: GridFocusState['focus'], rowId: GridRowId, groups: HeaderHierarchyGroup[], levels: number, headerRowIds: GridRowId[] ) => { const parent = groups.find((g) => g.columnIds.includes(columnId)) const focusedRowIndex = getHeaderRowIndex(headerRowIds, focus.rowId) const hasParent = parent && parent.level === levels - focusedRowIndex const ret = (focus.rowId === rowId || !hasParent) && focus.columnId === columnId return ret } export default function generateSelectors( selectors: Pick< StoreSimpleSelectors, | 'selectRawCurrentFocus' | 'selectUnsortedColumnIds' | 'selectHeaderHierarchy' | 'selectHiddenIds' | 'selectAreHeadersHidden' >, selectRowIds: (state: GridState) => GridRowId[], selectHeaderRowIds: (state: GridState) => GridRowId[], selectColumnSpanByRowId: SelectColumnSpanByRowId ) { const selectCurrentFocus = createSelector( [ selectors.selectRawCurrentFocus, selectors.selectUnsortedColumnIds, selectRowIds, selectHeaderRowIds, selectors.selectHiddenIds, selectors.selectAreHeadersHidden, ], ( currentFocus, columnIds, sortedRowIds, headerRowIds, hiddenIds, areHeadersHidden ) => { let { columnId, rowId, area } = currentFocus const { subFocus } = currentFocus const columnIdsWithoutHidden = columnIds.filter( (id) => !hiddenIds.includes(id) ) const columnIndex = Math.max( columnIds .filter((id) => !hiddenIds.includes(id)) .indexOf(columnId), 0 ) if (area !== 'header' && !isValidRow(rowId, area, sortedRowIds)) { area = 'header' rowId = headerRowIds[0] } if (area === 'header' && areHeadersHidden) { area = 'body' rowId = sortedRowIds[0] } if ( columnIdsWithoutHidden[columnIndex] !== columnId && columnId !== ROW_FOCUS_ID ) { columnId = columnIdsWithoutHidden[columnIndex] } if ( area !== currentFocus.area || columnId !== currentFocus.columnId || rowId !== currentFocus.rowId || subFocus !== currentFocus.subFocus ) { return { columnId, rowId, area, subFocus, } } return currentFocus } ) const selectHasFocus = ( state: GridState, columnId: string, area: GridFocusArea, rowId: GridRowId ) => { const focus = selectCurrentFocus(state) const headerRowIds = selectHeaderRowIds(state) if (area === 'header') { if (focus.area !== 'header') { return false } const rowIndex = getHeaderRowIndex(headerRowIds, rowId) const { levels, groups } = selectors.selectHeaderHierarchy(state) if (rowIndex === levels) { //We are at column header level return columnHeaderHasFocus( columnId, focus, rowId, groups, levels, headerRowIds ) } else if (rowIndex < levels) { //We are at group header level return groupHeaderHasFocus( headerRowIds[rowIndex], focus, groups.find((g) => g.id === columnId), levels, headerRowIds ) } return false } else { if (focus.rowId !== rowId || focus.area !== area) { return false } const columnSpan = selectColumnSpanByRowId(state, rowId) const config = columnSpan?.get(columnId) if (config?.positionColumnIds?.includes(focus.columnId)) { return true } return focus.columnId === columnId } } return { selectCurrentFocus, selectHasFocus } } export type SelectCurrentFocus = ReturnType< typeof generateSelectors >['selectCurrentFocus'] export type SelectHasFocus = ReturnType< typeof generateSelectors >['selectHasFocus']