fix(grid): preserve row selection during infinite-scroll append

This commit is contained in:
zipg
2026-08-05 09:47:26 +08:00
committed by GitHub
parent ffaede31b9
commit 509a1530f6
3 changed files with 29 additions and 3 deletions
@@ -141,7 +141,7 @@ import { isCancelSearchShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowSho
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGrid/dataGridScrollGutter";
import { canFetchNextDataGridSegment, canGoNextDataGridPage, dataGridTotalRowCountLabelKey, hasCompleteLocalDataGridResult, resolveDataGridPaginationTotal, type DataGridInexactTotalRowCountMode } from "@/lib/dataGrid/dataGridPagination";
import { dataGridCountQueryOptions } from "@/lib/dataGrid/dataGridQueryOptions";
import { dataGridBottomScrollTop, dataGridScrollPosition, isDataGridAtScrollBottom, isDataGridNearScrollBottom, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGrid/dataGridInfiniteScroll";
import { dataGridBottomScrollTop, dataGridScrollPosition, isDataGridAtScrollBottom, isDataGridNearScrollBottom, isDataGridPrefixAppend, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGrid/dataGridInfiniteScroll";
import { CANVAS_DATA_GRID_ROW_HEIGHT, canvasDataGridActionOverlayWidth, canvasDataGridActionReservedWidth, dataGridSearchMatchKey, drawCanvasDataGrid, type CanvasDevicePixelSize } from "@/lib/dataGrid/canvasDataGridRenderer";
import { DATA_GRID_DARK_STRIPED_ROW_BG, DATA_GRID_LIGHT_STRIPED_ROW_BG, dataGridActiveRowBackground } from "@/lib/dataGrid/dataGridPaintTheme";
import { createRowLowerTextCache } from "@/lib/dataGrid/dataGridRowLowerText";
@@ -7112,9 +7112,10 @@ watch(isTransposeMode, (active) => {
watch(
() => props.result,
() => {
(result, previousResult) => {
const shouldPreserveTranspose = preserveTransposeOnNextResult.value;
preserveTransposeOnNextResult.value = false;
if (isDataGridPrefixAppend(previousResult, result)) return;
if (getResetScrollAfterResult()) {
clearResetScrollAfterResult();
resetGridVerticalScroll();
@@ -9,6 +9,11 @@ export interface DataGridScrollMetrics {
clientHeight: number;
}
export interface DataGridAppendResult {
rows: readonly unknown[];
appended_from_row_count?: number;
}
export function dataGridScrollPosition(top: number, left: number): DataGridScrollPosition {
return {
top: Math.max(0, top),
@@ -30,6 +35,11 @@ export function isDataGridNearScrollBottom(metrics: DataGridScrollMetrics, thres
return metrics.scrollHeight - metrics.scrollTop - metrics.clientHeight < threshold;
}
export function isDataGridPrefixAppend(previous: DataGridAppendResult | undefined, next: DataGridAppendResult): boolean {
if (!previous || next.appended_from_row_count !== previous.rows.length || next.rows.length < previous.rows.length) return false;
return previous.rows.every((row, index) => row === next.rows[index]);
}
export function isDataGridAtScrollBottom(metrics: DataGridScrollMetrics, tolerance = 1): boolean {
const maxScrollTop = Math.max(0, metrics.scrollHeight - metrics.clientHeight);
return maxScrollTop - metrics.scrollTop <= tolerance;
+16 -1
View File
@@ -1,7 +1,7 @@
import { strict as assert } from "node:assert";
import { readFileSync } from "node:fs";
import { test } from "vitest";
import { dataGridScrollPosition, isDataGridNearScrollBottom, shouldCheckInfiniteScrollAfterScroll } from "../../apps/desktop/src/lib/dataGrid/dataGridInfiniteScroll.ts";
import { dataGridScrollPosition, isDataGridNearScrollBottom, isDataGridPrefixAppend, shouldCheckInfiniteScrollAfterScroll } from "../../apps/desktop/src/lib/dataGrid/dataGridInfiniteScroll.ts";
test("horizontal-only scroll does not check infinite scroll", () => {
assert.equal(shouldCheckInfiniteScrollAfterScroll(dataGridScrollPosition(240, 0), dataGridScrollPosition(240, 180)), false);
@@ -25,6 +25,21 @@ test("near-bottom check matches the grid threshold", () => {
assert.equal(isDataGridNearScrollBottom({ scrollTop: 800, scrollHeight: 1000, clientHeight: 100 }), false);
});
test("prefix-only append preserves the existing result identity", () => {
const first = [1, "Ada"];
const second = [2, "Linus"];
const previous = { rows: [first, second] };
assert.equal(isDataGridPrefixAppend(previous, { rows: [first, second, [3, "Grace"]], appended_from_row_count: 2 }), true);
});
test("append marker does not preserve state when an existing row was replaced", () => {
const first = [1, "Ada"];
const second = [2, "Linus"];
const previous = { rows: [first, second] };
assert.equal(isDataGridPrefixAppend(previous, { rows: [first, [...second], [3, "Grace"]], appended_from_row_count: 2 }), false);
assert.equal(isDataGridPrefixAppend(previous, { rows: [first, second, [3, "Grace"]] }), false);
});
test("infinite scroll requests only the next bounded segment", () => {
const source = readFileSync("apps/desktop/src/components/grid/DataGrid.vue", "utf8");
assert.match(source, /function infiniteScrollNextPage\(\) \{[\s\S]*?if \(!canFetchNextInfiniteScrollSegment\.value\) \{[\s\S]*?infiniteScrollAllLoaded = true;[\s\S]*?return;[\s\S]*?\}/);