diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 269e38c4bd..c0cdf04b5f 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -214,6 +214,7 @@ import { useConnectionStore } from "@/stores/connectionStore"; import { useQueryStore } from "@/stores/queryStore"; import { useSettingsStore } from "@/stores/settingsStore"; import { simpleDataGridOrderByMatchesSort, simpleDataGridOrderByReferencesMissingColumn, type DataGridSortDirection, type DataGridSortMode } from "@/lib/dataGrid/dataGridSort"; +import { resolveGridFocusRestoreTarget } from "@/lib/dataGrid/dataGridFocusRestore"; import { buildOrderedGridRows, type GridInsertRowPosition, type GridNewRowPlacement } from "@/lib/dataGrid/gridNewRowPlacement"; import { DATA_GRID_CONDITION_TOOLBAR_MIN_WIDTH, @@ -5414,6 +5415,8 @@ watch( function pauseCanvasGridWork() { dataGridIsActive = false; stopLoadingElapsedTimer(); + gridFocusActivationToken += 1; + gridRef.value?.setAttribute("data-grid-active", "false"); if (gridSurfaceBusy.value) finishDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner); canvasRuntime.pause(); gridScrollbarsRuntime.pause(); @@ -5428,6 +5431,7 @@ function pauseCanvasGridWork() { function resumeCanvasGridWork() { dataGridIsActive = true; startLoadingElapsedTimer(); + gridRef.value?.setAttribute("data-grid-active", "true"); if (gridSurfaceBusy.value) beginDataGridNativeSelectionBlock(dataGridNativeSelectionBlockOwner); canvasRuntime.resume(); gridScrollbarsRuntime.resume(); @@ -5443,6 +5447,30 @@ function clearInternalClipboardCopy() { clearDataGridClipboardCopy(); } +// Remember the last element that held focus inside the grid. Switching to +// another tab moves focus onto the tab strip (or body) and the kept-alive +// grid never gets it back on its own, which breaks arrow-key cell navigation +// after returning to the tab. +let lastFocusedWithinGrid: HTMLElement | null = null; +let gridFocusActivationToken = 0; + +function onGridFocusIn(event: FocusEvent) { + if (event.target instanceof HTMLElement) lastFocusedWithinGrid = event.target; +} + +function restoreGridFocusAfterActivation() { + if (!lastFocusedWithinGrid) return; + const activationToken = ++gridFocusActivationToken; + nextTick(() => { + if (!dataGridIsActive || activationToken !== gridFocusActivationToken) return; + if (editingCell.value) return; // the cell editor restores its own input focus + const target = resolveGridFocusRestoreTarget(gridRef.value, lastFocusedWithinGrid, document.activeElement); + target?.focus({ preventScroll: true }); + }); +} + +onActivated(restoreGridFocusAfterActivation); + onMounted(resumeCanvasGridWork); onActivated(resumeCanvasGridWork); onMounted(() => { @@ -8426,7 +8454,18 @@ const gridContextMenuItems = computed(() => {