feat(grid): add go-to-column toolbar action

This commit is contained in:
kkk000111999 2026-06-28 14:52:25 +08:00 committed by GitHub
parent 433907e96e
commit 9fc2e25fbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 0 deletions

View File

@ -63,6 +63,7 @@ import {
PanelRight,
TableProperties,
Database,
Columns3,
} from "@lucide/vue";
import { Button } from "@/components/ui/button";
import QueryLoadingState from "@/components/common/QueryLoadingState.vue";
@ -1648,6 +1649,9 @@ const nullColumnsHidden = ref(false);
const autoHiddenNullColumnIndexes = ref<Set<number>>(new Set());
const highlightedColumnIndex = ref<number | null>(null);
let highlightedColumnTimer = 0;
const goToColumnOpen = ref(false);
const goToColumnSearch = ref("");
const columnOrderKeys = computed(() => uniqueDataGridColumnOrderKeys(props.result.columns, props.sourceColumns));
const columnLayoutScopeKey = computed(() =>
dataGridColumnLayoutScopeKey({
@ -1669,6 +1673,18 @@ const displayableColumnIndexes = computed(() =>
.filter(({ column }) => !isHiddenGridColumn(props.databaseType, column, props.tableMeta?.primaryKeys ?? []))
.map(({ index }) => index),
);
const goToColumnItems = computed(() =>
displayableColumnIndexes.value.map((index) => ({
index,
name: props.result.columns[index] ?? `#${index + 1}`,
sourceName: props.sourceColumns?.[index],
})),
);
const filteredGoToColumns = computed(() => {
const query = goToColumnSearch.value.trim().toLocaleLowerCase();
if (!query) return goToColumnItems.value;
return goToColumnItems.value.filter((column) => column.name.toLocaleLowerCase().includes(query) || column.sourceName?.toLocaleLowerCase().includes(query));
});
const orderedDisplayableColumnIndexes = computed(() =>
orderedColumnIndexes({
availableIndexes: displayableColumnIndexes.value,
@ -1780,12 +1796,33 @@ const firstVisibleColumnIndex = computed(() => visibleColumnIndexes.value[0] ??
function actualColumnIndex(visibleColumnIndex: number): number {
return visibleColumnIndexes.value[visibleColumnIndex] ?? visibleColumnIndex;
}
function scrollToColumn(columnIndex: number) {
goToColumnOpen.value = false;
goToColumnSearch.value = "";
scrollToColumnIndex(columnIndex);
gridRef.value?.focus();
}
function onGoToColumnKeydown(event: KeyboardEvent) {
if (event.key === "Escape") {
goToColumnOpen.value = false;
goToColumnSearch.value = "";
}
}
watch(goToColumnOpen, (open) => {
if (!open) goToColumnSearch.value = "";
});
function matchesTableInfoColumn(resultColumn: string, sourceColumn: string | undefined, columnName: string): boolean {
const target = columnName.toLocaleLowerCase();
return resultColumn.toLocaleLowerCase() === target || sourceColumn?.toLocaleLowerCase() === target;
}
function scrollToTableInfoColumn(columnName: string) {
const columnIndex = props.result.columns.findIndex((column, index) => matchesTableInfoColumn(column, props.sourceColumns?.[index], columnName));
scrollToColumnIndex(columnIndex);
}
function scrollToColumnIndex(columnIndex: number) {
if (columnIndex < 0 || !displayableColumnIndexes.value.includes(columnIndex)) return;
if (hiddenColumnIndexes.value.has(columnIndex)) {
@ -7165,6 +7202,35 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
</TooltipTrigger>
<TooltipContent side="bottom" class="max-w-sm"> {{ dataGridRenderMode === "canvas" ? t("grid.canvasRenderMode") : t("grid.domRenderMode") }} · {{ t("grid.renderModeHint") }} </TooltipContent>
</Tooltip>
<Tooltip v-if="props.result.columns.length">
<TooltipTrigger as-child>
<Popover v-model:open="goToColumnOpen">
<PopoverTrigger as-child>
<Button variant="ghost" size="sm" :class="['data-grid-topbar-action-button h-5 shrink-0 text-xs px-1.5', compactDataGridToolbar ? 'data-grid-topbar-action-button--compact' : '', goToColumnOpen ? 'text-primary bg-primary/10' : '']">
<Columns3 class="data-grid-topbar-action-icon w-3 h-3" />
<span class="data-grid-topbar-action-label" :class="{ 'data-grid-topbar-action-label--compact': compactDataGridToolbar }">{{ t("grid.goToColumn") }}</span>
</Button>
</PopoverTrigger>
<PopoverContent align="end" class="w-56 p-2" @keydown="onGoToColumnKeydown">
<div class="relative mb-1">
<Search class="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
<input v-model="goToColumnSearch" :placeholder="t('grid.searchColumn')" class="h-8 w-full rounded-md border bg-transparent pl-7 pr-6 text-xs outline-none focus-visible:border-ring/50 focus-visible:ring-1 focus-visible:ring-ring/25" />
<button v-if="goToColumnSearch" type="button" class="absolute right-1.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground" @click="goToColumnSearch = ''">
<X class="h-3.5 w-3.5" />
</button>
</div>
<div class="max-h-56 overflow-auto rounded border">
<button v-for="column in filteredGoToColumns" :key="column.index" type="button" class="flex w-full items-center gap-1.5 px-2 py-1.5 text-left text-xs hover:bg-accent hover:text-accent-foreground" @click="scrollToColumn(column.index)">
<span class="min-w-0 truncate">{{ column.name }}</span>
<span class="ml-auto shrink-0 font-mono text-[10px] text-muted-foreground">#{{ column.index + 1 }}</span>
</button>
<div v-if="!filteredGoToColumns.length" class="px-2 py-3 text-center text-xs text-muted-foreground">{{ t("grid.noColumnsFound") }}</div>
</div>
</PopoverContent>
</Popover>
</TooltipTrigger>
<TooltipContent side="bottom">{{ t("grid.goToColumn") }}</TooltipContent>
</Tooltip>
<Tooltip v-if="editable && (tableMeta || customSaveHandler)">
<TooltipTrigger as-child>
<Button variant="ghost" size="sm" :class="['data-grid-topbar-action-button h-5 shrink-0 text-xs px-1.5', compactDataGridToolbar ? 'data-grid-topbar-action-button--compact' : '']" @click="addRow">

View File

@ -804,6 +804,9 @@ export default {
tableInfoEmpty: "No metadata",
tableInfoSearch: "Search...",
tableInfoNoResults: "No results",
goToColumn: "Go to column",
searchColumn: "Search column...",
noColumnsFound: "No columns found",
queryError: "Query Error",
dataUnavailable: "Table data needs to be reloaded.",
dataUnavailableHintPrefix: "Press ",

View File

@ -807,6 +807,9 @@ export default withEnglishFallback({
tableInfoEmpty: "暂无元数据",
tableInfoSearch: "搜索...",
tableInfoNoResults: "无匹配结果",
goToColumn: "跳转列",
searchColumn: "搜索列...",
noColumnsFound: "未找到列",
queryError: "查询出错",
dataUnavailable: "表数据需要重新加载。",
dataUnavailableHintPrefix: "按 ",