fix(grid): reserve header control width

This commit is contained in:
t8y2
2026-05-13 23:29:49 +08:00
parent 5832e1b194
commit 530cfa0b7c
3 changed files with 73 additions and 33 deletions
+18 -33
View File
@@ -1,20 +1,15 @@
import { ref, computed, type ComputedRef, type Ref } from "vue";
import { useElementSize } from "@vueuse/core";
import {
calculateDataGridColumnWidth,
DATA_GRID_COL_MIN_WIDTH,
DATA_GRID_SAMPLE_ROWS,
} from "@/lib/dataGridColumnWidth";
type CellValue = string | number | boolean | null;
export const COL_MIN_WIDTH = 60;
export const COL_MAX_WIDTH = 400;
const COL_CHAR_WIDTH = 8;
const COL_HEADER_PADDING = 48;
const COL_CELL_PADDING = 28;
const COL_SAMPLE_ROWS = 50;
const ROW_NUM_WIDTH = 48;
function estimateTextWidth(text: string, padding: number): number {
return text.length * COL_CHAR_WIDTH + padding;
}
export interface UseDataGridColumnResizeOptions {
columns: ComputedRef<string[]>;
rows: ComputedRef<CellValue[][]>;
@@ -28,21 +23,18 @@ export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions)
const { width: gridWidth } = useElementSize(gridRef);
let isResizing = false;
function sampleColumnValues(rowData: CellValue[][], colIdx: number): CellValue[] {
return rowData.slice(0, DATA_GRID_SAMPLE_ROWS).map((row) => row[colIdx] ?? null);
}
function initColumnWidths() {
if (columnWidths.value.length !== columns.value.length) {
const rowData = rows.value;
const sampleCount = Math.min(rowData.length, COL_SAMPLE_ROWS);
columnWidths.value = columns.value.map((colName, colIdx) => {
let maxWidth = estimateTextWidth(colName, COL_HEADER_PADDING);
for (let i = 0; i < sampleCount; i++) {
const val = rowData[i]?.[colIdx];
if (val == null) continue;
const text = typeof val === "object" ? JSON.stringify(val) : String(val);
const displayLen = Math.min(text.length, 60);
const w = displayLen * COL_CHAR_WIDTH + COL_CELL_PADDING;
if (w > maxWidth) maxWidth = w;
}
return Math.max(COL_MIN_WIDTH, Math.min(COL_MAX_WIDTH, Math.round(maxWidth)));
return calculateDataGridColumnWidth({
columnName: colName,
sampleValues: sampleColumnValues(rowData, colIdx),
});
});
}
}
@@ -53,7 +45,7 @@ export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions)
const startX = event.clientX;
const startWidth = columnWidths.value[colIdx];
const onMove = (e: MouseEvent) => {
columnWidths.value[colIdx] = Math.max(60, startWidth + e.clientX - startX);
columnWidths.value[colIdx] = Math.max(DATA_GRID_COL_MIN_WIDTH, startWidth + e.clientX - startX);
};
const onUp = () => {
document.removeEventListener("mousemove", onMove);
@@ -70,17 +62,10 @@ export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions)
const colName = columns.value[colIdx];
if (!colName) return;
const rowData = rows.value;
const sampleCount = Math.min(rowData.length, COL_SAMPLE_ROWS);
let maxWidth = estimateTextWidth(colName, COL_HEADER_PADDING);
for (let i = 0; i < sampleCount; i++) {
const val = rowData[i]?.[colIdx];
if (val == null) continue;
const text = typeof val === "object" ? JSON.stringify(val) : String(val);
const displayLen = Math.min(text.length, 60);
const w = displayLen * COL_CHAR_WIDTH + COL_CELL_PADDING;
if (w > maxWidth) maxWidth = w;
}
columnWidths.value[colIdx] = Math.max(COL_MIN_WIDTH, Math.min(COL_MAX_WIDTH, Math.round(maxWidth)));
columnWidths.value[colIdx] = calculateDataGridColumnWidth({
columnName: colName,
sampleValues: sampleColumnValues(rowData, colIdx),
});
}
const baseTotalWidth = computed(() => columnWidths.value.reduce((a, b) => a + b, 0));
+34
View File
@@ -0,0 +1,34 @@
type CellValue = string | number | boolean | null;
export const DATA_GRID_COL_MIN_WIDTH = 60;
export const DATA_GRID_COL_MAX_WIDTH = 400;
export const DATA_GRID_CHAR_WIDTH = 8;
export const DATA_GRID_HEADER_CONTROL_WIDTH = 64;
export const DATA_GRID_CELL_PADDING = 28;
export const DATA_GRID_SAMPLE_ROWS = 50;
function estimateTextWidth(text: string, padding: number): number {
return text.length * DATA_GRID_CHAR_WIDTH + padding;
}
function displaySampleValue(value: CellValue): string | null {
if (value == null) return null;
return typeof value === "object" ? JSON.stringify(value) : String(value);
}
export function calculateDataGridColumnWidth(options: {
columnName: string;
sampleValues: readonly CellValue[];
}): number {
let maxWidth = estimateTextWidth(options.columnName, DATA_GRID_HEADER_CONTROL_WIDTH);
for (const value of options.sampleValues.slice(0, DATA_GRID_SAMPLE_ROWS)) {
const text = displaySampleValue(value);
if (text == null) continue;
const displayLen = Math.min(text.length, 60);
const width = displayLen * DATA_GRID_CHAR_WIDTH + DATA_GRID_CELL_PADDING;
if (width > maxWidth) maxWidth = width;
}
return Math.max(DATA_GRID_COL_MIN_WIDTH, Math.min(DATA_GRID_COL_MAX_WIDTH, Math.round(maxWidth)));
}