import { createSelector } from '../utils' import type { StoreSimpleSelectors } from '../selectors' import type { CellColSpanParams, ColumnField, GridColumnBorder, } from '../../types' import { getLowestLeafs, getParentGroups } from '../reducer/column-utils' import type { ColumnState, GridFocusArea } from '../types' import { ACTIONS_COLUMN_ID, SELECTION_COLUMN_ID } from '../../constants' type ColumnBorderConfig = Record< GridFocusArea, { left: boolean; right: boolean } > function enableLeft(border?: GridColumnBorder) { return border === 'left' || border === 'both' } function enableRight(border?: GridColumnBorder) { return border === 'right' || border === 'both' } function getIndexInfo( columnId: string, parentId: string, entities: Record, sortedIds: string[], hiddenIds: string[] ) { const parentLeafs = getLowestLeafs(parentId, entities, hiddenIds).map( (e) => e.id ) const siblings = sortedIds.filter((id) => parentLeafs?.includes(id)) const index = siblings.indexOf(columnId) const first = index === 0 const last = index === siblings.length - 1 return { first, last } } export default function generateSelectors( selectors: Pick< StoreSimpleSelectors, | 'selectColumnIdsWithHidden' | 'selectColumnEntities' | 'selectHiddenIds' | 'selectIsSpreadsheet' > ) { const selectStickyColumnIds = createSelector( [selectors.selectColumnEntities], (entities) => { const left = new Set() const right = new Set() for (const column of Object.values(entities)) { if (column.data.sticky === 'left') { left.add(column.id) } else if (column.data.sticky === 'right') { right.add(column.id) } } return { left, right } } ) const selectColSpanConfig = createSelector( [ selectors.selectColumnEntities, selectors.selectColumnIdsWithHidden, selectors.selectHiddenIds, ], (entities, ids, hiddenIds) => { const colSpanFunctions = ids.reduce< { sourceColumn: string colSpan: | ColumnField[] | (( props: CellColSpanParams ) => ColumnField[]) }[] >((acc, id) => { const column = entities[id] const colSpan = column?.data.cell?.colSpan if (colSpan) { acc.push({ sourceColumn: id, colSpan }) } return acc }, []) const idsWithoutHidden = ids.filter( (colId) => !hiddenIds.includes(colId) && colId !== SELECTION_COLUMN_ID && colId !== ACTIONS_COLUMN_ID ) return { colSpanFunctions, columnIds: idsWithoutHidden, } } ) const selectBorderLayout = createSelector( [ selectors.selectColumnEntities, selectors.selectColumnIdsWithHidden, selectors.selectHiddenIds, selectors.selectIsSpreadsheet, ], (entities, idsWithHidden, hiddenIds, isSpreadsheet) => { const ids = idsWithHidden.filter((id) => !hiddenIds.includes(id)) const sortedColumnData = ids .map((id) => entities[id]?.data) .filter(Boolean) const borderMap = new Map() for (let i = 0; i < sortedColumnData.length; i++) { const column = sortedColumnData[i] const prevColumn = sortedColumnData[i - 1] const veryFirst = i === 0 const columnBaseBorder = column.border let headerLeft = enableLeft( columnBaseBorder === 'left' || isSpreadsheet ? 'both' : undefined ) let headerRight = enableRight( columnBaseBorder === 'right' || isSpreadsheet ? 'both' : undefined ) let cellLeft = headerLeft let cellRight = headerRight let footerLeft = headerLeft let footerRight = headerRight const [firstParent, secondParent] = getParentGroups( column.id, entities ) if (firstParent) { const position = getIndexInfo( column.id, firstParent.id, entities, ids, hiddenIds ) const parentPosition = secondParent ? getIndexInfo( column.id, secondParent.id, entities, ids, hiddenIds ) : null const parentBorder = firstParent.data.border const secondParentBorder = secondParent?.data.border if (position.first) { headerLeft = true if ( enableLeft(parentBorder) || (parentPosition?.first && enableLeft(secondParentBorder)) ) { cellLeft = true footerLeft = true } } if (position.last) { headerRight = true if ( enableRight(parentBorder) || (parentPosition?.last && enableRight(secondParentBorder)) ) { cellRight = true footerRight = true } } } const config: ColumnBorderConfig = { header: { left: veryFirst ? headerLeft : false, right: headerRight, }, body: { left: veryFirst ? cellLeft : false, right: cellRight, }, footer: { left: veryFirst ? footerLeft : false, right: footerRight, }, } if (prevColumn) { const prevColumnConfig = borderMap.get(prevColumn.id) if (prevColumnConfig) { if (headerLeft) prevColumnConfig.header.right = true if (cellLeft) prevColumnConfig.body.right = true if (footerLeft) prevColumnConfig.footer.right = true } } borderMap.set(column.id, config) } return borderMap } ) return { selectStickyColumnIds, selectBorderLayout, selectColSpanConfig } } export type SelectBorderLayout = ReturnType< typeof generateSelectors >['selectBorderLayout'] export type SelectColSpanConfig = ReturnType< typeof generateSelectors >['selectColSpanConfig'] export type SelectStickyColumnIds = ReturnType< typeof generateSelectors >['selectStickyColumnIds']