react-image-gallery 1.0.3 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -9
- package/build/image-gallery.js +183 -91
- package/gulpfile.js +33 -34
- package/package.json +1 -1
- package/src/ImageGallery.jsx +141 -73
package/README.md
CHANGED
|
@@ -4,21 +4,23 @@ React Carousel Image Gallery
|
|
|
4
4
|
[](https://badge.fury.io/js/react-image-gallery)
|
|
5
5
|
[](http://www.npmjs.com/package/react-image-gallery)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
### Live Demo (try it on mobile for swipe support)
|
|
8
|
+
[`linxtion.com/demo/react-image-gallery`](http://linxtion.com/demo/react-image-gallery)
|
|
9
9
|
|
|
10
10
|

|
|
11
11
|
|
|
12
12
|
React image gallery is a React component for building image galleries and carousels
|
|
13
13
|
|
|
14
|
-
Features
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
* Mobile swipe gestures
|
|
17
|
+
* Thumbnail navigation
|
|
18
|
+
* Fullscreen support
|
|
19
|
+
* Custom rendered slides
|
|
20
|
+
* RTL support
|
|
21
|
+
* Responsive design
|
|
20
22
|
* Tons of customization options (see props below)
|
|
21
|
-
* Lightweight ~
|
|
23
|
+
* Lightweight ~7kb gzipped
|
|
22
24
|
|
|
23
25
|
## Getting started
|
|
24
26
|
|
|
@@ -70,6 +72,7 @@ class MyGallery extends React.Component {
|
|
|
70
72
|
* Available Properties
|
|
71
73
|
* `original` - image src url
|
|
72
74
|
* `thumbnail` - image thumbnail src url
|
|
75
|
+
* `fullscreen` - image for fullscreen (defaults to original)
|
|
73
76
|
* `originalClass` - custom image class
|
|
74
77
|
* `thumbnailClass` - custom thumbnail class
|
|
75
78
|
* `renderItem` - Function for custom renderer (see renderItem below)
|
|
@@ -136,6 +139,7 @@ class MyGallery extends React.Component {
|
|
|
136
139
|
* `onThumbnailClick`: Function, `callback(event, index)`
|
|
137
140
|
* `onImageLoad`: Function, `callback(event)`
|
|
138
141
|
* `onSlide`: Function, `callback(currentIndex)`
|
|
142
|
+
* `onBeforeSlide`: Function, `callback(nextIndex)`
|
|
139
143
|
* `onScreenChange`: Function, `callback(fullscreenElement)`
|
|
140
144
|
* `onPause`: Function, `callback(currentIndex)`
|
|
141
145
|
* `onPlay`: Function, `callback(currentIndex)`
|
package/build/image-gallery.js
CHANGED
|
@@ -73,7 +73,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
73
73
|
_this.state = {
|
|
74
74
|
currentIndex: props.startIndex,
|
|
75
75
|
thumbsTranslate: 0,
|
|
76
|
-
|
|
76
|
+
currentSlideOffset: 0,
|
|
77
77
|
galleryWidth: 0,
|
|
78
78
|
thumbnailsWrapperWidth: 0,
|
|
79
79
|
thumbnailsWrapperHeight: 0,
|
|
@@ -93,6 +93,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
93
93
|
_this.handleScreenChange = _this.handleScreenChange.bind(_this);
|
|
94
94
|
_this.handleSwiping = _this.handleSwiping.bind(_this);
|
|
95
95
|
_this.onThumbnailMouseLeave = _this.onThumbnailMouseLeave.bind(_this);
|
|
96
|
+
_this.handleImageError = _this.handleImageError.bind(_this);
|
|
96
97
|
_this.pauseOrPlay = _this.pauseOrPlay.bind(_this);
|
|
97
98
|
_this.renderThumbInner = _this.renderThumbInner.bind(_this);
|
|
98
99
|
_this.renderItem = _this.renderItem.bind(_this);
|
|
@@ -132,13 +133,15 @@ var ImageGallery = function (_React$Component) {
|
|
|
132
133
|
lazyLoad = _props.lazyLoad,
|
|
133
134
|
slideDuration = _props.slideDuration,
|
|
134
135
|
startIndex = _props.startIndex,
|
|
135
|
-
thumbnailPosition = _props.thumbnailPosition
|
|
136
|
+
thumbnailPosition = _props.thumbnailPosition,
|
|
137
|
+
showThumbnails = _props.showThumbnails;
|
|
136
138
|
var currentIndex = this.state.currentIndex;
|
|
137
139
|
|
|
138
140
|
var itemsSizeChanged = prevProps.items.length !== items.length;
|
|
139
141
|
var itemsChanged = !(0, _lodash6.default)(prevProps.items, items);
|
|
140
142
|
var startIndexUpdated = prevProps.startIndex !== startIndex;
|
|
141
143
|
var thumbnailsPositionChanged = prevProps.thumbnailPosition !== thumbnailPosition;
|
|
144
|
+
var showThumbnailsChanged = prevProps.showThumbnails !== showThumbnails;
|
|
142
145
|
|
|
143
146
|
if (thumbnailsPositionChanged) {
|
|
144
147
|
// re-initialize resizeObserver because slides was unmounted and mounted again
|
|
@@ -146,7 +149,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
146
149
|
this.initResizeObserver(this.imageGallerySlideWrapper);
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
if (itemsSizeChanged) {
|
|
152
|
+
if (itemsSizeChanged || showThumbnailsChanged) {
|
|
150
153
|
this.handleResize();
|
|
151
154
|
}
|
|
152
155
|
if (prevState.currentIndex !== currentIndex) {
|
|
@@ -173,9 +176,9 @@ var ImageGallery = function (_React$Component) {
|
|
|
173
176
|
window.removeEventListener('mousedown', this.handleMouseDown);
|
|
174
177
|
this.removeScreenChangeEvent();
|
|
175
178
|
this.removeResizeObserver();
|
|
176
|
-
if (this.
|
|
177
|
-
window.clearInterval(this.
|
|
178
|
-
this.
|
|
179
|
+
if (this.playPauseIntervalId) {
|
|
180
|
+
window.clearInterval(this.playPauseIntervalId);
|
|
181
|
+
this.playPauseIntervalId = null;
|
|
179
182
|
}
|
|
180
183
|
if (this.transitionTimer) {
|
|
181
184
|
window.clearTimeout(this.transitionTimer);
|
|
@@ -351,39 +354,50 @@ var ImageGallery = function (_React$Component) {
|
|
|
351
354
|
// For taking care of infinite swipe when there are only two slides
|
|
352
355
|
var _state4 = this.state,
|
|
353
356
|
currentIndex = _state4.currentIndex,
|
|
354
|
-
|
|
357
|
+
currentSlideOffset = _state4.currentSlideOffset,
|
|
355
358
|
previousIndex = _state4.previousIndex;
|
|
356
359
|
|
|
360
|
+
var indexChanged = currentIndex !== previousIndex;
|
|
361
|
+
var firstSlideWasPrevSlide = index === 0 && previousIndex === 0;
|
|
362
|
+
var secondSlideWasPrevSlide = index === 1 && previousIndex === 1;
|
|
363
|
+
var firstSlideIsNextSlide = index === 0 && currentIndex === 1;
|
|
364
|
+
var secondSlideIsNextSlide = index === 1 && currentIndex === 0;
|
|
365
|
+
var swipingEnded = currentSlideOffset === 0;
|
|
357
366
|
var baseTranslateX = -100 * currentIndex;
|
|
358
|
-
var translateX = baseTranslateX + index * 100 +
|
|
367
|
+
var translateX = baseTranslateX + index * 100 + currentSlideOffset;
|
|
359
368
|
|
|
360
369
|
// keep track of user swiping direction
|
|
361
|
-
|
|
370
|
+
// important to understand how to translateX based on last direction
|
|
371
|
+
if (currentSlideOffset > 0) {
|
|
362
372
|
this.direction = 'left';
|
|
363
|
-
} else if (
|
|
373
|
+
} else if (currentSlideOffset < 0) {
|
|
364
374
|
this.direction = 'right';
|
|
365
375
|
}
|
|
366
376
|
|
|
367
|
-
// when swiping make sure the
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
translateX = 100 +
|
|
377
|
+
// when swiping between two slides make sure the next and prev slides
|
|
378
|
+
// are on both left and right
|
|
379
|
+
if (secondSlideIsNextSlide && currentSlideOffset > 0) {
|
|
380
|
+
// swiping right
|
|
381
|
+
translateX = -100 + currentSlideOffset;
|
|
382
|
+
}
|
|
383
|
+
if (firstSlideIsNextSlide && currentSlideOffset < 0) {
|
|
384
|
+
// swiping left
|
|
385
|
+
translateX = 100 + currentSlideOffset;
|
|
372
386
|
}
|
|
373
387
|
|
|
374
|
-
if (
|
|
375
|
-
// when
|
|
376
|
-
if (
|
|
388
|
+
if (indexChanged) {
|
|
389
|
+
// when indexChanged move the slide to the correct side
|
|
390
|
+
if (firstSlideWasPrevSlide && swipingEnded && this.direction === 'left') {
|
|
377
391
|
translateX = 100;
|
|
378
|
-
} else if (
|
|
392
|
+
} else if (secondSlideWasPrevSlide && swipingEnded && this.direction === 'right') {
|
|
379
393
|
translateX = -100;
|
|
380
394
|
}
|
|
381
395
|
} else {
|
|
382
|
-
// keep the slide on the correct
|
|
383
|
-
if (
|
|
396
|
+
// keep the slide on the correct side if the swipe was not successful
|
|
397
|
+
if (secondSlideIsNextSlide && swipingEnded && this.direction === 'left') {
|
|
384
398
|
translateX = -100;
|
|
385
399
|
}
|
|
386
|
-
if (
|
|
400
|
+
if (firstSlideIsNextSlide && swipingEnded && this.direction === 'right') {
|
|
387
401
|
translateX = 100;
|
|
388
402
|
}
|
|
389
403
|
}
|
|
@@ -405,7 +419,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
405
419
|
value: function getSlideStyle(index) {
|
|
406
420
|
var _state5 = this.state,
|
|
407
421
|
currentIndex = _state5.currentIndex,
|
|
408
|
-
|
|
422
|
+
currentSlideOffset = _state5.currentSlideOffset,
|
|
409
423
|
slideStyle = _state5.slideStyle;
|
|
410
424
|
var _props5 = this.props,
|
|
411
425
|
infinite = _props5.infinite,
|
|
@@ -418,17 +432,17 @@ var ImageGallery = function (_React$Component) {
|
|
|
418
432
|
|
|
419
433
|
// calculates where the other slides belong based on currentIndex
|
|
420
434
|
// if it is RTL the base line should be reversed
|
|
421
|
-
var translateX = (baseTranslateX + index * 100) * (isRTL ? -1 : 1) +
|
|
435
|
+
var translateX = (baseTranslateX + index * 100) * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
422
436
|
|
|
423
437
|
if (infinite && items.length > 2) {
|
|
424
438
|
if (currentIndex === 0 && index === totalSlides) {
|
|
425
439
|
// make the last slide the slide before the first
|
|
426
440
|
// if it is RTL the base line should be reversed
|
|
427
|
-
translateX = -100 * (isRTL ? -1 : 1) +
|
|
441
|
+
translateX = -100 * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
428
442
|
} else if (currentIndex === totalSlides && index === 0) {
|
|
429
443
|
// make the first slide the slide after the last
|
|
430
444
|
// if it is RTL the base line should be reversed
|
|
431
|
-
translateX = 100 * (isRTL ? -1 : 1) +
|
|
445
|
+
translateX = 100 * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
432
446
|
}
|
|
433
447
|
}
|
|
434
448
|
|
|
@@ -490,12 +504,13 @@ var ImageGallery = function (_React$Component) {
|
|
|
490
504
|
}
|
|
491
505
|
}, {
|
|
492
506
|
key: 'getSlideItems',
|
|
493
|
-
value: function getSlideItems(
|
|
507
|
+
value: function getSlideItems() {
|
|
494
508
|
var _this4 = this;
|
|
495
509
|
|
|
496
510
|
var currentIndex = this.state.currentIndex;
|
|
497
511
|
var _props7 = this.props,
|
|
498
512
|
infinite = _props7.infinite,
|
|
513
|
+
items = _props7.items,
|
|
499
514
|
slideOnThumbnailOver = _props7.slideOnThumbnailOver,
|
|
500
515
|
onClick = _props7.onClick,
|
|
501
516
|
lazyLoad = _props7.lazyLoad,
|
|
@@ -531,7 +546,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
531
546
|
var slide = _react2.default.createElement(
|
|
532
547
|
'div',
|
|
533
548
|
{
|
|
534
|
-
key: 'slide-' + item.original,
|
|
549
|
+
key: 'slide-' + item.original + '-' + index,
|
|
535
550
|
tabIndex: '-1',
|
|
536
551
|
className: 'image-gallery-slide ' + alignment + ' ' + originalClass,
|
|
537
552
|
style: slideStyle,
|
|
@@ -562,7 +577,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
562
577
|
thumbnails.push(_react2.default.createElement(
|
|
563
578
|
'button',
|
|
564
579
|
{
|
|
565
|
-
key: 'thumbnail-' + item.original,
|
|
580
|
+
key: 'thumbnail-' + item.original + '-' + index,
|
|
566
581
|
type: 'button',
|
|
567
582
|
tabIndex: '0',
|
|
568
583
|
'aria-pressed': currentIndex === index ? 'true' : 'false',
|
|
@@ -597,7 +612,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
597
612
|
var igBulletClass = (0, _clsx2.default)('image-gallery-bullet', item.bulletClass, { active: currentIndex === index });
|
|
598
613
|
bullets.push(_react2.default.createElement('button', {
|
|
599
614
|
type: 'button',
|
|
600
|
-
key: 'bullet-' + item.original,
|
|
615
|
+
key: 'bullet-' + item.original + '-' + index,
|
|
601
616
|
className: igBulletClass,
|
|
602
617
|
onClick: bulletOnClick,
|
|
603
618
|
'aria-pressed': currentIndex === index ? 'true' : 'false',
|
|
@@ -762,9 +777,9 @@ var ImageGallery = function (_React$Component) {
|
|
|
762
777
|
if (!isTransitioning && !scrollingUpDown) {
|
|
763
778
|
var side = dir === _reactSwipeable.RIGHT ? 1 : -1;
|
|
764
779
|
|
|
765
|
-
var
|
|
766
|
-
if (Math.abs(
|
|
767
|
-
|
|
780
|
+
var currentSlideOffset = absX / galleryWidth * 100;
|
|
781
|
+
if (Math.abs(currentSlideOffset) >= 100) {
|
|
782
|
+
currentSlideOffset = 100;
|
|
768
783
|
}
|
|
769
784
|
|
|
770
785
|
var swipingTransition = {
|
|
@@ -772,21 +787,21 @@ var ImageGallery = function (_React$Component) {
|
|
|
772
787
|
};
|
|
773
788
|
|
|
774
789
|
this.setState({
|
|
775
|
-
|
|
790
|
+
currentSlideOffset: side * currentSlideOffset,
|
|
776
791
|
slideStyle: swipingTransition
|
|
777
792
|
});
|
|
778
793
|
} else {
|
|
779
794
|
// don't move the slide
|
|
780
|
-
this.setState({
|
|
795
|
+
this.setState({ currentSlideOffset: 0 });
|
|
781
796
|
}
|
|
782
797
|
}
|
|
783
798
|
}, {
|
|
784
799
|
key: 'sufficientSwipe',
|
|
785
800
|
value: function sufficientSwipe() {
|
|
786
|
-
var
|
|
801
|
+
var currentSlideOffset = this.state.currentSlideOffset;
|
|
787
802
|
var swipeThreshold = this.props.swipeThreshold;
|
|
788
803
|
|
|
789
|
-
return Math.abs(
|
|
804
|
+
return Math.abs(currentSlideOffset) > swipeThreshold;
|
|
790
805
|
}
|
|
791
806
|
}, {
|
|
792
807
|
key: 'handleOnSwiped',
|
|
@@ -872,13 +887,13 @@ var ImageGallery = function (_React$Component) {
|
|
|
872
887
|
|
|
873
888
|
switch (key) {
|
|
874
889
|
case LEFT_ARROW:
|
|
875
|
-
if (this.canSlideLeft() && !this.
|
|
876
|
-
this.slideLeft();
|
|
890
|
+
if (this.canSlideLeft() && !this.playPauseIntervalId) {
|
|
891
|
+
this.slideLeft(event);
|
|
877
892
|
}
|
|
878
893
|
break;
|
|
879
894
|
case RIGHT_ARROW:
|
|
880
|
-
if (this.canSlideRight() && !this.
|
|
881
|
-
this.slideRight();
|
|
895
|
+
if (this.canSlideRight() && !this.playPauseIntervalId) {
|
|
896
|
+
this.slideRight(event);
|
|
882
897
|
}
|
|
883
898
|
break;
|
|
884
899
|
case ESC_KEY:
|
|
@@ -961,7 +976,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
961
976
|
}, {
|
|
962
977
|
key: 'togglePlay',
|
|
963
978
|
value: function togglePlay() {
|
|
964
|
-
if (this.
|
|
979
|
+
if (this.playPauseIntervalId) {
|
|
965
980
|
this.pause();
|
|
966
981
|
} else {
|
|
967
982
|
this.play();
|
|
@@ -973,12 +988,16 @@ var ImageGallery = function (_React$Component) {
|
|
|
973
988
|
/*
|
|
974
989
|
handles screen change events that the browser triggers e.g. esc key
|
|
975
990
|
*/
|
|
976
|
-
var
|
|
991
|
+
var _props13 = this.props,
|
|
992
|
+
onScreenChange = _props13.onScreenChange,
|
|
993
|
+
useBrowserFullscreen = _props13.useBrowserFullscreen;
|
|
977
994
|
|
|
978
995
|
var fullScreenElement = document.fullscreenElement || document.msFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement;
|
|
979
996
|
|
|
980
|
-
if
|
|
981
|
-
this.
|
|
997
|
+
// check if screenchange element is the gallery
|
|
998
|
+
var isFullscreen = this.imageGallery.current === fullScreenElement;
|
|
999
|
+
if (onScreenChange) onScreenChange(isFullscreen);
|
|
1000
|
+
if (useBrowserFullscreen) this.setState({ isFullscreen: isFullscreen });
|
|
982
1001
|
}
|
|
983
1002
|
}, {
|
|
984
1003
|
key: 'slideToIndex',
|
|
@@ -986,14 +1005,15 @@ var ImageGallery = function (_React$Component) {
|
|
|
986
1005
|
var _state12 = this.state,
|
|
987
1006
|
currentIndex = _state12.currentIndex,
|
|
988
1007
|
isTransitioning = _state12.isTransitioning;
|
|
989
|
-
var
|
|
990
|
-
items =
|
|
991
|
-
slideDuration =
|
|
1008
|
+
var _props14 = this.props,
|
|
1009
|
+
items = _props14.items,
|
|
1010
|
+
slideDuration = _props14.slideDuration,
|
|
1011
|
+
onBeforeSlide = _props14.onBeforeSlide;
|
|
992
1012
|
|
|
993
1013
|
|
|
994
1014
|
if (!isTransitioning) {
|
|
995
1015
|
if (event) {
|
|
996
|
-
if (this.
|
|
1016
|
+
if (this.playPauseIntervalId) {
|
|
997
1017
|
// user triggered event while ImageGallery is playing, reset interval
|
|
998
1018
|
this.pause(false);
|
|
999
1019
|
this.play(false);
|
|
@@ -1008,50 +1028,102 @@ var ImageGallery = function (_React$Component) {
|
|
|
1008
1028
|
nextIndex = 0;
|
|
1009
1029
|
}
|
|
1010
1030
|
|
|
1031
|
+
if (onBeforeSlide && nextIndex !== currentIndex) {
|
|
1032
|
+
onBeforeSlide(nextIndex);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1011
1035
|
this.setState({
|
|
1012
1036
|
previousIndex: currentIndex,
|
|
1013
1037
|
currentIndex: nextIndex,
|
|
1014
1038
|
isTransitioning: nextIndex !== currentIndex,
|
|
1015
|
-
|
|
1039
|
+
currentSlideOffset: 0,
|
|
1016
1040
|
slideStyle: { transition: 'all ' + slideDuration + 'ms ease-out' }
|
|
1017
1041
|
}, this.onSliding);
|
|
1018
1042
|
}
|
|
1019
1043
|
}
|
|
1020
1044
|
}, {
|
|
1021
1045
|
key: 'slideLeft',
|
|
1022
|
-
value: function slideLeft() {
|
|
1046
|
+
value: function slideLeft(event) {
|
|
1023
1047
|
var isRTL = this.props.isRTL;
|
|
1024
1048
|
|
|
1025
1049
|
if (isRTL) {
|
|
1026
|
-
this.slideNext();
|
|
1050
|
+
this.slideNext(event);
|
|
1027
1051
|
} else {
|
|
1028
|
-
this.slidePrevious();
|
|
1052
|
+
this.slidePrevious(event);
|
|
1029
1053
|
}
|
|
1030
1054
|
}
|
|
1031
1055
|
}, {
|
|
1032
1056
|
key: 'slideRight',
|
|
1033
|
-
value: function slideRight() {
|
|
1057
|
+
value: function slideRight(event) {
|
|
1034
1058
|
var isRTL = this.props.isRTL;
|
|
1035
1059
|
|
|
1036
1060
|
if (isRTL) {
|
|
1037
|
-
this.slidePrevious();
|
|
1061
|
+
this.slidePrevious(event);
|
|
1038
1062
|
} else {
|
|
1039
|
-
this.slideNext();
|
|
1063
|
+
this.slideNext(event);
|
|
1040
1064
|
}
|
|
1041
1065
|
}
|
|
1042
1066
|
}, {
|
|
1043
1067
|
key: 'slidePrevious',
|
|
1044
1068
|
value: function slidePrevious(event) {
|
|
1045
|
-
var
|
|
1069
|
+
var _this6 = this;
|
|
1046
1070
|
|
|
1047
|
-
this.
|
|
1071
|
+
var _state13 = this.state,
|
|
1072
|
+
currentIndex = _state13.currentIndex,
|
|
1073
|
+
currentSlideOffset = _state13.currentSlideOffset,
|
|
1074
|
+
isTransitioning = _state13.isTransitioning;
|
|
1075
|
+
var items = this.props.items;
|
|
1076
|
+
|
|
1077
|
+
var nextIndex = currentIndex - 1;
|
|
1078
|
+
|
|
1079
|
+
if (isTransitioning) return;
|
|
1080
|
+
|
|
1081
|
+
if (items.length === 2) {
|
|
1082
|
+
/*
|
|
1083
|
+
When there are only 2 slides fake a tiny swipe to get the slides
|
|
1084
|
+
on the correct side for transitioning
|
|
1085
|
+
*/
|
|
1086
|
+
this.setState({
|
|
1087
|
+
currentSlideOffset: currentSlideOffset + 0.001, // this will reset once index changes
|
|
1088
|
+
slideStyle: { transition: 'none' } // move the slide over instantly
|
|
1089
|
+
}, function () {
|
|
1090
|
+
// add 25ms timeout to avoid delay in moving slides over
|
|
1091
|
+
window.setTimeout(function () {
|
|
1092
|
+
return _this6.slideToIndex(nextIndex, event);
|
|
1093
|
+
}, 25);
|
|
1094
|
+
});
|
|
1095
|
+
} else {
|
|
1096
|
+
this.slideToIndex(nextIndex, event);
|
|
1097
|
+
}
|
|
1048
1098
|
}
|
|
1049
1099
|
}, {
|
|
1050
1100
|
key: 'slideNext',
|
|
1051
1101
|
value: function slideNext(event) {
|
|
1052
|
-
var
|
|
1102
|
+
var _this7 = this;
|
|
1103
|
+
|
|
1104
|
+
var _state14 = this.state,
|
|
1105
|
+
currentIndex = _state14.currentIndex,
|
|
1106
|
+
currentSlideOffset = _state14.currentSlideOffset,
|
|
1107
|
+
isTransitioning = _state14.isTransitioning;
|
|
1108
|
+
var items = this.props.items;
|
|
1053
1109
|
|
|
1054
|
-
|
|
1110
|
+
var nextIndex = currentIndex + 1;
|
|
1111
|
+
|
|
1112
|
+
if (isTransitioning) return;
|
|
1113
|
+
|
|
1114
|
+
if (items.length === 2) {
|
|
1115
|
+
// same as above for 2 slides
|
|
1116
|
+
this.setState({
|
|
1117
|
+
currentSlideOffset: currentSlideOffset - 0.001,
|
|
1118
|
+
slideStyle: { transition: 'none' }
|
|
1119
|
+
}, function () {
|
|
1120
|
+
window.setTimeout(function () {
|
|
1121
|
+
return _this7.slideToIndex(nextIndex, event);
|
|
1122
|
+
}, 25);
|
|
1123
|
+
});
|
|
1124
|
+
} else {
|
|
1125
|
+
this.slideToIndex(nextIndex, event);
|
|
1126
|
+
}
|
|
1055
1127
|
}
|
|
1056
1128
|
}, {
|
|
1057
1129
|
key: 'handleThumbnailMouseOver',
|
|
@@ -1086,19 +1158,19 @@ var ImageGallery = function (_React$Component) {
|
|
|
1086
1158
|
}, {
|
|
1087
1159
|
key: 'addScreenChangeEvent',
|
|
1088
1160
|
value: function addScreenChangeEvent() {
|
|
1089
|
-
var
|
|
1161
|
+
var _this8 = this;
|
|
1090
1162
|
|
|
1091
1163
|
screenChangeEvents.forEach(function (eventName) {
|
|
1092
|
-
document.addEventListener(eventName,
|
|
1164
|
+
document.addEventListener(eventName, _this8.handleScreenChange);
|
|
1093
1165
|
});
|
|
1094
1166
|
}
|
|
1095
1167
|
}, {
|
|
1096
1168
|
key: 'removeScreenChangeEvent',
|
|
1097
1169
|
value: function removeScreenChangeEvent() {
|
|
1098
|
-
var
|
|
1170
|
+
var _this9 = this;
|
|
1099
1171
|
|
|
1100
1172
|
screenChangeEvents.forEach(function (eventName) {
|
|
1101
|
-
document.removeEventListener(eventName,
|
|
1173
|
+
document.removeEventListener(eventName, _this9.handleScreenChange);
|
|
1102
1174
|
});
|
|
1103
1175
|
}
|
|
1104
1176
|
}, {
|
|
@@ -1167,15 +1239,15 @@ var ImageGallery = function (_React$Component) {
|
|
|
1167
1239
|
key: 'play',
|
|
1168
1240
|
value: function play() {
|
|
1169
1241
|
var shouldCallOnPlay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
1170
|
-
var
|
|
1171
|
-
onPlay =
|
|
1172
|
-
slideInterval =
|
|
1173
|
-
slideDuration =
|
|
1242
|
+
var _props15 = this.props,
|
|
1243
|
+
onPlay = _props15.onPlay,
|
|
1244
|
+
slideInterval = _props15.slideInterval,
|
|
1245
|
+
slideDuration = _props15.slideDuration;
|
|
1174
1246
|
var currentIndex = this.state.currentIndex;
|
|
1175
1247
|
|
|
1176
|
-
if (!this.
|
|
1248
|
+
if (!this.playPauseIntervalId) {
|
|
1177
1249
|
this.setState({ isPlaying: true });
|
|
1178
|
-
this.
|
|
1250
|
+
this.playPauseIntervalId = window.setInterval(this.pauseOrPlay, Math.max(slideInterval, slideDuration));
|
|
1179
1251
|
if (onPlay && shouldCallOnPlay) {
|
|
1180
1252
|
onPlay(currentIndex);
|
|
1181
1253
|
}
|
|
@@ -1188,9 +1260,9 @@ var ImageGallery = function (_React$Component) {
|
|
|
1188
1260
|
var onPause = this.props.onPause;
|
|
1189
1261
|
var currentIndex = this.state.currentIndex;
|
|
1190
1262
|
|
|
1191
|
-
if (this.
|
|
1192
|
-
window.clearInterval(this.
|
|
1193
|
-
this.
|
|
1263
|
+
if (this.playPauseIntervalId) {
|
|
1264
|
+
window.clearInterval(this.playPauseIntervalId);
|
|
1265
|
+
this.playPauseIntervalId = null;
|
|
1194
1266
|
this.setState({ isPlaying: false });
|
|
1195
1267
|
if (onPause && shouldCallOnPause) {
|
|
1196
1268
|
onPause(currentIndex);
|
|
@@ -1212,14 +1284,28 @@ var ImageGallery = function (_React$Component) {
|
|
|
1212
1284
|
this.loadedImages[item.original] = true;
|
|
1213
1285
|
return false;
|
|
1214
1286
|
}
|
|
1287
|
+
}, {
|
|
1288
|
+
key: 'handleImageLoaded',
|
|
1289
|
+
value: function handleImageLoaded(event, item) {
|
|
1290
|
+
var onImageLoad = this.props.onImageLoad;
|
|
1291
|
+
|
|
1292
|
+
var imageExists = this.loadedImages[item.original];
|
|
1293
|
+
if (!imageExists && onImageLoad) {
|
|
1294
|
+
this.loadedImages[item.original] = true; // prevent from call again
|
|
1295
|
+
// image just loaded, call onImageLoad
|
|
1296
|
+
onImageLoad(event);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1215
1299
|
}, {
|
|
1216
1300
|
key: 'renderItem',
|
|
1217
1301
|
value: function renderItem(item) {
|
|
1218
|
-
var
|
|
1219
|
-
|
|
1220
|
-
|
|
1302
|
+
var _this10 = this;
|
|
1303
|
+
|
|
1304
|
+
var isFullscreen = this.state.isFullscreen;
|
|
1305
|
+
var onImageError = this.props.onImageError;
|
|
1221
1306
|
|
|
1222
1307
|
var handleImageError = onImageError || this.handleImageError;
|
|
1308
|
+
var itemSrc = isFullscreen ? item.fullscreen || item.original : item.original;
|
|
1223
1309
|
|
|
1224
1310
|
return _react2.default.createElement(
|
|
1225
1311
|
'div',
|
|
@@ -1227,12 +1313,14 @@ var ImageGallery = function (_React$Component) {
|
|
|
1227
1313
|
item.imageSet ? _react2.default.createElement(
|
|
1228
1314
|
'picture',
|
|
1229
1315
|
{
|
|
1230
|
-
onLoad:
|
|
1316
|
+
onLoad: function onLoad(event) {
|
|
1317
|
+
return _this10.handleImageLoaded(event, item);
|
|
1318
|
+
},
|
|
1231
1319
|
onError: handleImageError
|
|
1232
1320
|
},
|
|
1233
|
-
item.imageSet.map(function (source) {
|
|
1321
|
+
item.imageSet.map(function (source, index) {
|
|
1234
1322
|
return _react2.default.createElement('source', {
|
|
1235
|
-
key: source.
|
|
1323
|
+
key: 'media-' + source.srcSet + '-' + index,
|
|
1236
1324
|
media: source.media,
|
|
1237
1325
|
srcSet: source.srcSet,
|
|
1238
1326
|
type: source.type
|
|
@@ -1241,16 +1329,18 @@ var ImageGallery = function (_React$Component) {
|
|
|
1241
1329
|
_react2.default.createElement('img', {
|
|
1242
1330
|
className: 'image-gallery-image',
|
|
1243
1331
|
alt: item.originalAlt,
|
|
1244
|
-
src:
|
|
1332
|
+
src: itemSrc
|
|
1245
1333
|
})
|
|
1246
1334
|
) : _react2.default.createElement('img', {
|
|
1247
1335
|
className: 'image-gallery-image',
|
|
1248
|
-
src:
|
|
1336
|
+
src: itemSrc,
|
|
1249
1337
|
alt: item.originalAlt,
|
|
1250
1338
|
srcSet: item.srcSet,
|
|
1251
1339
|
sizes: item.sizes,
|
|
1252
1340
|
title: item.originalTitle,
|
|
1253
|
-
onLoad:
|
|
1341
|
+
onLoad: function onLoad(event) {
|
|
1342
|
+
return _this10.handleImageLoaded(event, item);
|
|
1343
|
+
},
|
|
1254
1344
|
onError: handleImageError
|
|
1255
1345
|
}),
|
|
1256
1346
|
item.description && _react2.default.createElement(
|
|
@@ -1287,11 +1377,11 @@ var ImageGallery = function (_React$Component) {
|
|
|
1287
1377
|
}, {
|
|
1288
1378
|
key: 'render',
|
|
1289
1379
|
value: function render() {
|
|
1290
|
-
var
|
|
1291
|
-
currentIndex =
|
|
1292
|
-
isFullscreen =
|
|
1293
|
-
modalFullscreen =
|
|
1294
|
-
isPlaying =
|
|
1380
|
+
var _state15 = this.state,
|
|
1381
|
+
currentIndex = _state15.currentIndex,
|
|
1382
|
+
isFullscreen = _state15.isFullscreen,
|
|
1383
|
+
modalFullscreen = _state15.modalFullscreen,
|
|
1384
|
+
isPlaying = _state15.isPlaying;
|
|
1295
1385
|
var _props16 = this.props,
|
|
1296
1386
|
additionalClass = _props16.additionalClass,
|
|
1297
1387
|
indexSeparator = _props16.indexSeparator,
|
|
@@ -1313,7 +1403,7 @@ var ImageGallery = function (_React$Component) {
|
|
|
1313
1403
|
|
|
1314
1404
|
var thumbnailStyle = this.getThumbnailStyle();
|
|
1315
1405
|
|
|
1316
|
-
var _getSlideItems = this.getSlideItems(
|
|
1406
|
+
var _getSlideItems = this.getSlideItems(),
|
|
1317
1407
|
slides = _getSlideItems.slides,
|
|
1318
1408
|
thumbnails = _getSlideItems.thumbnails,
|
|
1319
1409
|
bullets = _getSlideItems.bullets;
|
|
@@ -1328,8 +1418,8 @@ var ImageGallery = function (_React$Component) {
|
|
|
1328
1418
|
_react2.default.Fragment,
|
|
1329
1419
|
null,
|
|
1330
1420
|
showNav && _react2.default.createElement(
|
|
1331
|
-
|
|
1332
|
-
|
|
1421
|
+
_react2.default.Fragment,
|
|
1422
|
+
null,
|
|
1333
1423
|
renderLeftNav(this.slideLeft, !this.canSlideLeft()),
|
|
1334
1424
|
renderRightNav(this.slideRight, !this.canSlideRight())
|
|
1335
1425
|
),
|
|
@@ -1337,7 +1427,6 @@ var ImageGallery = function (_React$Component) {
|
|
|
1337
1427
|
_reactSwipeable.Swipeable,
|
|
1338
1428
|
{
|
|
1339
1429
|
className: 'image-gallery-swipe',
|
|
1340
|
-
key: 'swipeable',
|
|
1341
1430
|
delta: 0,
|
|
1342
1431
|
onSwiping: this.handleSwiping,
|
|
1343
1432
|
onSwiped: this.handleOnSwiped
|
|
@@ -1443,6 +1532,7 @@ ImageGallery.propTypes = {
|
|
|
1443
1532
|
bulletOnClick: _propTypes.func,
|
|
1444
1533
|
description: _propTypes.string,
|
|
1445
1534
|
original: _propTypes.string.isRequired,
|
|
1535
|
+
fullscreen: _propTypes.string,
|
|
1446
1536
|
originalAlt: _propTypes.string,
|
|
1447
1537
|
originalTitle: _propTypes.string,
|
|
1448
1538
|
thumbnail: _propTypes.string,
|
|
@@ -1481,6 +1571,7 @@ ImageGallery.propTypes = {
|
|
|
1481
1571
|
swipeThreshold: _propTypes.number,
|
|
1482
1572
|
swipingTransitionDuration: _propTypes.number,
|
|
1483
1573
|
onSlide: _propTypes.func,
|
|
1574
|
+
onBeforeSlide: _propTypes.func,
|
|
1484
1575
|
onScreenChange: _propTypes.func,
|
|
1485
1576
|
onPause: _propTypes.func,
|
|
1486
1577
|
onPlay: _propTypes.func,
|
|
@@ -1533,6 +1624,7 @@ ImageGallery.defaultProps = {
|
|
|
1533
1624
|
slideDuration: 450,
|
|
1534
1625
|
swipingTransitionDuration: 0,
|
|
1535
1626
|
onSlide: null,
|
|
1627
|
+
onBeforeSlide: null,
|
|
1536
1628
|
onScreenChange: null,
|
|
1537
1629
|
onPause: null,
|
|
1538
1630
|
onPlay: null,
|
package/gulpfile.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const babel = require('gulp-babel');
|
|
2
|
+
const browserify = require('browserify');
|
|
3
|
+
const concat = require('gulp-concat');
|
|
4
|
+
const connect = require('gulp-connect');
|
|
5
|
+
const gulp = require('gulp');
|
|
6
|
+
const livereload = require('gulp-livereload');
|
|
7
|
+
const rename = require('gulp-rename');
|
|
8
|
+
const sass = require('gulp-sass');
|
|
9
|
+
const uglify = require('gulp-uglify');
|
|
10
|
+
const cleanCSS = require('gulp-clean-css');
|
|
11
|
+
const source = require('vinyl-source-stream');
|
|
12
|
+
const buffer = require('vinyl-buffer');
|
|
13
|
+
const watchify = require('watchify');
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const babelOptions = {
|
|
16
16
|
plugins: ['transform-object-assign'],
|
|
17
|
-
presets: ['es2015', 'react', 'stage-0']
|
|
17
|
+
presets: ['es2015', 'react', 'stage-0'],
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
gulp.task('server',
|
|
20
|
+
gulp.task('server', () => {
|
|
21
21
|
connect.server({
|
|
22
22
|
host: '0.0.0.0',
|
|
23
23
|
root: ['example', 'build', 'styles'],
|
|
24
24
|
port: 8001,
|
|
25
|
-
livereload: true
|
|
25
|
+
livereload: true,
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
gulp.task('sass',
|
|
29
|
+
gulp.task('sass', () => {
|
|
30
30
|
gulp.src('./styles/scss/image-gallery.scss')
|
|
31
31
|
.pipe(sass())
|
|
32
32
|
.pipe(rename('image-gallery.css'))
|
|
@@ -34,26 +34,26 @@ gulp.task('sass', function () {
|
|
|
34
34
|
.pipe(livereload());
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
gulp.task('scripts',
|
|
37
|
+
gulp.task('scripts', () => {
|
|
38
38
|
watchify(browserify({
|
|
39
39
|
entries: './example/app.js',
|
|
40
40
|
extensions: ['.jsx'],
|
|
41
|
-
debug: true
|
|
41
|
+
debug: true,
|
|
42
42
|
}).transform('babelify', babelOptions))
|
|
43
43
|
.bundle()
|
|
44
|
-
.on('error', err =>
|
|
44
|
+
.on('error', (err) => console.error('error is', err))
|
|
45
45
|
.pipe(source('example.js'))
|
|
46
46
|
.pipe(buffer())
|
|
47
47
|
.pipe(gulp.dest('./example/'))
|
|
48
48
|
.pipe(livereload());
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
gulp.task('demo-src',
|
|
51
|
+
gulp.task('demo-src', () => {
|
|
52
52
|
process.env.NODE_ENV = 'production';
|
|
53
53
|
browserify({
|
|
54
54
|
entries: './example/app.js',
|
|
55
55
|
extensions: ['.jsx'],
|
|
56
|
-
debug: true
|
|
56
|
+
debug: true,
|
|
57
57
|
}).transform('babelify', babelOptions)
|
|
58
58
|
.bundle()
|
|
59
59
|
.pipe(source('demo.js'))
|
|
@@ -63,26 +63,25 @@ gulp.task('demo-src', function() {
|
|
|
63
63
|
|
|
64
64
|
gulp.src(['./styles/css/image-gallery.css', './example/app.css'])
|
|
65
65
|
.pipe(concat('demo.css'))
|
|
66
|
-
.pipe(cleanCSS({keepSpecialComments: false}))
|
|
66
|
+
.pipe(cleanCSS({ keepSpecialComments: false }))
|
|
67
67
|
.pipe(gulp.dest('./demo/'));
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
gulp.task('source-js',
|
|
71
|
-
|
|
70
|
+
gulp.task('source-js', () => (
|
|
71
|
+
gulp.src('./src/ImageGallery.jsx')
|
|
72
72
|
.pipe(concat('image-gallery.js'))
|
|
73
73
|
.pipe(babel(babelOptions))
|
|
74
|
-
.pipe(gulp.dest('./build'))
|
|
75
|
-
|
|
74
|
+
.pipe(gulp.dest('./build'))
|
|
75
|
+
));
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
gulp.
|
|
79
|
-
return gulp.src('./src/SVG.jsx')
|
|
77
|
+
gulp.task('svg-js', () => (
|
|
78
|
+
gulp.src('./src/SVG.jsx')
|
|
80
79
|
.pipe(concat('SVG.js'))
|
|
81
80
|
.pipe(babel(babelOptions))
|
|
82
|
-
.pipe(gulp.dest('./build'))
|
|
83
|
-
|
|
81
|
+
.pipe(gulp.dest('./build'))
|
|
82
|
+
));
|
|
84
83
|
|
|
85
|
-
gulp.task('watch',
|
|
84
|
+
gulp.task('watch', () => {
|
|
86
85
|
livereload.listen();
|
|
87
86
|
gulp.watch(['styles/**/*.scss'], ['sass']);
|
|
88
87
|
gulp.watch(['src/*.jsx', 'src/icons/*.jsx', 'example/app.js'], ['scripts']);
|
package/package.json
CHANGED
package/src/ImageGallery.jsx
CHANGED
|
@@ -42,6 +42,7 @@ export default class ImageGallery extends React.Component {
|
|
|
42
42
|
bulletOnClick: func,
|
|
43
43
|
description: string,
|
|
44
44
|
original: string.isRequired,
|
|
45
|
+
fullscreen: string,
|
|
45
46
|
originalAlt: string,
|
|
46
47
|
originalTitle: string,
|
|
47
48
|
thumbnail: string,
|
|
@@ -80,6 +81,7 @@ export default class ImageGallery extends React.Component {
|
|
|
80
81
|
swipeThreshold: number,
|
|
81
82
|
swipingTransitionDuration: number,
|
|
82
83
|
onSlide: func,
|
|
84
|
+
onBeforeSlide: func,
|
|
83
85
|
onScreenChange: func,
|
|
84
86
|
onPause: func,
|
|
85
87
|
onPlay: func,
|
|
@@ -133,6 +135,7 @@ export default class ImageGallery extends React.Component {
|
|
|
133
135
|
slideDuration: 450,
|
|
134
136
|
swipingTransitionDuration: 0,
|
|
135
137
|
onSlide: null,
|
|
138
|
+
onBeforeSlide: null,
|
|
136
139
|
onScreenChange: null,
|
|
137
140
|
onPause: null,
|
|
138
141
|
onPlay: null,
|
|
@@ -201,7 +204,7 @@ export default class ImageGallery extends React.Component {
|
|
|
201
204
|
this.state = {
|
|
202
205
|
currentIndex: props.startIndex,
|
|
203
206
|
thumbsTranslate: 0,
|
|
204
|
-
|
|
207
|
+
currentSlideOffset: 0,
|
|
205
208
|
galleryWidth: 0,
|
|
206
209
|
thumbnailsWrapperWidth: 0,
|
|
207
210
|
thumbnailsWrapperHeight: 0,
|
|
@@ -221,6 +224,7 @@ export default class ImageGallery extends React.Component {
|
|
|
221
224
|
this.handleScreenChange = this.handleScreenChange.bind(this);
|
|
222
225
|
this.handleSwiping = this.handleSwiping.bind(this);
|
|
223
226
|
this.onThumbnailMouseLeave = this.onThumbnailMouseLeave.bind(this);
|
|
227
|
+
this.handleImageError = this.handleImageError.bind(this);
|
|
224
228
|
this.pauseOrPlay = this.pauseOrPlay.bind(this);
|
|
225
229
|
this.renderThumbInner = this.renderThumbInner.bind(this);
|
|
226
230
|
this.renderItem = this.renderItem.bind(this);
|
|
@@ -258,12 +262,14 @@ export default class ImageGallery extends React.Component {
|
|
|
258
262
|
slideDuration,
|
|
259
263
|
startIndex,
|
|
260
264
|
thumbnailPosition,
|
|
265
|
+
showThumbnails,
|
|
261
266
|
} = this.props;
|
|
262
267
|
const { currentIndex } = this.state;
|
|
263
268
|
const itemsSizeChanged = prevProps.items.length !== items.length;
|
|
264
269
|
const itemsChanged = !isEqual(prevProps.items, items);
|
|
265
270
|
const startIndexUpdated = prevProps.startIndex !== startIndex;
|
|
266
271
|
const thumbnailsPositionChanged = prevProps.thumbnailPosition !== thumbnailPosition;
|
|
272
|
+
const showThumbnailsChanged = prevProps.showThumbnails !== showThumbnails;
|
|
267
273
|
|
|
268
274
|
if (thumbnailsPositionChanged) {
|
|
269
275
|
// re-initialize resizeObserver because slides was unmounted and mounted again
|
|
@@ -271,7 +277,7 @@ export default class ImageGallery extends React.Component {
|
|
|
271
277
|
this.initResizeObserver(this.imageGallerySlideWrapper);
|
|
272
278
|
}
|
|
273
279
|
|
|
274
|
-
if (itemsSizeChanged) {
|
|
280
|
+
if (itemsSizeChanged || showThumbnailsChanged) {
|
|
275
281
|
this.handleResize();
|
|
276
282
|
}
|
|
277
283
|
if (prevState.currentIndex !== currentIndex) {
|
|
@@ -299,9 +305,9 @@ export default class ImageGallery extends React.Component {
|
|
|
299
305
|
window.removeEventListener('mousedown', this.handleMouseDown);
|
|
300
306
|
this.removeScreenChangeEvent();
|
|
301
307
|
this.removeResizeObserver();
|
|
302
|
-
if (this.
|
|
303
|
-
window.clearInterval(this.
|
|
304
|
-
this.
|
|
308
|
+
if (this.playPauseIntervalId) {
|
|
309
|
+
window.clearInterval(this.playPauseIntervalId);
|
|
310
|
+
this.playPauseIntervalId = null;
|
|
305
311
|
}
|
|
306
312
|
if (this.transitionTimer) {
|
|
307
313
|
window.clearTimeout(this.transitionTimer);
|
|
@@ -442,37 +448,47 @@ export default class ImageGallery extends React.Component {
|
|
|
442
448
|
|
|
443
449
|
getTranslateXForTwoSlide(index) {
|
|
444
450
|
// For taking care of infinite swipe when there are only two slides
|
|
445
|
-
const { currentIndex,
|
|
451
|
+
const { currentIndex, currentSlideOffset, previousIndex } = this.state;
|
|
452
|
+
const indexChanged = currentIndex !== previousIndex;
|
|
453
|
+
const firstSlideWasPrevSlide = index === 0 && previousIndex === 0;
|
|
454
|
+
const secondSlideWasPrevSlide = index === 1 && previousIndex === 1;
|
|
455
|
+
const firstSlideIsNextSlide = index === 0 && currentIndex === 1;
|
|
456
|
+
const secondSlideIsNextSlide = index === 1 && currentIndex === 0;
|
|
457
|
+
const swipingEnded = currentSlideOffset === 0;
|
|
446
458
|
const baseTranslateX = -100 * currentIndex;
|
|
447
|
-
let translateX = baseTranslateX + (index * 100) +
|
|
459
|
+
let translateX = baseTranslateX + (index * 100) + currentSlideOffset;
|
|
448
460
|
|
|
449
461
|
// keep track of user swiping direction
|
|
450
|
-
|
|
462
|
+
// important to understand how to translateX based on last direction
|
|
463
|
+
if (currentSlideOffset > 0) {
|
|
451
464
|
this.direction = 'left';
|
|
452
|
-
} else if (
|
|
465
|
+
} else if (currentSlideOffset < 0) {
|
|
453
466
|
this.direction = 'right';
|
|
454
467
|
}
|
|
455
468
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
translateX = 100 +
|
|
469
|
+
|
|
470
|
+
// when swiping between two slides make sure the next and prev slides
|
|
471
|
+
// are on both left and right
|
|
472
|
+
if (secondSlideIsNextSlide && currentSlideOffset > 0) { // swiping right
|
|
473
|
+
translateX = -100 + currentSlideOffset;
|
|
474
|
+
}
|
|
475
|
+
if (firstSlideIsNextSlide && currentSlideOffset < 0) { // swiping left
|
|
476
|
+
translateX = 100 + currentSlideOffset;
|
|
461
477
|
}
|
|
462
478
|
|
|
463
|
-
if (
|
|
464
|
-
// when
|
|
465
|
-
if (
|
|
479
|
+
if (indexChanged) {
|
|
480
|
+
// when indexChanged move the slide to the correct side
|
|
481
|
+
if (firstSlideWasPrevSlide && swipingEnded && this.direction === 'left') {
|
|
466
482
|
translateX = 100;
|
|
467
|
-
} else if (
|
|
483
|
+
} else if (secondSlideWasPrevSlide && swipingEnded && this.direction === 'right') {
|
|
468
484
|
translateX = -100;
|
|
469
485
|
}
|
|
470
486
|
} else {
|
|
471
|
-
// keep the slide on the correct
|
|
472
|
-
if (
|
|
487
|
+
// keep the slide on the correct side if the swipe was not successful
|
|
488
|
+
if (secondSlideIsNextSlide && swipingEnded && this.direction === 'left') {
|
|
473
489
|
translateX = -100;
|
|
474
490
|
}
|
|
475
|
-
if (
|
|
491
|
+
if (firstSlideIsNextSlide && swipingEnded && this.direction === 'right') {
|
|
476
492
|
translateX = 100;
|
|
477
493
|
}
|
|
478
494
|
}
|
|
@@ -489,7 +505,7 @@ export default class ImageGallery extends React.Component {
|
|
|
489
505
|
}
|
|
490
506
|
|
|
491
507
|
getSlideStyle(index) {
|
|
492
|
-
const { currentIndex,
|
|
508
|
+
const { currentIndex, currentSlideOffset, slideStyle } = this.state;
|
|
493
509
|
const {
|
|
494
510
|
infinite,
|
|
495
511
|
items,
|
|
@@ -501,17 +517,17 @@ export default class ImageGallery extends React.Component {
|
|
|
501
517
|
|
|
502
518
|
// calculates where the other slides belong based on currentIndex
|
|
503
519
|
// if it is RTL the base line should be reversed
|
|
504
|
-
let translateX = (baseTranslateX + (index * 100)) * (isRTL ? -1 : 1) +
|
|
520
|
+
let translateX = (baseTranslateX + (index * 100)) * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
505
521
|
|
|
506
522
|
if (infinite && items.length > 2) {
|
|
507
523
|
if (currentIndex === 0 && index === totalSlides) {
|
|
508
524
|
// make the last slide the slide before the first
|
|
509
525
|
// if it is RTL the base line should be reversed
|
|
510
|
-
translateX = -100 * (isRTL ? -1 : 1) +
|
|
526
|
+
translateX = -100 * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
511
527
|
} else if (currentIndex === totalSlides && index === 0) {
|
|
512
528
|
// make the first slide the slide after the last
|
|
513
529
|
// if it is RTL the base line should be reversed
|
|
514
|
-
translateX = 100 * (isRTL ? -1 : 1) +
|
|
530
|
+
translateX = 100 * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
515
531
|
}
|
|
516
532
|
}
|
|
517
533
|
|
|
@@ -566,10 +582,11 @@ export default class ImageGallery extends React.Component {
|
|
|
566
582
|
};
|
|
567
583
|
}
|
|
568
584
|
|
|
569
|
-
getSlideItems(
|
|
585
|
+
getSlideItems() {
|
|
570
586
|
const { currentIndex } = this.state;
|
|
571
587
|
const {
|
|
572
588
|
infinite,
|
|
589
|
+
items,
|
|
573
590
|
slideOnThumbnailOver,
|
|
574
591
|
onClick,
|
|
575
592
|
lazyLoad,
|
|
@@ -605,7 +622,7 @@ export default class ImageGallery extends React.Component {
|
|
|
605
622
|
|
|
606
623
|
const slide = (
|
|
607
624
|
<div
|
|
608
|
-
key={`slide-${item.original}`}
|
|
625
|
+
key={`slide-${item.original}-${index}`}
|
|
609
626
|
tabIndex="-1"
|
|
610
627
|
className={`image-gallery-slide ${alignment} ${originalClass}`}
|
|
611
628
|
style={slideStyle}
|
|
@@ -640,7 +657,7 @@ export default class ImageGallery extends React.Component {
|
|
|
640
657
|
);
|
|
641
658
|
thumbnails.push(
|
|
642
659
|
<button
|
|
643
|
-
key={`thumbnail-${item.original}`}
|
|
660
|
+
key={`thumbnail-${item.original}-${index}`}
|
|
644
661
|
type="button"
|
|
645
662
|
tabIndex="0"
|
|
646
663
|
aria-pressed={currentIndex === index ? 'true' : 'false'}
|
|
@@ -673,7 +690,7 @@ export default class ImageGallery extends React.Component {
|
|
|
673
690
|
bullets.push(
|
|
674
691
|
<button
|
|
675
692
|
type="button"
|
|
676
|
-
key={`bullet-${item.original}`}
|
|
693
|
+
key={`bullet-${item.original}-${index}`}
|
|
677
694
|
className={igBulletClass}
|
|
678
695
|
onClick={bulletOnClick}
|
|
679
696
|
aria-pressed={currentIndex === index ? 'true' : 'false'}
|
|
@@ -806,9 +823,9 @@ export default class ImageGallery extends React.Component {
|
|
|
806
823
|
if (!isTransitioning && !scrollingUpDown) {
|
|
807
824
|
const side = dir === RIGHT ? 1 : -1;
|
|
808
825
|
|
|
809
|
-
let
|
|
810
|
-
if (Math.abs(
|
|
811
|
-
|
|
826
|
+
let currentSlideOffset = (absX / galleryWidth * 100);
|
|
827
|
+
if (Math.abs(currentSlideOffset) >= 100) {
|
|
828
|
+
currentSlideOffset = 100;
|
|
812
829
|
}
|
|
813
830
|
|
|
814
831
|
const swipingTransition = {
|
|
@@ -816,19 +833,19 @@ export default class ImageGallery extends React.Component {
|
|
|
816
833
|
};
|
|
817
834
|
|
|
818
835
|
this.setState({
|
|
819
|
-
|
|
836
|
+
currentSlideOffset: side * currentSlideOffset,
|
|
820
837
|
slideStyle: swipingTransition,
|
|
821
838
|
});
|
|
822
839
|
} else {
|
|
823
840
|
// don't move the slide
|
|
824
|
-
this.setState({
|
|
841
|
+
this.setState({ currentSlideOffset: 0 });
|
|
825
842
|
}
|
|
826
843
|
}
|
|
827
844
|
|
|
828
845
|
sufficientSwipe() {
|
|
829
|
-
const {
|
|
846
|
+
const { currentSlideOffset } = this.state;
|
|
830
847
|
const { swipeThreshold } = this.props;
|
|
831
|
-
return Math.abs(
|
|
848
|
+
return Math.abs(currentSlideOffset) > swipeThreshold;
|
|
832
849
|
}
|
|
833
850
|
|
|
834
851
|
handleOnSwiped({ event, dir, velocity }) {
|
|
@@ -894,13 +911,13 @@ export default class ImageGallery extends React.Component {
|
|
|
894
911
|
|
|
895
912
|
switch (key) {
|
|
896
913
|
case LEFT_ARROW:
|
|
897
|
-
if (this.canSlideLeft() && !this.
|
|
898
|
-
this.slideLeft();
|
|
914
|
+
if (this.canSlideLeft() && !this.playPauseIntervalId) {
|
|
915
|
+
this.slideLeft(event);
|
|
899
916
|
}
|
|
900
917
|
break;
|
|
901
918
|
case RIGHT_ARROW:
|
|
902
|
-
if (this.canSlideRight() && !this.
|
|
903
|
-
this.slideRight();
|
|
919
|
+
if (this.canSlideRight() && !this.playPauseIntervalId) {
|
|
920
|
+
this.slideRight(event);
|
|
904
921
|
}
|
|
905
922
|
break;
|
|
906
923
|
case ESC_KEY:
|
|
@@ -973,7 +990,7 @@ export default class ImageGallery extends React.Component {
|
|
|
973
990
|
}
|
|
974
991
|
|
|
975
992
|
togglePlay() {
|
|
976
|
-
if (this.
|
|
993
|
+
if (this.playPauseIntervalId) {
|
|
977
994
|
this.pause();
|
|
978
995
|
} else {
|
|
979
996
|
this.play();
|
|
@@ -985,23 +1002,25 @@ export default class ImageGallery extends React.Component {
|
|
|
985
1002
|
/*
|
|
986
1003
|
handles screen change events that the browser triggers e.g. esc key
|
|
987
1004
|
*/
|
|
988
|
-
const { onScreenChange } = this.props;
|
|
1005
|
+
const { onScreenChange, useBrowserFullscreen } = this.props;
|
|
989
1006
|
const fullScreenElement = document.fullscreenElement
|
|
990
1007
|
|| document.msFullscreenElement
|
|
991
1008
|
|| document.mozFullScreenElement
|
|
992
1009
|
|| document.webkitFullscreenElement;
|
|
993
1010
|
|
|
994
|
-
if
|
|
995
|
-
this.
|
|
1011
|
+
// check if screenchange element is the gallery
|
|
1012
|
+
const isFullscreen = this.imageGallery.current === fullScreenElement;
|
|
1013
|
+
if (onScreenChange) onScreenChange(isFullscreen);
|
|
1014
|
+
if (useBrowserFullscreen) this.setState({ isFullscreen });
|
|
996
1015
|
}
|
|
997
1016
|
|
|
998
1017
|
slideToIndex(index, event) {
|
|
999
1018
|
const { currentIndex, isTransitioning } = this.state;
|
|
1000
|
-
const { items, slideDuration } = this.props;
|
|
1019
|
+
const { items, slideDuration, onBeforeSlide } = this.props;
|
|
1001
1020
|
|
|
1002
1021
|
if (!isTransitioning) {
|
|
1003
1022
|
if (event) {
|
|
1004
|
-
if (this.
|
|
1023
|
+
if (this.playPauseIntervalId) {
|
|
1005
1024
|
// user triggered event while ImageGallery is playing, reset interval
|
|
1006
1025
|
this.pause(false);
|
|
1007
1026
|
this.play(false);
|
|
@@ -1016,42 +1035,80 @@ export default class ImageGallery extends React.Component {
|
|
|
1016
1035
|
nextIndex = 0;
|
|
1017
1036
|
}
|
|
1018
1037
|
|
|
1038
|
+
if (onBeforeSlide && nextIndex !== currentIndex) {
|
|
1039
|
+
onBeforeSlide(nextIndex);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1019
1042
|
this.setState({
|
|
1020
1043
|
previousIndex: currentIndex,
|
|
1021
1044
|
currentIndex: nextIndex,
|
|
1022
1045
|
isTransitioning: nextIndex !== currentIndex,
|
|
1023
|
-
|
|
1046
|
+
currentSlideOffset: 0,
|
|
1024
1047
|
slideStyle: { transition: `all ${slideDuration}ms ease-out` },
|
|
1025
1048
|
}, this.onSliding);
|
|
1026
1049
|
}
|
|
1027
1050
|
}
|
|
1028
1051
|
|
|
1029
|
-
slideLeft() {
|
|
1052
|
+
slideLeft(event) {
|
|
1030
1053
|
const { isRTL } = this.props;
|
|
1031
1054
|
if (isRTL) {
|
|
1032
|
-
this.slideNext();
|
|
1055
|
+
this.slideNext(event);
|
|
1033
1056
|
} else {
|
|
1034
|
-
this.slidePrevious();
|
|
1057
|
+
this.slidePrevious(event);
|
|
1035
1058
|
}
|
|
1036
1059
|
}
|
|
1037
1060
|
|
|
1038
|
-
slideRight() {
|
|
1061
|
+
slideRight(event) {
|
|
1039
1062
|
const { isRTL } = this.props;
|
|
1040
1063
|
if (isRTL) {
|
|
1041
|
-
this.slidePrevious();
|
|
1064
|
+
this.slidePrevious(event);
|
|
1042
1065
|
} else {
|
|
1043
|
-
this.slideNext();
|
|
1066
|
+
this.slideNext(event);
|
|
1044
1067
|
}
|
|
1045
1068
|
}
|
|
1046
1069
|
|
|
1047
1070
|
slidePrevious(event) {
|
|
1048
|
-
const { currentIndex } = this.state;
|
|
1049
|
-
|
|
1071
|
+
const { currentIndex, currentSlideOffset, isTransitioning } = this.state;
|
|
1072
|
+
const { items } = this.props;
|
|
1073
|
+
const nextIndex = currentIndex - 1;
|
|
1074
|
+
|
|
1075
|
+
if (isTransitioning) return;
|
|
1076
|
+
|
|
1077
|
+
if (items.length === 2) {
|
|
1078
|
+
/*
|
|
1079
|
+
When there are only 2 slides fake a tiny swipe to get the slides
|
|
1080
|
+
on the correct side for transitioning
|
|
1081
|
+
*/
|
|
1082
|
+
this.setState({
|
|
1083
|
+
currentSlideOffset: currentSlideOffset + 0.001, // this will reset once index changes
|
|
1084
|
+
slideStyle: { transition: 'none' }, // move the slide over instantly
|
|
1085
|
+
}, () => {
|
|
1086
|
+
// add 25ms timeout to avoid delay in moving slides over
|
|
1087
|
+
window.setTimeout(() => this.slideToIndex(nextIndex, event), 25);
|
|
1088
|
+
});
|
|
1089
|
+
} else {
|
|
1090
|
+
this.slideToIndex(nextIndex, event);
|
|
1091
|
+
}
|
|
1050
1092
|
}
|
|
1051
1093
|
|
|
1052
1094
|
slideNext(event) {
|
|
1053
|
-
const { currentIndex } = this.state;
|
|
1054
|
-
|
|
1095
|
+
const { currentIndex, currentSlideOffset, isTransitioning } = this.state;
|
|
1096
|
+
const { items } = this.props;
|
|
1097
|
+
const nextIndex = currentIndex + 1;
|
|
1098
|
+
|
|
1099
|
+
if (isTransitioning) return;
|
|
1100
|
+
|
|
1101
|
+
if (items.length === 2) {
|
|
1102
|
+
// same as above for 2 slides
|
|
1103
|
+
this.setState({
|
|
1104
|
+
currentSlideOffset: currentSlideOffset - 0.001,
|
|
1105
|
+
slideStyle: { transition: 'none' },
|
|
1106
|
+
}, () => {
|
|
1107
|
+
window.setTimeout(() => this.slideToIndex(nextIndex, event), 25);
|
|
1108
|
+
});
|
|
1109
|
+
} else {
|
|
1110
|
+
this.slideToIndex(nextIndex, event);
|
|
1111
|
+
}
|
|
1055
1112
|
}
|
|
1056
1113
|
|
|
1057
1114
|
handleThumbnailMouseOver(event, index) {
|
|
@@ -1152,9 +1209,9 @@ export default class ImageGallery extends React.Component {
|
|
|
1152
1209
|
slideDuration,
|
|
1153
1210
|
} = this.props;
|
|
1154
1211
|
const { currentIndex } = this.state;
|
|
1155
|
-
if (!this.
|
|
1212
|
+
if (!this.playPauseIntervalId) {
|
|
1156
1213
|
this.setState({ isPlaying: true });
|
|
1157
|
-
this.
|
|
1214
|
+
this.playPauseIntervalId = window.setInterval(
|
|
1158
1215
|
this.pauseOrPlay,
|
|
1159
1216
|
Math.max(slideInterval, slideDuration),
|
|
1160
1217
|
);
|
|
@@ -1167,9 +1224,9 @@ export default class ImageGallery extends React.Component {
|
|
|
1167
1224
|
pause(shouldCallOnPause = true) {
|
|
1168
1225
|
const { onPause } = this.props;
|
|
1169
1226
|
const { currentIndex } = this.state;
|
|
1170
|
-
if (this.
|
|
1171
|
-
window.clearInterval(this.
|
|
1172
|
-
this.
|
|
1227
|
+
if (this.playPauseIntervalId) {
|
|
1228
|
+
window.clearInterval(this.playPauseIntervalId);
|
|
1229
|
+
this.playPauseIntervalId = null;
|
|
1173
1230
|
this.setState({ isPlaying: false });
|
|
1174
1231
|
if (onPause && shouldCallOnPause) {
|
|
1175
1232
|
onPause(currentIndex);
|
|
@@ -1191,22 +1248,34 @@ export default class ImageGallery extends React.Component {
|
|
|
1191
1248
|
return false;
|
|
1192
1249
|
}
|
|
1193
1250
|
|
|
1251
|
+
handleImageLoaded(event, item) {
|
|
1252
|
+
const { onImageLoad } = this.props;
|
|
1253
|
+
const imageExists = this.loadedImages[item.original];
|
|
1254
|
+
if (!imageExists && onImageLoad) {
|
|
1255
|
+
this.loadedImages[item.original] = true; // prevent from call again
|
|
1256
|
+
// image just loaded, call onImageLoad
|
|
1257
|
+
onImageLoad(event);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1194
1261
|
renderItem(item) {
|
|
1195
|
-
const {
|
|
1262
|
+
const { isFullscreen } = this.state;
|
|
1263
|
+
const { onImageError } = this.props;
|
|
1196
1264
|
const handleImageError = onImageError || this.handleImageError;
|
|
1265
|
+
const itemSrc = isFullscreen ? (item.fullscreen || item.original) : item.original;
|
|
1197
1266
|
|
|
1198
1267
|
return (
|
|
1199
1268
|
<div>
|
|
1200
1269
|
{
|
|
1201
1270
|
item.imageSet ? (
|
|
1202
1271
|
<picture
|
|
1203
|
-
onLoad={
|
|
1272
|
+
onLoad={event => this.handleImageLoaded(event, item)}
|
|
1204
1273
|
onError={handleImageError}
|
|
1205
1274
|
>
|
|
1206
1275
|
{
|
|
1207
|
-
item.imageSet.map(source => (
|
|
1276
|
+
item.imageSet.map((source, index) => (
|
|
1208
1277
|
<source
|
|
1209
|
-
key={source.
|
|
1278
|
+
key={`media-${source.srcSet}-${index}`}
|
|
1210
1279
|
media={source.media}
|
|
1211
1280
|
srcSet={source.srcSet}
|
|
1212
1281
|
type={source.type}
|
|
@@ -1216,18 +1285,18 @@ export default class ImageGallery extends React.Component {
|
|
|
1216
1285
|
<img
|
|
1217
1286
|
className="image-gallery-image"
|
|
1218
1287
|
alt={item.originalAlt}
|
|
1219
|
-
src={
|
|
1288
|
+
src={itemSrc}
|
|
1220
1289
|
/>
|
|
1221
1290
|
</picture>
|
|
1222
1291
|
) : (
|
|
1223
1292
|
<img
|
|
1224
1293
|
className="image-gallery-image"
|
|
1225
|
-
src={
|
|
1294
|
+
src={itemSrc}
|
|
1226
1295
|
alt={item.originalAlt}
|
|
1227
1296
|
srcSet={item.srcSet}
|
|
1228
1297
|
sizes={item.sizes}
|
|
1229
1298
|
title={item.originalTitle}
|
|
1230
|
-
onLoad={
|
|
1299
|
+
onLoad={event => this.handleImageLoaded(event, item)}
|
|
1231
1300
|
onError={handleImageError}
|
|
1232
1301
|
/>
|
|
1233
1302
|
)
|
|
@@ -1296,7 +1365,7 @@ export default class ImageGallery extends React.Component {
|
|
|
1296
1365
|
} = this.props;
|
|
1297
1366
|
|
|
1298
1367
|
const thumbnailStyle = this.getThumbnailStyle();
|
|
1299
|
-
const { slides, thumbnails, bullets } = this.getSlideItems(
|
|
1368
|
+
const { slides, thumbnails, bullets } = this.getSlideItems();
|
|
1300
1369
|
const slideWrapperClass = clsx(
|
|
1301
1370
|
'image-gallery-slide-wrapper',
|
|
1302
1371
|
thumbnailPosition,
|
|
@@ -1311,15 +1380,14 @@ export default class ImageGallery extends React.Component {
|
|
|
1311
1380
|
<React.Fragment>
|
|
1312
1381
|
{
|
|
1313
1382
|
showNav && (
|
|
1314
|
-
<
|
|
1383
|
+
<React.Fragment>
|
|
1315
1384
|
{renderLeftNav(this.slideLeft, !this.canSlideLeft())}
|
|
1316
1385
|
{renderRightNav(this.slideRight, !this.canSlideRight())}
|
|
1317
|
-
</
|
|
1386
|
+
</React.Fragment>
|
|
1318
1387
|
)
|
|
1319
1388
|
}
|
|
1320
1389
|
<Swipeable
|
|
1321
1390
|
className="image-gallery-swipe"
|
|
1322
|
-
key="swipeable"
|
|
1323
1391
|
delta={0}
|
|
1324
1392
|
onSwiping={this.handleSwiping}
|
|
1325
1393
|
onSwiped={this.handleOnSwiped}
|