import { IStoreState, ID, IDimension } from '../../index.data'; import * as R from 'rambda'; import { getCell, getCellij } from './cell'; import { getColumn, getColumnByCell, getColumnByIndex, getColumnj } from './column'; import { getRow, getRowByCell, getRowi, getRowIndex } from './row'; import { ROW_INDICATOR_WIDTH } from '../../constants'; import { getTypedId } from '../../utils'; ///////////////////////////// get scope //////////////////////////////////////////////////// interface ISheetScope { fromI: number; toI: number; fromJ: number; toJ: number; } const empty_scope = { fromI: -1, fromJ: -1, toI: -1, toJ: -1, }; function getRowRangeScope(state: IStoreState, rowRange: [ID, ID]): ISheetScope { const i0 = getRowIndex(state, rowRange[0]); const i1 = getRowIndex(state, rowRange[1]); const j0 = 0; const j1 = state.columns.length - 1; // 注意: 在insert操作时, 假设先选选中cell0进行插入, 再已选中新插入的cell1, // 此时undo, 则立即删除cell1, 此时selection仍为cell1, 会报错 if (i0 < 0 || i1 < 0) { return empty_scope; } return { fromI: Math.min(i0, i1), toI: Math.max(i0, i1), fromJ: Math.min(j0, j1), toJ: Math.max(j0, j1), }; } function getColumnRangeScope(state: IStoreState, columnRange: [ID, ID]): ISheetScope { const i0 = 0; const i1 = state.rows.length - 1; const j0 = getColumnj(state, columnRange[0]); const j1 = getColumnj(state, columnRange[1]); // 注意: 在insert操作时, 假设先选选中cell0进行插入, 再已选中新插入的cell1, // 此时undo, 则立即删除cell1, 此时selection仍为cell1, 会报错 if (j0 < 0 || j1 < 0) { return empty_scope; } return { fromI: Math.min(i0, i1), toI: Math.max(i0, i1), fromJ: Math.min(j0, j1), toJ: Math.max(j0, j1), }; } function getCellRangeScope(state: IStoreState, cellRange: [ID, ID]): ISheetScope { // 必须 const id0 = getTypedId(cellRange[0] + ''); const id1 = getTypedId(cellRange[1] + ''); // 注意: 在insert操作时, 假设先选选中cell0进行插入, 再已选中新插入的cell1, // 此时undo, 则立即删除cell1, 此时selection仍为cell1, 会报错 if (!state.cells[cellRange[0]] || !state.cells[cellRange[1]]) { return empty_scope; } const i0 = getRowIndex(state, getRowByCell(state, id0).id); const i1 = getRowIndex(state, getRowByCell(state, id1).id); const j0 = getColumnj(state, getColumnByCell(state, id0).id!); const j1 = getColumnj(state, getColumnByCell(state, id1).id!); const { rowspan: rowspan0 = 1, colspan: colspan0 = 1 } = getCell(state, cellRange[0])!; const { rowspan: rowspan1 = 1, colspan: colspan1 = 1 } = getCell(state, cellRange[1])!; return { fromI: Math.min(i0, i1), toI: Math.max(i0 + rowspan0 - 1, i1 + rowspan1 - 1), fromJ: Math.min(j0, j1), toJ: Math.max(j0 + colspan0 - 1, j1 + colspan1 - 1), }; } export function getSelectionScope(state: IStoreState): ISheetScope { const { selectionType } = state; if (!selectionType) throw new Error('getSelectionScope 出错: selection type 不可为空'); return selectionType === 'cell' ? getCellRangeScope(state, state.selectedCellRange) : selectionType === 'row' ? getRowRangeScope(state, state.selectedRowRange) : selectionType === 'column' ? getColumnRangeScope(state, state.selectedColumnRange) : empty_scope; } //////////////////////////////////// helper //////////////////////////////////////////////// const empty_dimension = { width: 0, offsetWidth: 0, left: 0, offsetLeft: 0, height: 0, offsetHeight: 0, top: 0, offsetTop: 0, }; function getDimensionByScope(state: IStoreState, scope: ISheetScope): IDimension { if (scope.fromI < 0 || scope.fromJ < 0 || scope.toI < 0 || scope.toJ < 0) { return empty_dimension; } const { rows, columns, colDimensions, rowDimensions } = state; const rowDim0 = rowDimensions[rows[scope.fromI].id]; const rowDim1 = rowDimensions[rows[scope.toI].id]; const colDim0 = colDimensions[columns[scope.fromJ].id!]; const colDim1 = colDimensions[columns[scope.toJ].id!]; if ([rowDim0, rowDim1, colDim0, colDim1].some(d => !d)) { return empty_dimension; } return { width: colDim1.left + colDim1.width - colDim0.left, offsetWidth: colDim1.offsetLeft + colDim1.offsetWidth - colDim0.offsetLeft, left: colDim0.left, offsetLeft: colDim0.offsetLeft, height: rowDim1.top + rowDim1.height - rowDim0.top, offsetHeight: rowDim1.offsetTop + rowDim1.offsetHeight - rowDim0.offsetTop, top: rowDim0.top, offsetTop: rowDim0.offsetTop, }; } /////////////////////////////////// public ///////////////////////////////////////////////////// /** * 获取当前选择内容的dimensions * 由于 rowIndicator 和 colIndicator 的存在, 所以需要offset * @param state * @param offset 计算出的left需要加上offset.x, 计算出的right需要加上offset.y * @returns dimensions */ export function getSelectedDimensions(state: IStoreState): IDimension { const scope = getSelectionScope(state); return getDimensionByScope(state, scope); } /** * 取得fromCellId与toCellId的包裹wrapper的dimension * @param state * @param cellRange * @returns {width, height, left, top} */ export function getCellRangeDimensions( state: IStoreState, [fromCellId, toCellId]: [ID, ID], ): IDimension { const scope = getCellRangeScope(state, [fromCellId, toCellId]); return getDimensionByScope(state, scope); } export function getColumnRangeDimensions( state: IStoreState, [fromColumnId, toColumnId]: [ID, ID], ): IDimension { const scope = getColumnRangeScope(state, [fromColumnId, toColumnId]); return getDimensionByScope(state, scope); } export function getRowRangeDimensions( state: IStoreState, [fromRowId, toRowId]: [ID, ID], ): IDimension { const scope = getRowRangeScope(state, [fromRowId, toRowId]); return getDimensionByScope(state, scope); } /** * 获得column的左上角和右下角坐标 */ export function getColumnDimensions(state: IStoreState, columnId: ID): IDimension { const idx = getColumnj(state, columnId); const scope = { fromI: 0, toI: state.rows.length - 1, fromJ: idx, toJ: idx }; return getDimensionByScope(state, scope); } /** * 获得row的左上角和右下角坐标 */ export function getRowDimensions(state: IStoreState, rowId: ID): IDimension { const rowIndex = getRowIndex(state, rowId); const scope = { fromI: rowIndex, toI: rowIndex, fromJ: 0, toJ: state.columns.length - 1 }; return getDimensionByScope(state, scope); } /** * 获得cell的左上角和右下角坐标 */ export function getCellDimensions(state: IStoreState, _cellId: ID): IDimension { const cellId = getTypedId(_cellId as any); const cell = getCell(state, cellId)!; const { i, j } = getCellij(state, cellId); const { rowspan = 1, colspan = 1 } = cell; const scope = { fromI: i, toI: i + rowspan - 1, fromJ: j, toJ: j + colspan - 1, }; console.log('scope', scope); return getDimensionByScope(state, scope); } ///////////////////////////////////// else /////////////////////////////////////////////////////// export function selectionIsOneCell(state: IStoreState) { const { selectionType, selectedCellRange } = state; return ( selectionType === 'cell' && !!selectedCellRange && selectedCellRange.length === 2 && selectedCellRange[0] === selectedCellRange[1] ); } // 获取[当前]单元格 // 意义: // 单选时, 总是该单元格本身 // 多选时, 为选择时的第一个单元格 export function getPresentCell(state: IStoreState): ID | null { return selectionIsOneCell(state) ? state.selectedCellRange[0] : null; } // 获取当前处于显示状态 且可正常输入的 cell export function getActivedCell(state: IStoreState): ID | null { return state.isActiving ? getPresentCell(state) : null; } export function isCellSingleSelected(state: IStoreState, id: ID) { return selectionIsOneCell(state) && state.selectedCellRange[0] === id + ''; }