import { ICommand } from 'valor-unistore-undo'; import { IStoreState, ID, IRow, ICell, ICellNN } from '../index.data'; import { IRowWithCell } from '../SpreadSheetProvider/index.data'; import { insertTreeRow, deleteTreeRow, ensureSelectionWhenTreeDelete } from '../store/actions/row'; import { getCurrentRowIndex, restoreSelection } from './helper'; import { getRow, getRowi, isTreeRow } from '../store/selectors'; import * as R from 'rambda'; import { SpreadSheetRuntime } from '../RuntimeContext'; export interface InsertTreeRowCommandParams { type: 'sibling' | 'child' | 'auto'; rows: IRowWithCell[]; helpers: { normalizeRow: (rows: IRow[], row: IRowWithCell) => IRowWithCell; }; } interface IUndoContext { // 恢复父cell.value ( 仅在插入child, 并且parentId!=='-1'时用到) parentRowCells: Record; insertAt: number; // 第一行插在哪里 ( 供undo ) } class InsertTreeRowCommand extends ICommand< IStoreState, InsertTreeRowCommandParams, IUndoContext, SpreadSheetRuntime > { baseI?: number; /** * 批量插入, 注意 要插入的 rows必须是一个层级结构, 而不能是一个展平的或混合的结构 * 以下是正确的: * - row1 * - row1.1 * - row1.2 */ execute = () => { const { rows: oldRows, type: _type } = this.params; const rowsToInsert = oldRows.map(row => Object.assign({}, row, { area: 'tree' })); const state = this.store.getState(); const baseI = R.is(Number, this.baseI) ? this.baseI! : getCurrentRowIndex(state); this.baseI = baseI; if (baseI < 0) return false; const baseRow = state.rows[baseI]; if (!isTreeRow(baseRow) || baseI >= state.rows.length - 1) { // 不记录历史 return false; } // 暂不考虑 当前行.level = 3 , 而插入行.level=2的情形, 所以暂时用不到 if (_type === 'auto' && baseRow.level! > rowsToInsert[0].level!) { console.info('暂不考虑 当前行.level = 3 , 而插入行.level=2的情形, 忽略插入操作'); return false; } // 将auto转换为sibling/child const type = _type !== 'auto' ? _type : baseRow.level === rowsToInsert[0].level ? 'sibling' : 'child'; const newState = insertTreeRow( state, type, baseI, rowsToInsert, undefined, this.params.helpers, this.runtime!, ); this.store.setState(Object.assign(newState, { restoreSelection: true }) as any); // 保存undo环境 const treeContext = newState.treeContext![rowsToInsert[0].id]; const parentRow = treeContext['parentId'] !== '-1' && type === 'child' ? getRow(state, treeContext['parentId'])! : undefined; const parentRowCells = parentRow ? R.pick(parentRow.cellIds!.filter(Boolean) as any, state.cells) : {}; this.undoContext = { insertAt: getRowi(newState as IStoreState, rowsToInsert[0].id), parentRowCells: R.clone(parentRowCells), }; // 收尾 setTimeout(() => { this.runtime!.setSheetDimensions(); // restoreSelection(this.store, this.params['rows'][0].cells[0]!.id); this.runtime!.fsmService.send({ type: 'SELECT.ROW', selectedRow: rowsToInsert[0].id }); }); return true; // 记录历史 }; undo = () => { const deleteId = this.store.getState().rows[this.undoContext.insertAt].id; ensureSelectionWhenTreeDelete(this.store, deleteId, this.runtime!, () => { const state = this.store.getState(); // 特殊逻辑: 如果A(value:1) -> A1(value:100), 插入A1成功后, A可能变成100 // 此时, undo后, A.formulaDisabled=true, 因此不会重算, 因此 A.value=100, 撤销失败 // 所以需要预存A.value然后恢复k const stateWithParentCells = { ...state, cells: { ...state.cells, ...this.undoContext.parentRowCells }, }; const newState = deleteTreeRow( stateWithParentCells, this.undoContext.insertAt, this.params.helpers, this.runtime!, ); this.store.setState(Object.assign(newState, { readyDimensions: true })); }); }; } export default InsertTreeRowCommand;