fix(grid): restore keyboard focus after tab switches

This commit is contained in:
Freedom authored and GitHub committed 2026-08-04 13:14:34 +08:00
1 parent ae87353a28
commit 8228c2af17
3 files changed
+142 -1

No files matched your search

+40 -1
View File
@@ -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<ContextMenuItem[]>(() => {
</script>
<template>
<div ref="gridRef" data-grid-root class="h-full flex flex-col overflow-hidden outline-none" :class="{ 'data-grid--editing-cell': !!editingCell, 'data-grid--dark': isDark }" :style="gridStyle" tabindex="0" @keydown="onGridKeydown" @paste="onGridPaste">
<div
ref="gridRef"
data-grid-root
data-grid-active="true"
class="h-full flex flex-col overflow-hidden outline-none"
:class="{ 'data-grid--editing-cell': !!editingCell, 'data-grid--dark': isDark }"
:style="gridStyle"
tabindex="0"
@keydown="onGridKeydown"
@paste="onGridPaste"
@focusin="onGridFocusIn"
>
<CustomContextMenu :items="gridContextMenuItems" v-slot="{ onContextMenu }">
<div v-if="hasData || canShowWhereSearch" class="flex-1 flex flex-col overflow-hidden" @contextmenu="onContextMenu">
<!-- Search bar -->