mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
fix(grid): prevent horizontal scroll from loading more rows
This commit is contained in:
@@ -119,6 +119,7 @@ import { isEnumColumn, enumValuesForColumn } from "@/lib/dataGridEnumEditor";
|
||||
import { isCancelSearchShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowShortcut, isFocusSearchShortcut, isModRShortcut, isToggleTransposeShortcut } from "@/lib/keyboardShortcuts";
|
||||
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGridScrollGutter";
|
||||
import { canGoNextDataGridPage } from "@/lib/dataGridPagination";
|
||||
import { dataGridScrollPosition, isDataGridNearScrollBottom, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGridInfiniteScroll";
|
||||
import { CANVAS_DATA_GRID_ROW_HEIGHT, drawCanvasDataGrid } from "@/lib/canvasDataGridRenderer";
|
||||
import { dataGridSaveActionMode, dataGridSaveToolbarState } from "@/lib/dataGridSaveUi";
|
||||
import { EDITOR_FONT_FAMILY_CSS_VAR } from "@/lib/editorThemes";
|
||||
@@ -2058,6 +2059,7 @@ function refreshGridScrollerMetrics() {
|
||||
if (!scrollerEl) return;
|
||||
updateGridScrollbarGutter(scrollerEl);
|
||||
updateGridHorizontalViewport(scrollerEl);
|
||||
rememberInfiniteScrollPosition(scrollerEl);
|
||||
if (headerRef.value) {
|
||||
headerRef.value.scrollLeft = scrollerEl.scrollLeft;
|
||||
}
|
||||
@@ -2288,6 +2290,7 @@ function columnContentOffsetLeft(visibleColIdx: number): number {
|
||||
|
||||
let scrollingTimer = 0;
|
||||
const isScrolling = ref(false);
|
||||
let infiniteScrollPositions = new WeakMap<HTMLElement, DataGridScrollPosition>();
|
||||
|
||||
function markGridScrolling() {
|
||||
if (!isScrolling.value) isScrolling.value = true;
|
||||
@@ -2297,12 +2300,25 @@ function markGridScrolling() {
|
||||
}, 120);
|
||||
}
|
||||
|
||||
function rememberInfiniteScrollPosition(scroller: HTMLElement) {
|
||||
infiniteScrollPositions.set(scroller, dataGridScrollPosition(scroller.scrollTop, scroller.scrollLeft));
|
||||
}
|
||||
|
||||
function maybeCheckInfiniteScroll(scroller: HTMLElement) {
|
||||
const current = dataGridScrollPosition(scroller.scrollTop, scroller.scrollLeft);
|
||||
const previous = infiniteScrollPositions.get(scroller);
|
||||
infiniteScrollPositions.set(scroller, current);
|
||||
if (shouldCheckInfiniteScrollAfterScroll(previous, current)) {
|
||||
checkInfiniteScroll(scroller);
|
||||
}
|
||||
}
|
||||
|
||||
function onScrollerScroll(e: Event) {
|
||||
syncHeaderScroll(e);
|
||||
const target = e.target;
|
||||
if (target instanceof HTMLElement) {
|
||||
recordScrollPosition({ top: target.scrollTop, left: target.scrollLeft });
|
||||
checkInfiniteScroll(target);
|
||||
maybeCheckInfiniteScroll(target);
|
||||
}
|
||||
markGridScrolling();
|
||||
}
|
||||
@@ -2569,11 +2585,8 @@ function checkInfiniteScroll(scroller: HTMLElement) {
|
||||
infiniteScrollCheckScheduled = true;
|
||||
requestAnimationFrame(() => {
|
||||
infiniteScrollCheckScheduled = false;
|
||||
const { scrollTop, scrollHeight, clientHeight } = scroller;
|
||||
const threshold = 100;
|
||||
const distanceToBottom = scrollHeight - scrollTop - clientHeight;
|
||||
// Only trigger when near bottom AND page has changed since last trigger
|
||||
if (distanceToBottom < threshold && currentPage.value !== lastInfiniteScrollPage) {
|
||||
if (isDataGridNearScrollBottom(scroller) && currentPage.value !== lastInfiniteScrollPage) {
|
||||
lastInfiniteScrollPage = currentPage.value;
|
||||
infiniteScrollNextPage();
|
||||
}
|
||||
@@ -2587,6 +2600,7 @@ function changePageSize(size: number) {
|
||||
currentPage.value = 1;
|
||||
lastInfiniteScrollPage = 0;
|
||||
infiniteScrollAllLoaded = false;
|
||||
infiniteScrollPositions = new WeakMap();
|
||||
resetGridVerticalScroll(true);
|
||||
emit("paginate", 0, normalizedSize, currentWhereInput(), currentOrderBy());
|
||||
}
|
||||
@@ -2895,6 +2909,7 @@ function resetInfiniteScrollState() {
|
||||
infiniteScrollAllLoaded = false;
|
||||
isInfiniteScrollPaginating.value = false;
|
||||
infiniteScrollLoading.value = false;
|
||||
infiniteScrollPositions = new WeakMap();
|
||||
resetGridVerticalScroll(true);
|
||||
}
|
||||
|
||||
@@ -4193,7 +4208,7 @@ function onCanvasScroll(event: Event) {
|
||||
recordScrollPosition({ top: scrollTop, left: scrollLeft });
|
||||
markGridScrolling();
|
||||
scheduleCanvasDraw();
|
||||
checkInfiniteScroll(scroller);
|
||||
maybeCheckInfiniteScroll(scroller);
|
||||
}
|
||||
|
||||
function canvasWheelDeltaToPixels(delta: number, deltaMode: number, pageSize: number): number {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
export interface DataGridScrollPosition {
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
|
||||
export interface DataGridScrollMetrics {
|
||||
scrollTop: number;
|
||||
scrollHeight: number;
|
||||
clientHeight: number;
|
||||
}
|
||||
|
||||
export function dataGridScrollPosition(top: number, left: number): DataGridScrollPosition {
|
||||
return {
|
||||
top: Math.max(0, top),
|
||||
left: Math.max(0, left),
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldCheckInfiniteScrollAfterScroll(previous: DataGridScrollPosition | undefined, current: DataGridScrollPosition): boolean {
|
||||
if (!previous) return false;
|
||||
return previous.top !== current.top;
|
||||
}
|
||||
|
||||
export function isDataGridNearScrollBottom(metrics: DataGridScrollMetrics, threshold = 100): boolean {
|
||||
return metrics.scrollHeight - metrics.scrollTop - metrics.clientHeight < threshold;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { dataGridScrollPosition, isDataGridNearScrollBottom, shouldCheckInfiniteScrollAfterScroll } from "../../apps/desktop/src/lib/dataGridInfiniteScroll.ts";
|
||||
|
||||
test("horizontal-only scroll does not check infinite scroll", () => {
|
||||
assert.equal(shouldCheckInfiniteScrollAfterScroll(dataGridScrollPosition(240, 0), dataGridScrollPosition(240, 180)), false);
|
||||
});
|
||||
|
||||
test("vertical scroll checks infinite scroll even when horizontal offset also changes", () => {
|
||||
assert.equal(shouldCheckInfiniteScrollAfterScroll(dataGridScrollPosition(240, 0), dataGridScrollPosition(360, 180)), true);
|
||||
});
|
||||
|
||||
test("first scroll position only establishes the infinite scroll baseline", () => {
|
||||
assert.equal(shouldCheckInfiniteScrollAfterScroll(undefined, dataGridScrollPosition(360, 180)), false);
|
||||
});
|
||||
|
||||
test("near-bottom check matches the grid threshold", () => {
|
||||
assert.equal(isDataGridNearScrollBottom({ scrollTop: 801, scrollHeight: 1000, clientHeight: 100 }), true);
|
||||
assert.equal(isDataGridNearScrollBottom({ scrollTop: 800, scrollHeight: 1000, clientHeight: 100 }), false);
|
||||
});
|
||||
Reference in New Issue
Block a user