diff --git a/apps/desktop/src/composables/__tests__/useDataGridColumnResize.spec.ts b/apps/desktop/src/composables/__tests__/useDataGridColumnResize.spec.ts new file mode 100644 index 000000000..0556fd64f --- /dev/null +++ b/apps/desktop/src/composables/__tests__/useDataGridColumnResize.spec.ts @@ -0,0 +1,52 @@ +import { computed, ref } from "vue"; +import { describe, expect, it } from "vitest"; +import { DATA_GRID_COL_AUTO_FIT_MAX_WIDTH, DATA_GRID_COL_MAX_WIDTH, DATA_GRID_COL_MIN_WIDTH } from "@/lib/dataGrid/dataGridColumnWidth"; +import { DATA_GRID_ROW_NUM_WIDTH, resizeDataGridColumnWidth, useDataGridColumnResize } from "@/composables/useDataGridColumnResize"; + +function createResizeState(options: { columns: string[]; rows: Array>; columnIndexes?: number[] }) { + return useDataGridColumnResize({ + columns: computed(() => options.columns), + sourceRows: computed(() => options.rows), + columnIndexes: computed(() => options.columnIndexes ?? options.columns.map((_, index) => index)), + gridRef: ref(), + scrollbarGutter: ref(0), + }); +} + +describe("useDataGridColumnResize", () => { + it("keeps compact query result columns at content width instead of filling the viewport", () => { + const state = createResizeState({ + columns: ["id", "user_id"], + rows: [ + [1, 10], + [2, 20], + ], + }); + + state.initColumnWidths(); + + expect(state.renderedColumnWidths.value).toEqual(state.columnWidths.value); + expect(state.totalWidth.value).toBe(DATA_GRID_ROW_NUM_WIDTH + state.columnWidths.value.reduce((total, width) => total + width, 0)); + expect(Math.max(...state.renderedColumnWidths.value)).toBeLessThan(200); + }); + + it("keeps default widths bounded but lets auto-fit use the wider cap", () => { + const state = createResizeState({ + columns: ["description"], + rows: [["x".repeat(120)]], + }); + + state.initColumnWidths(); + expect(state.columnWidths.value[0]).toBe(DATA_GRID_COL_MAX_WIDTH); + + state.autoFitColumn(0); + + expect(state.columnWidths.value[0]).toBeGreaterThan(DATA_GRID_COL_MAX_WIDTH); + expect(state.columnWidths.value[0]).toBeLessThanOrEqual(DATA_GRID_COL_AUTO_FIT_MAX_WIDTH); + }); + + it("clamps manual column resizing to the minimum width", () => { + expect(resizeDataGridColumnWidth(120, -200)).toBe(DATA_GRID_COL_MIN_WIDTH); + expect(resizeDataGridColumnWidth(120, 30)).toBe(150); + }); +}); diff --git a/apps/desktop/src/composables/useDataGridColumnResize.ts b/apps/desktop/src/composables/useDataGridColumnResize.ts index 784db2ba6..7b5b688a6 100644 --- a/apps/desktop/src/composables/useDataGridColumnResize.ts +++ b/apps/desktop/src/composables/useDataGridColumnResize.ts @@ -1,11 +1,14 @@ import { ref, computed, watch, type ComputedRef, type Ref } from "vue"; -import { useElementSize } from "@vueuse/core"; import { calculateDataGridColumnWidth, DATA_GRID_AUTO_FIT_VALUE_TEXT_LIMIT, DATA_GRID_COL_AUTO_FIT_MAX_WIDTH, DATA_GRID_COL_MIN_WIDTH, DATA_GRID_SAMPLE_ROWS } from "@/lib/dataGrid/dataGridColumnWidth"; type CellValue = string | number | boolean | null; export const DATA_GRID_ROW_NUM_WIDTH = 48; +export function resizeDataGridColumnWidth(startWidth: number, deltaX: number): number { + return Math.max(DATA_GRID_COL_MIN_WIDTH, startWidth + deltaX); +} + export interface UseDataGridColumnResizeOptions { columns: ComputedRef; sourceRows: ComputedRef; @@ -15,10 +18,9 @@ export interface UseDataGridColumnResizeOptions { } export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions) { - const { columns, sourceRows, columnIndexes, gridRef, scrollbarGutter } = options; + const { columns, sourceRows, columnIndexes } = options; const columnWidths = ref([]); - const { width: gridWidth } = useElementSize(gridRef); let isResizing = false; let previousColumnIndexes: number[] = []; @@ -59,7 +61,7 @@ export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions) const startX = event.clientX; const startWidth = columnWidths.value[colIdx]; const onMove = (e: MouseEvent) => { - columnWidths.value[colIdx] = Math.max(DATA_GRID_COL_MIN_WIDTH, startWidth + e.clientX - startX); + columnWidths.value[colIdx] = resizeDataGridColumnWidth(startWidth, e.clientX - startX); }; const onUp = () => { document.removeEventListener("mousemove", onMove); @@ -83,19 +85,7 @@ export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions) }); } - const baseTotalWidth = computed(() => columnWidths.value.reduce((a, b) => a + b, 0)); - - const renderedColumnWidths = computed(() => { - const widths = columnWidths.value; - if (widths.length === 0) return widths; - - const availableWidth = Math.max(0, gridWidth.value - (scrollbarGutter?.value ?? 0)); - const extraWidth = Math.max(0, availableWidth - DATA_GRID_ROW_NUM_WIDTH - baseTotalWidth.value); - if (extraWidth === 0) return widths; - - const extraPerColumn = extraWidth / widths.length; - return widths.map((width) => width + extraPerColumn); - }); + const renderedColumnWidths = computed(() => columnWidths.value); const totalWidth = computed(() => renderedColumnWidths.value.reduce((a, b) => a + b, 0) + DATA_GRID_ROW_NUM_WIDTH);