/** * 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"; const DEFAULT_FONT = "14px Inter, sans-serif"; const DEFAULT_LINE_HEIGHT = 20; const DEFAULT_ROW_PADDING = 12; const CELL_VERTICAL_PADDING = 8; // top + bottom per cell export interface RowHeightEntry { height: number; isCustomRenderer: boolean; } export function usePretextRowHeights( data: Ref, columns: Ref[]>, columnWidths: Ref, options?: { font?: string; lineHeight?: number; rowPadding?: number; } ) { const font = options?.font ?? DEFAULT_FONT; const lineHeight = options?.lineHeight ?? DEFAULT_LINE_HEIGHT; const rowPadding = options?.rowPadding ?? DEFAULT_ROW_PADDING; const rowHeights = computed(() => { if (!data.value.length || !columns.value.length || !columnWidths.value.length) { return []; } return data.value.map((row) => { let maxCellHeight = lineHeight; // minimum 1 line for (let i = 0; i < columns.value.length; i++) { const col = columns.value[i]; const colWidth = columnWidths.value[i] ?? 100; // Check if custom renderer exists (we can't measure these with pretext) const hasCustomRenderer = !!(col.cellRenderer || col.slot); if (hasCustomRenderer) { // For custom renderers, we use a placeholder height // Actual height will be measured at runtime via useRuntimeHeightAugment // For now, use a reasonable minimum const placeholderHeight = 44; // default row height maxCellHeight = Math.max(maxCellHeight, placeholderHeight); continue; } // Get raw cell value const rawValue = (row as any)[col.dataKey || col.key]; const cellText = rawValue == null ? "" : String(rawValue); if (!cellText) continue; // Calculate available width for text (excluding cell padding) const availableWidth = colWidth - CELL_VERTICAL_PADDING * 2; if (availableWidth <= 0) continue; try { const cellHeight = measureTextHeight( cellText, font, availableWidth, lineHeight ); maxCellHeight = Math.max(maxCellHeight, cellHeight); } catch { // Fallback: assume single line } } const totalHeight = maxCellHeight + rowPadding * 2 + CELL_VERTICAL_PADDING * 2; return { height: totalHeight, isCustomRenderer: false, }; }); }); // Total height (sum of all row heights) - useful for virtualizer const totalHeight = computed(() => rowHeights.value.reduce((sum, entry) => sum + entry.height, 0) ); return { rowHeights, totalHeight, }; }