* Choose the best column layout that balances scroll burden vs whitespace. * * 1. If a single column fits within SCROLL_TOLERANCE × viewportHeight, * use one column — the small scroll is preferable to the whitespace * of an extra column (e.g. one long thread + one tiny thread). * 2. Other
(
heights: number[],
maxColumns: number,
viewportHeight: number,
flexOrder: boolean = false,
minColumns: number = 1,
)
| 2802 | */ |
| 2803 | |
| 2804 | function chooseBestColumnLayout( |
| 2805 | heights: number[], |
| 2806 | maxColumns: number, |
| 2807 | viewportHeight: number, |
| 2808 | flexOrder: boolean = false, |
| 2809 | minColumns: number = 1, |
| 2810 | ): number[][] { |
| 2811 | if (heights.length === 0) return []; |
| 2812 | |
| 2813 | const cap = Math.min(maxColumns, heights.length); |
| 2814 | const start = Math.min(Math.max(minColumns, 1), cap); |
| 2815 | const tolerantHeight = viewportHeight * SCROLL_TOLERANCE; |
| 2816 | |
| 2817 | // Compute effective column height including gaps between threads |
| 2818 | const columnEffectiveHeight = (col: number[]) => { |
| 2819 | const contentH = col.reduce((sum, idx) => sum + heights[idx], 0); |
| 2820 | const gapH = Math.max(0, col.length - 1) * LAYOUT_THREAD_GAP; |
| 2821 | return contentH + gapH; |
| 2822 | }; |
| 2823 | |
| 2824 | // Evaluate every candidate column count (start … cap). |
| 2825 | // Pick the smallest n whose tallest column fits within tolerance. |
| 2826 | // If none fits, pick the one with the shortest tallest column. |
| 2827 | let bestLayout: number[][] = []; |
| 2828 | let bestMaxH = Infinity; |
| 2829 | |
| 2830 | for (let n = start; n <= cap; n++) { |
| 2831 | const layout = computeThreadColumnLayout(heights, n, flexOrder); |
| 2832 | const maxH = Math.max(...layout.map(columnEffectiveHeight)); |
| 2833 | |
| 2834 | // Smallest n that fits within tolerance → least whitespace |
| 2835 | if (maxH <= tolerantHeight) { |
| 2836 | return layout; |
| 2837 | } |
| 2838 | |
| 2839 | // Otherwise track the layout with the shortest tallest column |
| 2840 | if (maxH < bestMaxH) { |
| 2841 | bestMaxH = maxH; |
| 2842 | bestLayout = layout; |
| 2843 | } |
| 2844 | } |
| 2845 | |
| 2846 | return bestLayout; |
| 2847 | } |
| 2848 | |
| 2849 | export const DataThread: FC<{sx?: SxProps}> = function ({ sx }) { |
| 2850 | const { t } = useTranslation(); |
no test coverage detected