@santigp258/react-simple-lightbox-video-image
Version:
A simple and customizable react lightbox component that support video and image. Also enables swipe and doble tap gesture. This component can be compatible with Next js
663 lines (595 loc) • 21 kB
JavaScript
import React, { useEffect, useState, useRef, useCallback } from 'react';
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
var DefaultStyle = function DefaultStyle() {
return React.createElement("style", null, "\n @keyframes react_simple_image_video_spinner {\n 0% {\n transform: translate3d(-50%, -50%, 0) rotate(0deg);\n }\n 100% {\n transform: translate3d(-50%, -50%, 0) rotate(360deg);\n }\n }\n \n .react-lightbox-component-icon-hover {\n background-color: rgba(119, 119, 119, .1);\n border-radius: 100%;\n }\n .react-lightbox-component-icon-hover:hover {\n background-color: rgba(119, 119, 119, .6);\n \n }\n \n .react-lightbox-component-icon-right {\n right: 0px;\n }\n .react-lightbox-component-icon-left {\n left: 10px;\n }\n @media (min-width: 768px) {\n .react-lightbox-component-icon-right {\n right: 10px;\n }\n .react-lightbox-component-icon-left {\n left: 10px;\n }\n }\n \n ");
};
var MIN_SCALE = 1;
var MAX_SCALE = 4;
var SETTLE_RANGE = 0.001;
var ADDITIONAL_LIMIT = 0.2;
var DOUBLE_TAP_THRESHOLD = 300;
var ANIMATION_SPEED = 0.04;
var RESET_ANIMATION_SPEED = 0.08;
var INITIAL_X = 0;
var INITIAL_Y = 0;
var INITIAL_SCALE = 1;
var settle = function settle(val, target, range) {
var lowerRange = val > target - range && val < target;
var upperRange = val < target + range && val > target;
return lowerRange || upperRange ? target : val;
};
var getPointFromTouch = function getPointFromTouch(touch) {
return {
x: touch.clientX,
y: touch.clientY
};
};
var getDistanceBetweenPoints = function getDistanceBetweenPoints(pointA, pointB) {
return Math.sqrt(Math.pow(pointA.y - pointB.y, 2) + Math.pow(pointA.x - pointB.x, 2));
};
var between = function between(min, max, value) {
return Math.min(max, Math.max(min, value));
};
var useHiddenScroll = function useHiddenScroll(preventHidden) {
if (preventHidden === void 0) {
preventHidden = false;
}
useEffect(function () {
var body = document.querySelector('body');
if (body && !preventHidden) {
body.style.touchAction = 'none';
body.style.overflow = 'hidden';
}
return function () {
if (body) {
body.style.touchAction = '';
body.style.overflow = '';
}
};
}, []);
};
var ChevronLeft = function ChevronLeft() {
return React.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
height: "48px",
className: "react-lightbox-component-icon-hover",
viewBox: "0 0 24 24",
width: "48px",
fill: "#FFFFFF"
}, React.createElement("path", {
d: "M0 0h24v24H0z",
fill: "none"
}), React.createElement("path", {
d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"
}));
};
var ChevronRight = function ChevronRight() {
return React.createElement("svg", {
className: "react-lightbox-component-icon-hover ",
xmlns: "http://www.w3.org/2000/svg",
height: "48px",
viewBox: "0 0 24 24",
width: "48px",
fill: "#FFFFFF"
}, React.createElement("path", {
d: "M0 0h24v24H0z",
fill: "none"
}), React.createElement("path", {
d: "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"
}));
};
var ChevronClose = function ChevronClose() {
return React.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
height: "36px",
viewBox: "0 0 24 24",
width: "36px",
fill: "#FFFFFF"
}, React.createElement("path", {
d: "M0 0h24v24H0z",
fill: "none"
}), React.createElement("path", {
d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
}));
};
var _excluded = ["variant"];
var Chevron = function Chevron(_ref) {
var variant = _ref.variant,
props = _objectWithoutPropertiesLoose(_ref, _excluded);
return React.createElement("div", Object.assign({}, props), variant === 'right' ? React.createElement(ChevronRight, null) : null, variant === 'left' ? React.createElement(ChevronLeft, null) : null, variant === 'close' ? React.createElement(ChevronClose, null) : null);
};
var Resource = function Resource(_ref) {
var resource = _ref.resource,
CustomImage = _ref.CustomImage,
CustomVideo = _ref.CustomVideo,
onLoad = _ref.onLoad,
x = _ref.x,
y = _ref.y,
scale = _ref.scale,
imageClassname = _ref.imageClassname,
imageContainerStyle = _ref.imageContainerStyle,
imageContainerClassName = _ref.imageContainerClassName,
imageStyle = _ref.imageStyle,
frameClassname = _ref.frameClassname,
frameStyle = _ref.frameStyle;
if (resource.type === 'photo') {
return React.createElement("div", {
style: _extends({
pointerEvents: scale === 1 ? 'auto' : 'none',
transform: "translate(" + x + "px, " + y + "px) scale(" + scale + ")",
transition: 'transform 0.5s ease-out'
}, imageContainerStyle),
className: imageContainerClassName
}, CustomImage ? React.createElement(CustomImage, {
title: resource.title,
className: imageClassname,
style: imageStyle,
alt: resource.altTag,
src: resource.url,
onLoad: onLoad
}) : React.createElement("img", {
title: resource.title,
className: imageClassname,
style: imageStyle,
alt: resource.altTag,
src: resource.url,
onLoad: onLoad
}));
}
if (resource.type === 'video') {
return React.createElement(React.Fragment, null, CustomVideo ? React.createElement(CustomVideo, {
onLoad: onLoad,
style: _extends({
pointerEvents: scale === 1 ? 'auto' : 'none',
maxWidth: '100%',
maxHeight: '100%',
width: '100%',
height: '100%',
transform: "translate(" + x + "px, " + y + "px)",
transition: 'transform 0.5s ease-out'
}, frameStyle),
className: frameClassname,
alt: resource.altTag,
src: resource.url,
title: resource.title
}) : React.createElement("iframe", {
width: "560",
height: "315",
src: resource.url,
frameBorder: "0",
allow: "autoplay; encrypted-media",
title: resource.title,
allowFullScreen: true,
className: frameClassname,
style: _extends({
pointerEvents: scale === 1 ? 'auto' : 'none',
maxWidth: '100%',
maxHeight: '100%',
width: '100%',
height: '100%',
transform: "translate(" + x + "px, " + y + "px)",
transition: 'transform 0.5s ease-out'
}, frameStyle),
onLoad: onLoad
}));
}
return null;
};
/**
* A simple and customizable react lightbox component that support video and image. Also enables swipe and doble tap gesture
*/
var ReactSimpleImageVideoLightbox = function ReactSimpleImageVideoLightbox(_ref) {
var data = _ref.data,
_ref$startIndex = _ref.startIndex,
startIndex = _ref$startIndex === void 0 ? 0 : _ref$startIndex,
_ref$showResourceCoun = _ref.showResourceCount,
showResourceCount = _ref$showResourceCoun === void 0 ? true : _ref$showResourceCoun,
_ref$backdropBg = _ref.backdropBg,
backdropBg = _ref$backdropBg === void 0 ? 'rgba(0,0,0,.5)' : _ref$backdropBg,
preventHidden = _ref.preventHidden,
onCloseCallback = _ref.onCloseCallback,
onNavigationCallback = _ref.onNavigationCallback,
CustomImage = _ref.CustomImage,
CustomVideo = _ref.CustomVideo,
CustomLoader = _ref.CustomLoader,
CustomResourceCount = _ref.CustomResourceCount,
CustomChevronRight = _ref.CustomChevronRight,
CustomChevronLeft = _ref.CustomChevronLeft,
CustomCloseIcon = _ref.CustomCloseIcon,
imageContainerStyle = _ref.imageContainerStyle,
containerStyle = _ref.containerStyle,
containerClassName = _ref.containerClassName,
imageContainerClassName = _ref.imageContainerClassName,
resourceContainerStyle = _ref.resourceContainerStyle,
resourceContainerClassName = _ref.resourceContainerClassName,
imageClassname = _ref.imageClassname,
imageStyle = _ref.imageStyle,
resourceCountClassname = _ref.resourceCountClassname,
resourceCountStyle = _ref.resourceCountStyle,
frameClassname = _ref.frameClassname,
frameStyle = _ref.frameStyle;
var _useState = useState({
x: INITIAL_X,
y: INITIAL_Y,
width: window.innerWidth,
height: window.innerHeight,
scale: INITIAL_SCALE
}),
state = _useState[0],
setState = _useState[1];
/* STATES */
var animation = useRef(0);
var _useState2 = useState(0),
lastTouchEnd = _useState2[0],
setLastTouchEnd = _useState2[1];
var _useState3 = useState(startIndex),
index = _useState3[0],
setIndex = _useState3[1];
var _useState4 = useState(0),
swipeStartX = _useState4[0],
setSwipeStartX = _useState4[1];
var _useState5 = useState(0),
swipeStartY = _useState5[0],
setSwipeStartY = _useState5[1];
var _useState6 = useState(0),
lastDistance = _useState6[0],
setLastDistance = _useState6[1];
var _useState7 = useState(true),
loading = _useState7[0],
setLoading = _useState7[1];
var _useState8 = useState(false),
swiping = _useState8[0],
setSwiping = _useState8[1];
var _useState9 = useState(false),
isDoubleTap = _useState9[0],
setIsDoubleTap = _useState9[1];
useHiddenScroll(preventHidden);
var zoomTo = function zoomTo(scaleZoom) {
var frame = function frame() {
if (state.scale === scaleZoom) return;
var distance = scaleZoom - state.scale;
var targetScale = state.scale + ANIMATION_SPEED * distance;
zoom(settle(targetScale, state.scale, SETTLE_RANGE));
animation.current = requestAnimationFrame(frame);
};
animation.current = requestAnimationFrame(frame);
};
var reset = useCallback(function () {
var newScaleValue = state.scale;
var x = state.x;
var y = state.y;
var frame = function frame() {
if (newScaleValue === INITIAL_SCALE && x === INITIAL_X && y === INITIAL_Y) return;
var scaleDelta = INITIAL_SCALE - state.scale;
var targetScale = settle(RESET_ANIMATION_SPEED * scaleDelta + state.scale, INITIAL_SCALE, SETTLE_RANGE);
var nextWidth = window.innerWidth * targetScale;
var nextHeight = window.innerHeight * targetScale;
setState({
width: nextWidth,
height: nextHeight,
x: INITIAL_X,
y: INITIAL_Y,
scale: targetScale
});
/* State not updating inside recursion solved with this:*/
x = INITIAL_X;
y = INITIAL_Y;
newScaleValue = targetScale;
animation.current = requestAnimationFrame(frame);
};
animation.current = requestAnimationFrame(frame);
}, [state]);
var handleTouchStart = function handleTouchStart(event) {
animation.current && cancelAnimationFrame(animation.current);
if (event.touches.length === 1) {
/* prevent unnecesary re render */
if (isDoubleTap) setIsDoubleTap(false);
handleTapStart(event);
}
if (event.touches.length === 2) {
setIsDoubleTap(true);
handlePinchStart(event);
}
};
var handleTouchMove = function handleTouchMove(event) {
if (event.touches.length === 1) handlePanMove(event);
if (event.touches.length === 2) handlePinchMove(event);
};
var handleTouchEnd = function handleTouchEnd(event) {
if (event.touches.length > 0) return null;
if (state.scale > MAX_SCALE) return zoomTo(MAX_SCALE);
if (state.scale < MIN_SCALE) return zoomTo(MIN_SCALE);
if (lastTouchEnd && lastTouchEnd + DOUBLE_TAP_THRESHOLD > event.timeStamp) {
reset();
}
if (isDoubleTap) {
return setState({
x: INITIAL_X,
y: INITIAL_Y,
width: window.innerWidth,
height: window.innerHeight,
scale: INITIAL_SCALE
});
}
if (swiping && state.scale === 1) {
handleSwipe(event);
}
setLastTouchEnd(event.timeStamp);
};
var handleSwipe = function handleSwipe(event) {
var swipeDelta = event.changedTouches[0].clientX - swipeStartX;
if (swipeDelta < -(window.innerWidth / 3)) {
swipeRight();
} else if (swipeDelta > window.innerWidth / 3) {
swipeLeft();
} else {
reset();
}
};
var swipeLeft = useCallback(function () {
var currentIndex = index;
if (currentIndex > 0) {
setTimeout(function () {
setState(function (state) {
return _extends({}, state, {
x: INITIAL_X
});
});
setIndex(currentIndex - 1);
setSwiping(false);
setLoading(true);
if (onNavigationCallback) onNavigationCallback(currentIndex - 1);
}, 500);
} else {
reset();
}
}, [index, reset, onNavigationCallback]);
var swipeRight = useCallback(function () {
var currentIndex = index;
if (currentIndex < data.length - 1) {
setTimeout(function () {
setState(function (state) {
return _extends({}, state, {
x: INITIAL_X
});
});
setIndex(currentIndex + 1);
setSwiping(false);
setLoading(true);
if (onNavigationCallback) onNavigationCallback(currentIndex - 1);
}, 500);
} else {
reset();
}
}, [index, reset, onNavigationCallback, data]);
useEffect(function () {
var handleKeyDown = function handleKeyDown(e) {
if (e.code === 'ArrowLeft') {
swipeLeft();
}
if (e.code === 'ArrowRight') {
swipeRight();
}
};
window.addEventListener('keydown', handleKeyDown);
return function () {
window.removeEventListener('keydown', handleKeyDown);
};
}, [swipeLeft, swipeRight]);
var handleTapStart = function handleTapStart(event) {
setSwipeStartX(event.touches[0].clientX);
setSwipeStartY(event.touches[0].clientY);
if (state.scale === 1) {
setSwiping(true);
}
};
var handlePanMove = function handlePanMove(event) {
if (state.scale === 1) {
setState(function (state) {
return _extends({}, state, {
x: event.touches[0].clientX - swipeStartX
});
});
} else {
event.preventDefault();
setState(function (state) {
return _extends({}, state, {
x: event.touches[0].clientX - swipeStartX,
y: event.touches[0].clientY - swipeStartY
});
});
}
};
var handlePinchStart = function handlePinchStart(event) {
var pointA = getPointFromTouch(event.touches[0]);
var pointB = getPointFromTouch(event.touches[1]);
setLastDistance(getDistanceBetweenPoints(pointA, pointB));
};
var handlePinchMove = function handlePinchMove(event) {
event.preventDefault();
var pointA = getPointFromTouch(event.touches[0]);
var pointB = getPointFromTouch(event.touches[1]);
var distance = getDistanceBetweenPoints(pointA, pointB);
var scaleZoom = between(MIN_SCALE - ADDITIONAL_LIMIT, MAX_SCALE + ADDITIONAL_LIMIT, state.scale * (distance / lastDistance));
zoom(scaleZoom);
setLastDistance(distance);
};
var zoom = function zoom(scale) {
var nextWidth = window.innerWidth * scale;
var nextHeight = window.innerHeight * scale;
setState(function (state) {
return _extends({}, state, {
width: nextWidth,
height: nextHeight,
scale: scale
});
});
};
return React.createElement(React.Fragment, null, React.createElement(DefaultStyle, null), React.createElement("div", {
onTouchStart: handleTouchStart,
onTouchMove: handleTouchMove,
onTouchEnd: handleTouchEnd,
className: containerClassName,
style: containerStyle ? containerStyle : {
top: '0px',
left: '0px',
zIndex: 999,
overflow: 'hidden',
position: 'fixed',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
height: '100%',
width: '100%',
backgroundColor: backdropBg
}
}, showResourceCount ? React.createElement(React.Fragment, null, CustomResourceCount ? React.createElement(CustomResourceCount, {
index: index,
dataQuantity: data.length
}) : React.createElement("div", {
className: resourceCountClassname,
style: resourceCountStyle ? resourceCountStyle : {
position: 'absolute',
top: '10px',
left: '10px',
padding: '15px',
color: 'white',
fontWeight: 'bold'
}
}, React.createElement("span", null, index + 1), " / ", React.createElement("span", null, data.length))) : null, CustomCloseIcon ? React.createElement(CustomCloseIcon, {
variant: "close",
style: {
position: 'absolute',
top: '10px',
zIndex: 50,
right: '10px',
padding: '5px',
color: '#FFFFFF',
cursor: 'pointer'
},
className: "react-lightbox-component-icon-hover",
onClick: onCloseCallback
}) : React.createElement(Chevron, {
variant: "close",
style: {
position: 'absolute',
top: '10px',
zIndex: 50,
right: '10px',
padding: '5px',
color: '#FFFFFF',
cursor: 'pointer'
},
className: "react-lightbox-component-icon-hover",
onClick: onCloseCallback
}), index + 1 !== 1 ? React.createElement(React.Fragment, null, CustomChevronLeft ? React.createElement(CustomChevronLeft, {
variant: "left",
style: {
position: 'absolute',
zIndex: 1,
color: '#FFFFFF',
cursor: 'pointer'
},
onClick: swipeLeft,
className: "react-lightbox-component-icon-left"
}) : React.createElement(Chevron, {
variant: "left",
style: {
position: 'absolute',
zIndex: 1,
color: '#FFFFFF',
cursor: 'pointer'
},
onClick: swipeLeft,
className: "react-lightbox-component-icon-left"
})) : null, index + 1 !== data.length ? React.createElement(React.Fragment, null, CustomChevronRight ? React.createElement(CustomChevronRight, {
variant: "right",
style: {
position: 'absolute',
zIndex: 1,
color: '#FFFFFF',
cursor: 'pointer'
},
onClick: swipeRight,
className: "react-lightbox-component-icon-right"
}) : React.createElement(Chevron, {
variant: "right",
style: {
position: 'absolute',
zIndex: 1,
color: '#FFFFFF',
cursor: 'pointer'
},
onClick: swipeRight,
className: "react-lightbox-component-icon-right"
})) : null, loading ? React.createElement(React.Fragment, null, CustomLoader ? React.createElement(CustomLoader, null) : React.createElement("div", {
style: {
margin: 'auto',
position: 'fixed'
}
}, React.createElement("div", {
style: {
animation: '1.0s linear infinite react_simple_image_video_spinner',
border: 'solid 5px #ffffff',
borderBottomColor: '#cfd0d1',
borderRadius: '50%',
height: 30,
width: 30,
position: 'fixed',
transform: 'translate3d(-50%, -50%, 0)'
}
}))) : null, React.createElement("div", {
style: resourceContainerStyle,
className: resourceContainerClassName
}, data.map(function (resource, i) {
if (index === i) {
return React.createElement(Resource, {
key: i,
resource: resource,
x: state.x,
y: state.y,
scale: state.scale,
imageClassname: imageClassname,
imageContainerStyle: imageContainerStyle,
imageContainerClassName: imageContainerClassName,
imageStyle: imageStyle,
onLoad: function onLoad() {
setLoading(false);
},
CustomImage: CustomImage,
CustomVideo: CustomVideo,
frameClassname: frameClassname,
frameStyle: frameStyle
});
}
return null;
}))));
};
export default ReactSimpleImageVideoLightbox;
//# sourceMappingURL=react-simple-lightbox-video-image.esm.js.map