Skip to content

Commit

Permalink
fix(sheets-ui): merge setRangeValuesMutation at clipboardservice (#1665)
Browse files Browse the repository at this point in the history
* fix: setRangeValues merge

* fix: type

* fix: cr revision

* chore: fix test
  • Loading branch information
yuhongz authored Mar 23, 2024
1 parent 5e597fd commit bf9fc0d
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/sheets-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,4 @@ export { UniverSheetsUIPlugin } from './sheets-ui-plugin';
export { SheetCanvasView } from './views/sheet-canvas-view';
export { SHEET_VIEW_KEY } from './common/keys';
export { CanvasPopManagerService } from './services/canvas-pop-manager.service';
export { mergeSetRangeValues } from './services/clipboard/utils';
63 changes: 61 additions & 2 deletions packages/sheets-ui/src/services/clipboard/__tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
* limitations under the License.
*/

import type { IRange } from '@univerjs/core';
import type { IMutationInfo, IRange } from '@univerjs/core';
import { SetRangeValuesMutation } from '@univerjs/sheets';

import { describe, expect, it } from 'vitest';

import { getRepeatRange } from '../utils';
import { getRepeatRange, mergeSetRangeValues } from '../utils';

describe('test getRepeatRange', () => {
it('repeat row 2 times', () => {
Expand Down Expand Up @@ -134,3 +136,60 @@ describe('test getRepeatRange', () => {
]);
});
});

describe('test "mergeSetRangeValues"', () => {
it('empty array, will do nothing', () => {
const mutations: IMutationInfo[] = [];
expect(mergeSetRangeValues(mutations)).toStrictEqual([]);
});

it ('no setRangeValues mutations, will no nothing', () => {
const mutations: IMutationInfo[] = [{ id: 'whatever', params: {} }];
expect(mergeSetRangeValues(mutations)).toStrictEqual([{ id: 'whatever', params: {} }]);
});

it ('setRangeValues mutations are not applied in same worksheet, will do nothing', () => {
const mutations: IMutationInfo[] = [
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '3', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: 'whatever', params: {} }];
expect(mergeSetRangeValues(mutations)).toStrictEqual([
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '3', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: 'whatever', params: {} }]);
});
it ('part of setRangeValues mutations are applied in same worksheet, will merge these part', () => {
const mutations: IMutationInfo[] = [
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 3: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '3', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: 'whatever', params: {} }];
expect(mergeSetRangeValues(mutations as IMutationInfo[])).toStrictEqual([
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' }, 3: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '3', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: 'whatever', params: {} }]);
});

it ('If some setRangeValues appear discontinuously, they will be merged separately.', () => {
const mutations: IMutationInfo[] = [
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { f: 'formula' } } } } },
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 3: { v: 'value' } } } } },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 4: { v: 'value' } } } } },
];
expect(mergeSetRangeValues(mutations as IMutationInfo[])).toStrictEqual([
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value', f: 'formula' } } } } },
{ id: 'whatever', params: {} },
{ id: SetRangeValuesMutation.id, params: { unitId: '1', subUnitId: '2', cellValue: { 1: { 2: { v: 'value' }, 3: { v: 'value' }, 4: { v: 'value' } } } } },
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import type {
} from './type';
import { COPY_TYPE } from './type';
import { USMToHtmlService } from './usm-to-html/convertor';
import { clipboardItemIsFromExcel } from './utils';
import { clipboardItemIsFromExcel, mergeSetRangeValues } from './utils';

export const PREDEFINED_HOOK_NAME = {
DEFAULT_COPY: 'default-copy',
Expand Down Expand Up @@ -536,8 +536,8 @@ export class SheetClipboardService extends Disposable implements ISheetClipboard
pasteType,
};

const redoMutationsInfo: IMutationInfo[] = [];
const undoMutationsInfo: IMutationInfo[] = [];
let redoMutationsInfo: IMutationInfo[] = [];
let undoMutationsInfo: IMutationInfo[] = [];

// if hooks are not special or default, it will be executed in any case.
// other hooks will be executed only when the paste type is the same as the hook name, including the default one
Expand Down Expand Up @@ -583,6 +583,9 @@ export class SheetClipboardService extends Disposable implements ISheetClipboard
redoMutationsInfo.push(setSelectionOperation);
}

redoMutationsInfo = mergeSetRangeValues(redoMutationsInfo);
undoMutationsInfo = mergeSetRangeValues(undoMutationsInfo);

this._logService.log('[SheetClipboardService]', 'pasting mutations', {
undoMutationsInfo,
redoMutationsInfo,
Expand Down
54 changes: 53 additions & 1 deletion packages/sheets-ui/src/services/clipboard/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
* limitations under the License.
*/

import type { IRange } from '@univerjs/core';
import { ObjectMatrix } from '@univerjs/core';
import type { ICellData, IMutationInfo, IObjectMatrixPrimitiveType, IRange, Nullable } from '@univerjs/core';
import type { ISetRangeValuesMutationParams } from '@univerjs/sheets';
import { SetRangeValuesMutation } from '@univerjs/sheets';

/**
*
Expand Down Expand Up @@ -99,3 +102,52 @@ export async function clipboardItemIsFromExcel(html: string): Promise<boolean> {

return false;
}

export function mergeCellValues(...cellValues: IObjectMatrixPrimitiveType<Nullable<ICellData>>[]) {
if (cellValues.length === 1) {
return cellValues[0];
}
const newMatrix = new ObjectMatrix<IObjectMatrixPrimitiveType<Nullable<ICellData>>>();
cellValues.forEach((cellValue) => {
if (cellValue) {
const matrix = new ObjectMatrix(cellValue);
matrix.forValue((row, col, value) => {
newMatrix.setValue(row, col, { ...newMatrix.getValue(row, col), ...value });
});
}
});
return newMatrix.getMatrix();
}

export function getRangeValuesMergeable(m1: IMutationInfo<ISetRangeValuesMutationParams>, m2: IMutationInfo<ISetRangeValuesMutationParams>) {
return m1.id === m2.id
&& m1.params.unitId === m2.params.unitId
&& m1.params.subUnitId === m2.params.subUnitId;
}

export function mergeSetRangeValues(mutations: IMutationInfo[]) {
const newMutations: IMutationInfo[] = [];
for (let i = 0; i < mutations.length;) {
let cursor = 1;
if (mutations[i].id === SetRangeValuesMutation.id) {
const current = mutations[i] as IMutationInfo<ISetRangeValuesMutationParams>;
const toMerge = [current];
while (i + cursor < mutations.length && getRangeValuesMergeable(current, mutations[i + cursor] as IMutationInfo<ISetRangeValuesMutationParams>)) {
toMerge.push(mutations[i + cursor] as IMutationInfo<ISetRangeValuesMutationParams>);
cursor += 1;
}
const merged = mergeCellValues(...toMerge.map((m: IMutationInfo<ISetRangeValuesMutationParams>) => m.params.cellValue || {}));
newMutations.push({
...current,
params: {
...current.params,
cellValue: merged,
},
});
} else {
newMutations.push(mutations[i]);
}
i += cursor;
}
return newMutations;
}

0 comments on commit bf9fc0d

Please sign in to comment.