react-image-gallery 1.3.0 → 1.4.0
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/LICENSE +1 -1
- package/README.md +9 -4
- package/build/image-gallery.es.js +1 -0
- package/build/image-gallery.umd.js +1 -0
- package/package.json +62 -44
- package/styles/css/image-gallery.css +1 -1
- package/styles/scss/image-gallery.scss +81 -20
- package/.eslintrc.json +0 -47
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -39
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/workflows/eslint.yml +0 -56
- package/babel.config.js +0 -3
- package/jest.config.js +0 -16
- package/src/components/ImageGallery.jsx +0 -1727
- package/src/components/ImageGallery.test.jsx +0 -22
- package/src/components/Item.jsx +0 -75
- package/src/components/SVG.jsx +0 -59
- package/src/components/SwipeWrapper.jsx +0 -37
- package/src/components/controls/Fullscreen.jsx +0 -25
- package/src/components/controls/LeftNav.jsx +0 -26
- package/src/components/controls/PlayPause.jsx +0 -25
- package/src/components/controls/RightNav.jsx +0 -26
- package/webpack.build.js +0 -171
- package/webpack.config.js +0 -52
|
@@ -1,1727 +0,0 @@
|
|
|
1
|
-
import clsx from "clsx";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import throttle from "lodash-es/throttle";
|
|
4
|
-
import debounce from "lodash-es/debounce";
|
|
5
|
-
import isEqual from "react-fast-compare";
|
|
6
|
-
import ResizeObserver from "resize-observer-polyfill";
|
|
7
|
-
import { LEFT, RIGHT, UP, DOWN } from "react-swipeable";
|
|
8
|
-
import { arrayOf, bool, func, number, oneOf, shape, string } from "prop-types";
|
|
9
|
-
import Item from "src/components/Item";
|
|
10
|
-
import Fullscreen from "src/components/controls/Fullscreen";
|
|
11
|
-
import LeftNav from "src/components/controls/LeftNav";
|
|
12
|
-
import RightNav from "src/components/controls/RightNav";
|
|
13
|
-
import PlayPause from "src/components/controls/PlayPause";
|
|
14
|
-
import SwipeWrapper from "src/components/SwipeWrapper";
|
|
15
|
-
|
|
16
|
-
const screenChangeEvents = [
|
|
17
|
-
"fullscreenchange",
|
|
18
|
-
"MSFullscreenChange",
|
|
19
|
-
"mozfullscreenchange",
|
|
20
|
-
"webkitfullscreenchange",
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
const imageSetType = arrayOf(
|
|
24
|
-
shape({
|
|
25
|
-
srcSet: string,
|
|
26
|
-
media: string,
|
|
27
|
-
})
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
function isEnterOrSpaceKey(event) {
|
|
31
|
-
const key = parseInt(event.keyCode || event.which || 0, 10);
|
|
32
|
-
const ENTER_KEY_CODE = 66;
|
|
33
|
-
const SPACEBAR_KEY_CODE = 62;
|
|
34
|
-
return key === ENTER_KEY_CODE || key === SPACEBAR_KEY_CODE;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
class ImageGallery extends React.Component {
|
|
38
|
-
constructor(props) {
|
|
39
|
-
super(props);
|
|
40
|
-
this.state = {
|
|
41
|
-
currentIndex: props.startIndex,
|
|
42
|
-
thumbsTranslate: 0,
|
|
43
|
-
thumbsSwipedTranslate: 0,
|
|
44
|
-
currentSlideOffset: 0,
|
|
45
|
-
galleryWidth: 0,
|
|
46
|
-
thumbnailsWrapperWidth: 0,
|
|
47
|
-
thumbnailsWrapperHeight: 0,
|
|
48
|
-
thumbsStyle: { transition: `all ${props.slideDuration}ms ease-out` },
|
|
49
|
-
isFullscreen: false,
|
|
50
|
-
isSwipingThumbnail: false,
|
|
51
|
-
isPlaying: false,
|
|
52
|
-
};
|
|
53
|
-
this.loadedImages = {};
|
|
54
|
-
this.imageGallery = React.createRef();
|
|
55
|
-
this.thumbnailsWrapper = React.createRef();
|
|
56
|
-
this.thumbnails = React.createRef();
|
|
57
|
-
this.imageGallerySlideWrapper = React.createRef();
|
|
58
|
-
|
|
59
|
-
// bindings
|
|
60
|
-
this.handleImageLoaded = this.handleImageLoaded.bind(this);
|
|
61
|
-
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
62
|
-
this.handleMouseDown = this.handleMouseDown.bind(this);
|
|
63
|
-
this.handleResize = this.handleResize.bind(this);
|
|
64
|
-
this.handleTouchMove = this.handleTouchMove.bind(this);
|
|
65
|
-
this.handleOnSwiped = this.handleOnSwiped.bind(this);
|
|
66
|
-
this.handleScreenChange = this.handleScreenChange.bind(this);
|
|
67
|
-
this.handleSwiping = this.handleSwiping.bind(this);
|
|
68
|
-
this.handleThumbnailSwiping = this.handleThumbnailSwiping.bind(this);
|
|
69
|
-
this.handleOnThumbnailSwiped = this.handleOnThumbnailSwiped.bind(this);
|
|
70
|
-
this.onThumbnailMouseLeave = this.onThumbnailMouseLeave.bind(this);
|
|
71
|
-
this.handleImageError = this.handleImageError.bind(this);
|
|
72
|
-
this.pauseOrPlay = this.pauseOrPlay.bind(this);
|
|
73
|
-
this.renderThumbInner = this.renderThumbInner.bind(this);
|
|
74
|
-
this.renderItem = this.renderItem.bind(this);
|
|
75
|
-
this.slideLeft = this.slideLeft.bind(this);
|
|
76
|
-
this.slideRight = this.slideRight.bind(this);
|
|
77
|
-
this.toggleFullScreen = this.toggleFullScreen.bind(this);
|
|
78
|
-
this.togglePlay = this.togglePlay.bind(this);
|
|
79
|
-
|
|
80
|
-
// Used to update the throttle if slideDuration changes
|
|
81
|
-
this.unthrottledSlideToIndex = this.slideToIndex;
|
|
82
|
-
this.slideToIndex = throttle(
|
|
83
|
-
this.unthrottledSlideToIndex,
|
|
84
|
-
props.slideDuration,
|
|
85
|
-
{ trailing: false }
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
if (props.lazyLoad) {
|
|
89
|
-
this.lazyLoaded = [];
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
componentDidMount() {
|
|
94
|
-
const { autoPlay, useWindowKeyDown } = this.props;
|
|
95
|
-
if (autoPlay) {
|
|
96
|
-
this.play();
|
|
97
|
-
}
|
|
98
|
-
if (useWindowKeyDown) {
|
|
99
|
-
window.addEventListener("keydown", this.handleKeyDown);
|
|
100
|
-
} else {
|
|
101
|
-
this.imageGallery.current.addEventListener("keydown", this.handleKeyDown);
|
|
102
|
-
}
|
|
103
|
-
window.addEventListener("mousedown", this.handleMouseDown);
|
|
104
|
-
window.addEventListener("touchmove", this.handleTouchMove, {
|
|
105
|
-
passive: false,
|
|
106
|
-
});
|
|
107
|
-
// we're using resize observer to help with detecting containers size changes as images load
|
|
108
|
-
this.initSlideWrapperResizeObserver(this.imageGallerySlideWrapper);
|
|
109
|
-
this.initThumbnailWrapperResizeObserver(this.thumbnailsWrapper);
|
|
110
|
-
this.addScreenChangeEvent();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
componentDidUpdate(prevProps, prevState) {
|
|
114
|
-
const {
|
|
115
|
-
items,
|
|
116
|
-
lazyLoad,
|
|
117
|
-
slideDuration,
|
|
118
|
-
slideInterval,
|
|
119
|
-
startIndex,
|
|
120
|
-
thumbnailPosition,
|
|
121
|
-
showThumbnails,
|
|
122
|
-
useWindowKeyDown,
|
|
123
|
-
} = this.props;
|
|
124
|
-
const { currentIndex, isPlaying } = this.state;
|
|
125
|
-
const itemsSizeChanged = prevProps.items.length !== items.length;
|
|
126
|
-
const itemsChanged = !isEqual(prevProps.items, items);
|
|
127
|
-
const startIndexUpdated = prevProps.startIndex !== startIndex;
|
|
128
|
-
const thumbnailsPositionChanged =
|
|
129
|
-
prevProps.thumbnailPosition !== thumbnailPosition;
|
|
130
|
-
const showThumbnailsChanged = prevProps.showThumbnails !== showThumbnails;
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
slideInterval !== prevProps.slideInterval ||
|
|
134
|
-
slideDuration !== prevProps.slideDuration
|
|
135
|
-
) {
|
|
136
|
-
// refresh setInterval
|
|
137
|
-
if (isPlaying) {
|
|
138
|
-
this.pause();
|
|
139
|
-
this.play();
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (thumbnailsPositionChanged) {
|
|
144
|
-
// re-initialize resizeObserver because element was unmounted and mounted again
|
|
145
|
-
this.removeResizeObserver();
|
|
146
|
-
this.initSlideWrapperResizeObserver(this.imageGallerySlideWrapper);
|
|
147
|
-
this.initThumbnailWrapperResizeObserver(this.thumbnailsWrapper);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// re-inititalize if thumbnails are shown again
|
|
151
|
-
if (showThumbnailsChanged && showThumbnails) {
|
|
152
|
-
this.initThumbnailWrapperResizeObserver(this.thumbnailsWrapper);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// remove thumbnails resize observer if not showing thumbnails
|
|
156
|
-
if (showThumbnailsChanged && !showThumbnails) {
|
|
157
|
-
this.removeThumbnailsResizeObserver();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (itemsSizeChanged || showThumbnailsChanged) {
|
|
161
|
-
this.handleResize();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (prevState.currentIndex !== currentIndex) {
|
|
165
|
-
this.slideThumbnailBar();
|
|
166
|
-
}
|
|
167
|
-
// if slideDuration changes, update slideToIndex throttle
|
|
168
|
-
if (prevProps.slideDuration !== slideDuration) {
|
|
169
|
-
this.slideToIndex = throttle(
|
|
170
|
-
this.unthrottledSlideToIndex,
|
|
171
|
-
slideDuration,
|
|
172
|
-
{ trailing: false }
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
if (lazyLoad && (!prevProps.lazyLoad || itemsChanged)) {
|
|
176
|
-
this.lazyLoaded = [];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (useWindowKeyDown !== prevProps.useWindowKeyDown) {
|
|
180
|
-
if (useWindowKeyDown) {
|
|
181
|
-
this.imageGallery.current.removeEventListener(
|
|
182
|
-
"keydown",
|
|
183
|
-
this.handleKeyDown
|
|
184
|
-
);
|
|
185
|
-
window.addEventListener("keydown", this.handleKeyDown);
|
|
186
|
-
} else {
|
|
187
|
-
window.removeEventListener("keydown", this.handleKeyDown);
|
|
188
|
-
this.imageGallery.current.addEventListener(
|
|
189
|
-
"keydown",
|
|
190
|
-
this.handleKeyDown
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (startIndexUpdated || itemsChanged) {
|
|
196
|
-
// reset to start index if new items are added
|
|
197
|
-
// do not transition when new items are added
|
|
198
|
-
this.setState({
|
|
199
|
-
currentIndex: startIndex,
|
|
200
|
-
slideStyle: { transition: "none" },
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
componentWillUnmount() {
|
|
206
|
-
const { useWindowKeyDown } = this.props;
|
|
207
|
-
window.removeEventListener("mousedown", this.handleMouseDown);
|
|
208
|
-
window.removeEventListener("touchmove", this.handleTouchMove);
|
|
209
|
-
this.removeScreenChangeEvent();
|
|
210
|
-
this.removeResizeObserver();
|
|
211
|
-
if (this.playPauseIntervalId) {
|
|
212
|
-
window.clearInterval(this.playPauseIntervalId);
|
|
213
|
-
this.playPauseIntervalId = null;
|
|
214
|
-
}
|
|
215
|
-
if (this.transitionTimer) {
|
|
216
|
-
window.clearTimeout(this.transitionTimer);
|
|
217
|
-
}
|
|
218
|
-
if (useWindowKeyDown) {
|
|
219
|
-
window.removeEventListener("keydown", this.handleKeyDown);
|
|
220
|
-
} else {
|
|
221
|
-
this.imageGallery.current.removeEventListener(
|
|
222
|
-
"keydown",
|
|
223
|
-
this.handleKeyDown
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
onSliding() {
|
|
229
|
-
const { currentIndex, isTransitioning } = this.state;
|
|
230
|
-
const { onSlide, slideDuration } = this.props;
|
|
231
|
-
this.transitionTimer = window.setTimeout(() => {
|
|
232
|
-
if (isTransitioning) {
|
|
233
|
-
this.setState({
|
|
234
|
-
isTransitioning: !isTransitioning,
|
|
235
|
-
// reset swiping thumbnail after transitioning to new slide,
|
|
236
|
-
// so we can resume thumbnail auto translate
|
|
237
|
-
isSwipingThumbnail: false,
|
|
238
|
-
});
|
|
239
|
-
if (onSlide) {
|
|
240
|
-
onSlide(currentIndex);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}, slideDuration + 50);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
onThumbnailClick(event, index) {
|
|
247
|
-
const { onThumbnailClick, items } = this.props;
|
|
248
|
-
const { currentIndex } = this.state;
|
|
249
|
-
// blur element to remove outline cause by focus
|
|
250
|
-
event.target.parentNode.parentNode.blur();
|
|
251
|
-
if (currentIndex !== index) {
|
|
252
|
-
if (items.length === 2) {
|
|
253
|
-
this.slideToIndexWithStyleReset(index, event);
|
|
254
|
-
} else {
|
|
255
|
-
this.slideToIndex(index, event);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (onThumbnailClick) {
|
|
259
|
-
onThumbnailClick(event, index);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
onBulletClick = (event, index) => {
|
|
264
|
-
const { onBulletClick, items } = this.props;
|
|
265
|
-
const { currentIndex } = this.state;
|
|
266
|
-
// blur element to remove outline caused by focus
|
|
267
|
-
event.target.blur();
|
|
268
|
-
if (currentIndex !== index) {
|
|
269
|
-
if (items.length === 2) {
|
|
270
|
-
this.slideToIndexWithStyleReset(index, event);
|
|
271
|
-
} else {
|
|
272
|
-
this.slideToIndex(index, event);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (onBulletClick) {
|
|
276
|
-
onBulletClick(event, index);
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
onThumbnailMouseOver(event, index) {
|
|
281
|
-
if (this.thumbnailMouseOverTimer) {
|
|
282
|
-
window.clearTimeout(this.thumbnailMouseOverTimer);
|
|
283
|
-
this.thumbnailMouseOverTimer = null;
|
|
284
|
-
}
|
|
285
|
-
this.thumbnailMouseOverTimer = window.setTimeout(() => {
|
|
286
|
-
this.slideToIndex(index);
|
|
287
|
-
this.pause();
|
|
288
|
-
}, 300);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
onThumbnailMouseLeave() {
|
|
292
|
-
if (this.thumbnailMouseOverTimer) {
|
|
293
|
-
const { autoPlay } = this.props;
|
|
294
|
-
window.clearTimeout(this.thumbnailMouseOverTimer);
|
|
295
|
-
this.thumbnailMouseOverTimer = null;
|
|
296
|
-
if (autoPlay) {
|
|
297
|
-
this.play();
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
setThumbsTranslate(thumbsTranslate) {
|
|
303
|
-
this.setState({ thumbsTranslate });
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
setModalFullscreen(state) {
|
|
307
|
-
const { onScreenChange } = this.props;
|
|
308
|
-
this.setState({ modalFullscreen: state });
|
|
309
|
-
// manually call because browser does not support screenchange events
|
|
310
|
-
if (onScreenChange) {
|
|
311
|
-
onScreenChange(state);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
getThumbsTranslate(indexDifference) {
|
|
316
|
-
const { disableThumbnailScroll, items } = this.props;
|
|
317
|
-
const { thumbnailsWrapperWidth, thumbnailsWrapperHeight } = this.state;
|
|
318
|
-
// the scroll space that is hidden on the left & right / top & bottom
|
|
319
|
-
// when the screen is not large enough to fit all thumbnails
|
|
320
|
-
let hiddenScroll;
|
|
321
|
-
const thumbsElement = this.thumbnails && this.thumbnails.current;
|
|
322
|
-
|
|
323
|
-
if (disableThumbnailScroll) return 0;
|
|
324
|
-
|
|
325
|
-
if (thumbsElement) {
|
|
326
|
-
// total scroll required to see the last thumbnail
|
|
327
|
-
if (this.isThumbnailVertical()) {
|
|
328
|
-
if (thumbsElement.scrollHeight <= thumbnailsWrapperHeight) {
|
|
329
|
-
return 0;
|
|
330
|
-
}
|
|
331
|
-
hiddenScroll = thumbsElement.scrollHeight - thumbnailsWrapperHeight;
|
|
332
|
-
} else {
|
|
333
|
-
if (
|
|
334
|
-
thumbsElement.scrollWidth <= thumbnailsWrapperWidth ||
|
|
335
|
-
thumbnailsWrapperWidth <= 0
|
|
336
|
-
) {
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
hiddenScroll = thumbsElement.scrollWidth - thumbnailsWrapperWidth;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// scroll-x or y required per index change
|
|
343
|
-
const perIndexScroll = hiddenScroll / (items.length - 1);
|
|
344
|
-
return indexDifference * perIndexScroll;
|
|
345
|
-
}
|
|
346
|
-
return 0;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
getThumbnailPositionClassName(thumbnailPosition) {
|
|
350
|
-
// get the specific thumbnailPosition className
|
|
351
|
-
const leftClassName = "image-gallery-thumbnails-left";
|
|
352
|
-
const rightClassName = "image-gallery-thumbnails-right";
|
|
353
|
-
const bottomClassName = "image-gallery-thumbnails-bottom";
|
|
354
|
-
const topClassName = "image-gallery-thumbnails-top";
|
|
355
|
-
|
|
356
|
-
switch (thumbnailPosition) {
|
|
357
|
-
case "left":
|
|
358
|
-
thumbnailPosition = ` ${leftClassName}`;
|
|
359
|
-
break;
|
|
360
|
-
case "right":
|
|
361
|
-
thumbnailPosition = ` ${rightClassName}`;
|
|
362
|
-
break;
|
|
363
|
-
case "bottom":
|
|
364
|
-
thumbnailPosition = ` ${bottomClassName}`;
|
|
365
|
-
break;
|
|
366
|
-
case "top":
|
|
367
|
-
thumbnailPosition = ` ${topClassName}`;
|
|
368
|
-
break;
|
|
369
|
-
default:
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return thumbnailPosition;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
getAlignmentClassName(index) {
|
|
377
|
-
// Necessary for lazing loading
|
|
378
|
-
const { currentIndex } = this.state;
|
|
379
|
-
const { infinite, items } = this.props;
|
|
380
|
-
let alignment = "";
|
|
381
|
-
const leftClassName = "image-gallery-left";
|
|
382
|
-
const centerClassName = "image-gallery-center";
|
|
383
|
-
const rightClassName = "image-gallery-right";
|
|
384
|
-
|
|
385
|
-
switch (index) {
|
|
386
|
-
case currentIndex - 1:
|
|
387
|
-
alignment = ` ${leftClassName}`;
|
|
388
|
-
break;
|
|
389
|
-
case currentIndex:
|
|
390
|
-
alignment = ` ${centerClassName}`;
|
|
391
|
-
break;
|
|
392
|
-
case currentIndex + 1:
|
|
393
|
-
alignment = ` ${rightClassName}`;
|
|
394
|
-
break;
|
|
395
|
-
default:
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (items.length >= 3 && infinite) {
|
|
400
|
-
if (index === 0 && currentIndex === items.length - 1) {
|
|
401
|
-
// set first slide as right slide if were sliding right from last slide
|
|
402
|
-
alignment = ` ${rightClassName}`;
|
|
403
|
-
} else if (index === items.length - 1 && currentIndex === 0) {
|
|
404
|
-
// set last slide as left slide if were sliding left from first slide
|
|
405
|
-
alignment = ` ${leftClassName}`;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
return alignment;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
getTranslateXForTwoSlide(index) {
|
|
413
|
-
// For taking care of infinite swipe when there are only two slides
|
|
414
|
-
const { currentIndex, currentSlideOffset, previousIndex } = this.state;
|
|
415
|
-
const indexChanged = currentIndex !== previousIndex;
|
|
416
|
-
const firstSlideWasPrevSlide = index === 0 && previousIndex === 0;
|
|
417
|
-
const secondSlideWasPrevSlide = index === 1 && previousIndex === 1;
|
|
418
|
-
const firstSlideIsNextSlide = index === 0 && currentIndex === 1;
|
|
419
|
-
const secondSlideIsNextSlide = index === 1 && currentIndex === 0;
|
|
420
|
-
const swipingEnded = currentSlideOffset === 0;
|
|
421
|
-
const baseTranslateX = -100 * currentIndex;
|
|
422
|
-
let translateX = baseTranslateX + index * 100 + currentSlideOffset;
|
|
423
|
-
|
|
424
|
-
// keep track of user swiping direction
|
|
425
|
-
// important to understand how to translateX based on last direction
|
|
426
|
-
if (currentSlideOffset > 0) {
|
|
427
|
-
this.direction = "left";
|
|
428
|
-
} else if (currentSlideOffset < 0) {
|
|
429
|
-
this.direction = "right";
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// when swiping between two slides make sure the next and prev slides
|
|
433
|
-
// are on both left and right
|
|
434
|
-
if (secondSlideIsNextSlide && currentSlideOffset > 0) {
|
|
435
|
-
// swiping right
|
|
436
|
-
translateX = -100 + currentSlideOffset;
|
|
437
|
-
}
|
|
438
|
-
if (firstSlideIsNextSlide && currentSlideOffset < 0) {
|
|
439
|
-
// swiping left
|
|
440
|
-
translateX = 100 + currentSlideOffset;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (indexChanged) {
|
|
444
|
-
// when indexChanged move the slide to the correct side
|
|
445
|
-
if (firstSlideWasPrevSlide && swipingEnded && this.direction === "left") {
|
|
446
|
-
translateX = 100;
|
|
447
|
-
} else if (
|
|
448
|
-
secondSlideWasPrevSlide &&
|
|
449
|
-
swipingEnded &&
|
|
450
|
-
this.direction === "right"
|
|
451
|
-
) {
|
|
452
|
-
translateX = -100;
|
|
453
|
-
}
|
|
454
|
-
} else {
|
|
455
|
-
// keep the slide on the correct side if the swipe was not successful
|
|
456
|
-
if (secondSlideIsNextSlide && swipingEnded && this.direction === "left") {
|
|
457
|
-
translateX = -100;
|
|
458
|
-
}
|
|
459
|
-
if (firstSlideIsNextSlide && swipingEnded && this.direction === "right") {
|
|
460
|
-
translateX = 100;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return translateX;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
getThumbnailBarHeight() {
|
|
468
|
-
if (this.isThumbnailVertical()) {
|
|
469
|
-
const { gallerySlideWrapperHeight } = this.state;
|
|
470
|
-
return { height: gallerySlideWrapperHeight };
|
|
471
|
-
}
|
|
472
|
-
return {};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
getSlideStyle(index) {
|
|
476
|
-
const { currentIndex, currentSlideOffset, slideStyle } = this.state;
|
|
477
|
-
const { infinite, items, useTranslate3D, isRTL } = this.props;
|
|
478
|
-
const baseTranslateX = -100 * currentIndex;
|
|
479
|
-
const totalSlides = items.length - 1;
|
|
480
|
-
|
|
481
|
-
// calculates where the other slides belong based on currentIndex
|
|
482
|
-
// if it is RTL the base line should be reversed
|
|
483
|
-
let translateX =
|
|
484
|
-
(baseTranslateX + index * 100) * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
485
|
-
|
|
486
|
-
if (infinite && items.length > 2) {
|
|
487
|
-
if (currentIndex === 0 && index === totalSlides) {
|
|
488
|
-
// make the last slide the slide before the first
|
|
489
|
-
// if it is RTL the base line should be reversed
|
|
490
|
-
translateX = -100 * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
491
|
-
} else if (currentIndex === totalSlides && index === 0) {
|
|
492
|
-
// make the first slide the slide after the last
|
|
493
|
-
// if it is RTL the base line should be reversed
|
|
494
|
-
translateX = 100 * (isRTL ? -1 : 1) + currentSlideOffset;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Special case when there are only 2 items with infinite on
|
|
499
|
-
if (infinite && items.length === 2) {
|
|
500
|
-
translateX = this.getTranslateXForTwoSlide(index);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
let translate = `translate(${translateX}%, 0)`;
|
|
504
|
-
|
|
505
|
-
if (useTranslate3D) {
|
|
506
|
-
translate = `translate3d(${translateX}%, 0, 0)`;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// don't show some slides while transitioning to avoid background transitions
|
|
510
|
-
const isVisible = this.isSlideVisible(index);
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
display: isVisible ? "inherit" : "none",
|
|
514
|
-
WebkitTransform: translate,
|
|
515
|
-
MozTransform: translate,
|
|
516
|
-
msTransform: translate,
|
|
517
|
-
OTransform: translate,
|
|
518
|
-
transform: translate,
|
|
519
|
-
...slideStyle,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
getCurrentIndex() {
|
|
524
|
-
const { currentIndex } = this.state;
|
|
525
|
-
return currentIndex;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
getThumbnailStyle() {
|
|
529
|
-
let translate;
|
|
530
|
-
const { useTranslate3D, isRTL } = this.props;
|
|
531
|
-
const { thumbsTranslate, thumbsStyle } = this.state;
|
|
532
|
-
const verticalTranslateValue = isRTL
|
|
533
|
-
? thumbsTranslate * -1
|
|
534
|
-
: thumbsTranslate;
|
|
535
|
-
|
|
536
|
-
if (this.isThumbnailVertical()) {
|
|
537
|
-
translate = `translate(0, ${thumbsTranslate}px)`;
|
|
538
|
-
if (useTranslate3D) {
|
|
539
|
-
translate = `translate3d(0, ${thumbsTranslate}px, 0)`;
|
|
540
|
-
}
|
|
541
|
-
} else {
|
|
542
|
-
translate = `translate(${verticalTranslateValue}px, 0)`;
|
|
543
|
-
if (useTranslate3D) {
|
|
544
|
-
translate = `translate3d(${verticalTranslateValue}px, 0, 0)`;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
return {
|
|
548
|
-
WebkitTransform: translate,
|
|
549
|
-
MozTransform: translate,
|
|
550
|
-
msTransform: translate,
|
|
551
|
-
OTransform: translate,
|
|
552
|
-
transform: translate,
|
|
553
|
-
...thumbsStyle,
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
getSlideItems() {
|
|
558
|
-
const { currentIndex } = this.state;
|
|
559
|
-
const {
|
|
560
|
-
items,
|
|
561
|
-
slideOnThumbnailOver,
|
|
562
|
-
onClick,
|
|
563
|
-
lazyLoad,
|
|
564
|
-
onTouchMove,
|
|
565
|
-
onTouchEnd,
|
|
566
|
-
onTouchStart,
|
|
567
|
-
onMouseOver,
|
|
568
|
-
onMouseLeave,
|
|
569
|
-
renderItem,
|
|
570
|
-
renderThumbInner,
|
|
571
|
-
showThumbnails,
|
|
572
|
-
showBullets,
|
|
573
|
-
} = this.props;
|
|
574
|
-
|
|
575
|
-
const slides = [];
|
|
576
|
-
const thumbnails = [];
|
|
577
|
-
const bullets = [];
|
|
578
|
-
|
|
579
|
-
items.forEach((item, index) => {
|
|
580
|
-
const alignment = this.getAlignmentClassName(index);
|
|
581
|
-
const originalClass = item.originalClass ? ` ${item.originalClass}` : "";
|
|
582
|
-
const thumbnailClass = item.thumbnailClass
|
|
583
|
-
? ` ${item.thumbnailClass}`
|
|
584
|
-
: "";
|
|
585
|
-
const handleRenderItem = item.renderItem || renderItem || this.renderItem;
|
|
586
|
-
const handleRenderThumbInner =
|
|
587
|
-
item.renderThumbInner || renderThumbInner || this.renderThumbInner;
|
|
588
|
-
|
|
589
|
-
const showItem = !lazyLoad || alignment || this.lazyLoaded[index];
|
|
590
|
-
if (showItem && lazyLoad && !this.lazyLoaded[index]) {
|
|
591
|
-
this.lazyLoaded[index] = true;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
const slideStyle = this.getSlideStyle(index);
|
|
595
|
-
|
|
596
|
-
const slide = (
|
|
597
|
-
<div
|
|
598
|
-
aria-label={`Go to Slide ${index + 1}`}
|
|
599
|
-
key={`slide-${index}`}
|
|
600
|
-
tabIndex="-1"
|
|
601
|
-
className={`image-gallery-slide ${alignment} ${originalClass}`}
|
|
602
|
-
style={slideStyle}
|
|
603
|
-
onClick={onClick}
|
|
604
|
-
onKeyUp={this.handleSlideKeyUp}
|
|
605
|
-
onTouchMove={onTouchMove}
|
|
606
|
-
onTouchEnd={onTouchEnd}
|
|
607
|
-
onTouchStart={onTouchStart}
|
|
608
|
-
onMouseOver={onMouseOver}
|
|
609
|
-
onFocus={onMouseOver}
|
|
610
|
-
onMouseLeave={onMouseLeave}
|
|
611
|
-
role="button"
|
|
612
|
-
>
|
|
613
|
-
{showItem ? (
|
|
614
|
-
handleRenderItem(item)
|
|
615
|
-
) : (
|
|
616
|
-
<div style={{ height: "100%" }} />
|
|
617
|
-
)}
|
|
618
|
-
</div>
|
|
619
|
-
);
|
|
620
|
-
|
|
621
|
-
slides.push(slide);
|
|
622
|
-
|
|
623
|
-
// Don't add thumbnails if there is none
|
|
624
|
-
if (showThumbnails && item.thumbnail) {
|
|
625
|
-
const igThumbnailClass = clsx(
|
|
626
|
-
"image-gallery-thumbnail",
|
|
627
|
-
thumbnailClass,
|
|
628
|
-
{ active: currentIndex === index }
|
|
629
|
-
);
|
|
630
|
-
thumbnails.push(
|
|
631
|
-
<button
|
|
632
|
-
key={`thumbnail-${index}`}
|
|
633
|
-
type="button"
|
|
634
|
-
tabIndex="0"
|
|
635
|
-
aria-pressed={currentIndex === index ? "true" : "false"}
|
|
636
|
-
aria-label={`Go to Slide ${index + 1}`}
|
|
637
|
-
className={igThumbnailClass}
|
|
638
|
-
onMouseLeave={
|
|
639
|
-
slideOnThumbnailOver ? this.onThumbnailMouseLeave : null
|
|
640
|
-
}
|
|
641
|
-
onMouseOver={(event) => this.handleThumbnailMouseOver(event, index)}
|
|
642
|
-
onFocus={(event) => this.handleThumbnailMouseOver(event, index)}
|
|
643
|
-
onKeyUp={(event) => this.handleThumbnailKeyUp(event, index)}
|
|
644
|
-
onClick={(event) => this.onThumbnailClick(event, index)}
|
|
645
|
-
>
|
|
646
|
-
{handleRenderThumbInner(item)}
|
|
647
|
-
</button>
|
|
648
|
-
);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
if (showBullets) {
|
|
652
|
-
const igBulletClass = clsx("image-gallery-bullet", item.bulletClass, {
|
|
653
|
-
active: currentIndex === index,
|
|
654
|
-
});
|
|
655
|
-
bullets.push(
|
|
656
|
-
<button
|
|
657
|
-
type="button"
|
|
658
|
-
key={`bullet-${index}`}
|
|
659
|
-
className={igBulletClass}
|
|
660
|
-
onClick={(event) => this.onBulletClick(event, index)}
|
|
661
|
-
aria-pressed={currentIndex === index ? "true" : "false"}
|
|
662
|
-
aria-label={`Go to Slide ${index + 1}`}
|
|
663
|
-
/>
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
return {
|
|
669
|
-
slides,
|
|
670
|
-
thumbnails,
|
|
671
|
-
bullets,
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
ignoreIsTransitioning() {
|
|
676
|
-
/*
|
|
677
|
-
Ignore isTransitioning because were not going to sibling slides
|
|
678
|
-
e.g. center to left or center to right
|
|
679
|
-
*/
|
|
680
|
-
const { items } = this.props;
|
|
681
|
-
const { previousIndex, currentIndex } = this.state;
|
|
682
|
-
const totalSlides = items.length - 1;
|
|
683
|
-
|
|
684
|
-
// we want to show the in between slides transition
|
|
685
|
-
const slidingMoreThanOneSlideLeftOrRight =
|
|
686
|
-
Math.abs(previousIndex - currentIndex) > 1;
|
|
687
|
-
const notGoingFromFirstToLast = !(
|
|
688
|
-
previousIndex === 0 && currentIndex === totalSlides
|
|
689
|
-
);
|
|
690
|
-
const notGoingFromLastToFirst = !(
|
|
691
|
-
previousIndex === totalSlides && currentIndex === 0
|
|
692
|
-
);
|
|
693
|
-
|
|
694
|
-
return (
|
|
695
|
-
slidingMoreThanOneSlideLeftOrRight &&
|
|
696
|
-
notGoingFromFirstToLast &&
|
|
697
|
-
notGoingFromLastToFirst
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
isFirstOrLastSlide(index) {
|
|
702
|
-
const { items } = this.props;
|
|
703
|
-
const totalSlides = items.length - 1;
|
|
704
|
-
const isLastSlide = index === totalSlides;
|
|
705
|
-
const isFirstSlide = index === 0;
|
|
706
|
-
return isLastSlide || isFirstSlide;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
slideIsTransitioning(index) {
|
|
710
|
-
/*
|
|
711
|
-
returns true if the gallery is transitioning and the index is not the
|
|
712
|
-
previous or currentIndex
|
|
713
|
-
*/
|
|
714
|
-
const { isTransitioning, previousIndex, currentIndex } = this.state;
|
|
715
|
-
const indexIsNotPreviousOrNextSlide = !(
|
|
716
|
-
index === previousIndex || index === currentIndex
|
|
717
|
-
);
|
|
718
|
-
return isTransitioning && indexIsNotPreviousOrNextSlide;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
isSlideVisible(index) {
|
|
722
|
-
/*
|
|
723
|
-
Show slide if slide is the current slide and the next slide
|
|
724
|
-
OR
|
|
725
|
-
The slide is going more than one slide left or right, but not going from
|
|
726
|
-
first to last and not going from last to first
|
|
727
|
-
|
|
728
|
-
Edge case:
|
|
729
|
-
If you go to the first or last slide, when they're
|
|
730
|
-
not left, or right of each other they will try to catch up in the background
|
|
731
|
-
so unless were going from first to last or vice versa we don't want the first
|
|
732
|
-
or last slide to show up during the transition
|
|
733
|
-
*/
|
|
734
|
-
return (
|
|
735
|
-
!this.slideIsTransitioning(index) ||
|
|
736
|
-
(this.ignoreIsTransitioning() && !this.isFirstOrLastSlide(index))
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
slideThumbnailBar() {
|
|
741
|
-
const { currentIndex, isSwipingThumbnail } = this.state;
|
|
742
|
-
const nextTranslate = -this.getThumbsTranslate(currentIndex);
|
|
743
|
-
if (isSwipingThumbnail) {
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
if (currentIndex === 0) {
|
|
748
|
-
this.setState({ thumbsTranslate: 0, thumbsSwipedTranslate: 0 });
|
|
749
|
-
} else {
|
|
750
|
-
this.setState({
|
|
751
|
-
thumbsTranslate: nextTranslate,
|
|
752
|
-
thumbsSwipedTranslate: nextTranslate,
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
canSlide() {
|
|
758
|
-
const { items } = this.props;
|
|
759
|
-
return items.length >= 2;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
canSlideLeft() {
|
|
763
|
-
const { infinite } = this.props;
|
|
764
|
-
return infinite || this.canSlidePrevious();
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
canSlideRight() {
|
|
768
|
-
const { infinite } = this.props;
|
|
769
|
-
return infinite || this.canSlideNext();
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
canSlidePrevious() {
|
|
773
|
-
const { currentIndex } = this.state;
|
|
774
|
-
return currentIndex > 0;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
canSlideNext() {
|
|
778
|
-
const { currentIndex } = this.state;
|
|
779
|
-
const { items } = this.props;
|
|
780
|
-
return currentIndex < items.length - 1;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
handleSwiping({ event, absX, dir }) {
|
|
784
|
-
const { disableSwipe, stopPropagation } = this.props;
|
|
785
|
-
const { galleryWidth, isTransitioning, swipingUpDown, swipingLeftRight } =
|
|
786
|
-
this.state;
|
|
787
|
-
|
|
788
|
-
// if the initial swiping is up/down prevent moving the slides until swipe ends
|
|
789
|
-
if ((dir === UP || dir === DOWN || swipingUpDown) && !swipingLeftRight) {
|
|
790
|
-
if (!swipingUpDown) {
|
|
791
|
-
this.setState({ swipingUpDown: true });
|
|
792
|
-
}
|
|
793
|
-
return;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
if ((dir === LEFT || dir === RIGHT) && !swipingLeftRight) {
|
|
797
|
-
this.setState({ swipingLeftRight: true });
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
if (disableSwipe) return;
|
|
801
|
-
|
|
802
|
-
const { swipingTransitionDuration } = this.props;
|
|
803
|
-
if (stopPropagation) {
|
|
804
|
-
event.preventDefault();
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
if (!isTransitioning) {
|
|
808
|
-
const side = dir === RIGHT ? 1 : -1;
|
|
809
|
-
|
|
810
|
-
let currentSlideOffset = (absX / galleryWidth) * 100;
|
|
811
|
-
if (Math.abs(currentSlideOffset) >= 100) {
|
|
812
|
-
currentSlideOffset = 100;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
const swipingTransition = {
|
|
816
|
-
transition: `transform ${swipingTransitionDuration}ms ease-out`,
|
|
817
|
-
};
|
|
818
|
-
|
|
819
|
-
this.setState({
|
|
820
|
-
currentSlideOffset: side * currentSlideOffset,
|
|
821
|
-
slideStyle: swipingTransition,
|
|
822
|
-
});
|
|
823
|
-
} else {
|
|
824
|
-
// don't move the slide
|
|
825
|
-
this.setState({ currentSlideOffset: 0 });
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
handleThumbnailSwiping({ event, absX, absY, dir }) {
|
|
830
|
-
const { stopPropagation, swipingThumbnailTransitionDuration } = this.props;
|
|
831
|
-
const {
|
|
832
|
-
thumbsSwipedTranslate,
|
|
833
|
-
thumbnailsWrapperHeight,
|
|
834
|
-
thumbnailsWrapperWidth,
|
|
835
|
-
swipingUpDown,
|
|
836
|
-
swipingLeftRight,
|
|
837
|
-
} = this.state;
|
|
838
|
-
|
|
839
|
-
if (this.isThumbnailVertical()) {
|
|
840
|
-
// if the initial swiping is left/right, prevent moving the thumbnail bar until swipe ends
|
|
841
|
-
if (
|
|
842
|
-
(dir === LEFT || dir === RIGHT || swipingLeftRight) &&
|
|
843
|
-
!swipingUpDown
|
|
844
|
-
) {
|
|
845
|
-
if (!swipingLeftRight) {
|
|
846
|
-
this.setState({ swipingLeftRight: true });
|
|
847
|
-
}
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
if ((dir === UP || dir === DOWN) && !swipingUpDown) {
|
|
852
|
-
this.setState({ swipingUpDown: true });
|
|
853
|
-
}
|
|
854
|
-
} else {
|
|
855
|
-
// if the initial swiping is up/down, prevent moving the thumbnail bar until swipe ends
|
|
856
|
-
if ((dir === UP || dir === DOWN || swipingUpDown) && !swipingLeftRight) {
|
|
857
|
-
if (!swipingUpDown) {
|
|
858
|
-
this.setState({ swipingUpDown: true });
|
|
859
|
-
}
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
if ((dir === LEFT || dir === RIGHT) && !swipingLeftRight) {
|
|
864
|
-
this.setState({ swipingLeftRight: true });
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
const thumbsElement = this.thumbnails && this.thumbnails.current;
|
|
869
|
-
const emptySpaceMargin = 20; // 20px to add some margin to show empty space
|
|
870
|
-
|
|
871
|
-
let thumbsTranslate;
|
|
872
|
-
let totalSwipeableLength;
|
|
873
|
-
let hasSwipedPassedEnd;
|
|
874
|
-
let hasSwipedPassedStart;
|
|
875
|
-
let isThumbnailBarSmallerThanContainer;
|
|
876
|
-
|
|
877
|
-
if (this.isThumbnailVertical()) {
|
|
878
|
-
const slideY = dir === DOWN ? absY : -absY;
|
|
879
|
-
thumbsTranslate = thumbsSwipedTranslate + slideY;
|
|
880
|
-
totalSwipeableLength =
|
|
881
|
-
thumbsElement.scrollHeight - thumbnailsWrapperHeight + emptySpaceMargin;
|
|
882
|
-
hasSwipedPassedEnd = Math.abs(thumbsTranslate) > totalSwipeableLength;
|
|
883
|
-
hasSwipedPassedStart = thumbsTranslate > emptySpaceMargin;
|
|
884
|
-
isThumbnailBarSmallerThanContainer =
|
|
885
|
-
thumbsElement.scrollHeight <= thumbnailsWrapperHeight;
|
|
886
|
-
} else {
|
|
887
|
-
const slideX = dir === RIGHT ? absX : -absX;
|
|
888
|
-
thumbsTranslate = thumbsSwipedTranslate + slideX;
|
|
889
|
-
totalSwipeableLength =
|
|
890
|
-
thumbsElement.scrollWidth - thumbnailsWrapperWidth + emptySpaceMargin;
|
|
891
|
-
hasSwipedPassedEnd = Math.abs(thumbsTranslate) > totalSwipeableLength;
|
|
892
|
-
hasSwipedPassedStart = thumbsTranslate > emptySpaceMargin;
|
|
893
|
-
isThumbnailBarSmallerThanContainer =
|
|
894
|
-
thumbsElement.scrollWidth <= thumbnailsWrapperWidth;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
if (isThumbnailBarSmallerThanContainer) {
|
|
898
|
-
// no need to swipe a thumbnail bar smaller/shorter than its container
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
if ((dir === LEFT || dir === UP) && hasSwipedPassedEnd) {
|
|
903
|
-
// prevent further swipeing
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
if ((dir === RIGHT || dir === DOWN) && hasSwipedPassedStart) {
|
|
908
|
-
// prevent further swipeing
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (stopPropagation) event.stopPropagation();
|
|
913
|
-
|
|
914
|
-
const swipingTransition = {
|
|
915
|
-
transition: `transform ${swipingThumbnailTransitionDuration}ms ease-out`,
|
|
916
|
-
};
|
|
917
|
-
|
|
918
|
-
this.setState({
|
|
919
|
-
thumbsTranslate,
|
|
920
|
-
thumbsStyle: swipingTransition,
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
handleOnThumbnailSwiped() {
|
|
925
|
-
const { thumbsTranslate } = this.state;
|
|
926
|
-
const { slideDuration } = this.props;
|
|
927
|
-
this.resetSwipingDirection();
|
|
928
|
-
this.setState({
|
|
929
|
-
isSwipingThumbnail: true,
|
|
930
|
-
thumbsSwipedTranslate: thumbsTranslate,
|
|
931
|
-
thumbsStyle: { transition: `all ${slideDuration}ms ease-out` },
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
sufficientSwipe() {
|
|
936
|
-
const { currentSlideOffset } = this.state;
|
|
937
|
-
const { swipeThreshold } = this.props;
|
|
938
|
-
return Math.abs(currentSlideOffset) > swipeThreshold;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
resetSwipingDirection() {
|
|
942
|
-
const { swipingUpDown, swipingLeftRight } = this.state;
|
|
943
|
-
if (swipingUpDown) {
|
|
944
|
-
// user stopped swipingUpDown, reset
|
|
945
|
-
this.setState({ swipingUpDown: false });
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
if (swipingLeftRight) {
|
|
949
|
-
// user stopped swipingLeftRight, reset
|
|
950
|
-
this.setState({ swipingLeftRight: false });
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
handleOnSwiped({ event, dir, velocity }) {
|
|
955
|
-
const { disableSwipe, stopPropagation, flickThreshold } = this.props;
|
|
956
|
-
|
|
957
|
-
if (disableSwipe) return;
|
|
958
|
-
|
|
959
|
-
const { isRTL } = this.props;
|
|
960
|
-
if (stopPropagation) event.stopPropagation();
|
|
961
|
-
this.resetSwipingDirection();
|
|
962
|
-
|
|
963
|
-
// if it is RTL the direction is reversed
|
|
964
|
-
const swipeDirection = (dir === LEFT ? 1 : -1) * (isRTL ? -1 : 1);
|
|
965
|
-
const isSwipeUpOrDown = dir === UP || dir === DOWN;
|
|
966
|
-
const isLeftRightFlick = velocity > flickThreshold && !isSwipeUpOrDown;
|
|
967
|
-
this.handleOnSwipedTo(swipeDirection, isLeftRightFlick);
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
handleOnSwipedTo(swipeDirection, isLeftRightFlick) {
|
|
971
|
-
const { currentIndex, isTransitioning } = this.state;
|
|
972
|
-
let slideTo = currentIndex;
|
|
973
|
-
|
|
974
|
-
if ((this.sufficientSwipe() || isLeftRightFlick) && !isTransitioning) {
|
|
975
|
-
// slideto the next/prev slide
|
|
976
|
-
slideTo += swipeDirection;
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
// If we can't swipe left or right, stay in the current index (noop)
|
|
980
|
-
if (
|
|
981
|
-
(swipeDirection === -1 && !this.canSlideLeft()) ||
|
|
982
|
-
(swipeDirection === 1 && !this.canSlideRight())
|
|
983
|
-
) {
|
|
984
|
-
slideTo = currentIndex;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
this.unthrottledSlideToIndex(slideTo);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
handleTouchMove(event) {
|
|
991
|
-
const { swipingLeftRight } = this.state;
|
|
992
|
-
if (swipingLeftRight) {
|
|
993
|
-
// prevent background scrolling up and down while swiping left and right
|
|
994
|
-
event.preventDefault();
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
handleMouseDown() {
|
|
999
|
-
// keep track of mouse vs keyboard usage for a11y
|
|
1000
|
-
this.imageGallery.current.classList.add("image-gallery-using-mouse");
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
handleKeyDown(event) {
|
|
1004
|
-
const { disableKeyDown, useBrowserFullscreen } = this.props;
|
|
1005
|
-
const { isFullscreen } = this.state;
|
|
1006
|
-
// keep track of mouse vs keyboard usage for a11y
|
|
1007
|
-
this.imageGallery.current.classList.remove("image-gallery-using-mouse");
|
|
1008
|
-
|
|
1009
|
-
if (disableKeyDown) return;
|
|
1010
|
-
const LEFT_ARROW = 37;
|
|
1011
|
-
const RIGHT_ARROW = 39;
|
|
1012
|
-
const ESC_KEY = 27;
|
|
1013
|
-
const key = parseInt(event.keyCode || event.which || 0, 10);
|
|
1014
|
-
|
|
1015
|
-
switch (key) {
|
|
1016
|
-
case LEFT_ARROW:
|
|
1017
|
-
if (this.canSlideLeft() && !this.playPauseIntervalId) {
|
|
1018
|
-
this.slideLeft(event);
|
|
1019
|
-
}
|
|
1020
|
-
break;
|
|
1021
|
-
case RIGHT_ARROW:
|
|
1022
|
-
if (this.canSlideRight() && !this.playPauseIntervalId) {
|
|
1023
|
-
this.slideRight(event);
|
|
1024
|
-
}
|
|
1025
|
-
break;
|
|
1026
|
-
case ESC_KEY:
|
|
1027
|
-
if (isFullscreen && !useBrowserFullscreen) {
|
|
1028
|
-
this.exitFullScreen();
|
|
1029
|
-
}
|
|
1030
|
-
break;
|
|
1031
|
-
default:
|
|
1032
|
-
break;
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
handleImageError(event) {
|
|
1037
|
-
const { onErrorImageURL } = this.props;
|
|
1038
|
-
if (onErrorImageURL && event.target.src.indexOf(onErrorImageURL) === -1) {
|
|
1039
|
-
/* eslint-disable no-param-reassign */
|
|
1040
|
-
event.target.src = onErrorImageURL;
|
|
1041
|
-
/* eslint-enable no-param-reassign */
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
removeThumbnailsResizeObserver() {
|
|
1046
|
-
if (
|
|
1047
|
-
this.resizeThumbnailWrapperObserver &&
|
|
1048
|
-
this.thumbnailsWrapper &&
|
|
1049
|
-
this.thumbnailsWrapper.current
|
|
1050
|
-
) {
|
|
1051
|
-
this.resizeThumbnailWrapperObserver.unobserve(
|
|
1052
|
-
this.thumbnailsWrapper.current
|
|
1053
|
-
);
|
|
1054
|
-
this.resizeThumbnailWrapperObserver = null;
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
removeResizeObserver() {
|
|
1059
|
-
if (
|
|
1060
|
-
this.resizeSlideWrapperObserver &&
|
|
1061
|
-
this.imageGallerySlideWrapper &&
|
|
1062
|
-
this.imageGallerySlideWrapper.current
|
|
1063
|
-
) {
|
|
1064
|
-
this.resizeSlideWrapperObserver.unobserve(
|
|
1065
|
-
this.imageGallerySlideWrapper.current
|
|
1066
|
-
);
|
|
1067
|
-
this.resizeSlideWrapperObserver = null;
|
|
1068
|
-
}
|
|
1069
|
-
this.removeThumbnailsResizeObserver();
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
handleResize() {
|
|
1073
|
-
const { currentIndex } = this.state;
|
|
1074
|
-
|
|
1075
|
-
// component has been unmounted
|
|
1076
|
-
if (!this.imageGallery) {
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
if (this.imageGallery && this.imageGallery.current) {
|
|
1081
|
-
this.setState({ galleryWidth: this.imageGallery.current.offsetWidth });
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
if (
|
|
1085
|
-
this.imageGallerySlideWrapper &&
|
|
1086
|
-
this.imageGallerySlideWrapper.current
|
|
1087
|
-
) {
|
|
1088
|
-
this.setState({
|
|
1089
|
-
gallerySlideWrapperHeight:
|
|
1090
|
-
this.imageGallerySlideWrapper.current.offsetHeight,
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// Adjust thumbnail container when thumbnail width or height is adjusted
|
|
1095
|
-
this.setThumbsTranslate(-this.getThumbsTranslate(currentIndex));
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
initSlideWrapperResizeObserver(element) {
|
|
1099
|
-
if (element && !element.current) return;
|
|
1100
|
-
// keeps track of gallery height changes for vertical thumbnail height
|
|
1101
|
-
this.resizeSlideWrapperObserver = new ResizeObserver(
|
|
1102
|
-
debounce((entries) => {
|
|
1103
|
-
if (!entries) return;
|
|
1104
|
-
entries.forEach((entry) => {
|
|
1105
|
-
this.setState(
|
|
1106
|
-
{ thumbnailsWrapperWidth: entry.contentRect.width },
|
|
1107
|
-
this.handleResize
|
|
1108
|
-
);
|
|
1109
|
-
});
|
|
1110
|
-
}, 50)
|
|
1111
|
-
);
|
|
1112
|
-
this.resizeSlideWrapperObserver.observe(element.current);
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
initThumbnailWrapperResizeObserver(element) {
|
|
1116
|
-
if (element && !element.current) return; // thumbnails are not always available
|
|
1117
|
-
this.resizeThumbnailWrapperObserver = new ResizeObserver(
|
|
1118
|
-
debounce((entries) => {
|
|
1119
|
-
if (!entries) return;
|
|
1120
|
-
entries.forEach((entry) => {
|
|
1121
|
-
this.setState(
|
|
1122
|
-
{ thumbnailsWrapperHeight: entry.contentRect.height },
|
|
1123
|
-
this.handleResize
|
|
1124
|
-
);
|
|
1125
|
-
});
|
|
1126
|
-
}, 50)
|
|
1127
|
-
);
|
|
1128
|
-
this.resizeThumbnailWrapperObserver.observe(element.current);
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
toggleFullScreen() {
|
|
1132
|
-
const { isFullscreen } = this.state;
|
|
1133
|
-
if (isFullscreen) {
|
|
1134
|
-
this.exitFullScreen();
|
|
1135
|
-
} else {
|
|
1136
|
-
this.fullScreen();
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
togglePlay() {
|
|
1141
|
-
if (this.playPauseIntervalId) {
|
|
1142
|
-
this.pause();
|
|
1143
|
-
} else {
|
|
1144
|
-
this.play();
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
handleScreenChange() {
|
|
1149
|
-
/*
|
|
1150
|
-
handles screen change events that the browser triggers e.g. esc key
|
|
1151
|
-
*/
|
|
1152
|
-
const { onScreenChange, useBrowserFullscreen } = this.props;
|
|
1153
|
-
const fullScreenElement =
|
|
1154
|
-
document.fullscreenElement ||
|
|
1155
|
-
document.msFullscreenElement ||
|
|
1156
|
-
document.mozFullScreenElement ||
|
|
1157
|
-
document.webkitFullscreenElement;
|
|
1158
|
-
|
|
1159
|
-
// check if screenchange element is the gallery
|
|
1160
|
-
const isFullscreen = this.imageGallery.current === fullScreenElement;
|
|
1161
|
-
if (onScreenChange) onScreenChange(isFullscreen);
|
|
1162
|
-
if (useBrowserFullscreen) this.setState({ isFullscreen });
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
slideToIndex(index, event) {
|
|
1166
|
-
const { currentIndex, isTransitioning } = this.state;
|
|
1167
|
-
const { items, slideDuration, onBeforeSlide } = this.props;
|
|
1168
|
-
|
|
1169
|
-
if (!isTransitioning) {
|
|
1170
|
-
if (event) {
|
|
1171
|
-
if (this.playPauseIntervalId) {
|
|
1172
|
-
// user triggered event while ImageGallery is playing, reset interval
|
|
1173
|
-
this.pause(false);
|
|
1174
|
-
this.play(false);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
const slideCount = items.length - 1;
|
|
1179
|
-
let nextIndex = index;
|
|
1180
|
-
if (index < 0) {
|
|
1181
|
-
nextIndex = slideCount;
|
|
1182
|
-
} else if (index > slideCount) {
|
|
1183
|
-
nextIndex = 0;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
if (onBeforeSlide && nextIndex !== currentIndex) {
|
|
1187
|
-
onBeforeSlide(nextIndex);
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
this.setState(
|
|
1191
|
-
{
|
|
1192
|
-
previousIndex: currentIndex,
|
|
1193
|
-
currentIndex: nextIndex,
|
|
1194
|
-
isTransitioning: nextIndex !== currentIndex,
|
|
1195
|
-
currentSlideOffset: 0,
|
|
1196
|
-
slideStyle: { transition: `all ${slideDuration}ms ease-out` },
|
|
1197
|
-
},
|
|
1198
|
-
this.onSliding
|
|
1199
|
-
);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
slideLeft(event) {
|
|
1204
|
-
const { isRTL } = this.props;
|
|
1205
|
-
this.slideTo(event, isRTL ? "right" : "left");
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
slideRight(event) {
|
|
1209
|
-
const { isRTL } = this.props;
|
|
1210
|
-
this.slideTo(event, isRTL ? "left" : "right");
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
slideTo(event, direction) {
|
|
1214
|
-
const { currentIndex, isTransitioning } = this.state;
|
|
1215
|
-
const { items } = this.props;
|
|
1216
|
-
const nextIndex = currentIndex + (direction === "left" ? -1 : 1);
|
|
1217
|
-
|
|
1218
|
-
if (isTransitioning) return;
|
|
1219
|
-
|
|
1220
|
-
if (items.length === 2) {
|
|
1221
|
-
this.slideToIndexWithStyleReset(nextIndex, event);
|
|
1222
|
-
} else {
|
|
1223
|
-
this.slideToIndex(nextIndex, event);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
slideToIndexWithStyleReset(nextIndex, event) {
|
|
1228
|
-
/*
|
|
1229
|
-
When there are only 2 slides fake a tiny swipe to get the slides
|
|
1230
|
-
on the correct side for transitioning
|
|
1231
|
-
*/
|
|
1232
|
-
const { currentIndex, currentSlideOffset } = this.state;
|
|
1233
|
-
this.setState(
|
|
1234
|
-
{
|
|
1235
|
-
// this will reset once index changes
|
|
1236
|
-
currentSlideOffset:
|
|
1237
|
-
currentSlideOffset + (currentIndex > nextIndex ? 0.001 : -0.001),
|
|
1238
|
-
slideStyle: { transition: "none" }, // move the slide over instantly
|
|
1239
|
-
},
|
|
1240
|
-
() => {
|
|
1241
|
-
// add 25ms timeout to avoid delay in moving slides over
|
|
1242
|
-
window.setTimeout(() => this.slideToIndex(nextIndex, event), 25);
|
|
1243
|
-
}
|
|
1244
|
-
);
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
handleThumbnailMouseOver(event, index) {
|
|
1248
|
-
const { slideOnThumbnailOver } = this.props;
|
|
1249
|
-
if (slideOnThumbnailOver) this.onThumbnailMouseOver(event, index);
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
handleThumbnailKeyUp(event, index) {
|
|
1253
|
-
// a11y support ^_^
|
|
1254
|
-
if (isEnterOrSpaceKey(event)) this.onThumbnailClick(event, index);
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
handleSlideKeyUp(event) {
|
|
1258
|
-
// a11y support ^_^
|
|
1259
|
-
if (isEnterOrSpaceKey(event)) {
|
|
1260
|
-
const { onClick } = this.props;
|
|
1261
|
-
onClick(event);
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
isThumbnailVertical() {
|
|
1266
|
-
const { thumbnailPosition } = this.props;
|
|
1267
|
-
return thumbnailPosition === "left" || thumbnailPosition === "right";
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
addScreenChangeEvent() {
|
|
1271
|
-
screenChangeEvents.forEach((eventName) => {
|
|
1272
|
-
document.addEventListener(eventName, this.handleScreenChange);
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
removeScreenChangeEvent() {
|
|
1277
|
-
screenChangeEvents.forEach((eventName) => {
|
|
1278
|
-
document.removeEventListener(eventName, this.handleScreenChange);
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
fullScreen() {
|
|
1283
|
-
const { useBrowserFullscreen } = this.props;
|
|
1284
|
-
const gallery = this.imageGallery.current;
|
|
1285
|
-
if (useBrowserFullscreen) {
|
|
1286
|
-
if (gallery.requestFullscreen) {
|
|
1287
|
-
gallery.requestFullscreen();
|
|
1288
|
-
} else if (gallery.msRequestFullscreen) {
|
|
1289
|
-
gallery.msRequestFullscreen();
|
|
1290
|
-
} else if (gallery.mozRequestFullScreen) {
|
|
1291
|
-
gallery.mozRequestFullScreen();
|
|
1292
|
-
} else if (gallery.webkitRequestFullscreen) {
|
|
1293
|
-
gallery.webkitRequestFullscreen();
|
|
1294
|
-
} else {
|
|
1295
|
-
// fallback to fullscreen modal for unsupported browsers
|
|
1296
|
-
this.setModalFullscreen(true);
|
|
1297
|
-
}
|
|
1298
|
-
} else {
|
|
1299
|
-
this.setModalFullscreen(true);
|
|
1300
|
-
}
|
|
1301
|
-
this.setState({ isFullscreen: true });
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
exitFullScreen() {
|
|
1305
|
-
const { isFullscreen } = this.state;
|
|
1306
|
-
const { useBrowserFullscreen } = this.props;
|
|
1307
|
-
if (isFullscreen) {
|
|
1308
|
-
if (useBrowserFullscreen) {
|
|
1309
|
-
if (document.exitFullscreen) {
|
|
1310
|
-
document.exitFullscreen();
|
|
1311
|
-
} else if (document.webkitExitFullscreen) {
|
|
1312
|
-
document.webkitExitFullscreen();
|
|
1313
|
-
} else if (document.mozCancelFullScreen) {
|
|
1314
|
-
document.mozCancelFullScreen();
|
|
1315
|
-
} else if (document.msExitFullscreen) {
|
|
1316
|
-
document.msExitFullscreen();
|
|
1317
|
-
} else {
|
|
1318
|
-
// fallback to fullscreen modal for unsupported browsers
|
|
1319
|
-
this.setModalFullscreen(false);
|
|
1320
|
-
}
|
|
1321
|
-
} else {
|
|
1322
|
-
this.setModalFullscreen(false);
|
|
1323
|
-
}
|
|
1324
|
-
this.setState({ isFullscreen: false });
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
pauseOrPlay() {
|
|
1329
|
-
const { infinite } = this.props;
|
|
1330
|
-
const { currentIndex } = this.state;
|
|
1331
|
-
if (!infinite && !this.canSlideRight()) {
|
|
1332
|
-
this.pause();
|
|
1333
|
-
} else {
|
|
1334
|
-
this.slideToIndex(currentIndex + 1);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
play(shouldCallOnPlay = true) {
|
|
1339
|
-
const { onPlay, slideInterval, slideDuration } = this.props;
|
|
1340
|
-
const { currentIndex } = this.state;
|
|
1341
|
-
if (!this.playPauseIntervalId) {
|
|
1342
|
-
this.setState({ isPlaying: true });
|
|
1343
|
-
this.playPauseIntervalId = window.setInterval(
|
|
1344
|
-
this.pauseOrPlay,
|
|
1345
|
-
Math.max(slideInterval, slideDuration)
|
|
1346
|
-
);
|
|
1347
|
-
if (onPlay && shouldCallOnPlay) {
|
|
1348
|
-
onPlay(currentIndex);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
pause(shouldCallOnPause = true) {
|
|
1354
|
-
const { onPause } = this.props;
|
|
1355
|
-
const { currentIndex } = this.state;
|
|
1356
|
-
if (this.playPauseIntervalId) {
|
|
1357
|
-
window.clearInterval(this.playPauseIntervalId);
|
|
1358
|
-
this.playPauseIntervalId = null;
|
|
1359
|
-
this.setState({ isPlaying: false });
|
|
1360
|
-
if (onPause && shouldCallOnPause) {
|
|
1361
|
-
onPause(currentIndex);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
isImageLoaded(item) {
|
|
1367
|
-
/*
|
|
1368
|
-
Keep track of images loaded so that onImageLoad prop is not
|
|
1369
|
-
called multiple times when re-render the images
|
|
1370
|
-
*/
|
|
1371
|
-
const imageExists = this.loadedImages[item.original];
|
|
1372
|
-
if (imageExists) {
|
|
1373
|
-
return true;
|
|
1374
|
-
}
|
|
1375
|
-
// add image as loaded
|
|
1376
|
-
this.loadedImages[item.original] = true;
|
|
1377
|
-
return false;
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
handleImageLoaded(event, original) {
|
|
1381
|
-
const { onImageLoad } = this.props;
|
|
1382
|
-
const imageExists = this.loadedImages[original];
|
|
1383
|
-
if (!imageExists && onImageLoad) {
|
|
1384
|
-
this.loadedImages[original] = true; // prevent from call again
|
|
1385
|
-
// image just loaded, call onImageLoad
|
|
1386
|
-
onImageLoad(event);
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
renderItem(item) {
|
|
1391
|
-
const { isFullscreen } = this.state;
|
|
1392
|
-
const { onImageError } = this.props;
|
|
1393
|
-
const handleImageError = onImageError || this.handleImageError;
|
|
1394
|
-
|
|
1395
|
-
return (
|
|
1396
|
-
<Item
|
|
1397
|
-
description={item.description}
|
|
1398
|
-
fullscreen={item.fullscreen}
|
|
1399
|
-
handleImageLoaded={this.handleImageLoaded}
|
|
1400
|
-
isFullscreen={isFullscreen}
|
|
1401
|
-
onImageError={handleImageError}
|
|
1402
|
-
original={item.original}
|
|
1403
|
-
originalAlt={item.originalAlt}
|
|
1404
|
-
originalHeight={item.originalHeight}
|
|
1405
|
-
originalWidth={item.originalWidth}
|
|
1406
|
-
originalTitle={item.originalTitle}
|
|
1407
|
-
sizes={item.sizes}
|
|
1408
|
-
loading={item.loading}
|
|
1409
|
-
srcSet={item.srcSet}
|
|
1410
|
-
/>
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
renderThumbInner(item) {
|
|
1415
|
-
const { onThumbnailError } = this.props;
|
|
1416
|
-
const handleThumbnailError = onThumbnailError || this.handleImageError;
|
|
1417
|
-
|
|
1418
|
-
return (
|
|
1419
|
-
<span className="image-gallery-thumbnail-inner">
|
|
1420
|
-
<img
|
|
1421
|
-
className="image-gallery-thumbnail-image"
|
|
1422
|
-
src={item.thumbnail}
|
|
1423
|
-
height={item.thumbnailHeight}
|
|
1424
|
-
width={item.thumbnailWidth}
|
|
1425
|
-
alt={item.thumbnailAlt}
|
|
1426
|
-
title={item.thumbnailTitle}
|
|
1427
|
-
loading={item.thumbnailLoading}
|
|
1428
|
-
onError={handleThumbnailError}
|
|
1429
|
-
/>
|
|
1430
|
-
{item.thumbnailLabel && (
|
|
1431
|
-
<div className="image-gallery-thumbnail-label">
|
|
1432
|
-
{item.thumbnailLabel}
|
|
1433
|
-
</div>
|
|
1434
|
-
)}
|
|
1435
|
-
</span>
|
|
1436
|
-
);
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
render() {
|
|
1440
|
-
const { currentIndex, isFullscreen, modalFullscreen, isPlaying } =
|
|
1441
|
-
this.state;
|
|
1442
|
-
|
|
1443
|
-
const {
|
|
1444
|
-
additionalClass,
|
|
1445
|
-
disableThumbnailSwipe,
|
|
1446
|
-
indexSeparator, // deprecate soon, and allow custom render
|
|
1447
|
-
isRTL,
|
|
1448
|
-
items,
|
|
1449
|
-
thumbnailPosition,
|
|
1450
|
-
renderFullscreenButton,
|
|
1451
|
-
renderCustomControls,
|
|
1452
|
-
renderLeftNav,
|
|
1453
|
-
renderRightNav,
|
|
1454
|
-
showBullets,
|
|
1455
|
-
showFullscreenButton,
|
|
1456
|
-
showIndex,
|
|
1457
|
-
showThumbnails,
|
|
1458
|
-
showNav,
|
|
1459
|
-
showPlayButton,
|
|
1460
|
-
renderPlayPauseButton,
|
|
1461
|
-
} = this.props;
|
|
1462
|
-
|
|
1463
|
-
const thumbnailStyle = this.getThumbnailStyle();
|
|
1464
|
-
const { slides, thumbnails, bullets } = this.getSlideItems();
|
|
1465
|
-
const slideWrapperClass = clsx(
|
|
1466
|
-
"image-gallery-slide-wrapper",
|
|
1467
|
-
this.getThumbnailPositionClassName(thumbnailPosition),
|
|
1468
|
-
{ "image-gallery-rtl": isRTL }
|
|
1469
|
-
);
|
|
1470
|
-
|
|
1471
|
-
const slideWrapper = (
|
|
1472
|
-
<div ref={this.imageGallerySlideWrapper} className={slideWrapperClass}>
|
|
1473
|
-
{renderCustomControls && renderCustomControls()}
|
|
1474
|
-
{this.canSlide() ? (
|
|
1475
|
-
<React.Fragment>
|
|
1476
|
-
{showNav && (
|
|
1477
|
-
<React.Fragment>
|
|
1478
|
-
{renderLeftNav(this.slideLeft, !this.canSlideLeft())}
|
|
1479
|
-
{renderRightNav(this.slideRight, !this.canSlideRight())}
|
|
1480
|
-
</React.Fragment>
|
|
1481
|
-
)}
|
|
1482
|
-
<SwipeWrapper
|
|
1483
|
-
className="image-gallery-swipe"
|
|
1484
|
-
delta={0}
|
|
1485
|
-
onSwiping={this.handleSwiping}
|
|
1486
|
-
onSwiped={this.handleOnSwiped}
|
|
1487
|
-
>
|
|
1488
|
-
<div className="image-gallery-slides">{slides}</div>
|
|
1489
|
-
</SwipeWrapper>
|
|
1490
|
-
</React.Fragment>
|
|
1491
|
-
) : (
|
|
1492
|
-
<div className="image-gallery-slides">{slides}</div>
|
|
1493
|
-
)}
|
|
1494
|
-
{showPlayButton && renderPlayPauseButton(this.togglePlay, isPlaying)}
|
|
1495
|
-
{showBullets && (
|
|
1496
|
-
<div className="image-gallery-bullets">
|
|
1497
|
-
<div
|
|
1498
|
-
className="image-gallery-bullets-container"
|
|
1499
|
-
role="navigation"
|
|
1500
|
-
aria-label="Bullet Navigation"
|
|
1501
|
-
>
|
|
1502
|
-
{bullets}
|
|
1503
|
-
</div>
|
|
1504
|
-
</div>
|
|
1505
|
-
)}
|
|
1506
|
-
{showFullscreenButton &&
|
|
1507
|
-
renderFullscreenButton(this.toggleFullScreen, isFullscreen)}
|
|
1508
|
-
{showIndex && (
|
|
1509
|
-
<div className="image-gallery-index">
|
|
1510
|
-
<span className="image-gallery-index-current">
|
|
1511
|
-
{currentIndex + 1}
|
|
1512
|
-
</span>
|
|
1513
|
-
<span className="image-gallery-index-separator">
|
|
1514
|
-
{indexSeparator}
|
|
1515
|
-
</span>
|
|
1516
|
-
<span className="image-gallery-index-total">{items.length}</span>
|
|
1517
|
-
</div>
|
|
1518
|
-
)}
|
|
1519
|
-
</div>
|
|
1520
|
-
);
|
|
1521
|
-
|
|
1522
|
-
const igClass = clsx("image-gallery", additionalClass, {
|
|
1523
|
-
"fullscreen-modal": modalFullscreen,
|
|
1524
|
-
});
|
|
1525
|
-
const igContentClass = clsx(
|
|
1526
|
-
"image-gallery-content",
|
|
1527
|
-
this.getThumbnailPositionClassName(thumbnailPosition),
|
|
1528
|
-
{ fullscreen: isFullscreen }
|
|
1529
|
-
);
|
|
1530
|
-
const thumbnailWrapperClass = clsx(
|
|
1531
|
-
"image-gallery-thumbnails-wrapper",
|
|
1532
|
-
this.getThumbnailPositionClassName(thumbnailPosition),
|
|
1533
|
-
{ "thumbnails-wrapper-rtl": !this.isThumbnailVertical() && isRTL },
|
|
1534
|
-
{
|
|
1535
|
-
"thumbnails-swipe-horizontal":
|
|
1536
|
-
!this.isThumbnailVertical() && !disableThumbnailSwipe,
|
|
1537
|
-
},
|
|
1538
|
-
{
|
|
1539
|
-
"thumbnails-swipe-vertical":
|
|
1540
|
-
this.isThumbnailVertical() && !disableThumbnailSwipe,
|
|
1541
|
-
}
|
|
1542
|
-
);
|
|
1543
|
-
return (
|
|
1544
|
-
<div ref={this.imageGallery} className={igClass} aria-live="polite">
|
|
1545
|
-
<div className={igContentClass}>
|
|
1546
|
-
{(thumbnailPosition === "bottom" || thumbnailPosition === "right") &&
|
|
1547
|
-
slideWrapper}
|
|
1548
|
-
{showThumbnails && thumbnails.length > 0 ? (
|
|
1549
|
-
<SwipeWrapper
|
|
1550
|
-
className={thumbnailWrapperClass}
|
|
1551
|
-
delta={0}
|
|
1552
|
-
onSwiping={!disableThumbnailSwipe && this.handleThumbnailSwiping}
|
|
1553
|
-
onSwiped={!disableThumbnailSwipe && this.handleOnThumbnailSwiped}
|
|
1554
|
-
>
|
|
1555
|
-
<div
|
|
1556
|
-
className="image-gallery-thumbnails"
|
|
1557
|
-
ref={this.thumbnailsWrapper}
|
|
1558
|
-
style={this.getThumbnailBarHeight()}
|
|
1559
|
-
>
|
|
1560
|
-
<nav
|
|
1561
|
-
ref={this.thumbnails}
|
|
1562
|
-
className="image-gallery-thumbnails-container"
|
|
1563
|
-
style={thumbnailStyle}
|
|
1564
|
-
aria-label="Thumbnail Navigation"
|
|
1565
|
-
>
|
|
1566
|
-
{thumbnails}
|
|
1567
|
-
</nav>
|
|
1568
|
-
</div>
|
|
1569
|
-
</SwipeWrapper>
|
|
1570
|
-
) : null}
|
|
1571
|
-
{(thumbnailPosition === "top" || thumbnailPosition === "left") &&
|
|
1572
|
-
slideWrapper}
|
|
1573
|
-
</div>
|
|
1574
|
-
</div>
|
|
1575
|
-
);
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
ImageGallery.propTypes = {
|
|
1580
|
-
flickThreshold: number,
|
|
1581
|
-
items: arrayOf(
|
|
1582
|
-
shape({
|
|
1583
|
-
bulletClass: string,
|
|
1584
|
-
bulletOnClick: func,
|
|
1585
|
-
description: string,
|
|
1586
|
-
original: string,
|
|
1587
|
-
originalHeight: number,
|
|
1588
|
-
originalWidth: number,
|
|
1589
|
-
loading: string,
|
|
1590
|
-
thumbnailHeight: number,
|
|
1591
|
-
thumbnailWidth: number,
|
|
1592
|
-
thumbnailLoading: string,
|
|
1593
|
-
fullscreen: string,
|
|
1594
|
-
originalAlt: string,
|
|
1595
|
-
originalTitle: string,
|
|
1596
|
-
thumbnail: string,
|
|
1597
|
-
thumbnailAlt: string,
|
|
1598
|
-
thumbnailLabel: string,
|
|
1599
|
-
thumbnailTitle: string,
|
|
1600
|
-
originalClass: string,
|
|
1601
|
-
thumbnailClass: string,
|
|
1602
|
-
renderItem: func,
|
|
1603
|
-
renderThumbInner: func,
|
|
1604
|
-
imageSet: imageSetType,
|
|
1605
|
-
srcSet: string,
|
|
1606
|
-
sizes: string,
|
|
1607
|
-
})
|
|
1608
|
-
).isRequired,
|
|
1609
|
-
showNav: bool,
|
|
1610
|
-
autoPlay: bool,
|
|
1611
|
-
lazyLoad: bool,
|
|
1612
|
-
infinite: bool,
|
|
1613
|
-
showIndex: bool,
|
|
1614
|
-
showBullets: bool,
|
|
1615
|
-
showThumbnails: bool,
|
|
1616
|
-
showPlayButton: bool,
|
|
1617
|
-
showFullscreenButton: bool,
|
|
1618
|
-
disableThumbnailScroll: bool,
|
|
1619
|
-
disableKeyDown: bool,
|
|
1620
|
-
disableSwipe: bool,
|
|
1621
|
-
disableThumbnailSwipe: bool,
|
|
1622
|
-
useBrowserFullscreen: bool,
|
|
1623
|
-
onErrorImageURL: string,
|
|
1624
|
-
indexSeparator: string,
|
|
1625
|
-
thumbnailPosition: oneOf(["top", "bottom", "left", "right"]),
|
|
1626
|
-
startIndex: number,
|
|
1627
|
-
slideDuration: number,
|
|
1628
|
-
slideInterval: number,
|
|
1629
|
-
slideOnThumbnailOver: bool,
|
|
1630
|
-
swipeThreshold: number,
|
|
1631
|
-
swipingTransitionDuration: number,
|
|
1632
|
-
swipingThumbnailTransitionDuration: number,
|
|
1633
|
-
onSlide: func,
|
|
1634
|
-
onBeforeSlide: func,
|
|
1635
|
-
onScreenChange: func,
|
|
1636
|
-
onPause: func,
|
|
1637
|
-
onPlay: func,
|
|
1638
|
-
onClick: func,
|
|
1639
|
-
onImageLoad: func,
|
|
1640
|
-
onImageError: func,
|
|
1641
|
-
onTouchMove: func,
|
|
1642
|
-
onTouchEnd: func,
|
|
1643
|
-
onTouchStart: func,
|
|
1644
|
-
onMouseOver: func,
|
|
1645
|
-
onMouseLeave: func,
|
|
1646
|
-
onBulletClick: func,
|
|
1647
|
-
onThumbnailError: func,
|
|
1648
|
-
onThumbnailClick: func,
|
|
1649
|
-
renderCustomControls: func,
|
|
1650
|
-
renderLeftNav: func,
|
|
1651
|
-
renderRightNav: func,
|
|
1652
|
-
renderPlayPauseButton: func,
|
|
1653
|
-
renderFullscreenButton: func,
|
|
1654
|
-
renderItem: func,
|
|
1655
|
-
renderThumbInner: func,
|
|
1656
|
-
stopPropagation: bool,
|
|
1657
|
-
additionalClass: string,
|
|
1658
|
-
useTranslate3D: bool,
|
|
1659
|
-
isRTL: bool,
|
|
1660
|
-
useWindowKeyDown: bool,
|
|
1661
|
-
};
|
|
1662
|
-
|
|
1663
|
-
ImageGallery.defaultProps = {
|
|
1664
|
-
onErrorImageURL: "",
|
|
1665
|
-
additionalClass: "",
|
|
1666
|
-
showNav: true,
|
|
1667
|
-
autoPlay: false,
|
|
1668
|
-
lazyLoad: false,
|
|
1669
|
-
infinite: true,
|
|
1670
|
-
showIndex: false,
|
|
1671
|
-
showBullets: false,
|
|
1672
|
-
showThumbnails: true,
|
|
1673
|
-
showPlayButton: true,
|
|
1674
|
-
showFullscreenButton: true,
|
|
1675
|
-
disableThumbnailScroll: false,
|
|
1676
|
-
disableKeyDown: false,
|
|
1677
|
-
disableSwipe: false,
|
|
1678
|
-
disableThumbnailSwipe: false,
|
|
1679
|
-
useTranslate3D: true,
|
|
1680
|
-
isRTL: false,
|
|
1681
|
-
useBrowserFullscreen: true,
|
|
1682
|
-
flickThreshold: 0.4,
|
|
1683
|
-
stopPropagation: false,
|
|
1684
|
-
indexSeparator: " / ",
|
|
1685
|
-
thumbnailPosition: "bottom",
|
|
1686
|
-
startIndex: 0,
|
|
1687
|
-
slideDuration: 450,
|
|
1688
|
-
swipingTransitionDuration: 0,
|
|
1689
|
-
swipingThumbnailTransitionDuration: 0,
|
|
1690
|
-
onSlide: null,
|
|
1691
|
-
onBeforeSlide: null,
|
|
1692
|
-
onScreenChange: null,
|
|
1693
|
-
onPause: null,
|
|
1694
|
-
onPlay: null,
|
|
1695
|
-
onClick: null,
|
|
1696
|
-
onImageLoad: null,
|
|
1697
|
-
onImageError: null,
|
|
1698
|
-
onTouchMove: null,
|
|
1699
|
-
onTouchEnd: null,
|
|
1700
|
-
onTouchStart: null,
|
|
1701
|
-
onMouseOver: null,
|
|
1702
|
-
onMouseLeave: null,
|
|
1703
|
-
onBulletClick: null,
|
|
1704
|
-
onThumbnailError: null,
|
|
1705
|
-
onThumbnailClick: null,
|
|
1706
|
-
renderCustomControls: null,
|
|
1707
|
-
renderThumbInner: null,
|
|
1708
|
-
renderItem: null,
|
|
1709
|
-
slideInterval: 3000,
|
|
1710
|
-
slideOnThumbnailOver: false,
|
|
1711
|
-
swipeThreshold: 30,
|
|
1712
|
-
renderLeftNav: (onClick, disabled) => (
|
|
1713
|
-
<LeftNav onClick={onClick} disabled={disabled} />
|
|
1714
|
-
),
|
|
1715
|
-
renderRightNav: (onClick, disabled) => (
|
|
1716
|
-
<RightNav onClick={onClick} disabled={disabled} />
|
|
1717
|
-
),
|
|
1718
|
-
renderPlayPauseButton: (onClick, isPlaying) => (
|
|
1719
|
-
<PlayPause onClick={onClick} isPlaying={isPlaying} />
|
|
1720
|
-
),
|
|
1721
|
-
renderFullscreenButton: (onClick, isFullscreen) => (
|
|
1722
|
-
<Fullscreen onClick={onClick} isFullscreen={isFullscreen} />
|
|
1723
|
-
),
|
|
1724
|
-
useWindowKeyDown: true,
|
|
1725
|
-
};
|
|
1726
|
-
|
|
1727
|
-
export default ImageGallery;
|