fix(frontend): guard startup and loading shortcuts

This commit is contained in:
zipg authored and GitHub committed 2026-08-03 16:57:26 +08:00
1 parent 246dfad018
commit 658e3d16ad
17 files changed
+420 -15

No files matched your search

+35 -12
View File
@@ -187,7 +187,8 @@ import { useToast } from "@/composables/useToast";
import { useNavigationTargets } from "@/composables/useNavigationTargets";
import { useDataGridExport, type MongoCopyUpdateTarget } from "@/composables/useDataGridExport";
import { eventTargetAllowsNativeClipboard, isPlainClipboardShortcut, readTextFromClipboard } from "@/lib/common/clipboard";
import { claimDataGridPaste, clearDataGridClipboardCopy, parseDataGridClipboard, planDataGridPaste } from "@/lib/dataGrid/dataGridClipboard";
import { claimDataGridPaste, claimDataGridSelectAll, clearDataGridClipboardCopy, parseDataGridClipboard, planDataGridPaste } from "@/lib/dataGrid/dataGridClipboard";
import { beginDataGridNativeSelectionBlock, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection";
import { DATA_GRID_COPY_EXTRACTOR_DESCRIPTORS, DATA_GRID_COPY_EXTRACTOR_IDS, extractorUnavailableForDatabase, type DataGridCopyExtractorId } from "@/lib/dataGrid/dataGridCopyExtractor";
import { columnNamesForCopy } from "@/lib/dataGrid/dataGridColumnNameCopy";
import { DATA_GRID_ROW_NUM_WIDTH, dataGridRowNumberColumnWidth, resolveDataGridMaxRowNumber, useDataGridColumnResize } from "@/composables/useDataGridColumnResize";
@@ -2541,8 +2542,17 @@ const canFetchNextInfiniteScrollSegment = computed(() =>
);
const canJumpLastPage = computed(() => canGoNextPage.value && (hasKnownPaginationTotalRowCount.value || allRowsLoaded.value || !!props.tableMeta || !!props.countSql || !!props.countTotalRows));
const totalRowCountBusy = computed(() => props.totalRowCountLoading === true || manualTotalRowCountLoading.value);
/** Full-grid busy state: query loading or on-demand COUNT (e.g. jump to last page). */
const gridSurfaceBusy = computed(() => props.loading === true || totalRowCountBusy.value);
/** Full-grid busy state: refresh dispatch, query loading, or on-demand COUNT (e.g. jump to last page). */
const gridSurfaceBusy = computed(() => isRefreshingData.value || props.loading === true || totalRowCountBusy.value);
const dataGridNativeSelectionBlockOwner = {};
watch(
gridSurfaceBusy,
(busy, prevBusy) => {
if (busy) beginDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner);
else if (prevBusy) finishDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner);
},
{ immediate: true },
);
const canCalculateTotalRowCount = computed(() => !!props.countTotalRows || (!!props.connectionId && (!!props.tableMeta || !!props.countSql)));
const showExactTotalCountAction = computed(() => canCalculateTotalRowCount.value && (totalRowCountIsExact.value === false || typeof displayedTotalRowCount.value !== "number"));
watch(
@@ -3288,6 +3298,7 @@ async function onToolbarRefresh() {
}
preserveTransposeOnNextResult.value = showTranspose.value;
isRefreshingData.value = true;
beginDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner);
emit("reload", props.sql, searchText.value, currentWhereInput(), currentOrderBy(), pageSize.value, (currentPage.value - 1) * pageSize.value, "refresh");
}
@@ -5374,6 +5385,8 @@ watch(
function pauseCanvasGridWork() {
dataGridIsActive = false;
stopLoadingElapsedTimer();
if (gridSurfaceBusy.value) finishDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner);
canvasRuntime.pause();
gridScrollbarsRuntime.pause();
disconnectCellEditResizeObserver();
@@ -5386,6 +5399,8 @@ function pauseCanvasGridWork() {
function resumeCanvasGridWork() {
dataGridIsActive = true;
startLoadingElapsedTimer();
if (gridSurfaceBusy.value) beginDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner);
canvasRuntime.resume();
gridScrollbarsRuntime.resume();
nextTick(() => {
@@ -6537,8 +6552,8 @@ async function onGridKeydown(event: KeyboardEvent) {
return;
}
if (clipboardShortcut(event, "a")) {
if (!hasData.value) return;
event.preventDefault();
const intent = claimDataGridSelectAll(event, gridSurfaceBusy.value, hasData.value);
if (intent !== "select") return;
selectAllCells();
return;
}
@@ -7903,11 +7918,21 @@ function stopLoadingElapsedTimer() {
}
}
function startLoadingElapsedTimer() {
function startLoadingElapsedTimer(reset = false) {
stopLoadingElapsedTimer();
if (!dataGridIsActive || !gridSurfaceBusy.value) return;
_loadingStart = Date.now();
loadingElapsed.value = 0;
if (!dataGridIsActive || !gridSurfaceBusy.value) {
if (!gridSurfaceBusy.value) {
_loadingStart = 0;
loadingElapsed.value = 0;
}
return;
}
if (reset || !_loadingStart) {
_loadingStart = Date.now();
loadingElapsed.value = 0;
} else {
loadingElapsed.value = Date.now() - _loadingStart;
}
const updateOnNextFrame = () => {
if (!dataGridIsActive || !gridSurfaceBusy.value) return;
loadingElapsed.value = Date.now() - _loadingStart;
@@ -7926,7 +7951,7 @@ watch(gridSurfaceBusy, (isLoading) => {
});
}
if (isLoading) {
startLoadingElapsedTimer();
startLoadingElapsedTimer(true);
} else if (isDebugLoggingEnabled()) {
nextTick(() => {
requestAnimationFrame(() => {
@@ -7941,11 +7966,9 @@ watch(gridSurfaceBusy, (isLoading) => {
});
onActivated(() => {
startLoadingElapsedTimer();
autoRefresh.start();
});
onDeactivated(() => {
stopLoadingElapsedTimer();
autoRefresh.stop();
});
@@ -0,0 +1,14 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dataGridSource = readFileSync(new URL("../DataGrid.vue", import.meta.url), "utf8");
describe("DataGrid loading elapsed timer", () => {
it("resets for a new load but resumes from the original start after KeepAlive activation", () => {
expect(dataGridSource).toContain("function startLoadingElapsedTimer(reset = false)");
expect(dataGridSource).toContain("if (reset || !_loadingStart)");
expect(dataGridSource).toContain("startLoadingElapsedTimer(true);");
expect(dataGridSource).toMatch(/function resumeCanvasGridWork\(\) \{\s*dataGridIsActive = true;\s*startLoadingElapsedTimer\(\);/);
expect(dataGridSource).toMatch(/function pauseCanvasGridWork\(\) \{\s*dataGridIsActive = false;\s*stopLoadingElapsedTimer\(\);/);
});
});