/** * usePretextRowHeights * * Pre-computes row heights using pretext text measurement. * For each row, measures each cell's height at the given column width, * then takes the maximum + padding as the row height. */ import { computed, type Ref } from "vue"; import { measureTextHeight } from "./measureText"; import type { ListTableColumn } from "./types"; import { toRefs } from "@vueuse/core"; export interface RowHeightEntry { height: number; isCustomRenderer: boolean; } export function resolveRowHeights( data: Ref, columns: Ref[]>, columnWidths: Ref, lineMaxWidth: number, formatCellValue: (row: T, col: ListTableColumn) => string, style: { font: string; lineHeight: number; padding: { top: number; bottom: number; left: number; right: number }; }, options?: { fixedRowHeight?: number; debug?: boolean; } ) { const { font, lineHeight, padding } = style; const { rowHeights, cellHeights } = toRefs( computed(() => { if (!data.value.length || !columns.value.length || !columnWidths.value.length) { return { rowHeights: [], cellHeights: [] }; } const rowHeights: number[] = []; const cellHeights: Array[] | undefined = options?.debug ? [] : undefined; for (const row of data.value) { let maxCellHeight = lineHeight; // minimum 1 line let rowCellHeights: Array = []; for (let i = 0; i < columns.value.length; i++) { const col = columns.value[i]; // Use flex basis width for height calculation as an approximation // of actual column width (CSS flex layout determines actual width) const colWidth = columnWidths.value[i] ?? 120; // Check if custom renderer exists (we can't measure these with pretext) const isCustomRenderer = !!(col.cellRenderer || col.slot); let rowHeightEntry: RowHeightEntry; if (options?.fixedRowHeight) { rowHeightEntry = { height: options.fixedRowHeight, isCustomRenderer, }; } else if (isCustomRenderer) { rowHeightEntry = { height: 42, isCustomRenderer, }; } else { try { const cellText = formatCellValue(row, col); if (!cellText) throw "Invalid Cell Text"; // Calculate available width for text (excluding cell padding) const availableWidth = Math.min( colWidth - padding.left - padding.right, col.maxTextWidth ?? lineMaxWidth ); if (availableWidth <= 0) throw "Invalid Column Width"; rowHeightEntry = { height: measureTextHeight(cellText, font, availableWidth, lineHeight), isCustomRenderer, }; } catch { // Fallback: assume single line rowHeightEntry = { height: 42, isCustomRenderer, }; } } maxCellHeight = Math.max(maxCellHeight, rowHeightEntry?.height ?? 0); if (options?.debug) { rowCellHeights.push(rowHeightEntry); } } const rowHeight = maxCellHeight + padding.top + padding.bottom; rowHeights.push(rowHeight); cellHeights?.push(rowCellHeights); } return { rowHeights, cellHeights, }; }) ); // Total height (sum of all row heights) - useful for virtualizer const totalHeight = computed(() => rowHeights.value.reduce((sum, height) => sum + height, 0)); return { rowHeights, totalHeight, cellHeights, }; }