Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sheets-ui): merge setRangeValuesMutation at clipboardservice #1665

Merged
merged 4 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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';
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;
}
Loading