Hone logo
Hone
Problems

React Masonry Layout Component

Creating a masonry layout is a common requirement for displaying content in a visually appealing and dynamic way, often seen on image-heavy websites or portfolios. This challenge asks you to build a reusable React component that implements a masonry layout, dynamically arranging items to fill the available space efficiently. This component will be useful for displaying content where items have varying heights and a flexible, responsive arrangement is desired.

Problem Description

You are tasked with creating a Masonry component in React using TypeScript. This component should accept an array of items as a prop and render them in a masonry layout. Each item will be represented by a simple <div> element with a unique data-id attribute. The component should dynamically calculate the positions of each item to create the masonry effect, ensuring that items are placed as close to the top-left as possible, without overlapping. The layout should be responsive, adjusting to different screen sizes and container widths.

Key Requirements:

  • Dynamic Item Placement: The component must dynamically calculate the position of each item based on the heights of previously placed items.
  • Responsive Layout: The layout should adapt to different screen sizes and container widths.
  • Reusable Component: The component should be generic enough to handle different types of content within the items.
  • TypeScript: The code must be written in TypeScript.
  • No External Libraries: You are not allowed to use external libraries like react-masonry-css or similar. The masonry layout logic must be implemented from scratch.

Expected Behavior:

  • The component should render a container element that holds the masonry layout.
  • Each item in the input array should be rendered as a <div> element within the container.
  • The <div> elements should be positioned using CSS to create the masonry effect.
  • The layout should reflow as the window size changes.
  • Items should be placed in columns, with the number of columns determined by the container width and a configurable columns prop (defaulting to 3).

Edge Cases to Consider:

  • Empty input array: The component should render an empty container.
  • Single item in the array: The component should render the single item without any masonry effect.
  • Items with varying heights: The component should correctly handle items with significantly different heights.
  • Container width smaller than item width: The component should handle this gracefully, potentially wrapping items to the next column.
  • Very large number of items: Consider performance implications and potential optimizations.

Examples

Example 1:

Input:  items = [{ id: 1, height: 100 }, { id: 2, height: 200 }, { id: 3, height: 150 }, { id: 4, height: 250 }, { id: 5, height: 120 }]
Output: A masonry layout with 5 items arranged in columns, with varying heights.
Explanation: The items are positioned to minimize empty space and create a visually appealing masonry effect. The number of columns is determined by the container width (defaulting to 3).

Example 2:

Input: items = []
Output: An empty container element.
Explanation: When the input array is empty, the component renders an empty container without any items.

Example 3:

Input: items = [{ id: 1, height: 300 }]
Output: A single item rendered within the container.
Explanation: When there is only one item, it is rendered without any masonry effect.

Constraints

  • Container Width: The container width should be responsive and adapt to the browser window size. Assume the container will take up 100% of its parent's width.
  • Item Height: Each item in the items array will have a height property (number) representing its desired height.
  • Columns: The component should accept an optional columns prop (number, default 3) to control the number of columns in the layout.
  • Performance: The component should be reasonably performant, even with a large number of items (e.g., up to 100). Avoid unnecessary re-renders.
  • CSS: Use CSS for layout, not JavaScript.

Notes

  • Consider using CSS Grid or Flexbox to achieve the masonry layout. While CSS Grid is generally preferred for this type of layout, Flexbox can also be used.
  • Think about how to efficiently calculate the positions of the items without excessive DOM manipulation.
  • The data-id attribute on each item <div> is for identification purposes and can be used for styling or other purposes.
  • Focus on creating a clean, reusable, and well-documented component.
  • The height property of each item is a desired height. The actual height of the rendered item might be different due to CSS styling. The masonry layout should still work correctly.
Loading editor...
typescript