diff --git a/build/image-gallery.js b/build/image-gallery.js index 900a231c..f55227c7 100644 --- a/build/image-gallery.js +++ b/build/image-gallery.js @@ -24,6 +24,10 @@ var _lodash3 = require('lodash.debounce'); var _lodash4 = _interopRequireDefault(_lodash3); +var _resizeObserverPolyfill = require('resize-observer-polyfill'); + +var _resizeObserverPolyfill2 = _interopRequireDefault(_resizeObserverPolyfill); + var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); @@ -46,15 +50,292 @@ var ImageGallery = function (_React$Component) { var _this = _possibleConstructorReturn(this, (ImageGallery.__proto__ || Object.getPrototypeOf(ImageGallery)).call(this, props)); + _this.slideToIndex = function (index, event) { + var _this$state = _this.state, + currentIndex = _this$state.currentIndex, + isTransitioning = _this$state.isTransitioning; + + + if (!isTransitioning) { + if (event) { + if (_this._intervalId) { + // user triggered event while ImageGallery is playing, reset interval + _this.pause(false); + _this.play(false); + } + } + + var slideCount = _this.props.items.length - 1; + var nextIndex = index; + + if (index < 0) { + nextIndex = slideCount; + } else if (index > slideCount) { + nextIndex = 0; + } + + _this.setState({ + previousIndex: currentIndex, + currentIndex: nextIndex, + isTransitioning: nextIndex !== currentIndex, + offsetPercentage: 0, + style: { + transition: 'all ' + _this.props.slideDuration + 'ms ease-out' + } + }, _this._onSliding); + } + }; + _this._onSliding = function () { var isTransitioning = _this.state.isTransitioning; - - window.setTimeout(function () { + _this._transitionTimer = window.setTimeout(function () { if (isTransitioning) { _this.setState({ isTransitioning: !isTransitioning }); } - }, _this.props.slideDuration); + }, _this.props.slideDuration + 50); + }; + + _this._handleScreenChange = function () { + /* + handles screen change events that the browser triggers e.g. esc key + */ + var fullScreenElement = document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement; + + if (_this.props.onScreenChange) { + _this.props.onScreenChange(fullScreenElement); + } + + _this.setState({ isFullscreen: !!fullScreenElement }); + }; + + _this._toggleFullScreen = function () { + if (_this.state.isFullscreen) { + _this.exitFullScreen(); + } else { + _this.fullScreen(); + } + }; + + _this._togglePlay = function () { + if (_this._intervalId) { + _this.pause(); + } else { + _this.play(); + } + }; + + _this._initGalleryResizing = function (element) { + /* + When image-gallery-slide-wrapper unmounts and mounts when thumbnail bar position is changed + ref is called twice, once with null and another with the element. + Make sure element is available before calling observe. + */ + if (element) { + _this._imageGallerySlideWrapper = element; + _this.resizeObserver = new _resizeObserverPolyfill2.default(_this._createResizeObserver); + _this.resizeObserver.observe(element); + } + }; + + _this._createResizeObserver = (0, _lodash4.default)(function (entries) { + entries.forEach(function () { + _this._handleResize(); + }); + }, 300); + + _this._handleResize = function () { + var currentIndex = _this.state.currentIndex; + + if (_this._imageGallery) { + _this.setState({ + galleryWidth: _this._imageGallery.offsetWidth + }); + } + + if (_this._imageGallerySlideWrapper) { + _this.setState({ + gallerySlideWrapperHeight: _this._imageGallerySlideWrapper.offsetHeight + }); + } + + if (_this._thumbnailsWrapper) { + if (_this._isThumbnailHorizontal()) { + _this.setState({ thumbnailsWrapperHeight: _this._thumbnailsWrapper.offsetHeight }); + } else { + _this.setState({ thumbnailsWrapperWidth: _this._thumbnailsWrapper.offsetWidth }); + } + } + + // Adjust thumbnail container when thumbnail width or height is adjusted + _this._setThumbsTranslate(-_this._getThumbsTranslate(currentIndex)); + }; + + _this._handleKeyDown = function (event) { + var LEFT_ARROW = 37; + var RIGHT_ARROW = 39; + var ESC_KEY = 27; + var key = parseInt(event.keyCode || event.which || 0); + + switch (key) { + case LEFT_ARROW: + if (_this._canSlideLeft() && !_this._intervalId) { + _this._slideLeft(); + } + break; + case RIGHT_ARROW: + if (_this._canSlideRight() && !_this._intervalId) { + _this._slideRight(); + } + break; + case ESC_KEY: + if (_this.state.isFullscreen && !_this.props.useBrowserFullscreen) { + _this.exitFullScreen(); + } + } + }; + + _this._handleImageError = function (event) { + if (_this.props.defaultImage && event.target.src.indexOf(_this.props.defaultImage) === -1) { + event.target.src = _this.props.defaultImage; + } + }; + + _this._handleOnSwiped = function (e, deltaX, deltaY, isFlick) { + var _this$state2 = _this.state, + scrollingUpDown = _this$state2.scrollingUpDown, + scrollingLeftRight = _this$state2.scrollingLeftRight; + + if (scrollingUpDown) { + // user stopped scrollingUpDown + _this.setState({ scrollingUpDown: false }); + } + + if (scrollingLeftRight) { + // user stopped scrollingLeftRight + _this.setState({ scrollingLeftRight: false }); + } + + if (!scrollingUpDown) { + // don't swipe if user is scrolling + var side = deltaX > 0 ? 1 : -1; + _this._handleOnSwipedTo(side, isFlick); + } + }; + + _this._handleSwiping = function (e, deltaX, deltaY, delta) { + var swipingTransitionDuration = _this.props.swipingTransitionDuration; + + _this._setScrollDirection(deltaX, deltaY); + var _this$state3 = _this.state, + galleryWidth = _this$state3.galleryWidth, + isTransitioning = _this$state3.isTransitioning, + scrollingUpDown = _this$state3.scrollingUpDown; + + if (!isTransitioning && !scrollingUpDown) { + var side = deltaX < 0 ? 1 : -1; + + var offsetPercentage = delta / galleryWidth * 100; + if (Math.abs(offsetPercentage) >= 100) { + offsetPercentage = 100; + } + + var swipingTransition = { + transition: 'transform ' + swipingTransitionDuration + 'ms ease-out' + }; + + _this.setState({ + offsetPercentage: side * offsetPercentage, + style: swipingTransition + }); + } else { + // don't move the slide + _this.setState({ offsetPercentage: 0 }); + } + }; + + _this._slideLeft = function (event) { + _this.props.isRTL ? _this._slideNext() : _this.ـslidePrevious(); + }; + + _this._slideRight = function (event) { + _this.props.isRTL ? _this.ـslidePrevious() : _this._slideNext(); + }; + + _this.ـslidePrevious = function (event) { + _this.slideToIndex(_this.state.currentIndex - 1, event); + }; + + _this._slideNext = function (event) { + _this.slideToIndex(_this.state.currentIndex + 1, event); + }; + + _this._renderItem = function (item) { + var onImageError = _this.props.onImageError || _this._handleImageError; + + return _react2.default.createElement( + 'div', + { className: 'image-gallery-image' }, + item.imageSet ? _react2.default.createElement( + 'picture', + { + onLoad: _this.props.onImageLoad, + onError: onImageError + }, + item.imageSet.map(function (source, index) { + return _react2.default.createElement('source', { + key: index, + media: source.media, + srcSet: source.srcSet, + type: source.type + }); + }), + _react2.default.createElement('img', { + alt: item.originalAlt, + src: item.original + }) + ) : _react2.default.createElement('img', { + src: item.original, + alt: item.originalAlt, + srcSet: item.srcSet, + sizes: item.sizes, + title: item.originalTitle, + onLoad: _this.props.onImageLoad, + onError: onImageError + }), + item.description && _react2.default.createElement( + 'span', + { className: 'image-gallery-description' }, + item.description + ) + ); + }; + + _this._renderThumbInner = function (item) { + var onThumbnailError = _this.props.onThumbnailError || _this._handleImageError; + + return _react2.default.createElement( + 'div', + { className: 'image-gallery-thumbnail-inner' }, + _react2.default.createElement('img', { + src: item.thumbnail, + alt: item.thumbnailAlt, + title: item.thumbnailTitle, + onError: onThumbnailError + }), + item.thumbnailLabel && _react2.default.createElement( + 'div', + { className: 'image-gallery-thumbnail-label' }, + item.thumbnailLabel + ) + ); + }; + + _this._onThumbnailClick = function (event, index) { + _this.slideToIndex(index, event); + if (_this.props.onThumbnailClick) { + _this.props.onThumbnailClick(event, index); + } }; _this.state = { @@ -68,6 +349,10 @@ var ImageGallery = function (_React$Component) { isPlaying: false }; + // Used to update the throttle if slideDuration changes + _this._unthrottledSlideToIndex = _this.slideToIndex; + _this.slideToIndex = (0, _lodash2.default)(_this._unthrottledSlideToIndex, props.slideDuration, { trailing: false }); + if (props.lazyLoad) { _this._lazyLoaded = []; } @@ -96,47 +381,31 @@ var ImageGallery = function (_React$Component) { }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps, prevState) { - if (prevProps.thumbnailPosition !== this.props.thumbnailPosition || prevProps.showThumbnails !== this.props.showThumbnails || prevState.thumbnailsWrapperHeight !== this.state.thumbnailsWrapperHeight || prevState.thumbnailsWrapperWidth !== this.state.thumbnailsWrapperWidth) { + var itemsChanged = prevProps.items.length !== this.props.items.length; + if (itemsChanged) { this._handleResize(); } - if (prevState.currentIndex !== this.state.currentIndex) { if (this.props.onSlide) { this.props.onSlide(this.state.currentIndex); } - this._updateThumbnailTranslate(prevState); + this._updateThumbnailTranslate(prevState.currentIndex); } if (prevProps.slideDuration !== this.props.slideDuration) { this.slideToIndex = (0, _lodash2.default)(this._unthrottledSlideToIndex, this.props.slideDuration, { trailing: false }); } } - }, { - key: 'componentWillMount', - value: function componentWillMount() { - // Used to update the throttle if slideDuration changes - this._unthrottledSlideToIndex = this.slideToIndex.bind(this); - this.slideToIndex = (0, _lodash2.default)(this._unthrottledSlideToIndex, this.props.slideDuration, { trailing: false }); - - this._handleResize = this._handleResize.bind(this); - this._debounceResize = (0, _lodash4.default)(this._handleResize, 500); - this._handleScreenChange = this._handleScreenChange.bind(this); - this._handleKeyDown = this._handleKeyDown.bind(this); - this._thumbnailDelay = 300; - } }, { key: 'componentDidMount', value: function componentDidMount() { - this._handleResize(); - if (this.props.autoPlay) { this.play(); } if (!this.props.disableArrowKeys) { window.addEventListener('keydown', this._handleKeyDown); } - window.addEventListener('resize', this._debounceResize); this._onScreenChangeEvent(); } }, { @@ -146,11 +415,6 @@ var ImageGallery = function (_React$Component) { window.removeEventListener('keydown', this._handleKeyDown); } - if (this._debounceResize) { - window.removeEventListener('resize', this._debounceResize); - this._debounceResize.cancel(); - } - this._offScreenChangeEvent(); if (this._intervalId) { @@ -158,8 +422,12 @@ var ImageGallery = function (_React$Component) { this._intervalId = null; } - if (this._resizeTimer) { - window.clearTimeout(this._resizeTimer); + if (this.resizeObserver && this._imageGallerySlideWrapper) { + this.resizeObserver.unobserve(this._imageGallerySlideWrapper); + } + + if (this._transitionTimer) { + window.clearTimeout(this._transitionTimer); } } }, { @@ -262,59 +530,11 @@ var ImageGallery = function (_React$Component) { this.setState({ isFullscreen: false }); } } - }, { - key: 'slideToIndex', - value: function slideToIndex(index, event) { - var currentIndex = this.state.currentIndex; - - - if (event) { - if (this._intervalId) { - // user triggered event while ImageGallery is playing, reset interval - this.pause(false); - this.play(false); - } - } - - var slideCount = this.props.items.length - 1; - var nextIndex = index; - - if (index < 0) { - nextIndex = slideCount; - } else if (index > slideCount) { - nextIndex = 0; - } - - this.setState({ - previousIndex: currentIndex, - currentIndex: nextIndex, - isFlick: false, // reset isFlick state after slide - isTransitioning: currentIndex !== nextIndex, - offsetPercentage: 0, - style: { - transition: 'all ' + this.props.slideDuration + 'ms ease-out' - } - }, this._onSliding); - } }, { key: 'getCurrentIndex', value: function getCurrentIndex() { return this.state.currentIndex; } - }, { - key: '_handleScreenChange', - value: function _handleScreenChange() { - /* - handles screen change events that the browser triggers e.g. esc key - */ - var fullScreenElement = document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement; - - if (this.props.onScreenChange) { - this.props.onScreenChange(fullScreenElement); - } - - this.setState({ isFullscreen: !!fullScreenElement }); - } }, { key: '_onScreenChangeEvent', value: function _onScreenChangeEvent() { @@ -333,55 +553,6 @@ var ImageGallery = function (_React$Component) { document.removeEventListener(eventName, _this4._handleScreenChange); }); } - }, { - key: '_toggleFullScreen', - value: function _toggleFullScreen() { - if (this.state.isFullscreen) { - this.exitFullScreen(); - } else { - this.fullScreen(); - } - } - }, { - key: '_togglePlay', - value: function _togglePlay() { - if (this._intervalId) { - this.pause(); - } else { - this.play(); - } - } - }, { - key: '_handleResize', - value: function _handleResize() { - var _this5 = this; - - // delay initial resize to get the accurate this._imageGallery height/width - this._resizeTimer = window.setTimeout(function () { - if (_this5._imageGallery) { - _this5.setState({ - galleryWidth: _this5._imageGallery.offsetWidth - }); - } - - // adjust thumbnail container when thumbnail width or height is adjusted - _this5._setThumbsTranslate(-_this5._getThumbsTranslate(_this5.state.currentIndex > 0 ? 1 : 0) * _this5.state.currentIndex); - - if (_this5._imageGallerySlideWrapper) { - _this5.setState({ - gallerySlideWrapperHeight: _this5._imageGallerySlideWrapper.offsetHeight - }); - } - - if (_this5._thumbnailsWrapper) { - if (_this5._isThumbnailHorizontal()) { - _this5.setState({ thumbnailsWrapperHeight: _this5._thumbnailsWrapper.offsetHeight }); - } else { - _this5.setState({ thumbnailsWrapperWidth: _this5._thumbnailsWrapper.offsetWidth }); - } - } - }, 50); - } }, { key: '_isThumbnailHorizontal', value: function _isThumbnailHorizontal() { @@ -390,125 +561,51 @@ var ImageGallery = function (_React$Component) { return thumbnailPosition === 'left' || thumbnailPosition === 'right'; } }, { - key: '_handleKeyDown', - value: function _handleKeyDown(event) { - var LEFT_ARROW = 37; - var RIGHT_ARROW = 39; - var ESC_KEY = 27; - var key = parseInt(event.keyCode || event.which || 0); + key: '_setScrollDirection', + value: function _setScrollDirection(deltaX, deltaY) { + var _state = this.state, + scrollingUpDown = _state.scrollingUpDown, + scrollingLeftRight = _state.scrollingLeftRight; - switch (key) { - case LEFT_ARROW: - if (this._canSlideLeft() && !this._intervalId) { - this._slideLeft(); - } - break; - case RIGHT_ARROW: - if (this._canSlideRight() && !this._intervalId) { - this._slideRight(); - } - break; - case ESC_KEY: - if (this.state.isFullscreen && !this.props.useBrowserFullscreen) { - this.exitFullScreen(); - } - } - } - }, { - key: '_handleMouseOverThumbnails', - value: function _handleMouseOverThumbnails(index) { - var _this6 = this; - - if (this.props.slideOnThumbnailHover) { - this.setState({ hovering: true }); - if (this._thumbnailTimer) { - window.clearTimeout(this._thumbnailTimer); - this._thumbnailTimer = null; - } - this._thumbnailTimer = window.setTimeout(function () { - _this6.slideToIndex(index); - }, this._thumbnailDelay); - } - } - }, { - key: '_handleMouseLeaveThumbnails', - value: function _handleMouseLeaveThumbnails() { - if (this._thumbnailTimer) { - window.clearTimeout(this._thumbnailTimer); - this._thumbnailTimer = null; - if (this.props.autoPlay === true) { - this.play(false); - } - } - this.setState({ hovering: false }); - } - }, { - key: '_handleImageError', - value: function _handleImageError(event) { - if (this.props.defaultImage && event.target.src.indexOf(this.props.defaultImage) === -1) { - event.target.src = this.props.defaultImage; + var x = Math.abs(deltaX); + var y = Math.abs(deltaY); + + // If y > x the user is scrolling up and down + if (y > x && !scrollingUpDown && !scrollingLeftRight) { + this.setState({ scrollingUpDown: true }); + } else if (!scrollingLeftRight && !scrollingUpDown) { + this.setState({ scrollingLeftRight: true }); } } - }, { - key: '_handleOnSwiped', - value: function _handleOnSwiped(ev, x, y, isFlick) { - this.setState({ isFlick: isFlick }); - } - }, { - key: '_sufficientSwipeOffset', - value: function _sufficientSwipeOffset() { - return Math.abs(this.state.offsetPercentage) > this.props.swipeThreshold; - } - }, { - key: '_shouldSlideOnSwipe', - value: function _shouldSlideOnSwipe() { - return this._sufficientSwipeOffset() || this.state.isFlick; - } }, { key: '_handleOnSwipedTo', - value: function _handleOnSwipedTo(index) { - var slideTo = this.state.currentIndex; + value: function _handleOnSwipedTo(side, isFlick) { + var _state2 = this.state, + currentIndex = _state2.currentIndex, + isTransitioning = _state2.isTransitioning; + + var slideTo = currentIndex; - if (this._shouldSlideOnSwipe()) { - slideTo += index; + if ((this._sufficientSwipeOffset() || isFlick) && !isTransitioning) { + slideTo += side; } - if (index < 0) { + if (side < 0) { if (!this._canSlideLeft()) { - slideTo = this.state.currentIndex; + slideTo = currentIndex; } } else { if (!this._canSlideRight()) { - slideTo = this.state.currentIndex; + slideTo = currentIndex; } } this._unthrottledSlideToIndex(slideTo); } }, { - key: '_handleSwiping', - value: function _handleSwiping(index, _, delta) { - var swipingTransitionDuration = this.props.swipingTransitionDuration; - var _state = this.state, - galleryWidth = _state.galleryWidth, - isTransitioning = _state.isTransitioning; - - - var offsetPercentage = index * (delta / galleryWidth * 100); - if (Math.abs(offsetPercentage) >= 100) { - offsetPercentage = index * 100; - } - - var swipingTransition = { - transition: 'transform ' + swipingTransitionDuration + 'ms ease-out' - }; - - if (!isTransitioning) { - this.setState({ - offsetPercentage: offsetPercentage, - style: swipingTransition - }); - } + key: '_sufficientSwipeOffset', + value: function _sufficientSwipeOffset() { + return Math.abs(this.state.offsetPercentage) > this.props.swipeThreshold; } }, { key: '_canNavigate', @@ -518,26 +615,40 @@ var ImageGallery = function (_React$Component) { }, { key: '_canSlideLeft', value: function _canSlideLeft() { - return this.props.infinite || this.state.currentIndex > 0; + return this.props.infinite || (this.props.isRTL ? this._canSlideNext() : this._canSlidePrevious()); } }, { key: '_canSlideRight', value: function _canSlideRight() { - return this.props.infinite || this.state.currentIndex < this.props.items.length - 1; + return this.props.infinite || (this.props.isRTL ? this._canSlidePrevious() : this._canSlideNext()); + } + }, { + key: '_canSlidePrevious', + value: function _canSlidePrevious() { + return this.state.currentIndex > 0; + } + }, { + key: '_canSlideNext', + value: function _canSlideNext() { + return this.state.currentIndex < this.props.items.length - 1; } }, { key: '_updateThumbnailTranslate', - value: function _updateThumbnailTranslate(prevState) { + value: function _updateThumbnailTranslate(previousIndex) { + var _state3 = this.state, + thumbsTranslate = _state3.thumbsTranslate, + currentIndex = _state3.currentIndex; + if (this.state.currentIndex === 0) { this._setThumbsTranslate(0); } else { - var indexDifference = Math.abs(prevState.currentIndex - this.state.currentIndex); + var indexDifference = Math.abs(previousIndex - currentIndex); var scroll = this._getThumbsTranslate(indexDifference); if (scroll > 0) { - if (prevState.currentIndex < this.state.currentIndex) { - this._setThumbsTranslate(this.state.thumbsTranslate - scroll); - } else if (prevState.currentIndex > this.state.currentIndex) { - this._setThumbsTranslate(this.state.thumbsTranslate + scroll); + if (previousIndex < currentIndex) { + this._setThumbsTranslate(thumbsTranslate - scroll); + } else if (previousIndex > currentIndex) { + this._setThumbsTranslate(thumbsTranslate + scroll); } } } @@ -554,14 +665,13 @@ var ImageGallery = function (_React$Component) { return 0; } - var _state2 = this.state, - thumbnailsWrapperWidth = _state2.thumbnailsWrapperWidth, - thumbnailsWrapperHeight = _state2.thumbnailsWrapperHeight; + var _state4 = this.state, + thumbnailsWrapperWidth = _state4.thumbnailsWrapperWidth, + thumbnailsWrapperHeight = _state4.thumbnailsWrapperHeight; var totalScroll = void 0; if (this._thumbnails) { - // total scroll required to see the last thumbnail if (this._isThumbnailHorizontal()) { if (this._thumbnails.scrollHeight <= thumbnailsWrapperHeight) { @@ -620,9 +730,9 @@ var ImageGallery = function (_React$Component) { }, { key: '_isGoingFromFirstToLast', value: function _isGoingFromFirstToLast() { - var _state3 = this.state, - currentIndex = _state3.currentIndex, - previousIndex = _state3.previousIndex; + var _state5 = this.state, + currentIndex = _state5.currentIndex, + previousIndex = _state5.previousIndex; var totalSlides = this.props.items.length - 1; return previousIndex === 0 && currentIndex === totalSlides; @@ -630,9 +740,9 @@ var ImageGallery = function (_React$Component) { }, { key: '_isGoingFromLastToFirst', value: function _isGoingFromLastToFirst() { - var _state4 = this.state, - currentIndex = _state4.currentIndex, - previousIndex = _state4.previousIndex; + var _state6 = this.state, + currentIndex = _state6.currentIndex, + previousIndex = _state6.previousIndex; var totalSlides = this.props.items.length - 1; return previousIndex === totalSlides && currentIndex === 0; @@ -641,10 +751,10 @@ var ImageGallery = function (_React$Component) { key: '_getTranslateXForTwoSlide', value: function _getTranslateXForTwoSlide(index) { // For taking care of infinite swipe when there are only two slides - var _state5 = this.state, - currentIndex = _state5.currentIndex, - offsetPercentage = _state5.offsetPercentage, - previousIndex = _state5.previousIndex; + var _state7 = this.state, + currentIndex = _state7.currentIndex, + offsetPercentage = _state7.offsetPercentage, + previousIndex = _state7.previousIndex; var baseTranslateX = -100 * currentIndex; var translateX = baseTranslateX + index * 100 + offsetPercentage; @@ -691,36 +801,73 @@ var ImageGallery = function (_React$Component) { } return {}; } + }, { + key: '_shouldPushSlideOnInfiniteMode', + value: function _shouldPushSlideOnInfiniteMode(index) { + /* + Push(show) slide if slide is the current slide, and the next slide + OR + The slide is going more than 1 slide left, or right, but not going from + first to last and not going from last to first + There is an edge case where if you go to the first or last slide, when they're + not left, or right of each other they will try to catch up in the background + so unless were going from first to last or vice versa we don't want the first + or last slide to show up during our transition + */ + return !this._slideIsTransitioning(index) || this._ignoreIsTransitioning() && !this._isFirstOrLastSlide(index); + } }, { key: '_slideIsTransitioning', value: function _slideIsTransitioning(index) { - var _state6 = this.state, - isTransitioning = _state6.isTransitioning, - previousIndex = _state6.previousIndex, - currentIndex = _state6.currentIndex; + /* + returns true if the gallery is transitioning and the index is not the + previous or currentIndex + */ + var _state8 = this.state, + isTransitioning = _state8.isTransitioning, + previousIndex = _state8.previousIndex, + currentIndex = _state8.currentIndex; - return isTransitioning && !(index === previousIndex || index == currentIndex); + var indexIsNotPreviousOrNextSlide = !(index === previousIndex || index === currentIndex); + return isTransitioning && indexIsNotPreviousOrNextSlide; + } + }, { + key: '_isFirstOrLastSlide', + value: function _isFirstOrLastSlide(index) { + var totalSlides = this.props.items.length - 1; + var isLastSlide = index === totalSlides; + var isFirstSlide = index === 0; + return isLastSlide || isFirstSlide; } }, { key: '_ignoreIsTransitioning', value: function _ignoreIsTransitioning() { - // Ignore isTransitioning because were not going to sibling slides - // e.g. center to left or center to right - var _state7 = this.state, - previousIndex = _state7.previousIndex, - currentIndex = _state7.currentIndex; + /* + Ignore isTransitioning because were not going to sibling slides + e.g. center to left or center to right + */ + var _state9 = this.state, + previousIndex = _state9.previousIndex, + currentIndex = _state9.currentIndex; - return Math.abs(previousIndex - currentIndex) > 1 && !this._isGoingFromFirstToLast() && !this._isGoingFromLastToFirst(); + var totalSlides = this.props.items.length - 1; + // we want to show the in between slides transition + var slidingMoreThanOneSlideLeftOrRight = Math.abs(previousIndex - currentIndex) > 1; + var notGoingFromFirstToLast = !(previousIndex === 0 && currentIndex === totalSlides); + var notGoingFromLastToFirst = !(previousIndex === totalSlides && currentIndex === 0); + + return slidingMoreThanOneSlideLeftOrRight && notGoingFromFirstToLast && notGoingFromLastToFirst; } }, { key: '_getSlideStyle', value: function _getSlideStyle(index) { - var _state8 = this.state, - currentIndex = _state8.currentIndex, - offsetPercentage = _state8.offsetPercentage; + var _state10 = this.state, + currentIndex = _state10.currentIndex, + offsetPercentage = _state10.offsetPercentage; var _props2 = this.props, infinite = _props2.infinite, - items = _props2.items; + items = _props2.items, + useTranslate3D = _props2.useTranslate3D; var baseTranslateX = -100 * currentIndex; var totalSlides = items.length - 1; @@ -743,7 +890,11 @@ var ImageGallery = function (_React$Component) { translateX = this._getTranslateXForTwoSlide(index); } - var translate = 'translate3d(' + translateX + '%, 0, 0)'; + var translate = 'translate(' + translateX + '%, 0)'; + + if (useTranslate3D) { + translate = 'translate3d(' + translateX + '%, 0, 0)'; + } return { WebkitTransform: translate, @@ -757,11 +908,23 @@ var ImageGallery = function (_React$Component) { key: '_getThumbnailStyle', value: function _getThumbnailStyle() { var translate = void 0; + var _props3 = this.props, + useTranslate3D = _props3.useTranslate3D, + isRTL = _props3.isRTL; + var thumbsTranslate = this.state.thumbsTranslate; + + var verticalTranslateValue = isRTL ? thumbsTranslate * -1 : thumbsTranslate; if (this._isThumbnailHorizontal()) { - translate = 'translate3d(0, ' + this.state.thumbsTranslate + 'px, 0)'; + translate = 'translate(0, ' + thumbsTranslate + 'px)'; + if (useTranslate3D) { + translate = 'translate3d(0, ' + thumbsTranslate + 'px, 0)'; + } } else { - translate = 'translate3d(' + this.state.thumbsTranslate + 'px, 0, 0)'; + translate = 'translate(' + verticalTranslateValue + 'px, 0)'; + if (useTranslate3D) { + translate = 'translate3d(' + verticalTranslateValue + 'px, 0, 0)'; + } } return { WebkitTransform: translate, @@ -771,156 +934,104 @@ var ImageGallery = function (_React$Component) { transform: translate }; } - }, { - key: '_slideLeft', - value: function _slideLeft(event) { - this.slideToIndex(this.state.currentIndex - 1, event); - } - }, { - key: '_slideRight', - value: function _slideRight(event) { - this.slideToIndex(this.state.currentIndex + 1, event); - } - }, { - key: '_renderItem', - value: function _renderItem(item) { - var onImageError = this.props.onImageError || this._handleImageError; - - return _react2.default.createElement( - 'div', - { className: 'image-gallery-image' }, - _react2.default.createElement('img', { - src: item.original, - alt: item.originalAlt, - srcSet: item.srcSet, - sizes: item.sizes, - title: item.originalTitle, - onLoad: this.props.onImageLoad, - onError: onImageError.bind(this) - }), - item.description && _react2.default.createElement( - 'span', - { className: 'image-gallery-description' }, - item.description - ) - ); - } - }, { - key: '_renderThumbInner', - value: function _renderThumbInner(item) { - var onThumbnailError = this._handleImageError; - if (this.props.onThumbnailError) { - onThumbnailError = this.props.onThumbnailError; - } - - return _react2.default.createElement( - 'div', - null, - _react2.default.createElement('img', { - src: item.thumbnail, - alt: item.thumbnailAlt, - title: item.thumbnailTitle, - onError: onThumbnailError.bind(this) - }), - _react2.default.createElement( - 'div', - { className: 'image-gallery-thumbnail-label' }, - item.thumbnailLabel - ) - ); - } }, { key: 'render', value: function render() { - var _this7 = this; + var _this5 = this; - var _state9 = this.state, - currentIndex = _state9.currentIndex, - isFullscreen = _state9.isFullscreen, - modalFullscreen = _state9.modalFullscreen, - isPlaying = _state9.isPlaying; - var infinite = this.props.infinite; + var _state11 = this.state, + currentIndex = _state11.currentIndex, + isFullscreen = _state11.isFullscreen, + modalFullscreen = _state11.modalFullscreen, + isPlaying = _state11.isPlaying, + scrollingLeftRight = _state11.scrollingLeftRight; + var _props4 = this.props, + infinite = _props4.infinite, + preventDefaultTouchmoveEvent = _props4.preventDefaultTouchmoveEvent, + isRTL = _props4.isRTL; var thumbnailStyle = this._getThumbnailStyle(); var thumbnailPosition = this.props.thumbnailPosition; - var slideLeft = this._slideLeft.bind(this); - var slideRight = this._slideRight.bind(this); + var slideLeft = this._slideLeft; + var slideRight = this._slideRight; var slides = []; var thumbnails = []; var bullets = []; - this.props.items.map(function (item, index) { - var alignment = _this7._getAlignmentClassName(index); + this.props.items.forEach(function (item, index) { + var alignment = _this5._getAlignmentClassName(index); var originalClass = item.originalClass ? ' ' + item.originalClass : ''; var thumbnailClass = item.thumbnailClass ? ' ' + item.thumbnailClass : ''; - var renderItem = item.renderItem || _this7.props.renderItem || _this7._renderItem.bind(_this7); + var renderItem = item.renderItem || _this5.props.renderItem || _this5._renderItem; - var renderThumbInner = item.renderThumbInner || _this7.props.renderThumbInner || _this7._renderThumbInner.bind(_this7); + var renderThumbInner = item.renderThumbInner || _this5.props.renderThumbInner || _this5._renderThumbInner; - var showItem = !_this7.props.lazyLoad || alignment || _this7._lazyLoaded[index]; - if (showItem && _this7.props.lazyLoad) { - _this7._lazyLoaded[index] = true; + var showItem = !_this5.props.lazyLoad || alignment || _this5._lazyLoaded[index]; + if (showItem && _this5.props.lazyLoad) { + _this5._lazyLoaded[index] = true; } - var slideStyle = _this7._getSlideStyle(index); + var slideStyle = _this5._getSlideStyle(index); + var slide = _react2.default.createElement( 'div', { key: index, className: 'image-gallery-slide' + alignment + originalClass, - style: _extends(slideStyle, _this7.state.style), - onClick: _this7.props.onClick, - onTouchMove: _this7.props.onTouchMove, - onTouchEnd: _this7.props.onTouchEnd, - onTouchStart: _this7.props.onTouchStart + style: _extends(slideStyle, _this5.state.style), + onClick: _this5.props.onClick, + onTouchMove: _this5.props.onTouchMove, + onTouchEnd: _this5.props.onTouchEnd, + onTouchStart: _this5.props.onTouchStart, + onMouseOver: _this5.props.onMouseOver, + onMouseLeave: _this5.props.onMouseLeave, + role: _this5.props.onClick && 'button' }, showItem ? renderItem(item) : _react2.default.createElement('div', { style: { height: '100%' } }) ); if (infinite) { // don't add some slides while transitioning to avoid background transitions - if (!_this7._slideIsTransitioning(index) || _this7._ignoreIsTransitioning()) { + if (_this5._shouldPushSlideOnInfiniteMode(index)) { slides.push(slide); } } else { slides.push(slide); } - if (_this7.props.showThumbnails) { + if (_this5.props.showThumbnails) { thumbnails.push(_react2.default.createElement( 'a', { - onMouseOver: _this7._handleMouseOverThumbnails.bind(_this7, index), - onMouseLeave: _this7._handleMouseLeaveThumbnails.bind(_this7, index), key: index, role: 'button', 'aria-pressed': currentIndex === index ? 'true' : 'false', 'aria-label': 'Go to Slide ' + (index + 1), className: 'image-gallery-thumbnail' + (currentIndex === index ? ' active' : '') + thumbnailClass, onClick: function onClick(event) { - _this7.slideToIndex.call(_this7, index, event); - if (_this7.props.onThumbnailClick) { - _this7.props.onThumbnailClick(event, index); - } + return _this5._onThumbnailClick(event, index); } }, renderThumbInner(item) )); } - if (_this7.props.showBullets) { + if (_this5.props.showBullets) { + var bulletOnClick = function bulletOnClick(event) { + if (item.bulletOnClick) { + item.bulletOnClick({ item: item, itemIndex: index, currentIndex: currentIndex }); + } + return _this5.slideToIndex.call(_this5, index, event); + }; bullets.push(_react2.default.createElement('button', { key: index, type: 'button', - className: 'image-gallery-bullet ' + (currentIndex === index ? 'active' : ''), - - onClick: function onClick(event) { - return _this7.slideToIndex.call(_this7, index, event); - }, + className: ['image-gallery-bullet', currentIndex === index ? 'active' : '', item.bulletClass || ''].join(' '), + onClick: bulletOnClick, 'aria-pressed': currentIndex === index ? 'true' : 'false', 'aria-label': 'Go to Slide ' + (index + 1) })); @@ -930,36 +1041,29 @@ var ImageGallery = function (_React$Component) { var slideWrapper = _react2.default.createElement( 'div', { - ref: function ref(i) { - return _this7._imageGallerySlideWrapper = i; - }, - className: 'image-gallery-slide-wrapper ' + thumbnailPosition + ref: this._initGalleryResizing, + className: 'image-gallery-slide-wrapper ' + thumbnailPosition + ' ' + (isRTL ? 'image-gallery-rtl' : '') }, this.props.renderCustomControls && this.props.renderCustomControls(), - this.props.showFullscreenButton && this.props.renderFullscreenButton(this._toggleFullScreen.bind(this), isFullscreen), - this.props.showPlayButton && this.props.renderPlayPauseButton(this._togglePlay.bind(this), isPlaying), + this.props.showFullscreenButton && this.props.renderFullscreenButton(this._toggleFullScreen, isFullscreen), + this.props.showPlayButton && this.props.renderPlayPauseButton(this._togglePlay, isPlaying), this._canNavigate() ? [this.props.showNav && _react2.default.createElement( 'span', { key: 'navigation' }, this.props.renderLeftNav(slideLeft, !this._canSlideLeft()), this.props.renderRightNav(slideRight, !this._canSlideRight()) - ), this.props.disableSwipe ? _react2.default.createElement( - 'div', - { className: 'image-gallery-slides', key: 'slides' }, - slides - ) : _react2.default.createElement( + ), _react2.default.createElement( _reactSwipeable2.default, { className: 'image-gallery-swipe', + disabled: this.props.disableSwipe, key: 'swipeable', - delta: 1, - onSwipingLeft: this._handleSwiping.bind(this, -1), - onSwipingRight: this._handleSwiping.bind(this, 1), - onSwiped: this._handleOnSwiped.bind(this), - onSwipedLeft: this._handleOnSwipedTo.bind(this, 1), - onSwipedRight: this._handleOnSwipedTo.bind(this, -1), - onSwipedDown: this._handleOnSwipedTo.bind(this, 0), - onSwipedUp: this._handleOnSwipedTo.bind(this, 0) + delta: 0, + flickThreshold: this.props.flickThreshold, + onSwiping: this._handleSwiping, + onSwiped: this._handleOnSwiped, + stopPropagation: this.props.stopPropagation, + preventDefaultTouchmoveEvent: preventDefaultTouchmoveEvent || scrollingLeftRight }, _react2.default.createElement( 'div', @@ -1005,13 +1109,17 @@ var ImageGallery = function (_React$Component) { ) ); + var classNames = ['image-gallery', this.props.additionalClass, modalFullscreen ? 'fullscreen-modal' : ''].filter(function (name) { + return typeof name === 'string'; + }).join(' '); + return _react2.default.createElement( 'div', { ref: function ref(i) { - return _this7._imageGallery = i; + return _this5._imageGallery = i; }, - className: 'image-gallery' + (modalFullscreen ? ' fullscreen-modal' : ''), + className: classNames, 'aria-live': 'polite' }, _react2.default.createElement( @@ -1023,7 +1131,7 @@ var ImageGallery = function (_React$Component) { this.props.showThumbnails && _react2.default.createElement( 'div', { - className: 'image-gallery-thumbnails-wrapper ' + thumbnailPosition, + className: 'image-gallery-thumbnails-wrapper ' + thumbnailPosition + ' ' + (!this._isThumbnailHorizontal() && isRTL ? 'thumbnails-wrapper-rtl' : ''), style: this._getThumbnailBarHeight() }, _react2.default.createElement( @@ -1031,14 +1139,14 @@ var ImageGallery = function (_React$Component) { { className: 'image-gallery-thumbnails', ref: function ref(i) { - return _this7._thumbnailsWrapper = i; + return _this5._thumbnailsWrapper = i; } }, _react2.default.createElement( 'div', { ref: function ref(t) { - return _this7._thumbnails = t; + return _this5._thumbnails = t; }, className: 'image-gallery-thumbnails-container', style: thumbnailStyle, @@ -1058,6 +1166,7 @@ var ImageGallery = function (_React$Component) { }(_react2.default.Component); ImageGallery.propTypes = { + flickThreshold: _propTypes2.default.number, items: _propTypes2.default.array.isRequired, showNav: _propTypes2.default.bool, autoPlay: _propTypes2.default.bool, @@ -1068,11 +1177,11 @@ ImageGallery.propTypes = { showThumbnails: _propTypes2.default.bool, showPlayButton: _propTypes2.default.bool, showFullscreenButton: _propTypes2.default.bool, - slideOnThumbnailHover: _propTypes2.default.bool, disableThumbnailScroll: _propTypes2.default.bool, disableArrowKeys: _propTypes2.default.bool, disableSwipe: _propTypes2.default.bool, useBrowserFullscreen: _propTypes2.default.bool, + preventDefaultTouchmoveEvent: _propTypes2.default.bool, defaultImage: _propTypes2.default.string, indexSeparator: _propTypes2.default.string, thumbnailPosition: _propTypes2.default.string, @@ -1091,6 +1200,8 @@ ImageGallery.propTypes = { onTouchMove: _propTypes2.default.func, onTouchEnd: _propTypes2.default.func, onTouchStart: _propTypes2.default.func, + onMouseOver: _propTypes2.default.func, + onMouseLeave: _propTypes2.default.func, onThumbnailError: _propTypes2.default.func, onThumbnailClick: _propTypes2.default.func, renderCustomControls: _propTypes2.default.func, @@ -1098,7 +1209,11 @@ ImageGallery.propTypes = { renderRightNav: _propTypes2.default.func, renderPlayPauseButton: _propTypes2.default.func, renderFullscreenButton: _propTypes2.default.func, - renderItem: _propTypes2.default.func + renderItem: _propTypes2.default.func, + stopPropagation: _propTypes2.default.bool, + additionalClass: _propTypes2.default.string, + useTranslate3D: _propTypes2.default.bool, + isRTL: _propTypes2.default.bool }; ImageGallery.defaultProps = { items: [], @@ -1111,11 +1226,15 @@ ImageGallery.defaultProps = { showThumbnails: true, showPlayButton: true, showFullscreenButton: true, - slideOnThumbnailHover: false, disableThumbnailScroll: false, disableArrowKeys: false, disableSwipe: false, + useTranslate3D: true, + isRTL: false, useBrowserFullscreen: true, + preventDefaultTouchmoveEvent: false, + flickThreshold: 0.4, + stopPropagation: false, indexSeparator: ' / ', thumbnailPosition: 'bottom', startIndex: 0, diff --git a/package.json b/package.json index f94bfc1b..7b3010d2 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "prop-types": "^15.5.8", - "react-swipeable": "^4.2.2", + "react-swipeable": "SectorLabs/react-swipeable#4.3.0-sl", "resize-observer-polyfill": "^1.5.0" } } diff --git a/src/ImageGallery.jsx b/src/ImageGallery.jsx index 2130d0e5..25c01962 100644 --- a/src/ImageGallery.jsx +++ b/src/ImageGallery.jsx @@ -549,9 +549,9 @@ export default class ImageGallery extends React.Component { } _handleSwiping = (e, deltaX, deltaY, delta) => { - const { galleryWidth, isTransitioning, scrollingUpDown } = this.state; const { swipingTransitionDuration } = this.props; this._setScrollDirection(deltaX, deltaY); + const { galleryWidth, isTransitioning, scrollingUpDown } = this.state; if (!isTransitioning && !scrollingUpDown) { const side = deltaX < 0 ? 1 : -1;