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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@visactor/vtable",
"comment": "fix: prefer event clipboard data for copy and cut, fall back to it for paste when the async Clipboard API is unavailable, preserve cut source cells until paste succeeds, and normalize pasted HTML cell content (GitHub #5274)",
"type": "patch"
}
],
"packageName": "@visactor/vtable",
"email": "892739385@qq.com"
}
6 changes: 4 additions & 2 deletions packages/vtable/src/ListTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1846,7 +1846,8 @@ export class ListTable extends BaseTable implements ListTableAPI {
values: (string | number)[][],
workOnEditableCell = false,
triggerEvent = true,
noTriggerChangeCellValuesEvent?: boolean
noTriggerChangeCellValuesEvent?: boolean,
shouldCancel?: () => boolean
) {
return listTableChangeCellValues(
startCol,
Expand All @@ -1855,7 +1856,8 @@ export class ListTable extends BaseTable implements ListTableAPI {
workOnEditableCell,
triggerEvent,
this,
noTriggerChangeCellValuesEvent
noTriggerChangeCellValuesEvent,
shouldCancel
);
}

Expand Down
23 changes: 20 additions & 3 deletions packages/vtable/src/PivotTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2047,7 +2047,19 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
* @param values 多个单元格的数据数组
* @param workOnEditableCell 是否仅更改可编辑单元格
*/
changeCellValues(startCol: number, startRow: number, values: string[][], workOnEditableCell = false) {
changeCellValues(
startCol: number,
startRow: number,
values: (string | number)[][],
workOnEditableCell = false,
triggerEvent = true,
_noTriggerChangeCellValuesEvent?: boolean,
shouldCancel?: () => boolean
): boolean[][] {
const changedCellResults: boolean[][] = [];
if (shouldCancel?.()) {
return changedCellResults;
}
let pasteColEnd = startCol;
let pasteRowEnd = startRow;
// const rowCount = values.length;
Expand Down Expand Up @@ -2078,6 +2090,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
if (startRow + i > this.rowCount - 1) {
break;
}
changedCellResults[i] = [];
pasteRowEnd = startRow + i;
const rowValues = values[i];
let thisRowPasteColEnd = startCol;
Expand All @@ -2095,12 +2108,13 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
let newValue: string | number = value;
const oldValue = oldValues[i][j];
const rawValue = beforeChangeValues[i][j];
if (typeof rawValue === 'number' && isAllDigits(value)) {
if (typeof rawValue === 'number' && typeof value === 'string' && isAllDigits(value)) {
newValue = parseFloat(value);
}
changedCellResults[i][j] = true;
this._changeCellValueToDataSet(startCol + j, startRow + i, oldValue, newValue);
const changedValue = this.getCellOriginValue(startCol + j, startRow + i);
if (changedValue !== oldValue) {
if (changedValue !== oldValue && triggerEvent) {
this.fireListeners(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, {
col: startCol + j,
row: startRow + i,
Expand All @@ -2109,6 +2123,8 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
changedValue
});
}
} else {
changedCellResults[i][j] = false;
}
}
pasteColEnd = Math.max(pasteColEnd, thisRowPasteColEnd);
Expand Down Expand Up @@ -2167,6 +2183,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
}

this.scenegraph.updateNextFrame();
return changedCellResults;
}

private _changeCellValueToDataSet(col: number, row: number, oldValue: string | number, newValue: string | number) {
Expand Down
120 changes: 115 additions & 5 deletions packages/vtable/src/core/record-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,53 @@ function refreshCustomMergeCellGroups(table: ListTable) {
}
}

type ChangeCellTargetSnapshot = {
col: number;
row: number;
isHeader: boolean;
recordIndex?: number | number[];
field?: any;
};

function isSameRecordIndex(
sourceRecordIndex: number | number[] | undefined,
currentRecordIndex: number | number[] | undefined
): boolean {
if (Array.isArray(sourceRecordIndex) || Array.isArray(currentRecordIndex)) {
return (
Array.isArray(sourceRecordIndex) &&
Array.isArray(currentRecordIndex) &&
sourceRecordIndex.length === currentRecordIndex.length &&
sourceRecordIndex.every((value, index) => value === currentRecordIndex[index])
);
}
return sourceRecordIndex === currentRecordIndex;
}

function isTargetCellSnapshotCurrent(table: ListTable, snapshot: ChangeCellTargetSnapshot): boolean {
if (table.isHeader(snapshot.col, snapshot.row) !== snapshot.isHeader) {
return false;
}
if (snapshot.isHeader) {
return true;
}
const recordShowIndex = table.getRecordShowIndexByCell(snapshot.col, snapshot.row);
const recordIndex = recordShowIndex >= 0 ? table.dataSource.getIndexKey(recordShowIndex) : undefined;
const { field } = table.internalProps.layoutMap.getBody(snapshot.col, snapshot.row);
return isSameRecordIndex(snapshot.recordIndex, recordIndex) && snapshot.field === field;
}

function areTargetCellSnapshotsCurrent(table: ListTable, snapshots: ChangeCellTargetSnapshot[][]): boolean {
for (let i = 0; i < snapshots.length; i++) {
for (let j = 0; j < snapshots[i].length; j++) {
if (!isTargetCellSnapshotCurrent(table, snapshots[i][j])) {
return false;
}
}
}
return true;
}

/**
* 更改单元格数据 会触发change_cell_value事件
* @param col
Expand Down Expand Up @@ -164,7 +211,8 @@ export async function listTableChangeCellValues(
workOnEditableCell: boolean,
triggerEvent: boolean,
table: ListTable,
noTriggerChangeCellValuesEvent?: boolean
noTriggerChangeCellValuesEvent?: boolean,
shouldCancel?: () => boolean
): Promise<boolean[][]> {
const changedCellResults: boolean[][] = [];
let pasteColEnd = startCol;
Expand All @@ -173,6 +221,7 @@ export async function listTableChangeCellValues(
//#region 提前组织好未更改前的数据
const beforeChangeValues: (string | number)[][] = [];
const oldValues: (string | number)[][] = [];
const targetSnapshots: ChangeCellTargetSnapshot[][] = [];
let cellUpdateType: 'normal' | 'sort' | 'group';

for (let i = 0; i < values.length; i++) {
Expand All @@ -182,17 +231,26 @@ export async function listTableChangeCellValues(
const rowValues = values[i];
const rawRowValues: (string | number)[] = [];
const oldRowValues: (string | number)[] = [];
const rowTargetSnapshots: ChangeCellTargetSnapshot[] = [];
beforeChangeValues.push(rawRowValues);
oldValues.push(oldRowValues);
targetSnapshots.push(rowTargetSnapshots);
for (let j = 0; j < rowValues.length; j++) {
if (startCol + j > table.colCount - 1) {
break;
}
const col = startCol + j;
const row = startRow + i;
cellUpdateType = getCellUpdateType(startCol + j, startRow + i, table, cellUpdateType);
const beforeChangeValue = table.getCellRawValue(startCol + j, startRow + i);
const beforeChangeValue = table.getCellRawValue(col, row);
rawRowValues.push(beforeChangeValue);
const oldValue = table.getCellOriginValue(startCol + j, startRow + i);
const oldValue = table.getCellOriginValue(col, row);
oldRowValues.push(oldValue);
const isHeader = table.isHeader(col, row);
const recordShowIndex = table.getRecordShowIndexByCell(col, row);
const recordIndex = recordShowIndex >= 0 ? table.dataSource.getIndexKey(recordShowIndex) : undefined;
const { field } = table.internalProps.layoutMap.getBody(col, row);
rowTargetSnapshots.push({ col, row, isHeader, recordIndex, field });
}
}

Expand All @@ -206,6 +264,43 @@ export async function listTableChangeCellValues(
changedValue: string | number;
}[] = [];

const preValidatedCellResults: boolean[][] | null = shouldCancel && workOnEditableCell ? [] : null;
if (preValidatedCellResults) {
for (let i = 0; i < values.length; i++) {
if (shouldCancel?.()) {
return changedCellResults;
}
if (startRow + i > table.rowCount - 1) {
break;
}
preValidatedCellResults[i] = [];
const rowValues = values[i];
for (let j = 0; j < rowValues.length; j++) {
if (startCol + j > table.colCount - 1) {
break;
}
let isCanChange = false;
if (table.isHasEditorDefine(startCol + j, startRow + i)) {
const editor = table.getEditor(startCol + j, startRow + i);
const oldValue = oldValues[i][j];
const value = rowValues[j];
const maybePromiseOrValue =
editor?.validateValue?.(value, oldValue, { col: startCol + j, row: startRow + i }, table) ?? true;
const validateResult = isPromise(maybePromiseOrValue) ? await maybePromiseOrValue : maybePromiseOrValue;
if (shouldCancel?.()) {
return changedCellResults;
}
isCanChange =
validateResult === true || validateResult === 'validate-exit' || validateResult === 'validate-not-exit';
}
preValidatedCellResults[i][j] = isCanChange;
}
}
}
if (shouldCancel?.() || !areTargetCellSnapshotsCurrent(table, targetSnapshots)) {
return changedCellResults;
}

//#endregion
for (let i = 0; i < values.length; i++) {
if (startRow + i > table.rowCount - 1) {
Expand All @@ -221,7 +316,9 @@ export async function listTableChangeCellValues(
}
thisRowPasteColEnd = startCol + j;
let isCanChange = false;
if (workOnEditableCell === false) {
if (preValidatedCellResults) {
isCanChange = preValidatedCellResults[i]?.[j] === true;
} else if (workOnEditableCell === false) {
isCanChange = true;
} else {
if (table.isHasEditorDefine(startCol + j, startRow + i)) {
Expand All @@ -232,6 +329,9 @@ export async function listTableChangeCellValues(
editor?.validateValue?.(value, oldValue, { col: startCol + j, row: startRow + i }, table) ?? true;
if (isPromise(maybePromiseOrValue)) {
const validateResult = await maybePromiseOrValue;
if (shouldCancel?.()) {
return changedCellResults;
}
isCanChange =
validateResult === true || validateResult === 'validate-exit' || validateResult === 'validate-not-exit';
} else {
Expand All @@ -256,7 +356,17 @@ export async function listTableChangeCellValues(
if (table.isHeader(startCol + j, startRow + i)) {
table.internalProps.layoutMap.updateColumnTitle(startCol + j, startRow + i, value as string);
} else {
table.dataSource.changeFieldValue(value, recordShowIndex, field, startCol + j, startRow + i, table);
const changeResult = table.dataSource.changeFieldValue(
value,
recordShowIndex,
field,
startCol + j,
startRow + i,
table
);
if (isPromise(changeResult)) {
await changeResult;
}
}
const changedValue = table.getCellOriginValue(startCol + j, startRow + i);
if (oldValue !== changedValue && triggerEvent) {
Expand Down
17 changes: 9 additions & 8 deletions packages/vtable/src/data/DataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ export class DataSource extends EventTarget implements DataSourceAPI {
col?: number,
row?: number,
table?: BaseTableAPI
): FieldData {
): FieldData | Promise<FieldData> {
if (field === null) {
return undefined;
}
Expand All @@ -752,20 +752,21 @@ export class DataSource extends EventTarget implements DataSourceAPI {
formatValue = parseFloat(value);
}
if (isPromise(record)) {
record
return record
.then(record => {
record[field as string | number] = formatValue;
return formatValue;
})
.catch((err: Error) => {
console.error('VTable Error:', err);
return undefined;
});
}
if (record) {
record[field] = formatValue;
} else {
if (record) {
record[field] = formatValue;
} else {
this.records[dataIndex as number] = this.addRecordRule === 'Array' ? [] : {};
this.records[dataIndex as number][field] = formatValue;
}
this.records[dataIndex as number] = this.addRecordRule === 'Array' ? [] : {};
this.records[dataIndex as number][field] = formatValue;
}
}
}
Expand Down
Loading
Loading