fix(grid): stabilize header height during horizontal scroll

This commit is contained in:
LRcoding authored and GitHub committed 2026-07-17 01:17:49 +08:00
1 parent d3947d246c
commit 51b4c79847
5 files changed
+114 -2

No files matched your search

@@ -196,6 +196,7 @@ import type { DataGridSortDirection, DataGridSortMode } from "@/lib/dataGrid/dat
import { DATA_GRID_COMPACT_TOPBAR_WIDTH, type DataGridReloadIntent, type DataGridToolbarActionCapability, type DataGridToolbarAutoRefreshCapability, type DataGridToolbarSaveCapability } from "@/lib/dataGrid/dataGridToolbar";
import { getTableMetadataCapabilities } from "@/lib/table/tableMetadataCapabilities";
import { getTableStructureCapabilities } from "@/lib/table/tableStructureCapabilities";
import { reserveDataGridHeaderLine } from "@/lib/dataGrid/dataGridHeaderLayout";
import { supportsTableStructureEditing } from "@/lib/database/databaseCapabilities";
import { rememberDataGridConditionHistory } from "@/lib/dataGrid/dataGridConditionHistory";
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
@@ -441,6 +442,10 @@ function headerColumnType(column: string, actualColIdx: number): string {
return resolved ? shortTypeName(compactHeaderColumnType(resolved)) : "";
}
const reserveColumnTypeLine = computed(() => reserveDataGridHeaderLine(showColumnTypesInHeader.value, props.result.columns, (column, index) => headerColumnType(column, index)));
// Match the rendered header columns so comments from unprojected metadata cannot add an empty row.
const reserveColumnCommentLine = computed(() => reserveDataGridHeaderLine(showColumnCommentsInHeader.value, props.result.columns, (column) => headerColumnComment(column)));
function shortTypeName(t: string): string {
const s = t.toLowerCase();
if (s === "character varying") return "varchar";
@@ -7769,6 +7774,8 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
:tooltip-disabled="columnHeaderTooltipsDisabled"
:column-type="headerColumnType(col.name, col.actualColIdx)"
:column-comment="headerColumnComment(col.name)"
:show-type-line="reserveColumnTypeLine"
:show-comment-line="reserveColumnCommentLine"
:tooltip-column-type="columnTypeMap.get(col.name)"
:tooltip-column-comment="columnCommentMap.get(col.name)"
:type-class="typeColorClass(headerColumnType(col.name, col.actualColIdx))"
@@ -15,6 +15,8 @@ defineProps<{
columnComment?: string;
tooltipColumnType?: string;
tooltipColumnComment?: string;
showTypeLine?: boolean;
showCommentLine?: boolean;
typeClass?: HTMLAttributes["class"];
dragClass?: HTMLAttributes["class"];
columnStyle?: CSSProperties;
@@ -51,8 +53,10 @@ const emit = defineEmits<{
<span class="flex min-w-0 items-center gap-1 overflow-hidden">
<span class="flex min-w-0 flex-1 flex-col overflow-hidden">
<span class="min-w-0 truncate leading-4">{{ name }}</span>
<span v-if="columnType" class="min-w-0 truncate text-[10px] font-normal leading-3" :class="typeClass" :title="columnType">{{ columnType }}</span>
<span v-if="columnComment" class="min-w-0 truncate text-[10px] font-normal leading-3 text-muted-foreground" :title="columnComment">{{ columnComment }}</span>
<span v-if="showTypeLine" data-grid-header-type-line class="h-3 min-w-0 truncate text-[10px] font-normal leading-3" :class="[typeClass, { invisible: !columnType }]" :title="columnType || undefined" :aria-hidden="columnType ? undefined : true">{{ columnType }}</span>
<span v-if="showCommentLine" data-grid-header-comment-line class="h-3 min-w-0 truncate text-[10px] font-normal leading-3 text-muted-foreground" :class="{ invisible: !columnComment }" :title="columnComment || undefined" :aria-hidden="columnComment ? undefined : true">{{
columnComment
}}</span>
</span>
<slot name="actions" />
</span>
@@ -212,6 +212,67 @@ describe("DataGridColumnHeader", () => {
dispatch(handle, "dblclick");
expect(autoFit).toHaveBeenCalledOnce();
});
it("keeps configured type and comment lines mounted for columns without values", () => {
const empty = mountComponent(DataGridColumnHeader, {
name: "id",
actualColumnIndex: 0,
visibleColumnIndex: 0,
showTypeLine: true,
showCommentLine: true,
copyColumnNameLabel: "copy",
columnNameLabel: "name",
columnTypeLabel: "type",
columnCommentLabel: "comment",
});
const emptyType = findOne(empty.root, (node) => node.props["data-grid-header-type-line"] === "");
const emptyComment = findOne(empty.root, (node) => node.props["data-grid-header-comment-line"] === "");
expect(String(emptyType.props.class)).toContain("h-3");
expect(String(emptyType.props.class)).toContain("invisible");
expect(emptyType.props.title).toBeUndefined();
expect(String(emptyComment.props.class)).toContain("h-3");
expect(String(emptyComment.props.class)).toContain("invisible");
expect(emptyComment.props.title).toBeUndefined();
const populated = mountComponent(DataGridColumnHeader, {
name: "status",
actualColumnIndex: 1,
visibleColumnIndex: 1,
columnType: "varchar",
columnComment: "Current status",
showTypeLine: true,
showCommentLine: true,
copyColumnNameLabel: "copy",
columnNameLabel: "name",
columnTypeLabel: "type",
columnCommentLabel: "comment",
});
const populatedType = findOne(populated.root, (node) => node.props["data-grid-header-type-line"] === "");
const populatedComment = findOne(populated.root, (node) => node.props["data-grid-header-comment-line"] === "");
expect(String(populatedType.props.class)).not.toContain("invisible");
expect(populatedType.props.title).toBe("varchar");
expect(String(populatedComment.props.class)).not.toContain("invisible");
expect(populatedComment.props.title).toBe("Current status");
});
it("omits optional header lines when both display settings are off", () => {
const mounted = mountComponent(DataGridColumnHeader, {
name: "id",
actualColumnIndex: 0,
visibleColumnIndex: 0,
columnType: "number",
columnComment: "Identifier",
copyColumnNameLabel: "copy",
columnNameLabel: "name",
columnTypeLabel: "type",
columnCommentLabel: "comment",
});
expect(findAll(mounted.root, (node) => node.props["data-grid-header-type-line"] === "")).toHaveLength(0);
expect(findAll(mounted.root, (node) => node.props["data-grid-header-comment-line"] === "")).toHaveLength(0);
});
});
describe("DataGridFilterBuilder", () => {