├── README.md ├── index.html ├── scripts └── main.jsx ├── styles └── main.scss └── todo.md /README.md: -------------------------------------------------------------------------------- 1 | # React-Image-Gallery 2 | Repository for How to Learn React and Create Stunning Image Gallery tutorial on [Alex Devero Blog](http://blog.alexdevero.com/learn-react-practice-create-gallery/). 3 | 4 | ## Assets: 5 | - Bootstrap 4 alpha framework hosted on [CDN](https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.min.css) 6 | - React library hosted on [CDN](https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js) 7 | - ReactDOM library hosted on [CDN](https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.min.js) 8 | 9 | ## Links: 10 | - Live demo on [CodePen](http://codepen.io/alexdevero/pen/pEXjmJ) 11 | - Tutorial on [Alex Devero Blog](http://blog.alexdevero.com/learn-react-practice-create-gallery/) 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | How to Learn React and Create Stunning Image Gallery 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /scripts/main.jsx: -------------------------------------------------------------------------------- 1 | // Cache gallery container 2 | const galleryContainer = document.querySelector('.react-gallery'); 3 | 4 | // Create new array with URLs for images 5 | let imgUrls = [ 6 | 'https://source.unsplash.com/3Z70SDuYs5g/800x600', 7 | 'https://source.unsplash.com/01vFmYAOqQ0/800x600', 8 | 'https://source.unsplash.com/2Bjq3A7rGn4/800x600', 9 | 'https://source.unsplash.com/t20pc32VbrU/800x600', 10 | 'https://source.unsplash.com/pHANr-CpbYM/800x600', 11 | 'https://source.unsplash.com/3PmwYw2uErY/800x600', 12 | 'https://source.unsplash.com/uOi3lg8fGl4/800x600', 13 | 'https://source.unsplash.com/CwkiN6_qpDI/800x600', 14 | 'https://source.unsplash.com/9O1oQ9SzQZQ/800x600', 15 | 'https://source.unsplash.com/E4944K_4SvI/800x600', 16 | 'https://source.unsplash.com/-hI5dX2ObAs/800x600', 17 | 'https://source.unsplash.com/vZlTg_McCDo/800x600' 18 | ]; 19 | 20 | // Component for gallery image 21 | class GalleryImage extends React.Component { 22 | render() { 23 | return( 24 | {this.props.alt} 25 | ) 26 | } 27 | } 28 | 29 | // Component for gallery modal 30 | class GalleryModal extends React.Component { 31 | render() { 32 | if (this.props.isOpen === false) { 33 | return null; 34 | } 35 | 36 | return( 37 |
38 |
39 | 40 | 41 | 42 |
43 |
44 | ) 45 | } 46 | } 47 | 48 | // Component for gallery 49 | class Gallery extends React.Component{ 50 | constructor(props) { 51 | super(props); 52 | 53 | this.state = { 54 | showModal: false, 55 | url: '' 56 | } 57 | 58 | this.openModal = this.openModal.bind(this); 59 | 60 | this.closeModal = this.closeModal.bind(this); 61 | } 62 | 63 | render() { 64 | return( 65 |
66 |
67 | { 68 | imgUrls.map((url, index) => { 69 | return
70 |
71 | 72 | 73 | this.openModal(url, e)}> 74 |
75 |
76 | }) 77 | } 78 |
79 | 80 | 81 |
82 | ) 83 | } 84 | 85 | // Function for opening modal dialog 86 | openModal(url, e) { 87 | this.setState({ 88 | showModal: true, 89 | url: url 90 | }) 91 | }; 92 | 93 | // Function for closing modal dialog 94 | closeModal() { 95 | this.setState({ 96 | showModal: false, 97 | url: '' 98 | }) 99 | } 100 | } 101 | 102 | // Let's render the whole thing 103 | ReactDOM.render( 104 | 105 | , galleryContainer); 106 | -------------------------------------------------------------------------------- /styles/main.scss: -------------------------------------------------------------------------------- 1 | // Main Sass file 2 | // Variables 3 | $black: #111; 4 | $radius: 4px; 5 | $transition: all .25s ease-in-out; 6 | 7 | html, 8 | body { 9 | min-height: 100%; 10 | height: 100%; 11 | } 12 | 13 | html { 14 | font-size: 16px; 15 | } 16 | 17 | body { 18 | position: relative; 19 | font-size: 100%; 20 | } 21 | 22 | .gallery-container { 23 | padding-top: .9375rem; 24 | } 25 | 26 | .gallery-card { 27 | position: relative; 28 | overflow: hidden; 29 | margin-bottom: 1.875rem; 30 | } 31 | 32 | .gallery-thumbnail { 33 | max-width: 100%; 34 | height: auto; 35 | border-radius: $radius; 36 | } 37 | 38 | .card-icon-open { 39 | display: block; 40 | position: absolute; 41 | top: 50%; 42 | left: 50%; 43 | font-size: 2rem; 44 | color: #fff; 45 | cursor: pointer; 46 | opacity: 0; 47 | transform: translate(-50%, -50%); 48 | transition: $transition; 49 | 50 | &:focus, 51 | &:hover { 52 | color: $black; 53 | } 54 | } 55 | 56 | .gallery-thumbnail:focus ~ .card-icon-open, 57 | .gallery-thumbnail:hover ~ .card-icon-open, 58 | .gallery-thumbnail ~ .card-icon-open:focus, 59 | .gallery-thumbnail ~ .card-icon-open:hover { 60 | opacity: 1; 61 | } 62 | 63 | .modal-overlay { 64 | position: fixed; 65 | top: 0; 66 | left: 0; 67 | z-index: 10; 68 | width: 100%; 69 | height: 100%; 70 | background: rgba(21,21,21,.75); 71 | } 72 | 73 | .modal-body { 74 | position: absolute; 75 | top: 50%; 76 | left: 50%; 77 | z-index: 11; 78 | padding: 0; 79 | overflow: auto; 80 | max-width: 100%; 81 | max-height: 100%; 82 | border-radius: $radius; 83 | transform: translate(-50%, -50%); 84 | } 85 | 86 | .modal-close { 87 | position: absolute; 88 | top: 0; 89 | right: 8px; 90 | font-size: 1.5rem; 91 | color: $black; 92 | transition: $transition; 93 | 94 | &:focus, 95 | &:hover { 96 | color: #fff; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | # Todo: 2 | - 3 | --------------------------------------------------------------------------------