forked from mengyxu/noob-components
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.0 KiB
102 lines
3.0 KiB
|
3 months ago
|
/**
|
||
|
|
* 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<T>(
|
||
|
|
data: Ref<T[]>,
|
||
|
|
columns: Ref<ListTableColumn<T>[]>,
|
||
|
|
columnWidths: Ref<number[]>,
|
||
|
|
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<RowHeightEntry[]>(() => {
|
||
|
|
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,
|
||
|
|
};
|
||
|
|
}
|