feat(desktop): add pinned auto-show table info panel

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-09-06 15:01:27 +08:00
1 parent 17441ad0d2
commit 0ef8369e2b
14 files changed
+104

No files matched your search

@@ -47,6 +47,7 @@ import {
Eraser,
Columns3,
PencilRuler,
Pin,
Settings2,
WandSparkles,
Camera,
@@ -493,6 +494,7 @@ interface DataGridProps {
primaryKeys: string[];
};
tableInfoTab?: TableInfoTab;
autoShowTableInfo?: boolean;
pageOffset?: number;
pageLimit?: number;
countSql?: string;
@@ -11005,6 +11007,8 @@ function clampCellDetailPanelSize(value: number, layout = cellDetailPanelLayout.
// module-global leaks the drawer into other kept-alive tabs.
const showTableInfo = ref(false);
const activeTableInfoTab = ref<TableInfoTab>(settingsStore.editorSettings.tableInfoActiveTab);
const canPinTableInfo = computed(() => props.context === "table-data");
const tableInfoDrawerPinned = computed(() => settingsStore.editorSettings.tableInfoDrawerPinned);
const ddlContent = ref("");
const tableInfoColumns = ref<ColumnInfo[]>(props.tableMeta?.columns ?? []);
const tableInfoColumnsLoading = ref(false);
@@ -11337,6 +11341,10 @@ async function toggleTableInfo(tab?: TableInfoTab) {
await selectTableInfoTab(nextTab);
}
function toggleTableInfoDrawerPinned() {
settingsStore.updateEditorSettings({ tableInfoDrawerPinned: !tableInfoDrawerPinned.value });
}
async function selectTableInfoTab(tab: TableInfoTab) {
const tabSupported = tableInfoTabs.value.some((item) => item.id === tab);
const nextTab = tabSupported ? tab : tableInfoTabs.value[0]?.id;
@@ -11358,6 +11366,16 @@ watch(
{ immediate: true },
);
watch(
() => props.autoShowTableInfo,
(shouldShow) => {
if (!shouldShow || !props.tableMeta) return;
showTableInfo.value = true;
void selectTableInfoTab(activeTableInfoTab.value);
},
{ immediate: true },
);
async function fetchDdl(force = settingsStore.editorSettings.refreshDdlOnOpen) {
if (!props.connectionId || !props.tableMeta) return;
showTableInfo.value = true;
@@ -11648,6 +11666,7 @@ watch(
if (showIndexIndicatorsInHeader.value && canShowTableIndexes.value && currentIndexTableIdentity.value) {
void fetchIndexes();
}
if (props.autoShowTableInfo && props.tableMeta) showTableInfo.value = true;
if (showTableInfo.value) selectTableInfoTab(activeTableInfoTab.value);
if (showTableInfo.value) void fetchTableOwner();
},
@@ -14320,6 +14339,19 @@ function openGridSnapshot() {
<PencilRuler class="w-3 h-3" />
<span class="table-info-action-label">{{ t("contextMenu.editStructure") }}</span>
</Button>
<Button
v-if="canPinTableInfo"
variant="ghost"
size="icon"
class="h-5 w-5"
:class="{ 'bg-accent text-primary': tableInfoDrawerPinned }"
:title="tableInfoDrawerPinned ? t('grid.unpinTableInfo') : t('grid.pinTableInfo')"
:aria-label="tableInfoDrawerPinned ? t('grid.unpinTableInfo') : t('grid.pinTableInfo')"
:aria-pressed="tableInfoDrawerPinned"
@click="toggleTableInfoDrawerPinned"
>
<Pin class="w-3 h-3" :class="{ 'fill-current': tableInfoDrawerPinned }" />
</Button>
<Button variant="ghost" size="icon" class="h-5 w-5" @click="showTableInfo = false">
<X class="w-3 h-3" />
</Button>
@@ -2294,6 +2294,7 @@ defineExpose({
:execution-database="activeDataTabExecutionDatabase"
:table-meta="activeDataTabTableMeta"
:table-info-tab="activeTab.tableInfoTab"
:auto-show-table-info="settingsStore.editorSettings.tableInfoDrawerPinned"
:page-offset="activeTab.resultPageOffset"
:page-limit="activeTab.resultPageLimit"
:total-row-count="activeTab.resultTotalRowCount"
@@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import en from "../locales/en";
import es from "../locales/es";
import it_ from "../locales/it";
import ja from "../locales/ja";
import ko from "../locales/ko";
import ptBR from "../locales/pt-BR";
import tr from "../locales/tr";
import zhCN from "../locales/zh-CN";
import zhTW from "../locales/zh-TW";
const locales: Array<[string, Record<string, unknown>]> = [
["en", en as Record<string, unknown>],
["es", es],
["it", it_],
["ja", ja],
["ko", ko],
["pt-BR", ptBR],
["tr", tr],
["zh-CN", zhCN],
["zh-TW", zhTW],
];
function gridEntry(locale: Record<string, unknown>, key: string): unknown {
const grid = locale["grid"];
if (!grid || typeof grid !== "object") return undefined;
return (grid as Record<string, unknown>)[key];
}
describe("table-info pin i18n messages", () => {
it.each(["pinTableInfo", "unpinTableInfo"] as const)("en resolves grid.%s to text", (key) => {
expect(gridEntry(en as Record<string, unknown>, key), `grid.${key} missing from locales/en.ts`).toBeTypeOf("string");
});
it.each(locales.slice(1))("%s translates table-info pin labels instead of using the English fallback", (_name, locale) => {
for (const key of ["pinTableInfo", "unpinTableInfo"] as const) {
const value = gridEntry(locale, key);
expect(value, `${_name} grid.${key}`).toBeTypeOf("string");
expect(value).not.toBe(gridEntry(en as Record<string, unknown>, key));
}
});
});
+2
View File
@@ -2160,6 +2160,8 @@ export default {
rowsPerPageShort: " rows",
columnDetails: "Column Details",
tableInfo: "Table Info",
pinTableInfo: "Pin table info panel",
unpinTableInfo: "Unpin table info panel",
tableInfoColumns: "Columns",
tableInfoIndexes: "Indexes",
columnPrimaryIndex: "Primary key index",
+2
View File
@@ -2020,6 +2020,8 @@ export default withEnglishFallback({
searchColumn: "Buscar columna/comentario...",
noColumnsFound: "No se encontraron columnas",
tableInfo: "Información de tabla",
pinTableInfo: "Fijar el panel de información de la tabla",
unpinTableInfo: "Desfijar el panel de información de la tabla",
tableInfoColumns: "Columnas",
tableInfoIndexes: "Índices",
columnPrimaryIndex: "Índice de clave primaria",
+2
View File
@@ -2007,6 +2007,8 @@ export default withEnglishFallback({
rowsPerPageShort: " righe",
columnDetails: "Dettagli Colonna",
tableInfo: "Informazioni Tabella",
pinTableInfo: "Blocca il pannello delle informazioni sulla tabella",
unpinTableInfo: "Sblocca il pannello delle informazioni sulla tabella",
tableInfoColumns: "Colonne",
tableInfoIndexes: "Indici",
columnPrimaryIndex: "Indice chiave primaria",
+2
View File
@@ -2019,6 +2019,8 @@ export default withEnglishFallback({
rowsPerPageShort: " 行",
columnDetails: "列詳細",
tableInfo: "テーブル情報",
pinTableInfo: "テーブル情報パネルを固定",
unpinTableInfo: "テーブル情報パネルの固定を解除",
tableInfoColumns: "列",
tableInfoIndexes: "インデックス",
columnPrimaryIndex: "主キーインデックス",
+2
View File
@@ -1983,6 +1983,8 @@ export default withEnglishFallback({
rowsPerPageShort: "행",
columnDetails: "컬럼 상세",
tableInfo: "테이블 정보",
pinTableInfo: "테이블 정보 패널 고정",
unpinTableInfo: "테이블 정보 패널 고정 해제",
tableInfoColumns: "컬럼",
tableInfoIndexes: "인덱스",
columnPrimaryIndex: "기본 키 인덱스",
+2
View File
@@ -2009,6 +2009,8 @@ export default withEnglishFallback({
rowsPerPageShort: " linhas",
columnDetails: "Detalhes da Coluna",
tableInfo: "Informações da Tabela",
pinTableInfo: "Fixar painel de informações da tabela",
unpinTableInfo: "Desafixar painel de informações da tabela",
tableInfoColumns: "Colunas",
tableInfoIndexes: "Índices",
columnPrimaryIndex: "Índice de chave primária",
+2
View File
@@ -2154,6 +2154,8 @@ export default withEnglishFallback({
rowsPerPageShort: " satır",
columnDetails: "Sütun Ayrıntıları",
tableInfo: "Tablo Bilgisi",
pinTableInfo: "Tablo bilgileri panelini sabitle",
unpinTableInfo: "Tablo bilgileri panelinin sabitlemesini kaldır",
tableInfoColumns: "Sütunlar",
tableInfoIndexes: "Dizinler",
columnPrimaryIndex: "Birincil anahtar dizini",
+2
View File
@@ -2084,6 +2084,8 @@ export default withEnglishFallback({
rowsPerPageShort: " 行/页",
columnDetails: "列详情",
tableInfo: "表属性",
pinTableInfo: "固定表属性面板",
unpinTableInfo: "取消固定表属性面板",
tableInfoColumns: "字段",
tableInfoIndexes: "索引",
columnPrimaryIndex: "主键索引",
+2
View File
@@ -2012,6 +2012,8 @@ export default withEnglishFallback({
rowsPerPageShort: " 筆/頁",
columnDetails: "欄位詳情",
tableInfo: "資料表屬性",
pinTableInfo: "固定資料表屬性面板",
unpinTableInfo: "取消固定資料表屬性面板",
tableInfoColumns: "欄位",
tableInfoIndexes: "索引",
columnPrimaryIndex: "主鍵索引",
@@ -23,6 +23,13 @@ describe("normalizeEditorSettings", () => {
expect(normalizeEditorSettings({ refreshDdlOnOpen: "true" } as any).refreshDdlOnOpen).toBe(false);
});
it("keeps table-info drawer pinning disabled unless explicitly enabled", () => {
expect(normalizeEditorSettings({}).tableInfoDrawerPinned).toBe(false);
expect(normalizeEditorSettings({ tableInfoDrawerPinned: true }).tableInfoDrawerPinned).toBe(true);
expect(normalizeEditorSettings({ tableInfoDrawerPinned: false }).tableInfoDrawerPinned).toBe(false);
expect(normalizeEditorSettings({ tableInfoDrawerPinned: "true" } as any).tableInfoDrawerPinned).toBe(false);
});
it("enables SQL variable substitution by default and only preserves booleans", () => {
expect(normalizeEditorSettings({}).sqlVariableSubstitutionEnabled).toBe(true);
expect(normalizeEditorSettings({ sqlVariableSubstitutionEnabled: true }).sqlVariableSubstitutionEnabled).toBe(true);
+4
View File
@@ -776,6 +776,7 @@ export interface EditorSettings {
tableFontSize: number;
structureEditorDensity: StructureEditorDensity;
tableInfoActiveTab: TableInfoTab;
tableInfoDrawerPinned: boolean;
tableInfoDrawerWidth: number;
cellDetailDrawerWidth: number;
cellDetailPanelLayout: CellDetailPanelLayout;
@@ -1004,6 +1005,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
tableFontSize: TABLE_FONT_SIZE_DEFAULT,
structureEditorDensity: "compact",
tableInfoActiveTab: "ddl",
tableInfoDrawerPinned: false,
tableInfoDrawerWidth: 320,
cellDetailDrawerWidth: 380,
cellDetailPanelLayout: "bottom",
@@ -1448,6 +1450,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
tableFontSize: normalizeTableFontSize(settings.tableFontSize),
structureEditorDensity: normalizeStructureEditorDensity(settings.structureEditorDensity),
tableInfoActiveTab: normalizeTableInfoTab(settings.tableInfoActiveTab),
tableInfoDrawerPinned: settings.tableInfoDrawerPinned === true,
tableInfoDrawerWidth: normalizeDrawerWidth(settings.tableInfoDrawerWidth, 240, DEFAULT_EDITOR_SETTINGS.tableInfoDrawerWidth),
cellDetailDrawerWidth: normalizeDrawerWidth(settings.cellDetailDrawerWidth, 260, DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth),
cellDetailPanelLayout: normalizeCellDetailPanelLayout(settings.cellDetailPanelLayout),
@@ -2161,6 +2164,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.tableFontSize !== undefined) editorSettings.value.tableFontSize = normalizeTableFontSize(partial.tableFontSize);
if (partial.structureEditorDensity !== undefined) editorSettings.value.structureEditorDensity = normalizeStructureEditorDensity(partial.structureEditorDensity);
if (partial.tableInfoActiveTab !== undefined) editorSettings.value.tableInfoActiveTab = normalizeTableInfoTab(partial.tableInfoActiveTab);
if (partial.tableInfoDrawerPinned !== undefined) editorSettings.value.tableInfoDrawerPinned = partial.tableInfoDrawerPinned === true;
if (partial.tableInfoDrawerWidth !== undefined) editorSettings.value.tableInfoDrawerWidth = normalizeDrawerWidth(partial.tableInfoDrawerWidth, 240, DEFAULT_EDITOR_SETTINGS.tableInfoDrawerWidth);
if (partial.cellDetailDrawerWidth !== undefined) editorSettings.value.cellDetailDrawerWidth = normalizeDrawerWidth(partial.cellDetailDrawerWidth, 260, DEFAULT_EDITOR_SETTINGS.cellDetailDrawerWidth);
if (partial.cellDetailPanelLayout !== undefined) editorSettings.value.cellDetailPanelLayout = normalizeCellDetailPanelLayout(partial.cellDetailPanelLayout);