fix(grid): allow resizing the transposed metadata info column

This commit is contained in:
lxk955 authored and GitHub committed 2026-10-27 23:56:45 +08:00
1 parent fbb588eecb
commit 045715a90d
4 files changed
+114 -3

No files matched your search

+30 -3
View File
@@ -149,9 +149,12 @@ import { sumGridCountValues } from "@/lib/dataGrid/dataGridCountValue";
import {
buildVisibleTransposeRows,
averageTransposeRecordWidth,
calculateTransposeInfoWidth,
calculateTransposeRecordWidth,
defaultTransposeInfoWidth,
defaultTransposeRecordWidth,
minTransposeFieldWidth,
minTransposeInfoWidth,
minTransposeRecordWidth,
nextContextTransposeState,
nextKeyboardTransposeState,
@@ -12416,7 +12419,8 @@ const transposeReserveTypeLine = computed(() => showTransposeFieldMetadata.value
const transposeReserveCommentLine = computed(() => showTransposeFieldMetadata.value && showTransposeColumnComments.value && transposeRows.value.some((row) => row.comment));
const transposeRowHeight = computed(() => 30 + (transposeReserveTypeLine.value ? 14 : 0) + (transposeReserveCommentLine.value ? 14 : 0));
const isTransposeMode = computed(() => showTranspose.value && transposeRows.value.length > 0);
const transposeInfoWidth = computed(() => (transposeMetadataInInfoColumn.value ? 360 : 0));
const transposeInfoWidthOverride = ref<number | null>(null);
const transposeInfoWidth = computed(() => (transposeMetadataInInfoColumn.value ? (transposeInfoWidthOverride.value ?? defaultTransposeInfoWidth(columnWidthDensity.value)) : 0));
const transposeTotalWidth = computed(() => transposePinnedWidth.value + transposeRecordOffsets.value[transposeRecordOffsets.value.length - 1] + transposeEndSpacerWidth.value + transposeInfoWidth.value);
function transposeScrollElement(): HTMLElement | undefined {
@@ -12604,6 +12608,28 @@ function autoFitTransposeRecord(recordIndex: number) {
transposeRecordWidths.value = next;
}
function onTransposeInfoResizeStart(event: MouseEvent) {
event.preventDefault();
const startX = event.clientX;
const startWidth = transposeInfoWidth.value;
const onMove = (e: MouseEvent) => {
transposeInfoWidthOverride.value = Math.max(minTransposeInfoWidth(columnWidthDensity.value), startWidth + e.clientX - startX);
updateTransposeViewport();
};
const onUp = () => {
document.removeEventListener("mousemove", onMove);
document.removeEventListener("mouseup", onUp);
};
document.addEventListener("mousemove", onMove);
document.addEventListener("mouseup", onUp);
}
function autoFitTransposeInfo() {
const texts = visibleColumnIndexes.value.map((columnIndex) => columnInfoText(columnIndex, false));
transposeInfoWidthOverride.value = calculateTransposeInfoWidth(texts, { density: columnWidthDensity.value });
updateTransposeViewport();
}
function currentTransposeViewportRowIndex(): number {
if (displayRowCount.value === 0) return 0;
const rowIndex = transposeRowIndex.value ?? transposeScopeRecordIndexes.value[transposeRecordWindow.value.start] ?? 0;
@@ -15651,8 +15677,9 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
<div class="absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-primary/30" @mousedown.stop="onTransposeRecordResizeStart(recordIndex, $event)" @dblclick.stop="autoFitTransposeRecord(recordIndex)" />
</div>
<div class="shrink-0" :style="{ width: `${transposeAfterSpacerWidth}px` }" />
<div v-if="transposeMetadataInInfoColumn" class="data-grid-header-cell shrink-0 border-r border-border px-3 py-1.5" :style="{ width: `${transposeInfoWidth}px` }">
{{ t("grid.columnInfo") }}
<div v-if="transposeMetadataInInfoColumn" class="data-grid-header-cell relative flex shrink-0 items-center border-r border-border px-3 py-1.5 truncate" :style="{ width: `${transposeInfoWidth}px` }">
<span class="min-w-0 flex-1 truncate">{{ t("grid.columnInfo") }}</span>
<div data-grid-transpose-info-resize-handle class="absolute right-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-primary/30" @mousedown.stop="onTransposeInfoResizeStart" @dblclick.stop="autoFitTransposeInfo" />
</div>
</div>
</template>
@@ -238,4 +238,45 @@ describe("DataGrid transpose sorting", () => {
await settle();
expect(transposeFieldNames(host)).toEqual(["zeta", "alpha"]);
});
it("allows resizing and auto-fitting the independent transpose info column", async () => {
const { host, settingsStore } = mountGrid();
settingsStore.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: true });
await settle();
await openTranspose(host);
const handle = host.querySelector<HTMLElement>("[data-grid-transpose-info-resize-handle]");
expect(handle).not.toBeNull();
const headerInfoCell = handle!.parentElement as HTMLElement;
expect(headerInfoCell.style.width).toBe("360px");
const rowInfoCell = host.querySelector<HTMLElement>("[data-grid-transpose-column-info]");
expect(rowInfoCell?.style.width).toBe("360px");
// Drag to widen (+150px)
handle!.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, clientX: 200 }));
document.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, cancelable: true, clientX: 350 }));
document.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true }));
await settle();
expect(headerInfoCell.style.width).toBe("510px");
expect(rowInfoCell?.style.width).toBe("510px");
// Drag to narrow below min width (should clamp to minTransposeInfoWidth = 120px)
handle!.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, clientX: 350 }));
document.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, cancelable: true, clientX: -100 }));
document.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true }));
await settle();
expect(headerInfoCell.style.width).toBe("120px");
expect(rowInfoCell?.style.width).toBe("120px");
// Double-click to auto-fit
handle!.dispatchEvent(new MouseEvent("dblclick", { bubbles: true, cancelable: true }));
await settle();
const autoFittedWidth = parseInt(headerInfoCell.style.width, 10);
expect(autoFittedWidth).toBeGreaterThan(120);
expect(rowInfoCell?.style.width).toBe(`${autoFittedWidth}px`);
});
});
@@ -3,8 +3,11 @@ import {
averageTransposeRecordWidth,
buildVisibleTransposeRows,
calculateTransposeRecordWidth,
calculateTransposeInfoWidth,
defaultTransposeInfoWidth,
defaultTransposeRecordWidth,
minTransposeFieldWidth,
minTransposeInfoWidth,
shouldAutoTransposeSingleRow,
transposeAnchorRowIndex,
transposeEndAlignmentSpacerWidth,
@@ -59,6 +62,23 @@ describe("dataGridTranspose density widths", () => {
expect(transposeFieldWidth([], { density: "compact" })).toBe(minTransposeFieldWidth("compact"));
});
it("scales default and min transpose info width with density", () => {
expect(defaultTransposeInfoWidth("compact")).toBeLessThan(defaultTransposeInfoWidth("standard"));
expect(defaultTransposeInfoWidth("standard")).toBe(360);
expect(defaultTransposeInfoWidth("standard")).toBeLessThan(defaultTransposeInfoWidth("comfortable"));
expect(minTransposeInfoWidth("compact")).toBeLessThan(minTransposeInfoWidth("standard"));
expect(minTransposeInfoWidth("standard")).toBe(120);
expect(minTransposeInfoWidth("standard")).toBeLessThan(minTransposeInfoWidth("comfortable"));
const longMetadata = ["varchar(255) · Nullable: Yes · " + "a".repeat(120)];
const compactWidth = calculateTransposeInfoWidth(longMetadata, { density: "compact" });
const standardWidth = calculateTransposeInfoWidth(longMetadata, { density: "standard" });
expect(compactWidth).toBeLessThan(standardWidth);
expect(standardWidth).toBeGreaterThan(defaultTransposeInfoWidth("standard"));
expect(calculateTransposeInfoWidth([])).toBe(defaultTransposeInfoWidth("standard"));
});
it("keeps compact record width independent of field order", () => {
const longValue = "x".repeat(100);
@@ -105,6 +105,9 @@ const TRANSPOSE_RECORD_MIN_WIDTH = 96;
const TRANSPOSE_RECORD_DEFAULT_WIDTH = 168;
const TRANSPOSE_FIELD_MIN_WIDTH = 104;
const TRANSPOSE_FIELD_MAX_WIDTH = 220;
export const TRANSPOSE_INFO_DEFAULT_WIDTH = 360;
export const TRANSPOSE_INFO_MIN_WIDTH = 120;
export const TRANSPOSE_INFO_MAX_WIDTH = 800;
// 转置视图每种密度对应的缩放因子(不再依赖 charWidth)
const TRANSPOSE_DENSITY_SCALE: Record<ColumnWidthDensity, number> = {
@@ -134,6 +137,26 @@ export function minTransposeFieldWidth(density: ColumnWidthDensity): number {
return densityScaledWidth(TRANSPOSE_FIELD_MIN_WIDTH, density);
}
export function defaultTransposeInfoWidth(density: ColumnWidthDensity): number {
return densityScaledWidth(TRANSPOSE_INFO_DEFAULT_WIDTH, density);
}
export function minTransposeInfoWidth(density: ColumnWidthDensity): number {
return densityScaledWidth(TRANSPOSE_INFO_MIN_WIDTH, density);
}
export function calculateTransposeInfoWidth(texts: readonly string[], options: TransposeFieldWidthOptions = {}): number {
const density = options.density ?? "standard";
const preset = COLUMN_WIDTH_DENSITY_PRESETS[density];
const minWidth = options.minWidth ?? minTransposeInfoWidth(density);
const maxWidth = options.maxWidth ?? densityScaledWidth(TRANSPOSE_INFO_MAX_WIDTH, density);
const charWidth = options.charWidth ?? preset.charWidth;
const padding = options.padding ?? preset.cellPadding + 16;
const longest = texts.reduce((max, text) => Math.max(max, text.length), 0);
if (longest === 0) return defaultTransposeInfoWidth(density);
return Math.min(maxWidth, Math.max(minWidth, Math.ceil(longest * charWidth + padding)));
}
export function calculateTransposeRecordWidth(values: readonly unknown[], density: ColumnWidthDensity): number {
const preset = COLUMN_WIDTH_DENSITY_PRESETS[density];
const minWidth = minTransposeRecordWidth(density);