All files Gallery.js

88.37% Statements 38/43
81.48% Branches 22/27
77.78% Functions 7/9
89.47% Lines 34/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113                      6x 6x                     6x     6x 6x     1x 1x                 12x   12x 6x   6x 6x     6x 1x       6x 4x 4x 4x 4x   6x 6x     6x 5x 5x   6x 1x 1x 1x   6x   12x   42x 42x                                     1x                 1x            
import React from 'react';
import PropTypes from 'prop-types';
import ResizeObserver from 'resize-observer-polyfill';
import Photo, { photoPropType } from './Photo';
import { computeSizes, computeSizesColumns } from './utils';
 
class Gallery extends React.Component {
  state = {
    containerWidth: 0,
  };
  componentDidMount() {
    this.animationFrameID = null;
    this.observer = new ResizeObserver(entries => {
      // only do something if width changes
      const newWidth = entries[0].contentRect.width;
      if (this.state.containerWidth !== newWidth) {
        // put in an animation frame to stop "benign errors" from
        // ResizObserver https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
        this.animationFrameID = window.requestAnimationFrame(() => {
          this.setState({ containerWidth: Math.floor(newWidth) });
        });
      }
    });
    this.observer.observe(this._gallery);
  }
  componentWillUnmount() {
    this.observer.disconnect();
    window.cancelAnimationFrame(this.animationFrameID);
  }
  handleClick = (event, { index }) => {
    const { photos, onClick } = this.props;
    onClick(event, {
      index,
      photo: photos[index],
      previous: photos[index - 1] || null,
      next: photos[index + 1] || null,
    });
  };
 
  render() {
    const containerWidth = this.state.containerWidth;
    // no containerWidth until after first render with refs, skip calculations and render nothing
    if (!containerWidth) return <div ref={c => (this._gallery = c)}>&nbsp;</div>;
    const { ImageComponent = Photo } = this.props;
    // subtract 1 pixel because the browser may round up a pixel
    const { margin, onClick, direction } = this.props;
    let { columns } = this.props;
 
    // allow parent to calculate columns from containerWidth
    if (typeof columns === 'function') {
      columns = columns(containerWidth);
    }
 
    // set default breakpoints if user doesn't specify columns prop
    if (columns === undefined) {
      columns = 1;
      Eif (containerWidth >= 500) columns = 2;
      if (containerWidth >= 900) columns = 3;
      Iif (containerWidth >= 1500) columns = 4;
    }
    const photos = this.props.photos;
    const width = containerWidth - 1;
    let galleryStyle, thumbs;
 
    if (direction === 'row') {
      galleryStyle = { display: 'flex', flexWrap: 'wrap', flexDirection: 'row' };
      thumbs = computeSizes({ width, columns, margin, photos });
    }
    if (direction === 'column') {
      galleryStyle = { position: 'relative' };
      thumbs = computeSizesColumns({ width, columns, margin, photos });
      galleryStyle.height = thumbs[thumbs.length - 1].containerHeight;
    }
    return (
      <div className="react-photo-gallery--gallery">
        <div ref={c => (this._gallery = c)} style={galleryStyle}>
          {thumbs.map((photo, index) => {
            const { left, top, containerHeight, ...rest } = photo;
            return (
              <ImageComponent
                key={photo.key || photo.src}
                margin={margin}
                index={index}
                photo={rest}
                direction={direction}
                left={left}
                top={top}
                onClick={onClick ? this.handleClick : null}
              />
            );
          })}
        </div>
      </div>
    );
  }
}
 
Gallery.propTypes = {
  photos: PropTypes.arrayOf(photoPropType).isRequired,
  direction: PropTypes.string,
  onClick: PropTypes.func,
  columns: PropTypes.oneOfType([PropTypes.func, PropTypes.number]),
  margin: PropTypes.number,
  ImageComponent: PropTypes.func,
};
 
Gallery.defaultProps = {
  margin: 2,
  direction: 'row',
};
 
export default Gallery;