├── .commitlintrc.js ├── .czrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc ├── .nvm ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENCE.md ├── README.md ├── babel-jest.config.js ├── demo └── src │ ├── Examples │ ├── Masonry │ │ ├── index.js │ │ └── index.md │ ├── Responsive │ │ ├── index.js │ │ └── index.md │ └── index.js │ ├── example.gif │ ├── index.js │ └── routes.js ├── example ├── .gitignore ├── LICENSE ├── README.md ├── gatsby-config.js ├── package.json ├── src │ └── pages │ │ └── index.js ├── static │ └── favicon.ico └── yarn.lock ├── jest.config.js ├── nwb.config.js ├── package.json ├── src ├── Masonry │ ├── index.js │ └── index.test.js ├── ResponsiveMasonry │ ├── index.js │ └── index.test.js └── index.js ├── tsconfig.json ├── types └── index.d.ts └── yarn.lock /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | } 4 | -------------------------------------------------------------------------------- /.czrc: -------------------------------------------------------------------------------- 1 | { 2 | "path": "cz-conventional-changelog" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.md] 12 | indent_size = 4 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true, 6 | jest: true, 7 | "jest/globals": true, 8 | }, 9 | parserOptions: { 10 | sourceType: "module", 11 | ecmaVersion: 2019, 12 | }, 13 | plugins: ["jest"], 14 | extends: ["eslint:recommended", "plugin:react/recommended", "prettier"], 15 | settings: { 16 | react: { 17 | version: "detect", 18 | }, 19 | }, 20 | rules: { 21 | "no-control-regex": 0, 22 | "react/prop-types": "off", 23 | "react/display-name": "off", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /demo/dist 3 | /es 4 | /lib 5 | /node_modules 6 | /umd 7 | *.log 8 | /.yarn 9 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.js": [ 3 | "prettier --no-bracket-spacing --no-semi --trailing-comma=es5 --write", 4 | "git add" 5 | ] 6 | } -------------------------------------------------------------------------------- /.nvm: -------------------------------------------------------------------------------- 1 | v12 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "semi": false, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 13 5 | 6 | before_install: 7 | - npm install codecov 8 | 9 | after_success: 10 | - cat ./coverage/lcov.info | ./node_modules/.bin/codecov 11 | 12 | branches: 13 | only: 14 | - master 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.7.0 - 2025-01-20 2 | 3 | - add responsive gutters by @kriskuiper 4 | 5 | # 2.6.0 - 2024-12-07 6 | 7 | - Added: add `sequential` prop to distribute children by height sequentially by @yangchristina 8 | 9 | # 2.4.0 - 2024-10-12 10 | 11 | - Added: add typescript types by @zvonimirr 12 | 13 | # 2.3.0 - 2024-07-25 14 | 15 | - Added: allow custom masonry and item HTML tags by @zvonimirr 16 | 17 | # 2.2.0 - 2024-02-19 18 | 19 | - Added: add inner width as default by @artemijkurganov 20 | - Fixed: Un even column items when Masonry child is invalid by @sarathjasrin 21 | 22 | # 2.0.0 - 2017-12-18 23 | 24 | - Added: ES6 modules build 25 | - Added: CommonJS build 26 | - Added: UMD build 27 | - Added: Demo 28 | - Added: Tests 29 | - Added: `style` prop 30 | 31 | # 1.5.0 - 2017-12-12 32 | 33 | - Fixed: SSR 34 | 35 | # 1.4.0 - 2017-11-14 36 | 37 | - Added: img alt for a11y accessibility 38 | - Updated: react v16 as a peer dependency 39 | 40 | # 1.3.3 - 2017-09-28 41 | 42 | - Added: custom className support 43 | - Added: single node children support 44 | - Fixed: do not reset this.container 45 | 46 | # 1.2.3 - 2017-06-14 47 | 48 | - Updated: handle default columns count 49 | 50 | # 1.2.2 - 2017-06-03 51 | 52 | - Added: `files` to package.json 53 | 54 | # 1.2.1 - 2017-06-03 55 | 56 | - Updated: Use `npm` to publish package 57 | 58 | # 1.2.0 - 2017-04-14 59 | 60 | - Updated: `react` version to `15.5` 61 | - Added: `prop-types` package in `peerDependencies` 62 | - Removed: `browser` field in `package.json` 63 | - Removed: `dist` task in `package.json` 64 | - Removed: `watch` task in `package.json` 65 | - Removed: `webpack` dev dependency 66 | 67 | # 1.1.0 - 2017-04-13 68 | 69 | - Added: `gutter` prop on `Mansory` component 70 | 71 | # 1.0.0 - 2017-04-05 72 | 73 | - Added: `Masonry` component 74 | - Added: `ResponsiveMasonry` component 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Prerequisites 4 | 5 | [Node.js](http://nodejs.org/) >= v4 must be installed. 6 | 7 | ## Installation 8 | 9 | * Running `yarn` or `npm install` in the components's root directory will install everything you need for development. 10 | 11 | ## Demo Development Server 12 | 13 | * `npm run start` will run a development server with the component's demo app at [http://localhost:1190](http://localhost:1190) with hot module reloading. 14 | 15 | ## Linting 16 | 17 | * `npm run lint` will lint the `src` and `demo/src` folders 18 | 19 | ## Running Tests 20 | 21 | * `npm test` will run the tests once and produce a coverage report in `coverage/`. 22 | 23 | * `npm run test:watch` will run the tests on every change. 24 | 25 | ## Building 26 | 27 | * `npm run build` will build the component for publishing to npm and also bundle the demo app. 28 | 29 | * `npm run clean` will delete built resources. 30 | 31 | > **Builds:** 32 | > 33 | > * CommonJS build => `/lib`, 34 | > * ES6 modules build => `/es` 35 | > * UMD build => `/umd` 36 | > * Demo build => `/demo/dist` 37 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Cédric Delpoux 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-responsive-masonry 2 | 3 | [![npm package][npm-badge]][npm] [![Travis][build-badge]][build] 4 | [![Codecov][codecov-badge]][codecov] ![Module formats][module-formats] 5 | 6 | A lightweight React responsive masonry component built with css flexbox. 7 | 8 | ## Getting started 9 | 10 | [![react-responsive-masonry](https://nodei.co/npm/react-responsive-masonry.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/react-responsive-masonry/) 11 | 12 | You can download `react-responsive-masonry` from the NPM registry via the `npm` or 13 | `yarn` commands 14 | 15 | ```shell 16 | yarn add react-responsive-masonry 17 | npm install react-responsive-masonry --save 18 | ``` 19 | 20 | If you don't use package manager and you want to include `react-responsive-masonry` 21 | directly in your html, you could get it from the UNPKG CDN 22 | 23 | ```html 24 | https://unpkg.com/react-responsive-masonry/umd/react-responsive-masonry.js 25 | ``` 26 | 27 | ## Demo 28 | 29 | See [Demo page][github-page] 30 | 31 | ## Example 32 | 33 | ![React-responsive-masonry gif](/demo/src/example.gif) 34 | 35 | ## Usage 36 | 37 | If you want the number of columns change by resizing the window, you need to wrap the `Masonry` component by the `ResponsiveMasonry` component. 38 | Otherwise, you only need to use the `Masonry` component. 39 | 40 | ```js 41 | import React from "react" 42 | import Masonry, {ResponsiveMasonry} from "react-responsive-masonry" 43 | 44 | // The number of columns and the gutter change by resizing the window 45 | class MyWrapper extends React.Component { 46 | render() { 47 | return ( 48 | 52 | 53 | 54 | 55 | {/* Children */} 56 | 57 | 58 | 59 | 60 | ) 61 | } 62 | } 63 | 64 | // The number of columns and the gutter don't change by resizing the window 65 | class MyWrapper extends Component { 66 | render() { 67 | return ( 68 | 69 | 70 | 71 | {/* Children */} 72 | 73 | 74 | 75 | ) 76 | } 77 | } 78 | ``` 79 | 80 | ## Props 81 | 82 | ### Masonry component 83 | 84 | | Name | PropType | Description | Default | 85 | | ------------ | -------- | ------------------------------------------------------ | ------- | 86 | | columnsCount | Number | Injected by ResponsiveMasonry | 3 | 87 | | gutter | String | Margin surrounding each item e.g. "10px" or "1.5rem" | "0" | 88 | | className | String | Custom CSS class applied to the container element | null | 89 | | style | Object | Style object for customizing the container element | {} | 90 | | containerTag | String | Tag name of the container element | "div" | 91 | | itemTag | String | Tag name of the item element | "div" | 92 | | itemStyle | Object | Style object applied to each item | {} | 93 | | sequential | Boolean | If true, items are placed in the order they are passed | false | 94 | 95 | ### ResponsiveMasonry component 96 | 97 | | Name | PropType | Description | Default | 98 | | ----------------------- | -------- | ---------------------------------------------------------------------------------------- | ------------------------ | 99 | | columnsCountBreakPoints | Object | Keys are breakpoints in px, values are the columns number | {350: 1, 750: 2, 900: 3} | 100 | | gutterBreakPoints | Object | Keys are breakpoints in px, values are the gutter value in any valid CSS value for 'gap' | | 101 | 102 | ## Contributing 103 | 104 | - ⇄ Pull/Merge requests and ★ Stars are always welcome. 105 | - For bugs and feature requests, please [create an issue][github-issue]. 106 | - Pull requests must be accompanied by passing automated tests (`npm test`). 107 | 108 | See [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines 109 | 110 | ## Changelog 111 | 112 | See [changelog](./CHANGELOG.md) 113 | 114 | ## License 115 | 116 | This project is licensed under the MIT License - see the 117 | [LICENCE.md](./LICENCE.md) file for details 118 | 119 | [npm-badge]: https://img.shields.io/npm/v/react-responsive-masonry.svg?style=flat-square 120 | [npm]: https://www.npmjs.org/package/react-responsive-masonry 121 | [build-badge]: https://img.shields.io/travis/cedricdelpoux/react-responsive-masonry/master.svg?style=flat-square 122 | [build]: https://travis-ci.org/cedricdelpoux/react-responsive-masonry 123 | [codecov-badge]: https://img.shields.io/codecov/c/github/cedricdelpoux/react-responsive-masonry.svg?style=flat-square 124 | [codecov]: https://codecov.io/gh/cedricdelpoux/react-responsive-masonry 125 | [module-formats]: https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20esm-green.svg?style=flat-square 126 | [github-page]: https://cedricdelpoux.github.io/react-responsive-masonry 127 | [github-issue]: https://github.com/cedricdelpoux/react-responsive-masonry/issues/new 128 | -------------------------------------------------------------------------------- /babel-jest.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | module.exports = { 3 | presets: ["@babel/preset-env", "@babel/preset-react"], 4 | } 5 | -------------------------------------------------------------------------------- /demo/src/Examples/Masonry/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Html} from "react-demo-page" 3 | 4 | import html from "./index.md" 5 | import Masonry from "../../../../src" 6 | 7 | const images = [ 8 | "https://picsum.photos/200/300?image=1050", 9 | null, 10 | "https://picsum.photos/400/400?image=1039", 11 | null, 12 | "https://picsum.photos/400/400?image=1080", 13 | null, 14 | "https://picsum.photos/200/200?image=997", 15 | "https://picsum.photos/500/400?image=287", 16 | "", 17 | "https://picsum.photos/400/500?image=955", 18 | "", 19 | "", 20 | "https://picsum.photos/200/300?image=916", 21 | null, 22 | "https://picsum.photos/300/300?image=110", 23 | "https://picsum.photos/300/300?image=206", 24 | ] 25 | 26 | export default class ExampleMasonry extends React.Component { 27 | render() { 28 | return ( 29 |
30 | 31 | 32 | {images.map((image, i) => 33 | image ? ( 34 | 39 | ) : ( 40 | "" 41 | ) 42 | )} 43 | 44 |
45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo/src/Examples/Masonry/index.md: -------------------------------------------------------------------------------- 1 | # Masonry 2 | 3 | The column count do not change dynamically 4 | 5 | ## Code 6 | 7 | ```js 8 | import React from "react" 9 | import Masonry from "react-responsive-masonry" 10 | 11 | const images = [ 12 | "https://picsum.photos/200/300?image=1050", 13 | //... 14 | "https://picsum.photos/300/300?image=206", 15 | ] 16 | 17 | class MyWrapper extends React.Component { 18 | render() { 19 | return ( 20 | 21 | {images.map((image, i) => ( 22 | 27 | ))} 28 | 29 | ) 30 | } 31 | } 32 | ``` 33 | 34 | ## Demo 35 | -------------------------------------------------------------------------------- /demo/src/Examples/Responsive/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {Html} from "react-demo-page" 3 | 4 | import html from "./index.md" 5 | import Masonry, {ResponsiveMasonry} from "../../../../src" 6 | 7 | const images = [ 8 | "https://picsum.photos/200/300?image=1050", 9 | "", 10 | "https://picsum.photos/400/400?image=1039", 11 | null, 12 | "https://picsum.photos/400/400?image=1080", 13 | "https://picsum.photos/200/200?image=997", 14 | "https://picsum.photos/500/400?image=287", 15 | null, 16 | "https://picsum.photos/400/500?image=955", 17 | "https://picsum.photos/200/300?image=916", 18 | "https://picsum.photos/300/300?image=110", 19 | "https://picsum.photos/300/300?image=206", 20 | ] 21 | 22 | export default class ExampleResponsiveMasonry extends React.Component { 23 | render() { 24 | return ( 25 |
26 | 27 | 31 | 32 | {images.map((image, i) => 33 | image ? ( 34 | 39 | ) : ( 40 | "" 41 | ) 42 | )} 43 | 44 | 45 |
46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /demo/src/Examples/Responsive/index.md: -------------------------------------------------------------------------------- 1 | # ResponsiveMasonry 2 | 3 | The column count change dynamically. `ResponsiveMasonry` component inject it in `Masonry` component. 4 | 5 | ## Code 6 | 7 | ```js 8 | import React from "react" 9 | import Masonry, {ResponsiveMasonry} from "react-responsive-masonry" 10 | 11 | const images = [ 12 | "https://picsum.photos/200/300?image=1050", 13 | //... 14 | "https://picsum.photos/300/300?image=206", 15 | ] 16 | 17 | class MyWrapper extends React.Component { 18 | render() { 19 | return ( 20 | 24 | 25 | {images.map((image, i) => ( 26 | 32 | ))} 33 | 34 | 35 | ) 36 | } 37 | } 38 | ``` 39 | 40 | ## Demo 41 | -------------------------------------------------------------------------------- /demo/src/Examples/index.js: -------------------------------------------------------------------------------- 1 | import ExampleMasonry from "./Masonry" 2 | import ExampleResponsiveMasonry from "./Responsive" 3 | 4 | export {ExampleMasonry, ExampleResponsiveMasonry} 5 | -------------------------------------------------------------------------------- /demo/src/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedricdelpoux/react-responsive-masonry/517f694ba74b3ae4aec6e4a9571e33c84b84a9fe/demo/src/example.gif -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {render} from "react-dom" 3 | 4 | import ReactDemoPage from "react-demo-page" 5 | import routes from "./routes" 6 | import pkg from "../../package.json" 7 | 8 | const header = { 9 | title: pkg.name, 10 | buttons: [ 11 | {label: "Github", url: pkg.homepage}, 12 | {label: "Npm", url: `https://www.npmjs.com/package/${pkg.name}`}, 13 | {label: "Download", url: `${pkg.homepage}/archive/master.zip`}, 14 | ], 15 | } 16 | 17 | const footer = { 18 | author: pkg.author, 19 | } 20 | 21 | const Demo = () => ( 22 | 29 | ) 30 | 31 | // eslint-disable-next-line 32 | render(, document.querySelector("#demo")) 33 | -------------------------------------------------------------------------------- /demo/src/routes.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import readmeHtml from "../../README.md" 4 | import {ExampleMasonry, ExampleResponsiveMasonry} from "./Examples" 5 | 6 | const routes = [ 7 | { 8 | path: "/", 9 | exact: true, 10 | component: ( 11 |
12 | 13 | 14 |
15 | ), 16 | label: "Demo", 17 | }, 18 | { 19 | path: "/readme", 20 | html: readmeHtml, 21 | label: "Read me", 22 | }, 23 | ] 24 | 25 | export default routes 26 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .cache 3 | public 4 | node_modules 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/LICENSE: -------------------------------------------------------------------------------- 1 | The BSD Zero Clause License (0BSD) 2 | 3 | Copyright (c) 2020 Gatsby Inc. 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | Gatsby 5 | 6 |

7 |

8 | Gatsby's hello-world starter 9 |

10 | 11 | Kick off your project with this hello-world boilerplate. This starter ships with the main Gatsby configuration files you might need to get up and running blazing fast with the blazing fast app generator for React. 12 | 13 | _Have another more specific idea? You may want to check out our vibrant collection of [official and community-created starters](https://www.gatsbyjs.com/docs/gatsby-starters/)._ 14 | 15 | ## 🚀 Quick start 16 | 17 | 1. **Create a Gatsby site.** 18 | 19 | Use the Gatsby CLI to create a new site, specifying the hello-world starter. 20 | 21 | ```shell 22 | # create a new Gatsby site using the hello-world starter 23 | gatsby new my-hello-world-starter https://github.com/gatsbyjs/gatsby-starter-hello-world 24 | ``` 25 | 26 | 1. **Start developing.** 27 | 28 | Navigate into your new site’s directory and start it up. 29 | 30 | ```shell 31 | cd my-hello-world-starter/ 32 | gatsby develop 33 | ``` 34 | 35 | 1. **Open the source code and start editing!** 36 | 37 | Your site is now running at `http://localhost:8000`! 38 | 39 | _Note: You'll also see a second link: _`http://localhost:8000/___graphql`_. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby tutorial](https://www.gatsbyjs.com/tutorial/part-five/#introducing-graphiql)._ 40 | 41 | Open the `my-hello-world-starter` directory in your code editor of choice and edit `src/pages/index.js`. Save your changes and the browser will update in real time! 42 | 43 | ## 🧐 What's inside? 44 | 45 | A quick look at the top-level files and directories you'll see in a Gatsby project. 46 | 47 | . 48 | ├── node_modules 49 | ├── src 50 | ├── .gitignore 51 | ├── .prettierrc 52 | ├── gatsby-browser.js 53 | ├── gatsby-config.js 54 | ├── gatsby-node.js 55 | ├── gatsby-ssr.js 56 | ├── LICENSE 57 | ├── package-lock.json 58 | ├── package.json 59 | └── README.md 60 | 61 | 1. **`/node_modules`**: This directory contains all of the modules of code that your project depends on (npm packages) are automatically installed. 62 | 63 | 2. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site (what you see in the browser) such as your site header or a page template. `src` is a convention for “source code”. 64 | 65 | 3. **`.gitignore`**: This file tells git which files it should not track / not maintain a version history for. 66 | 67 | 4. **`.prettierrc`**: This is a configuration file for [Prettier](https://prettier.io/). Prettier is a tool to help keep the formatting of your code consistent. 68 | 69 | 5. **`gatsby-browser.js`**: This file is where Gatsby expects to find any usage of the [Gatsby browser APIs](https://www.gatsbyjs.com/docs/browser-apis/) (if any). These allow customization/extension of default Gatsby settings affecting the browser. 70 | 71 | 6. **`gatsby-config.js`**: This is the main configuration file for a Gatsby site. This is where you can specify information about your site (metadata) like the site title and description, which Gatsby plugins you’d like to include, etc. (Check out the [config docs](https://www.gatsbyjs.com/docs/gatsby-config/) for more detail). 72 | 73 | 7. **`gatsby-node.js`**: This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](https://www.gatsbyjs.com/docs/node-apis/) (if any). These allow customization/extension of default Gatsby settings affecting pieces of the site build process. 74 | 75 | 8. **`gatsby-ssr.js`**: This file is where Gatsby expects to find any usage of the [Gatsby server-side rendering APIs](https://www.gatsbyjs.com/docs/ssr-apis/) (if any). These allow customization of default Gatsby settings affecting server-side rendering. 76 | 77 | 9. **`LICENSE`**: This Gatsby starter is licensed under the 0BSD license. This means that you can see this file as a placeholder and replace it with your own license. 78 | 79 | 10. **`package-lock.json`** (See `package.json` below, first). This is an automatically generated file based on the exact versions of your npm dependencies that were installed for your project. **(You won’t change this file directly).** 80 | 81 | 11. **`package.json`**: A manifest file for Node.js projects, which includes things like metadata (the project’s name, author, etc). This manifest is how npm knows which packages to install for your project. 82 | 83 | 12. **`README.md`**: A text file containing useful reference information about your project. 84 | 85 | ## 🎓 Learning Gatsby 86 | 87 | Looking for more guidance? Full documentation for Gatsby lives [on the website](https://www.gatsbyjs.com/). Here are some places to start: 88 | 89 | - **For most developers, we recommend starting with our [in-depth tutorial for creating a site with Gatsby](https://www.gatsbyjs.com/tutorial/).** It starts with zero assumptions about your level of ability and walks through every step of the process. 90 | 91 | - **To dive straight into code samples, head [to our documentation](https://www.gatsbyjs.com/docs/).** In particular, check out the _Guides_, _API Reference_, and _Advanced Tutorials_ sections in the sidebar. 92 | 93 | ## 💫 Deploy 94 | 95 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/gatsbyjs/gatsby-starter-hello-world) 96 | 97 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/gatsbyjs/gatsby-starter-hello-world) 98 | 99 | 100 | -------------------------------------------------------------------------------- /example/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | flags: { 3 | FAST_DEV: true, 4 | DEV_SSR: true, 5 | }, 6 | plugins: [], 7 | } 8 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-responsive-masonry-example", 3 | "private": true, 4 | "scripts": { 5 | "build": "gatsby build", 6 | "dev": "gatsby develop", 7 | "serve": "gatsby serve", 8 | "clean": "gatsby clean" 9 | }, 10 | "dependencies": { 11 | "gatsby": "^4.1.4", 12 | "react": "link:../node_modules/react", 13 | "react-dom": "link:../node_modules/react-dom" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Masonry, {ResponsiveMasonry} from "../../../src" 4 | 5 | const images = [ 6 | "https://picsum.photos/200/300?image=1050", 7 | "https://picsum.photos/400/400?image=1039", 8 | "https://picsum.photos/400/400?image=1080", 9 | "https://picsum.photos/200/200?image=997", 10 | "https://picsum.photos/500/400?image=287", 11 | "https://picsum.photos/400/500?image=955", 12 | "https://picsum.photos/200/300?image=916", 13 | "https://picsum.photos/300/300?image=110", 14 | "https://picsum.photos/300/300?image=206", 15 | ] 16 | 17 | const PageIndex = () => ( 18 | 19 | 20 | {images.map((image, i) => ( 21 | 27 | ))} 28 | 29 | 30 | ) 31 | 32 | export default PageIndex 33 | -------------------------------------------------------------------------------- /example/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cedricdelpoux/react-responsive-masonry/517f694ba74b3ae4aec6e4a9571e33c84b84a9fe/example/static/favicon.ico -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | coverageDirectory: "./coverage/", 3 | collectCoverage: true, 4 | moduleNameMapper: { 5 | "\\.(css)$": "/node_modules/jest-css-modules", 6 | }, 7 | testEnvironment: "jsdom", 8 | transform: { 9 | "\\.js$": ["babel-jest", {configFile: "./babel-jest.config.js"}], 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /nwb.config.js: -------------------------------------------------------------------------------- 1 | const extraWebpackConfig = { 2 | module: { 3 | rules: [ 4 | { 5 | test: /\.md$/, 6 | loader: "html-loader!markdown-loader", 7 | exclude: /node_modules/, 8 | }, 9 | ], 10 | }, 11 | } 12 | 13 | // eslint-disable-next-line 14 | module.exports = { 15 | type: "react-component", 16 | npm: { 17 | cjs: true, 18 | esModules: true, 19 | umd: { 20 | global: "ReactResponsiveMasonry", 21 | externals: { 22 | react: "React", 23 | "prop-types": "PropTypes", 24 | }, 25 | }, 26 | }, 27 | webpack: { 28 | extra: extraWebpackConfig, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-responsive-masonry", 3 | "version": "2.7.1", 4 | "author": { 5 | "name": "Cédric Delpoux", 6 | "email": "cedric.delpoux@gmail.com" 7 | }, 8 | "description": "React responsive masonry component built with css flexbox", 9 | "files": [ 10 | "es", 11 | "lib", 12 | "umd" 13 | ], 14 | "homepage": "https://github.com/cedricdelpoux/react-responsive-masonry#readme", 15 | "keywords": [ 16 | "react", 17 | "masonry", 18 | "css", 19 | "flexbox", 20 | "responsive" 21 | ], 22 | "license": "MIT", 23 | "main": "lib/index.js", 24 | "module": "es/index.js", 25 | "types": "types/index.d.ts", 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/cedricdelpoux/react-responsive-masonry.git" 29 | }, 30 | "scripts": { 31 | "openssl:legacy": "NODE_OPTIONS=--openssl-legacy-provider", 32 | "build": "yarn run openssl:legacy nwb build-react-component", 33 | "clean": "nwb clean-module && nwb clean-demo", 34 | "deploy": "gh-pages -d demo/dist", 35 | "lint": "eslint src demo/src", 36 | "prepublish": "yarn run clean && yarn run build", 37 | "start": "yarn run openssl:legacy nwb serve-react-demo --port 1190", 38 | "test": "jest --colors --no-cache", 39 | "test:watch": "yarn test -- --watch", 40 | "prepare": "husky install" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.25.8", 44 | "@babel/preset-env": "^7.25.8", 45 | "@babel/preset-react": "^7.25.7", 46 | "@commitlint/cli": "^14.1.0", 47 | "@commitlint/config-conventional": "^14.1.0", 48 | "babel-eslint": "^10.1.0", 49 | "babel-jest": "^27.3.1", 50 | "cz-conventional-changelog": "3.3.0", 51 | "enzyme": "^3.11.0", 52 | "enzyme-adapter-react-16": "^1.15.6", 53 | "eslint": "^8.2.0", 54 | "eslint-config-prettier": "^8.3.0", 55 | "eslint-plugin-jest": "^25.2.4", 56 | "eslint-plugin-prettier": "^4.0.0", 57 | "eslint-plugin-react": "^7.37.1", 58 | "gh-pages": "^3.2.3", 59 | "html-loader": "^1.1.0", 60 | "husky": "^7.0.0", 61 | "jest": "^27.3.1", 62 | "jest-css-modules": "^2.1.0", 63 | "jsdom": "^18.1.0", 64 | "lint-staged": "^12.0.2", 65 | "markdown-loader": "^6.0.0", 66 | "nwb": "^0.25.2", 67 | "prettier": "^2.4.1", 68 | "prop-types": "^15.7.2", 69 | "react": "^16.13.1", 70 | "react-demo-page": "^0.3.6", 71 | "react-dom": "^16.13.1", 72 | "react-test-renderer": "^16.13.1" 73 | }, 74 | "dependencies": {} 75 | } 76 | -------------------------------------------------------------------------------- /src/Masonry/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | 4 | class Masonry extends React.Component { 5 | constructor() { 6 | super() 7 | this.state = {columns: [], childRefs: [], hasDistributed: false} 8 | } 9 | 10 | componentDidUpdate() { 11 | if (!this.state.hasDistributed && !this.props.sequential) 12 | this.distributeChildren() 13 | } 14 | 15 | static getDerivedStateFromProps(props, state) { 16 | const {children, columnsCount} = props 17 | const hasColumnsChanged = columnsCount !== state.columns.length 18 | if (state && children === state.children && !hasColumnsChanged) return null 19 | return { 20 | ...Masonry.getEqualCountColumns(children, columnsCount), 21 | children, 22 | hasDistributed: false, 23 | } 24 | } 25 | 26 | shouldComponentUpdate(nextProps) { 27 | return ( 28 | nextProps.children !== this.state.children || 29 | nextProps.columnsCount !== this.props.columnsCount 30 | ) 31 | } 32 | 33 | distributeChildren() { 34 | const {children, columnsCount} = this.props 35 | const columnHeights = Array(columnsCount).fill(0) 36 | 37 | const isReady = this.state.childRefs.every( 38 | (ref) => ref.current.getBoundingClientRect().height 39 | ) 40 | 41 | if (!isReady) return 42 | 43 | const columns = Array.from({length: columnsCount}, () => []) 44 | let validIndex = 0 45 | React.Children.forEach(children, (child) => { 46 | if (child && React.isValidElement(child)) { 47 | // .current is undefined if ref was passed to a functional component without forwardRef 48 | // now passing ref into a wrapper div so it should always be defined 49 | const childHeight = 50 | this.state.childRefs[validIndex].current.getBoundingClientRect() 51 | .height 52 | const minHeightColumnIndex = columnHeights.indexOf( 53 | Math.min(...columnHeights) 54 | ) 55 | columnHeights[minHeightColumnIndex] += childHeight 56 | columns[minHeightColumnIndex].push(child) 57 | validIndex++ 58 | } 59 | }) 60 | 61 | this.setState((p) => ({...p, columns, hasDistributed: true})) 62 | } 63 | 64 | static getEqualCountColumns(children, columnsCount) { 65 | const columns = Array.from({length: columnsCount}, () => []) 66 | let validIndex = 0 67 | const childRefs = [] 68 | React.Children.forEach(children, (child) => { 69 | if (child && React.isValidElement(child)) { 70 | const ref = React.createRef() 71 | childRefs.push(ref) 72 | columns[validIndex % columnsCount].push( 73 |
78 | {child} 79 |
80 | // React.cloneElement(child, {ref}) // cannot attach refs to functional components without forwardRef 81 | ) 82 | validIndex++ 83 | } 84 | }) 85 | return {columns, childRefs} 86 | } 87 | 88 | renderColumns() { 89 | const {gutter, itemTag, itemStyle} = this.props 90 | return this.state.columns.map((column, i) => 91 | React.createElement( 92 | itemTag, 93 | { 94 | key: i, 95 | style: { 96 | display: "flex", 97 | flexDirection: "column", 98 | justifyContent: "flex-start", 99 | alignContent: "stretch", 100 | flex: 1, 101 | width: 0, 102 | gap: gutter, 103 | ...itemStyle, 104 | }, 105 | }, 106 | column.map((item) => item) 107 | ) 108 | ) 109 | } 110 | 111 | render() { 112 | const {gutter, className, style, containerTag} = this.props 113 | 114 | return React.createElement( 115 | containerTag, 116 | { 117 | style: { 118 | display: "flex", 119 | flexDirection: "row", 120 | justifyContent: "center", 121 | alignContent: "stretch", 122 | boxSizing: "border-box", 123 | width: "100%", 124 | gap: gutter, 125 | ...style, 126 | }, 127 | className, 128 | }, 129 | this.renderColumns() 130 | ) 131 | } 132 | } 133 | 134 | Masonry.propTypes = { 135 | children: PropTypes.oneOfType([ 136 | PropTypes.arrayOf(PropTypes.node), 137 | PropTypes.node, 138 | ]).isRequired, 139 | columnsCount: PropTypes.number, 140 | gutter: PropTypes.string, 141 | className: PropTypes.string, 142 | style: PropTypes.object, 143 | containerTag: PropTypes.string, 144 | itemTag: PropTypes.string, 145 | itemStyle: PropTypes.object, 146 | sequential: PropTypes.bool, 147 | } 148 | 149 | Masonry.defaultProps = { 150 | columnsCount: 3, 151 | gutter: "0", 152 | className: null, 153 | style: {}, 154 | containerTag: "div", 155 | itemTag: "div", 156 | itemStyle: {}, 157 | sequential: false, 158 | } 159 | 160 | export default Masonry 161 | -------------------------------------------------------------------------------- /src/Masonry/index.test.js: -------------------------------------------------------------------------------- 1 | import {configure, mount} from "enzyme" 2 | import Adapter from "enzyme-adapter-react-16" 3 | import React from "react" 4 | 5 | import Masonry from "./" 6 | 7 | configure({adapter: new Adapter()}) 8 | 9 | const content = "Content" 10 | const MasonryFixture = ( 11 | 12 |
{content}
13 |
14 | ) 15 | 16 | const CustomTagsFixture = ( 17 | 18 |
{content}
19 |
20 | ) 21 | 22 | describe("Masonry", () => { 23 | it("renders", () => { 24 | mount(MasonryFixture) 25 | }) 26 | 27 | it("renders with custom tags", () => { 28 | mount(CustomTagsFixture) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/ResponsiveMasonry/index.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | useCallback, 3 | useEffect, 4 | useLayoutEffect, 5 | useMemo, 6 | useState, 7 | } from "react" 8 | 9 | import PropTypes from "prop-types" 10 | 11 | const DEFAULT_COLUMNS_COUNT = 1 12 | const DEFAULT_GUTTER = "10px" 13 | 14 | const useIsomorphicLayoutEffect = 15 | typeof window !== "undefined" ? useLayoutEffect : useEffect 16 | 17 | const useHasMounted = () => { 18 | const [hasMounted, setHasMounted] = useState(false) 19 | useIsomorphicLayoutEffect(() => { 20 | setHasMounted(true) 21 | }, []) 22 | return hasMounted 23 | } 24 | 25 | const useWindowWidth = () => { 26 | const hasMounted = useHasMounted() 27 | const [width, setWidth] = useState( 28 | typeof window !== "undefined" ? window.innerWidth : 0 29 | ) 30 | 31 | const handleResize = useCallback(() => { 32 | if (!hasMounted) return 33 | setWidth(window.innerWidth) 34 | }, [hasMounted]) 35 | 36 | useIsomorphicLayoutEffect(() => { 37 | if (hasMounted) { 38 | window.addEventListener("resize", handleResize) 39 | handleResize() 40 | return () => window.removeEventListener("resize", handleResize) 41 | } 42 | }, [hasMounted, handleResize]) 43 | 44 | return width 45 | } 46 | 47 | const MasonryResponsive = ({ 48 | columnsCountBreakPoints = { 49 | 350: 1, 50 | 750: 2, 51 | 900: 3, 52 | }, 53 | gutterBreakPoints = {}, 54 | children, 55 | className = null, 56 | style = null, 57 | }) => { 58 | const windowWidth = useWindowWidth() 59 | 60 | const getResponsiveValue = useCallback( 61 | (breakPoints, defaultValue) => { 62 | const sortedBreakPoints = Object.keys(breakPoints).sort((a, b) => a - b) 63 | let value = 64 | sortedBreakPoints.length > 0 65 | ? breakPoints[sortedBreakPoints[0]] 66 | : defaultValue 67 | 68 | sortedBreakPoints.forEach((breakPoint) => { 69 | if (breakPoint < windowWidth) { 70 | value = breakPoints[breakPoint] 71 | } 72 | }) 73 | 74 | return value 75 | }, 76 | [windowWidth] 77 | ) 78 | 79 | const columnsCount = useMemo( 80 | () => getResponsiveValue(columnsCountBreakPoints, DEFAULT_COLUMNS_COUNT), 81 | [getResponsiveValue, columnsCountBreakPoints] 82 | ) 83 | const gutter = useMemo( 84 | () => getResponsiveValue(gutterBreakPoints, DEFAULT_GUTTER), 85 | [getResponsiveValue, gutterBreakPoints] 86 | ) 87 | 88 | return ( 89 |
90 | {React.Children.map(children, (child, index) => 91 | React.cloneElement(child, { 92 | key: index, 93 | columnsCount, 94 | gutter, 95 | }) 96 | )} 97 |
98 | ) 99 | } 100 | 101 | MasonryResponsive.propTypes = { 102 | children: PropTypes.oneOfType([ 103 | PropTypes.arrayOf(PropTypes.node), 104 | PropTypes.node, 105 | ]).isRequired, 106 | columnsCountBreakPoints: PropTypes.object, 107 | className: PropTypes.string, 108 | style: PropTypes.object, 109 | } 110 | 111 | export default MasonryResponsive 112 | -------------------------------------------------------------------------------- /src/ResponsiveMasonry/index.test.js: -------------------------------------------------------------------------------- 1 | import {configure, mount} from "enzyme" 2 | import Adapter from "enzyme-adapter-react-16" 3 | import React from "react" 4 | import {renderToString} from "react-dom/server" 5 | import {act} from "react-dom/test-utils" 6 | 7 | import Masonry from "../" 8 | import ResponsiveMasonry from "./" 9 | 10 | configure({adapter: new Adapter()}) 11 | 12 | const columnsCountBreakPoints = {350: 1, 750: 2, 900: 3} 13 | const gutterBreakPoints = { 350: '10px', 750: '20px', 900: '30px' } 14 | const content = "Content" 15 | const ResponsiveFixture = ( 16 | 17 | 18 |
{content}
19 |
20 |
21 | ) 22 | const ResponsiveCustomTagsFixture = ( 23 | 24 | 25 |
{content}
26 |
{content}
27 |
28 |
29 | ) 30 | 31 | describe("ResponsiveMasonry", () => { 32 | it("renders", () => { 33 | mount(ResponsiveFixture) 34 | }) 35 | 36 | it("should render on server", () => { 37 | const result = renderToString(ResponsiveFixture) 38 | expect(result.match(RegExp(content))).not.toBeNull() 39 | }) 40 | 41 | it("call resize event", () => { 42 | var resizeEvent = new Event("resize") 43 | 44 | act(() => { 45 | window.dispatchEvent(resizeEvent) 46 | }) 47 | }) 48 | }) 49 | 50 | describe("ResponsiveMasonry with custom tags", () => { 51 | it("renders", () => { 52 | mount(ResponsiveCustomTagsFixture) 53 | }) 54 | 55 | it("should render on server", () => { 56 | const result = renderToString(ResponsiveCustomTagsFixture) 57 | 58 | expect(result.match(RegExp(content))).not.toBeNull() 59 | expect(result.match(RegExp(" { 64 | var resizeEvent = new Event("resize") 65 | 66 | act(() => { 67 | window.dispatchEvent(resizeEvent) 68 | }) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Masonry from "./Masonry" 2 | import ResponsiveMasonry from "./ResponsiveMasonry" 3 | 4 | export default Masonry 5 | export {ResponsiveMasonry} 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "node16", 4 | "lib": [ 5 | "es6" 6 | ], 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "strictFunctionTypes": true, 10 | "strictNullChecks": true, 11 | "typeRoots": ["./types"], 12 | "noEmit": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "jsx": "react" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-responsive-masonry" { 2 | import React from "react" 3 | 4 | /** 5 | * List of props for the Masonry component. 6 | * 7 | * @typedef {Object} MasonryProps 8 | * @property {React.ReactNode} children 9 | * @property {number} columnsCount 10 | * @property {string} gutter 11 | * @property {string | null} className 12 | * @property {React.CSSProperties} style 13 | * @property {string} containerTag 14 | * @property {string} itemTag 15 | * @property {React.CSSProperties} itemStyle 16 | */ 17 | export interface MasonryProps { 18 | /** 19 | * Children to be rendered in the Masonry component as items. 20 | * 21 | * @type {React.ReactNode} 22 | */ 23 | children: React.ReactNode 24 | /** 25 | * Number of columns to be rendered in the Masonry component. 26 | * Default is 3. 27 | * 28 | * @type {number} 29 | */ 30 | columnsCount?: number 31 | /** 32 | * Gutter size between the columns. 33 | * Default is "0". 34 | * 35 | * @type {string} 36 | */ 37 | gutter?: string 38 | /** 39 | * Class name for the Masonry component container. 40 | * Default is null. 41 | * 42 | * @type {string | null} 43 | */ 44 | className?: string | null 45 | /** 46 | * Style object for the Masonry component container. 47 | * Default is {}. 48 | * 49 | * @type {React.CSSProperties} 50 | */ 51 | style?: React.CSSProperties 52 | /** 53 | * Tag name for the Masonry component container. 54 | * Default is "div". 55 | * 56 | * @type {string} 57 | */ 58 | containerTag?: string 59 | /** 60 | * Tag name for the Masonry component item. 61 | * Default is "div". 62 | * 63 | * @type {string} 64 | */ 65 | itemTag?: string 66 | /** 67 | * Style object for the Masonry component item. 68 | * Default is {}. 69 | * 70 | * @type {React.CSSProperties} 71 | */ 72 | itemStyle?: React.CSSProperties 73 | } 74 | 75 | /** 76 | * List of props for the ResponsiveMasonry component. 77 | * 78 | * @typedef {Object} ResponsiveMasonryProps 79 | * @property {React.ReactNode} children 80 | * @property {{[key: number]: number}} columnsCountBreakPoints 81 | * @property {string | null} className 82 | * @property {React.CSSProperties | null} style 83 | */ 84 | export interface ResponsiveMasonryProps { 85 | /** 86 | * Children to be rendered in the ResponsiveMasonry component as items. 87 | * 88 | * @type {React.ReactNode} 89 | */ 90 | children: React.ReactNode 91 | /** 92 | * Breakpoints for the number of columns to be rendered in the ResponsiveMasonry component. 93 | * Default is { 350: 1, 750: 2, 900: 3 } 94 | * 95 | * @type {{[breakpoint: number]: number}} 96 | */ 97 | columnsCountBreakPoints?: {[breakpoint: number]: number} 98 | /** 99 | * Breakpoints for the gutter size in the ResponsiveMasonry component. 100 | * 101 | * @type {{[breakpoint: number]: number}} 102 | */ 103 | gutterBreakPoints?: {[breakpoint: number]: number} 104 | /** 105 | * Class name for the ResponsiveMasonry component container. 106 | * Default is null. 107 | * 108 | * @type {string | null} 109 | */ 110 | className?: string | null 111 | /** 112 | * Style object for the ResponsiveMasonry component container. 113 | * Default is null. 114 | * 115 | * @type {React.CSSProperties | null} 116 | */ 117 | style?: React.CSSProperties | null 118 | } 119 | 120 | const Masonry: React.FC 121 | const ResponsiveMasonry: React.FC 122 | 123 | export default Masonry 124 | export {ResponsiveMasonry} 125 | } 126 | --------------------------------------------------------------------------------