dantrain / react-stonecutter

Animated grid layout component for React
http://dantrain.github.io/react-stonecutter
MIT License
1.23k stars 72 forks source link

Help implementing custom layout #51

Open diegoaguilar opened 2 years ago

diegoaguilar commented 2 years ago

I've found myself with the following situation:

I tried to pass a custom layout overwriting the pinterest layout code.

I've got this:

import { chunk } from "lodash";

export default function(items, props) {
  const { columns, columnWidth, gutterWidth, gutterHeight } = props;

  const columnHeights = [];
  for (let i = 0; i < columns; i++) {
    columnHeights.push(0);
  }

  const positions = items.map(itemProps => {
    const column = columnHeights.indexOf(Math.min.apply(null, columnHeights));

    const height =
      itemProps.itemHeight || (itemProps.itemRect && itemProps.itemRect.height);

    if (!(height && typeof height === "number")) {
      throw new Error(
        'Each child must have an "itemHeight" prop or an "itemRect.height" prop.'
      );
    }

    const x = column * columnWidth + column * gutterWidth;
    const y = columnHeights[column];

    columnHeights[column] += Math.round(height) + gutterHeight;

    return [x, y];
  });

  const allHeights = items.map(itemProps => 
    itemProps.itemHeight || (itemProps.itemRect && itemProps.itemRect.height)
  );

  const chunkHeights = chunk(allHeights, columns);

  const gridWidth = columns * columnWidth + (columns - 1) * gutterWidth;
  const gridHeight = Math.max.apply(null, columnHeights);

  const rows = chunk(positions, columns);
  let previewRowYStart = 0;
  let previewRowYEnd = 0;
  const orderedPositions = rows.map((row: any, rowIndex) => {
    const columnsYPositions = row.map(([x, y]) => y);
    const max = Math.max(...columnsYPositions);
    let yPosition = max;
    if (rowIndex !== 0) {
      const overlap = previewRowYEnd >= max;
      if (overlap) yPosition = (max + (previewRowYEnd - max)) * 1.05;
    }

    previewRowYStart = rowIndex === 0 ? 0 : max;
    previewRowYEnd = max + Math.max(...chunkHeights[rowIndex] as number[]);

    return row.map(([ x ]) => [ x, yPosition ]);
  });

  return { positions: orderedPositions.flat(1), gridWidth, gridHeight };
}

Where basically I'm trying to order the item Y position into chunks according to their row, take the highest and set a common Y for all of them. Also I'd take previous row height and detect the chance of an overlap, adding some extra distance if overlapping.

This seems to work except that in some cases, the distance between rows is huge. I've tried to adjust the yPosition calculation when there's an overlap and also reduced the gutter but I can't get a consistent and logic result.

Any suggestions or directions would be appreciated

diegoaguilar commented 2 years ago

@dantrain if you could take a look I'd be so pleased