fix(sidebar): follow activation mode for object browsing

This commit is contained in:
wojiukankan authored and GitHub committed 2026-09-01 00:19:58 +08:00
1 parent ef2fdb941c
commit f958bf4602
19 files changed
+117 -95

No files matched your search

@@ -512,7 +512,7 @@ const dataTabReuseModeHelp = ref<DataTabReuseMode | null>(null);
const editRoutineSourceOpenMode = ref(settingsStore.editorSettings.routineSourceOpenMode);
const editSidebarTableSearchEnabled = ref(settingsStore.editorSettings.sidebarTableSearchEnabled);
const editAutoSelectActiveSidebarNode = ref(settingsStore.editorSettings.autoSelectActiveSidebarNode);
const editSidebarOpenDatabaseOnSingleClick = ref(settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick);
const editSidebarBrowseObjectsOnDatabaseActivation = ref(settingsStore.editorSettings.sidebarBrowseObjectsOnDatabaseActivation);
const editOpenTabsRestoreMode = ref<OpenTabsRestoreMode>(settingsStore.editorSettings.openTabsRestoreMode);
const editDisconnectTabHandlingMode = ref<DisconnectTabHandlingMode>(settingsStore.editorSettings.disconnectTabHandlingMode);
const editDataTabReuseMode = ref<DataTabReuseMode>(settingsStore.editorSettings.dataTabReuseMode);
@@ -656,7 +656,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
routineSourceOpenMode: editRoutineSourceOpenMode.value,
sidebarTableSearchEnabled: editSidebarTableSearchEnabled.value,
autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value,
sidebarOpenDatabaseOnSingleClick: editSidebarOpenDatabaseOnSingleClick.value,
sidebarBrowseObjectsOnDatabaseActivation: editSidebarBrowseObjectsOnDatabaseActivation.value,
openTabsRestoreMode: editOpenTabsRestoreMode.value,
disconnectTabHandlingMode: editDisconnectTabHandlingMode.value,
dataTabReuseMode: editDataTabReuseMode.value,
@@ -1052,7 +1052,7 @@ function syncEditorSettingsDraftFromStore() {
editRoutineSourceOpenMode.value = settingsStore.editorSettings.routineSourceOpenMode;
editSidebarTableSearchEnabled.value = settingsStore.editorSettings.sidebarTableSearchEnabled;
editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode;
editSidebarOpenDatabaseOnSingleClick.value = settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick;
editSidebarBrowseObjectsOnDatabaseActivation.value = settingsStore.editorSettings.sidebarBrowseObjectsOnDatabaseActivation;
editOpenTabsRestoreMode.value = settingsStore.editorSettings.openTabsRestoreMode;
editDisconnectTabHandlingMode.value = settingsStore.editorSettings.disconnectTabHandlingMode;
editDataTabReuseMode.value = settingsStore.editorSettings.dataTabReuseMode;
@@ -1313,7 +1313,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editRoutineSourceOpenMode.value = DEFAULT_EDITOR_SETTINGS.routineSourceOpenMode;
editSidebarTableSearchEnabled.value = DEFAULT_EDITOR_SETTINGS.sidebarTableSearchEnabled;
editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode;
editSidebarOpenDatabaseOnSingleClick.value = DEFAULT_EDITOR_SETTINGS.sidebarOpenDatabaseOnSingleClick;
editSidebarBrowseObjectsOnDatabaseActivation.value = DEFAULT_EDITOR_SETTINGS.sidebarBrowseObjectsOnDatabaseActivation;
editOpenTabsRestoreMode.value = DEFAULT_EDITOR_SETTINGS.openTabsRestoreMode;
editDisconnectTabHandlingMode.value = DEFAULT_EDITOR_SETTINGS.disconnectTabHandlingMode;
editDataTabReuseMode.value = DEFAULT_EDITOR_SETTINGS.dataTabReuseMode;
@@ -1447,7 +1447,7 @@ function resetAllDefaults() {
editRoutineSourceOpenMode.value = DEFAULT_EDITOR_SETTINGS.routineSourceOpenMode;
editSidebarTableSearchEnabled.value = DEFAULT_EDITOR_SETTINGS.sidebarTableSearchEnabled;
editAutoSelectActiveSidebarNode.value = DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode;
editSidebarOpenDatabaseOnSingleClick.value = DEFAULT_EDITOR_SETTINGS.sidebarOpenDatabaseOnSingleClick;
editSidebarBrowseObjectsOnDatabaseActivation.value = DEFAULT_EDITOR_SETTINGS.sidebarBrowseObjectsOnDatabaseActivation;
editOpenTabsRestoreMode.value = DEFAULT_EDITOR_SETTINGS.openTabsRestoreMode;
editDisconnectTabHandlingMode.value = DEFAULT_EDITOR_SETTINGS.disconnectTabHandlingMode;
editDataTabReuseMode.value = DEFAULT_EDITOR_SETTINGS.dataTabReuseMode;
@@ -5396,6 +5396,15 @@ onUnmounted(() => {
</Button>
</div>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="flex items-center gap-2">
<Label for="sidebar-browse-objects-on-database-activation">{{ t("settings.sidebarBrowseObjectsOnDatabaseActivation") }}</Label>
<HelpTooltip :label="t('settings.sidebarBrowseObjectsOnDatabaseActivation')">
{{ t("settings.sidebarBrowseObjectsOnDatabaseActivationDescription") }}
</HelpTooltip>
</div>
<Switch id="sidebar-browse-objects-on-database-activation" v-model="editSidebarBrowseObjectsOnDatabaseActivation" />
</div>
<div class="space-y-2">
<div class="flex items-center gap-2">
<Label>{{ t("settings.reuseDataTab") }}</Label>
@@ -5556,15 +5565,6 @@ onUnmounted(() => {
<Switch id="editor-prefill-new-query" v-model="editPrefillNewQueryWithSelect" class="mt-0.5" />
</div>
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="flex items-center gap-2">
<Label for="sidebar-open-database-on-single-click">{{ t("settings.sidebarOpenDatabaseOnSingleClick") }}</Label>
<HelpTooltip :label="t('settings.sidebarOpenDatabaseOnSingleClick')">
{{ t("settings.sidebarOpenDatabaseOnSingleClickDescription") }}
</HelpTooltip>
</div>
<Switch id="sidebar-open-database-on-single-click" v-model="editSidebarOpenDatabaseOnSingleClick" />
</div>
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="flex items-center gap-2">
<Label for="sidebar-table-search-enabled">{{ t("settings.sidebarTableSearchEnabled") }}</Label>
@@ -138,16 +138,16 @@ describe("EditorSettingsDialog live preview placement", () => {
const objectDisplay = navigationSection.indexOf('t("settings.sidebarObjectDisplay")');
const routineOpenMode = navigationSection.indexOf('t("settings.routineSourceOpenMode")');
const tableNavigation = navigationSection.indexOf('id="editor-click-table-navigation-ddl"');
const openDatabaseOnClick = navigationSection.indexOf('id="sidebar-open-database-on-single-click"');
const browseObjectsOnDatabaseActivation = navigationSection.indexOf('id="sidebar-browse-objects-on-database-activation"');
const tableSearch = navigationSection.indexOf('id="sidebar-table-search-enabled"');
const activeNodeSelection = navigationSection.indexOf('id="auto-select-active-sidebar-node"');
expect(adjacentDataTabs).toBeGreaterThanOrEqual(0);
expect(browseObjectsOnDatabaseActivation).toBeGreaterThanOrEqual(0);
expect(adjacentDataTabs).toBeGreaterThan(browseObjectsOnDatabaseActivation);
expect(objectDisplay).toBeGreaterThan(adjacentDataTabs);
expect(routineOpenMode).toBeGreaterThan(objectDisplay);
expect(tableNavigation).toBeGreaterThan(routineOpenMode);
expect(openDatabaseOnClick).toBeGreaterThan(tableNavigation);
expect(tableSearch).toBeGreaterThan(openDatabaseOnClick);
expect(tableSearch).toBeGreaterThan(tableNavigation);
expect(activeNodeSelection).toBeGreaterThan(tableSearch);
});
@@ -1005,7 +1005,7 @@ function runRowClickAction(clickDetail: number, requestId: number) {
void openObjectBrowser();
return;
}
const action = treeNodeRowAction(node.type, canExpand.value, settingsStore.editorSettings.sidebarActivation, currentDatabaseType(), settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick, canOpenObjectBrowser.value);
const action = treeNodeRowAction(node.type, canExpand.value, settingsStore.editorSettings.sidebarActivation, currentDatabaseType(), settingsStore.editorSettings.sidebarBrowseObjectsOnDatabaseActivation, canOpenObjectBrowser.value);
// WebKit can keep incrementing click.detail while the pointer moves quickly
// between adjacent rows. Nacos entries are idempotent navigation targets, so
// do not mistake that rapid one-click switching for a double-click toggle.
@@ -1370,7 +1370,7 @@ function onDoubleClick(event: MouseEvent) {
void openObjectBrowser();
return;
}
const action = treeNodeRowDoubleClickAction(activeNode.value.type, canOpenObjectBrowser.value, settingsStore.editorSettings.sidebarActivation, canExpand.value, currentDatabaseType(), canOpenConnectionDatabaseBrowser.value, settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick);
const action = treeNodeRowDoubleClickAction(activeNode.value.type, canOpenObjectBrowser.value, settingsStore.editorSettings.sidebarActivation, canExpand.value, currentDatabaseType(), canOpenConnectionDatabaseBrowser.value, settingsStore.editorSettings.sidebarBrowseObjectsOnDatabaseActivation);
if (action === "open-database-browser") {
void openDatabaseBrowser();
} else if (action === "open-object-browser") {
@@ -86,7 +86,7 @@ import { sidebarTreeRuntimeKey } from "@/lib/sidebar/sidebarTreeRuntime";
import { treeNodePinKey } from "@/lib/app/pinnedItems";
import { isTreeGroupNodeType } from "@/lib/sidebar/treeNodeGroup";
import { customTypeCapabilities } from "@/lib/database/databaseObjectCapabilities";
import { shouldActivateTreeNodeOnSingleClick, shouldOpenObjectBrowserOnSingleClick } from "@/lib/sidebar/treeNodeClick";
import { shouldActivateTreeNodeOnSingleClick } from "@/lib/sidebar/treeNodeClick";
const { t } = useI18n();
@@ -1362,10 +1362,6 @@ function onClick(event: MouseEvent) {
}
selectSingleTreeNode(props.node);
rowRef.value?.focus({ preventScroll: true });
if (shouldOpenObjectBrowserOnSingleClick(props.node.type, settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick)) {
treeRuntime.handleRowClick(props.node, event.detail);
return;
}
if (!shouldActivateTreeNodeOnSingleClick(props.node.type, settingsStore.editorSettings.sidebarActivation) && props.node.type !== "load-more") return;
treeRuntime.handleRowClick(props.node, event.detail);
}
+2 -2
View File
@@ -6486,8 +6486,8 @@ export default {
routineSourceOpenModeDialogDescription: "Open source in a popup dialog for quick viewing and editing.",
autoSelectActiveSidebarNode: "Always select opened item",
autoSelectActiveSidebarNodeDescription: "When switching tabs, select the matching visible table, collection, or SQL file in the sidebar.",
sidebarOpenDatabaseOnSingleClick: "Browse objects on database single click",
sidebarOpenDatabaseOnSingleClickDescription: 'When enabled, single-clicking a database or schema node also expands the sidebar and opens the "Browse Objects" tab.',
sidebarBrowseObjectsOnDatabaseActivation: "Browse objects when activating a database",
sidebarBrowseObjectsOnDatabaseActivationDescription: 'When enabled, activating a database or schema node using the selected single- or double-click mode also opens the "Browse Objects" tab.',
openTabsRestoreMode: "Restore tabs on launch",
openTabsRestoreModeDescription: "Choose how DBX restores tabs that were open last time.",
openTabsRestoreModeAll: "Keep all tabs",
+2 -2
View File
@@ -6157,8 +6157,8 @@ export default withEnglishFallback({
sidebarObjectDisplaySimpleDescription: "Muestra tablas y vistas directamente bajo cada base de datos o esquema para reducir niveles intermedios.",
autoSelectActiveSidebarNode: "Seleccionar siempre el elemento abierto",
autoSelectActiveSidebarNodeDescription: "Al cambiar de pestaña, selecciona la tabla, colección o archivo SQL visible correspondiente en la barra lateral.",
sidebarOpenDatabaseOnSingleClick: "Explorar objetos al hacer clic en la base de datos",
sidebarOpenDatabaseOnSingleClickDescription: 'Si está activado, al hacer un solo clic en un nodo de base de datos o esquema se expande la barra lateral y se abre la pestaña "Explorar objetos".',
sidebarBrowseObjectsOnDatabaseActivation: "Explorar objetos al activar una base de datos",
sidebarBrowseObjectsOnDatabaseActivationDescription: 'Si está activado, al activar un nodo de base de datos o esquema con el modo seleccionado de uno o dos clics también se abre la pestaña "Explorar objetos".',
openTabsRestoreMode: "Restaurar pestañas al iniciar",
openTabsRestoreModeDescription: "Elige cómo DBX restaura las pestañas que estaban abiertas la última vez.",
openTabsRestoreModeAll: "Conservar todas las pestañas",
+2 -2
View File
@@ -6157,8 +6157,8 @@ export default withEnglishFallback({
sidebarObjectDisplaySimpleDescription: "Elenca tabelle e viste direttamente sotto ogni database o schema per ridurre i livelli intermedi.",
autoSelectActiveSidebarNode: "Seleziona sempre l'elemento aperto",
autoSelectActiveSidebarNodeDescription: "Quando passi da una scheda all'altra, seleziona la tabella, la collezione o il file SQL visibile corrispondente nella barra laterale.",
sidebarOpenDatabaseOnSingleClick: "Sfoglia oggetti al clic sul database",
sidebarOpenDatabaseOnSingleClickDescription: 'Se abilitato, un singolo clic su un nodo database o schema espande la barra laterale e apre la scheda "Esplora Oggetti".',
sidebarBrowseObjectsOnDatabaseActivation: "Esplora oggetti all'attivazione di un database",
sidebarBrowseObjectsOnDatabaseActivationDescription: "Se abilitato, l'attivazione di un nodo database o schema con la modalità a clic singolo o doppio selezionata apre anche la scheda «Esplora oggetti».",
openTabsRestoreMode: "Ripristina schede all'avvio",
openTabsRestoreModeDescription: "Scegli come DBX ripristina le schede aperte l'ultima volta.",
openTabsRestoreModeAll: "Mantieni tutte le schede",
+2 -2
View File
@@ -6183,8 +6183,8 @@ export default withEnglishFallback({
sidebarObjectDisplaySimpleDescription: "中間レベルを減らすために、各データベースまたはスキーマの直下にテーブルとビューを一覧表示します。",
autoSelectActiveSidebarNode: "開いた項目を常に選択",
autoSelectActiveSidebarNodeDescription: "タブ切替時に、サイドバーで一致する表示中のテーブル、コレクション、またはSQLファイルを選択します。",
sidebarOpenDatabaseOnSingleClick: "データベースのシングルクリックでオブジェクトを参照",
sidebarOpenDatabaseOnSingleClickDescription: "有効にすると、データベースまたはスキーマノードをシングルクリックした際に、サイドバーを展開すると同時に「オブジェクトを参照」タブを開きます。",
sidebarBrowseObjectsOnDatabaseActivation: "データベースノードのアクティブ化時にオブジェクトを参照",
sidebarBrowseObjectsOnDatabaseActivationDescription: "有効にすると、選択したシングルクリックまたはダブルクリック方式でデータベースまたはスキーマノードをアクティブ化した際に、サイドバーを展開すると同時に「オブジェクトを参照」タブを開きます。",
openTabsRestoreMode: "起動時にタブを復元",
openTabsRestoreModeDescription: "DBX 起動時に前回開いていたタブをどのように復元するかを選択します。",
openTabsRestoreModeAll: "すべてのタブを保持",
+2 -2
View File
@@ -5877,8 +5877,8 @@ export default withEnglishFallback({
sidebarObjectDisplaySimpleDescription: "중간 수준을 줄이기 위해 각 데이터베이스나 스키마 아래에 테이블과 뷰를 직접 나열합니다.",
autoSelectActiveSidebarNode: "열린 항목 항상 선택",
autoSelectActiveSidebarNodeDescription: "탭을 전환할 때 사이드바에서 일치하는 표시된 테이블, 컬렉션 또는 SQL 파일을 선택합니다.",
sidebarOpenDatabaseOnSingleClick: "데이터베이스 클릭 시 객체 탐색",
sidebarOpenDatabaseOnSingleClickDescription: '활성화하면 데이터베이스 또는 스키마 노드를 한 번 클릭할 때 사이드바를 펼치면서 "객체 탐색" 탭을 엽니다.',
sidebarBrowseObjectsOnDatabaseActivation: "데이터베이스 노드 활성화 시 객체 탐색",
sidebarBrowseObjectsOnDatabaseActivationDescription: '활성화하면 선택한 한 번 클릭 또는 두 번 클릭 방식으로 데이터베이스나 스키마 노드를 활성화할 때 사이드바를 펼치면서 "객체 탐색" 탭을 엽니다.',
openTabsRestoreMode: "실행 시 탭 복원",
openTabsRestoreModeDescription: "DBX가 지난번에 열려 있던 탭을 복원하는 방법을 선택하세요.",
openTabsRestoreModeAll: "모든 탭 유지",
+2 -2
View File
@@ -6159,8 +6159,8 @@ export default withEnglishFallback({
sidebarObjectDisplaySimpleDescription: "Listar tabelas e views diretamente sob cada banco de dados ou schema para reduzir os níveis intermediários.",
autoSelectActiveSidebarNode: "Sempre selecionar o item aberto",
autoSelectActiveSidebarNodeDescription: "Ao alternar abas, selecionar a tabela, coleção ou arquivo SQL correspondente visível na barra lateral.",
sidebarOpenDatabaseOnSingleClick: "Explorar objetos ao clicar no banco de dados",
sidebarOpenDatabaseOnSingleClickDescription: 'Quando ativado, um clique único em um nó de banco de dados ou esquema expande a barra lateral e abre a aba "Explorar Objetos".',
sidebarBrowseObjectsOnDatabaseActivation: "Explorar objetos ao ativar um banco de dados",
sidebarBrowseObjectsOnDatabaseActivationDescription: 'Quando ativado, ativar um nó de banco de dados ou esquema com o modo selecionado de clique simples ou duplo também abre a aba "Explorar Objetos".',
openTabsRestoreMode: "Restaurar abas ao iniciar",
openTabsRestoreModeDescription: "Escolha como o DBX restaura as abas que estavam abertas na última vez.",
openTabsRestoreModeAll: "Manter todas as abas",
+2 -2
View File
@@ -6469,8 +6469,8 @@ export default withEnglishFallback({
routineSourceOpenModeDialogDescription: "在弹出对话框中打开源码,方便快速查看和编辑。",
autoSelectActiveSidebarNode: "始终选中已打开项目",
autoSelectActiveSidebarNodeDescription: "切换标签页时,在侧边栏选中匹配的可见表、集合或 SQL 文件。",
sidebarOpenDatabaseOnSingleClick: "单击数据库时浏览对象",
sidebarOpenDatabaseOnSingleClickDescription: "开启后,单击数据库或模式节点时,会同时展开侧边栏并打开“浏览对象”标签页。",
sidebarBrowseObjectsOnDatabaseActivation: "激活数据库节点时浏览对象",
sidebarBrowseObjectsOnDatabaseActivationDescription: "开启后,按照所选的单击或双击激活方式操作数据库或模式节点时,会同时展开侧边栏并打开“浏览对象”标签页。",
openTabsRestoreMode: "启动时恢复标签页",
openTabsRestoreModeDescription: "选择 DBX 启动时如何恢复上次打开的标签页。",
openTabsRestoreModeAll: "保留所有标签",
+2 -2
View File
@@ -5475,8 +5475,8 @@ export default withEnglishFallback({
sidebarObjectDisplaySimpleDescription: "直接在每個資料庫或 schema 下列出資料表與檢視,減少中間層級。",
autoSelectActiveSidebarNode: "一律選取已開啟項目",
autoSelectActiveSidebarNodeDescription: "切換分頁時,在側邊欄選取相符的可見資料表、集合或 SQL 檔案。",
sidebarOpenDatabaseOnSingleClick: "單擊資料庫時瀏覽物件",
sidebarOpenDatabaseOnSingleClickDescription: "開啟後,單擊資料庫或結構描述節點時,會同時展開側邊欄並開啟「瀏覽物件」分頁。",
sidebarBrowseObjectsOnDatabaseActivation: "啟用資料庫節點時瀏覽物件",
sidebarBrowseObjectsOnDatabaseActivationDescription: "開啟後,依照所選的單擊或雙擊啟用方式操作資料庫或結構描述節點時,會同時展開側邊欄並開啟「瀏覽物件」分頁。",
openTabsRestoreMode: "啟動時還原分頁",
openTabsRestoreModeDescription: "選擇 DBX 啟動時如何還原上次開啟的分頁。",
openTabsRestoreModeAll: "保留所有分頁",
+30 -26
View File
@@ -5,7 +5,7 @@ import {
objectSourceKindForTreeNode,
objectSourceTargetForTreeNode,
shouldActivateTreeNodeOnSingleClick,
shouldOpenObjectBrowserOnSingleClick,
shouldBrowseObjectsOnDatabaseActivation,
shouldRunTreeNodeRowAction,
treeNodeRowAction,
treeNodeRowDoubleClickAction,
@@ -90,51 +90,55 @@ describe("treeNodeClick", () => {
expect(treeNodeRowDoubleClickAction("connection", false, "double", false, "postgres", true)).toBe("none");
});
it("opens the object browser on single click when the database single-click switch is on", () => {
it("browses objects on single-click activation only when enabled", () => {
expect(treeNodeRowAction("database", true, "single", "postgres", true, true)).toBe("open-object-browser-and-expand");
expect(treeNodeRowAction("database", false, "single", "postgres", true, true)).toBe("open-object-browser");
expect(treeNodeRowAction("schema", true, "single", "postgres", true, true)).toBe("open-object-browser-and-expand");
expect(treeNodeRowAction("object-browser", false, "single", "postgres", true, true)).toBe("open-object-browser");
expect(treeNodeRowAction("database", true, "single", "postgres", false, true)).toBe("toggle");
expect(treeNodeRowAction("schema", true, "single", "postgres", false, true)).toBe("toggle");
});
it("lets the database single-click switch override double-click activation", () => {
expect(treeNodeRowAction("database", true, "double", "postgres", true, true)).toBe("open-object-browser-and-expand");
expect(treeNodeRowAction("schema", false, "double", "postgres", true, true)).toBe("open-object-browser");
it("keeps the first click selection-only in double-click activation", () => {
expect(treeNodeRowAction("database", true, "double", "postgres", true, true)).toBe("none");
expect(treeNodeRowAction("schema", true, "double", "postgres", true, true)).toBe("none");
expect(treeNodeRowAction("database", true, "double", "postgres", false, true)).toBe("none");
});
it("does not reopen the object browser when the single-click switch already handled the gesture", () => {
it("does not run a second database action after single-click activation", () => {
expect(treeNodeRowDoubleClickAction("database", true, "single", true, "postgres", false, true)).toBe("none");
expect(treeNodeRowDoubleClickAction("schema", true, "double", true, "postgres", false, true)).toBe("none");
expect(treeNodeRowDoubleClickAction("object-browser", true, "single", false, "postgres", false, true)).toBe("none");
expect(treeNodeRowDoubleClickAction("database", true, "single", true, "postgres", false, false)).toBe("none");
expect(treeNodeRowDoubleClickAction("schema", true, "single", true, "postgres", false, true)).toBe("none");
});
it("keeps the existing double-click fallback when single-click browsing is unavailable", () => {
expect(treeNodeRowDoubleClickAction("database", true, "single", true, "postgres", false, false)).toBe("open-object-browser");
it("browses objects on double-click activation only when enabled", () => {
expect(treeNodeRowDoubleClickAction("database", true, "double", true, "postgres", false, true)).toBe("open-object-browser-and-expand");
expect(treeNodeRowDoubleClickAction("schema", true, "double", false, "postgres", false, true)).toBe("open-object-browser");
expect(treeNodeRowDoubleClickAction("database", true, "double", true, "postgres", false, false)).toBe("toggle");
expect(treeNodeRowDoubleClickAction("schema", true, "double", false, "postgres", false, false)).toBe("none");
});
it("falls back to expansion when object browsing is unsupported", () => {
expect(treeNodeRowAction("database", true, "single", "postgres", true, false)).toBe("toggle");
expect(treeNodeRowAction("database", true, "double", "postgres", true, false)).toBe("none");
expect(treeNodeRowDoubleClickAction("database", false, "double", true, "postgres", false, true)).toBe("toggle");
});
it("keeps the original single-click behavior when the database single-click switch is off or unsupported", () => {
expect(treeNodeRowAction("database", true, "single", "postgres", false, true)).toBe("toggle");
expect(treeNodeRowAction("database", true, "double", "postgres", false, true)).toBe("none");
// Capability off (e.g. unsupported database type) falls back to the previous behavior.
expect(treeNodeRowAction("database", true, "single", "postgres", true, false)).toBe("toggle");
expect(treeNodeRowAction("database", true, "double", "postgres", true, false)).toBe("none");
});
it("does not route non-database nodes through the database single-click switch", () => {
it("does not route non-database nodes through the database activation switch", () => {
expect(treeNodeRowAction("table", false, "single", "postgres", true, true)).toBe("open-data");
expect(treeNodeRowAction("table", false, "double", "postgres", true, true)).toBe("none");
expect(shouldOpenObjectBrowserOnSingleClick("table", true)).toBe(false);
expect(shouldOpenObjectBrowserOnSingleClick("database", true)).toBe(true);
expect(shouldOpenObjectBrowserOnSingleClick("database", false)).toBe(false);
expect(shouldOpenObjectBrowserOnSingleClick("mongo-db", true)).toBe(true);
expect(shouldOpenObjectBrowserOnSingleClick("mongo-db", false)).toBe(false);
expect(shouldBrowseObjectsOnDatabaseActivation("table", true)).toBe(false);
expect(shouldBrowseObjectsOnDatabaseActivation("database", true)).toBe(true);
expect(shouldBrowseObjectsOnDatabaseActivation("database", false)).toBe(false);
expect(shouldBrowseObjectsOnDatabaseActivation("mongo-db", true)).toBe(true);
expect(shouldBrowseObjectsOnDatabaseActivation("object-browser", true)).toBe(false);
expect(treeNodeRowDoubleClickAction("object-browser", true, "double", false, "postgres", false, false)).toBe("open-object-browser");
});
it("opens the MongoDB object browser from mongo-db nodes like SQL databases", () => {
expect(treeNodeRowAction("mongo-db", true, "single", "mongodb", true, true)).toBe("open-object-browser-and-expand");
expect(treeNodeRowAction("mongo-db", true, "single", "mongodb", false, true)).toBe("toggle");
expect(treeNodeRowDoubleClickAction("mongo-db", true, "single", true, "mongodb")).toBe("open-object-browser");
expect(treeNodeRowDoubleClickAction("mongo-db", true, "single", true, "mongodb", false, true)).toBe("none");
expect(treeNodeRowDoubleClickAction("mongo-db", true, "double", true, "mongodb", false, true)).toBe("open-object-browser-and-expand");
});
it("expands package containers while preserving source behavior for leaf packages", () => {
@@ -65,7 +65,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"routineSourceOpenMode",
"sidebarTableSearchEnabled",
"autoSelectActiveSidebarNode",
"sidebarOpenDatabaseOnSingleClick",
"sidebarBrowseObjectsOnDatabaseActivation",
"openTabsRestoreMode",
"disconnectTabHandlingMode",
"dataTabReuseMode",
@@ -170,7 +170,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
{ id: "navigation-object-info", category: "navigation", titleKey: "settings.sidebarObjectInfoMode", descriptionKey: "settings.sidebarObjectInfoModeDescription", targetId: "navigation" },
{ id: "navigation-table-search", category: "navigation", titleKey: "settings.sidebarTableSearchEnabled", descriptionKey: "settings.sidebarTableSearchEnabledDescription", targetId: "navigation" },
{ id: "navigation-active-node", category: "navigation", titleKey: "settings.autoSelectActiveSidebarNode", descriptionKey: "settings.autoSelectActiveSidebarNodeDescription", targetId: "navigation" },
{ id: "navigation-open-database-on-single-click", category: "navigation", titleKey: "settings.sidebarOpenDatabaseOnSingleClick", descriptionKey: "settings.sidebarOpenDatabaseOnSingleClickDescription", targetId: "navigation" },
{ id: "navigation-browse-objects-on-database-activation", category: "navigation", titleKey: "settings.sidebarBrowseObjectsOnDatabaseActivation", descriptionKey: "settings.sidebarBrowseObjectsOnDatabaseActivationDescription", targetId: "navigation" },
{ id: "navigation-tabs-restore", category: "navigation", titleKey: "settings.openTabsRestoreMode", descriptionKey: "settings.openTabsRestoreModeDescription", targetId: "navigation" },
{ id: "navigation-sidebar-scroll", category: "navigation", titleKey: "settings.sidebarAllowHorizontalScroll", descriptionKey: "settings.sidebarAllowHorizontalScrollDescription", targetId: "navigation" },
{ id: "navigation-sidebar-indent", category: "navigation", titleKey: "settings.sidebarIndent", descriptionKey: "settings.sidebarIndentDescription", targetId: "navigation" },
+11 -13
View File
@@ -49,11 +49,10 @@ export function isRepeatableNavigationTreeNode(type: TreeNodeType): boolean {
export function shouldActivateTreeNodeOnSingleClick(type: TreeNodeType, activation: SidebarActivation = "single"): boolean {
return activation !== "double" || isDirectNavigationTreeNode(type);
}
const objectBrowserNodeTypes = new Set<TreeNodeType>(["database", "schema", "object-browser", "mongo-db"]);
const databaseActivationNodeTypes = new Set<TreeNodeType>(["database", "schema", "mongo-db"]);
/** 开关“单击数据库同时打开数据库项目”命中时,单击数据库类节点直接打开对象浏览器标签页。 */
export function shouldOpenObjectBrowserOnSingleClick(type: TreeNodeType, enabled: boolean): boolean {
return enabled && objectBrowserNodeTypes.has(type);
export function shouldBrowseObjectsOnDatabaseActivation(type: TreeNodeType, enabled: boolean): boolean {
return enabled && databaseActivationNodeTypes.has(type);
}
const sourceNodeTypes = new Set<TreeNodeType>(["materialized_view", "procedure", "function", "trigger", "event", "sequence", "synonym", "job", "package", "package-body", "type", "type-body"]);
const savedSqlNodeTypes = new Set<TreeNodeType>(["saved-sql-file"]);
@@ -132,12 +131,11 @@ function canOpenTreeNodeSource(type: TreeNodeType, dbType?: DatabaseType): boole
return true;
}
export function treeNodeRowAction(type: TreeNodeType, canExpand: boolean, activation: SidebarActivation = "single", dbType?: DatabaseType, openDatabaseOnSingleClick = false, canOpenObjectBrowser = false): TreeNodeRowAction {
// “单击数据库同时打开数据库项目”开关优先于激活方式:单击即等效双击的打开行为。
if (openDatabaseOnSingleClick && canOpenObjectBrowser && shouldOpenObjectBrowserOnSingleClick(type, true)) {
export function treeNodeRowAction(type: TreeNodeType, canExpand: boolean, activation: SidebarActivation = "single", dbType?: DatabaseType, browseObjectsOnDatabaseActivation = false, canOpenObjectBrowser = false): TreeNodeRowAction {
if (!shouldActivateTreeNodeOnSingleClick(type, activation)) return "none";
if (canOpenObjectBrowser && shouldBrowseObjectsOnDatabaseActivation(type, browseObjectsOnDatabaseActivation)) {
return canExpand ? "open-object-browser-and-expand" : "open-object-browser";
}
if (!shouldActivateTreeNodeOnSingleClick(type, activation)) return "none";
if (type === "extension") return "open-extension-details";
if (savedSqlNodeTypes.has(type)) return "open-saved-sql";
if (dataNodeTypes.has(type)) return "open-data";
@@ -158,26 +156,26 @@ export function shouldRunTreeNodeRowAction(action: TreeNodeRowAction, clickDetai
return action !== "none" && clickDetail <= 1;
}
export function treeNodeRowDoubleClickAction(type: TreeNodeType, canOpenObjectBrowser: boolean, activation: SidebarActivation = "single", canExpand = false, dbType?: DatabaseType, canOpenDatabaseBrowser = false, openDatabaseOnSingleClick = false): TreeNodeRowDoubleClickAction {
export function treeNodeRowDoubleClickAction(type: TreeNodeType, canOpenObjectBrowser: boolean, activation: SidebarActivation = "single", canExpand = false, dbType?: DatabaseType, canOpenDatabaseBrowser = false, browseObjectsOnDatabaseActivation = false): TreeNodeRowDoubleClickAction {
// Single-click activation already handles the first click in a dblclick
// sequence. Only double-click activation needs a second-stage table action.
if (type === "table") return activation === "double" ? "activate-data" : "none";
if (openDatabaseOnSingleClick && canOpenObjectBrowser && shouldOpenObjectBrowserOnSingleClick(type, true)) return "none";
// 双击激活模式下,双击连接节点应当展开树(与单击模式下单击展开一致),
// “打开数据库浏览”只保留给单击激活模式下的双击手势。
if (type === "connection" && canOpenDatabaseBrowser && activation !== "double") return "open-database-browser";
if (activation === "double") {
if (canOpenObjectBrowser && shouldBrowseObjectsOnDatabaseActivation(type, browseObjectsOnDatabaseActivation)) {
return canExpand ? "open-object-browser-and-expand" : "open-object-browser";
}
if (canOpenObjectBrowser && type === "object-browser") return "open-object-browser";
if (type === "extension") return "open-extension-details";
if (dataNodeTypes.has(type)) return "open-data";
if (type === "type" && customTypeCapabilities(dbType).details) return canExpand ? "toggle" : "none";
if (sourceNodeTypes.has(type) && canOpenTreeNodeSource(type, dbType)) return "open-source";
if (savedSqlNodeTypes.has(type)) return "open-saved-sql";
if (toggleLeafNodeTypes.has(type)) return "toggle";
if (canOpenObjectBrowser && objectBrowserNodeTypes.has(type) && canExpand) return "open-object-browser-and-expand";
if (canOpenObjectBrowser && objectBrowserNodeTypes.has(type)) return "open-object-browser";
if (canExpand) return "toggle";
}
if (canOpenObjectBrowser && objectBrowserNodeTypes.has(type)) return "open-object-browser";
return "none";
}
+9 -4
View File
@@ -660,7 +660,7 @@ export interface EditorSettings {
sidebarTableSearchLocal: boolean;
sidebarGlobalSearchLocal: boolean;
autoSelectActiveSidebarNode: boolean;
sidebarOpenDatabaseOnSingleClick: boolean;
sidebarBrowseObjectsOnDatabaseActivation: boolean;
openTabsRestoreMode: OpenTabsRestoreMode;
disconnectTabHandlingMode: DisconnectTabHandlingMode;
dataTabReuseMode: DataTabReuseMode;
@@ -877,7 +877,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
sidebarTableSearchLocal: true,
sidebarGlobalSearchLocal: false,
autoSelectActiveSidebarNode: false,
sidebarOpenDatabaseOnSingleClick: false,
sidebarBrowseObjectsOnDatabaseActivation: false,
openTabsRestoreMode: "all",
disconnectTabHandlingMode: "close-tabs",
dataTabReuseMode: DEFAULT_DATA_TAB_REUSE_MODE,
@@ -1284,7 +1284,12 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
sidebarTableSearchLocal: typeof settings.sidebarTableSearchLocal === "boolean" ? settings.sidebarTableSearchLocal : DEFAULT_EDITOR_SETTINGS.sidebarTableSearchLocal,
sidebarGlobalSearchLocal: typeof settings.sidebarGlobalSearchLocal === "boolean" ? settings.sidebarGlobalSearchLocal : DEFAULT_EDITOR_SETTINGS.sidebarGlobalSearchLocal,
autoSelectActiveSidebarNode: settings.autoSelectActiveSidebarNode ?? DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode,
sidebarOpenDatabaseOnSingleClick: typeof settings.sidebarOpenDatabaseOnSingleClick === "boolean" ? settings.sidebarOpenDatabaseOnSingleClick : DEFAULT_EDITOR_SETTINGS.sidebarOpenDatabaseOnSingleClick,
sidebarBrowseObjectsOnDatabaseActivation:
typeof settings.sidebarBrowseObjectsOnDatabaseActivation === "boolean"
? settings.sidebarBrowseObjectsOnDatabaseActivation
: typeof (settings as Partial<EditorSettings> & { sidebarOpenDatabaseOnSingleClick?: unknown }).sidebarOpenDatabaseOnSingleClick === "boolean"
? (settings as Partial<EditorSettings> & { sidebarOpenDatabaseOnSingleClick: boolean }).sidebarOpenDatabaseOnSingleClick
: DEFAULT_EDITOR_SETTINGS.sidebarBrowseObjectsOnDatabaseActivation,
openTabsRestoreMode: normalizeOpenTabsRestoreMode(
(settings as Partial<EditorSettings>).openTabsRestoreMode,
(
@@ -1907,7 +1912,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.sidebarTableSearchLocal !== undefined) editorSettings.value.sidebarTableSearchLocal = partial.sidebarTableSearchLocal;
if (partial.sidebarGlobalSearchLocal !== undefined) editorSettings.value.sidebarGlobalSearchLocal = partial.sidebarGlobalSearchLocal;
if (partial.autoSelectActiveSidebarNode !== undefined) editorSettings.value.autoSelectActiveSidebarNode = partial.autoSelectActiveSidebarNode;
if (partial.sidebarOpenDatabaseOnSingleClick !== undefined) editorSettings.value.sidebarOpenDatabaseOnSingleClick = partial.sidebarOpenDatabaseOnSingleClick === true;
if (partial.sidebarBrowseObjectsOnDatabaseActivation !== undefined) editorSettings.value.sidebarBrowseObjectsOnDatabaseActivation = partial.sidebarBrowseObjectsOnDatabaseActivation === true;
if (partial.openTabsRestoreMode !== undefined) editorSettings.value.openTabsRestoreMode = normalizeOpenTabsRestoreMode(partial.openTabsRestoreMode);
if (partial.disconnectTabHandlingMode !== undefined) editorSettings.value.disconnectTabHandlingMode = normalizeDisconnectTabHandlingMode(partial.disconnectTabHandlingMode);
if (partial.dataTabReuseMode !== undefined) editorSettings.value.dataTabReuseMode = normalizeDataTabReuseMode(partial.dataTabReuseMode);