45 | ```
46 |
47 |
48 | ## Props
49 |
50 | Props | Type | Default | Example
51 | :--- | :---: | :---: | :---
52 | images | Array (String) | **required** | `{['https://some-url.com/image.jpg', importedImage]}` `//Local image should be imported first`
53 | countFrom | Number | 5 | `{2}` `//Should be from 1 to 5`
54 | hideOverlay | Boolean | false | `{true}`
55 | renderOverlay | Function | `() => 'Preview Image'` | `{() => }`
56 | overlayBackgroundColor | String | `#222222` | `'green'` or `'#000000'` or `'rgb(255, 26, 26)'`
57 | onClickEach | Function | null | `{({src, index}) => {}}`
58 |
59 |
60 | ## Pull Requests
61 |
62 | Feel free to make Pull Requests for your feature/fix.
63 | To run the project, run
64 | ```
65 | npm install
66 | ```
67 | or
68 | ```
69 | yarn
70 | ```
71 | then
72 | ```
73 | npm start
74 | ```
75 |
76 |
77 | ## License
78 |
79 | [MIT](./LICENSE)
80 |
--------------------------------------------------------------------------------
/demo/src/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {render} from 'react-dom'
3 | import '../../src/css/style.css';
4 | import FbImageLibrary from '../../src'
5 |
6 | const images = ['https://images.pexels.com/photos/248797/pexels-photo-248797.jpeg?auto=compress&cs=tinysrgb&h=350',
7 | 'https://www.gettyimages.ie/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg',
8 | 'https://wallpaperbrowse.com/media/images/soap-bubble-1958650_960_720.jpg',
9 | 'https://cdn.pixabay.com/photo/2016/10/27/22/53/heart-1776746_960_720.jpg',
10 | 'https://images.pexels.com/photos/257840/pexels-photo-257840.jpeg?auto=compress&cs=tinysrgb&h=350',
11 | "https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&h=350",
12 | "https://wallpaperbrowse.com/media/images/3848765-wallpaper-images-download.jpg"]
13 |
14 | class Demo extends Component {
15 | render() {
16 | return
17 |
18 |
19 | }
20 | }
21 |
22 | render(, document.querySelector('#demo'))
23 |
--------------------------------------------------------------------------------
/nwb.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'react-component',
3 | npm: {
4 | esModules: true,
5 | umd: false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-fb-image-grid",
3 | "version": "0.1.5",
4 | "description": "A beautifully featured image grid library for React which represents the images just like the facebook grid images with the count for extra as well",
5 | "main": "lib/index.js",
6 | "module": "es/index.js",
7 | "files": [
8 | "css",
9 | "es",
10 | "lib",
11 | "umd"
12 | ],
13 | "scripts": {
14 | "build": "nwb build-react-component --copy-files",
15 | "clean": "nwb clean-module && nwb clean-demo",
16 | "prepublishOnly": "npm run build",
17 | "start": "nwb serve-react-demo",
18 | "test": "nwb test-react",
19 | "test:coverage": "nwb test-react --coverage",
20 | "test:watch": "nwb test-react --server"
21 | },
22 | "dependencies": {
23 | "@material-ui/core": "^3.1.2",
24 | "@material-ui/icons": "^3.0.1",
25 | "react-bootstrap": "^0.32.4",
26 | "react-image-lightbox": "^5.0.0"
27 | },
28 | "peerDependencies": {
29 | "react": "16.x"
30 | },
31 | "devDependencies": {
32 | "nwb": "0.23.x",
33 | "react": "^16.5.2",
34 | "react-dom": "^16.5.2"
35 | },
36 | "author": "Mohammad Kashif Sulaiman",
37 | "homepage": "https://github.com/Expertizo/react-fb-image-grid",
38 | "license": "MIT",
39 | "repository": "https://github.com/Expertizo/react-fb-image-grid",
40 | "keywords": [
41 | "react-facebook-images",
42 | "react-images",
43 | "react-grid",
44 | "react-facebook-images-grid",
45 | "react-fb-images-grid",
46 | "react-fb-images",
47 | "react-beautiful-images"
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/Images.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Image, Grid, Row, Col } from 'react-bootstrap';
3 | import Modal from './Modal'
4 | import PropTypes from 'prop-types';
5 |
6 | class Images extends Component {
7 | static defaultProps = {
8 | images: [],
9 | hideOverlay: false,
10 | renderOverlay: () => 'Preview Image',
11 | overlayBackgroundColor: '#222222',
12 | onClickEach: null,
13 | countFrom: 5
14 | }
15 |
16 | constructor(props) {
17 | super(props)
18 |
19 | this.state = {
20 | modal: false,
21 | countFrom: props.countFrom > 0 && props.countFrom < 5 ? props.countFrom : 5,
22 | conditionalRender: false
23 | };
24 |
25 | this.openModal = this.openModal.bind(this);
26 | this.onClose = this.onClose.bind(this);
27 |
28 | if(props.countFrom <= 0 || props.countFrom > 5) {
29 | console.warn('countFrom is limited to 5!')
30 | }
31 | }
32 |
33 | openModal(index) {
34 | const {onClickEach, images} = this.props;
35 |
36 | if(onClickEach) {
37 | return onClickEach({src: images[index], index})
38 | }
39 |
40 | this.setState({modal: true, url: images[index], index})
41 | }
42 |
43 | onClose() {
44 | this.setState({modal: false})
45 | }
46 |
47 | renderOne() {
48 | const {images} = this.props;
49 | const {countFrom} = this.state;
50 | const overlay = images.length > countFrom && countFrom == 1 ? this.renderCountOverlay(true) : this.renderOverlay();
51 |
52 | return
53 |
54 |
55 | {overlay}
56 |
57 |
58 | ;
59 | }
60 |
61 | renderTwo() {
62 | const {images} = this.props;
63 | const {countFrom} = this.state;
64 | const overlay = images.length > countFrom && [2, 3].includes(+countFrom) ? this.renderCountOverlay(true) : this.renderOverlay();
65 | const conditionalRender = [3, 4].includes(images.length) || images.length > +countFrom && [3, 4].includes(+countFrom);
66 |
67 | return
68 |
69 |
70 | {this.renderOverlay()}
71 |
72 |
73 | {overlay}
74 |
75 |
76 | ;
77 | }
78 |
79 | renderThree(more) {
80 | const {images} = this.props;
81 | const {countFrom} = this.state;
82 | const overlay = !countFrom || countFrom > 5 || images.length > countFrom && [4, 5].includes(+countFrom) ? this.renderCountOverlay(true) : this.renderOverlay(conditionalRender ? 3 : 4);
83 | const conditionalRender = images.length == 4 || images.length > +countFrom && +countFrom == 4;
84 |
85 | return
86 |
87 |
88 | {this.renderOverlay(conditionalRender ? 1 : 2)}
89 |
90 |
91 | {this.renderOverlay(conditionalRender ? 2 : 3)}
92 |
93 |
94 | {overlay}
95 |
96 |
97 | ;
98 | }
99 |
100 | renderOverlay(id) {
101 | const {hideOverlay, renderOverlay, overlayBackgroundColor} = this.props;
102 |
103 | if(hideOverlay) {
104 | return false
105 | }
106 |
107 | return [
108 | ,
109 |
110 | {renderOverlay()}
111 |
112 | ]
113 | }
114 |
115 | renderCountOverlay(more) {
116 | const {images} = this.props;
117 | const {countFrom} = this.state;
118 | const extra = images.length - (countFrom && countFrom > 5 ? 5 : countFrom);
119 |
120 | return [more && , more && ]
121 | }
122 |
123 | render(){
124 | const {modal, index, countFrom} = this.state;
125 | const {images} = this.props;
126 | const imagesToShow = [...images];
127 |
128 | if(countFrom && images.length > countFrom) {
129 | imagesToShow.length = countFrom;
130 | }
131 |
132 | return(
133 |
134 | {[1, 3, 4].includes(imagesToShow.length) && this.renderOne()}
135 | {imagesToShow.length >= 2 && imagesToShow.length != 4 && this.renderTwo()}
136 | {imagesToShow.length >= 4 && this.renderThree()}
137 |
138 | {modal && }
139 |
140 | )
141 | }
142 |
143 | }
144 |
145 | Images.propTypes = {
146 | images: PropTypes.array.isRequired,
147 | hideOverlay: PropTypes.bool,
148 | renderOverlay: PropTypes.func,
149 | overlayBackgroundColor: PropTypes.string,
150 | onClickEach: PropTypes.func,
151 | countFrom: PropTypes.number,
152 | };
153 |
154 | export default Images;
--------------------------------------------------------------------------------
/src/components/Modal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Image, Modal, Grid, Row, Col } from 'react-bootstrap';
3 | import Lightbox from 'react-image-lightbox';
4 | import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
5 |
6 | class ModalComponent extends Component {
7 | constructor(props) {
8 | super(props)
9 | this.state = {
10 | images: props.images || [],
11 | currentImageIndex: props.index
12 | };
13 |
14 | this.onMovePrevRequest = this.onMovePrevRequest.bind(this);
15 | this.onMoveNextRequest = this.onMoveNextRequest.bind(this);
16 | }
17 |
18 | onMovePrevRequest() {
19 | const {currentImageIndex, images} = this.state;
20 |
21 | this.setState({
22 | currentImageIndex: (currentImageIndex + images.length - 1) % images.length,
23 | })
24 | }
25 |
26 | onMoveNextRequest() {
27 | const {currentImageIndex, images} = this.state;
28 |
29 | this.setState({
30 | currentImageIndex: (currentImageIndex + 1) % images.length,
31 | })
32 | }
33 |
34 | render(){
35 | const {images, currentImageIndex} = this.state;
36 | const {onClose, index} = this.props;
37 |
38 | return(
39 |
47 | )
48 | }
49 |
50 | }
51 |
52 | export default ModalComponent;
--------------------------------------------------------------------------------
/src/css/style.css:
--------------------------------------------------------------------------------
1 | @import 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css';
2 | @import 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css';
3 |
4 | /* Images Component CSS */
5 |
6 | .grid-container {
7 | text-align: center;
8 | margin: auto;
9 | width: 100%;
10 | padding: 10px;
11 | }
12 |
13 | .container {
14 | padding-left: 0;
15 | padding-right: 0;
16 | width: 100% !important;
17 | }
18 |
19 | .container *{
20 | margin: 0;
21 | padding: 0;
22 | cursor: pointer;
23 | }
24 |
25 | .col-md-4 {
26 | width: 33.33333333%;
27 | }
28 |
29 | .border {
30 | border: 2px solid white;
31 | border-radius: 6px;
32 | }
33 |
34 | .background {
35 | background-size: cover !important;
36 | background-position: center !important;
37 | background-repeat: no-repeat !important;
38 | }
39 |
40 | .height-one {
41 | width: 100%;
42 | padding-top: 100%;
43 | }
44 |
45 | .height-two {
46 | width: 50%;
47 | padding-top: 50%;
48 | }
49 |
50 | .height-three {
51 | width: 33.3333%;
52 | padding-top: 33.3333%;
53 | }
54 |
55 | .cover {
56 | background-color: #222;
57 | opacity: 0.8;
58 | position: absolute;
59 | right: 0;
60 | top: 0;
61 | left: 0;
62 | bottom: 0;
63 | border-radius: 6px;
64 | }
65 |
66 | .cover-text {
67 | right: 0;
68 | left: 0;
69 | bottom: 0;
70 | color: white;
71 | font-size: 7%;
72 | position: absolute;
73 | top: 50%;
74 | -webkit-transform: translate(0%, -50%);
75 | -ms-transform: translate(0%, -50%);
76 | transform: translate(0%, -50%);
77 | text-align: center;
78 | }
79 |
80 | .cover-text > p {
81 | margin: 0;
82 | position: absolute;
83 | top: 50%;
84 | left: 50%;
85 | transform: translate(-50%, -50%);
86 | }
87 |
88 | .slide {
89 | height: 0;
90 | bottom: 100%;
91 | transition: .5s ease;
92 | overflow: hidden;
93 | font-size: 3%;
94 | }
95 |
96 | .border:hover .slide {
97 | bottom: 0;
98 | height: auto;
99 | }
100 |
101 | .border:hover .animate-text {
102 | top: 62%
103 | }
104 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './css/style.css';
2 | import FbGridImages from './components/Images'
3 |
4 | export default FbGridImages;
--------------------------------------------------------------------------------
/tests/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/index-test.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import React from 'react'
3 | import {render, unmountComponentAtNode} from 'react-dom'
4 |
5 | import Component from 'src/'
6 |
7 | describe('Component', () => {
8 | let node
9 |
10 | beforeEach(() => {
11 | node = document.createElement('div')
12 | })
13 |
14 | afterEach(() => {
15 | unmountComponentAtNode(node)
16 | })
17 |
18 | it('displays a welcome message', () => {
19 | render(, node, () => {
20 | expect(node.innerHTML).toContain('Welcome to React components')
21 | })
22 | })
23 | })
24 |
--------------------------------------------------------------------------------