react-photo-gallery
Advanced tools
Comparing version 6.0.29 to 6.1.0
@@ -24,2 +24,10 @@ import React from 'react'; | ||
it('renders correctly with direction set to column', () => { | ||
const component = mount( | ||
<Gallery photos={photos} onClick={handleClick} direction={'column'}/> | ||
); | ||
component.setState({containerWidth: '1139'}) | ||
expect(component).toMatchSnapshot(); | ||
}); | ||
it('unmounts', () => { | ||
@@ -26,0 +34,0 @@ const wrapper = mount(<Gallery photos={photos} />); |
@@ -6,4 +6,4 @@ There is an example app that comes with the project. To build the examples locally [clone the repo](https://github.com/neptunian/react-photo-gallery), cd into the project directory and run: | ||
``` | ||
npm install | ||
npm start | ||
yarn install | ||
yarn start | ||
``` | ||
@@ -10,0 +10,0 @@ |
@@ -16,2 +16,3 @@ --- | ||
* Uses actual image elements, optionally pass in srcSet and sizes attributes | ||
* Supports row or column direction layout | ||
* Supports passing in a custom image component for implementation of things like image selection, favorites, captions, or whatever your little heart desires! | ||
@@ -37,3 +38,4 @@ * SSR app compatible | ||
* [Basic](https://codesandbox.io/s/9yx911wl9y) | ||
* [Basic Row Layout](https://codesandbox.io/s/9yx911wl9y) | ||
* [Basic Column Layout](https://codesandbox.io/s/r09k1xj614) | ||
* [With Lightbox](https://codesandbox.io/s/5vn3lvz2n4) | ||
@@ -90,2 +92,3 @@ * [Dynamic Columns](https://codesandbox.io/s/ll7ym48027) | ||
margin | number | 2 | optional; number of margin pixels around each entire image | ||
direction | string | 'row' | optional; column or row based layout | ||
ImageComponent | function | default component | optional; use a different image component than the default provided to display your photo | ||
@@ -112,5 +115,8 @@ | ||
:-----------------------|:--------------|:-------------- | ||
margin | string | margin prop optionally passed into Gallery by user | ||
index | number | the index of the photo within the Gallery | ||
photo | object | the individual object passed into Gallery's `photos` array prop, with all the same props except recalculated height and width | ||
onClick | function | the onClick function optionally passsed into Gallery by user | ||
margin | string | optional; margin prop optionally passed into Gallery by user | ||
index | number | required; the index of the photo within the Gallery | ||
photo | object | required; the individual object passed into Gallery's `photos` array prop, with all the same props except recalculated height and width | ||
direction | string | optional; direction passed into Gallery | ||
top | number | required if direction is 'column'; top position of this image, only passed if direction prop was 'column' | ||
left | number | required if direction is 'column'; left position of this image, only passed if direction prop was 'column' | ||
onClick | function | optional; the onClick function optionally passsed into Gallery by user |
@@ -5,3 +5,4 @@ # Summary | ||
* Examples | ||
* [Basic](examples/basic.md) | ||
* [Basic (Rows)](examples/basic-rows.md) | ||
* [Basic (Columns)](examples/basic-columns.md) | ||
* [srcSet and sizes](examples/srcset-and-sizes.md) | ||
@@ -13,2 +14,2 @@ * [Dynamic Columns](examples/dynamic-columns.md) | ||
* [Local app](examples/local-app.md) | ||
* [srcSet and sizes](api/srcset-and-sizes.md) | ||
* [srcSet and sizes](srcset-and-sizes.md) |
@@ -82,5 +82,6 @@ import React from 'react'; | ||
return <div ref={measureRef} className="App"> | ||
<ExampleBasic columns={columns} photos={this.state.photos.slice(0,6)} /> | ||
<ExampleWithLightbox columns={columns} photos={this.state.photos.slice(6,12)} /> | ||
<ExampleCustomComponentSelection columns={columns} photos={this.state.photos.slice(12,18)} /> | ||
<ExampleBasic title={'Basic Row Layout'} columns={columns} photos={this.state.photos.slice(0,6)} /> | ||
<ExampleBasic title={'Basic Column Layout'} direction="column" columns={columns} photos={this.state.photos.slice(6, 12)} /> | ||
<ExampleWithLightbox columns={columns} photos={this.state.photos.slice(12, 18)} /> | ||
<ExampleCustomComponentSelection columns={columns} photos={this.state.photos.slice(18, 26)} /> | ||
<ExampleDynamicLoading columns={columns} photos={this.state.photos} /> | ||
@@ -87,0 +88,0 @@ </div> |
import React from 'react'; | ||
import Gallery from 'react-photo-gallery'; | ||
const ExampleBasic = ({photos, columns}) => { | ||
const ExampleBasic = ({photos, columns, title, direction}) => { | ||
return ( | ||
<div> | ||
<h2>Basic</h2> | ||
<Gallery photos={photos} columns={columns} /> | ||
<h2>{title}</h2> | ||
<Gallery photos={photos} columns={columns} direction={direction}/> | ||
</div> | ||
@@ -10,0 +10,0 @@ ); |
@@ -53,4 +53,4 @@ import React from 'react'; | ||
<div> | ||
<h2>Loading Photos Dynamically</h2> | ||
<Gallery photos={this.state.photos} columns={this.props.columns} /> | ||
<h2>Loading Photos Dynamically in Column Layout</h2> | ||
<Gallery photos={this.state.photos} columns={this.props.columns} direction={'column'} /> | ||
{!this.state.loadedAll && <div className="loading-msg" id="msg-loading-more">Loading</div>} | ||
@@ -57,0 +57,0 @@ </div> |
@@ -18,3 +18,3 @@ import React from 'react'; | ||
const SelectedImage = ({ index, onClick, photo, margin}) => { | ||
const SelectedImage = ({ index, onClick, photo, margin, direction, top, left}) => { | ||
//calculate x,y scale | ||
@@ -25,4 +25,9 @@ const sx = (100 - ((30 / photo.width) * 100)) / 100; | ||
if (direction === 'column'){ | ||
cont.position = 'absolute'; | ||
cont.left = left; | ||
cont.top = top; | ||
} | ||
return ( | ||
<div style={{margin, width:photo.width, ...cont}} className={(!photo.selected ? 'not-selected' : '')}> | ||
<div style={{margin, height: photo.height, width:photo.width, ...cont}} className={(!photo.selected ? 'not-selected' : '')}> | ||
<Checkmark selected={photo.selected ? true : false}/> | ||
@@ -29,0 +34,0 @@ <img style={photo.selected ? {...imgStyle, ...selectedImgStyle} : {...imgStyle}} {...photo} onClick={(e) => onClick(e, {index, photo})} /> |
@@ -29,2 +29,4 @@ 'use strict'; | ||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -91,10 +93,22 @@ | ||
var width = this.state.containerWidth - 1; | ||
var _props = this.props, | ||
photos = _props.photos, | ||
columns = _props.columns, | ||
margin = _props.margin, | ||
onClick = _props.onClick; | ||
onClick = _props.onClick, | ||
direction = _props.direction; | ||
var thumbs = (0, _utils.computeSizes)({ width: width, columns: columns, margin: margin, photos: photos }); | ||
var photos = this.props.photos; | ||
var width = this.state.containerWidth - 1; | ||
var galleryStyle = void 0, | ||
thumbs = void 0; | ||
if (direction === 'row') { | ||
galleryStyle = { display: 'flex', flexWrap: 'wrap', flexDirection: 'row' }; | ||
thumbs = (0, _utils.computeSizes)({ width: width, columns: columns, margin: margin, photos: photos }); | ||
} | ||
if (direction === 'column') { | ||
galleryStyle = { position: 'relative' }; | ||
thumbs = (0, _utils.computeSizesColumns)({ width: width, columns: columns, margin: margin, photos: photos }); | ||
galleryStyle.height = thumbs[thumbs.length - 1].containerHeight; | ||
} | ||
return _react2.default.createElement( | ||
@@ -107,4 +121,9 @@ 'div', | ||
return _this3._gallery = c; | ||
}, style: { display: 'flex', flexWrap: 'wrap' } }, | ||
}, style: galleryStyle }, | ||
thumbs.map(function (photo, index) { | ||
var left = photo.left, | ||
top = photo.top, | ||
containerHeight = photo.containerHeight, | ||
rest = _objectWithoutProperties(photo, ['left', 'top', 'containerHeight']); | ||
return _react2.default.createElement(ImageComponent, { | ||
@@ -114,3 +133,6 @@ key: photo.key || photo.src, | ||
index: index, | ||
photo: photo, | ||
photo: rest, | ||
direction: direction, | ||
left: left, | ||
top: top, | ||
onClick: onClick ? _this3.handleClick : null | ||
@@ -129,2 +151,3 @@ }); | ||
photos: _propTypes2.default.arrayOf(_Photo.photoPropType).isRequired, | ||
direction: _propTypes2.default.string, | ||
onClick: _propTypes2.default.func, | ||
@@ -138,5 +161,6 @@ columns: _propTypes2.default.number, | ||
columns: 3, | ||
margin: 2 | ||
margin: 2, | ||
direction: 'row' | ||
}; | ||
exports.default = Gallery; |
@@ -26,5 +26,13 @@ 'use strict'; | ||
photo = _ref.photo, | ||
margin = _ref.margin; | ||
margin = _ref.margin, | ||
direction = _ref.direction, | ||
top = _ref.top, | ||
left = _ref.left; | ||
var imgStyle = { margin: margin }; | ||
if (direction === 'column') { | ||
imgStyle.position = 'absolute'; | ||
imgStyle.left = left; | ||
imgStyle.top = top; | ||
} | ||
@@ -53,7 +61,19 @@ var handleClick = function handleClick(event) { | ||
Photo.propTypes = { | ||
index: _propTypes2.default.number, | ||
index: _propTypes2.default.number.isRequired, | ||
onClick: _propTypes2.default.func, | ||
photo: photoPropType | ||
photo: photoPropType.isRequired, | ||
margin: _propTypes2.default.number, | ||
top: function top(props) { | ||
if (props.direction === 'column' && typeof props.top !== 'number') { | ||
return new Error('top is a required number when direction is set to `column`'); | ||
} | ||
}, | ||
left: function left(props) { | ||
if (props.direction === 'column' && typeof props.left !== 'number') { | ||
return new Error('left is a required number when direction is set to `column`'); | ||
} | ||
}, | ||
direction: _propTypes2.default.string | ||
}; | ||
exports.default = Photo; |
@@ -12,2 +12,3 @@ 'use strict'; | ||
exports.computeSizes = computeSizes; | ||
exports.computeSizesColumns = computeSizesColumns; | ||
@@ -49,3 +50,2 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
}, []); | ||
// calculate total ratio of each row, and adjust each cell height and width | ||
@@ -75,2 +75,52 @@ // accordingly. | ||
}, []); | ||
} | ||
function computeSizesColumns(_ref3) { | ||
var photos = _ref3.photos, | ||
columns = _ref3.columns, | ||
width = _ref3.width, | ||
margin = _ref3.margin; | ||
// calculate each colWidth based on total width and column amount | ||
var colWidth = (width - margin * 2 * columns) / columns; | ||
// loop through each photo to assign adjusted height and width based on colWidth | ||
var photosWithSizes = photos.map(function (photo) { | ||
var newHeight = photo.height / photo.width * colWidth; | ||
return _extends({}, photo, { | ||
width: round(colWidth, 1), | ||
height: round(newHeight, 1) | ||
}); | ||
}); | ||
// store all possible left positions | ||
// and current top positions for each column | ||
var colLeftPositions = []; | ||
var colCurrTopPositions = []; | ||
for (var i = 0; i < columns; i++) { | ||
colLeftPositions[i] = round(i * (colWidth + margin * 2), 1); | ||
colCurrTopPositions[i] = 0; | ||
} | ||
// loop through each photo, then loop thru each "column" | ||
// find column with the smallest height and assign to photo's 'top' | ||
// update that column's height with this photo's height | ||
var photosPositioned = photosWithSizes.map(function (photo) { | ||
var smallestCol = colCurrTopPositions.reduce(function (acc, item, i) { | ||
acc = item < colCurrTopPositions[acc] ? i : acc; | ||
return acc; | ||
}, 0); | ||
photo.top = colCurrTopPositions[smallestCol]; | ||
photo.left = colLeftPositions[smallestCol]; | ||
colCurrTopPositions[smallestCol] = colCurrTopPositions[smallestCol] + photo.height + margin * 2; | ||
// store the tallest col to use for gallery height because of abs positioned elements | ||
var tallestCol = colCurrTopPositions.reduce(function (acc, item, i) { | ||
acc = item > colCurrTopPositions[acc] ? i : acc; | ||
return acc; | ||
}, 0); | ||
photo.containerHeight = colCurrTopPositions[tallestCol]; | ||
return photo; | ||
}); | ||
return photosPositioned; | ||
} |
{ | ||
"name": "react-photo-gallery", | ||
"version": "6.0.29", | ||
"version": "6.1.0", | ||
"description": "Responsive React Photo Gallery Component", | ||
@@ -5,0 +5,0 @@ "main": "lib/Gallery.js", |
@@ -12,2 +12,3 @@ # React Photo Gallery | ||
* Uses actual image elements, optionally pass in srcSet and sizes attributes | ||
* Supports row or column direction layout | ||
* Supports passing in a custom image component for implementation of things like image selection, favorites, captions, or whatever your little heart desires! | ||
@@ -33,3 +34,4 @@ * SSR app compatible | ||
* [Basic](https://codesandbox.io/s/9yx911wl9y) | ||
* [Basic Row Layout](https://codesandbox.io/s/9yx911wl9y) | ||
* [Basic Column Layout](https://codesandbox.io/s/r09k1xj614) | ||
* [With Lightbox](https://codesandbox.io/s/5vn3lvz2n4) | ||
@@ -86,2 +88,3 @@ * [Dynamic Columns](https://codesandbox.io/s/ll7ym48027) | ||
margin | number | 2 | optional; number of margin pixels around each entire image | ||
direction | string | 'row' | optional; column or row based layout | ||
ImageComponent | function | default component | optional; use a different image component than the default provided to display your photo | ||
@@ -108,5 +111,8 @@ | ||
:-----------------------|:--------------|:-------------- | ||
margin | string | margin prop optionally passed into Gallery by user | ||
index | number | the index of the photo within the Gallery | ||
photo | object | the individual object passed into Gallery's `photos` array prop, with all the same props except recalculated height and width | ||
onClick | function | the onClick function optionally passsed into Gallery by user | ||
margin | string | optional; margin prop optionally passed into Gallery by user | ||
index | number | required; the index of the photo within the Gallery | ||
photo | object | required; the individual object passed into Gallery's `photos` array prop, with all the same props except recalculated height and width | ||
direction | string | optional; direction passed into Gallery | ||
top | number | required if direction is 'column'; top position of this image, only passed if direction prop was 'column' | ||
left | number | required if direction is 'column'; left position of this image, only passed if direction prop was 'column' | ||
onClick | function | optional; the onClick function optionally passsed into Gallery by user |
@@ -5,3 +5,3 @@ import React from 'react'; | ||
import Photo, { photoPropType } from './Photo'; | ||
import { computeSizes } from './utils'; | ||
import { computeSizes, computeSizesColumns } from './utils'; | ||
@@ -34,9 +34,21 @@ class Gallery extends React.Component { | ||
// subtract 1 pixel because the browser may round up a pixel | ||
const { columns, margin, onClick, direction } = this.props; | ||
const photos = this.props.photos; | ||
const width = this.state.containerWidth - 1; | ||
const { photos, columns, margin, onClick } = this.props; | ||
const thumbs = computeSizes({ width, columns, margin, photos }); | ||
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={{ display: 'flex', flexWrap: 'wrap' }}> | ||
<div ref={c => (this._gallery = c)} style={galleryStyle}> | ||
{thumbs.map((photo, index) => { | ||
const { left, top, containerHeight, ...rest } = photo; | ||
return ( | ||
@@ -47,3 +59,6 @@ <ImageComponent | ||
index={index} | ||
photo={photo} | ||
photo={rest} | ||
direction={direction} | ||
left={left} | ||
top={top} | ||
onClick={onClick ? this.handleClick : null} | ||
@@ -61,2 +76,3 @@ /> | ||
photos: PropTypes.arrayOf(photoPropType).isRequired, | ||
direction: PropTypes.string, | ||
onClick: PropTypes.func, | ||
@@ -71,4 +87,5 @@ columns: PropTypes.number, | ||
margin: 2, | ||
direction: 'row', | ||
}; | ||
export default Gallery; |
@@ -6,4 +6,9 @@ import React from 'react'; | ||
const Photo = ({ index, onClick, photo, margin }) => { | ||
const Photo = ({ index, onClick, photo, margin, direction, top, left }) => { | ||
const imgStyle = { margin: margin }; | ||
if (direction === 'column') { | ||
imgStyle.position = 'absolute'; | ||
imgStyle.left = left; | ||
imgStyle.top = top; | ||
} | ||
@@ -34,7 +39,19 @@ const handleClick = event => { | ||
Photo.propTypes = { | ||
index: PropTypes.number, | ||
index: PropTypes.number.isRequired, | ||
onClick: PropTypes.func, | ||
photo: photoPropType, | ||
photo: photoPropType.isRequired, | ||
margin: PropTypes.number, | ||
top: props => { | ||
if (props.direction === 'column' && typeof props.top !== 'number') { | ||
return new Error('top is a required number when direction is set to `column`'); | ||
} | ||
}, | ||
left: props => { | ||
if (props.direction === 'column' && typeof props.left !== 'number') { | ||
return new Error('left is a required number when direction is set to `column`'); | ||
} | ||
}, | ||
direction: PropTypes.string, | ||
}; | ||
export default Photo; |
@@ -26,3 +26,2 @@ export function round(value, decimals) { | ||
}, []); | ||
// calculate total ratio of each row, and adjust each cell height and width | ||
@@ -49,1 +48,47 @@ // accordingly. | ||
} | ||
export function computeSizesColumns({ photos, columns, width, margin }) { | ||
// calculate each colWidth based on total width and column amount | ||
let colWidth = (width - margin * 2 * columns) / columns; | ||
// loop through each photo to assign adjusted height and width based on colWidth | ||
const photosWithSizes = photos.map(photo => { | ||
const newHeight = photo.height / photo.width * colWidth; | ||
return { | ||
...photo, | ||
width: round(colWidth, 1), | ||
height: round(newHeight, 1), | ||
}; | ||
}); | ||
// store all possible left positions | ||
// and current top positions for each column | ||
const colLeftPositions = []; | ||
const colCurrTopPositions = []; | ||
for (var i = 0; i < columns; i++) { | ||
colLeftPositions[i] = round(i * (colWidth + margin * 2), 1); | ||
colCurrTopPositions[i] = 0; | ||
} | ||
// loop through each photo, then loop thru each "column" | ||
// find column with the smallest height and assign to photo's 'top' | ||
// update that column's height with this photo's height | ||
const photosPositioned = photosWithSizes.map(photo => { | ||
const smallestCol = colCurrTopPositions.reduce((acc, item, i) => { | ||
acc = item < colCurrTopPositions[acc] ? i : acc; | ||
return acc; | ||
}, 0); | ||
photo.top = colCurrTopPositions[smallestCol]; | ||
photo.left = colLeftPositions[smallestCol]; | ||
colCurrTopPositions[smallestCol] = colCurrTopPositions[smallestCol] + photo.height + margin * 2; | ||
// store the tallest col to use for gallery height because of abs positioned elements | ||
const tallestCol = colCurrTopPositions.reduce((acc, item, i) => { | ||
acc = item > colCurrTopPositions[acc] ? i : acc; | ||
return acc; | ||
}, 0); | ||
photo.containerHeight = colCurrTopPositions[tallestCol]; | ||
return photo; | ||
}); | ||
return photosPositioned; | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
870884
64
2687
115
1