diff --git a/examples/src/sheets/main.ts b/examples/src/sheets/main.ts index 57eeb2dcec8..42c0e07038b 100644 --- a/examples/src/sheets/main.ts +++ b/examples/src/sheets/main.ts @@ -23,7 +23,6 @@ import { UniverDocsMentionUIPlugin } from '@univerjs/docs-mention-ui'; import { UniverDocsUIPlugin } from '@univerjs/docs-ui'; import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'; import { UniverRenderEnginePlugin } from '@univerjs/engine-render'; -import { DEFAULT_WORKBOOK_DATA_DEMO } from '@univerjs/mockdata'; import { UniverRPCMainThreadPlugin } from '@univerjs/rpc'; import { UniverSheetsPlugin } from '@univerjs/sheets'; import { UniverSheetsConditionalFormattingPlugin } from '@univerjs/sheets-conditional-formatting'; @@ -127,7 +126,75 @@ userManagerService.setCurrentUser(mockUser); // create univer sheet instance if (!IS_E2E) { - univer.createUnit(UniverInstanceType.UNIVER_SHEET, DEFAULT_WORKBOOK_DATA_DEMO); + univer.createUnit(UniverInstanceType.UNIVER_SHEET, { + id: 'test', + appVersion: '3.0.0-alpha', + sheets: { + // 1 + // 2-3- + // 4 + // | + sheet1: { + id: 'sheet1', + cellData: { + 0: { + 0: { + v: 'A1', + s: 's1', + }, + }, + 1: { + 1: { + v: 'B2', + s: 's2', + }, + 4: { + v: 'E2', + s: 's3', + }, + }, + 2: { + 1: { + v: 'B3', + s: 's4', + }, + }, + }, + mergeData: [ + { startRow: 1, endRow: 1, startColumn: 2, endColumn: 3 }, + { + startRow: 2, + endRow: 3, + startColumn: 2, + endColumn: 2, + }, + { + startRow: 10, + endRow: 15, + startColumn: 2, + endColumn: 2, + }, + { + startRow: 10, + endRow: 10, + startColumn: 10, + endColumn: 15, + }, + ], + rowCount: 20, + columnCount: 20, + }, + }, + locale: LocaleType.ZH_CN, + name: '', + sheetOrder: [], + styles: { + s1: { bg: { rgb: '#ff0000' } }, + s2: { bl: 0 }, + s3: { bl: 1 }, + s4: { fs: 12 }, + }, + }); } setTimeout(() => { diff --git a/packages/sheets-drawing-ui/src/controllers/sheet-drawing-transform-affected.controller.ts b/packages/sheets-drawing-ui/src/controllers/sheet-drawing-transform-affected.controller.ts index 950d89ce9f6..f7b5aeb4133 100644 --- a/packages/sheets-drawing-ui/src/controllers/sheet-drawing-transform-affected.controller.ts +++ b/packages/sheets-drawing-ui/src/controllers/sheet-drawing-transform-affected.controller.ts @@ -15,7 +15,7 @@ */ import type { ICommandInfo, IDrawingParam, IMutationInfo, IRange, ITransformState, Nullable, Workbook } from '@univerjs/core'; -import type { IInsertColCommandParams, IInsertRowCommandParams, IMoveColsCommandParams, IMoveRangeCommandParams, IMoveRowsCommandParams, IRemoveRowColCommandParams, ISetColHiddenMutationParams, ISetColVisibleMutationParams, ISetRowHiddenMutationParams, ISetRowVisibleMutationParams, ISetSpecificColsVisibleCommandParams, ISetSpecificRowsVisibleCommandParams, ISetWorksheetActiveOperationParams, ISetWorksheetColWidthMutationParams, ISetWorksheetRowHeightMutationParams, ISetWorksheetRowIsAutoHeightMutationParams } from '@univerjs/sheets'; +import type { IDeleteRangeMoveLeftCommandParams, IDeleteRangeMoveUpCommandParams, IInsertColCommandParams, IInsertRowCommandParams, IMoveColsCommandParams, IMoveRangeCommandParams, IMoveRowsCommandParams, InsertRangeMoveDownCommandParams, InsertRangeMoveRightCommandParams, IRemoveRowColCommandParams, ISetColHiddenMutationParams, ISetColVisibleMutationParams, ISetRowHiddenMutationParams, ISetRowVisibleMutationParams, ISetSpecificColsVisibleCommandParams, ISetSpecificRowsVisibleCommandParams, ISetWorksheetActiveOperationParams, ISetWorksheetColWidthMutationParams, ISetWorksheetRowHeightMutationParams, ISetWorksheetRowIsAutoHeightMutationParams } from '@univerjs/sheets'; import type { ISheetDrawing, ISheetDrawingPosition } from '@univerjs/sheets-drawing'; import { Disposable, ICommandService, Inject, IUniverInstanceService, Rectangle } from '@univerjs/core'; import { type IDrawingJsonUndo1, IDrawingManagerService } from '@univerjs/drawing'; @@ -87,6 +87,7 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen this.disposeWithMe( this._sheetInterceptorService.interceptCommand({ + // eslint-disable-next-line complexity getMutations: (commandInfo) => { if (!UPDATE_COMMANDS.includes(commandInfo.id)) { return { redos: [], undos: [] }; @@ -106,16 +107,16 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen } else if (cId === RemoveColCommand.id) { return this._moveColInterceptor(commandInfo.params as IRemoveRowColCommandParams, 'remove'); } else if (cId === DeleteRangeMoveLeftCommand.id) { - const { range } = commandInfo.params as IRemoveRowColCommandParams; + const { range } = commandInfo.params as IDeleteRangeMoveLeftCommandParams; return this._getRangeMoveUndo(range, RangeMoveUndoType.deleteLeft); } else if (cId === DeleteRangeMoveUpCommand.id) { - const { range } = commandInfo.params as IRemoveRowColCommandParams; + const { range } = commandInfo.params as IDeleteRangeMoveUpCommandParams; return this._getRangeMoveUndo(range, RangeMoveUndoType.deleteUp); } else if (cId === InsertRangeMoveDownCommand.id) { - const { range } = commandInfo.params as IRemoveRowColCommandParams; + const { range } = commandInfo.params as InsertRangeMoveDownCommandParams; return this._getRangeMoveUndo(range, RangeMoveUndoType.insertDown); } else if (cId === InsertRangeMoveRightCommand.id) { - const { range } = commandInfo.params as IRemoveRowColCommandParams; + const { range } = commandInfo.params as InsertRangeMoveRightCommandParams; return this._getRangeMoveUndo(range, RangeMoveUndoType.insertRight); } else if (cId === SetRowHiddenCommand.id || cId === SetSpecificRowsVisibleCommand.id) { const params = commandInfo.params as ISetRowHiddenMutationParams | ISetSpecificRowsVisibleCommandParams; @@ -569,7 +570,7 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen return this._createUndoAndRedoMutation(unitId, subUnitId, updateDrawings); } - private _getUnitIdAndSubUnitId(params: IInsertRowCommandParams | IRemoveRowColCommandParams, type: 'insert' | 'remove') { + private _getUnitIdAndSubUnitId(params: IInsertRowCommandParams | IRemoveRowColCommandParams | IInsertColCommandParams, type: 'insert' | 'remove') { let unitId: string; let subUnitId: string; if (type === 'insert') { @@ -677,16 +678,20 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen return { redos, undos }; } + // eslint-disable-next-line max-lines-per-function private _moveRowInterceptor(params: IInsertRowCommandParams | IRemoveRowColCommandParams, type: 'insert' | 'remove') { const ids = this._getUnitIdAndSubUnitId(params, type); if (ids == null) { return { redos: [], undos: [] }; } const { unitId, subUnitId } = ids; - const { range } = params; - const rowStartIndex = range.startRow; - const rowEndIndex = range.endRow; + let ranges: IRange[] = []; + if (type === 'insert') { + ranges = [(params as IInsertRowCommandParams).range]; + } else { + ranges = (params as IRemoveRowColCommandParams).ranges; + } const redos: IMutationInfo[] = []; const undos: IMutationInfo[] = []; @@ -695,37 +700,42 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen const updateDrawings: Partial[] = []; const deleteDrawings: Partial[] = []; - Object.keys(data).forEach((drawingId) => { - const drawing = data[drawingId]; - const { sheetTransform, transform, anchorType = SheetDrawingAnchorType.Position } = drawing; + ranges.forEach((range) => { + const rowStartIndex = range.startRow; + const rowEndIndex = range.endRow; - if (sheetTransform == null || transform == null) { - return; - } - let newSheetTransform: Nullable; - let newTransform: Nullable; - if (type === 'insert') { - const param = this._expandRow(sheetTransform, transform, rowStartIndex, rowEndIndex, anchorType); - newSheetTransform = param?.newSheetTransform; - newTransform = param?.newTransform; - } else { - const { from, to } = sheetTransform; - const { row: fromRow } = from; - const { row: toRow } = to; - if (anchorType === SheetDrawingAnchorType.Both && fromRow >= rowStartIndex && toRow <= rowEndIndex) { - // delete drawing - deleteDrawings.push({ unitId, subUnitId, drawingId }); - } else { - const param = this._shrinkRow(sheetTransform, transform, rowStartIndex, rowEndIndex, anchorType); + Object.keys(data).forEach((drawingId) => { + const drawing = data[drawingId]; + const { sheetTransform, transform, anchorType = SheetDrawingAnchorType.Position } = drawing; + + if (sheetTransform == null || transform == null) { + return; + } + let newSheetTransform: Nullable; + let newTransform: Nullable; + if (type === 'insert') { + const param = this._expandRow(sheetTransform, transform, rowStartIndex, rowEndIndex, anchorType); newSheetTransform = param?.newSheetTransform; newTransform = param?.newTransform; + } else { + const { from, to } = sheetTransform; + const { row: fromRow } = from; + const { row: toRow } = to; + if (anchorType === SheetDrawingAnchorType.Both && fromRow >= rowStartIndex && toRow <= rowEndIndex) { + // delete drawing + deleteDrawings.push({ unitId, subUnitId, drawingId }); + } else { + const param = this._shrinkRow(sheetTransform, transform, rowStartIndex, rowEndIndex, anchorType); + newSheetTransform = param?.newSheetTransform; + newTransform = param?.newTransform; + } } - } - if (!newSheetTransform || !newTransform) { - return; - } - const params = { unitId, subUnitId, drawingId, transform: newTransform, sheetTransform: newSheetTransform }; - updateDrawings.push(params); + if (!newSheetTransform || !newTransform) { + return; + } + const params = { unitId, subUnitId, drawingId, transform: newTransform, sheetTransform: newSheetTransform }; + updateDrawings.push(params); + }); }); if (updateDrawings.length === 0 && deleteDrawings.length === 0) { @@ -757,16 +767,20 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen }; } + // eslint-disable-next-line max-lines-per-function private _moveColInterceptor(params: IInsertColCommandParams | IRemoveRowColCommandParams, type: 'insert' | 'remove') { const ids = this._getUnitIdAndSubUnitId(params, type); if (ids == null) { return { redos: [], undos: [] }; } const { unitId, subUnitId } = ids; - const { range } = params; - const colStartIndex = range.startColumn; - const colEndIndex = range.endColumn; + let ranges: IRange[] = []; + if (type === 'insert') { + ranges = [(params as IInsertColCommandParams).range]; + } else { + ranges = (params as IRemoveRowColCommandParams).ranges; + } const redos: IMutationInfo[] = []; const undos: IMutationInfo[] = []; @@ -775,39 +789,44 @@ export class SheetDrawingTransformAffectedController extends Disposable implemen const updateDrawings: Partial[] = []; const deleteDrawings: Partial[] = []; - Object.keys(data).forEach((drawingId) => { - const drawing = data[drawingId]; - const { sheetTransform, transform, anchorType = SheetDrawingAnchorType.Position } = drawing; + ranges.forEach((range) => { + const colStartIndex = range.startColumn; + const colEndIndex = range.endColumn; - if (sheetTransform == null || transform == null) { - return; - } - let newSheetTransform: Nullable; - let newTransform: Nullable; - if (type === 'insert') { - const param = this._expandCol(sheetTransform, transform, colStartIndex, colEndIndex, anchorType); - newSheetTransform = param?.newSheetTransform; - newTransform = param?.newTransform; - } else { - const { from, to } = sheetTransform; - const { column: fromColumn } = from; - const { column: toColumn } = to; - if (anchorType === SheetDrawingAnchorType.Both && fromColumn >= colStartIndex && toColumn <= colEndIndex) { - // delete drawing - deleteDrawings.push({ unitId, subUnitId, drawingId }); - } else { - const param = this._shrinkCol(sheetTransform, transform, colStartIndex, colEndIndex, anchorType); + Object.keys(data).forEach((drawingId) => { + const drawing = data[drawingId]; + const { sheetTransform, transform, anchorType = SheetDrawingAnchorType.Position } = drawing; + + if (sheetTransform == null || transform == null) { + return; + } + let newSheetTransform: Nullable; + let newTransform: Nullable; + if (type === 'insert') { + const param = this._expandCol(sheetTransform, transform, colStartIndex, colEndIndex, anchorType); newSheetTransform = param?.newSheetTransform; newTransform = param?.newTransform; + } else { + const { from, to } = sheetTransform; + const { column: fromColumn } = from; + const { column: toColumn } = to; + if (anchorType === SheetDrawingAnchorType.Both && fromColumn >= colStartIndex && toColumn <= colEndIndex) { + // delete drawing + deleteDrawings.push({ unitId, subUnitId, drawingId }); + } else { + const param = this._shrinkCol(sheetTransform, transform, colStartIndex, colEndIndex, anchorType); + newSheetTransform = param?.newSheetTransform; + newTransform = param?.newTransform; + } } - } - if (!newSheetTransform || !newTransform) { - return; - } + if (!newSheetTransform || !newTransform) { + return; + } - const params = { unitId, subUnitId, drawingId, transform: newTransform, sheetTransform: newSheetTransform }; - updateDrawings.push(params); + const params = { unitId, subUnitId, drawingId, transform: newTransform, sheetTransform: newSheetTransform }; + updateDrawings.push(params); + }); }); if (updateDrawings.length === 0 && deleteDrawings.length === 0) { diff --git a/packages/sheets-filter/src/controllers/__tests__/sheets-filter.controller.spec.ts b/packages/sheets-filter/src/controllers/__tests__/sheets-filter.controller.spec.ts index db0dbfd736d..f4b4f733337 100644 --- a/packages/sheets-filter/src/controllers/__tests__/sheets-filter.controller.spec.ts +++ b/packages/sheets-filter/src/controllers/__tests__/sheets-filter.controller.spec.ts @@ -19,7 +19,7 @@ import type { IWorkbookData, Workbook } from '@univerjs/core'; import { ICommandService, Inject, Injector, IUniverInstanceService, LocaleService, LocaleType, Plugin, RANGE_TYPE, UndoCommand, Univer, UniverInstanceType } from '@univerjs/core'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import type { ISetRangeValuesMutationParams } from '@univerjs/sheets'; +import type { IRemoveRowColCommandParams, ISetRangeValuesMutationParams } from '@univerjs/sheets'; import { CopySheetCommand, InsertColMutation, InsertSheetMutation, MoveColsCommand, MoveColsMutation, MoveRangeCommand, MoveRangeMutation, MoveRowsCommand, MoveRowsMutation, RefRangeService, RemoveColCommand, RemoveColMutation, RemoveRowCommand, RemoveRowMutation, SetRangeValuesMutation, SetSelectionsOperation, SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets'; import { SHEET_FILTER_SNAPSHOT_ID, SheetsFilterService } from '../../services/sheet-filter.service'; import { SheetsFilterController } from '../sheets-filter.controller'; @@ -317,13 +317,17 @@ describe('test controller of sheets filter', () => { }, ], })).toBeTruthy(); - expect(await commandService.executeCommand(RemoveRowCommand.id, { - unitId: 'test', - subUnitId: 'sheet1', - range: { startRow: 0, startColumn: 0, endRow: 0, endColumn: 5 }, - })).toBeTruthy(); + + const params: IRemoveRowColCommandParams = { + ranges: [ + { startRow: 0, startColumn: 0, endRow: 0, endColumn: 5 }, + { startRow: 1, startColumn: 0, endRow: 1, endColumn: 5 }, + ], + }; + + expect(await commandService.executeCommand(RemoveRowCommand.id, params)).toBeTruthy(); expect((sheetsFilterService as SheetsFilterService).getFilterModel('test', 'sheet1')!.getRange()) - .toEqual({ startRow: 2, startColumn: 0, endRow: 4, endColumn: 5 }); + .toEqual({ startRow: 1, startColumn: 0, endRow: 3, endColumn: 5 }); }); }); diff --git a/packages/sheets-filter/src/controllers/sheets-filter.controller.ts b/packages/sheets-filter/src/controllers/sheets-filter.controller.ts index 9157af40684..a06058f8f27 100644 --- a/packages/sheets-filter/src/controllers/sheets-filter.controller.ts +++ b/packages/sheets-filter/src/controllers/sheets-filter.controller.ts @@ -15,7 +15,7 @@ */ import type { ICellData, ICommandInfo, IMutationInfo, IObjectArrayPrimitiveType, IRange, Nullable, Workbook } from '@univerjs/core'; -import type { EffectRefRangeParams, IAddWorksheetMergeMutationParams, ICopySheetCommandParams, IInsertColCommandParams, IInsertRowCommandParams, IInsertRowMutationParams, IMoveColsCommandParams, IMoveRangeCommandParams, IMoveRowsCommandParams, IRemoveColMutationParams, IRemoveRowsMutationParams, IRemoveSheetCommandParams, ISetRangeValuesMutationParams, ISetWorksheetActiveOperationParams, ISheetCommandSharedParams } from '@univerjs/sheets'; +import type { EffectRefRangeParams, IAddWorksheetMergeMutationParams, ICopySheetCommandParams, IInsertColCommandParams, IInsertRowCommandParams, IInsertRowMutationParams, IMoveColsCommandParams, IMoveRangeCommandParams, IMoveRowsCommandParams, IRemoveRowColCommandParams, IRemoveRowsMutationParams, IRemoveSheetCommandParams, ISetRangeValuesMutationParams, ISetWorksheetActiveOperationParams, ISheetCommandSharedParams } from '@univerjs/sheets'; import type { ISetSheetsFilterCriteriaMutationParams, ISetSheetsFilterRangeMutationParams } from '../commands/mutations/sheets-filter.mutation'; import type { FilterColumn } from '../models/filter-model'; @@ -109,11 +109,11 @@ export class SheetsFilterController extends Disposable { return this._handleInsertColCommand(params, _unitId, _subUnitId); } case RemoveColCommand.id: { - const params = config.params as IRemoveColMutationParams; + const params = config.params as IRemoveRowColCommandParams; return this._handleRemoveColCommand(params, unitId, subUnitId); } case RemoveRowCommand.id: { - const params = config.params as IRemoveRowsMutationParams; + const params = config.params as IRemoveRowColCommandParams; return this._handleRemoveRowCommand(params, unitId, subUnitId); } case EffectRefRangId.MoveColsCommandId: { @@ -243,72 +243,77 @@ export class SheetsFilterController extends Disposable { }; } - private _handleRemoveColCommand(config: IRemoveColMutationParams, unitId: string, subUnitId: string) { + private _handleRemoveColCommand(config: IRemoveRowColCommandParams, unitId: string, subUnitId: string) { const filterModel = this._sheetsFilterService.getFilterModel(unitId, subUnitId); const filterRange = filterModel?.getRange() ?? null; if (!filterModel || !filterRange) { return this._handleNull(); } const { startColumn, endColumn } = filterRange; - const { startColumn: removeStartColumn, endColumn: removeEndColumn } = config.range; - if (removeStartColumn > endColumn) { - return this._handleNull(); - } const redos: IMutationInfo[] = []; const undos: IMutationInfo[] = []; - const rangeRemoveCount = - removeEndColumn < startColumn - ? 0 : - Math.min(removeEndColumn, endColumn) - Math.max(removeStartColumn, startColumn) + 1; - - const removeCount = removeEndColumn - removeStartColumn + 1; + for (let i = 0; i < config.ranges.length; i++) { + const range = config.ranges[i]; + const { startColumn: removeStartColumn, endColumn: removeEndColumn } = range; - const filterColumn = filterModel.getAllFilterColumns(); - filterColumn.forEach((column) => { - const [col, filter] = column; - if (col <= removeEndColumn && col >= removeStartColumn) { - redos.push({ id: SetSheetsFilterCriteriaMutation.id, params: { unitId, subUnitId, col, criteria: null } }); - undos.push({ id: SetSheetsFilterCriteriaMutation.id, params: { unitId, subUnitId, col, criteria: { ...filter.serialize(), colId: col } } }); + if (removeStartColumn > endColumn) { + continue; // Skip this range and move to the next one } - }); - const shifted = filterColumn.filter((column) => { - const [col, _] = column; - return col > removeEndColumn; - }); + const rangeRemoveCount = + removeEndColumn < startColumn + ? 0 : + Math.min(removeEndColumn, endColumn) - Math.max(removeStartColumn, startColumn) + 1; - let newRangeCriteria: { undos: IMutationInfo[]; redos: IMutationInfo[] } = { undos: [], redos: [] }; - if (shifted.length > 0) { - const { oldRange, newRange } = this.moveCriteria(unitId, subUnitId, shifted, -removeCount); - newRangeCriteria = newRange; - redos.push(...oldRange.redos); - undos.unshift(...oldRange.undos); - } + const removeCount = removeEndColumn - removeStartColumn + 1; - if (rangeRemoveCount === endColumn - startColumn + 1) { - const removeFilterRangeMutationParams: ISheetCommandSharedParams = { - unitId, - subUnitId, - }; - redos.push({ id: RemoveSheetsFilterMutation.id, params: removeFilterRangeMutationParams }); - undos.unshift({ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }); - } else { - const newStartColumn = startColumn <= removeStartColumn - ? startColumn : - (rangeRemoveCount === 0 ? startColumn - removeCount : removeStartColumn); - const newEndColumn = startColumn <= removeStartColumn ? endColumn - rangeRemoveCount : endColumn - removeCount; - const setFilterRangeMutationParams: ISetSheetsFilterRangeMutationParams = { - unitId, - subUnitId, - range: { ...filterRange, startColumn: newStartColumn, endColumn: newEndColumn }, - }; - redos.push({ id: SetSheetsFilterRangeMutation.id, params: setFilterRangeMutationParams }); - undos.unshift({ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }); + const filterColumn = filterModel.getAllFilterColumns(); + filterColumn.forEach((column) => { + const [col, filter] = column; + if (col <= removeEndColumn && col >= removeStartColumn) { + redos.push({ id: SetSheetsFilterCriteriaMutation.id, params: { unitId, subUnitId, col, criteria: null } }); + undos.push({ id: SetSheetsFilterCriteriaMutation.id, params: { unitId, subUnitId, col, criteria: { ...filter.serialize(), colId: col } } }); + } + }); - redos.push(...newRangeCriteria.redos); - undos.unshift(...newRangeCriteria.undos); + const shifted = filterColumn.filter((column) => { + const [col, _] = column; + return col > removeEndColumn; + }); + + let newRangeCriteria: { undos: IMutationInfo[]; redos: IMutationInfo[] } = { undos: [], redos: [] }; + if (shifted.length > 0) { + const { oldRange, newRange } = this.moveCriteria(unitId, subUnitId, shifted, -removeCount); + newRangeCriteria = newRange; + redos.push(...oldRange.redos); + undos.unshift(...oldRange.undos); + } + + if (rangeRemoveCount === endColumn - startColumn + 1) { + const removeFilterRangeMutationParams: ISheetCommandSharedParams = { + unitId, + subUnitId, + }; + redos.push({ id: RemoveSheetsFilterMutation.id, params: removeFilterRangeMutationParams }); + undos.unshift({ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }); + } else { + const newStartColumn = startColumn <= removeStartColumn + ? startColumn : + (rangeRemoveCount === 0 ? startColumn - removeCount : removeStartColumn); + const newEndColumn = startColumn <= removeStartColumn ? endColumn - rangeRemoveCount : endColumn - removeCount; + const setFilterRangeMutationParams: ISetSheetsFilterRangeMutationParams = { + unitId, + subUnitId, + range: { ...filterRange, startColumn: newStartColumn, endColumn: newEndColumn }, + }; + redos.push({ id: SetSheetsFilterRangeMutation.id, params: setFilterRangeMutationParams }); + undos.unshift({ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }); + + redos.push(...newRangeCriteria.redos); + undos.unshift(...newRangeCriteria.undos); + } } return { @@ -317,7 +322,8 @@ export class SheetsFilterController extends Disposable { }; } - private _handleRemoveRowCommand(config: IRemoveRowsMutationParams, unitId: string, subUnitId: string) { + // eslint-disable-next-line max-lines-per-function + private _handleRemoveRowCommand(config: IRemoveRowColCommandParams, unitId: string, subUnitId: string) { const filterModel = this._sheetsFilterService.getFilterModel(unitId, subUnitId); if (!filterModel) { return this._handleNull(); @@ -325,74 +331,93 @@ export class SheetsFilterController extends Disposable { const filterRange = filterModel.getRange(); const { startRow, endRow } = filterRange; - const { startRow: removeStartRow, endRow: removeEndRow } = config.range; - if (removeStartRow > endRow) { - return this._handleNull(); - } - if (removeEndRow < startRow) { - return { - undos: [{ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }], - redos: [{ - id: SetSheetsFilterRangeMutation.id, params: { - range: { - ...filterRange, - startRow: startRow - (removeEndRow - removeStartRow + 1), - endRow: endRow - (removeEndRow - removeStartRow + 1), - }, - unitId, subUnitId, - }, - }], - }; - } + let cumulativeShift = 0; // To accumulate row shifts for ranges before the filter range const redos: IMutationInfo[] = []; const undos: IMutationInfo[] = []; - const filterColumn = filterModel.getAllFilterColumns(); - const filterHeaderIsRemoved = startRow <= removeEndRow && startRow >= removeStartRow; + for (let i = 0; i < config.ranges.length; i++) { + const range = config.ranges[i]; + const { startRow: removeStartRow, endRow: removeEndRow } = range; - undos.push({ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }); + if (removeEndRow < startRow) { + // If the removal range is before the filter range, accumulate shift + cumulativeShift += removeEndRow - removeStartRow + 1; + continue; // Skip to the next range + } - const count = Math.min(removeEndRow, endRow) - Math.max(removeStartRow, startRow) + 1; - if (count === endRow - startRow + 1 || filterHeaderIsRemoved) { - const removeFilterRangeMutationParams: ISheetCommandSharedParams = { - unitId, - subUnitId, - }; - redos.push({ id: RemoveSheetsFilterMutation.id, params: removeFilterRangeMutationParams }); + if (removeStartRow > endRow) { + // If the removal range is after the filter range, no need to do anything + continue; + } - filterColumn.forEach((column) => { - const [offset, filter] = column; - const setCriteriaMutationParams: ISetSheetsFilterCriteriaMutationParams = { + const filterColumn = filterModel.getAllFilterColumns(); + const filterHeaderIsRemoved = startRow <= removeEndRow && startRow >= removeStartRow; + + const count = Math.min(removeEndRow, endRow) - Math.max(removeStartRow, startRow) + 1; + + if (count === endRow - startRow + 1 || filterHeaderIsRemoved) { + // If the entire filter range is removed or the header row is affected + const removeFilterRangeMutationParams: ISheetCommandSharedParams = { unitId, subUnitId, - col: offset, - criteria: { ...filter.serialize(), colId: offset }, }; - undos.push({ id: SetSheetsFilterCriteriaMutation.id, params: setCriteriaMutationParams }); - }); - } else { - const worksheet = this._univerInstanceService.getUniverSheetInstance(unitId)?.getSheetBySheetId(subUnitId); - if (!worksheet) { - return this._handleNull(); - } - const hiddenRows = []; - for (let r = removeStartRow; r <= removeEndRow; r++) { - if (worksheet.getRowFiltered(r)) { - hiddenRows.push(r); + redos.push({ id: RemoveSheetsFilterMutation.id, params: removeFilterRangeMutationParams }); + + filterColumn.forEach((column) => { + const [offset, filter] = column; + const setCriteriaMutationParams: ISetSheetsFilterCriteriaMutationParams = { + unitId, + subUnitId, + col: offset, + criteria: { ...filter.serialize(), colId: offset }, + }; + undos.push({ id: SetSheetsFilterCriteriaMutation.id, params: setCriteriaMutationParams }); + }); + } else { + // If part of the range is removed, adjust the filter range accordingly + const worksheet = this._univerInstanceService.getUniverSheetInstance(unitId)?.getSheetBySheetId(subUnitId); + if (!worksheet) { + return this._handleNull(); } + + const hiddenRows: number[] = []; + for (let r = removeStartRow; r <= removeEndRow; r++) { + if (worksheet.getRowFiltered(r)) { + hiddenRows.push(r); + } + } + + const afterStartRow = Math.min(startRow, removeStartRow); + const afterEndRow = afterStartRow + (endRow - startRow) - count + hiddenRows.length; + + const setFilterRangeMutationParams: ISetSheetsFilterRangeMutationParams = { + unitId, + subUnitId, + range: { + ...filterRange, + startRow: afterStartRow, + endRow: afterEndRow, + }, + }; + redos.push({ id: SetSheetsFilterRangeMutation.id, params: setFilterRangeMutationParams }); } - const afterStartRow = Math.min(startRow, removeStartRow); - const afterEndRow = afterStartRow + (endRow - startRow) - count + hiddenRows.length; - const setFilterRangeMutationParams: ISetSheetsFilterRangeMutationParams = { + } + + // Record undo for filter range before processing this range + undos.push({ id: SetSheetsFilterRangeMutation.id, params: { range: filterRange, unitId, subUnitId } }); + + // Apply the cumulative shift for rows before the filter range + if (cumulativeShift > 0) { + // The shift applies only before the filter, so adjust both startRow and endRow + const updatedStartRow = startRow - cumulativeShift; + const updatedEndRow = endRow - cumulativeShift; + + const finalSetFilterRangeMutationParams: ISetSheetsFilterRangeMutationParams = { unitId, subUnitId, - range: { - ...filterRange, - startRow: afterStartRow, - endRow: afterEndRow, - }, + range: { ...filterRange, startRow: updatedStartRow, endRow: updatedEndRow }, }; - redos.push({ id: SetSheetsFilterRangeMutation.id, params: setFilterRangeMutationParams }); + redos.push({ id: SetSheetsFilterRangeMutation.id, params: finalSetFilterRangeMutationParams }); } return { diff --git a/packages/sheets-filter/src/models/__tests__/filter-interceptor.spec.ts b/packages/sheets-filter/src/models/__tests__/filter-interceptor.spec.ts index afb023103c8..f03216e4775 100644 --- a/packages/sheets-filter/src/models/__tests__/filter-interceptor.spec.ts +++ b/packages/sheets-filter/src/models/__tests__/filter-interceptor.spec.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { Dependency, IWorkbookData, LocaleType } from '@univerjs/core'; +import type { IInsertColCommandParams, IInsertRowCommandParams, IMoveColsCommandParams, IMoveRowsCommandParams, IRemoveRowColCommandParams, IRemoveSheetCommandParams, ISetSelectionsOperationParams } from '@univerjs/sheets'; import { Direction, ICommandService, Inject, Injector, Plugin, RANGE_TYPE, Univer, UniverInstanceType } from '@univerjs/core'; -import type { IInsertColCommandParams, IInsertRowCommandParams, IMoveColsCommandParams, IMoveRowsCommandParams, IRemoveRowColCommandParams, IRemoveSheetCommandParams, ISetSelectionsOperationParams } from '@univerjs/sheets'; import { InsertColCommand, InsertColMutation, InsertRowCommand, InsertRowMutation, MoveColsCommand, MoveColsMutation, MoveRowsCommand, MoveRowsMutation, RefRangeService, RemoveColCommand, RemoveColMutation, RemoveRowCommand, RemoveRowMutation, RemoveSheetCommand, RemoveSheetMutation, SetSelectionsOperation, SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets'; -import { SHEET_FILTER_SNAPSHOT_ID, SheetsFilterService } from '../../services/sheet-filter.service'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { SheetsFilterController } from '../../controllers/sheets-filter.controller'; +import { SHEET_FILTER_SNAPSHOT_ID, SheetsFilterService } from '../../services/sheet-filter.service'; describe('Test "Filter Interceptor"', () => { let univer: Univer; @@ -89,9 +89,9 @@ describe('Test "Filter Interceptor"', () => { describe('Test "remove Command"', () => { it('remove col command, in filter range', async () => { const removeColCommandParams = { - unitId: 'workbookId', subUnitId: 'worksheetId', range: { - startColumn: 1, endColumn: 1, startRow: 0, endRow: 4, type: RANGE_TYPE.COLUMN, - }, direction: Direction.RIGHT, + unitId: 'workbookId', subUnitId: 'worksheetId', ranges: [{ + startColumn: 1, endColumn: 1, startRow: 0, endRow: 4, rangeType: RANGE_TYPE.COLUMN, + }], direction: Direction.RIGHT, } as IRemoveRowColCommandParams; await commandService.executeCommand(RemoveColCommand.id, removeColCommandParams); expect(sheetsFilterService.getFilterModel('workbookId', 'worksheetId')!.getRange()).toStrictEqual({ startColumn: 1, endColumn: 1, startRow: 1, endRow: 2 }); @@ -99,9 +99,9 @@ describe('Test "Filter Interceptor"', () => { }); it('remove col command, before filter range', async () => { const removeColCommandParams = { - unitId: 'workbookId', subUnitId: 'worksheetId', range: { - startColumn: 0, endColumn: 0, startRow: 0, endRow: 4, type: RANGE_TYPE.COLUMN, - }, direction: Direction.RIGHT, + unitId: 'workbookId', subUnitId: 'worksheetId', ranges: [{ + startColumn: 0, endColumn: 0, startRow: 0, endRow: 4, rangeType: RANGE_TYPE.COLUMN, + }], direction: Direction.RIGHT, } as IRemoveRowColCommandParams; await commandService.executeCommand(RemoveColCommand.id, removeColCommandParams); expect(sheetsFilterService.getFilterModel('workbookId', 'worksheetId')!.getRange()).toStrictEqual({ startColumn: 0, endColumn: 1, startRow: 1, endRow: 2 }); @@ -110,9 +110,9 @@ describe('Test "Filter Interceptor"', () => { it('remove row command, in filter range', async () => { const removeRowCommandParams = { - unitId: 'workbookId', subUnitId: 'worksheetId', range: { - startColumn: 0, endColumn: 3, startRow: 1, endRow: 1, type: RANGE_TYPE.ROW, - }, direction: Direction.DOWN, + unitId: 'workbookId', subUnitId: 'worksheetId', ranges: [{ + startColumn: 0, endColumn: 3, startRow: 1, endRow: 1, rangeType: RANGE_TYPE.ROW, + }], direction: Direction.DOWN, } as IRemoveRowColCommandParams; await commandService.executeCommand(RemoveRowCommand.id, removeRowCommandParams); expect(sheetsFilterService.getFilterModel('workbookId', 'worksheetId')).toBe(null); @@ -120,9 +120,9 @@ describe('Test "Filter Interceptor"', () => { it('remove row command, before filter range', async () => { const removeRowCommandParams = { - unitId: 'workbookId', subUnitId: 'worksheetId', range: { - startColumn: 0, endColumn: 3, startRow: 0, endRow: 0, type: RANGE_TYPE.ROW, - }, direction: Direction.DOWN, + unitId: 'workbookId', subUnitId: 'worksheetId', ranges: [{ + startColumn: 0, endColumn: 3, startRow: 0, endRow: 0, rangeType: RANGE_TYPE.ROW, + }], direction: Direction.DOWN, } as IRemoveRowColCommandParams; await commandService.executeCommand(RemoveRowCommand.id, removeRowCommandParams); expect(sheetsFilterService.getFilterModel('workbookId', 'worksheetId')!.getRange()).toStrictEqual({ startColumn: 1, endColumn: 2, startRow: 0, endRow: 1 }); diff --git a/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts b/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts index 4ad8d0fdf66..558b8c25f9f 100644 --- a/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts +++ b/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts @@ -43,7 +43,8 @@ export interface IFormulaReferenceMoveParam { type: FormulaReferenceMoveType; unitId: string; sheetId: string; - range?: IRange; + range?: IRange; // insert/delete range, insert rowss/columns + ranges?: IRange[]; // remove rows/columns from?: IRange; to?: IRange; sheetName?: string; @@ -214,7 +215,7 @@ export function refRangeFormula(oldFormulaData: IFormulaData, const redoFormulaData: Record>>> = {}; const undoFormulaData: Record>>> = {}; - const { type, unitId: targetUnitId, sheetId, range, from, to } = formulaReferenceMoveParam; + const { type, unitId: targetUnitId, sheetId, range, ranges, from, to } = formulaReferenceMoveParam; // Iterate over all unitId in oldFormulaData const allUnitIds = new Set([...Object.keys(oldFormulaData), ...Object.keys(newFormulaData)]); @@ -243,7 +244,7 @@ export function refRangeFormula(oldFormulaData: IFormulaData, if (unitId !== targetUnitId || currentSheetId !== sheetId) { rangeList = processFormulaRange(newFormulaMatrix); } else { - rangeList = processFormulaChanges(oldFormulaMatrix, type, from, to, range); + rangeList = processFormulaChanges(oldFormulaMatrix, type, from, to, range, ranges); } const sheetRedoFormulaData = getRedoFormulaData(rangeList, oldFormulaMatrix, newFormulaMatrix); @@ -273,7 +274,7 @@ export function refRangeFormula(oldFormulaData: IFormulaData, }; } -function processFormulaChanges(oldFormulaMatrix: ObjectMatrix>, type: FormulaReferenceMoveType, from: Nullable, to: Nullable, range: Nullable) { +function processFormulaChanges(oldFormulaMatrix: ObjectMatrix>, type: FormulaReferenceMoveType, from: Nullable, to: Nullable, range: Nullable, ranges: Nullable) { // When undoing and redoing, the traversal order may be different. Record the range list of all single formula offsets, and then retrieve the traversal as needed. const rangeList: IRangeChange[] = []; @@ -293,6 +294,11 @@ function processFormulaChanges(oldFormulaMatrix: ObjectMatrix, w const { params } = command; if (!params) return null; - const { range } = params; + const { ranges } = params; const { unitId, sheetId } = getCurrentSheetInfo(workbook); return { type: FormulaReferenceMoveType.RemoveRow, - range, + ranges, unitId, sheetId, }; @@ -294,12 +294,12 @@ function handleRefRemoveCol(command: ICommandInfo, w const { params } = command; if (!params) return null; - const { range } = params; + const { ranges } = params; const { unitId, sheetId } = getCurrentSheetInfo(workbook); return { type: FormulaReferenceMoveType.RemoveColumn, - range, + ranges, unitId, sheetId, }; diff --git a/packages/sheets-formula/src/services/__test__/formula-ref.spec.ts b/packages/sheets-formula/src/services/__test__/formula-ref.spec.ts index 1ec9fbcd324..12d3711d768 100644 --- a/packages/sheets-formula/src/services/__test__/formula-ref.spec.ts +++ b/packages/sheets-formula/src/services/__test__/formula-ref.spec.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import type { IRemoveRowColCommandParams } from '@univerjs/sheets'; import type { ITestBed } from './util'; import { ICommandService, type IRange } from '@univerjs/core'; import { InsertColCommand, MoveRangeCommand, RemoveColCommand } from '@univerjs/sheets'; @@ -226,17 +227,18 @@ describe('FormulaRefRangeService', () => { } ); - await testBed.get(ICommandService).executeCommand( - RemoveColCommand.id, - { - range: { + const params: IRemoveRowColCommandParams = { + ranges: [ + { startColumn: 0, endColumn: 1, startRow: 0, endRow: 9999, }, - } - ); + ], + }; + + await testBed.get(ICommandService).executeCommand(RemoveColCommand.id, params); expect(newFormulas).toEqual( [ diff --git a/packages/sheets-ui/src/commands/commands/remove-row-col-confirm.command.ts b/packages/sheets-ui/src/commands/commands/remove-row-col-confirm.command.ts index 1ced89f89ce..adbf3ae9a4e 100644 --- a/packages/sheets-ui/src/commands/commands/remove-row-col-confirm.command.ts +++ b/packages/sheets-ui/src/commands/commands/remove-row-col-confirm.command.ts @@ -15,8 +15,8 @@ */ import type { IAccessor, ICommand } from '@univerjs/core'; -import { CommandType, ICommandService, IUniverInstanceService, LocaleService } from '@univerjs/core'; import type { IRemoveRowColCommandParams } from '@univerjs/sheets'; +import { CommandType, ICommandService, IUniverInstanceService, LocaleService } from '@univerjs/core'; import { getSheetCommandTarget, RemoveColCommand, RemoveRowCommand, SheetsSelectionsService } from '@univerjs/sheets'; import { IConfirmService } from '@univerjs/ui'; @@ -28,13 +28,8 @@ export const RemoveRowConfirmCommand: ICommand = { handler: async (accessor: IAccessor, params?: IRemoveRowColCommandParams) => { const selectionManagerService = accessor.get(SheetsSelectionsService); - let range = params?.range; - if (!range) { - range = selectionManagerService.getCurrentLastSelection()?.range; - } - if (!range) { - return false; - } + const ranges = params?.ranges || selectionManagerService.getCurrentSelections().map((s) => s.range); + if (ranges.length === 0) return false; const commandService = accessor.get(ICommandService); const univerInstanceService = accessor.get(IUniverInstanceService); @@ -45,7 +40,7 @@ export const RemoveRowConfirmCommand: ICommand = { const { worksheet } = target; const allRowRanges = worksheet.getVisibleRows(); - if (isAllRowsCovered(allRowRanges, [range])) { + if (isAllRowsCovered(allRowRanges, ranges)) { const confirmService = accessor.get(IConfirmService); const localeService = accessor.get(LocaleService); @@ -62,7 +57,7 @@ export const RemoveRowConfirmCommand: ICommand = { return false; } - await commandService.executeCommand(RemoveRowCommand.id, { range }); + await commandService.executeCommand(RemoveRowCommand.id, { ranges }); return true; }, }; @@ -73,13 +68,8 @@ export const RemoveColConfirmCommand: ICommand = { handler: async (accessor: IAccessor, params?: IRemoveRowColCommandParams) => { const selectionManagerService = accessor.get(SheetsSelectionsService); - let range = params?.range; - if (!range) { - range = selectionManagerService.getCurrentLastSelection()?.range; - } - if (!range) { - return false; - } + const ranges = params?.ranges || selectionManagerService.getCurrentSelections().map((s) => s.range); + if (ranges.length === 0) return false; const commandService = accessor.get(ICommandService); const univerInstanceService = accessor.get(IUniverInstanceService); @@ -90,7 +80,7 @@ export const RemoveColConfirmCommand: ICommand = { const { worksheet } = target; const allColumnRanges = worksheet.getVisibleCols(); - if (isAllColumnsCovered(allColumnRanges, [range])) { + if (isAllColumnsCovered(allColumnRanges, ranges)) { const confirmService = accessor.get(IConfirmService); const localeService = accessor.get(LocaleService); @@ -107,7 +97,7 @@ export const RemoveColConfirmCommand: ICommand = { return false; } - await commandService.executeCommand(RemoveColCommand.id, { range }); + await commandService.executeCommand(RemoveColCommand.id, { ranges }); return true; }, }; diff --git a/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts index cbbfaa4123b..ba3bab2be16 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts @@ -14,6 +14,22 @@ * limitations under the License. */ +import type { ICommandInfo, IFreeze, IRange, IStyleSheet, IWorksheetData, Nullable, Workbook } from '@univerjs/core'; +import type { IMouseEvent, IPointerEvent, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; +import type { + IInsertColCommandParams, + IInsertRowCommandParams, + IMoveColsCommandParams, + IMoveRowsCommandParams, + IRemoveRowColCommandParams, + ISetColHiddenMutationParams, + ISetFrozenMutationParams, + ISetRowHiddenMutationParams, + ISetWorksheetColWidthMutationParams, + ISetWorksheetRowAutoHeightMutationParams, + ISetWorksheetRowHeightMutationParams, +} from '@univerjs/sheets'; +import type { IViewportScrollState } from '../../services/scroll-manager.service'; import { ColorKit, createInterceptorKey, @@ -28,6 +44,7 @@ import { toDisposable, } from '@univerjs/core'; import { CURSOR_TYPE, Rect, SHEET_VIEWPORT_KEY, TRANSFORM_CHANGE_OBSERVABLE_TYPE, Vector2 } from '@univerjs/engine-render'; + import { InsertColCommand, InsertRangeMoveDownCommand, @@ -52,30 +69,13 @@ import { SheetsSelectionsService, } from '@univerjs/sheets'; import { Subscription } from 'rxjs'; -import type { ICommandInfo, IFreeze, IRange, IStyleSheet, IWorksheetData, Nullable, Workbook } from '@univerjs/core'; -import type { IMouseEvent, IPointerEvent, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; - -import type { - IInsertColCommandParams, - IInsertRowCommandParams, - IMoveColsCommandParams, - IMoveRowsCommandParams, - IRemoveRowColCommandParams, - ISetColHiddenMutationParams, - ISetFrozenMutationParams, - ISetRowHiddenMutationParams, - ISetWorksheetColWidthMutationParams, - ISetWorksheetRowAutoHeightMutationParams, - ISetWorksheetRowHeightMutationParams, -} from '@univerjs/sheets'; import { ScrollCommand } from '../../commands/commands/set-scroll.command'; import { SetZoomRatioOperation } from '../../commands/operations/set-zoom-ratio.operation'; -import { SHEET_COMPONENT_HEADER_LAYER_INDEX } from '../../common/keys'; +import { SHEET_COMPONENT_HEADER_LAYER_INDEX } from '../../common/keys'; import { SheetScrollManagerService } from '../../services/scroll-manager.service'; import { SheetSkeletonManagerService } from '../../services/sheet-skeleton-manager.service'; import { getCoordByOffset, getSheetObject } from '../utils/component-tools'; -import type { IViewportScrollState } from '../../services/scroll-manager.service'; enum FREEZE_DIRECTION_TYPE { ROW, @@ -1372,28 +1372,33 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM if (command.id === RemoveColCommand.id || command.id === RemoveRowCommand.id) { const params = command.params as IRemoveRowColCommandParams; - const range = params.range; - - if (range.rangeType === RANGE_TYPE.COLUMN && range.startColumn < freeze.startColumn) { - const deleteFreezeColCount = - Math.min(freeze.startColumn, range.endColumn + 1) - range.startColumn; - - const newFreeze: IFreeze = { - ...freeze, - startColumn: Math.max(1, freeze.startColumn - deleteFreezeColCount), - xSplit: Math.max(1, freeze.xSplit - deleteFreezeColCount), - }; - - return createFreezeMutationAndRefresh(newFreeze); + const ranges = params.ranges; + + let totalDeletedCols = 0; + let totalDeletedRows = 0; + + for (const range of ranges) { + if (range.rangeType === RANGE_TYPE.COLUMN && range.startColumn < freeze.startColumn) { + const deleteFreezeColCount = + Math.min(freeze.startColumn, range.endColumn + 1) - range.startColumn; + totalDeletedCols += deleteFreezeColCount; + } + + if (range.rangeType === RANGE_TYPE.ROW && range.startRow < freeze.startRow) { + const deleteFreezeRowCount = + Math.min(freeze.startRow, range.endRow + 1) - range.startRow; + totalDeletedRows += deleteFreezeRowCount; + } } - if (params.range.rangeType === RANGE_TYPE.ROW && range.startRow < freeze.startRow) { - const deleteFreezeRowCount = Math.min(freeze.startRow, range.endRow + 1) - range.startRow; - + // If any columns or rows were deleted, update the freeze settings + if (totalDeletedCols > 0 || totalDeletedRows > 0) { const newFreeze: IFreeze = { ...freeze, - startRow: Math.max(1, freeze.startRow - deleteFreezeRowCount), - ySplit: Math.max(1, freeze.ySplit - deleteFreezeRowCount), + startColumn: Math.max(1, freeze.startColumn - totalDeletedCols), + xSplit: Math.max(1, freeze.xSplit - totalDeletedCols), + startRow: Math.max(1, freeze.startRow - totalDeletedRows), + ySplit: Math.max(1, freeze.ySplit - totalDeletedRows), }; return createFreezeMutationAndRefresh(newFreeze); diff --git a/packages/sheets/src/commands/commands/__tests__/insert-remove-rows-cols.command.spec.ts b/packages/sheets/src/commands/commands/__tests__/insert-remove-rows-cols.command.spec.ts index 041f0adbf2c..90c249352e2 100644 --- a/packages/sheets/src/commands/commands/__tests__/insert-remove-rows-cols.command.spec.ts +++ b/packages/sheets/src/commands/commands/__tests__/insert-remove-rows-cols.command.spec.ts @@ -314,12 +314,10 @@ describe('Test insert and remove rows cols commands', () => { describe('Remove row where contain mergeCell', () => { it('reduce merge cell length', async () => { await commandService.executeCommand(RemoveRowCommand.id, { - range: { - startRow: 12, - endRow: 13, - startColumn: 1, - endColumn: 1, - }, + ranges: [ + { startRow: 12, endRow: 12, startColumn: 1, endColumn: 1 }, + { startRow: 13, endRow: 13, startColumn: 1, endColumn: 1 }, + ], } as IRemoveRowColCommandParams); expect(getMergedInfo(12, 2)).toEqual({ startRow: 10, endRow: 13, startColumn: 2, endColumn: 2 }); }); @@ -327,12 +325,11 @@ describe('Test insert and remove rows cols commands', () => { describe('Remove col where contain mergeCell', () => { it('reduce merge cell length', async () => { await commandService.executeCommand(RemoveColCommand.id, { - range: { - startRow: 1, - endRow: 1, - startColumn: 12, - endColumn: 13, - }, + ranges: [ + { startRow: 1, endRow: 1, startColumn: 12, endColumn: 12 }, + { startRow: 1, endRow: 1, startColumn: 13, endColumn: 13 }, + + ], } as IRemoveRowColCommandParams); expect(getMergedInfo(10, 12)).toEqual({ startRow: 10, endRow: 10, startColumn: 10, endColumn: 13 }); }); diff --git a/packages/sheets/src/commands/commands/__tests__/remove-rows-cols.command.spec.ts b/packages/sheets/src/commands/commands/__tests__/remove-rows-cols.command.spec.ts index 40aa2a9b1bd..e63de32592f 100644 --- a/packages/sheets/src/commands/commands/__tests__/remove-rows-cols.command.spec.ts +++ b/packages/sheets/src/commands/commands/__tests__/remove-rows-cols.command.spec.ts @@ -103,13 +103,13 @@ describe('Test remove rows cols', () => { selectRow(0, 0); const result = await commandService.executeCommand(RemoveRowCommand.id, { - range: { + ranges: [{ startRow: 0, endRow: 0, startColumn: 0, endColumn: 19, rangeType: RANGE_TYPE.ROW, - }, + }], }); expect(result).toEqual(true); expect(getCellInfo(0, 0)?.v).toEqual('B1'); @@ -127,13 +127,13 @@ describe('Test remove rows cols', () => { selectRow(0, 0); const result = await commandService.executeCommand(RemoveColCommand.id, { - range: { + ranges: [{ startRow: 0, endRow: 19, startColumn: 0, endColumn: 0, rangeType: RANGE_TYPE.COLUMN, - }, + }], }); expect(result).toEqual(true); expect(getCellInfo(0, 0)?.v).toEqual('A2'); diff --git a/packages/sheets/src/commands/commands/remove-row-col.command.ts b/packages/sheets/src/commands/commands/remove-row-col.command.ts index a0c5eb0ccb8..b8735587521 100644 --- a/packages/sheets/src/commands/commands/remove-row-col.command.ts +++ b/packages/sheets/src/commands/commands/remove-row-col.command.ts @@ -43,12 +43,10 @@ import { followSelectionOperation } from './utils/selection-utils'; import { getSheetCommandTarget } from './utils/target-util'; export interface IRemoveRowColCommandParams { - range: IRange; + ranges: IRange[]; } -export interface IRemoveRowColCommandInterceptParams extends IRemoveRowColCommandParams { - ranges?: IRange[]; -} +export interface IRemoveRowColCommandInterceptParams extends IRemoveRowColCommandParams {} export const RemoveRowCommandId = 'sheet.command.remove-row'; /** @@ -64,9 +62,8 @@ export const RemoveRowCommand: ICommand = { const selectionManagerService = accessor.get(SheetsSelectionsService); const sheetInterceptorService = accessor.get(SheetInterceptorService); - let totalRange = params?.range; - if (!totalRange) totalRange = selectionManagerService.getCurrentLastSelection()?.range; - if (!totalRange) return false; + const ranges = params?.ranges || selectionManagerService.getCurrentSelections()?.map((sel) => sel.range) || []; + if (ranges.length === 0) return false; const univerInstanceService = accessor.get(IUniverInstanceService); const target = getSheetCommandTarget(univerInstanceService); @@ -74,40 +71,37 @@ export const RemoveRowCommand: ICommand = { const { workbook, worksheet, subUnitId, unitId } = target; - totalRange = { - ...totalRange, - startColumn: 0, - endColumn: Math.max(worksheet.getMaxColumns() - 1, 0), - }; + const filteredRanges: IRange[] = []; - const filterOutRowsInRemove: number[] = []; - for (let i = totalRange.startRow; i <= totalRange.endRow; i++) { - if (worksheet.getRowFiltered(i)) { - filterOutRowsInRemove.push(i); + ranges.forEach((range) => { + const filterOutRowsInRemove: number[] = []; + for (let i = range.startRow; i <= range.endRow; i++) { + if (worksheet.getRowFiltered(i)) { + filterOutRowsInRemove.push(i); + } } - } - const ranges: IRange[] = []; - if (filterOutRowsInRemove.length) { - const starts = [totalRange.startRow, ...filterOutRowsInRemove.map((r) => r + 1)]; - const ends = [...filterOutRowsInRemove.map((r) => r - 1), totalRange.endRow]; - for (let i = starts.length - 1; i >= 0; i--) { - if (starts[i] <= ends[i]) { - ranges.push({ - startRow: starts[i], - endRow: ends[i], - startColumn: totalRange.startColumn, - endColumn: totalRange.endColumn, - }); + if (filterOutRowsInRemove.length) { + const starts = [range.startRow, ...filterOutRowsInRemove.map((r) => r + 1)]; + const ends = [...filterOutRowsInRemove.map((r) => r - 1), range.endRow]; + for (let i = starts.length - 1; i >= 0; i--) { + if (starts[i] <= ends[i]) { + filteredRanges.push({ + startRow: starts[i], + endRow: ends[i], + startColumn: range.startColumn, + endColumn: range.endColumn, + }); + } } + } else { + filteredRanges.push(range); } - } else { - ranges.push(totalRange); - } + }); const canPerform = await sheetInterceptorService.beforeCommandExecute({ id: RemoveRowCommand.id, - params: { range: totalRange, ranges } as IRemoveRowColCommandInterceptParams, + params: { ranges: filteredRanges } as IRemoveRowColCommandInterceptParams, }); if (!canPerform) { @@ -117,8 +111,7 @@ export const RemoveRowCommand: ICommand = { const redos: IMutationInfo[] = []; const undos: IMutationInfo[] = []; - ranges.forEach((range) => { - // row count + filteredRanges.forEach((range) => { const removeRowsParams: IRemoveRowsMutationParams = { unitId, subUnitId, @@ -141,7 +134,7 @@ export const RemoveRowCommand: ICommand = { const intercepted = sheetInterceptorService.onCommandExecute({ id: RemoveRowCommand.id, - params: { range: totalRange, ranges } as IRemoveRowColCommandInterceptParams, + params: { ranges: filteredRanges } as IRemoveRowColCommandInterceptParams, }); const commandService = accessor.get(ICommandService); @@ -150,7 +143,7 @@ export const RemoveRowCommand: ICommand = { ...(intercepted.preRedos ?? []), ...redos, ...intercepted.redos, - followSelectionOperation(totalRange, workbook, worksheet), + followSelectionOperation(filteredRanges[0], workbook, worksheet), ], commandService ); @@ -180,17 +173,17 @@ export const RemoveColCommandId = 'sheet.command.remove-col'; /** * This command would remove the selected columns. These selected rows can be non-continuous. */ -export const RemoveColCommand: ICommand = { +export const RemoveColCommand: ICommand = { type: CommandType.COMMAND, id: RemoveColCommandId, + // eslint-disable-next-line max-lines-per-function handler: async (accessor: IAccessor, params?: IRemoveRowColCommandParams) => { const selectionManagerService = accessor.get(SheetsSelectionsService); const sheetInterceptorService = accessor.get(SheetInterceptorService); - let range = params?.range; - if (!range) range = selectionManagerService.getCurrentLastSelection()?.range; - if (!range) return false; + const ranges = params?.ranges || selectionManagerService.getCurrentSelections()?.map((sel) => sel.range) || []; + if (ranges.length === 0) return false; const univerInstanceService = accessor.get(IUniverInstanceService); const target = getSheetCommandTarget(univerInstanceService); @@ -198,30 +191,40 @@ export const RemoveColCommand: ICommand = { const { workbook, worksheet, subUnitId, unitId } = target; - range = { - ...range, - startRow: 0, - endRow: Math.max(worksheet.getMaxRows() - 1, 0), - }; - - // col count - const removeColParams: IRemoveColMutationParams = { - unitId, - subUnitId, - range, - }; - const undoRemoveColParams: IInsertColMutationParams = RemoveColMutationFactory(accessor, removeColParams); - - const removedCols = worksheet.getCellMatrix().getSlice(0, worksheet.getRowCount() - 1, range.startColumn, range.endColumn); - const undoSetRangeValuesParams: ISetRangeValuesMutationParams = { - unitId, - subUnitId, - cellValue: removedCols.getMatrix(), - }; + const redos: IMutationInfo[] = []; + const undos: IMutationInfo[] = []; + + ranges.forEach((range) => { + range = { + ...range, + startRow: 0, + endRow: Math.max(worksheet.getMaxRows() - 1, 0), + }; + + const removeColParams: IRemoveColMutationParams = { + unitId, + subUnitId, + range, + }; + const undoRemoveColParams: IInsertColMutationParams = RemoveColMutationFactory(accessor, removeColParams); + + const removedCols = worksheet.getCellMatrix().getSlice(0, worksheet.getRowCount() - 1, range.startColumn, range.endColumn); + const undoSetRangeValuesParams: ISetRangeValuesMutationParams = { + unitId, + subUnitId, + cellValue: removedCols.getMatrix(), + }; + + redos.push({ id: RemoveColMutation.id, params: removeColParams }); + undos.unshift( + { id: InsertColMutation.id, params: undoRemoveColParams }, + { id: SetRangeValuesMutation.id, params: undoSetRangeValuesParams } + ); + }); const canPerform = await sheetInterceptorService.beforeCommandExecute({ id: RemoveColCommand.id, - params: { range } as IRemoveRowColCommandParams, + params: { ranges } as IRemoveRowColCommandParams, }); if (!canPerform) { @@ -230,32 +233,32 @@ export const RemoveColCommand: ICommand = { const intercepted = sheetInterceptorService.onCommandExecute({ id: RemoveColCommand.id, - params: { range } as IRemoveRowColCommandParams, + params: { ranges } as IRemoveRowColCommandParams, }); const commandService = accessor.get(ICommandService); const result = sequenceExecute( [ ...(intercepted.preRedos ?? []), - { id: RemoveColMutation.id, params: removeColParams }, + ...redos, ...intercepted.redos, - followSelectionOperation(range, workbook, worksheet), + followSelectionOperation(ranges[0], workbook, worksheet), ], commandService ); if (result.result) { - const undoRedoService = accessor.get(IUndoRedoService); - undoRedoService.pushUndoRedo({ + accessor.get(IUndoRedoService).pushUndoRedo({ unitID: unitId, undoMutations: [ ...(intercepted.preUndos ?? []), - { id: InsertColMutation.id, params: undoRemoveColParams }, - { id: SetRangeValuesMutation.id, params: undoSetRangeValuesParams }, - ...intercepted.undos], + ...undos, + ...intercepted.undos, + ], redoMutations: [ ...(intercepted.preRedos ?? []), - { id: RemoveColMutation.id, params: removeColParams }, - ...intercepted.redos], + ...redos, + ...intercepted.redos, + ], }); return true; diff --git a/packages/sheets/src/controllers/merge-cell.controller.ts b/packages/sheets/src/controllers/merge-cell.controller.ts index b9a42c5686d..66501ea031c 100644 --- a/packages/sheets/src/controllers/merge-cell.controller.ts +++ b/packages/sheets/src/controllers/merge-cell.controller.ts @@ -22,8 +22,6 @@ import type { import type { IAddWorksheetMergeMutationParams, IInsertColMutationParams, - IRemoveColMutationParams, - IRemoveRowsMutationParams, IRemoveWorksheetMergeMutationParams, } from '../basics/interfaces/mutation-interface'; @@ -34,6 +32,7 @@ import type { InsertRangeMoveRightCommandParams } from '../commands/commands/ins import type { IInsertColCommandParams, IInsertRowCommandParams } from '../commands/commands/insert-row-col.command'; import type { IMoveRangeCommandParams } from '../commands/commands/move-range.command'; import type { IMoveColsCommandParams, IMoveRowsCommandParams } from '../commands/commands/move-rows-cols.command'; +import type { IRemoveRowColCommandParams } from '../commands/commands/remove-row-col.command'; import type { IMoveRowsMutationParams } from '../commands/mutations/move-rows-cols.mutation'; import type { ISetWorksheetActiveOperationParams } from '../commands/operations/set-worksheet-active.operation'; import type { EffectRefRangeParams } from '../services/ref-range/type'; @@ -234,31 +233,31 @@ export class MergeCellController extends Disposable { refRangeHandle(config: EffectRefRangeParams, unitId: string, subUnitId: string) { switch (config.id) { case EffectRefRangId.MoveColsCommandId: { - const params = config.params as unknown as IMoveColsCommandParams; + const params = config.params as IMoveColsCommandParams; return this._handleMoveColsCommand(params, unitId, subUnitId); } case EffectRefRangId.MoveRowsCommandId: { - const params = config.params as unknown as IMoveRowsCommandParams; + const params = config.params as IMoveRowsCommandParams; return this._handleMoveRowsCommand(params, unitId, subUnitId); } case InsertRowCommand.id: { - const params = config.params as unknown as IInsertRowCommandParams; + const params = config.params as IInsertRowCommandParams; const _unitId = params.unitId || unitId; const _subUnitId = params.subUnitId || subUnitId; return this._handleInsertRowCommand(params, _unitId, _subUnitId); } case InsertColCommand.id: { - const params = config.params as unknown as IInsertColCommandParams; + const params = config.params as IInsertColCommandParams; const _unitId = params.unitId || unitId; const _subUnitId = params.subUnitId || subUnitId; return this._handleInsertColCommand(params, _unitId, _subUnitId); } case RemoveColCommand.id: { - const params = config.params as unknown as IRemoveColMutationParams; + const params = config.params as IRemoveRowColCommandParams; return this._handleRemoveColCommand(params, unitId, subUnitId); } case RemoveRowCommand.id: { - const params = config.params as unknown as IRemoveRowsMutationParams; + const params = config.params as IRemoveRowColCommandParams; return this._handleRemoveRowCommand(params, unitId, subUnitId); } @@ -267,19 +266,19 @@ export class MergeCellController extends Disposable { return this._handleMoveRangeCommand(params, unitId, subUnitId); } case InsertRangeMoveRightCommand.id: { - const params = config.params as unknown as InsertRangeMoveRightCommandParams; + const params = config.params as InsertRangeMoveRightCommandParams; return this._handleInsertRangeMoveRightCommand(params, unitId, subUnitId); } case InsertRangeMoveDownCommand.id: { - const params = config.params as unknown as InsertRangeMoveDownCommandParams; + const params = config.params as InsertRangeMoveDownCommandParams; return this._handleInsertRangeMoveDownCommand(params, unitId, subUnitId); } case DeleteRangeMoveUpCommand.id: { - const params = config.params as unknown as IDeleteRangeMoveUpCommandParams; + const params = config.params as IDeleteRangeMoveUpCommandParams; return this._handleDeleteRangeMoveUpCommand(params, unitId, subUnitId); } case DeleteRangeMoveLeftCommand.id: { - const params = config.params as unknown as IDeleteRangeMoveLeftCommandParams; + const params = config.params as IDeleteRangeMoveLeftCommandParams; return this._handleDeleteRangeMoveLeftCommand(params, unitId, subUnitId); } } @@ -618,7 +617,7 @@ export class MergeCellController extends Disposable { return { redos, undos }; } - private _handleRemoveColCommand(config: IRemoveColMutationParams, unitId: string, subUnitId: string) { + private _handleRemoveColCommand(config: IRemoveRowColCommandParams, unitId: string, subUnitId: string) { const workbook = getWorkbook(this._univerInstanceService, unitId); if (!workbook) { return this._handleNull(); @@ -627,39 +626,55 @@ export class MergeCellController extends Disposable { if (!worksheet) { return this._handleNull(); } - const { range } = config; - const { startColumn, endColumn } = range; + const { ranges } = config; - const oldMergeCells = Tools.deepClone(worksheet.getMergeData()).reduce((mergeCellsHasLapping, cell) => { - if (Rectangle.intersects(range, cell)) { - mergeCellsHasLapping.push(cell); - } - return mergeCellsHasLapping; - }, [] as IRange[]); + let oldMergeCells: IRange[] = []; + let newMergeCells: IRange[] = []; + + // Step 1: Collect all old merge cells affected by any of the ranges + ranges.forEach((range) => { + const currentOldMergeCells = Tools.deepClone(worksheet.getMergeData()).reduce((mergeCellsHasLapping, cell) => { + if (Rectangle.intersects(range, cell)) { + mergeCellsHasLapping.push(cell); + } + return mergeCellsHasLapping; + }, [] as IRange[]); + oldMergeCells = oldMergeCells.concat(currentOldMergeCells); + }); + // If no merge cells are affected, return early if (oldMergeCells.length === 0) { return this._handleNull(); } - const newMergeCells = Tools.deepClone(worksheet.getMergeData()).reduce((mergeCellsHasLapping, cell) => { - if (Rectangle.intersects(range, cell)) { - if (startColumn <= cell.startColumn && endColumn >= cell.endColumn) { - return mergeCellsHasLapping; - } else if (startColumn >= cell.startColumn && endColumn <= cell.endColumn) { - cell.endColumn -= endColumn - startColumn + 1; - } else if (startColumn < cell.startColumn) { - cell.startColumn = startColumn; - cell.endColumn -= endColumn - startColumn + 1; - } else if (endColumn > cell.endColumn) { - cell.endColumn = startColumn - 1; - } - if (this._checkIsMergeCell(cell)) { - mergeCellsHasLapping.push(cell); + // Step 2: Calculate new merge cells considering all ranges together + oldMergeCells.forEach((cell) => { + ranges.forEach((range) => { + const { startColumn, endColumn } = range; + + if (Rectangle.intersects(range, cell)) { + if (startColumn <= cell.startColumn && endColumn >= cell.endColumn) { + // This range fully covers the merged cell, remove it + + } else if (startColumn >= cell.startColumn && endColumn <= cell.endColumn) { + // This range is within the merged cell, shrink it + cell.endColumn -= endColumn - startColumn + 1; + } else if (startColumn < cell.startColumn) { + // Adjust start column and shrink the merge + cell.startColumn = startColumn; + cell.endColumn -= endColumn - startColumn + 1; + } else if (endColumn > cell.endColumn) { + // Adjust end column + cell.endColumn = startColumn - 1; + } } - } - return mergeCellsHasLapping; - }, [] as IRange[]); + }); + }); + + // Step 3: Filter out invalid merge cells after adjustment + newMergeCells = oldMergeCells.filter((cell) => this._checkIsMergeCell(cell)); + // Prepare mutation parameters for undo/redo const removeMergeMutationParams: IRemoveWorksheetMergeMutationParams = { unitId, subUnitId, @@ -679,6 +694,7 @@ export class MergeCellController extends Disposable { addMergeMutationParams ); + // Step 4: Return mutations for undo/redo operations const preRedos = [{ id: RemoveWorksheetMergeMutation.id, params: removeMergeMutationParams }]; const redos = [{ id: AddWorksheetMergeMutation.id, params: addMergeMutationParams }]; const preUndos = [{ id: RemoveWorksheetMergeMutation.id, params: undoAddMergeParams }]; @@ -686,8 +702,8 @@ export class MergeCellController extends Disposable { return { preUndos, undos, preRedos, redos }; } - private _handleRemoveRowCommand(config: IRemoveRowsMutationParams, unitId: string, subUnitId: string) { - const { range } = config; + private _handleRemoveRowCommand(config: IRemoveRowColCommandParams, unitId: string, subUnitId: string) { + const { ranges } = config; const workbook = getWorkbook(this._univerInstanceService, unitId); if (!workbook) { return this._handleNull(); @@ -697,38 +713,53 @@ export class MergeCellController extends Disposable { return this._handleNull(); } - const { startRow, endRow } = range; + let oldMergeCells: IRange[] = []; + let newMergeCells: IRange[] = []; - const oldMergeCells = Tools.deepClone(worksheet.getMergeData()).reduce((mergeCellsHasLapping, cell) => { - if (Rectangle.intersects(range, cell)) { - mergeCellsHasLapping.push(cell); - } - return mergeCellsHasLapping; - }, [] as IRange[]); + // Step 1: Collect all old merge cells affected by any of the ranges + ranges.forEach((range) => { + const currentOldMergeCells = Tools.deepClone(worksheet.getMergeData()).reduce((mergeCellsHasLapping, cell) => { + if (Rectangle.intersects(range, cell)) { + mergeCellsHasLapping.push(cell); + } + return mergeCellsHasLapping; + }, [] as IRange[]); + oldMergeCells = oldMergeCells.concat(currentOldMergeCells); + }); + // If no merge cells are affected, return early if (oldMergeCells.length === 0) { return this._handleNull(); } - const newMergeCells = Tools.deepClone(worksheet.getMergeData()).reduce((mergeCellsHasLapping, cell) => { - if (Rectangle.intersects(range, cell)) { - if (startRow <= cell.startRow && endRow >= cell.endRow) { - return mergeCellsHasLapping; - } else if (startRow >= cell.startRow && endRow <= cell.endRow) { - cell.endRow -= endRow - startRow + 1; - } else if (startRow < cell.startRow) { - cell.startRow = startRow; - cell.endRow -= endRow - startRow + 1; - } else if (endRow > cell.endRow) { - cell.endRow = startRow - 1; - } - if (this._checkIsMergeCell(cell)) { - mergeCellsHasLapping.push(cell); + // Step 2: Calculate new merge cells considering all ranges together + oldMergeCells.forEach((cell) => { + ranges.forEach((range) => { + const { startRow, endRow } = range; + + if (Rectangle.intersects(range, cell)) { + if (startRow <= cell.startRow && endRow >= cell.endRow) { + // This range fully covers the merged cell, remove it + + } else if (startRow >= cell.startRow && endRow <= cell.endRow) { + // This range is within the merged cell, shrink it + cell.endRow -= endRow - startRow + 1; + } else if (startRow < cell.startRow) { + // Adjust start row and shrink the merge + cell.startRow = startRow; + cell.endRow -= endRow - startRow + 1; + } else if (endRow > cell.endRow) { + // Adjust end row + cell.endRow = startRow - 1; + } } - } - return mergeCellsHasLapping; - }, [] as IRange[]); + }); + }); + + // Step 3: Filter out invalid merge cells after adjustment + newMergeCells = oldMergeCells.filter((cell) => this._checkIsMergeCell(cell)); + // Prepare mutation parameters for undo/redo const removeMergeMutationParams: IRemoveWorksheetMergeMutationParams = { unitId, subUnitId, @@ -748,6 +779,7 @@ export class MergeCellController extends Disposable { addMergeMutationParams ); + // Step 4: Return mutations for undo/redo operations const preRedos = [{ id: RemoveWorksheetMergeMutation.id, params: removeMergeMutationParams }]; const redos = [{ id: AddWorksheetMergeMutation.id, params: addMergeMutationParams }]; const preUndos = [{ id: RemoveWorksheetMergeMutation.id, params: undoAddMergeParams }]; diff --git a/packages/sheets/src/services/permission/range-permission/range-protection.ref-range.ts b/packages/sheets/src/services/permission/range-permission/range-protection.ref-range.ts index f84f43bfcea..3932c104a4f 100644 --- a/packages/sheets/src/services/permission/range-permission/range-protection.ref-range.ts +++ b/packages/sheets/src/services/permission/range-permission/range-protection.ref-range.ts @@ -48,9 +48,9 @@ import { InsertColMutation, InsertRowMutation } from '../../../commands/mutation import { type IMoveRowsMutationParams, MoveColsMutation, MoveRowsMutation } from '../../../commands/mutations/move-rows-cols.mutation'; import { RemoveColMutation, RemoveRowMutation } from '../../../commands/mutations/remove-row-col.mutation'; import { SetRangeProtectionMutation } from '../../../commands/mutations/set-range-protection.mutation'; -import { RangeProtectionCache } from '../../../model/range-protection.cache'; import { RangeProtectionRenderModel } from '../../../model/range-protection-render.model'; import { RangeProtectionRuleModel } from '../../../model/range-protection-rule.model'; +import { RangeProtectionCache } from '../../../model/range-protection.cache'; import { RefRangeService } from '../../../services/ref-range/ref-range.service'; import { SheetInterceptorService } from '../../sheet-interceptor/sheet-interceptor.service'; @@ -159,36 +159,40 @@ export class RangeProtectionRefRangeService extends Disposable { private _getRefRangeMutationsByDeleteCols(params: IRemoveRowColCommandParams, unitId: string, subUnitId: string) { const permissionRangeLapRules = this._selectionProtectionRuleModel.getSubunitRuleList(unitId, subUnitId).filter((rule) => { return rule.ranges.some((range) => { - return Rectangle.intersects(range, params.range); + return params.ranges.some((removeRange) => Rectangle.intersects(range, removeRange)); }); }); - const removeRange = params.range; if (permissionRangeLapRules.length) { const redoMutations: IMutationInfo[] = []; const undoMutations: IMutationInfo[] = []; + permissionRangeLapRules.forEach((rule) => { const cloneRule = Tools.deepClone(rule); const rangesByRemove = cloneRule.ranges.reduce((p, c) => { - if (Rectangle.intersects(c, removeRange)) { + if (params.ranges.some((removeRange) => Rectangle.intersects(c, removeRange))) { const cloneRange = Tools.deepClone(c); - const { startColumn, endColumn } = removeRange; - if (startColumn <= cloneRange.startColumn && endColumn >= cloneRange.endColumn) { - return p; - } else if (startColumn >= cloneRange.startColumn && endColumn <= cloneRange.endColumn) { - cloneRange.endColumn -= endColumn - startColumn + 1; - } else if (startColumn < cloneRange.startColumn) { - cloneRange.startColumn = startColumn; - cloneRange.endColumn -= endColumn - startColumn + 1; - } else if (endColumn > cloneRange.endColumn) { - cloneRange.endColumn = startColumn - 1; - } + params.ranges.forEach((removeRange) => { + const { startColumn, endColumn } = removeRange; + if (startColumn <= cloneRange.startColumn && endColumn >= cloneRange.endColumn) { + return p; + } else if (startColumn >= cloneRange.startColumn && endColumn <= cloneRange.endColumn) { + cloneRange.endColumn -= endColumn - startColumn + 1; + } else if (startColumn < cloneRange.startColumn) { + cloneRange.startColumn = startColumn; + cloneRange.endColumn -= endColumn - startColumn + 1; + } else if (endColumn > cloneRange.endColumn) { + cloneRange.endColumn = startColumn - 1; + } + }); + if (this._checkIsRightRange(cloneRange)) { p.push(cloneRange); } } return p; }, [] as IRange[]); + cloneRule.ranges = rangesByRemove; if (cloneRule.ranges.length) { redoMutations.push({ id: SetRangeProtectionMutation.id, params: { unitId, subUnitId, rule: cloneRule, ruleId: rule.id } }); @@ -207,36 +211,40 @@ export class RangeProtectionRefRangeService extends Disposable { private _getRefRangeMutationsByDeleteRows(params: IRemoveRowColCommandParams, unitId: string, subUnitId: string) { const permissionRangeLapRules = this._selectionProtectionRuleModel.getSubunitRuleList(unitId, subUnitId).filter((rule) => { return rule.ranges.some((range) => { - return Rectangle.intersects(range, params.range); + return params.ranges.some((removeRange) => Rectangle.intersects(range, removeRange)); }); }); - const removeRange = params.range; if (permissionRangeLapRules.length) { const redoMutations: { id: string; params: ISetRangeProtectionMutationParams }[] = []; const undoMutations: { id: string; params: ISetRangeProtectionMutationParams }[] = []; + permissionRangeLapRules.forEach((rule) => { const cloneRule = Tools.deepClone(rule); const rangesByRemove = cloneRule.ranges.reduce((p, c) => { - if (Rectangle.intersects(c, removeRange)) { + if (params.ranges.some((removeRange) => Rectangle.intersects(c, removeRange))) { const cloneRange = Tools.deepClone(c); - const { startRow, endRow } = removeRange; - if (startRow <= cloneRange.startRow && endRow >= cloneRange.endRow) { - return p; - } else if (startRow >= cloneRange.startRow && endRow <= cloneRange.endRow) { - cloneRange.endRow -= endRow - startRow + 1; - } else if (startRow < cloneRange.startRow) { - cloneRange.startRow = startRow; - cloneRange.endRow -= endRow - startRow + 1; - } else if (endRow > cloneRange.endRow) { - cloneRange.endRow = startRow - 1; - } + params.ranges.forEach((removeRange) => { + const { startRow, endRow } = removeRange; + if (startRow <= cloneRange.startRow && endRow >= cloneRange.endRow) { + return p; + } else if (startRow >= cloneRange.startRow && endRow <= cloneRange.endRow) { + cloneRange.endRow -= endRow - startRow + 1; + } else if (startRow < cloneRange.startRow) { + cloneRange.startRow = startRow; + cloneRange.endRow -= endRow - startRow + 1; + } else if (endRow > cloneRange.endRow) { + cloneRange.endRow = startRow - 1; + } + }); + if (this._checkIsRightRange(cloneRange)) { p.push(cloneRange); } } return p; }, [] as IRange[]); + cloneRule.ranges = rangesByRemove; redoMutations.push({ id: SetRangeProtectionMutation.id, params: { unitId, subUnitId, rule: cloneRule, ruleId: rule.id } }); undoMutations.push({ id: SetRangeProtectionMutation.id, params: { unitId, subUnitId, rule, ruleId: rule.id } }); diff --git a/packages/sheets/src/services/ref-range/__tests__/utils.spec.ts b/packages/sheets/src/services/ref-range/__tests__/utils.spec.ts index ed7caf6e419..6a08ac321bc 100644 --- a/packages/sheets/src/services/ref-range/__tests__/utils.spec.ts +++ b/packages/sheets/src/services/ref-range/__tests__/utils.spec.ts @@ -15,8 +15,12 @@ */ import type { IMutationInfo, IRange } from '@univerjs/core'; -import { beforeEach, describe, expect, it } from 'vitest'; +import type { IRemoveSheetMutationParams } from '../../../basics'; +import type { IMoveRowsMutationParams } from '../../../commands/mutations/move-rows-cols.mutation'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { MoveRowsMutation } from '../../../commands/mutations/move-rows-cols.mutation'; +import { RemoveSheetMutation } from '../../../commands/mutations/remove-sheet.mutation'; import { EffectRefRangId } from '../type'; import { adjustRangeOnMutation, @@ -39,10 +43,6 @@ import { handleRemoveRowCommon, runRefRangeMutations, } from '../util'; -import { RemoveSheetMutation } from '../../../commands/mutations/remove-sheet.mutation'; -import type { IRemoveSheetMutationParams } from '../../../basics'; -import type { IMoveRowsMutationParams } from '../../../commands/mutations/move-rows-cols.mutation'; -import { MoveRowsMutation } from '../../../commands/mutations/move-rows-cols.mutation'; const countRange = ([a, b, c, d]: readonly [number, number, number, number]) => (a * 1000 + b * 100 + c * 10 + d); @@ -1241,15 +1241,15 @@ describe('test ref-range move', () => { }); }); describe('handleIRemoveCol', () => { - let range: IRange; + let ranges: IRange[]; describe('the range contain targetRange', () => { beforeEach(() => { - range = { startRow: 0, endColumn: 10, startColumn: 5, endRow: 99 }; + ranges = [{ startRow: 0, endColumn: 10, startColumn: 5, endRow: 99 }]; }); it('the targetRange is single col', () => { const targetRange = { startRow: 0, endColumn: 5, startColumn: 5, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1258,7 +1258,7 @@ describe('test ref-range move', () => { it('the targetRange is multiple col ', () => { const targetRange = { startRow: 0, endColumn: 5, startColumn: 6, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1267,7 +1267,7 @@ describe('test ref-range move', () => { it('the targetRange is equal range', () => { const targetRange = { startRow: 0, endColumn: 5, startColumn: 10, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1276,12 +1276,12 @@ describe('test ref-range move', () => { }); describe('the targetRange in the range right', () => { beforeEach(() => { - range = { startRow: 0, endColumn: 10, startColumn: 5, endRow: 99 }; + ranges = [{ startRow: 0, endColumn: 10, startColumn: 5, endRow: 99 }]; }); it('targetRange is overlap with range ', () => { const targetRange = { startRow: 0, endColumn: 11, startColumn: 10, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1290,7 +1290,7 @@ describe('test ref-range move', () => { it('targetRange is no overlap with range ', () => { const targetRange = { startRow: 0, endColumn: 12, startColumn: 11, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1299,12 +1299,12 @@ describe('test ref-range move', () => { }); describe('the targetRange in the range left', () => { beforeEach(() => { - range = { startRow: 0, endColumn: 10, startColumn: 5, endRow: 99 }; + ranges = [{ startRow: 0, endColumn: 10, startColumn: 5, endRow: 99 }]; }); it('targetRange is overlap with range ', () => { const targetRange = { startRow: 0, endColumn: 5, startColumn: 4, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1313,7 +1313,7 @@ describe('test ref-range move', () => { it('targetRange is no overlap with range ', () => { const targetRange = { startRow: 0, endColumn: 3, startColumn: 2, endRow: 99 }; const operators = handleIRemoveCol( - { params: { range }, id: EffectRefRangId.RemoveColCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveColCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1322,15 +1322,15 @@ describe('test ref-range move', () => { }); }); describe('handleIRemoveRow', () => { - let range: IRange; + let ranges: IRange[]; describe('the range contain targetRange', () => { beforeEach(() => { - range = { startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }; + ranges = [{ startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }]; }); it('the targetRange is single row', () => { const targetRange = { startRow: 5, endRow: 5, startColumn: 0, endColumn: 99 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1339,7 +1339,7 @@ describe('test ref-range move', () => { it('the targetRange is multiple row ', () => { const targetRange = { startRow: 0, endRow: 5, startColumn: 0, endColumn: 99 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1353,12 +1353,12 @@ describe('test ref-range move', () => { }); describe('the targetRange in the range top', () => { beforeEach(() => { - range = { startRow: 5, endColumn: 99, startColumn: 0, endRow: 10 }; + ranges = [{ startRow: 5, endColumn: 99, startColumn: 0, endRow: 10 }]; }); it('targetRange is overlap with range ', () => { const targetRange = { startRow: 4, endRow: 5, endColumn: 99, startColumn: 0 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1367,7 +1367,7 @@ describe('test ref-range move', () => { it('targetRange is no overlap with range ', () => { const targetRange = { startRow: 0, endColumn: 99, startColumn: 0, endRow: 3 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1376,12 +1376,12 @@ describe('test ref-range move', () => { }); describe('the targetRange in the range bottom', () => { beforeEach(() => { - range = { startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }; + ranges = [{ startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }]; }); it('targetRange is overlap with range ', () => { const targetRange = { startRow: 10, endRow: 11, startColumn: 0, endColumn: 99 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1390,7 +1390,7 @@ describe('test ref-range move', () => { it('targetRange is no overlap with range ', () => { const targetRange = { startRow: 11, endRow: 13, endColumn: 99, startColumn: 0 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -1456,16 +1456,16 @@ describe('test different situations of adjustRangeOnMutation', () => { it('should be delete', () => { const targetRange: IRange = { startRow: 5, endRow: 7, startColumn: 1, endColumn: 1 }; - const resultRange = handleRemoveRowCommon({ range: { ...targetRange } }, targetRange); + const resultRange = handleRemoveRowCommon({ ranges: [{ ...targetRange }] }, targetRange); expect(resultRange.length).toBe(0); - const resultRange2 = handleRemoveRowCommon({ range: { ...targetRange } }, { ...targetRange, startRow: 4 }); + const resultRange2 = handleRemoveRowCommon({ ranges: [{ ...targetRange }] }, { ...targetRange, startRow: 4 }); expect(resultRange2).toEqual([{ startRow: 4, endRow: 4, startColumn: 1, endColumn: 1 }]); }); it('should not be delete ', () => { const targetRange: IRange = { startRow: 5, endRow: 7, startColumn: 1, endColumn: 1 }; - const resultRange = handleRemoveRowCommon({ range: { ...targetRange }, ranges: [{ startRow: 5, endRow: 5, startColumn: 1, endColumn: 1 }, { startRow: 6, endRow: 6, startColumn: 1, endColumn: 1 }] }, targetRange); + const resultRange = handleRemoveRowCommon({ ranges: [{ startRow: 5, endRow: 5, startColumn: 1, endColumn: 1 }, { startRow: 6, endRow: 6, startColumn: 1, endColumn: 1 }] }, targetRange); expect(resultRange).toEqual([{ startRow: 5, endRow: 5, startColumn: 1, endColumn: 1 }]); }); }); diff --git a/packages/sheets/src/services/ref-range/__tests__/watch-range.spec.ts b/packages/sheets/src/services/ref-range/__tests__/watch-range.spec.ts index 44836cf524f..2d0811ca20d 100644 --- a/packages/sheets/src/services/ref-range/__tests__/watch-range.spec.ts +++ b/packages/sheets/src/services/ref-range/__tests__/watch-range.spec.ts @@ -15,18 +15,18 @@ */ import type { IMutationInfo, IRange } from '@univerjs/core'; -import { beforeEach, describe, expect, it } from 'vitest'; +import type { IRemoveColMutationParams, IRemoveRowsMutationParams } from '../../../basics'; +import type { IMoveRowsMutationParams } from '../../../commands/mutations/move-rows-cols.mutation'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { MoveRowsMutation } from '../../../commands/mutations/move-rows-cols.mutation'; +import { RemoveColMutation, RemoveRowMutation } from '../../../commands/mutations/remove-row-col.mutation'; import { EffectRefRangId } from '../type'; import { adjustRangeOnMutation, handleIRemoveRow, runRefRangeMutations, } from '../util'; -import type { IRemoveColMutationParams, IRemoveRowsMutationParams } from '../../../basics'; -import type { IMoveRowsMutationParams } from '../../../commands/mutations/move-rows-cols.mutation'; -import { MoveRowsMutation } from '../../../commands/mutations/move-rows-cols.mutation'; -import { RemoveColMutation, RemoveRowMutation } from '../../../commands/mutations/remove-row-col.mutation'; describe('test ref-range move', () => { describe('handleMoveRows', () => { @@ -392,7 +392,7 @@ describe('test ref-range move', () => { }); describe('handleIRemoveRow', () => { - let range: IRange; + let ranges: IRange[]; let getRemoveRowMutation: (range: IRange) => IMutationInfo; beforeEach(() => { @@ -403,18 +403,18 @@ describe('test ref-range move', () => { describe('the range contain targetRange', () => { beforeEach(() => { - range = { startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }; + ranges = [{ startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }]; }); it('the targetRange is single row', () => { const targetRange = { startRow: 5, endRow: 5, startColumn: 0, endColumn: 99 }; - const result = adjustRangeOnMutation(targetRange, getRemoveRowMutation(range)); + const result = adjustRangeOnMutation(targetRange, getRemoveRowMutation(ranges[0])); expect(result).toBe(null); }); it('the targetRange is multiple row ', () => { const targetRange = { startRow: 0, endRow: 5, startColumn: 0, endColumn: 99 }; - const result = adjustRangeOnMutation(targetRange, getRemoveRowMutation(range)); + const result = adjustRangeOnMutation(targetRange, getRemoveRowMutation(ranges[0])); expect(result).toEqual({ endColumn: 99, endRow: 4, @@ -426,13 +426,13 @@ describe('test ref-range move', () => { describe('the targetRange in the range top', () => { beforeEach(() => { - range = { startRow: 5, endColumn: 99, startColumn: 0, endRow: 10 }; + ranges = [{ startRow: 5, endColumn: 99, startColumn: 0, endRow: 10 }]; }); it('targetRange is overlap with range ', () => { const targetRange = { startRow: 4, endRow: 5, endColumn: 99, startColumn: 0 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -442,7 +442,7 @@ describe('test ref-range move', () => { it('targetRange is no overlap with range ', () => { const targetRange = { startRow: 0, endColumn: 99, startColumn: 0, endRow: 3 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -452,13 +452,13 @@ describe('test ref-range move', () => { describe('the targetRange in the range bottom', () => { beforeEach(() => { - range = { startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }; + ranges = [{ startRow: 5, endRow: 10, startColumn: 0, endColumn: 99 }]; }); it('targetRange is overlap with range ', () => { const targetRange = { startRow: 10, endRow: 11, startColumn: 0, endColumn: 99 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); @@ -468,7 +468,7 @@ describe('test ref-range move', () => { it('targetRange is no overlap with range ', () => { const targetRange = { startRow: 11, endRow: 13, endColumn: 99, startColumn: 0 }; const operators = handleIRemoveRow( - { params: { range }, id: EffectRefRangId.RemoveRowCommandId }, + { params: { ranges }, id: EffectRefRangId.RemoveRowCommandId }, targetRange ); const result = runRefRangeMutations(operators!, targetRange); diff --git a/packages/sheets/src/services/ref-range/ref-range.service.ts b/packages/sheets/src/services/ref-range/ref-range.service.ts index b03bcde9e0e..dd89cec25f8 100644 --- a/packages/sheets/src/services/ref-range/ref-range.service.ts +++ b/packages/sheets/src/services/ref-range/ref-range.service.ts @@ -231,28 +231,41 @@ export class RefRangeService extends Disposable { } case EffectRefRangId.RemoveRowCommandId: { const params = command; - - const rowStart = params.params!.range.startRow; - const range: IRange = { - startRow: rowStart, - endRow: worksheet.getRowCount() - 1, - startColumn: 0, - endColumn: worksheet.getColumnCount() - 1, - rangeType: RANGE_TYPE.ROW, - }; - return this._checkRange([range], unitId, subUnitId); + const ranges = params.params!.ranges; + + // Iterate through each range in the ranges array + const rangeList: IRange[] = ranges.map((range) => { + const rowStart = range.startRow; + return { + startRow: rowStart, + endRow: worksheet.getRowCount() - 1, + startColumn: 0, + endColumn: worksheet.getColumnCount() - 1, + rangeType: RANGE_TYPE.ROW, + }; + }); + + // Now check the range(s) + return this._checkRange(rangeList, unitId, subUnitId); } case EffectRefRangId.RemoveColCommandId: { const params = command; - const colStart = params.params!.range.startColumn; - const range: IRange = { - startRow: 0, - endRow: worksheet.getRowCount() - 1, - startColumn: colStart, - endColumn: worksheet.getColumnCount() - 1, - rangeType: RANGE_TYPE.COLUMN, - }; - return this._checkRange([range], unitId, subUnitId); + const ranges = params.params!.ranges; + + // Iterate through each range in the ranges array + const rangeList: IRange[] = ranges.map((range) => { + const colStart = range.startColumn; + return { + startRow: 0, + endRow: worksheet.getRowCount() - 1, + startColumn: colStart, + endColumn: worksheet.getColumnCount() - 1, + rangeType: RANGE_TYPE.COLUMN, + }; + }); + + // Now check the range(s) + return this._checkRange(rangeList, unitId, subUnitId); } case EffectRefRangId.DeleteRangeMoveUpCommandId: case EffectRefRangId.InsertRangeMoveDownCommandId: { diff --git a/packages/sheets/src/services/ref-range/util.ts b/packages/sheets/src/services/ref-range/util.ts index e0560b382e5..2a1dc255cfa 100644 --- a/packages/sheets/src/services/ref-range/util.ts +++ b/packages/sheets/src/services/ref-range/util.ts @@ -495,42 +495,48 @@ export const handleBaseRemoveRange = (_removeRange: IRange, _targetRange: IRange return { step: 0, length: 0 }; }; export const handleIRemoveCol = (param: IRemoveRowColCommand, targetRange: IRange) => { - const range = param.params?.range; - if (!range) { + const ranges = param.params?.ranges; + if (!ranges) { return []; } const operators: IOperator[] = []; - const result = handleBaseRemoveRange(range, targetRange); - if (!result) { - operators.push({ type: OperatorType.Delete }); - } else { - const { step, length } = result; - operators.push({ - type: OperatorType.HorizontalMove, - step, - length, - }); + for (const range of ranges) { + const result = handleBaseRemoveRange(range, targetRange); + if (!result) { + operators.push({ type: OperatorType.Delete }); + } else { + const { step, length } = result; + operators.push({ + type: OperatorType.HorizontalMove, + step, + length, + }); + } } + return operators; }; export const handleIRemoveRow = (param: IRemoveRowColCommand, targetRange: IRange) => { - const range = param.params?.range; - if (!range) { + const ranges = param.params?.ranges; + if (!ranges) { return []; } const operators: IOperator[] = []; - const result = handleBaseRemoveRange(rotateRange(range), rotateRange(targetRange)); - if (!result) { - operators.push({ type: OperatorType.Delete }); - } else { - const { step, length } = result; - operators.push({ - type: OperatorType.VerticalMove, - step, - length, - }); + for (const range of ranges) { + const result = handleBaseRemoveRange(rotateRange(range), rotateRange(targetRange)); + if (!result) { + operators.push({ type: OperatorType.Delete }); + } else { + const { step, length } = result; + operators.push({ + type: OperatorType.VerticalMove, + step, + length, + }); + } } + return operators; }; @@ -884,7 +890,7 @@ export const handleDeleteRangeMoveUpCommon = (param: IDeleteRangeMoveUpCommand, }; export const handleRemoveRowCommon = (param: IRemoveRowColCommandInterceptParams, targetRange: IRange) => { - const ranges = param.ranges ?? [param.range]; + const ranges = param.ranges; const matrix = new ObjectMatrix(); Range.foreach(targetRange, (row, col) => { @@ -1299,13 +1305,13 @@ export function getEffectedRangesOnCommand(command: EffectRefRangeParams, deps: } case EffectRefRangId.RemoveRowCommandId: { const params = command; - const range: IRange = params.params!.range; - return [range]; + const ranges: IRange[] = params.params!.ranges; + return ranges; } case EffectRefRangId.RemoveColCommandId: { const params = command; - const range: IRange = params.params!.range; - return [range]; + const ranges: IRange[] = params.params!.ranges; + return ranges; } case EffectRefRangId.DeleteRangeMoveUpCommandId: case EffectRefRangId.InsertRangeMoveDownCommandId: { @@ -1516,16 +1522,16 @@ export function getSeparateEffectedRangesOnCommand(accessor: IAccessor, command: } case EffectRefRangId.RemoveRowCommandId: { const params = command.params!; - const range: IRange = params.range; + const ranges: IRange[] = params.ranges; const target = getSheetCommandTarget(univerInstanceService)!; return { unitId: target.unitId, subUnitId: target.subUnitId, ranges: [ - range, + ...ranges, { - ...range, - startRow: range.endRow + 1, + ...ranges[ranges.length - 1], + startRow: ranges[ranges.length - 1].endRow + 1, endRow: Number.MAX_SAFE_INTEGER, }, ], @@ -1533,17 +1539,17 @@ export function getSeparateEffectedRangesOnCommand(accessor: IAccessor, command: } case EffectRefRangId.RemoveColCommandId: { const params = command.params!; - const range: IRange = params.range; + const ranges: IRange[] = params.ranges; const target = getSheetCommandTarget(univerInstanceService)!; return { unitId: target.unitId, subUnitId: target.subUnitId, ranges: [ - range, + ...ranges, { - ...range, - startColumn: range.endColumn + 1, + ...ranges[ranges.length - 1], + startColumn: ranges[ranges.length - 1].endColumn + 1, endColumn: Number.MAX_SAFE_INTEGER, }, ],