fix(ui): keep driver store/settings page switching and tab active state (#7035)

This commit is contained in:
zipg authored and t8y2 committed 2026-08-24 14:34:59 +08:00
1 parent 0efce9e9a9
commit c8dbeeaf7d
6 files changed
+50 -13

No files matched your search

+13 -10
View File
@@ -535,6 +535,13 @@ function openSettings(initialTab = "appearance", initialSection?: string) {
activateSettingsPage();
}
type MainContentSurface = "query" | "settings" | "driverStore";
function activateMainContentSurface(surface: MainContentSurface) {
settingsStore.settingsPageActive = surface === "settings";
driverStoreActive.value = surface === "driverStore";
}
watch(
() => settingsStore.settingsNavigationRequest,
(request) => {
@@ -546,23 +553,20 @@ watch(
function activateSettingsPage() {
settingsPageTabOpen.value = true;
settingsStore.settingsPageActive = true;
driverStoreActive.value = false;
activateMainContentSurface("settings");
}
function activateQuerySurface() {
driverStoreActive.value = false;
settingsStore.settingsPageActive = false;
activateMainContentSurface("query");
}
function closeSettingsPage() {
settingsPageTabOpen.value = false;
settingsStore.settingsPageActive = false;
if (settingsReturnSurface.value === "driverStore" && driverStoreTabOpen.value) {
driverStoreActive.value = true;
activateMainContentSurface("driverStore");
return;
}
driverStoreActive.value = false;
activateMainContentSurface("query");
}
const driverStoreFocus = ref<DriverStoreFocus | null>(null);
@@ -578,13 +582,12 @@ function openDriverStorePage(target?: "agent" | "jdbc" | "storage" | "runtime" |
driverStoreFocus.value = target ?? null;
}
driverStoreTabOpen.value = true;
driverStoreActive.value = true;
settingsStore.settingsPageActive = false;
activateMainContentSurface("driverStore");
}
function closeDriverStorePage() {
driverStoreTabOpen.value = false;
driverStoreActive.value = false;
activateMainContentSurface("query");
driverStoreActiveTab.value = "agent";
driverStoreFocus.value = null;
}
@@ -2224,8 +2224,8 @@ watch(driverStoreTab, (tab) => {
</Tabs>
</div>
</div>
<AgentOfflineExportDialog v-model:open="offlineExportDialogOpen" :preview="offlineExportPreview" :loading="offlineExportLoading" :exporting="offlineExporting" :error="offlineExportError" @confirm="exportOfflinePackage" />
</div>
<AgentOfflineExportDialog v-model:open="offlineExportDialogOpen" :preview="offlineExportPreview" :loading="offlineExportLoading" :exporting="offlineExporting" :error="offlineExportError" @confirm="exportOfflinePackage" />
</template>
<style>
@@ -0,0 +1,12 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const source = readFileSync(new URL("../DriverStoreDialog.vue", import.meta.url), "utf8");
const templateStart = source.indexOf("<template>");
const template = source.slice(templateStart, source.indexOf("\n<style", templateStart)).trim();
describe("DriverStoreDialog visibility", () => {
it("keeps the page and its auxiliary dialog under one root so component v-show works", () => {
expect(template).toMatch(/^<template>\s*<div class="driver-store-view[^"]*">[\s\S]*<AgentOfflineExportDialog[^>]*\/>\s*<\/div>\s*<\/template>$/);
});
});
@@ -500,6 +500,11 @@ function tabColorStyle(tab: QueryTab) {
};
}
function specialTabActiveStyle(active: boolean | undefined): CSSProperties | undefined {
if (!active) return undefined;
return isClassicLayout.value ? { boxShadow: "inset 0 -2px 0 var(--ring)" } : { borderColor: "var(--ring)" };
}
function tabIconClass(tab: QueryTab) {
if (tab.externalSqlFileMissing) return "text-amber-600 dark:text-amber-400";
if (tab.mode === "mq") return "";
@@ -764,7 +769,7 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', settingsPageActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
: ['h-7 rounded-md border font-medium', settingsPageActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
"
:style="isClassicLayout && settingsPageActive ? { boxShadow: '0 1px 0 0 var(--color-background)' } : {}"
:style="specialTabActiveStyle(settingsPageActive)"
:data-active-tab="settingsPageActive"
@click="emit('activate-settings-page')"
@mousedown.middle.prevent="emit('close-settings-page')"
@@ -791,7 +796,7 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', driverStoreActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
: ['h-7 rounded-md border font-medium', driverStoreActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
"
:style="isClassicLayout && driverStoreActive ? { boxShadow: '0 1px 0 0 var(--color-background)' } : {}"
:style="specialTabActiveStyle(driverStoreActive)"
:data-active-tab="driverStoreActive"
@click="emit('activate-driver-store')"
@mousedown.middle.prevent="emit('close-driver-store')"
@@ -95,6 +95,15 @@ describe("AppTabBar right-side close action", () => {
});
});
describe("AppTabBar special page selection", () => {
it("shows the active settings or driver-manager tab with the same ring used by regular tabs", () => {
expect(tabBarSource).toContain("function specialTabActiveStyle(active: boolean | undefined)");
expect(tabBarSource).toContain('return isClassicLayout.value ? { boxShadow: "inset 0 -2px 0 var(--ring)" } : { borderColor: "var(--ring)" };');
expect(tabBarSource).toContain(':style="specialTabActiveStyle(settingsPageActive)"');
expect(tabBarSource).toContain(':style="specialTabActiveStyle(driverStoreActive)"');
});
});
describe("AppTabBar overflow search", () => {
it("filters every open tab by its display and source titles", () => {
expect(tabBarSource).toContain('const tabSearchQuery = ref("");');
@@ -5,6 +5,14 @@ const appSource = readFileSync(new URL("../../../App.vue", import.meta.url), "ut
const settingsDialogSource = readFileSync(new URL("../../../components/editor/EditorSettingsDialog.vue", import.meta.url), "utf8");
describe("settings page navigation", () => {
it("keeps query, settings, and driver-manager surfaces mutually exclusive", () => {
expect(appSource).toContain('type MainContentSurface = "query" | "settings" | "driverStore";');
expect(appSource).toMatch(/function activateMainContentSurface\(surface: MainContentSurface\) \{\s*settingsStore\.settingsPageActive = surface === "settings";\s*driverStoreActive\.value = surface === "driverStore";/);
expect(appSource).toMatch(/function activateSettingsPage\(\) \{\s*settingsPageTabOpen\.value = true;\s*activateMainContentSurface\("settings"\);/);
expect(appSource).toMatch(/function activateQuerySurface\(\) \{\s*activateMainContentSurface\("query"\);/);
expect(appSource).toMatch(/driverStoreTabOpen\.value = true;\s*activateMainContentSurface\("driverStore"\);/);
});
it("replays repeated navigation requests for the same tab", () => {
expect(appSource).toContain("settingsNavigationRequestId.value += 1");
expect(appSource).toContain(':navigation-request-id="settingsNavigationRequestId"');