Animated grid layout component for React

Overview

react-stonecutter npm version

Animated grid layout component for React, inspired by Masonry.
Choose between CSS Transitions or React-Motion for animation.

Demo

Installation

With npm:

npm install --save react-stonecutter

Usage

A simple layout with items of equal height:

import { SpringGrid } from 'react-stonecutter';
<SpringGrid
  component="ul"
  columns={5}
  columnWidth={150}
  gutterWidth={5}
  gutterHeight={5}
  itemHeight={200}
  springConfig={{ stiffness: 170, damping: 26 }}
>
  <li key="A">A</li>
  <li key="B">B</li>
  <li key="C">C</li>
</SpringGrid>

A Pinterest-style layout with varying item heights, this time using CSS transitions:

import { CSSGrid, layout } from 'react-stonecutter';
<CSSGrid
  component="ul"
  columns={5}
  columnWidth={150}
  gutterWidth={5}
  gutterHeight={5}
  layout={layout.pinterest}
  duration={800}
  easing="ease-out"
>
  <li key="A" itemHeight={150}>A</li>
  <li key="B" itemHeight={120}>B</li>
  <li key="C" itemHeight={170}>C</li>
</CSSGrid>

To render React components as children, wrap them in simple elements. This gives you full control of your markup:

<SpringGrid
  component="ul"
  // ...etc.
>
  {data.map((datum) => (
    <li key={datum.id}>
      <MyComponent datum={datum} />
    </li>
  ))}
</SpringGrid>

If you don't know the heights of your items ahead of time, use the measureItems higher-order component to measure them in the browser before layout:

import { SpringGrid, measureItems } from 'react-stonecutter';

const Grid = measureItems(SpringGrid);
<Grid
  // ...etc.
>
  <li key="A">Who controls the British crown?</li>
  <li key="B">Who keeps the metric system down?</li>
  <li key="C">We do!</li>
  <li key="D">We do!</li>
</Grid>

If your grid spans the page and you want to vary the number of columns based on the viewport width, use the makeResponsive higher-order component which makes use of enquire.js:

import { CSSGrid, measureItems, makeResponsive } from 'react-stonecutter';

const Grid = makeResponsive(measureItems(CSSGrid), {
  maxWidth: 1920,
  minPadding: 100
});

API Reference

Exports:

  • SpringGrid
  • CSSGrid
  • measureItems
  • makeResponsive
  • layout
  • enterExitStyle
  • easings

SpringGrid and CSSGrid props

columns={Number}
Number of columns. Required.
You can wrap the Grid component in the makeResponsive higher-order component to set this dynamically.

columnWidth={Number}
Width of a single column, by default in px units. Required.

gutterWidth={Number}
Width of space between columns. Default: 0.

gutterHeight={Number}
Height of vertical space between items. Default: 0.

component={String}
Change the HTML tagName of the Grid element, for example to 'ul' or 'ol' for a list. Default: 'div'.

layout={Function}
Use one of the included layouts, or create your own. Defaults to a 'simple' layout with items of fixed height.
Included layouts:

import { layout } from 'react-stonecutter';

const { simple, pinterest } = layout;

The function is passed two parameters; an Array of the props of each item, and the props of the Grid itself. It must return an object with this shape:

{
  positions: // an Array of [x, y] coordinate pairs like this: [[0, 0], [20, 0], [0, 30]]
  gridWidth: // width of the entire grid (Number)
  gridHeight: // height of the entire grid (Number)
}

Have a look at the code for the included layouts to get a feel for creating your own.

enter={Function}
entered={Function}
exit={Function}
These allow you to change how items animate as they appear and disappear from the grid. Supply functions that return objects with the opacity and transform values for an item's start and end states. By default the item's scale and opacity go from 0 to 1 and back to 0 on exit, like this:

enter={() => ({ scale: 0, opacity: 0 })}
entered={() => ({ scale: 1, opacity: 1 })}
exit={() => ({ scale: 0, opacity: 0 })}

The functions are passed three parameters, the item props, grid props and grid state which includes the current height and width of the grid. For example to have disappearing items fall off the bottom of the grid:

exit={(itemProps, gridProps, gridState) => ({ translateY: gridState.gridHeight + 500 })}

CSS transform-functions are split up so they can be easily animated individually. Supported functions:

  • translateX
  • translateY
  • translateZ
  • skew
  • skewX
  • skewY
  • scale
  • scaleX
  • scaleY
  • rotate
  • rotateX
  • rotateY

Some example functions are included:

import { enterExitStyle } from 'react-stonecutter';

const { enter, entered, exit } = enterExitStyle.foldUp;

Check out the demo to see them in action.

perspective={Number}
The perspective distance used for 3D transforms. If you are using a transform function like rotateX, use this to strengthen the effect. Default is no perspective applied.

lengthUnit={String}
The length unit used throughout. Default: 'px'. Experimental. You could try using 'em' or 'rem' and then adjust the font-size for a fluid layout, but it may not work well with the measureItems and makeResponsive higher-order components. % does not work well due to the way CSS transforms work.

angleUnit={String}
The angle unit. Affects transform-functions such as rotate. Default: 'deg'.

SpringGrid only props

springConfig={Object}
Configuration of the React-Motion spring. See the React-Motion docs for more info. Default: { stiffness: 60, damping: 14, precision: 0.1 }.

CSSGrid only props

duration={Number}
Animation duration in ms. Required.

easing={String}
Animation easing function in CSS transition-timing-function format. Some Penner easings are included for convenience:

import { easings } from 'react-stonecutter';

const { quadIn, quadOut, /* ..etc. */  } = easings;

Default: easings.cubicOut.

measureItems options

Pass like this:

const Grid = measureItems(SpringGrid, { measureImages: true })

measureImages: Boolean
If set to true, waits for images to load before measuring items and adding them to the Grid. This may be necessary if you don't know the height of your images ahead of time. Powered by imagesLoaded.

background: Boolean|String
This option is passed through to the imagesLoaded library. It allows you to wait for background images to load, in addition to <img> tags.

makeResponsive options

Pass like this:

const Grid = makeResponsive(SpringGrid, { maxWidth: 1920 })

maxWidth: Number
Maximum width for the Grid in px.

minPadding: Number
Minimum horizontal length between the edge of the Grid and the edge of the viewport in px. Default: 0.

defaultColumns: Number
Default number of columns before the breakpoints kick in. May be useful when rendering server-side in a universal app. Default: 4.

Alternatives

If you have a list already nicely laid out by the browser, check out React Flip Move which uses the very cool FLIP technique.

License

MIT

Issues
  • Images are overlapped.

    Images are overlapped.

    Hi ,

    I don't know the heights of my items ahead of time and try to use the measureItems higher-order component to measure them in the browser before layout using the following snippet.

    import { SpringGrid, measureItems } from 'react-stonecutter';

    const Grid = measureItems(SpringGrid); <Grid component="ul" columns={3} columnWidth={350} gutterWidth={3} gutterHeight={3} layout={layout.pinterest}

  • IMG A
  • IMG B
  • IMG C
  • IMG D
  • But I am getting all images are overlapped with the single image. Any idea about this issue. I want to implement "Pinterest grid layout".

    image

    enhancement 
    opened by manimuthukumar6 7
  • when add item too frequent,  layout got exception

    when add item too frequent, layout got exception

    image

    the reason is that when imagesloaded callback is called, the DOM still don't have height (0). so when layout try to layout the item it throws the error.

    my current solution is using lodash debounce to slow down the speed of add items.

    i will fix it by check the height element when imagesloaded

    Uncaught Error: Each child must have an "itemHeight" prop or an "itemRect.height" prop.(anonymous function) @ react-stonecutter.js:1298t.default @ react-stonecutter.js:1292t.default.u.default.createClass.doLayout @ react-stonecutter.js:403CSSGrid_componentWillReceiveProps @ react-stonecutter.js:399O.updateComponent @ ReactCompositeComponent.js:601O.receiveComponent @ ReactCompositeComponent.js:534s.receiveComponent @ ReactReconciler.js:131l.updateChildren @ ReactChildReconciler.js:94D.Mixin._reconcilerUpdateChildren @ ReactMultiChild.js:213D.Mixin._updateChildren @ ReactMultiChild.js:315D.Mixin.updateChildren @ ReactMultiChild.js:303v.Mixin._updateDOMChildren @ ReactDOMComponent.js:938v.Mixin.updateComponent @ ReactDOMComponent.js:767v.Mixin.receiveComponent @ ReactDOMComponent.js:723s.receiveComponent @ ReactReconciler.js:131O._updateRenderedComponent @ ReactCompositeComponent.js:737O._performComponentUpdate @ ReactCompositeComponent.js:715O.updateComponent @ ReactCompositeComponent.js:634O.performUpdateIfNecessary @ ReactCompositeComponent.js:548s.performUpdateIfNecessary @ ReactReconciler.js:165u @ ReactUpdates.js:151i.perform @ Transaction.js:138i.perform @ Transaction.js:138p.perform @ ReactUpdates.js:90T @ ReactUpdates.js:173i.closeAll @ Transaction.js:204i.perform @ Transaction.js:151d.batchedUpdates @ ReactDefaultBatchingStrategy.js:63s @ ReactUpdates.js:201o @ ReactUpdateQueue.js:25f.enqueueSetState @ ReactUpdateQueue.js:209o.setState @ ReactComponent.js:64u.default.createClass.updateRects @ react-stonecutter.js:1208r @ index.js:149d @ index.js:196p @ index.js:184
    
    
    opened by stormslowly 7
  • window not defined error

    window not defined error

    If I include this module in my react project I get window undefined error thrown related to enquire.js library.

    bug 
    opened by huntersinclairtiv 6
  • Enter and exit animations not working with makeResponsive.

    Enter and exit animations not working with makeResponsive.

    The description is here: http://stackoverflow.com/questions/41926765/react-stone-cutter-component-enter-and-exit-animations.

    I have the same issue for CSSGrid.

    opened by DariuszMusielak 5
  • Can't scroll page

    Can't scroll page

    Page in question at:

    http://merrillaudio.isaacrivera.com/#/news

    It can't be scrolled. I assume it is re-rendering. Any insights?

    duplicate 
    opened by izkrivera 4
  • Cards collapse all in one piled up deck

    Cards collapse all in one piled up deck

    I have used this tool and it works awesome(great job) but I am facing a problem when minimizing the browser window width below 480px using my mouse or opening the application on a smartphone. Every card is collapsing into each other for all rows and columns and forming like a piled up deck of cards.

    I am using the div as a tag and I created my card using bootstrap grid base system (e.g: col-xs-12 col-sm-12 col-md-12 col-lg-12)

    ===========================

    let Catalogue = measureItems(SpringGrid,{ measureImages: true , background:true });
    Catalogue = makeResponsive(Catalogue, {
                  maxWidth: 1900,
                  minPadding: 80,
                  defaultColumns: 4
                });
    
    const CardCatalogue = ({dataItems}) => {
    
      const setupGrid = {
            layout:layouts.pinterest,//simple, pinterest
            widthColumn:400,
            itemsHeight:530,
            stiffness:250,
            damping:15,
            perspectiveAmt:600,
            guttersWidth:10,
            guttersHeight:10,
            columns:3,
            duration:800
          };
    
      return(
        <Catalogue
          component='div' 
          columnWidth={setupGrid.widthColumn} 
          gutterWidth={setupGrid.guttersWidth} 
          gutterHeight={setupGrid.guttersHeight} 
          layout={setupGrid.layout}
          springConfig={{ stiffness: setupGrid.stiffness, damping: setupGrid.damping }}
          perspective={setupGrid.perspectiveAmt}
          duration={setupGrid.duration}>
        {
          dataItems.map((item) => (
            <div className='card2 tariffCardWidth400px card-1 row noPadMar borderThicker' key={item.id}> <ItemCard  key={item.id} item={item} /></div>
          ))
        }
        </Catalogue>
      );
    };
    
    CardCatalogue.propTypes = {
      dataItems: PropTypes.array.isRequired
    };
    
    export default CardCatalogue;
    

    =========================================

    From what I am seeing in the html source code in chrome the transform style is going from (for 2 items) item1: position: absolute; top: 0px; left: 0px; z-index: 2; opacity: 1; transform: translateX(0px) translateY(0px) perspective(600px) scale(1); item2: position: absolute; top: 0px; left: 0px; z-index: 2; opacity: 1; transform: translateX(0px) translateY(553px) perspective(600px) scale(1);

    into for both of the items position: absolute; top: 0px; left: 0px; z-index: 2; opacity: 1; transform: translateX(-410px) translateY(0px) perspective(600px) scale(1);

    Its weird because -410 is the width for each item 400+10(gutter).

    Am I doing something wrong?

    =================================================

    css that i use on the div inside the map is

    .tariffCardWidth400px{
      width: 400px;
    }
    
    .card2 {
      background: #ffffff;
      border-radius: 1px;
      display: inline-block;
      position: relative;
      overflow-y: auto;
      padding: 0;
      margin: 0;
    border-bottom-width: 40px!important;
      border-bottom-color: #49C5D1!important;
    }
    
    .card-1 {
      box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
      transition: all 0.3s cubic-bezier(.25,.8,.25,1);
    }
    
    .card-1:hover {
      box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
    }
    
    .noPadMar{
      padding-left: 0px!important;
      padding-right: 0px!important;
      padding-top: 0px!important;
      padding-bottom: 0px!important;
    
      margin-left: 0px!important;
      margin-right: 0px!important;
      margin-top: 0px!important;
      margin-bottom: 0px!important;
    }
    
    .borderThicker{
      border-color: #e7eaec;
      border-width: thin;
      border-style: solid;
    }
    
    bug 
    opened by DbTheo84 4
  • Internal Library errors, due to dependancies allowing React 15.4

    Internal Library errors, due to dependancies allowing React 15.4

    React 15.4 changes the internals of react, and react-dom. For both of these dependancies, react-stonecutter uses ^15.3.2 instead of ~15.3.2.

    Using ^ allows npm to skip up to the latest minor version, thus using react 15.4.0. If this dependency changed to ~, stonecutter would stop at 15.3.N, where N is the last published version of 15.3.

    (I'd love just make a pull request for what's basically a one-character change, but sadly I'm on a company-owned machine right now with all kinds of restrictions on open source stuff)

    opened by matthewoden 4
  • Fit to parent

    Fit to parent

    I am wondering if its possible to make the grid fit to the width of the parent, instead of defining a maxWidth via makeResponsive.

    I am using the SprintGrid to render items within a parent div. It seem as though the component always has a fixed width that I can't control unless I specify the full width on init.

    Any ideas? Kai

    opened by kaipradel 3
  • Dynamic height calculation not working properly when images are getting loaded

    Dynamic height calculation not working properly when images are getting loaded

    If it's a new image getting loaded, the calculation is wrong and panels are overlaying cross each other, any suggestions?

    opened by mkliu 3
  • Not isomorphic friendly

    Not isomorphic friendly

    First I must say this is a great package! Easy to use (i actually find the Grid example component easier so I'm using that one).

    I'm using React Starter Kit - www.reactstarterkit.com, isomorphic is great coz it allows my user to see content without any javascript being downloaded. But looks like this library isn't isomorphic friendly. Layout isn't calculated when I render from the server side, therefore even though my content is sent down on the initial load, but no UI is shown.

    I'm not using responsive, so it should be doable to do layout on the sever side. Tried to do some debugging but can't find how to solve it. Any suggestions?

    opened by mkliu 3
  • How to move elements location?

    How to move elements location?

    I am using this control, but I want to move a tile to the bottom if it doesn't have an image associated with it. Is there a way to move a tile programatically? Also, do tiles support resizing and drag and drop?

    opened by lukedukeus 0
  • Add Typescript type definition

    Add Typescript type definition

    I wanted to use this library within a Typescript environment. As I found no other type definitions, I created some based on the documentation from the README.

    I wanted to share them so other may not need to recreate them. You can merge them if you're interested in these definitions and the changes look good for you.

    opened by jzipfler 0
  • React does not recognize the `itemHeight` prop on a DOM element

    React does not recognize the `itemHeight` prop on a DOM element

    Hi when adding itemHeight to an li to a css grid like the example I get the message React does not recognize theitemHeightprop on a DOM element. When I remove the prop I get a warning Uncaught Error: Each child must have an "itemHeight" prop or an "itemRect.height" prop. How can I work around this?

    opened by zanedev 2
  • measureItems causes entire tree to be re-rendered

    measureItems causes entire tree to be re-rendered

    I'm creating my grid like this:

    private render() {
          const Grid = makeResponsive(measureItems(SpringGrid), { maxWidth: 1024, minPadding: 50, defaultColumns: 2 });
          let children = this.props.items.map(i =><li key={i.Id}><ItemCard item={i}  /></li>);
            
          return <div className='items-panel'>
              <Grid component='ul' columnWidth={300} gutterWidth={10} gutterHeight={10} layout={layout.pinterest}>
                        {children}
              </Grid>
          </div>;
    }
    

    When I pass in only one item as my items, I'm seeing ItemsCard's constructor being called twice: Once as a result of being in the elementsToMeasure collection, and then again after a state change when the element has been measured. On that second pass 'cloneElement' adds the itemRect property to the object props, and as a result, the initial object is torn down and removed from the DOM and the new item is added.

    Is there any way to make this more efficient so that it doesn't recalcuate, as my ItemCard is quite a large component.

    Is there some optimization I need to do in my ItemCard? I attempted to add the lifecycle events for componentWillReceiveProps but it is never called due to cloning.

    opened by WieserSoftware 0
  • allow any element

    allow any element

    Currently, if you provide custom react components under <CSSGrid>, it will throw an error. The code is here https://github.com/dantrain/react-stonecutter/blob/master/src/utils/assertIsElement.js

    The solution is to wrap it with an extra <div>. Unfortunately, it doesn't work well for me because I also use react-dnd, and I have a weird bug on mobile.

    Is possible to remove this validation? I am using a fork with this change and it works without any problems. The custom component must only apply provided style to the main <div>.

    enhancement 
    opened by lstkz 1
  • Sorting option for SpringGrid

    Sorting option for SpringGrid

    Update:

    • Added a prop for this option.
    • Added the respective example for demo.
    opened by raghavmac 0
  • Sorting option for SpringGrid

    Sorting option for SpringGrid

    react-stonecutter is an amazing package, many thanks. In addition, I thought it would have been great if it had sorting option for SpringGrid. Currently it works as per the masonry but compromises with the order (well, nothing wrong in it).

    sorted

    enhancement 
    opened by raghavmac 3
  • Undefined this.measureElement in measureElementWithImages

    Undefined this.measureElement in measureElementWithImages

    Hi, I would like to ask about this.measureElement method in measureElement HOC. There is no method like that.

    https://github.com/dantrain/react-stonecutter/commit/058a04d7c21cc776fdd116a5c510f37f35ce3ea4#diff-2a698486281c90b4bc475613f67e168e

    Regards!

    bug 
    opened by admkowal 1
  • Infinte Scroll & Filtering?

    Infinte Scroll & Filtering?

    I think these 2 features are very important. Are you planning to add these features

    enhancement 
    opened by WebTalentTop 1
  • Overlapping items when height changes

    Overlapping items when height changes

    Here is how it looks like. I think the reason is that the item changes size after the grid is created. I don't know how to fix this. Tried measureImages but wont work. Any help? thanks

    image

    image

    opened by hackhat 2
Releases(v0.3.10)
A no-frills flexbox grid system for React, powered by styled-components.

Quick Jump Quick Jump Features Requirements Installation Using yarn Using npm Usage Basic Example Responsive Example Upgrading Documentation Grid.Prov

Garet McKinley 853 Sep 16, 2021
FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved.

FlexLayout is a layout manager that arranges React components in multiple tab sets, tabs can be resized and moved.

Caplin 442 Sep 19, 2021
A multi window layout manager for webapps

Golden Layout Version 2 Version 2.0 is now available from NPM. This version is a substantial change from the previous (1.5.9) version. The change can

null 5.4k Sep 21, 2021
Animated grid layout component for React

react-stonecutter Animated grid layout component for React, inspired by Masonry. Choose between CSS Transitions or React-Motion for animation. Demo In

Dan Train 1.1k Sep 13, 2021
Resizable Flex layout container components for advanced React web applications

About Re-F|ex Re-F|ex is a React flex-based layout component library which I created because none of the components I found out there could satisfy my

Philippe Leefsma 416 Sep 19, 2021
The layout engine for React

Responsive, sortable, filterable and draggable grid layouts with React Design Principles ????‍?? Muuri-React is the React implementation of the amazin

Paolo Longo 211 Sep 15, 2021
Responsive Data Driven Filter Grid & Data Card Component

Responsive Data Driven Filter Grid & Data Card Component

Leonardo Vidal 0 Sep 11, 2021
Unopinionated, standard compliant flexbox component. No propietary APIs. Nothing but flexbox.

This project sunsetting I created this a relatively long time ago, gave it visibility on the internet and used it in multiple projects, in production,

Nacho Álvarez 320 Sep 10, 2021
Auto Responsive Layout Library For React

autoresponsive-react Auto responsive grid layout library for React. Who are using ⭐ ⭐ ⭐ alibaba/ice ⭐ ⭐ ⭐ ice-lab/react-materials ⭐ ⭐ ⭐ ant-design/ant

达峰的夏天 1.5k Sep 16, 2021
Build full featured Windows fluent UI apps using ReactJS.

Build full featured Windows fluent UI apps using ReactJS. Provides a set of accessible, reusable, and composable React components that make it super easy to create websites and apps.

Vivek Verma 147 Sep 17, 2021
A React.js component for using @desandro's Masonry

React Masonry Component IE8 support if you wish to have IE8 support, v2 with React 0.14 is the highest version available. Table of contents Usage Basi

Eirik L. Vullum 1.3k Sep 3, 2021
windows 11 in react 💻🌈⚡

windows 11 in react ????⚡

Blue edge 4.8k Sep 17, 2021
The Masonry List implementation which looks like the `FlatList` in React Native

react-native-masonry-list Pinterest like listview made in React Native. It just behaves like the FlatList so it is easy to use. Installation yarn add

Hyo 109 Sep 22, 2021
React components that allow you to divide a page or container into nestable anchored, scrollable and resizable spaces.

React Spaces An easy to understand and nestable layout system, React Spaces allow you to divide a page or container into anchored, scrollable and resi

Allan Eagle 986 Sep 20, 2021
React splitter component, written in TypeScript.

Splitters for React v. 1.2.0 New version changes fixed issue v. 1.1.0 New version changes fixed issue with getBoundingClientRect in React 16 NPM Demo

Martin Novák 134 Jul 30, 2021
Carpatin is a React JS Admin Dashboard Template that focuses on the management flows of a back-office application. We leverage the Material-UI power of stylizing the components in a way that feels more professional.

Carpatin is a React JS Admin Dashboard Template that focuses on the management flows of a back-office application. We leverage the Material-UI power of stylizing the components in a way that feels more professional.

Devias 30 Sep 8, 2021
Collection of Tailwind Layouts

The website includes a client-side playground which allows components to be rendered and tweaked in real-time.

Alexis H. Munsayac 26 Sep 10, 2021
My portfolio website simulating macOS's GUI, developed with React and tailwindcss.

My portfolio website simulating macOS's GUI, developed with React and tailwindcss.

Xiaohan Zou 2.4k Sep 15, 2021
The UI design language and React library for Conversational UI

The UI design language and React library for Conversational UI

Alibaba 1.1k Sep 16, 2021