expo-popcore-old/components/sker/water-fall/use-masonry-layout.ts

48 lines
1.3 KiB
TypeScript

import { useMemo } from 'react';
export type LayoutItem<T> = {
item: T;
x: number;
y: number;
width: number;
height: number;
};
export type MasonryLayout<T> = {
items: LayoutItem<T>[];
totalHeight: number;
};
export function useMasonryLayout<T>(
data: T[],
numColumns: number,
containerWidth: number,
getItemHeight: (item: T, width: number) => number | undefined,
gap: number,
estimatedHeight: number
): MasonryLayout<T> {
return useMemo(() => {
if (containerWidth <= 0 || data.length === 0) {
return { items: [], totalHeight: 0 };
}
const columnHeights = Array(numColumns).fill(0);
const columnWidth = (containerWidth - gap * (numColumns - 1)) / numColumns;
const layoutItems = data.map((item) => {
const shortestColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));
const x = shortestColumnIndex * (columnWidth + gap);
const y = columnHeights[shortestColumnIndex];
const height = getItemHeight(item, columnWidth) ?? estimatedHeight;
columnHeights[shortestColumnIndex] = y + height + gap;
return { item, x, y, width: columnWidth, height };
});
const totalHeight = Math.max(...columnHeights) - gap;
return { items: layoutItems, totalHeight: Math.max(0, totalHeight) };
}, [data, numColumns, containerWidth, getItemHeight, gap, estimatedHeight]);
}