UNPKG

@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
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