diff --git a/.eslintrc.json b/.eslintrc.json
index e5f74dbb..8df87e5c 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -44,4 +44,4 @@
}
}
]
-}
+}
\ No newline at end of file
diff --git a/example/App.jsx b/example/App.jsx
index 1139c353..80c2458f 100644
--- a/example/App.jsx
+++ b/example/App.jsx
@@ -19,6 +19,7 @@ class App extends React.Component {
showPlayButton: true,
showGalleryPlayButton: true,
showNav: true,
+ slideVertically: false,
isRTL: false,
slideDuration: 450,
slideInterval: 2000,
@@ -201,6 +202,7 @@ class App extends React.Component {
slideOnThumbnailOver={this.state.slideOnThumbnailOver}
additionalClass="app-image-gallery"
useWindowKeyDown={this.state.useWindowKeyDown}
+ slideVertically={this.state.slideVertically}
/>
@@ -333,6 +335,18 @@ class App extends React.Component {
/>
+
+
+
+
2) {
if (currentIndex === 0 && index === totalSlides) {
// make the last slide the slide before the first
// if it is RTL the base line should be reversed
- translateX = -100 * (isRTL ? -1 : 1) + currentSlideOffset;
+ translateValue = -100 * (isRTL ? -1 : 1) + currentSlideOffset;
} else if (currentIndex === totalSlides && index === 0) {
// make the first slide the slide after the last
// if it is RTL the base line should be reversed
- translateX = 100 * (isRTL ? -1 : 1) + currentSlideOffset;
+ translateValue = 100 * (isRTL ? -1 : 1) + currentSlideOffset;
}
}
// Special case when there are only 2 items with infinite on
if (infinite && items.length === 2) {
- translateX = this.getTranslateXForTwoSlide(index);
+ translateValue = this.getTranslateXForTwoSlide(index);
}
- let translate = `translate(${translateX}%, 0)`;
+ let translate = slideVertically
+ ? `translate(0, ${translateValue}%)`
+ : `translate(${translateValue}%, 0)`;
if (useTranslate3D) {
- translate = `translate3d(${translateX}%, 0, 0)`;
+ translate = slideVertically
+ ? `translate3d(0, ${translateValue}%, 0)`
+ : `translate3d(${translateValue}%, 0, 0)`;
}
// don't show some slides while transitioning to avoid background transitions
@@ -781,16 +788,23 @@ class ImageGallery extends React.Component {
}
handleSwiping({ event, absX, dir }) {
- const { disableSwipe, stopPropagation } = this.props;
- const { galleryWidth, isTransitioning, swipingUpDown, swipingLeftRight } =
- this.state;
+ const { disableSwipe, stopPropagation, swipingTransitionDuration } =
+ this.props;
+ const {
+ galleryWidth,
+ isTransitioning,
+ swipingUpDown,
+ swipingLeftRight,
+ } = this.state;
+
+ const { slideVertically } = this.props;
// if the initial swiping is up/down prevent moving the slides until swipe ends
if ((dir === UP || dir === DOWN || swipingUpDown) && !swipingLeftRight) {
if (!swipingUpDown) {
this.setState({ swipingUpDown: true });
}
- return;
+ if (!slideVertically) return;
}
if ((dir === LEFT || dir === RIGHT) && !swipingLeftRight) {
@@ -799,15 +813,28 @@ class ImageGallery extends React.Component {
if (disableSwipe) return;
- const { swipingTransitionDuration } = this.props;
if (stopPropagation) {
event.preventDefault();
}
if (!isTransitioning) {
- const side = dir === RIGHT ? 1 : -1;
+ const isSwipeLeftOrRight = dir === LEFT || dir === RIGHT;
+ const isSwipeTopOrDown = dir === UP || dir === DOWN;
+
+ if (isSwipeLeftOrRight && slideVertically) return;
+ if (isSwipeTopOrDown && !slideVertically) return;
+
+ const sides = {
+ [LEFT]: -1,
+ [RIGHT]: 1,
+ [UP]: -1,
+ [DOWN]: 1,
+ };
+
+ const side = sides[dir];
let currentSlideOffset = (absX / galleryWidth) * 100;
+
if (Math.abs(currentSlideOffset) >= 100) {
currentSlideOffset = 100;
}
@@ -953,6 +980,7 @@ class ImageGallery extends React.Component {
handleOnSwiped({ event, dir, velocity }) {
const { disableSwipe, stopPropagation, flickThreshold } = this.props;
+ const { slideVertically } = this.props;
if (disableSwipe) return;
@@ -961,17 +989,24 @@ class ImageGallery extends React.Component {
this.resetSwipingDirection();
// if it is RTL the direction is reversed
- const swipeDirection = (dir === LEFT ? 1 : -1) * (isRTL ? -1 : 1);
+ let swipeDirection = (dir === LEFT ? 1 : -1) * (isRTL ? -1 : 1);
+ if (slideVertically) swipeDirection = dir === UP ? 1 : -1;
+
const isSwipeUpOrDown = dir === UP || dir === DOWN;
+ const isSwipeLeftOrRight = dir === LEFT || dir === RIGHT;
const isLeftRightFlick = velocity > flickThreshold && !isSwipeUpOrDown;
- this.handleOnSwipedTo(swipeDirection, isLeftRightFlick);
+ const isTopDownFlick = velocity > flickThreshold && !isSwipeLeftOrRight;
+
+ const isFlick = slideVertically ? isTopDownFlick : isLeftRightFlick;
+
+ this.handleOnSwipedTo(swipeDirection, isFlick);
}
- handleOnSwipedTo(swipeDirection, isLeftRightFlick) {
+ handleOnSwipedTo(swipeDirection, isFlick) {
const { currentIndex, isTransitioning } = this.state;
let slideTo = currentIndex;
- if ((this.sufficientSwipe() || isLeftRightFlick) && !isTransitioning) {
+ if ((this.sufficientSwipe() || isFlick) && !isTransitioning) {
// slideto the next/prev slide
slideTo += swipeDirection;
}
@@ -1437,8 +1472,14 @@ class ImageGallery extends React.Component {
}
render() {
- const { currentIndex, isFullscreen, modalFullscreen, isPlaying } =
- this.state;
+ const {
+ currentIndex,
+ isFullscreen,
+ modalFullscreen,
+ isPlaying,
+ } = this.state;
+
+ const { slideVertically } = this.props;
const {
additionalClass,
@@ -1451,6 +1492,8 @@ class ImageGallery extends React.Component {
renderCustomControls,
renderLeftNav,
renderRightNav,
+ renderTopNav,
+ renderBottomNav,
showBullets,
showFullscreenButton,
showIndex,
@@ -1475,8 +1518,12 @@ class ImageGallery extends React.Component {
{showNav && (
- {renderLeftNav(this.slideLeft, !this.canSlideLeft())}
- {renderRightNav(this.slideRight, !this.canSlideRight())}
+ {slideVertically
+ ? renderTopNav(this.slideLeft, !this.canSlideLeft())
+ : renderLeftNav(this.slideLeft, !this.canSlideLeft())}
+ {slideVertically
+ ? renderBottomNav(this.slideRight, !this.canSlideRight())
+ : renderRightNav(this.slideRight, !this.canSlideRight())}
)}
- {slides}
+
+ {slides}
+
) : (
@@ -1649,6 +1702,8 @@ ImageGallery.propTypes = {
renderCustomControls: func,
renderLeftNav: func,
renderRightNav: func,
+ renderTopNav: func,
+ renderBottomNav: func,
renderPlayPauseButton: func,
renderFullscreenButton: func,
renderItem: func,
@@ -1658,6 +1713,7 @@ ImageGallery.propTypes = {
useTranslate3D: bool,
isRTL: bool,
useWindowKeyDown: bool,
+ slideVertically: bool,
};
ImageGallery.defaultProps = {
@@ -1709,12 +1765,19 @@ ImageGallery.defaultProps = {
slideInterval: 3000,
slideOnThumbnailOver: false,
swipeThreshold: 30,
+ slideVertically: false,
renderLeftNav: (onClick, disabled) => (
),
renderRightNav: (onClick, disabled) => (
),
+ renderTopNav: (onClick, disabled) => (
+
+ ),
+ renderBottomNav: (onClick, disabled) => (
+
+ ),
renderPlayPauseButton: (onClick, isPlaying) => (
),
diff --git a/src/components/SVG.jsx b/src/components/SVG.jsx
index 58aa9bcb..9cc59c4f 100644
--- a/src/components/SVG.jsx
+++ b/src/components/SVG.jsx
@@ -3,6 +3,8 @@ import { number, oneOf, string } from "prop-types";
const left = ;
const right = ;
+const top = ;
+const bottom = ;
const maximize = (
);
@@ -20,6 +22,8 @@ const pause = (
const iconMapper = {
left,
right,
+ top,
+ bottom,
maximize,
minimize,
play,
@@ -52,8 +56,16 @@ const SVG = (props) => {
SVG.propTypes = {
strokeWidth: number,
viewBox: string,
- icon: oneOf(["left", "right", "maximize", "minimize", "play", "pause"])
- .isRequired,
+ icon: oneOf([
+ "left",
+ "right",
+ "top",
+ "bottom",
+ "maximize",
+ "minimize",
+ "play",
+ "pause",
+ ]).isRequired,
};
export default SVG;
diff --git a/src/components/controls/BottomNav.jsx b/src/components/controls/BottomNav.jsx
new file mode 100644
index 00000000..e9482910
--- /dev/null
+++ b/src/components/controls/BottomNav.jsx
@@ -0,0 +1,26 @@
+import React from "react";
+import { bool, func } from "prop-types";
+import SVG from "src/components/SVG";
+
+const BottomNav = React.memo(({ disabled, onClick }) => {
+ return (
+
+ );
+});
+
+BottomNav.displayName = "BottomNav";
+
+BottomNav.propTypes = {
+ disabled: bool.isRequired,
+ onClick: func.isRequired,
+};
+
+export default BottomNav;
diff --git a/src/components/controls/TopNav.jsx b/src/components/controls/TopNav.jsx
new file mode 100644
index 00000000..c8142d2b
--- /dev/null
+++ b/src/components/controls/TopNav.jsx
@@ -0,0 +1,26 @@
+import React from "react";
+import { bool, func } from "prop-types";
+import SVG from "src/components/SVG";
+
+const TopNav = React.memo(({ disabled, onClick }) => {
+ return (
+
+ );
+});
+
+TopNav.displayName = "TopNav";
+
+TopNav.propTypes = {
+ disabled: bool.isRequired,
+ onClick: func.isRequired,
+};
+
+export default TopNav;
diff --git a/styles/scss/image-gallery.scss b/styles/scss/image-gallery.scss
index 445c334a..3eaf8180 100644
--- a/styles/scss/image-gallery.scss
+++ b/styles/scss/image-gallery.scss
@@ -86,6 +86,46 @@ $ig-shadow: 0 2px 2px lighten($ig-black, 10%);
left: 0;
}
+.image-gallery-top-nav,
+.image-gallery-bottom-nav {
+ padding: 10px 10px;
+ left: 50%;
+ transform: translateX(-50%);
+
+ .image-gallery-svg {
+ height: 120px;
+ width: 60px;
+ }
+
+ @media (max-width: $ig-small-screen) {
+ .image-gallery-svg {
+ height: 72px;
+ width: 36px;
+ }
+ }
+
+ @media (max-width: $ig-xsmall-screen) {
+ .image-gallery-svg {
+ height: 48px;
+ width: 24px;
+ }
+ }
+
+ &[disabled] {
+ cursor: disabled;
+ opacity: .6;
+ pointer-events: none;
+ }
+}
+
+.image-gallery-top-nav {
+ top: 0;
+}
+
+.image-gallery-bottom-nav {
+ bottom: 0;
+}
+
.image-gallery-left-nav,
.image-gallery-right-nav {
padding: 50px 10px;