|
|
|
|
@ -8,6 +8,7 @@
@@ -8,6 +8,7 @@
|
|
|
|
|
import { computed, type Ref } from "vue"; |
|
|
|
|
import { measureTextHeight } from "./measureText"; |
|
|
|
|
import type { ListTableColumn } from "./types"; |
|
|
|
|
import { toRefs } from "@vueuse/core"; |
|
|
|
|
|
|
|
|
|
const DEFAULT_FONT = "14px Inter, sans-serif"; |
|
|
|
|
const DEFAULT_LINE_HEIGHT = 20; |
|
|
|
|
@ -17,10 +18,9 @@ const CELL_VERTICAL_PADDING = 12; // top + bottom per cell
@@ -17,10 +18,9 @@ const CELL_VERTICAL_PADDING = 12; // top + bottom per cell
|
|
|
|
|
export interface RowHeightEntry { |
|
|
|
|
height: number; |
|
|
|
|
isCustomRenderer: boolean; |
|
|
|
|
cellHeights?: RowHeightEntry[]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function usePretextRowHeights<T>( |
|
|
|
|
export function resolveRowHeights<T>( |
|
|
|
|
data: Ref<T[]>, |
|
|
|
|
columns: Ref<ListTableColumn<T>[]>, |
|
|
|
|
columnWidths: Ref<number[]>, |
|
|
|
|
@ -29,6 +29,7 @@ export function usePretextRowHeights<T>(
@@ -29,6 +29,7 @@ export function usePretextRowHeights<T>(
|
|
|
|
|
font?: string; |
|
|
|
|
lineHeight?: number; |
|
|
|
|
rowPadding?: number; |
|
|
|
|
fixedRowHeight?: number; |
|
|
|
|
debug?: boolean; |
|
|
|
|
} |
|
|
|
|
) { |
|
|
|
|
@ -36,74 +37,85 @@ export function usePretextRowHeights<T>(
@@ -36,74 +37,85 @@ export function usePretextRowHeights<T>(
|
|
|
|
|
const lineHeight = options?.lineHeight ?? DEFAULT_LINE_HEIGHT; |
|
|
|
|
const rowPadding = options?.rowPadding ?? DEFAULT_ROW_PADDING; |
|
|
|
|
|
|
|
|
|
const rowHeights = computed<RowHeightEntry[]>(() => { |
|
|
|
|
if (!data.value.length || !columns.value.length || !columnWidths.value.length) { |
|
|
|
|
return []; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return data.value.map((row) => { |
|
|
|
|
let maxCellHeight = lineHeight; // minimum 1 line
|
|
|
|
|
let cellHeights: RowHeightEntry[] | undefined = options?.debug ? [] : undefined; |
|
|
|
|
|
|
|
|
|
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] ?? 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); |
|
|
|
|
|
|
|
|
|
cellHeights?.push({ |
|
|
|
|
height: placeholderHeight, |
|
|
|
|
isCustomRenderer: true, |
|
|
|
|
}); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Get raw cell value
|
|
|
|
|
const cellText = formatCellValue(row, col); |
|
|
|
|
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); |
|
|
|
|
const { rowHeights, cellHeights } = toRefs( |
|
|
|
|
computed(() => { |
|
|
|
|
if (!data.value.length || !columns.value.length || !columnWidths.value.length) { |
|
|
|
|
return { rowHeights: [], cellHeights: [] }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cellHeights?.push({ |
|
|
|
|
height: cellHeight, |
|
|
|
|
isCustomRenderer: false, |
|
|
|
|
}); |
|
|
|
|
} catch { |
|
|
|
|
// Fallback: assume single line
|
|
|
|
|
const rowHeights: number[] = []; |
|
|
|
|
const cellHeights: Array<RowHeightEntry | null>[] | undefined = options?.debug ? [] : undefined; |
|
|
|
|
|
|
|
|
|
for (const row of data.value) { |
|
|
|
|
let maxCellHeight = lineHeight; // minimum 1 line
|
|
|
|
|
let rowCellHeights: Array<RowHeightEntry | null> = []; |
|
|
|
|
|
|
|
|
|
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] ?? 100; |
|
|
|
|
|
|
|
|
|
// 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 = colWidth - CELL_VERTICAL_PADDING * 2; |
|
|
|
|
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 totalHeight = maxCellHeight + rowPadding * 2; |
|
|
|
|
const rowHeight = maxCellHeight + rowPadding * 2; |
|
|
|
|
rowHeights.push(rowHeight); |
|
|
|
|
cellHeights?.push(rowCellHeights); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
height: totalHeight, |
|
|
|
|
isCustomRenderer: false, |
|
|
|
|
cellHeights: cellHeights ?? undefined, |
|
|
|
|
rowHeights, |
|
|
|
|
cellHeights, |
|
|
|
|
}; |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Total height (sum of all row heights) - useful for virtualizer
|
|
|
|
|
const totalHeight = computed(() => rowHeights.value.reduce((sum, entry) => sum + entry.height, 0)); |
|
|
|
|
const totalHeight = computed(() => rowHeights.value.reduce((sum, height) => sum + height, 0)); |
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
rowHeights, |
|
|
|
|
totalHeight, |
|
|
|
|
cellHeights, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|