fix: preserve tab state when switching between tabs
Render all tabs simultaneously with display:none for inactive ones, instead of conditionally rendering only the active tab. This prevents React from unmounting/remounting components on tab switch, preserving query results, editor cursor, AI explanations, and other local state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,8 @@ import { EntityLookupPanel } from "@/components/lookup/EntityLookupPanel";
|
|||||||
|
|
||||||
export function TabContent() {
|
export function TabContent() {
|
||||||
const { tabs, activeTabId, updateTab } = useAppStore();
|
const { tabs, activeTabId, updateTab } = useAppStore();
|
||||||
const activeTab = tabs.find((t) => t.id === activeTabId);
|
|
||||||
|
|
||||||
if (!activeTab) {
|
if (tabs.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
|
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
|
||||||
Open a query tab or select a table to get started.
|
Open a query tab or select a table to get started.
|
||||||
@@ -18,56 +17,75 @@ export function TabContent() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (activeTab.type) {
|
return (
|
||||||
|
<>
|
||||||
|
{tabs.map((tab) => {
|
||||||
|
const isActive = tab.id === activeTabId;
|
||||||
|
let content: React.ReactNode;
|
||||||
|
|
||||||
|
switch (tab.type) {
|
||||||
case "query":
|
case "query":
|
||||||
return (
|
content = (
|
||||||
<WorkspacePanel
|
<WorkspacePanel
|
||||||
key={activeTab.id}
|
connectionId={tab.connectionId}
|
||||||
connectionId={activeTab.connectionId}
|
initialSql={tab.sql}
|
||||||
initialSql={activeTab.sql}
|
onSqlChange={(sql) => updateTab(tab.id, { sql })}
|
||||||
onSqlChange={(sql) => updateTab(activeTab.id, { sql })}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
case "table":
|
case "table":
|
||||||
return (
|
content = (
|
||||||
<TableDataView
|
<TableDataView
|
||||||
key={activeTab.id}
|
connectionId={tab.connectionId}
|
||||||
connectionId={activeTab.connectionId}
|
schema={tab.schema!}
|
||||||
schema={activeTab.schema!}
|
table={tab.table!}
|
||||||
table={activeTab.table!}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
case "structure":
|
case "structure":
|
||||||
return (
|
content = (
|
||||||
<TableStructure
|
<TableStructure
|
||||||
key={activeTab.id}
|
connectionId={tab.connectionId}
|
||||||
connectionId={activeTab.connectionId}
|
schema={tab.schema!}
|
||||||
schema={activeTab.schema!}
|
table={tab.table!}
|
||||||
table={activeTab.table!}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
case "roles":
|
case "roles":
|
||||||
return (
|
content = (
|
||||||
<RoleManagerView
|
<RoleManagerView
|
||||||
key={activeTab.id}
|
connectionId={tab.connectionId}
|
||||||
connectionId={activeTab.connectionId}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
case "sessions":
|
case "sessions":
|
||||||
return (
|
content = (
|
||||||
<SessionsView
|
<SessionsView
|
||||||
key={activeTab.id}
|
connectionId={tab.connectionId}
|
||||||
connectionId={activeTab.connectionId}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
case "lookup":
|
case "lookup":
|
||||||
return (
|
content = (
|
||||||
<EntityLookupPanel
|
<EntityLookupPanel
|
||||||
key={activeTab.id}
|
connectionId={tab.connectionId}
|
||||||
connectionId={activeTab.connectionId}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return null;
|
content = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={tab.id}
|
||||||
|
className="h-full"
|
||||||
|
style={{ display: isActive ? undefined : "none" }}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user