mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 04:28:51 +00:00
fix(grid): auto expand new rows for multi row paste
This commit is contained in:
@@ -8729,14 +8729,15 @@ function batchAppendPasteError(reason: string): string {
|
||||
return t(messages[reason] ?? "grid.batchAppendPasteInvalidTarget");
|
||||
}
|
||||
|
||||
function blankCellBatchAppendPasteTarget(pastedRows: readonly (readonly (string | null)[])[]): { rowId: number; columnIndexes: number[] } | null {
|
||||
function blankSelectionBatchAppendPasteTarget(pastedRows: readonly (readonly (string | null)[])[]): { rowId: number; columnIndexes: number[] } | null {
|
||||
if (pastedRows.length <= 1) return null;
|
||||
const range = selectedRange.value;
|
||||
if (!range || range.startRow !== range.endRow || range.startCol !== range.endCol) return null;
|
||||
const item = displayItemAt(range.startRow);
|
||||
if ((!item?.isNew && !item?.isDraft) || item.isDeleted || item.data.some((value) => value !== null && (typeof value !== "string" || value.trim() !== ""))) return null;
|
||||
if (!range || (range.startRow === range.endRow && range.startCol !== range.endCol)) return null;
|
||||
const selectedItems = Array.from({ length: range.endRow - range.startRow + 1 }, (_, offset) => displayItemAt(range.startRow + offset));
|
||||
if (selectedItems.some((item) => (!item?.isNew && !item?.isDraft) || item.isDeleted || item.data.some((value) => value !== null && (typeof value !== "string" || value.trim() !== "")))) return null;
|
||||
const item = selectedItems[0];
|
||||
const columnIndex = visibleColumnIndexes.value[range.startCol];
|
||||
if (columnIndex === undefined || !canEditCellItem(item, columnIndex)) return null;
|
||||
if (!item || columnIndex === undefined || !canEditCellItem(item, columnIndex)) return null;
|
||||
return { rowId: item.id, columnIndexes: visibleColumnIndexes.value.slice(range.startCol) };
|
||||
}
|
||||
|
||||
@@ -8760,7 +8761,7 @@ function pasteTextIntoGrid(text: string): boolean {
|
||||
if (targetRowId !== null) {
|
||||
return appendParsedRowsToBlankTarget(targetRowId, rows, visibleColumnIndexes.value);
|
||||
}
|
||||
const cellTarget = blankCellBatchAppendPasteTarget(rows);
|
||||
const cellTarget = blankSelectionBatchAppendPasteTarget(rows);
|
||||
if (cellTarget) return appendParsedRowsToBlankTarget(cellTarget.rowId, rows, cellTarget.columnIndexes);
|
||||
return pasteRowsIntoSelection(rows);
|
||||
}
|
||||
|
||||
+110
-7
@@ -180,6 +180,90 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
describe("DataGrid multi-row paste from a blank cell", () => {
|
||||
it("expands beyond pre-added blank rows when pasted rows exceed them", async () => {
|
||||
const { host } = mountGrid();
|
||||
await settle();
|
||||
for (let index = 0; index < 5; index++) await addBlankRow(host);
|
||||
|
||||
const blankRows = pendingRows(host);
|
||||
expect(blankRows).toHaveLength(5);
|
||||
await selectCell(visibleCells(blankRows[0]!)[0]!);
|
||||
await selectCell(visibleCells(blankRows[4]!)[0]!, { shiftKey: true });
|
||||
await paste(host, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
|
||||
|
||||
const rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(10);
|
||||
expect(rows.map((row) => visibleCellTexts(row)[0])).toEqual(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]);
|
||||
expect(visibleCellTexts(rows.at(-1)!)[0]).toBe("10");
|
||||
});
|
||||
|
||||
it("reuses exactly enough pre-added blank rows without adding an extra row", async () => {
|
||||
const { host } = mountGrid();
|
||||
await settle();
|
||||
for (let index = 0; index < 10; index++) await addBlankRow(host);
|
||||
|
||||
const blankRows = pendingRows(host);
|
||||
expect(blankRows).toHaveLength(10);
|
||||
await selectCell(visibleCells(blankRows[0]!)[0]!);
|
||||
await selectCell(visibleCells(blankRows[9]!)[0]!, { shiftKey: true });
|
||||
await paste(host, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
|
||||
|
||||
const rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(10);
|
||||
expect(rows.map((row) => visibleCellTexts(row)[0])).toEqual(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]);
|
||||
expect(visibleCellTexts(rows.at(-1)!)[0]).toBe("10");
|
||||
});
|
||||
|
||||
it("keeps extra pre-added blank rows when the clipboard has fewer rows", async () => {
|
||||
const { host } = mountGrid();
|
||||
await settle();
|
||||
for (let index = 0; index < 10; index++) await addBlankRow(host);
|
||||
|
||||
const blankRows = pendingRows(host);
|
||||
await selectCell(visibleCells(blankRows[0]!)[0]!);
|
||||
await selectCell(visibleCells(blankRows[9]!)[0]!, { shiftKey: true });
|
||||
await paste(host, "1\n2\n3\n4\n5");
|
||||
|
||||
const rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(10);
|
||||
expect(visibleCellTexts(rows[0]!)[0]).toBe("1");
|
||||
expect(visibleCellTexts(rows[4]!)[0]).toBe("5");
|
||||
expect(visibleCellTexts(rows[5]!)[0]).toBe("NULL");
|
||||
expect(visibleCellTexts(rows[9]!)[0]).toBe("NULL");
|
||||
});
|
||||
|
||||
it("keeps single-cell auto expansion with pre-added blank rows", async () => {
|
||||
const { host } = mountGrid();
|
||||
await settle();
|
||||
for (let index = 0; index < 5; index++) await addBlankRow(host);
|
||||
|
||||
const blankRows = pendingRows(host);
|
||||
await selectCell(visibleCells(blankRows[0]!)[0]!);
|
||||
await paste(host, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
|
||||
|
||||
const rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(10);
|
||||
expect(rows.map((row) => visibleCellTexts(row)[0])).toEqual(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]);
|
||||
expect(visibleCellTexts(rows.at(-1)!)[0]).toBe("10");
|
||||
});
|
||||
|
||||
it("preserves visible column mapping for a multi-row selection", async () => {
|
||||
const { host } = mountGrid({ hideNullColumns: true });
|
||||
await settle();
|
||||
for (let index = 0; index < 5; index++) await addBlankRow(host);
|
||||
|
||||
const blankRows = pendingRows(host);
|
||||
await selectCell(visibleCells(blankRows[0]!)[1]!);
|
||||
await selectCell(visibleCells(blankRows[4]!)[1]!, { shiftKey: true });
|
||||
const clipboardRows = Array.from({ length: 10 }, (_, index) => `value-${index + 1}\tnote-${index + 1}`).join("\n");
|
||||
await paste(host, clipboardRows);
|
||||
|
||||
const rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(10);
|
||||
expect(visibleCellTexts(rows[0]!)).toEqual(["NULL", "value-1", "note-1"]);
|
||||
expect(visibleCellTexts(rows.at(-1)!)).toEqual(["NULL", "value-10", "note-10"]);
|
||||
});
|
||||
|
||||
it("starts at the selected visible column, skips hidden columns, and appends rows", async () => {
|
||||
const { host } = mountGrid({ hideNullColumns: true });
|
||||
await settle();
|
||||
@@ -218,17 +302,18 @@ describe("DataGrid multi-row paste from a blank cell", () => {
|
||||
it("keeps row-number paste priority and starts from the first visible column", async () => {
|
||||
const { host } = mountGrid({ hideNullColumns: true });
|
||||
await settle();
|
||||
await addBlankRow(host);
|
||||
for (let index = 0; index < 5; index++) await addBlankRow(host);
|
||||
|
||||
const [blankRow] = pendingRows(host);
|
||||
expect(blankRow).toBeDefined();
|
||||
await selectRowNumber(blankRow!);
|
||||
await paste(host, "10\t20\n30\t40");
|
||||
const blankRows = pendingRows(host);
|
||||
expect(blankRows).toHaveLength(5);
|
||||
await selectRowNumber(blankRows[0]!);
|
||||
const clipboardRows = Array.from({ length: 10 }, (_, index) => `${index + 10}\t${index + 20}`).join("\n");
|
||||
await paste(host, clipboardRows);
|
||||
|
||||
const rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(2);
|
||||
expect(rows).toHaveLength(10);
|
||||
expect(visibleCellTexts(rows[0]!)).toEqual(["10", "20", "NULL"]);
|
||||
expect(visibleCellTexts(rows[1]!)).toEqual(["30", "40", "NULL"]);
|
||||
expect(visibleCellTexts(rows.at(-1)!)).toEqual(["19", "29", "NULL"]);
|
||||
});
|
||||
|
||||
it("keeps nonblank new rows on the ordinary bounded paste path", async () => {
|
||||
@@ -248,6 +333,24 @@ describe("DataGrid multi-row paste from a blank cell", () => {
|
||||
expect(visibleCellTexts(rows[0]!)[2]).toBe("first");
|
||||
});
|
||||
|
||||
it("does not expand a multi-row selection containing a nonblank new row", async () => {
|
||||
const { host } = mountGrid();
|
||||
await settle();
|
||||
for (let index = 0; index < 5; index++) await addBlankRow(host);
|
||||
|
||||
let rows = pendingRows(host);
|
||||
await selectCell(visibleCells(rows[0]!)[2]!);
|
||||
await paste(host, "occupied");
|
||||
rows = pendingRows(host);
|
||||
await selectCell(visibleCells(rows[0]!)[2]!);
|
||||
await selectCell(visibleCells(rows[4]!)[2]!, { shiftKey: true });
|
||||
await paste(host, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10");
|
||||
|
||||
rows = pendingRows(host);
|
||||
expect(rows).toHaveLength(5);
|
||||
expect(visibleCellTexts(rows.at(-1)!)[2]).toBe("5");
|
||||
});
|
||||
|
||||
it("keeps existing rows on the ordinary bounded paste path", async () => {
|
||||
const { host } = mountGrid();
|
||||
await settle();
|
||||
|
||||
Reference in New Issue
Block a user