feat(grid): add column header tooltip toggle

This commit is contained in:
OOMEcho authored and GitHub committed 2026-09-24 22:33:50 +08:00
1 parent 3294ade2cc
commit cd24f69daa
21 files changed
+173 -36

No files matched your search

@@ -661,6 +661,7 @@ const debugLogCopied = ref(false);
const debugLogDownloaded = ref(false);
const editShowColumnCommentsInHeader = ref(settingsStore.editorSettings.showColumnCommentsInHeader);
const editShowColumnTypesInHeader = ref(settingsStore.editorSettings.showColumnTypesInHeader);
const editShowColumnHeaderTooltips = ref(settingsStore.editorSettings.showColumnHeaderTooltips);
const editShowResultSourceDatabase = ref(settingsStore.editorSettings.showResultSourceDatabase);
const editDataGridShowTransposeFieldMetadata = ref(settingsStore.editorSettings.dataGridShowTransposeFieldMetadata);
const editColorizeDataGridCellTypes = ref(settingsStore.editorSettings.colorizeDataGridCellTypes);
@@ -1003,6 +1004,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
tabSortMode: editTabSortMode.value,
showColumnCommentsInHeader: editShowColumnCommentsInHeader.value,
showColumnTypesInHeader: editShowColumnTypesInHeader.value,
showColumnHeaderTooltips: editShowColumnHeaderTooltips.value,
showResultSourceDatabase: editShowResultSourceDatabase.value,
dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata.value,
colorizeDataGridCellTypes: editColorizeDataGridCellTypes.value,
@@ -1649,6 +1651,7 @@ function syncEditorSettingsDraftFromStore() {
editTabSortMode.value = settingsStore.editorSettings.tabSortMode;
editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader;
editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader;
editShowColumnHeaderTooltips.value = settingsStore.editorSettings.showColumnHeaderTooltips;
editShowResultSourceDatabase.value = settingsStore.editorSettings.showResultSourceDatabase;
editDataGridShowTransposeFieldMetadata.value = settingsStore.editorSettings.dataGridShowTransposeFieldMetadata;
editColorizeDataGridCellTypes.value = settingsStore.editorSettings.colorizeDataGridCellTypes;
@@ -1783,6 +1786,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
tabSortMode: editTabSortMode,
showColumnCommentsInHeader: editShowColumnCommentsInHeader,
showColumnTypesInHeader: editShowColumnTypesInHeader,
showColumnHeaderTooltips: editShowColumnHeaderTooltips,
showResultSourceDatabase: editShowResultSourceDatabase,
dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata,
colorizeDataGridCellTypes: editColorizeDataGridCellTypes,
@@ -2308,6 +2312,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
historyRetention.reset();
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader;
editShowColumnHeaderTooltips.value = DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips;
editShowResultSourceDatabase.value = DEFAULT_EDITOR_SETTINGS.showResultSourceDatabase;
editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata;
editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes;
@@ -2416,6 +2421,7 @@ function resetAllDefaults() {
editSidebarTablePageSize.value = DEFAULT_SIDEBAR_TABLE_PAGE_SIZE;
editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader;
editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader;
editShowColumnHeaderTooltips.value = DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips;
editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata;
editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes;
// Reset the selection only; saved schemes survive a full settings reset.
@@ -7952,6 +7958,17 @@ onUnmounted(() => {
</div>
<Switch id="show-column-types-in-header" v-model="editShowColumnTypesInHeader" />
</div>
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-column-header-tooltips">
{{ t("settings.showColumnHeaderTooltips") }}
</Label>
<p class="text-xs text-muted-foreground">
{{ t("settings.showColumnHeaderTooltipsDescription") }}
</p>
</div>
<Switch id="show-column-header-tooltips" v-model="editShowColumnHeaderTooltips" />
</div>
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="space-y-1">
<Label for="show-result-source-database">
@@ -760,6 +760,7 @@ const dataGridTopbarOverflowActionCount = ref(0);
const dataGridTopbarExpandedRequiredWidth = ref(0);
const showColumnCommentsInHeader = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader);
const showColumnTypesInHeader = computed(() => settingsStore.editorSettings.showColumnTypesInHeader);
const showColumnHeaderTooltips = computed(() => settingsStore.editorSettings.showColumnHeaderTooltips !== false);
const showTransposeFieldMetadata = computed(() => settingsStore.editorSettings.dataGridShowTransposeFieldMetadata);
const showIndexIndicatorsInHeader = computed(() => settingsStore.editorSettings.showIndexIndicatorsInHeader !== false);
const indexes = ref<IndexInfo[]>([]);
@@ -12740,7 +12741,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
:dark="isDark"
:frozen="col.visibleColIdx < frozenColumnCount"
:frozen-separator="frozenColumnCount > 0 && col.visibleColIdx === frozenColumnCount - 1"
:tooltip-disabled="columnHeaderTooltipsDisabled"
:tooltip-disabled="columnHeaderTooltipsDisabled || !showColumnHeaderTooltips"
:column-type="headerColumnType(col.name, col.actualColIdx)"
:column-comment="headerColumnComment(col.name, col.actualColIdx)"
:show-type-line="reserveColumnTypeLine"
@@ -1,11 +1,13 @@
// @vitest-environment happy-dom
import { readFileSync } from "node:fs";
import { nextTick } from "vue";
import { createApp, defineComponent, h, nextTick } from "vue";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPinia, setActivePinia } from "pinia";
import { dispatch, findAll, findOne, hostText, mountComponent } from "./vueHostHarness";
import type { DataGridCellDetail } from "@/lib/dataGrid/dataGridDetail";
const settingsMock = vi.hoisted(() => ({ store: null as any }));
const mocks = vi.hoisted(() => ({
editor: { create: vi.fn(), destroy: vi.fn(), setValue: vi.fn(), openSearch: vi.fn() },
updateSettings: vi.fn(),
@@ -16,40 +18,15 @@ const mocks = vi.hoisted(() => ({
toast: vi.fn(),
}));
vi.mock("vue-i18n", () => ({ useI18n: () => ({ t: (key: string) => key }) }));
vi.mock("vue-i18n", () => ({
useI18n: () => ({ t: (key: string) => key, locale: { value: "en" } }),
// DataGrid.vue's import graph reaches @/i18n, which builds a real i18n instance at module scope.
createI18n: () => ({ install: () => {}, global: { t: (key: string) => key, locale: { value: "en" } } }),
}));
vi.mock("@lucide/vue", async () => {
const { createPassthroughStub } = await import("./vueHostHarness");
const icon = createPassthroughStub("Icon", "i");
return {
Check: icon,
CaseSensitive: icon,
ChevronDown: icon,
ChevronUp: icon,
ChevronLeft: icon,
ChevronRight: icon,
ChevronsLeft: icon,
ChevronsRight: icon,
Download: icon,
ChevronsDown: icon,
Filter: icon,
Focus: icon,
FileDiff: icon,
Loader2: icon,
FileUp: icon,
Upload: icon,
Search: icon,
X: icon,
Code2: icon,
Copy: icon,
Eye: icon,
EyeOff: icon,
GripVertical: icon,
Info: icon,
Pencil: icon,
Plus: icon,
RotateCcw: icon,
Trash2: icon,
};
return new Proxy({}, { get: (_target, key) => (key === "__esModule" ? false : icon), has: () => true });
});
vi.mock("@/components/ui/button", async () => ({ Button: (await import("./vueHostHarness")).createPassthroughStub("Button", "button") }));
@@ -72,7 +49,7 @@ vi.mock("@/components/ui/dropdown-menu", async () => {
});
vi.mock("@/components/ui/popover", async () => {
const { createPassthroughStub } = await import("./vueHostHarness");
return { Popover: createPassthroughStub("Popover"), PopoverContent: createPassthroughStub("PopoverContent"), PopoverTrigger: createPassthroughStub("PopoverTrigger") };
return { Popover: createPassthroughStub("Popover"), PopoverAnchor: createPassthroughStub("PopoverAnchor"), PopoverContent: createPassthroughStub("PopoverContent"), PopoverTrigger: createPassthroughStub("PopoverTrigger") };
});
vi.mock("@/components/ui/tooltip", async () => {
const { createPassthroughStub } = await import("./vueHostHarness");
@@ -90,8 +67,24 @@ vi.mock("@/components/ui/LightTooltip.vue", async () => ({ default: (await impor
vi.mock("@/components/grid/TemporalCellEditor.vue", async () => ({ default: (await import("./vueHostHarness")).createPassthroughStub("TemporalCellEditor") }));
vi.mock("@/components/grid/DataGridValueTransform.vue", async () => ({ default: (await import("./vueHostHarness")).createPassthroughStub("DataGridValueTransform") }));
vi.mock("@/composables/useCellDetailEditor", () => ({ useCellDetailEditor: () => mocks.editor }));
vi.mock("@/composables/useTheme", () => ({ useTheme: () => ({ isDark: { value: false }, themePalette: { value: {} } }) }));
vi.mock("@/stores/settingsStore", () => ({ useSettingsStore: () => ({ editorSettings: { cellDetailJsonFormatted: true, theme: "default", fontSize: 13, fontFamily: "monospace" }, updateEditorSettings: mocks.updateSettings }) }));
vi.mock("@/composables/useTheme", async () => {
const { ref } = await import("vue");
// Real refs: DataGrid.vue both watches isDark and binds it to a Boolean prop.
const isDark = ref(false);
const themePalette = ref({});
return { useTheme: () => ({ isDark, themePalette }) };
});
vi.mock("@/stores/settingsStore", async () => {
const actual = await vi.importActual<typeof import("@/stores/settingsStore")>("@/stores/settingsStore");
const { reactive } = await import("vue");
// Reactive and complete: DataGrid.vue reads dozens of editor settings and must
// re-render when a test flips one.
settingsMock.store = {
editorSettings: reactive({ ...actual.DEFAULT_EDITOR_SETTINGS, cellDetailJsonFormatted: true, theme: "default", fontSize: 13, fontFamily: "monospace" }),
updateEditorSettings: mocks.updateSettings,
};
return { ...actual, useSettingsStore: () => settingsMock.store };
});
vi.mock("@/lib/dataGrid/geometryPreview", () => ({ isHexGeometry: () => false, renderWktOnCanvas: mocks.renderWkt }));
vi.mock("@/lib/common/clipboard", () => ({ copyToClipboard: mocks.copyToClipboard }));
vi.mock("@/composables/useToast", () => ({ useToast: () => ({ toast: mocks.toast }) }));
@@ -107,6 +100,7 @@ vi.mock("@/composables/useDataGridCellDetail", async () => {
import DataGridCellDetailDialog from "@/components/grid/DataGridCellDetailDialog.vue";
import DataGridCellDetailPanel from "@/components/grid/DataGridCellDetailPanel.vue";
import DataGrid from "@/components/grid/DataGrid.vue";
import DataGridColumnHeader from "@/components/grid/DataGridColumnHeader.vue";
import DataGridCopyColumnNamesDialog from "@/components/grid/DataGridCopyColumnNamesDialog.vue";
import DataGridFilterBuilder from "@/components/grid/DataGridFilterBuilder.vue";
@@ -1870,3 +1864,44 @@ describe("DataGridCopyColumnNamesDialog", () => {
expect(previewText(mounted)).toBe("id\ttype");
});
});
describe("DataGrid column header tooltips", () => {
// The rest of this file drives leaf components through the host renderer, but
// DataGrid.vue touches the DOM directly (querySelector/observers), so this
// case mounts it into happy-dom instead.
function mountDataGrid() {
setActivePinia(createPinia());
const host = document.createElement("div");
document.body.appendChild(host);
const app = createApp(defineComponent({ setup: () => () => h(DataGrid, { result: { columns: ["id", "name"], rows: [[1, "a"]] } }) }));
app.mount(host);
return {
host,
// `disabled` is not one of Vue's special boolean attributes, so binding it
// on the LightTooltip stub's <div> renders the literal "true"/"false".
headerTooltipsDisabled: () => [...host.querySelectorAll('[data-grid-column-index] [data-stub="LightTooltip"]')].map((node) => node.getAttribute("disabled") === "true"),
destroy() {
app.unmount();
host.remove();
},
};
}
it("follows the showColumnHeaderTooltips preference", async () => {
const grid = mountDataGrid();
await nextTick();
expect(settingsMock.store.editorSettings.showColumnHeaderTooltips).toBe(true);
expect(grid.headerTooltipsDisabled()).toEqual([false, false]);
settingsMock.store.editorSettings.showColumnHeaderTooltips = false;
await nextTick();
expect(grid.headerTooltipsDisabled()).toEqual([true, true]);
settingsMock.store.editorSettings.showColumnHeaderTooltips = true;
await nextTick();
expect(grid.headerTooltipsDisabled()).toEqual([false, false]);
grid.destroy();
});
});
+2
View File
@@ -7313,6 +7313,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "Cədvəl sütunlarının şərhlərini birbaşa sütun adlarının altında göstər.",
showColumnTypesInHeader: "Sütun növlərini adların altında göstər",
showColumnTypesInHeaderDescription: "Hər sütunun verilən növünü birbaşa cədvəldəki sütun adlarının altında göstər.",
showColumnHeaderTooltips: "Sütun başlığı üzərinə gətirildikdə ipucları",
showColumnHeaderTooltipsDescription: "Kursor nəticə cədvəlindəki sütun başlıqlarının üzərinə gətirildikdə ətraflı ipucları göstər. Bu pəncərələri dayandırmaq üçün söndürün.",
showResultSourceDatabase: "Nəticə adlarında verilənlər bazası adını göstər",
showResultSourceDatabaseDescription: "Söndürüldükdə nəticə sekmələri yalnız cədvəl adını göstərir; tam ad (verilənlər bazası adı ilə) ipucunda görünməyə davam edir.",
dataGridShowTransposeFieldMetadata: "Transpozisiya görünüşündə sahə metaverilənlərini göstər",
+2
View File
@@ -7961,6 +7961,8 @@ export default {
showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.",
showColumnTypesInHeader: "Show column types under names",
showColumnTypesInHeaderDescription: "Display each column's data type directly below grid column names.",
showColumnHeaderTooltips: "Column header hover tooltips",
showColumnHeaderTooltipsDescription: "Show detail tooltips when hovering result grid column headers. Turn off to stop hover popups.",
showResultSourceDatabase: "Include database name in result set names",
showResultSourceDatabaseDescription: "When off, result set tabs show only the table name; the full name stays visible in the tooltip.",
dataGridShowTransposeFieldMetadata: "Show field metadata in transpose view",
+2
View File
@@ -7339,6 +7339,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "Muestra los comentarios de columnas directamente debajo del nombre de la columna.",
showColumnTypesInHeader: "Mostrar tipos de columna bajo los nombres",
showColumnTypesInHeaderDescription: "Muestra el tipo de dato de cada columna directamente debajo del nombre de la columna.",
showColumnHeaderTooltips: "Sugerencias al pasar el cursor por los encabezados de columna",
showColumnHeaderTooltipsDescription: "Muestra sugerencias con detalles al pasar el cursor sobre los encabezados de columna de la cuadrícula de resultados. Desactívalo para ocultarlas.",
showResultSourceDatabase: "Incluir el nombre de la base de datos en los nombres de los resultados",
showResultSourceDatabaseDescription: "Si se desactiva, las pestañas de resultados solo muestran el nombre de la tabla; el nombre completo sigue visible en la información emergente.",
dataGridShowTransposeFieldMetadata: "Mostrar metadatos de campo en la vista transpuesta",
+2
View File
@@ -7263,6 +7263,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "Visualizza i commenti delle colonne della tabella direttamente sotto i nomi delle colonne nella griglia.",
showColumnTypesInHeader: "Mostra i tipi di colonna sotto i nomi",
showColumnTypesInHeaderDescription: "Visualizza il tipo di dato di ciascuna colonna direttamente sotto i nomi delle colonne nella griglia.",
showColumnHeaderTooltips: "Tooltip al passaggio del mouse sulle intestazioni di colonna",
showColumnHeaderTooltipsDescription: "Mostra tooltip con dettagli al passaggio del mouse sulle intestazioni di colonna della griglia dei risultati. Disattiva per nasconderli.",
showResultSourceDatabase: "Includi il nome del database nei nomi dei risultati",
showResultSourceDatabaseDescription: "Se disattivato, le schede dei risultati mostrano solo il nome della tabella; il nome completo resta visibile nel tooltip.",
dataGridShowTransposeFieldMetadata: "Mostra i metadati dei campi nella vista trasposta",
+2
View File
@@ -7296,6 +7296,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "グリッド列名の直下にテーブル列コメントを表示します。",
showColumnTypesInHeader: "列名の下にデータ型を表示",
showColumnTypesInHeaderDescription: "グリッド列名の直下に各列のデータ型を表示します。",
showColumnHeaderTooltips: "列ヘッダーのホバーツールチップ",
showColumnHeaderTooltipsDescription: "結果グリッドの列ヘッダーにホバーしたときに詳細ツールチップを表示します。オフにすると表示されません。",
showResultSourceDatabase: "結果セット名にデータベース名を含める",
showResultSourceDatabaseDescription: "オフにすると結果セットのタブにはテーブル名のみを表示し、完全な名前(データベース名を含む)はツールチップで確認できます。",
dataGridShowTransposeFieldMetadata: "転置ビューにフィールドメタデータを表示",
+2
View File
@@ -7043,6 +7043,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "그리드 컬럼 이름 바로 아래에 테이블 컬럼 주석을 표시합니다.",
showColumnTypesInHeader: "이름 아래에 컬럼 타입 표시",
showColumnTypesInHeaderDescription: "그리드 컬럼 이름 바로 아래에 각 컬럼의 데이터 타입을 표시합니다.",
showColumnHeaderTooltips: "컬럼 헤더 호버 툴팁",
showColumnHeaderTooltipsDescription: "결과 그리드의 컬럼 헤더에 마우스를 올리면 상세 툴팁을 표시합니다. 끄면 표시되지 않습니다.",
showResultSourceDatabase: "결과 세트 이름에 데이터베이스 이름 포함",
showResultSourceDatabaseDescription: "끄면 결과 세트 탭에 테이블 이름만 표시되며, 전체 이름(데이터베이스 이름 포함)은 툴팁에서 확인할 수 있습니다.",
dataGridShowTransposeFieldMetadata: "전치 보기에 필드 메타데이터 표시",
+2
View File
@@ -7265,6 +7265,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "Exibir comentários de colunas da tabela diretamente abaixo dos nomes das colunas da grade.",
showColumnTypesInHeader: "Mostrar tipos de coluna sob os nomes",
showColumnTypesInHeaderDescription: "Exibir o tipo de dado de cada coluna diretamente abaixo dos nomes das colunas da grade.",
showColumnHeaderTooltips: "Dicas ao passar o mouse nos cabeçalhos de coluna",
showColumnHeaderTooltipsDescription: "Mostra dicas com detalhes ao passar o mouse sobre os cabeçalhos de coluna da grade de resultados. Desative para ocultá-las.",
showResultSourceDatabase: "Incluir o nome do banco de dados nos nomes dos resultados",
showResultSourceDatabaseDescription: "Quando desativado, as abas de resultados mostram apenas o nome da tabela; o nome completo (com o banco de dados) continua na dica de ferramenta.",
dataGridShowTransposeFieldMetadata: "Mostrar metadados de campo na visualização transposta",
+2
View File
@@ -9428,6 +9428,8 @@ export default withEnglishFallback({
shortcutLowercaseSelection: "Преобразовать выделенное в нижний регистр",
shortcutConvertNamingStyle: "Переключить стиль именования",
shortcutExPasteSqlInCondition: "ExPaste: вставить как условие IN",
showColumnHeaderTooltips: "Всплывающие подсказки при наведении на имена полей",
showColumnHeaderTooltipsDescription: "Показывать подробную всплывающую подсказку при наведении курсора на имя поля в заголовке результатов; после отключения всплывающая подсказка при наведении больше не появляется.",
showResultSourceDatabase: "Имя набора результатов содержит имя базы данных",
showResultSourceDatabaseDescription: "После отключения вкладки наборов результатов будут отображать только имена таблиц; полное имя (включая имя базы данных) по-прежнему можно посмотреть во всплывающей подсказке.",
},
+2
View File
@@ -7195,6 +7195,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "Tablo sütun açıklamalarını doğrudan ızgara sütun adlarının altında göster.",
showColumnTypesInHeader: "Sütun türlerini adların altında göster",
showColumnTypesInHeaderDescription: "Her sütunun veri türünü doğrudan ızgara sütun adlarının altında göster.",
showColumnHeaderTooltips: "Sütun başlığı ipuçları",
showColumnHeaderTooltipsDescription: "Sonuç ızgarasında sütun başlıklarının üzerine gelindiğinde ayrıntı ipuçlarını göster. Açılır ipuçlarını durdurmak için kapatın.",
showResultSourceDatabase: "Sonuç adlarında veritabanı adını göster",
showResultSourceDatabaseDescription: "Kapalıyken sonuç sekmeleri yalnızca tablo adını gösterir; tam ad (veritabanı adı dahil) ipucunda görünmeye devam eder.",
dataGridShowTransposeFieldMetadata: "Devrik görünümde alan meta verisini göster",
+2
View File
@@ -7933,6 +7933,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。",
showColumnTypesInHeader: "在字段名下方显示数据类型",
showColumnTypesInHeaderDescription: "把每个字段的数据类型直接显示在结果表头字段名下方。",
showColumnHeaderTooltips: "字段名悬浮提示",
showColumnHeaderTooltipsDescription: "在结果表头字段名上悬浮时显示详情提示;关闭后悬浮不再弹出。",
showResultSourceDatabase: "结果集名称包含数据库名",
showResultSourceDatabaseDescription: "关闭后结果集页签只显示表名,完整名称(含数据库名)仍可在悬浮提示中查看。",
dataGridShowTransposeFieldMetadata: "在转置视图中显示字段元数据",
+2
View File
@@ -6582,6 +6582,8 @@ export default withEnglishFallback({
showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。",
showColumnTypesInHeader: "在欄位名稱下方顯示資料類型",
showColumnTypesInHeaderDescription: "直接在資料表格欄位名稱下方顯示每個欄位的資料類型。",
showColumnHeaderTooltips: "欄位名稱懸浮提示",
showColumnHeaderTooltipsDescription: "在資料表格欄位名稱上懸浮時顯示詳細提示;關閉後懸浮不再彈出。",
showResultSourceDatabase: "結果集名稱包含資料庫名稱",
showResultSourceDatabaseDescription: "關閉後結果集頁籤只顯示資料表名稱,完整名稱(含資料庫名稱)仍可在懸浮提示中查看。",
dataGridShowTransposeFieldMetadata: "在轉置檢視中顯示欄位中繼資料",
@@ -108,6 +108,21 @@ describe("settingsTransfer", () => {
expect(result.error.detail).toContain("wordWrap");
});
it("transfers the column header hover tooltip preference as a data setting", () => {
expect(transferCategoryForKey("showColumnHeaderTooltips")).toBe("data");
const result = parseSettingsTransferFile(fileWith({ showColumnHeaderTooltips: false }));
expect(result.ok).toBe(true);
if (!result.ok) return;
expect(result.value.editorSettings.showColumnHeaderTooltips).toBe(false);
expect(result.value.categories).toContain("data");
const rejected = parseSettingsTransferFile(fileWith({ showColumnHeaderTooltips: "no" }));
expect(rejected.ok).toBe(false);
if (rejected.ok) return;
expect(rejected.error.detail).toContain("showColumnHeaderTooltips");
});
it("rejects toolbar items whose known keys are not booleans", () => {
const result = parseSettingsTransferFile(fileWith({ toolbarItems: { dataTransfer: "yes" } }));
expect(result.ok).toBe(false);
@@ -46,6 +46,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"tabSortMode",
"showColumnCommentsInHeader",
"showColumnTypesInHeader",
"showColumnHeaderTooltips",
"showResultSourceDatabase",
"dataGridShowTransposeFieldMetadata",
"colorizeDataGridCellTypes",
@@ -221,6 +221,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
{ id: "query-result-max-rows", category: "data", titleKey: "settings.queryResultMaxRows", descriptionKey: "settings.queryResultMaxRowsDescription", targetId: "data" },
{ id: "data-grid-header-comments", category: "data", titleKey: "settings.showColumnCommentsInHeader", descriptionKey: "settings.showColumnCommentsInHeaderDescription", targetId: "data" },
{ id: "data-grid-header-types", category: "data", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "data" },
{ id: "data-grid-header-tooltips", category: "data", titleKey: "settings.showColumnHeaderTooltips", descriptionKey: "settings.showColumnHeaderTooltipsDescription", targetId: "data" },
{ id: "result-source-database-name", category: "data", titleKey: "settings.showResultSourceDatabase", descriptionKey: "settings.showResultSourceDatabaseDescription", targetId: "data" },
{ id: "data-grid-transpose-field-metadata", category: "data", titleKey: "settings.dataGridShowTransposeFieldMetadata", descriptionKey: "settings.dataGridShowTransposeFieldMetadataDescription", targetId: "data" },
{ id: "data-grid-cell-type-colors", category: "data", titleKey: "settings.colorizeDataGridCellTypes", descriptionKey: "settings.colorizeDataGridCellTypesDescription", targetId: "data" },
@@ -140,6 +140,7 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record<SettingsTransferCategoryId, readon
data: [
"showColumnCommentsInHeader",
"showColumnTypesInHeader",
"showColumnHeaderTooltips",
"showResultSourceDatabase",
"dataGridShowTransposeFieldMetadata",
"colorizeDataGridCellTypes",
@@ -269,6 +270,7 @@ const PASS_THROUGH_BOOLEAN_KEYS = [
"confirmUnsavedSqlClose",
"showColumnCommentsInHeader",
"showColumnTypesInHeader",
"showColumnHeaderTooltips",
"showResultSourceDatabase",
"colorizeDataGridCellTypes",
"showIndexIndicatorsInHeader",
+4
View File
@@ -871,6 +871,7 @@ export interface EditorSettings {
mongoViewMode: "document" | "table";
showColumnCommentsInHeader: boolean;
showColumnTypesInHeader: boolean;
showColumnHeaderTooltips: boolean;
/** 结果集页签/结果列表的名称是否带上库名(关闭后只显示表名,完整名称仍在悬浮提示中)。 */
showResultSourceDatabase: boolean;
dataGridShowTransposeFieldMetadata: boolean;
@@ -1151,6 +1152,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
mongoViewMode: "document",
showColumnCommentsInHeader: true,
showColumnTypesInHeader: true,
showColumnHeaderTooltips: true,
showResultSourceDatabase: true,
dataGridShowTransposeFieldMetadata: false,
colorizeDataGridCellTypes: false,
@@ -1707,6 +1709,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
mongoViewMode: settings.mongoViewMode === "table" ? "table" : DEFAULT_EDITOR_SETTINGS.mongoViewMode,
showColumnCommentsInHeader: settings.showColumnCommentsInHeader ?? DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader,
showColumnTypesInHeader: settings.showColumnTypesInHeader ?? DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader,
showColumnHeaderTooltips: settings.showColumnHeaderTooltips ?? DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips,
showResultSourceDatabase: settings.showResultSourceDatabase ?? DEFAULT_EDITOR_SETTINGS.showResultSourceDatabase,
dataGridShowTransposeFieldMetadata: settings.dataGridShowTransposeFieldMetadata === true,
colorizeDataGridCellTypes: settings.colorizeDataGridCellTypes ?? DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes,
@@ -2529,6 +2532,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.mongoViewMode !== undefined) editorSettings.value.mongoViewMode = partial.mongoViewMode;
if (partial.showColumnCommentsInHeader !== undefined) editorSettings.value.showColumnCommentsInHeader = partial.showColumnCommentsInHeader;
if (partial.showColumnTypesInHeader !== undefined) editorSettings.value.showColumnTypesInHeader = partial.showColumnTypesInHeader;
if (partial.showColumnHeaderTooltips !== undefined) editorSettings.value.showColumnHeaderTooltips = partial.showColumnHeaderTooltips;
if (partial.showResultSourceDatabase !== undefined) editorSettings.value.showResultSourceDatabase = partial.showResultSourceDatabase;
if (partial.dataGridShowTransposeFieldMetadata !== undefined) editorSettings.value.dataGridShowTransposeFieldMetadata = partial.dataGridShowTransposeFieldMetadata === true;
if (partial.colorizeDataGridCellTypes !== undefined) editorSettings.value.colorizeDataGridCellTypes = partial.colorizeDataGridCellTypes === true;