react-native-flexible-grid
Version:
React Native Flexible Grid is an advanced grid layout system inspired by CSS Grid, designed to facilitate responsive, customizable, and dynamic grid layouts in React Native applications. It supports both responsive and fixed layouts, enabling the creation
126 lines (104 loc) • 3.63 kB
text/typescript
import type { GridItem, TileItem } from './types';
export const calcResponsiveGrid = (
data: TileItem[],
maxItemsPerColumn: number,
containerWidth: number,
itemUnitHeight?: number,
autoAdjustItemWidth: boolean = true
): {
gridItems: GridItem[];
gridViewHeight: number;
} => {
const gridItems: GridItem[] = [];
const itemSizeUnit = containerWidth / maxItemsPerColumn;
let columnHeights: number[] = new Array(maxItemsPerColumn).fill(0);
const findAvailableWidth = (
startColumn: number,
currentTop: number
): number => {
// Check each column from the start position
let availableWidth = 0;
for (let i = startColumn; i < maxItemsPerColumn; i++) {
// Check if there's any item from above rows protruding into this space
const hasProtrudingItem = gridItems.some((item) => {
const itemBottom = item.top + item.height;
const itemRight = item.left + item.width;
return (
item.top < currentTop && // Item starts above current row
itemBottom > currentTop && // Item extends into current row
item.left <= i * itemSizeUnit && // Item starts at or before this column
itemRight > i * itemSizeUnit // Item extends into this column
);
});
if (hasProtrudingItem) {
break; // Stop counting available width when we hit a protruding item
}
availableWidth++;
}
return availableWidth;
};
data.forEach((item) => {
let widthRatio = item.widthRatio || 1;
const heightRatio = item.heightRatio || 1;
let columnIndex = findColumnForItem(
columnHeights,
widthRatio,
maxItemsPerColumn
);
if (autoAdjustItemWidth) {
// Get current row's height at the column index
const currentTop = columnHeights[columnIndex];
// Calculate available width considering both row end and protruding items
const availableWidth = findAvailableWidth(columnIndex, currentTop!);
// If widthRatio exceeds available space, adjust it
if (widthRatio > availableWidth) {
widthRatio = Math.max(1, availableWidth);
}
}
const itemWidth = widthRatio * itemSizeUnit;
const itemHeight = itemUnitHeight
? itemUnitHeight * heightRatio
: heightRatio * itemSizeUnit;
const top = columnHeights[columnIndex]!;
const left = columnIndex * itemSizeUnit;
gridItems.push({
...item,
top,
left,
width: itemWidth,
height: itemHeight,
});
// Update the column heights
for (let i = columnIndex; i < columnIndex + widthRatio; i++) {
columnHeights[i] = top + itemHeight;
}
});
return {
gridItems,
gridViewHeight: Math.max(...columnHeights),
};
};
const findColumnForItem = (
columnHeights: number[],
widthRatio: number,
maxItemsPerColumn: number
) => {
// If the item spans only one column, find the shortest column.
if (widthRatio === 1) {
return columnHeights.indexOf(Math.min(...columnHeights));
}
// If the item spans multiple columns, find the first place it can fit.
let minHeight = Math.min(...columnHeights);
let columnIndex = columnHeights.indexOf(minHeight);
for (let i = 0; i <= maxItemsPerColumn - widthRatio; i++) {
// Check if the item can fit in the next 'widthRatio' columns.
const columnsToCheck = columnHeights.slice(i, i + widthRatio);
if (columnsToCheck.every((height) => height === minHeight)) {
columnIndex = i;
break;
}
// Find the next set of columns where the item can fit.
minHeight = Math.min(...columnsToCheck);
}
return columnIndex;
};