Browse Source

WIP: PR2

dev
hechang27-sprt 3 months ago
parent
commit
6904ea408a
  1. 3
      bun.lock
  2. 3
      package.json
  3. 2
      packages/base/data/list-table-v2/index.ts
  4. 193
      packages/base/data/list-table-v2/list-table-v2.vue
  5. 134
      packages/base/data/list-table-v2/usePretextColumnWidths.ts
  6. 38
      packages/base/data/list-table-v2/usePretextRowHeights.ts

3
bun.lock

@ -21,6 +21,7 @@
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-addon-attach": "^0.9.0", "xterm-addon-attach": "^0.9.0",
"xterm-addon-fit": "^0.8.0", "xterm-addon-fit": "^0.8.0",
"yoga-layout": "^3.2.1",
}, },
"devDependencies": { "devDependencies": {
"@types/js-md5": "^0.8.0", "@types/js-md5": "^0.8.0",
@ -1888,6 +1889,8 @@
"yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"@microsoft/api-extractor/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], "@microsoft/api-extractor/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],

3
package.json

@ -105,7 +105,8 @@
"vuex": "^4.1.0", "vuex": "^4.1.0",
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-addon-attach": "^0.9.0", "xterm-addon-attach": "^0.9.0",
"xterm-addon-fit": "^0.8.0" "xterm-addon-fit": "^0.8.0",
"yoga-layout": "^3.2.1"
}, },
"devDependencies": { "devDependencies": {
"@types/js-md5": "^0.8.0", "@types/js-md5": "^0.8.0",

2
packages/base/data/list-table-v2/index.ts

@ -27,7 +27,7 @@ export {
} from "./measureText"; } from "./measureText";
// Hooks // Hooks
export { usePretextColumnWidths, type ColumnFlexConfig } from "./usePretextColumnWidths"; export { usePretextColumnWidths, computeFlexWidths, type ColumnFlexConfig } from "./usePretextColumnWidths";
export { usePretextRowHeights, type RowHeightEntry } from "./usePretextRowHeights"; export { usePretextRowHeights, type RowHeightEntry } from "./usePretextRowHeights";
export { export {
useVirtualRows, useVirtualRows,

193
packages/base/data/list-table-v2/list-table-v2.vue

@ -4,8 +4,17 @@
<div v-if="debug" class="debug-info"> <div v-if="debug" class="debug-info">
<div>Container Width: {{ containerWidth }}</div> <div>Container Width: {{ containerWidth }}</div>
<div>Total Height: {{ virtualTotalHeight }}</div> <div>Total Height: {{ virtualTotalHeight }}</div>
<div>Visible Rows: {{ visibleRows.length }}</div> <div>Visible Rows: {{ visibleRows.map((entry) => entry.height).join(",") }}</div>
<div>
Cell Heights:
{{
rowHeights.map((entry) =>
entry.cellHeights?.map((entry) => `${entry.isCustomRenderer ? "*" : ""}${entry.height}`).join(",")
)
}}
</div>
<div>Total Rows: {{ pageData.length }}</div> <div>Total Rows: {{ pageData.length }}</div>
<div>Column widths: {{ columnWidths.map((w) => Math.round(w)).join(",") }}</div>
<div> <div>
Column configs: Column configs:
{{ {{
@ -86,6 +95,8 @@ import { usePretextColumnWidths, type ColumnFlexConfig } from "./usePretextColum
import { usePretextRowHeights, type RowHeightEntry } from "./usePretextRowHeights"; import { usePretextRowHeights, type RowHeightEntry } from "./usePretextRowHeights";
import { useVirtualRows } from "./useVirtualRows"; import { useVirtualRows } from "./useVirtualRows";
const DEFAULT_FONT = "14px sans-serif";
const slots = useSlots(); const slots = useSlots();
const { t } = useI18n(); const { t } = useI18n();
const { state } = useStore(); const { state } = useStore();
@ -119,6 +130,7 @@ interface Props {
rowHeight?: number; rowHeight?: number;
estimatedRowHeight?: number; estimatedRowHeight?: number;
headerHeight?: number; headerHeight?: number;
font?: string;
debug?: boolean; debug?: boolean;
} }
@ -139,6 +151,7 @@ const props = withDefaults(defineProps<Props>(), {
rowHeight: undefined, rowHeight: undefined,
estimatedRowHeight: undefined, estimatedRowHeight: undefined,
headerHeight: 44, headerHeight: 44,
font: DEFAULT_FONT,
debug: false, debug: false,
}); });
@ -173,26 +186,88 @@ const columnsRef = toRef(props, "columns");
// Pass computed pageData to hooks // Pass computed pageData to hooks
const pageDataRef = toRef(pageData); const pageDataRef = toRef(pageData);
// =============================================================================
// Cell formatting
// =============================================================================
const getHeaderText = (col: ListTableColumn<T>): string => {
if (col.name) return col.name;
if (col.i18n) return t(col.i18n);
return col.key;
};
const formatCellValue = (row: T, col: ListTableColumn<T>): string => {
const value = (row as any)[col.dataKey || col.key];
if (col.dict) {
return formatterByDist(col.dict, value);
}
if (col.timestamp) {
return formatStamp(value);
}
if (col.filesize) {
return formatFileSize(value);
}
if (value === undefined || value === null || value === "") {
return "--";
}
return String(value);
};
const getValue = (value: any): string => {
if ((typeof value === "undefined" || value === null || value === "") && value !== 0) {
return "--";
}
return value;
};
const formatStamp = (value: any): string => {
const date = new Date(value * 1000);
const month = date.getMonth() < 9 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
const day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
const hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
const minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
const second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return `${date.getFullYear()}-${month}-${day} ${hour}:${minute}:${second}`;
};
const formatFileSize = (value: any): string => {
const k = value / 1024;
if (k < 1) return value + "B";
const m = k / 1024;
if (m < 1) return k.toFixed(2) + "K";
const g = m / 1024;
if (g < 1) return m.toFixed(2) + "M";
return g.toFixed(2) + "G";
};
const formatterByDist = (dictKey: string, cellData: any): string => {
if (!dictKey) return getValue(cellData);
const mapping = (state as any).dict[dictKey];
if (mapping == null) return getValue(cellData);
return mapping[cellData] == null ? cellData : mapping[cellData];
};
// ============================================================================= // =============================================================================
// Wire hooks // Wire hooks
// ============================================================================= // =============================================================================
// Column width computation via pretext // Column width computation via pretext
const { computedConfigs, totalFlexBasis } = usePretextColumnWidths(pageDataRef, columnsRef, containerWidth, { const { computedConfigs, totalFlexBasis, columnWidths } = usePretextColumnWidths(
font: "14px Inter, sans-serif", pageDataRef,
headerFont: "bold 14px Inter, sans-serif", columnsRef,
}); containerWidth,
{
// Column widths as array of numbers (for usePretextRowHeights) font: "14px Inter, sans-serif",
const columnWidths = computed<number[]>(() => { headerFont: "bold 14px Inter, sans-serif",
return computedConfigs.value.map((c) => c.flexBasis); }
}); );
// Row heights via pretext // Row heights via pretext - use actual column widths from flexbox algorithm
const { rowHeights, totalHeight } = usePretextRowHeights(pageDataRef, columnsRef, columnWidths, { const { rowHeights, totalHeight } = usePretextRowHeights(pageDataRef, columnsRef, columnWidths, formatCellValue, {
font: "14px Inter, sans-serif", font: props.font,
lineHeight: 20, lineHeight: 20,
rowPadding: 12, debug: props.debug,
}); });
// Virtualizer (needs rowHeights as number[] and viewportHeight) // Virtualizer (needs rowHeights as number[] and viewportHeight)
@ -237,14 +312,28 @@ const tableContainerStyle = computed(() => {
// Column styling // Column styling
// ============================================================================= // =============================================================================
function getColumnStyle(col: ListTableColumn<T>): Record<string, string> { function getColumnStyle(col: ListTableColumn<T>): Record<string, string> {
const config = computedConfigs.value.find((c) => c.key === col.key); // Find the column index to get the computed width
if (!config) { const colIndex = columnsRef.value.findIndex((c) => c.key === col.key);
return { flex: "1 1 100px", minWidth: "50px", maxWidth: "300px" }; const computedWidth = colIndex >= 0 ? columnWidths.value[colIndex] : 0;
if (!computedWidth || computedWidth <= 0) {
// Fallback to flex property if no computed width
const config = computedConfigs.value.find((c) => c.key === col.key);
if (!config) {
return { flex: "1 1 100px", minWidth: "50px", maxWidth: "300px" };
}
return {
flex: `${config.flexGrow} ${config.flexShrink} ${config.flexBasis}px`,
minWidth: `${config.minWidth}px`,
maxWidth: `${config.maxWidth}px`,
};
} }
// Use the yoga-computed width directly for consistent measurement
return { return {
flex: `${config.flexGrow} ${config.flexShrink} ${config.flexBasis}px`, width: `${computedWidth}px`,
minWidth: `${config.minWidth}px`, minWidth: `${computedWidth}px`,
maxWidth: `${config.maxWidth}px`, maxWidth: `${computedWidth}px`,
}; };
} }
@ -255,68 +344,6 @@ const resolvedHeaderHeight = computed(() => {
return props.headerHeight ?? 44; return props.headerHeight ?? 44;
}); });
// =============================================================================
// Cell formatting
// =============================================================================
const getHeaderText = (col: ListTableColumn<T>): string => {
if (col.name) return col.name;
if (col.i18n) return t(col.i18n);
return col.key;
};
const formatCellValue = (row: T, col: ListTableColumn<T>): string => {
const value = (row as any)[col.dataKey || col.key];
if (col.dict) {
return formatterByDist(col.dict, value);
}
if (col.timestamp) {
return formatStamp(value);
}
if (col.filesize) {
return formatFileSize(value);
}
if (value === undefined || value === null || value === "") {
return "--";
}
return String(value);
};
const getValue = (value: any): string => {
if ((typeof value === "undefined" || value === null || value === "") && value !== 0) {
return "--";
}
return value;
};
const formatStamp = (value: any): string => {
const date = new Date(value * 1000);
const month = date.getMonth() < 9 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
const day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
const hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
const minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
const second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return `${date.getFullYear()}-${month}-${day} ${hour}:${minute}:${second}`;
};
const formatFileSize = (value: any): string => {
const k = value / 1024;
if (k < 1) return value + "B";
const m = k / 1024;
if (m < 1) return k.toFixed(2) + "K";
const g = m / 1024;
if (g < 1) return m.toFixed(2) + "M";
return g.toFixed(2) + "G";
};
const formatterByDist = (dictKey: string, cellData: any): string => {
if (!dictKey) return getValue(cellData);
const mapping = (state as any).dict[dictKey];
if (mapping == null) return getValue(cellData);
return mapping[cellData] == null ? cellData : mapping[cellData];
};
// ============================================================================= // =============================================================================
// Scroll handling // Scroll handling
// ============================================================================= // =============================================================================
@ -499,7 +526,7 @@ onUnmounted(() => {
white-space: normal; white-space: normal;
word-break: break-word; word-break: break-word;
color: v-bind("state.style.tableColor"); color: v-bind("state.style.tableColor");
font-size: var(--el-font-size-base); font: v-bind("props.font");
} }
.my-pagination { .my-pagination {

134
packages/base/data/list-table-v2/usePretextColumnWidths.ts

@ -53,8 +53,7 @@ function computeStats(widths: number[]): { mean: number; variance: number } {
if (widths.length === 0) return { mean: 0, variance: 0 }; if (widths.length === 0) return { mean: 0, variance: 0 };
const mean = widths.reduce((sum, w) => sum + w, 0) / widths.length; const mean = widths.reduce((sum, w) => sum + w, 0) / widths.length;
const variance = const variance = widths.reduce((sum, w) => sum + Math.pow(w - mean, 2), 0) / widths.length;
widths.reduce((sum, w) => sum + Math.pow(w - mean, 2), 0) / widths.length;
return { mean, variance }; return { mean, variance };
} }
@ -71,6 +70,118 @@ function varianceToFlex(mean: number, variance: number): number {
return 0.1 + varianceScore * 1.9; return 0.1 + varianceScore * 1.9;
} }
/**
* Compute actual column widths using a flexbox-inspired distribution algorithm.
* - First, compute raw flex widths based on available space
* - Then, enforce minWidth/maxWidth constraints iteratively
* - If constraints cause overflow, reduce widths proportionally while respecting mins
*/
export function computeFlexWidths(configs: ColumnFlexConfig[], containerWidth: number): number[] {
if (!configs.length || containerWidth <= 0) {
return configs.map(() => 0);
}
// Calculate total flex basis
const totalFlexBasis = configs.reduce((sum, c) => sum + c.flexBasis, 0);
if (totalFlexBasis === 0) {
// Edge case: all zero bases - distribute evenly
const evenWidth = containerWidth / configs.length;
return configs.map((c) => clamp(evenWidth, c.minWidth, c.maxWidth));
}
// Calculate raw flex widths
let widths = computeRawFlexWidths(configs, containerWidth, totalFlexBasis);
// Apply min/max constraints iteratively until stable
// This handles the case where clamping some columns causes others to overflow
for (let iteration = 0; iteration < 10; iteration++) {
// Apply constraints
widths = widths.map((w, i) => clamp(w, configs[i].minWidth, configs[i].maxWidth));
const totalWidth = widths.reduce((sum, w) => sum + w, 0);
if (totalWidth <= containerWidth) {
// Fits! Distribute remaining space proportionally based on flexGrow
const remaining = containerWidth - totalWidth;
if (remaining > 0) {
const totalFlexGrow = configs.reduce((sum, c) => sum + c.flexGrow * c.flexBasis, 0);
if (totalFlexGrow > 0) {
widths = widths.map((w, i) => {
const growAmount = (configs[i].flexGrow * configs[i].flexBasis) / totalFlexGrow * remaining;
return w + growAmount;
});
}
}
break;
}
// Overflow - need to reduce widths that are above their minimums
const overflow = totalWidth - containerWidth;
// Calculate how much each column can be reduced (only those above minWidth)
const reducible = widths.map((w, i) => Math.max(0, w - configs[i].minWidth));
const totalReducible = reducible.reduce((sum, r) => sum + r, 0);
if (totalReducible === 0) {
// All at minimum - force even distribution
const evenWidth = containerWidth / configs.length;
widths = configs.map((c) => clamp(evenWidth, c.minWidth, c.maxWidth));
break;
}
// Reduce proportionally based on how much each column can be reduced
widths = widths.map((w, i) => {
const reduction = reducible[i] / totalReducible * overflow;
return Math.max(w - reduction, configs[i].minWidth);
});
}
// Final clamp
widths = widths.map((w, i) => clamp(w, configs[i].minWidth, configs[i].maxWidth));
return widths;
}
/**
* Compute raw flex widths without min/max constraints
*/
function computeRawFlexWidths(configs: ColumnFlexConfig[], containerWidth: number, totalFlexBasis: number): number[] {
const availableSpace = containerWidth - totalFlexBasis;
if (availableSpace === 0) {
return configs.map((c) => c.flexBasis);
}
if (availableSpace > 0) {
// Grow phase: distribute extra space proportionally by flexGrow * flexBasis
const totalGrowFactor = configs.reduce((sum, c) => sum + c.flexGrow * c.flexBasis, 0);
if (totalGrowFactor === 0) {
return configs.map((c) => c.flexBasis);
}
return configs.map((c) => {
const growAmount = (c.flexGrow * c.flexBasis) / totalGrowFactor * availableSpace;
return c.flexBasis + growAmount;
});
} else {
// Shrink phase: remove space proportionally by flexShrink * flexBasis
const totalShrinkFactor = configs.reduce((sum, c) => sum + c.flexShrink * c.flexBasis, 0);
if (totalShrinkFactor === 0) {
// No shrink - distribute loss evenly
const evenShrink = (-availableSpace) / configs.length;
return configs.map((c) => Math.max(0, c.flexBasis - evenShrink));
}
return configs.map((c) => {
const shrinkAmount = (c.flexShrink * c.flexBasis) / totalShrinkFactor * (-availableSpace);
return Math.max(0, c.flexBasis - shrinkAmount);
});
}
}
function clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}
export function usePretextColumnWidths<T>( export function usePretextColumnWidths<T>(
data: Ref<T[]>, data: Ref<T[]>,
columns: Ref<ListTableColumn<T>[]>, columns: Ref<ListTableColumn<T>[]>,
@ -97,14 +208,9 @@ export function usePretextColumnWidths<T>(
// User overrides (convert string values to numbers) // User overrides (convert string values to numbers)
const userFlexGrow = col.flexGrow; const userFlexGrow = col.flexGrow;
const userFlexShrink = col.flexShrink; const userFlexShrink = col.flexShrink;
const userFlexBasis = const userFlexBasis = col.flexBasis !== undefined && col.flexBasis !== "auto" ? Number(col.flexBasis) : undefined;
col.flexBasis !== undefined && col.flexBasis !== "auto" const userMinWidth = col.minWidth !== undefined ? Number(col.minWidth) : undefined;
? Number(col.flexBasis) const userMaxWidth = col.maxWidth !== undefined ? Number(col.maxWidth) : undefined;
: undefined;
const userMinWidth =
col.minWidth !== undefined ? Number(col.minWidth) : undefined;
const userMaxWidth =
col.maxWidth !== undefined ? Number(col.maxWidth) : undefined;
// Measure header width // Measure header width
const headerText = col.name || col.i18n || colKey; const headerText = col.name || col.i18n || colKey;
@ -151,12 +257,14 @@ export function usePretextColumnWidths<T>(
}); });
// Total flex basis (sum of all flexBasis values) // Total flex basis (sum of all flexBasis values)
const totalFlexBasis = computed(() => const totalFlexBasis = computed(() => computedConfigs.value.reduce((sum, c) => sum + c.flexBasis, 0));
computedConfigs.value.reduce((sum, c) => sum + c.flexBasis, 0)
); // Actual column widths computed using flexbox algorithm
const columnWidths = computed(() => computeFlexWidths(computedConfigs.value, containerWidth.value));
return { return {
computedConfigs, computedConfigs,
totalFlexBasis, totalFlexBasis,
columnWidths,
}; };
} }

38
packages/base/data/list-table-v2/usePretextRowHeights.ts

@ -11,22 +11,25 @@ import type { ListTableColumn } from "./types";
const DEFAULT_FONT = "14px Inter, sans-serif"; const DEFAULT_FONT = "14px Inter, sans-serif";
const DEFAULT_LINE_HEIGHT = 20; const DEFAULT_LINE_HEIGHT = 20;
const DEFAULT_ROW_PADDING = 12; const DEFAULT_ROW_PADDING = 5;
const CELL_VERTICAL_PADDING = 8; // top + bottom per cell const CELL_VERTICAL_PADDING = 12; // top + bottom per cell
export interface RowHeightEntry { export interface RowHeightEntry {
height: number; height: number;
isCustomRenderer: boolean; isCustomRenderer: boolean;
cellHeights?: RowHeightEntry[];
} }
export function usePretextRowHeights<T>( export function usePretextRowHeights<T>(
data: Ref<T[]>, data: Ref<T[]>,
columns: Ref<ListTableColumn<T>[]>, columns: Ref<ListTableColumn<T>[]>,
columnWidths: Ref<number[]>, columnWidths: Ref<number[]>,
formatCellValue: (row: T, col: ListTableColumn<T>) => string,
options?: { options?: {
font?: string; font?: string;
lineHeight?: number; lineHeight?: number;
rowPadding?: number; rowPadding?: number;
debug?: boolean;
} }
) { ) {
const font = options?.font ?? DEFAULT_FONT; const font = options?.font ?? DEFAULT_FONT;
@ -40,9 +43,12 @@ export function usePretextRowHeights<T>(
return data.value.map((row) => { return data.value.map((row) => {
let maxCellHeight = lineHeight; // minimum 1 line let maxCellHeight = lineHeight; // minimum 1 line
let cellHeights: RowHeightEntry[] | undefined = options?.debug ? [] : undefined;
for (let i = 0; i < columns.value.length; i++) { for (let i = 0; i < columns.value.length; i++) {
const col = columns.value[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; const colWidth = columnWidths.value[i] ?? 100;
// Check if custom renderer exists (we can't measure these with pretext) // Check if custom renderer exists (we can't measure these with pretext)
@ -54,13 +60,16 @@ export function usePretextRowHeights<T>(
// For now, use a reasonable minimum // For now, use a reasonable minimum
const placeholderHeight = 44; // default row height const placeholderHeight = 44; // default row height
maxCellHeight = Math.max(maxCellHeight, placeholderHeight); maxCellHeight = Math.max(maxCellHeight, placeholderHeight);
cellHeights?.push({
height: placeholderHeight,
isCustomRenderer: true,
});
continue; continue;
} }
// Get raw cell value // Get raw cell value
const rawValue = (row as any)[col.dataKey || col.key]; const cellText = formatCellValue(row, col);
const cellText = rawValue == null ? "" : String(rawValue);
if (!cellText) continue; if (!cellText) continue;
// Calculate available width for text (excluding cell padding) // Calculate available width for text (excluding cell padding)
@ -68,31 +77,30 @@ export function usePretextRowHeights<T>(
if (availableWidth <= 0) continue; if (availableWidth <= 0) continue;
try { try {
const cellHeight = measureTextHeight( const cellHeight = measureTextHeight(cellText, font, availableWidth, lineHeight);
cellText,
font,
availableWidth,
lineHeight
);
maxCellHeight = Math.max(maxCellHeight, cellHeight); maxCellHeight = Math.max(maxCellHeight, cellHeight);
cellHeights?.push({
height: cellHeight,
isCustomRenderer: false,
});
} catch { } catch {
// Fallback: assume single line // Fallback: assume single line
} }
} }
const totalHeight = maxCellHeight + rowPadding * 2 + CELL_VERTICAL_PADDING * 2; const totalHeight = maxCellHeight + rowPadding * 2;
return { return {
height: totalHeight, height: totalHeight,
isCustomRenderer: false, isCustomRenderer: false,
cellHeights: cellHeights ?? undefined,
}; };
}); });
}); });
// Total height (sum of all row heights) - useful for virtualizer // Total height (sum of all row heights) - useful for virtualizer
const totalHeight = computed(() => const totalHeight = computed(() => rowHeights.value.reduce((sum, entry) => sum + entry.height, 0));
rowHeights.value.reduce((sum, entry) => sum + entry.height, 0)
);
return { return {
rowHeights, rowHeights,

Loading…
Cancel
Save