import { setSelection } from '../data-tools' import type { GridState } from '../state' import type { GridRowId } from '../types' import type { ApiSection } from './types' export type GridSelectionApi = { selectAll: (ignoreFilter?: boolean) => void selectNone: (ignoreFilter?: boolean) => void select: ( id: GridRowId, value?: boolean, replaceSelection?: boolean, rangeSelection?: boolean ) => void } type SelectionEdges = { max: number min: number } const getSelectionEdges = ( selection: GridState['selection'], rowIds: GridRowId[] ): SelectionEdges => [...selection.selection].reduce( (prev, next) => { let newMax = prev.max let newMin = prev.min const nextIndex = rowIds.indexOf(next) if (nextIndex > newMax) { newMax = nextIndex } if (nextIndex < newMin) { newMin = nextIndex } return { max: newMax, min: newMin, } }, { max: 0, min: Infinity } ) const getRowRangeSelection = ( target: GridRowId, selection: GridState['selection'], rowIds: GridRowId[] ): Set => { const targetIndex = rowIds.indexOf(target) if (selection.anchor && selection.selection.has(selection.anchor)) { //We have an anchor and the anchor is included in the selection const anchorIndex = rowIds.indexOf(selection.anchor) const newSelection = new Set( rowIds.slice( Math.min(anchorIndex, targetIndex), Math.max(targetIndex, anchorIndex) + 1 ) ) return newSelection } else if (selection.selection.size) { //We have no anchor, expand current range if any const edges = getSelectionEdges(selection, rowIds) const floor = targetIndex > edges.max ? edges.min : targetIndex const ceiling = targetIndex < edges.min ? edges.max : targetIndex const newSelection = new Set(rowIds.slice(floor, ceiling + 1)) return newSelection } return new Set([target]) } export const createSelectionApi: ApiSection = ( store, events ) => ({ selectAll: () => { const rowIds = store.selectors.selectAllSelectableIds(store.getState()) events.emit('onSelectionChange', rowIds) }, selectNone: () => { events.emit('onSelectionChange', new Set()) }, select: ( id, value = true, replaceSelection = false, rangeSelection = false ) => { const selection = store.getState().selection if (selection.mode === 'none') { return } const state = store.getState() const rowIds = store.selectors.selectRowIds(state) const collection = store.selectors.selectRowCollection(state) if (rangeSelection && selection.mode === 'multi') { // TODO, need to merge this with setSelection behavior so it properly // handles selecting and de-selecting groups events.emit( 'onSelectionChange', getRowRangeSelection(id, selection, rowIds) ) } else if (value === true) { const matchingIds = store.selectors.selectFilterMatchesShowOnly(state) const startingSelection = replaceSelection || selection.mode === 'single' ? new Set() : selection.selection const newSelection = setSelection( rowIds, collection, startingSelection, id, true, matchingIds ) events.emit('onSelectionChange', newSelection) } else { const newSelection = setSelection( rowIds, collection, selection.selection, id, false ) events.emit('onSelectionChange', newSelection) } }, })