$black: #222;
*, *:before, *:after { box-sizing: border-box; }
html {
font-size: 16px;
}
body {
font-family: helvetica;
position: relative;
font-size: 100%;
background: #D1913C;
background: -webkit-linear-gradient(to right, #FFD194, #D1913C);
background: linear-gradient(to right, #FFD194, #D1913C);
}
.gallery-container {
padding: .9375rem 0;
}
.gallery-container h1 {
margin: 2rem 0;
padding: 0;
text-align: center;
color: #fff;
text-transform: uppercase;
font-size: 6.5vw;
font-weight: lighter;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 8px;
max-width: 1200px;
width: 100%;
margin: 0 auto;
@include bp( x-small ) {
grid-template-columns: repeat(1, 1fr);
}
@include bp( small ) {
grid-template-columns: repeat(2, 1fr);
}
// When above our large breakpoint, make sure we have 3 columns
@include bp( large ) {
grid-template-columns: repeat(3, 1fr);
}
img {
width: 100%;
border: 5px solid #fff;
}
}
.gallery-grid div {
position: relative;
cursor: pointer;
&:before, &:after {
transition: .3s opacity ease;
opacity: 0;
}
&:after {
content: '\02194';
font-size: 80px;
position: absolute;
transform: translate3d(-50%, -50%, 0) rotate(-45deg);
color: #fff;
left: 50%;
top: 50%;
display: block;
}
&:before {
content: "";
position: absolute;
top: 0;
bottom: 4px;
left: 0;
right: 0;
background: rgba(#222, 0.5);
display: block;
}
&:hover {
&:before, &:after {
opacity: 1;
transition: .3s opacity ease;
}
}
}
.modal {
position: fixed;
z-index: 999;
width: 50%;
max-width: 800px;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
@include bp( x-small ) {
width: 95%;
}
@include bp( small ) {
width: 80%;
}
@include bp( large ) {
width: 60%;
}
img {
width: 100%;
border: 5px solid #fff;
}
}
.modal-overlay {
position: fixed;
z-index: 1;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.5);
top: 0;
left: 0;
}
.modal-body a {
position: absolute;
display: inline;
color: $black;
text-decoration: none;
line-height: 36px;
font-size: 30px;
font-weight: lighter;
background: #fff;
border-radius: 5px;
height: 40px; width: 40px;
text-align: center;
}
.modal-body .modal-close {
right: 0; top: 0;
border-radius: 0 0 0 5px;
}
.modal-body .modal-next,
.modal-body .modal-prev {
right: 0; top: calc(50% - 25px);
border-radius: 5px 0 0 5px;
height: 50px;
line-height: 40px;
font-size: 60px;
}
.modal-body .modal-prev {
left: 0;
right: auto;
border-radius: 0 5px 5px 0;
}
.modal-body {
position: relative;
}
View Compiled
const imgUrls = ['https://source.unsplash.com/PC_lbSSxCZE/800x600','https://source.unsplash.com/lVmR1YaBGG4/800x600','https://source.unsplash.com/5KvPQc1Uklk/800x600','https://source.unsplash.com/GtYFwFrFbMA/800x600','https://source.unsplash.com/Igct8iZucFI/800x600','https://source.unsplash.com/M01DfkOqz7I/800x600','https://source.unsplash.com/MoI_cHNcSK8/800x600','https://source.unsplash.com/M0WbGFRTXqU/800x600','https://source.unsplash.com/s48nn4NtlZ4/800x600','https://source.unsplash.com/E4944K_4SvI/800x600','https://source.unsplash.com/F5Dxy9i8bxc/800x600','https://source.unsplash.com/iPum7Ket2jo/800x600'
];
class Gallery extends React.Component {
constructor(props) {
super(props);
this.state = { currentIndex: null };
this.closeModal = this.closeModal.bind(this);
this.findNext = this.findNext.bind(this);
this.findPrev = this.findPrev.bind(this);
this.renderImageContent = this.renderImageContent.bind(this);
}
renderImageContent(src, index) {
return (
<div onClick={(e) => this.openModal(e, index)}>
<img src={src} key={src} />
</div>
)
}
openModal(e, index) {
this.setState ({ currentIndex: index });
}
closeModal(e) {
if (e != undefined) {
e.preventDefault();
}
this.setState ({ currentIndex: null });
}
findPrev(e) {
if (e != undefined) {
e.preventDefault();
}
this.setState(prevState => ({
currentIndex: prevState.currentIndex -1
}));
}
findNext(e) {
if (e != undefined) {
e.preventDefault();
}
this.setState(prevState => ({
currentIndex: prevState.currentIndex + 1
}));
}
render() {
return (
<div className="gallery-container">
<h1>🔥 This Gallery Is Lit 🔥</h1>
<div className="gallery-grid">
{imgUrls.map(this.renderImageContent)}
</div>
<GalleryModal
closeModal={this.closeModal}
findPrev={this.findPrev}
findNext={this.findNext}
hasPrev={this.state.currentIndex > 0}
hasNext={this.state.currentIndex + 1 < imgUrls.length}
src={imgUrls[this.state.currentIndex]}
/>
</div>
)
}
}
class GalleryModal extends React.Component {
constructor() {
super();
this.handleKeyDown = this.handleKeyDown.bind(this);
}
componentDidMount() {
document.body.addEventListener('keydown', this.handleKeyDown);
}
componentWillUnMount() {
document.body.removeEventListener('keydown', this.handleKeyDown);
}
handleKeyDown(e) {
if (e.keyCode === 27)
this.props.closeModal();
if (e.keyCode === 37 && this.props.hasPrev)
this.props.findPrev();
if (e.keyCode === 39 && this.props.hasNext)
this.props.findNext();
}
render () {
const { closeModal, hasNext, hasPrev, findNext, findPrev, src } = this.props;
if (!src) {
console.log('whut')
return null;
}
return (
<div>
<div className="modal-overlay" onClick={closeModal}></div>
<div isOpen={!!src} className="modal">
<div className='modal-body'>
<a href="#" className='modal-close' onClick={closeModal} onKeyDown={this.handleKeyDown}>×</a>
{hasPrev && <a href="#" className='modal-prev' onClick={findPrev} onKeyDown={this.handleKeyDown}>‹</a>}
{hasNext && <a href="#" className='modal-next' onClick={findNext} onKeyDown={this.handleKeyDown}>›</a>}
<img src={src} />
</div>
</div>
</div>
)
}
}
ReactDOM.render(<Gallery />, document.querySelector('.gallery-container'));
View Compiled