Skip to content
Open
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
127 changes: 127 additions & 0 deletions packages/vtable/__tests__/plugins/custom-cell-style.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// @ts-nocheck
import { CustomCellStylePlugin } from '../../src/plugins/custom-cell-style';

function createMockTable(colCount = 5000, rowCount = 5000) {
return {
colCount,
rowCount,
getCellRange: (col: number, row: number) => ({
start: { col, row },
end: { col, row }
}),
getCellValue: jest.fn(),
getCellOriginValue: jest.fn(),
getCellHeaderPaths: jest.fn(),
scenegraph: {
updateCellContent: jest.fn(),
updateNextFrame: jest.fn()
}
};
}

describe('CustomCellStylePlugin', () => {
test('apply and clear single cell style without shrinking array', () => {
const table = createMockTable();
const plugin = new CustomCellStylePlugin(
table as any,
[
{
id: 's1',
style: { bgColor: 'red' }
}
] as any,
[] as any
);

plugin.arrangeCustomCellStyle({ col: 1, row: 2 }, 's1');
expect(plugin.getCustomCellStyleIds(1, 2)).toEqual(['s1']);
expect(plugin.getCustomCellStyle(1, 2)).toEqual({ bgColor: 'red' });

const beforeClearLength = plugin.customCellStyleArrangement.length;
plugin.arrangeCustomCellStyle({ col: 1, row: 2 }, null);
expect(plugin.getCustomCellStyleIds(1, 2)).toEqual([]);
expect(plugin.getCustomCellStyle(1, 2)).toBeUndefined();
expect(plugin.customCellStyleArrangement.length).toBe(beforeClearLength);
expect((plugin as any)._customCellStyleArrangementTombstoneCount).toBe(1);
});

test('does not delete wrong cell when index map is stale', () => {
const table = createMockTable();
const plugin = new CustomCellStylePlugin(
table as any,
[
{ id: 'a', style: { bgColor: 'red' } },
{ id: 'b', style: { bgColor: 'blue' } }
] as any,
[] as any
);

plugin.arrangeCustomCellStyle({ col: 1, row: 1 }, 'a');
plugin.arrangeCustomCellStyle({ col: 2, row: 2 }, 'b');

const arr = plugin.customCellStyleArrangement;
const tmp = arr[0];
arr[0] = arr[1];
arr[1] = tmp;

plugin.arrangeCustomCellStyle({ col: 1, row: 1 }, null);

expect(plugin.getCustomCellStyleIds(1, 1)).toEqual([]);
expect(plugin.getCustomCellStyleIds(2, 2)).toEqual(['b']);
});

test('compacts tombstones during massive clears and keeps index consistent', () => {
const table = createMockTable(10000, 2);
const plugin = new CustomCellStylePlugin(
table as any,
[
{
id: 's',
style: { bgColor: 'yellow' }
}
] as any,
[] as any
);

const total = 3000;
const removed = 2500;
for (let i = 0; i < total; i++) {
plugin.arrangeCustomCellStyle({ col: i, row: 0 }, 's');
}
for (let i = 0; i < removed; i++) {
plugin.arrangeCustomCellStyle({ col: i, row: 0 }, null);
}

expect((plugin as any)._customCellStyleArrangementTombstoneCount).toBeLessThan(2048);
expect((plugin as any)._customCellStyleArrangementIndex.size).toBe(total - removed);

for (let i = 0; i < removed; i++) {
expect(plugin.getCustomCellStyleIds(i, 0)).toEqual([]);
}
for (let i = removed; i < total; i++) {
expect(plugin.getCustomCellStyleIds(i, 0)).toEqual(['s']);
}
});

test('uses fast update when style only touches cellStyleKeys', () => {
const table = createMockTable();
const plugin = new CustomCellStylePlugin(
table as any,
[
{
id: 's',
style: { bgColor: 'red', color: '#000' }
}
] as any,
[] as any
);

plugin.arrangeCustomCellStyle({ col: 3, row: 4 }, 's');
const calls = (table as any).scenegraph.updateCellContent.mock.calls;
expect(calls.length).toBeGreaterThan(0);
const lastCall = calls[calls.length - 1];
expect(lastCall[0]).toBe(3);
expect(lastCall[1]).toBe(4);
expect(lastCall[2]).toBe(true);
});
});
2 changes: 1 addition & 1 deletion packages/vtable/src/PivotTable-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
registerTooltip,
registerAnimation
} from './components';
import { registerCustomCellStylePlugin } from './plugins/custom-cell-style'
import { registerCustomCellStylePlugin } from './plugins/custom-cell-style';
import {
registerChartCell,
registerCheckboxCell,
Expand Down
4 changes: 2 additions & 2 deletions packages/vtable/src/edit/edit-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class EditManager {
// 如果这里不加延时,会导致鼠标抬起pointerup的时候将table.getElement()元素设置成焦点,从而导致编辑器失去焦点(因为prepareEdit只是将editor的element设置pointerEvents为none)
if (editor && this.editingEditor !== editor) {
// 判断当前编辑器如果是当前需要准备的编辑器,则不进行准备编辑。这个是为了container-dom文件moveEditCellOnArrowKeys前后逻辑问题,前面有个selectCell会触发这个事件,后面有startEdit了,所以这个prepare就没必要了,触发的话反而有问题
editor.prepareEdit?.({
(editor as any).prepareEdit?.({
referencePosition,
container: this.table.getElement(),
table: this.table,
Expand Down Expand Up @@ -149,7 +149,7 @@ export class EditManager {
}
const editor = (this.table as ListTableAPI).getEditor(col, row);
if (editor) {
editElement && editor.setElement(editElement);
editElement && (editor as any).setElement?.(editElement);
// //自定义内容单元格不允许编辑
// if (this.table.getCustomRender(col, row) || this.table.getCustomLayout(col, row)) {
// console.warn("VTable Warn: cell has config custom render or layout, can't be edited");
Expand Down
2 changes: 1 addition & 1 deletion packages/vtable/src/event/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export class EventManager {
eventArgs.event.shiftKey && shiftMultiSelect,
(eventArgs.event.ctrlKey || eventArgs.event.metaKey) && ctrlMultiSelect,
false,
isSelectMoving ? false : (this.table.options.select?.makeSelectCellVisible ?? true)
isSelectMoving ? false : this.table.options.select?.makeSelectCellVisible ?? true
);

return true;
Expand Down
124 changes: 110 additions & 14 deletions packages/vtable/src/plugins/custom-cell-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export class CustomCellStylePlugin {
table: BaseTableAPI;
customCellStyle: CustomCellStyle[];
customCellStyleArrangement: CustomCellStyleArrangement[];
private _customCellStyleArrangementIndex: Map<string, number>;
private _customCellStyleArrangementTombstoneCount: number;

constructor(
table: BaseTableAPI,
Expand All @@ -31,6 +33,53 @@ export class CustomCellStylePlugin {
this.table = table;
this.customCellStyle = customCellStyle;
this.customCellStyleArrangement = customCellStyleArrangement;
this._customCellStyleArrangementIndex = new Map();
this._customCellStyleArrangementTombstoneCount = 0;
this._rebuildCustomCellStyleArrangementIndex();
}

private _getCustomCellStyleArrangementKey(cellPos: { col?: number; row?: number; range?: CellRange }) {
if (cellPos.range) {
const { start, end } = cellPos.range;
return `range:${start.col},${start.row},${end.col},${end.row}`;
}
if (cellPos.col === undefined || cellPos.row === undefined) {
return undefined;
}
return `cell:${cellPos.col},${cellPos.row}`;
}

private _rebuildCustomCellStyleArrangementIndex() {
this._customCellStyleArrangementIndex.clear();
this._customCellStyleArrangementTombstoneCount = 0;
for (let i = 0; i < this.customCellStyleArrangement.length; i++) {
if (!isValid((this.customCellStyleArrangement[i] as any).customStyleId)) {
this._customCellStyleArrangementTombstoneCount++;
continue;
}
const key = this._getCustomCellStyleArrangementKey(this.customCellStyleArrangement[i].cellPosition);
if (key) {
this._customCellStyleArrangementIndex.set(key, i);
}
}
}

private _compactCustomCellStyleArrangementIfNeeded() {
const length = this.customCellStyleArrangement.length;
if (this._customCellStyleArrangementTombstoneCount < 2048) {
return;
}
if (this._customCellStyleArrangementTombstoneCount * 4 < length) {
return;
}
const compacted = this.customCellStyleArrangement.filter(style => isValid((style as any).customStyleId));
if (compacted.length === this.customCellStyleArrangement.length) {
this._customCellStyleArrangementTombstoneCount = 0;
return;
}
this.customCellStyleArrangement.length = 0;
this.customCellStyleArrangement.push(...compacted);
this._rebuildCustomCellStyleArrangementIndex();
}

getCustomCellStyle(col: number, row: number) {
Expand All @@ -55,6 +104,9 @@ export class CustomCellStylePlugin {
}
});

if (!styles.length) {
return undefined;
}
return merge({}, ...styles);
// const styleOption = this.getCustomCellStyleOption(customStyleId);
// return styleOption?.style;
Expand All @@ -71,6 +123,9 @@ export class CustomCellStylePlugin {
for (let r = range.start.row; r <= range.end.row; r++) {
// eslint-disable-next-line no-loop-func
this.customCellStyleArrangement.forEach(style => {
if (!isValid(style.customStyleId)) {
return;
}
if (style.cellPosition.range) {
if (
style.cellPosition.range.start.col <= c &&
Expand All @@ -79,11 +134,11 @@ export class CustomCellStylePlugin {
style.cellPosition.range.end.row >= r
) {
// customStyleId = style.customStyleId;
customStyleIds.push(style.customStyleId);
customStyleIds.push(style.customStyleId as string);
}
} else if (style.cellPosition.col === c && style.cellPosition.row === r) {
// customStyleId = style.customStyleId;
customStyleIds.push(style.customStyleId);
customStyleIds.push(style.customStyleId as string);
}
});
}
Expand Down Expand Up @@ -147,17 +202,41 @@ export class CustomCellStylePlugin {
customStyleId: string | undefined | null,
forceFastUpdate?: boolean
) {
const index = this.customCellStyleArrangement.findIndex(style => {
if (style.cellPosition.range && cellPos.range) {
return (
style.cellPosition.range.start.col === cellPos.range.start.col &&
style.cellPosition.range.start.row === cellPos.range.start.row &&
style.cellPosition.range.end.col === cellPos.range.end.col &&
style.cellPosition.range.end.row === cellPos.range.end.row
);
const inputKey = this._getCustomCellStyleArrangementKey(cellPos);
let index = inputKey ? this._customCellStyleArrangementIndex.get(inputKey) ?? -1 : -1;
if (inputKey && index !== -1) {
const item = this.customCellStyleArrangement[index];
const itemKey = item ? this._getCustomCellStyleArrangementKey(item.cellPosition) : undefined;
if (!item || !isValid((item as any).customStyleId) || itemKey !== inputKey) {
index = this.customCellStyleArrangement.findIndex(style => {
if (!isValid((style as any).customStyleId)) {
return false;
}
return this._getCustomCellStyleArrangementKey(style.cellPosition) === inputKey;
});
if (index !== -1) {
this._customCellStyleArrangementIndex.set(inputKey, index);
} else {
this._customCellStyleArrangementIndex.delete(inputKey);
}
}
return style.cellPosition.col === cellPos.col && style.cellPosition.row === cellPos.row;
});
}
if (index === -1 && !inputKey) {
index = this.customCellStyleArrangement.findIndex(style => {
if (!isValid((style as any).customStyleId)) {
return false;
}
if (style.cellPosition.range && cellPos.range) {
return (
style.cellPosition.range.start.col === cellPos.range.start.col &&
style.cellPosition.range.start.row === cellPos.range.start.row &&
style.cellPosition.range.end.col === cellPos.range.end.col &&
style.cellPosition.range.end.row === cellPos.range.end.row
);
}
return style.cellPosition.col === cellPos.col && style.cellPosition.row === cellPos.row;
});
}

if (index === -1 && !customStyleId) {
// do nothing
Expand All @@ -172,6 +251,13 @@ export class CustomCellStylePlugin {
},
customStyleId: customStyleId
});
const pushedIndex = this.customCellStyleArrangement.length - 1;
const pushedKey = this._getCustomCellStyleArrangementKey(
this.customCellStyleArrangement[pushedIndex].cellPosition
);
if (pushedKey) {
this._customCellStyleArrangementIndex.set(pushedKey, pushedIndex);
}
} else if (this.customCellStyleArrangement[index].customStyleId === customStyleId) {
// same style
return;
Expand All @@ -180,10 +266,18 @@ export class CustomCellStylePlugin {
this.customCellStyleArrangement[index].customStyleId = customStyleId;
} else {
// delete useless style
this.customCellStyleArrangement.splice(index, 1);
const existedKey = this._getCustomCellStyleArrangementKey(this.customCellStyleArrangement[index].cellPosition);
if (isValid((this.customCellStyleArrangement[index] as any).customStyleId)) {
this._customCellStyleArrangementTombstoneCount++;
}
(this.customCellStyleArrangement[index] as any).customStyleId = null;
if (existedKey) {
this._customCellStyleArrangementIndex.delete(existedKey);
}
this._compactCustomCellStyleArrangementIfNeeded();
}

const style = this.getCustomCellStyleOption(customStyleId)?.style;
const style = customStyleId ? this.getCustomCellStyleOption(customStyleId)?.style : undefined;
// let forceFastUpdate;
if (style) {
forceFastUpdate = true;
Expand Down Expand Up @@ -226,6 +320,8 @@ export class CustomCellStylePlugin {
updateCustomCell(customCellStyle: CustomCellStyle[], customCellStyleArrangement: CustomCellStyleArrangement[]) {
this.customCellStyle.length = 0;
this.customCellStyleArrangement.length = 0;
this._customCellStyleArrangementIndex.clear();
this._customCellStyleArrangementTombstoneCount = 0;
customCellStyle.forEach((cellStyle: CustomCellStyle) => {
this.registerCustomCellStyle(cellStyle.id, cellStyle.style);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function createProgressBarCell(
} else {
height = table.getRowHeight(row);
}

// 检查是否有主从表插件,如果有则使用原始高度
if ((table as any).pluginManager) {
const masterDetailPlugin = (table as any).pluginManager.getPluginByName('Master Detail Plugin');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function updateComponent(
// const rowsHeight = table.getRowsHeight(cellRange.start.row, endRow);
const colsWidth = table.getColsWidth(computeRectCellRangeStartCol, computeRectCellRangeEndCol);
const rowsHeight = table.getRowsHeight(computeRectCellRangeStartRow, computeRectCellRangeEndRow);

const firstCellBound = scene.highPerformanceGetCell(
computeRectCellRangeStartCol,
computeRectCellRangeStartRow
Expand Down
Loading