fix(grid): append infinite scroll result segments

This commit is contained in:
t8y2 committed 2026-07-21 18:45:02 +08:00
1 parent 678033c82e
commit c153e93d54
9 files changed
+303 -30

No files matched your search

+26 -12
View File
@@ -2269,6 +2269,8 @@ const isInfiniteScrollPaginating = ref(false);
let lastInfiniteScrollPage = 0;
let infiniteScrollCheckScheduled = false;
let infiniteScrollAllLoaded = false;
let infiniteScrollRequestedOffset: number | undefined;
let infiniteScrollRequestedLimit: number | undefined;
// Tracks whether the current loading cycle was triggered by a refresh/rollback
// (as opposed to a normal paginate). Used to decide whether to auto-redirect
// when the current page no longer exists after data was deleted.
@@ -2313,9 +2315,19 @@ watch(
if (prevLoading && !loading && infiniteScrollLoading.value) {
infiniteScrollLoading.value = false;
isInfiniteScrollPaginating.value = false;
// Detect if the backend returned no new data for this page
const expectedRows = currentPage.value * pageSize.value;
if (props.result.rows.length < expectedRows) {
const requestedOffset = infiniteScrollRequestedOffset;
const requestedLimit = infiniteScrollRequestedLimit;
infiniteScrollRequestedOffset = undefined;
infiniteScrollRequestedLimit = undefined;
if (requestedOffset === undefined || props.result.appended_from_row_count !== requestedOffset) {
// Failed/stale append requests preserve the old result. Roll back the
// optimistic page marker so a later scroll can retry the same segment.
currentPage.value = Math.max(1, currentPage.value - 1);
lastInfiniteScrollPage = Math.max(0, currentPage.value - 1);
return;
}
const appendedRows = props.result.rows.length - requestedOffset;
if (props.result.rows.length >= infiniteScrollMaxRows.value || appendedRows < (requestedLimit ?? pageSize.value)) {
infiniteScrollAllLoaded = true;
}
}
@@ -2489,21 +2501,21 @@ function nextPage() {
function infiniteScrollNextPage() {
if (infiniteScrollLoading.value || props.loading) return;
const nextOffset = props.result.rows.length;
const remainingRows = infiniteScrollMaxRows.value - nextOffset;
if (remainingRows <= 0) return;
const nextLimit = Math.min(pageSize.value, remainingRows);
const nextPageNum = currentPage.value + 1;
const cumulativeLimit = nextPageNum * pageSize.value;
if (cumulativeLimit > infiniteScrollMaxRows.value) return;
// Stop if we already know all data is loaded
if (infiniteScrollAllLoaded) return;
// Skip if we already have this many rows loaded (e.g. cached data)
if (props.result.rows.length >= cumulativeLimit) {
currentPage.value = nextPageNum;
return;
}
infiniteScrollLoading.value = true;
isInfiniteScrollPaginating.value = true;
infiniteScrollRequestedOffset = nextOffset;
infiniteScrollRequestedLimit = nextLimit;
currentPage.value = nextPageNum;
// Load cumulative data (all rows up to current page) to append instead of replace
emit("paginate", 0, cumulativeLimit, currentWhereInput(), currentOrderBy());
// Fetch only the missing segment. Re-reading offset 0 grows transfer and replaces
// row identities, which would invalidate pending edits while the user scrolls.
emit("paginate", nextOffset, nextLimit, currentWhereInput(), currentOrderBy());
}
function checkInfiniteScroll(scroller: HTMLElement) {
if (!infiniteScrollEnabled.value || infiniteScrollLoading.value || props.loading) return;
@@ -2969,6 +2981,8 @@ function resetInfiniteScrollState() {
currentPage.value = 1;
lastInfiniteScrollPage = 0;
infiniteScrollAllLoaded = false;
infiniteScrollRequestedOffset = undefined;
infiniteScrollRequestedLimit = undefined;
isInfiniteScrollPaginating.value = false;
infiniteScrollLoading.value = false;
infiniteScrollPositions = new WeakMap();