dantrain / react-stonecutter

Animated grid layout component for React

Home Page:http://dantrain.github.io/react-stonecutter

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Help implementing custom layout

diegoaguilar opened this issue · comments

I've found myself with the following situation:

  • Items placed using pinterest layout are properly arranged. No overlapping, gutters correctly place and distributed
  • Items got an image and text
  • simple layout would just overlap / smash every item
  • I don't particularly need à la pinterest layout but every column starting a a common height

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

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