Skip to content

Commit

Permalink
fix(formula): update formula string in other sheet (#4107)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dushusir authored Nov 21, 2024
1 parent 1ec6dbe commit 3cf0e3b
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ const TEST_WORKBOOK_DATA_DEMO = (): IWorkbookData => ({
},
},
9: {
0: {
f: '=SUM(Sheet1!A1:A3)',
},
9: {
f: '=SUM(A2:C4)',
},
Expand Down Expand Up @@ -987,6 +990,42 @@ describe('Test update formula ', () => {
expect(valuesRedo).toStrictEqual([[{ f: '=SUM(A9)' }]]);
});

it('Remove row, changes to other sheet reference ranges', async () => {
const params: IRemoveRowColCommandParams = {
range: {
startRow: 1,
endRow: 1,
startColumn: 0,
endColumn: 19,
},
};

expect(await commandService.executeCommand(RemoveRowCommand.id, params)).toBeTruthy();

const values1 = getValues(1, 0, 2, 0, 'sheet2');
expect(values1).toStrictEqual([[null], [{ f: '=SUM(DefinedName1)' }]]);
const values2 = getValues(8, 0, 9, 0, 'sheet2');
expect(values2).toStrictEqual([[null], [{ f: '=SUM(Sheet1!A1:A2)' }]]);
const values3 = getValues(8, 9, 9, 9, 'sheet2');
expect(values3).toStrictEqual([[null], [{ f: '=SUM(A2:C4)' }]]);

expect(await commandService.executeCommand(UndoCommand.id)).toBeTruthy();
const valuesUndo1 = getValues(1, 0, 2, 0, 'sheet2');
expect(valuesUndo1).toStrictEqual([[null], [{ f: '=SUM(DefinedName1)' }]]);
const valuesUndo2 = getValues(8, 0, 9, 0, 'sheet2');
expect(valuesUndo2).toStrictEqual([[null], [{ f: '=SUM(Sheet1!A1:A3)' }]]);
const valuesUndo3 = getValues(8, 9, 9, 9, 'sheet2');
expect(valuesUndo3).toStrictEqual([[null], [{ f: '=SUM(A2:C4)' }]]);

expect(await commandService.executeCommand(RedoCommand.id)).toBeTruthy();
const valuesRedo1 = getValues(1, 0, 2, 0, 'sheet2');
expect(valuesRedo1).toStrictEqual([[null], [{ f: '=SUM(DefinedName1)' }]]);
const valuesRedo2 = getValues(8, 0, 9, 0, 'sheet2');
expect(valuesRedo2).toStrictEqual([[null], [{ f: '=SUM(Sheet1!A1:A2)' }]]);
const valuesRedo3 = getValues(8, 9, 9, 9, 'sheet2');
expect(valuesRedo3).toStrictEqual([[null], [{ f: '=SUM(A2:C4)' }]]);
});

it('Remove column, update reference', async () => {
const params: IRemoveRowColCommandParams = {
range: {
Expand Down
142 changes: 96 additions & 46 deletions packages/sheets-formula/src/controllers/utils/ref-range-formula.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,9 @@ export function getFormulaReferenceSheet(oldFormulaData: IFormulaData,
export function getFormulaReferenceRange(oldFormulaData: IFormulaData,
newFormulaData: IFormulaData,
formulaReferenceMoveParam: IFormulaReferenceMoveParam) {
const { sheetId: subUnitId, unitId } = formulaReferenceMoveParam;
const { redoFormulaData, undoFormulaData } = refRangeFormula(oldFormulaData, newFormulaData, formulaReferenceMoveParam);

// If the formula data is the same, no operation is required
// If the formula data is the same, no operation is required
if (Tools.diffValue(redoFormulaData, undoFormulaData)) {
return {
undos: [],
Expand All @@ -162,35 +161,39 @@ export function getFormulaReferenceRange(oldFormulaData: IFormulaData,
const redos: IMutationInfo[] = [];
const undos: IMutationInfo[] = [];

if (Object.keys(redoFormulaData).length !== 0) {
const redoSetRangeValuesMutationParams: ISetRangeValuesMutationParams = {
subUnitId,
unitId,
cellValue: redoFormulaData,
};

const redoMutation = {
id: SetRangeValuesMutation.id,
params: redoSetRangeValuesMutationParams,
};

redos.push(redoMutation);
}

if (Object.keys(undoFormulaData).length !== 0) {
const undoSetRangeValuesMutationParams: ISetRangeValuesMutationParams = {
subUnitId,
unitId,
cellValue: undoFormulaData,
};

const undoMutation = {
id: SetRangeValuesMutation.id,
params: undoSetRangeValuesMutationParams,
};
Object.keys(redoFormulaData).forEach((unitId) => {
Object.keys(redoFormulaData[unitId]).forEach((subUnitId) => {
if (Object.keys(redoFormulaData[unitId][subUnitId]).length !== 0) {
const redoSetRangeValuesMutationParams: ISetRangeValuesMutationParams = {
subUnitId,
unitId,
cellValue: redoFormulaData[unitId][subUnitId],
};
const redoMutation = {
id: SetRangeValuesMutation.id,
params: redoSetRangeValuesMutationParams,
};
redos.push(redoMutation);
}
});
});

undos.push(undoMutation);
}
Object.keys(undoFormulaData).forEach((unitId) => {
Object.keys(undoFormulaData[unitId]).forEach((subUnitId) => {
if (Object.keys(undoFormulaData[unitId][subUnitId]).length !== 0) {
const undoSetRangeValuesMutationParams: ISetRangeValuesMutationParams = {
subUnitId,
unitId,
cellValue: undoFormulaData[unitId][subUnitId],
};
const undoMutation = {
id: SetRangeValuesMutation.id,
params: undoSetRangeValuesMutationParams,
};
undos.push(undoMutation);
}
});
});

return {
undos,
Expand All @@ -208,28 +211,61 @@ export function getFormulaReferenceRange(oldFormulaData: IFormulaData,
export function refRangeFormula(oldFormulaData: IFormulaData,
newFormulaData: IFormulaData,
formulaReferenceMoveParam: IFormulaReferenceMoveParam) {
let redoFormulaData: IObjectMatrixPrimitiveType<Nullable<ICellData>> = {};
let undoFormulaData: IObjectMatrixPrimitiveType<Nullable<ICellData>> = {};
const redoFormulaData: Record<string, Record<string, IObjectMatrixPrimitiveType<Nullable<ICellData>>>> = {};
const undoFormulaData: Record<string, Record<string, IObjectMatrixPrimitiveType<Nullable<ICellData>>>> = {};

const { type, unitId, sheetId, range, from, to } = formulaReferenceMoveParam;
const { type, unitId: targetUnitId, sheetId, range, from, to } = formulaReferenceMoveParam;

if (checkFormulaDataNull(oldFormulaData, unitId, sheetId)) {
return {
redoFormulaData,
undoFormulaData,
};
}
// Iterate over all unitId in oldFormulaData
const allUnitIds = new Set([...Object.keys(oldFormulaData), ...Object.keys(newFormulaData)]);

allUnitIds.forEach((unitId) => {
if (checkFormulaDataNull(oldFormulaData, unitId, sheetId)) {
return;
}

const currentOldFormulaData = oldFormulaData[unitId]![sheetId];
const currentNewFormulaData = newFormulaData[unitId]![sheetId];
const allSheetIds = new Set([
...Object.keys(oldFormulaData[unitId] || {}),
...Object.keys(newFormulaData[unitId] || {}),
]);

const oldFormulaMatrix = new ObjectMatrix(currentOldFormulaData || {});
const newFormulaMatrix = new ObjectMatrix(currentNewFormulaData || {});
allSheetIds.forEach((currentSheetId) => {
const currentOldFormulaData = oldFormulaData[unitId]?.[currentSheetId];
const currentNewFormulaData = newFormulaData[unitId]?.[currentSheetId];

const rangeList = processFormulaChanges(oldFormulaMatrix, type, from, to, range);
const oldFormulaMatrix = new ObjectMatrix(currentOldFormulaData || {});
const newFormulaMatrix = new ObjectMatrix(currentNewFormulaData || {});

redoFormulaData = getRedoFormulaData(rangeList, oldFormulaMatrix, newFormulaMatrix);
undoFormulaData = getUndoFormulaData(rangeList, oldFormulaMatrix);
let rangeList: IRangeChange[] = [];

// If the sheet where the range is changed is different from the current sheet, the position will not be changed.
// Simply get the data from newFormulaMatrix to update the range.
if (unitId !== targetUnitId || currentSheetId !== sheetId) {
rangeList = processFormulaRange(newFormulaMatrix);
} else {
rangeList = processFormulaChanges(oldFormulaMatrix, type, from, to, range);
}

const sheetRedoFormulaData = getRedoFormulaData(rangeList, oldFormulaMatrix, newFormulaMatrix);
const sheetUndoFormulaData = getUndoFormulaData(rangeList, oldFormulaMatrix);

if (!redoFormulaData[unitId]) {
redoFormulaData[unitId] = {};
}
if (!undoFormulaData[unitId]) {
undoFormulaData[unitId] = {};
}

redoFormulaData[unitId][currentSheetId] = {
...redoFormulaData[unitId][currentSheetId],
...sheetRedoFormulaData,
};
undoFormulaData[unitId][currentSheetId] = {
...undoFormulaData[unitId][currentSheetId],
...sheetUndoFormulaData,
};
});
});

return {
redoFormulaData,
Expand Down Expand Up @@ -268,6 +304,20 @@ function processFormulaChanges(oldFormulaMatrix: ObjectMatrix<Nullable<IFormulaD
return rangeList;
}

function processFormulaRange(newFormulaMatrix: ObjectMatrix<Nullable<IFormulaDataItem>>) {
const rangeList: IRangeChange[] = [];

newFormulaMatrix.forValue((row, column, cell) => {
if (cell == null || !isFormulaDataItem(cell)) return true;

const newCell = cellToRange(row, column);

rangeList.push({ oldCell: newCell, newCell });
});

return rangeList;
}

function handleMove(type: FormulaReferenceMoveType, from: Nullable<IRange>, to: Nullable<IRange>, oldCell: IRange) {
if (from == null || to == null) {
return null;
Expand Down

0 comments on commit 3cf0e3b

Please sign in to comment.