react-spring-lightbox
Advanced tools
Comparing version 1.4.9 to 1.4.10-beta.0
@@ -1,1004 +0,2 @@ | ||
'use strict'; | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var React = require('react'); | ||
var React__default = _interopDefault(React); | ||
var _taggedTemplateLiteral = _interopDefault(require('@babel/runtime/helpers/taggedTemplateLiteral')); | ||
var styled = _interopDefault(require('styled-components')); | ||
var _slicedToArray = _interopDefault(require('@babel/runtime/helpers/slicedToArray')); | ||
var _toConsumableArray = _interopDefault(require('@babel/runtime/helpers/toConsumableArray')); | ||
var web = require('@react-spring/web'); | ||
var reactUseGesture = require('react-use-gesture'); | ||
var _defineProperty = _interopDefault(require('@babel/runtime/helpers/defineProperty')); | ||
var _classCallCheck = _interopDefault(require('@babel/runtime/helpers/classCallCheck')); | ||
var _createClass = _interopDefault(require('@babel/runtime/helpers/createClass')); | ||
var _assertThisInitialized = _interopDefault(require('@babel/runtime/helpers/assertThisInitialized')); | ||
var _inherits = _interopDefault(require('@babel/runtime/helpers/inherits')); | ||
var _possibleConstructorReturn = _interopDefault(require('@babel/runtime/helpers/possibleConstructorReturn')); | ||
var _getPrototypeOf = _interopDefault(require('@babel/runtime/helpers/getPrototypeOf')); | ||
var ReactDOM = _interopDefault(require('react-dom')); | ||
/** | ||
* Calculates the the translate(x,y) coordinates needed to zoom-in | ||
* to a point in an image. | ||
* | ||
* @param {ref} imageRef The image dom node used as a reference to calculate translate offsets | ||
* @param {number} scale The current transform scale of image | ||
* @param {number} pinchDelta The amount of change in the new transform scale | ||
* @param {array} touchOrigin The [x,y] coordinates of the zoom origin | ||
* @param {array} currentTranslate The current [x,y] translate values of image | ||
* | ||
* @returns {array} The next [x,y] translate values to apply to image | ||
*/ | ||
var getTranslateOffsetsFromScale = function getTranslateOffsetsFromScale(_ref) { | ||
var imageRef = _ref.imageRef, | ||
scale = _ref.scale, | ||
pinchDelta = _ref.pinchDelta, | ||
_ref$touchOrigin = _slicedToArray(_ref.touchOrigin, 2), | ||
touchOriginX = _ref$touchOrigin[0], | ||
touchOriginY = _ref$touchOrigin[1], | ||
_ref$currentTranslate = _slicedToArray(_ref.currentTranslate, 2), | ||
translateX = _ref$currentTranslate[0], | ||
translateY = _ref$currentTranslate[1]; | ||
var _imageRef$current$get = imageRef.current.getBoundingClientRect(), | ||
imageTopLeftY = _imageRef$current$get.top, | ||
imageTopLeftX = _imageRef$current$get.left, | ||
imageWidth = _imageRef$current$get.width, | ||
imageHeight = _imageRef$current$get.height; // Get the (x,y) touch position relative to image origin at the current scale | ||
var imageCoordX = (touchOriginX - imageTopLeftX - imageWidth / 2) / scale; | ||
var imageCoordY = (touchOriginY - imageTopLeftY - imageHeight / 2) / scale; // Calculate translateX/Y offset at the next scale to zoom to touch position | ||
var newTranslateX = -imageCoordX * pinchDelta + translateX; | ||
var newTranslateY = -imageCoordY * pinchDelta + translateY; | ||
return [newTranslateX, newTranslateY]; | ||
}; | ||
/** | ||
* Determines if the provided image is within the viewport | ||
* | ||
* @param {ref} imageRef The image dom node to measure against the viewport | ||
* | ||
* @returns {boolean} True if image needs to be resized to fit viewport, otherwise false | ||
*/ | ||
var imageIsOutOfBounds = function imageIsOutOfBounds(imageRef) { | ||
var _imageRef$current$get = imageRef.current.getBoundingClientRect(), | ||
topLeftY = _imageRef$current$get.top, | ||
topLeftX = _imageRef$current$get.left, | ||
bottomRightY = _imageRef$current$get.bottom, | ||
bottomRightX = _imageRef$current$get.right; | ||
var _window = window, | ||
windowHeight = _window.innerHeight, | ||
windowWidth = _window.innerWidth; | ||
if (topLeftX > windowWidth * (1 / 2) || topLeftY > windowHeight * (1 / 2) || bottomRightX < windowWidth * (1 / 2) || bottomRightY < windowHeight * (1 / 2)) return true; | ||
return false; | ||
}; | ||
/** | ||
* React Hook that returns the current window size | ||
* and report updates from the 'resize' window event | ||
* | ||
* @param {node} ref Dom node to watch for double clicks | ||
* @param {number} [latency=300] The amount of time (in milliseconds) to wait before differentiating a single from a double click | ||
* @param {function} onSingleClick A callback function for single click events | ||
* @param {function} onDoubleClick A callback function for double click events | ||
*/ | ||
var useDoubleClick = function useDoubleClick(_ref) { | ||
var ref = _ref.ref, | ||
_ref$latency = _ref.latency, | ||
latency = _ref$latency === void 0 ? 300 : _ref$latency, | ||
_ref$onSingleClick = _ref.onSingleClick, | ||
onSingleClick = _ref$onSingleClick === void 0 ? function () { | ||
return null; | ||
} : _ref$onSingleClick, | ||
_ref$onDoubleClick = _ref.onDoubleClick, | ||
onDoubleClick = _ref$onDoubleClick === void 0 ? function () { | ||
return null; | ||
} : _ref$onDoubleClick; | ||
React.useEffect(function () { | ||
var clickRef = ref.current; | ||
var clickCount = 0; | ||
var handleClick = function handleClick(e) { | ||
clickCount += 1; | ||
setTimeout(function () { | ||
if (clickCount === 1) onSingleClick(e);else if (clickCount === 2) onDoubleClick(e); | ||
clickCount = 0; | ||
}, latency); | ||
}; // Add event listener for click events | ||
clickRef.addEventListener('click', handleClick); // Remove event listener | ||
return function () { | ||
clickRef.removeEventListener('click', handleClick); | ||
}; | ||
}); | ||
}; | ||
/** | ||
* React Hook that returns the current window size | ||
* and report updates from the 'resize' window event | ||
* | ||
* @typedef {WindowSize} WindowSize | ||
* @property {number} width Window width | ||
* @property {number} height Window height | ||
* @returns {WindowSize} An object container the window width and height | ||
*/ | ||
var useWindowSize = function useWindowSize() { | ||
var _useState = React.useState({ | ||
width: window.innerWidth, | ||
height: window.innerHeight | ||
}), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
windowSize = _useState2[0], | ||
setWindowSize = _useState2[1]; | ||
React.useEffect(function () { | ||
var handleResize = function handleResize() { | ||
if (window.innerHeight !== windowSize.height || window.innerWidth !== windowSize.width) { | ||
setWindowSize({ | ||
width: window.innerWidth, | ||
height: window.innerHeight | ||
}); | ||
} | ||
}; // Add event listener for window resize events | ||
window.addEventListener('resize', handleResize); | ||
window.addEventListener('orientationchange', handleResize); // Remove event listener | ||
return function () { | ||
window.removeEventListener('resize', handleResize); | ||
window.addEventListener('orientationchange', handleResize); | ||
}; | ||
}); | ||
return windowSize; | ||
}; | ||
function _templateObject() { | ||
var data = _taggedTemplateLiteral(["\n width: auto;\n max-width: 100%;\n user-select: none;\n ::selection {\n background: none;\n }\n"]); | ||
_templateObject = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
/** | ||
* Animates pinch-zoom + panning on image using spring physics | ||
* | ||
* @param {string} src The source URL of this image | ||
* @param {string} alt The alt attribute for this image | ||
* @param {boolean} isCurrentImage True if this image is currently shown in pager, otherwise false | ||
* @param {function} setDisableDrag Function that can be called to disable dragging in the pager | ||
* @param {number} pagerHeight Fixed height of the image stage, used to restrict maximum height of images | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
* @param {boolean} pagerIsDragging Indicates parent ImagePager is in a state of dragging, if true click to zoom is disabled | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
* @see https://github.com/react-spring/react-spring | ||
*/ | ||
var Image = function Image(_ref) { | ||
var _useDoubleClick; | ||
var src = _ref.src, | ||
alt = _ref.alt, | ||
pagerHeight = _ref.pagerHeight, | ||
isCurrentImage = _ref.isCurrentImage, | ||
setDisableDrag = _ref.setDisableDrag, | ||
singleClickToZoom = _ref.singleClickToZoom, | ||
pagerIsDragging = _ref.pagerIsDragging; | ||
var _useState = React.useState(false), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
isPanningImage = _useState2[0], | ||
setIsPanningImage = _useState2[1]; | ||
var imageRef = React.useRef(); | ||
var defaultImageTransform = function defaultImageTransform() { | ||
return { | ||
scale: 1, | ||
translateX: 0, | ||
translateY: 0, | ||
config: _objectSpread(_objectSpread({}, web.config["default"]), {}, { | ||
precision: 0.01 | ||
}) | ||
}; | ||
}; | ||
/** | ||
* Animates scale and translate offsets of Image as they change in gestures | ||
* | ||
* @see https://www.react-spring.io/docs/hooks/use-spring | ||
*/ | ||
var _useSpring = web.useSpring(function () { | ||
return _objectSpread(_objectSpread({}, defaultImageTransform()), {}, { | ||
onFrame: function onFrame(f) { | ||
if (f.scale < 1 || !f.pinching) set(defaultImageTransform); // Prevent dragging image out of viewport | ||
if (f.scale > 1 && imageIsOutOfBounds(imageRef)) set(defaultImageTransform()); | ||
}, | ||
// Enable dragging in ImagePager if image is at the default size | ||
onRest: function onRest(f) { | ||
if (f.scale === 1) setDisableDrag(false); | ||
} | ||
}); | ||
}), | ||
_useSpring2 = _slicedToArray(_useSpring, 2), | ||
_useSpring2$ = _useSpring2[0], | ||
scale = _useSpring2$.scale, | ||
translateX = _useSpring2$.translateX, | ||
translateY = _useSpring2$.translateY, | ||
set = _useSpring2[1]; // Reset scale of this image when dragging to new image in ImagePager | ||
React.useEffect(function () { | ||
if (!isCurrentImage) set(defaultImageTransform); | ||
}); | ||
/** | ||
* Update Image scale and translate offsets during pinch/pan gestures | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture#usegesture-hook-supporting-multiple-gestures-at-once | ||
*/ | ||
var bind = reactUseGesture.useGesture({ | ||
onPinch: function onPinch(_ref2) { | ||
var _ref2$movement = _slicedToArray(_ref2.movement, 1), | ||
xMovement = _ref2$movement[0], | ||
_ref2$origin = _slicedToArray(_ref2.origin, 2), | ||
touchOriginX = _ref2$origin[0], | ||
touchOriginY = _ref2$origin[1], | ||
event = _ref2.event, | ||
ctrlKey = _ref2.ctrlKey, | ||
last = _ref2.last, | ||
cancel = _ref2.cancel; | ||
// Prevent ImagePager from registering isDragging | ||
setDisableDrag(true); // Disable click to zoom during pinch | ||
if (xMovement && !isPanningImage) setIsPanningImage(true); // Don't calculate new translate offsets on final frame | ||
if (last) { | ||
cancel(); | ||
return; | ||
} // Speed up pinch zoom when using mouse versus touch | ||
var SCALE_FACTOR = ctrlKey ? 1000 : 250; | ||
var pinchScale = scale.value + xMovement / SCALE_FACTOR; | ||
var pinchDelta = pinchScale - scale.value; | ||
var clientX = event.clientX, | ||
clientY = event.clientY; // Calculate the amount of x, y translate offset needed to | ||
// zoom-in to point as image scale grows | ||
var _getTranslateOffsetsF = getTranslateOffsetsFromScale({ | ||
imageRef: imageRef, | ||
scale: scale.value, | ||
pinchDelta: pinchDelta, | ||
currentTranslate: [translateX.value, translateY.value], | ||
// Use the [x, y] coords of mouse if a trackpad or ctrl + wheel event | ||
// Otherwise use touch origin | ||
touchOrigin: ctrlKey ? [clientX, clientY] : [touchOriginX, touchOriginY] | ||
}), | ||
_getTranslateOffsetsF2 = _slicedToArray(_getTranslateOffsetsF, 2), | ||
newTranslateX = _getTranslateOffsetsF2[0], | ||
newTranslateY = _getTranslateOffsetsF2[1]; // Restrict the amount of zoom between half and 3x image size | ||
if (pinchScale < 0.5) set({ | ||
scale: 0.5, | ||
pinching: true | ||
});else if (pinchScale > 3.0) set({ | ||
scale: 3.0, | ||
pinching: true | ||
});else set({ | ||
scale: pinchScale, | ||
translateX: newTranslateX, | ||
translateY: newTranslateY, | ||
pinching: true | ||
}); | ||
}, | ||
onPinchEnd: function onPinchEnd() { | ||
if (scale.value > 1) setDisableDrag(true);else set(defaultImageTransform); | ||
setIsPanningImage(false); | ||
}, | ||
onDragEnd: function onDragEnd() { | ||
return setIsPanningImage(false); | ||
}, | ||
onDrag: function onDrag(_ref3) { | ||
var _ref3$movement = _slicedToArray(_ref3.movement, 2), | ||
xMovement = _ref3$movement[0], | ||
yMovement = _ref3$movement[1], | ||
pinching = _ref3.pinching, | ||
event = _ref3.event, | ||
cancel = _ref3.cancel, | ||
first = _ref3.first, | ||
_ref3$memo = _ref3.memo, | ||
memo = _ref3$memo === void 0 ? { | ||
initialTranslateX: 0, | ||
initialTranslateY: 0 | ||
} : _ref3$memo; | ||
// Disable click to zoom during drag | ||
if (xMovement && yMovement && !isPanningImage) setIsPanningImage(true); | ||
if (event.touches && event.touches.length > 1) return; | ||
if (pinching || scale.value <= 1) return; // Prevent dragging image out of viewport | ||
if (scale.value > 1 && imageIsOutOfBounds(imageRef)) cancel();else { | ||
if (first) { | ||
return { | ||
initialTranslateX: translateX.value, | ||
initialTranslateY: translateY.value | ||
}; | ||
} // Translate image from dragging | ||
set({ | ||
translateX: memo.initialTranslateX + xMovement, | ||
translateY: memo.initialTranslateY + yMovement | ||
}); | ||
return memo; | ||
} | ||
} | ||
}, | ||
/** | ||
* useGesture config | ||
* @see https://github.com/react-spring/react-use-gesture#usegesture-config | ||
*/ | ||
{ | ||
domTarget: imageRef, | ||
event: { | ||
passive: false | ||
} | ||
}); | ||
/** | ||
* @see https://github.com/react-spring/react-use-gesture#adding-gestures-to-dom-nodes | ||
*/ | ||
React.useEffect(bind, [bind]); // Handle click/tap on image | ||
useDoubleClick((_useDoubleClick = {}, _defineProperty(_useDoubleClick, singleClickToZoom ? 'onSingleClick' : 'onDoubleClick', function (e) { | ||
if (pagerIsDragging || isPanningImage) { | ||
e.stopPropagation(); | ||
return; | ||
} // If tapped while already zoomed-in, zoom out to default scale | ||
if (scale.value !== 1) { | ||
set(defaultImageTransform); | ||
return; | ||
} // Zoom-in to origin of click on image | ||
var touchOriginX = e.clientX, | ||
touchOriginY = e.clientY; | ||
var pinchScale = scale.value + 1; | ||
var pinchDelta = pinchScale - scale.value; // Calculate the amount of x, y translate offset needed to | ||
// zoom-in to point as image scale grows | ||
var _getTranslateOffsetsF3 = getTranslateOffsetsFromScale({ | ||
imageRef: imageRef, | ||
scale: scale.value, | ||
pinchDelta: pinchDelta, | ||
currentTranslate: [translateX.value, translateY.value], | ||
touchOrigin: [touchOriginX, touchOriginY] | ||
}), | ||
_getTranslateOffsetsF4 = _slicedToArray(_getTranslateOffsetsF3, 2), | ||
newTranslateX = _getTranslateOffsetsF4[0], | ||
newTranslateY = _getTranslateOffsetsF4[1]; // Disable dragging in pager | ||
setDisableDrag(true); | ||
set({ | ||
scale: pinchScale, | ||
translateX: newTranslateX, | ||
translateY: newTranslateY, | ||
pinching: true | ||
}); | ||
}), _defineProperty(_useDoubleClick, "ref", imageRef), _defineProperty(_useDoubleClick, "latency", singleClickToZoom ? 0 : 200), _useDoubleClick)); | ||
return /*#__PURE__*/React__default.createElement(AnimatedImage, { | ||
ref: imageRef, | ||
className: "lightbox-image", | ||
style: _objectSpread({ | ||
transform: web.to([scale, translateX, translateY], function (s, x, y) { | ||
return "translate(".concat(x, "px, ").concat(y, "px) scale(").concat(s, ")"); | ||
}), | ||
maxHeight: pagerHeight | ||
}, isCurrentImage && { | ||
willChange: 'transform' | ||
}), | ||
src: src, | ||
alt: alt, | ||
draggable: "false", | ||
onDragStart: function onDragStart(e) { | ||
// Disable image ghost dragging in firefox | ||
e.preventDefault(); | ||
}, | ||
onClick: function onClick(e) { | ||
// Don't close lighbox when clicking image | ||
e.stopPropagation(); | ||
e.nativeEvent.stopImmediatePropagation(); | ||
} | ||
}); | ||
}; | ||
var AnimatedImage = styled(web.animated.img)(_templateObject()); | ||
function _templateObject4() { | ||
var data = _taggedTemplateLiteral(["\n position: relative;\n touch-action: none;\n user-select: none;\n"]); | ||
_templateObject4 = function _templateObject4() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject3() { | ||
var data = _taggedTemplateLiteral(["\n position: absolute;\n top: 0px;\n left: 0px;\n right: 0px;\n bottom: 0px;\n height: 100%;\n width: 100%;\n will-change: transform;\n touch-action: none;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n"]); | ||
_templateObject3 = function _templateObject3() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject2() { | ||
var data = _taggedTemplateLiteral(["\n width: 100%;\n display: flex;\n justify-content: center;\n"]); | ||
_templateObject2 = function _templateObject2() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject$1() { | ||
var data = _taggedTemplateLiteral(["\n display: flex;\n justify-content: center;\n align-items: center;\n"]); | ||
_templateObject$1 = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
* Gesture controlled surface that animates prev/next page changes via spring physics. | ||
* | ||
* @param {array} images Array of image objects to be shown in Lightbox | ||
* @param {number} currentIndex Index of image in images array that is currently shown | ||
* @param {function} onPrev True if this image is currently shown in pager, otherwise false | ||
* @param {function} onNext Function that can be called to disable dragging in the pager | ||
* @param {function} onClose Function that closes the Lightbox | ||
* @param {function} renderImageOverlay A React component that renders inside the image stage, useful for making overlays over the image | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
* @see https://github.com/react-spring/react-spring | ||
*/ | ||
var ImagePager = function ImagePager(_ref) { | ||
var images = _ref.images, | ||
currentIndex = _ref.currentIndex, | ||
onPrev = _ref.onPrev, | ||
onNext = _ref.onNext, | ||
onClose = _ref.onClose, | ||
renderImageOverlay = _ref.renderImageOverlay, | ||
singleClickToZoom = _ref.singleClickToZoom; | ||
var firstRender = React.useRef(true); | ||
var imageStageRef = React.useRef(_toConsumableArray(Array(images.length)).map(function () { | ||
return React__default.createRef(); | ||
})); | ||
var _useWindowSize = useWindowSize(), | ||
windowHeight = _useWindowSize.height, | ||
pageWidth = _useWindowSize.width; | ||
var _useState = React.useState(false), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
disableDrag = _useState2[0], | ||
setDisableDrag = _useState2[1]; | ||
var _useState3 = React.useState('100%'), | ||
_useState4 = _slicedToArray(_useState3, 2), | ||
pagerHeight = _useState4[0], | ||
setPagerHeight = _useState4[1]; | ||
var _useState5 = React.useState(false), | ||
_useState6 = _slicedToArray(_useState5, 2), | ||
isDragging = _useState6[0], | ||
setIsDragging = _useState6[1]; // Generate page positions based on current index | ||
var getPagePositions = function getPagePositions(i) { | ||
var down = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
var xDelta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | ||
var x = (i - currentIndex) * pageWidth + (down ? xDelta : 0); | ||
if (i < currentIndex - 1 || i > currentIndex + 1) return { | ||
x: x, | ||
display: 'none' | ||
}; | ||
return { | ||
x: x, | ||
display: 'flex' | ||
}; | ||
}; | ||
/** | ||
* Animates translateX of all images at the same time | ||
* | ||
* @see https://www.react-spring.io/docs/hooks/use-springs | ||
*/ | ||
var _useSprings = web.useSprings(images.length, getPagePositions), | ||
_useSprings2 = _slicedToArray(_useSprings, 2), | ||
props = _useSprings2[0], | ||
set = _useSprings2[1]; // Determine the absolute height of the image pager | ||
React.useEffect(function () { | ||
var currPagerHeight = imageStageRef.current[currentIndex].current.clientHeight - 50; | ||
if (pagerHeight !== currPagerHeight) setPagerHeight(currPagerHeight); | ||
}, [currentIndex, pagerHeight, windowHeight]); // Animate page change if currentIndex changes | ||
React.useEffect(function () { | ||
// No need to set page position for initial render | ||
if (firstRender.current) { | ||
firstRender.current = false; | ||
return; | ||
} // Update page positions after prev/next page state change | ||
set(getPagePositions); | ||
}); | ||
/** | ||
* Update each Image's visibility and translateX offset during dragging | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
*/ | ||
var bind = reactUseGesture.useGesture({ | ||
onWheel: function onWheel(_ref2) { | ||
var distance = _ref2.distance, | ||
velocity = _ref2.velocity, | ||
_ref2$direction = _slicedToArray(_ref2.direction, 2), | ||
xDir = _ref2$direction[0], | ||
yDir = _ref2$direction[1], | ||
ctrlKey = _ref2.ctrlKey; | ||
// Disable drag if Image has been zoomed in to allow for panning | ||
if (ctrlKey || disableDrag || velocity === 0) return; | ||
var draggedFarEnough = distance > pageWidth / 3; | ||
var draggedFastEnough = velocity > 1.5 && distance <= pageWidth / 3; // Handle next/prev image from valid drag | ||
if (draggedFarEnough || draggedFastEnough) { | ||
var goToIndex = xDir + yDir > 0 ? -1 : 1; | ||
if (goToIndex > 0) onNext();else if (goToIndex < 0) onPrev(); | ||
} | ||
}, | ||
onWheelEnd: function onWheelEnd() { | ||
set(function (i) { | ||
return getPagePositions(i, false, 0); | ||
}); | ||
setIsDragging(false); | ||
}, | ||
onDrag: function onDrag(_ref3) { | ||
var down = _ref3.down, | ||
_ref3$movement = _slicedToArray(_ref3.movement, 1), | ||
xMovement = _ref3$movement[0], | ||
_ref3$direction = _slicedToArray(_ref3.direction, 1), | ||
xDir = _ref3$direction[0], | ||
velocity = _ref3.velocity, | ||
distance = _ref3.distance, | ||
cancel = _ref3.cancel, | ||
touches = _ref3.touches; | ||
// Disable drag if Image has been zoomed in to allow for panning | ||
if (disableDrag || xMovement === 0) return; | ||
if (!isDragging) setIsDragging(true); | ||
var isHorizontalDrag = Math.abs(xDir) > 0.7; | ||
var draggedFarEnough = down && isHorizontalDrag && distance > pageWidth / 3.5; | ||
var draggedFastEnough = down && isHorizontalDrag && velocity > 2; // Handle next/prev image from valid drag | ||
if (draggedFarEnough || draggedFastEnough) { | ||
var goToIndex = xDir > 0 ? -1 : 1; // Cancel gesture animation | ||
cancel(); | ||
if (goToIndex > 0) onNext();else if (goToIndex < 0) onPrev(); | ||
} // Don't move pager during two+ finger touch events, i.e. pinch-zoom | ||
if (touches > 1) return; // Update page x-coordinates for single finger/mouse gestures | ||
set(function (i) { | ||
return getPagePositions(i, down, xMovement); | ||
}); | ||
}, | ||
onDragEnd: function onDragEnd() { | ||
return setIsDragging(false); | ||
} | ||
}, | ||
/** | ||
* useGesture config | ||
* @see https://github.com/react-spring/react-use-gesture#usegesture-config | ||
*/ | ||
{ | ||
domTarget: imageStageRef.current[currentIndex], | ||
event: { | ||
passive: true, | ||
capture: false | ||
} | ||
}); | ||
/** | ||
* @see https://github.com/react-spring/react-use-gesture#adding-gestures-to-dom-nodes | ||
*/ | ||
React.useEffect(bind, [bind, currentIndex]); | ||
return props.map(function (_ref4, i) { | ||
var x = _ref4.x, | ||
display = _ref4.display; | ||
return /*#__PURE__*/React__default.createElement(AnimatedImagePager, { | ||
role: "presentation", | ||
ref: imageStageRef.current[i], | ||
key: i, | ||
className: "lightbox-image-pager", | ||
style: { | ||
display: display, | ||
transform: x.to(function (xInterp) { | ||
return "translateX(".concat(xInterp, "px)"); | ||
}) | ||
}, | ||
onClick: function onClick() { | ||
return Math.abs(x.value) < 1 && !disableDrag && onClose(); | ||
} | ||
}, /*#__PURE__*/React__default.createElement(PagerContentWrapper, null, /*#__PURE__*/React__default.createElement(PagerInnerContentWrapper, null, /*#__PURE__*/React__default.createElement(ImageContainer, { | ||
onClick: function onClick(e) { | ||
e.stopPropagation(); | ||
e.nativeEvent.stopImmediatePropagation(); | ||
} | ||
}, /*#__PURE__*/React__default.createElement(Image, { | ||
setDisableDrag: setDisableDrag, | ||
src: images[i].src, | ||
alt: images[i].alt, | ||
pagerHeight: pagerHeight, | ||
isCurrentImage: i === currentIndex, | ||
pagerIsDragging: isDragging, | ||
singleClickToZoom: singleClickToZoom | ||
}), renderImageOverlay())))); | ||
}); | ||
}; | ||
var PagerInnerContentWrapper = styled.div(_templateObject$1()); | ||
var PagerContentWrapper = styled.div(_templateObject2()); | ||
var AnimatedImagePager = styled(web.animated.div)(_templateObject3()); | ||
var ImageContainer = styled.div(_templateObject4()); | ||
function _templateObject$2() { | ||
var data = _taggedTemplateLiteral(["\n flex-grow: 1;\n position: relative;\n height: 100%;\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n"]); | ||
_templateObject$2 = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
* Containing element for ImagePager and prev/next button controls | ||
* | ||
* @param {array} images Array of image objects to be shown in Lightbox | ||
* @param {number} currentIndex Index of image in images array that is currently shown | ||
* @param {function} onPrev True if this image is currently shown in pager, otherwise false | ||
* @param {function} onNext Function that can be called to disable dragging in the pager | ||
* @param {function} renderPrevButton A React component that is used for previous button in image pager | ||
* @param {function} renderNextButton A React component that is used for next button in image pager | ||
* @param {function} renderImageOverlay A React component that renders inside the image stage, useful for making overlays over the image | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
*/ | ||
var ImageStage = function ImageStage(_ref) { | ||
var images = _ref.images, | ||
currentIndex = _ref.currentIndex, | ||
onPrev = _ref.onPrev, | ||
onNext = _ref.onNext, | ||
onClose = _ref.onClose, | ||
renderPrevButton = _ref.renderPrevButton, | ||
renderNextButton = _ref.renderNextButton, | ||
renderImageOverlay = _ref.renderImageOverlay, | ||
singleClickToZoom = _ref.singleClickToZoom; | ||
// Extra sanity check that the next/prev image exists before moving to it | ||
var canPrev = currentIndex > 0; | ||
var canNext = currentIndex + 1 < images.length; | ||
return /*#__PURE__*/React__default.createElement(ImageStageContainer, { | ||
className: "lightbox-image-stage" | ||
}, renderPrevButton({ | ||
canPrev: canPrev | ||
}), /*#__PURE__*/React__default.createElement(ImagePager, { | ||
images: images, | ||
currentIndex: currentIndex, | ||
onClose: onClose, | ||
onNext: onNext, | ||
onPrev: onPrev, | ||
renderImageOverlay: renderImageOverlay, | ||
singleClickToZoom: singleClickToZoom | ||
}), renderNextButton({ | ||
canNext: canNext | ||
})); | ||
}; | ||
var ImageStageContainer = styled.div(_templateObject$2()); | ||
function _templateObject$3() { | ||
var data = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n position: fixed;\n z-index: 400;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n"]); | ||
_templateObject$3 = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$1(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
/** | ||
* Animates the lightbox as it opens/closes | ||
* | ||
* @param {ReactNode} children All child components of Lightbox | ||
* @param {boolean} isOpen Flag that dictates if the lightbox is open or closed | ||
* @param {string} className Classes are applied to the root lightbox component | ||
* @param {object} style Inline styles are applied to the root lightbox component | ||
* @param {object} pageTransitionConfig React-Spring useTransition config for page open/close animation | ||
* | ||
* @see https://www.react-spring.io/docs/hooks/use-transition | ||
*/ | ||
var PageContainer = function PageContainer(_ref) { | ||
var children = _ref.children, | ||
isOpen = _ref.isOpen, | ||
className = _ref.className, | ||
style = _ref.style, | ||
pageTransitionConfig = _ref.pageTransitionConfig; | ||
var defaultTransition = { | ||
from: { | ||
transform: 'scale(0.75)', | ||
opacity: 0 | ||
}, | ||
enter: { | ||
transform: 'scale(1)', | ||
opacity: 1 | ||
}, | ||
leave: { | ||
transform: 'scale(0.75)', | ||
opacity: 0 | ||
}, | ||
config: _objectSpread$1(_objectSpread$1({}, web.config["default"]), {}, { | ||
mass: 1, | ||
tension: 320, | ||
friction: 32 | ||
}) | ||
}; | ||
var transitions = web.useTransition(isOpen, null, _objectSpread$1(_objectSpread$1({}, defaultTransition), pageTransitionConfig)); | ||
return transitions.map(function (_ref2) { | ||
var item = _ref2.item, | ||
key = _ref2.key, | ||
props = _ref2.props; | ||
return item && /*#__PURE__*/React__default.createElement(AnimatedPageContainer, { | ||
key: key, | ||
className: "lightbox-container".concat(className ? " ".concat(className) : ''), | ||
style: _objectSpread$1(_objectSpread$1({}, props), style) | ||
}, children); | ||
}); | ||
}; | ||
var AnimatedPageContainer = styled(web.animated.div)(_templateObject$3()); | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
/** | ||
* Creates a SSR + next.js friendly React Portal inside <body /> | ||
* | ||
* Child components are rendered on the client side only | ||
* | ||
* @param {array|node} children Child components will be rendered to the portal | ||
* @see https://reactjs.org/docs/portals.html | ||
*/ | ||
var CreatePortal = /*#__PURE__*/function (_React$Component) { | ||
_inherits(CreatePortal, _React$Component); | ||
var _super = _createSuper(CreatePortal); | ||
function CreatePortal() { | ||
var _this; | ||
_classCallCheck(this, CreatePortal); | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this = _super.call.apply(_super, [this].concat(args)); | ||
_defineProperty(_assertThisInitialized(_this), "preventWheel", function (e) { | ||
return e.preventDefault(); | ||
}); | ||
return _this; | ||
} | ||
_createClass(CreatePortal, [{ | ||
key: "componentDidMount", | ||
// Only executes on the client-side | ||
value: function componentDidMount() { | ||
// Get the document body | ||
this.body = document.body; // Create a container <div /> for React Portal | ||
this.portalContainer = document.createElement('div'); | ||
this.portalContainer.setAttribute('class', 'lightbox-portal'); // Append the container to the document body | ||
this.body.appendChild(this.portalContainer); // Force a re-render as we're on the client side now | ||
// children prop will render to portalContainer | ||
this.forceUpdate(); // Add event listener to prevent trackpad/ctrl+mousewheel zooming of lightbox | ||
// Zooming is handled specifically within /ImageStage/components/Image | ||
this.portalContainer.addEventListener('wheel', this.preventWheel); | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
// Remove wheel event listener | ||
this.portalContainer.removeEventListener('wheel', this.preventWheel); // Cleanup Portal from DOM | ||
this.body.removeChild(this.portalContainer); | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
// Return null during SSR | ||
if (this.portalContainer === undefined) return null; | ||
var children = this.props.children; | ||
return ReactDOM.createPortal(children, this.portalContainer); | ||
} | ||
}]); | ||
return CreatePortal; | ||
}(React__default.Component); | ||
/** | ||
* Gesture controlled lightbox that interpolates animations with spring physics. | ||
* | ||
* @param {boolean} isOpen Flag that dictates if the lightbox is open or closed | ||
* @param {function} onClose Function that closes the Lightbox | ||
* @param {function} onPrev Function that changes currentIndex to previous image in images | ||
* @param {function} onNext Function that changes currentIndex to next image in images | ||
* @param {number} currentIndex Index of image in images array that is currently shown | ||
* @param {function} renderHeader A React component that renders above the image pager | ||
* @param {function} renderFooter A React component that renders below the image pager | ||
* @param {function} renderImageOverlay A React component that renders inside the image stage, useful for making overlays over the image | ||
* @param {function} renderPrevButton A React component that is used for previous button in image pager | ||
* @param {function} renderNextButton A React component that is used for next button in image pager | ||
* @param {array} images Array of image objects to be shown in Lightbox | ||
* @param {string} className Classes are applied to the root lightbox component | ||
* @param {object} style Inline styles are applied to the root lightbox component | ||
* @param {object} pageTransitionConfig React-Spring useTransition config for page open/close animation | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
* @see https://github.com/react-spring/react-spring | ||
*/ | ||
var Lightbox = function Lightbox(_ref) { | ||
var isOpen = _ref.isOpen, | ||
onClose = _ref.onClose, | ||
images = _ref.images, | ||
currentIndex = _ref.currentIndex, | ||
onPrev = _ref.onPrev, | ||
onNext = _ref.onNext, | ||
renderHeader = _ref.renderHeader, | ||
renderFooter = _ref.renderFooter, | ||
renderPrevButton = _ref.renderPrevButton, | ||
renderNextButton = _ref.renderNextButton, | ||
renderImageOverlay = _ref.renderImageOverlay, | ||
className = _ref.className, | ||
singleClickToZoom = _ref.singleClickToZoom, | ||
style = _ref.style, | ||
pageTransitionConfig = _ref.pageTransitionConfig; | ||
// Handle event listeners for keyboard | ||
React.useEffect(function () { | ||
/** | ||
* Prevent keyboard from controlling background page | ||
* when lightbox is open | ||
*/ | ||
var preventBackgroundScroll = function preventBackgroundScroll(e) { | ||
var keysToIgnore = ['ArrowUp', 'ArrowDown', 'End', 'Home', 'PageUp', 'PageDown']; | ||
if (isOpen && keysToIgnore.includes(e.key)) e.preventDefault(); | ||
}; | ||
/** | ||
* Navigate images with arrow keys, close on Esc key | ||
*/ | ||
var handleKeyboardInput = function handleKeyboardInput(e) { | ||
if (isOpen) { | ||
switch (e.key) { | ||
case 'ArrowLeft': | ||
onPrev(); | ||
break; | ||
case 'ArrowRight': | ||
onNext(); | ||
break; | ||
case 'Escape': | ||
onClose(); | ||
break; | ||
default: | ||
e.preventDefault(); | ||
break; | ||
} | ||
} | ||
}; | ||
document.addEventListener('keyup', handleKeyboardInput); | ||
document.addEventListener('keydown', preventBackgroundScroll); | ||
return function () { | ||
document.removeEventListener('keyup', handleKeyboardInput); | ||
document.removeEventListener('keydown', preventBackgroundScroll); | ||
}; | ||
}); | ||
return /*#__PURE__*/React__default.createElement(CreatePortal, null, /*#__PURE__*/React__default.createElement(PageContainer, { | ||
isOpen: isOpen, | ||
className: className, | ||
style: style, | ||
pageTransitionConfig: pageTransitionConfig | ||
}, renderHeader(), /*#__PURE__*/React__default.createElement(ImageStage, { | ||
images: images, | ||
onClose: onClose, | ||
currentIndex: currentIndex, | ||
onPrev: onPrev, | ||
onNext: onNext, | ||
renderPrevButton: renderPrevButton, | ||
renderNextButton: renderNextButton, | ||
renderImageOverlay: renderImageOverlay, | ||
singleClickToZoom: singleClickToZoom | ||
}), renderFooter())); | ||
}; | ||
Lightbox.defaultProps = { | ||
pageTransitionConfig: null, | ||
className: null, | ||
style: null, | ||
renderHeader: function renderHeader() { | ||
return null; | ||
}, | ||
renderFooter: function renderFooter() { | ||
return null; | ||
}, | ||
renderPrevButton: function renderPrevButton() { | ||
return null; | ||
}, | ||
renderNextButton: function renderNextButton() { | ||
return null; | ||
}, | ||
renderImageOverlay: function renderImageOverlay() { | ||
return null; | ||
}, | ||
singleClickToZoom: false | ||
}; | ||
module.exports = Lightbox; | ||
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var t=require("react"),n=e(t),r=e(require("styled-components")),i=e(require("@babel/runtime/helpers/slicedToArray")),a=e(require("@babel/runtime/helpers/toConsumableArray")),o=require("@react-spring/web"),c=require("react-use-gesture"),l=e(require("@babel/runtime/helpers/defineProperty")),u=e(require("@babel/runtime/helpers/classCallCheck")),s=e(require("@babel/runtime/helpers/createClass")),f=e(require("@babel/runtime/helpers/assertThisInitialized")),g=e(require("@babel/runtime/helpers/inherits")),p=e(require("@babel/runtime/helpers/possibleConstructorReturn")),d=e(require("@babel/runtime/helpers/getPrototypeOf")),m=e(require("react-dom")),v=function(e){var t=e.imageRef,n=e.scale,r=e.pinchDelta,a=i(e.touchOrigin,2),o=a[0],c=a[1],l=i(e.currentTranslate,2),u=l[0],s=l[1],f=t.current.getBoundingClientRect(),g=f.top;return[-((o-f.left-f.width/2)/n)*r+u,-((c-g-f.height/2)/n)*r+s]},h=function(e){var t=e.current.getBoundingClientRect(),n=t.top,r=t.left,i=t.bottom,a=t.right,o=window,c=o.innerHeight,l=o.innerWidth;return r>.5*l||n>.5*c||a<.5*l||i<.5*c};function y(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function b(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?y(Object(n),!0).forEach((function(t){l(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):y(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var w=function(e){var r,a=e.src,u=e.alt,s=e.pagerHeight,f=e.isCurrentImage,g=e.setDisableDrag,p=e.singleClickToZoom,d=e.pagerIsDragging,m=t.useState(!1),y=i(m,2),w=y[0],P=y[1],O=t.useRef(),x=function(){return{scale:1,translateX:0,translateY:0,config:b(b({},o.config.default),{},{precision:.01})}},E=o.useSpring((function(){return b(b({},x()),{},{onFrame:function(e){(e.scale<1||!e.pinching)&&N(x),e.scale>1&&h(O)&&N(x())},onRest:function(e){1===e.scale&&g(!1)}})})),k=i(E,2),I=k[0],D=I.scale,j=I.translateX,T=I.translateY,N=k[1];t.useEffect((function(){f||N(x)}));var R=c.useGesture({onPinch:function(e){var t=i(e.movement,1)[0],n=i(e.origin,2),r=n[0],a=n[1],o=e.event,c=e.ctrlKey,l=e.last,u=e.cancel;if(g(!0),t&&!w&&P(!0),l)u();else{var s=c?1e3:250,f=D.value+t/s,p=f-D.value,d=o.clientX,m=o.clientY,h=v({imageRef:O,scale:D.value,pinchDelta:p,currentTranslate:[j.value,T.value],touchOrigin:c?[d,m]:[r,a]}),y=i(h,2),b=y[0],C=y[1];N(f<.5?{scale:.5,pinching:!0}:f>3?{scale:3,pinching:!0}:{scale:f,translateX:b,translateY:C,pinching:!0})}},onPinchEnd:function(){D.value>1?g(!0):N(x),P(!1)},onDragEnd:function(){return P(!1)},onDrag:function(e){var t=i(e.movement,2),n=t[0],r=t[1],a=e.pinching,o=e.event,c=e.cancel,l=e.first,u=e.memo,s=void 0===u?{initialTranslateX:0,initialTranslateY:0}:u;if(n&&r&&!w&&P(!0),!(o.touches&&o.touches.length>1||a||D.value<=1))return D.value>1&&h(O)?void c():l?{initialTranslateX:j.value,initialTranslateY:T.value}:(N({translateX:s.initialTranslateX+n,translateY:s.initialTranslateY+r}),s)}},{domTarget:O,event:{passive:!1}});return t.useEffect(R,[R]),function(e){var n=e.ref,r=e.latency,i=void 0===r?300:r,a=e.onSingleClick,o=void 0===a?function(){return null}:a,c=e.onDoubleClick,l=void 0===c?function(){return null}:c;t.useEffect((function(){var e=n.current,t=0,r=function(e){t+=1,setTimeout((function(){1===t?o(e):2===t&&l(e),t=0}),i)};return e.addEventListener("click",r),function(){e.removeEventListener("click",r)}}))}((l(r={},p?"onSingleClick":"onDoubleClick",(function(e){if(d||w)e.stopPropagation();else if(1===D.value){var t=e.clientX,n=e.clientY,r=D.value+1,a=r-D.value,o=v({imageRef:O,scale:D.value,pinchDelta:a,currentTranslate:[j.value,T.value],touchOrigin:[t,n]}),c=i(o,2),l=c[0],u=c[1];g(!0),N({scale:r,translateX:l,translateY:u,pinching:!0})}else N(x)})),l(r,"ref",O),l(r,"latency",p?0:200),r)),n.createElement(C,{ref:O,className:"lightbox-image",style:b({transform:o.to([D,j,T],(function(e,t,n){return"translate(".concat(t,"px, ").concat(n,"px) scale(").concat(e,")")})),maxHeight:s},f&&{willChange:"transform"}),src:a,alt:u,draggable:"false",onDragStart:function(e){e.preventDefault()},onClick:function(e){e.stopPropagation(),e.nativeEvent.stopImmediatePropagation()}})},C=r(o.animated.img).withConfig({displayName:"Image__AnimatedImage",componentId:"sc-1k35vrn-0"})(["width:auto;max-width:100%;user-select:none;::selection{background:none;}"]),P=function(e){var r=e.images,l=e.currentIndex,u=e.onPrev,s=e.onNext,f=e.onClose,g=e.renderImageOverlay,p=e.singleClickToZoom,d=t.useRef(!0),m=t.useRef(a(Array(r.length)).map((function(){return n.createRef()}))),v=function(){var e=t.useState({width:window.innerWidth,height:window.innerHeight}),n=i(e,2),r=n[0],a=n[1];return t.useEffect((function(){var e=function(){window.innerHeight===r.height&&window.innerWidth===r.width||a({width:window.innerWidth,height:window.innerHeight})};return window.addEventListener("resize",e),window.addEventListener("orientationchange",e),function(){window.removeEventListener("resize",e),window.addEventListener("orientationchange",e)}})),r}(),h=v.height,y=v.width,b=t.useState(!1),C=i(b,2),P=C[0],I=C[1],D=t.useState("100%"),j=i(D,2),T=j[0],N=j[1],R=t.useState(!1),S=i(R,2),q=S[0],_=S[1],L=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,r=(e-l)*y+(t?n:0);return e<l-1||e>l+1?{x:r,display:"none"}:{x:r,display:"flex"}},A=o.useSprings(r.length,L),W=i(A,2),H=W[0],X=W[1];t.useEffect((function(){var e=m.current[l].current.clientHeight-50;T!==e&&N(e)}),[l,T,h]),t.useEffect((function(){d.current?d.current=!1:X(L)}));var B=c.useGesture({onWheel:function(e){var t=e.distance,n=e.velocity,r=i(e.direction,2),a=r[0],o=r[1];if(!e.ctrlKey&&!P&&0!==n&&(t>y/3||n>1.5&&t<=y/3)){var c=a+o>0?-1:1;c>0?s():c<0&&u()}},onWheelEnd:function(){X((function(e){return L(e,!1,0)})),_(!1)},onDrag:function(e){var t=e.down,n=i(e.movement,1)[0],r=i(e.direction,1)[0],a=e.velocity,o=e.distance,c=e.cancel,l=e.touches;if(!P&&0!==n){q||_(!0);var f=Math.abs(r)>.7;if(t&&f&&o>y/3.5||t&&f&&a>2){var g=r>0?-1:1;c(),g>0?s():g<0&&u()}l>1||X((function(e){return L(e,t,n)}))}},onDragEnd:function(){return _(!1)}},{domTarget:m.current[l],event:{passive:!0,capture:!1}});return t.useEffect(B,[B,l]),H.map((function(e,t){var i=e.x,a=e.display;return n.createElement(E,{role:"presentation",ref:m.current[t],key:t,className:"lightbox-image-pager",style:{display:a,transform:i.to((function(e){return"translateX(".concat(e,"px)")}))},onClick:function(){return Math.abs(i.value)<1&&!P&&f()}},n.createElement(x,null,n.createElement(O,null,n.createElement(k,{onClick:function(e){e.stopPropagation(),e.nativeEvent.stopImmediatePropagation()}},n.createElement(w,{setDisableDrag:I,src:r[t].src,alt:r[t].alt,pagerHeight:T,isCurrentImage:t===l,pagerIsDragging:q,singleClickToZoom:p}),g()))))}))},O=r.div.withConfig({displayName:"ImagePager__PagerInnerContentWrapper",componentId:"a18fy4-0"})(["display:flex;justify-content:center;align-items:center;"]),x=r.div.withConfig({displayName:"ImagePager__PagerContentWrapper",componentId:"a18fy4-1"})(["width:100%;display:flex;justify-content:center;"]),E=r(o.animated.div).withConfig({displayName:"ImagePager__AnimatedImagePager",componentId:"a18fy4-2"})(["position:absolute;top:0px;left:0px;right:0px;bottom:0px;height:100%;width:100%;will-change:transform;touch-action:none;display:flex;flex-direction:column;justify-content:center;align-items:center;"]),k=r.div.withConfig({displayName:"ImagePager__ImageContainer",componentId:"a18fy4-3"})(["position:relative;touch-action:none;user-select:none;"]),I=function(e){var t=e.images,r=e.currentIndex,i=e.onPrev,a=e.onNext,o=e.onClose,c=e.renderPrevButton,l=e.renderNextButton,u=e.renderImageOverlay,s=e.singleClickToZoom,f=r>0,g=r+1<t.length;return n.createElement(D,{className:"lightbox-image-stage"},c({canPrev:f}),n.createElement(P,{images:t,currentIndex:r,onClose:o,onNext:a,onPrev:i,renderImageOverlay:u,singleClickToZoom:s}),l({canNext:g}))},D=r.div.withConfig({displayName:"ImageStage__ImageStageContainer",componentId:"m6g63l-0"})(["flex-grow:1;position:relative;height:100%;width:100%;display:flex;justify-content:center;align-items:center;"]);function j(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function T(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?j(Object(n),!0).forEach((function(t){l(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):j(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var N=function(e){var t=e.children,r=e.isOpen,i=e.className,a=e.style,c=e.pageTransitionConfig,l={from:{transform:"scale(0.75)",opacity:0},enter:{transform:"scale(1)",opacity:1},leave:{transform:"scale(0.75)",opacity:0},config:T(T({},o.config.default),{},{mass:1,tension:320,friction:32})};return o.useTransition(r,null,T(T({},l),c)).map((function(e){var r=e.item,o=e.key,c=e.props;return r&&n.createElement(R,{key:o,className:"lightbox-container".concat(i?" ".concat(i):""),style:T(T({},c),a)},t)}))},R=r(o.animated.div).withConfig({displayName:"PageContainer__AnimatedPageContainer",componentId:"sc-19m9s01-0"})(["display:flex;flex-direction:column;position:fixed;z-index:400;top:0;bottom:0;left:0;right:0;"]);function S(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=d(e);if(t){var i=d(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return p(this,n)}}var q=function(e){g(n,e);var t=S(n);function n(){var e;u(this,n);for(var r=arguments.length,i=new Array(r),a=0;a<r;a++)i[a]=arguments[a];return e=t.call.apply(t,[this].concat(i)),l(f(e),"preventWheel",(function(e){return e.preventDefault()})),e}return s(n,[{key:"componentDidMount",value:function(){this.body=document.body,this.portalContainer=document.createElement("div"),this.portalContainer.setAttribute("class","lightbox-portal"),this.body.appendChild(this.portalContainer),this.forceUpdate(),this.portalContainer.addEventListener("wheel",this.preventWheel)}},{key:"componentWillUnmount",value:function(){this.portalContainer.removeEventListener("wheel",this.preventWheel),this.body.removeChild(this.portalContainer)}},{key:"render",value:function(){if(void 0===this.portalContainer)return null;var e=this.props.children;return m.createPortal(e,this.portalContainer)}}]),n}(n.Component),_=function(e){var r=e.isOpen,i=e.onClose,a=e.images,o=e.currentIndex,c=e.onPrev,l=e.onNext,u=e.renderHeader,s=e.renderFooter,f=e.renderPrevButton,g=e.renderNextButton,p=e.renderImageOverlay,d=e.className,m=e.singleClickToZoom,v=e.style,h=e.pageTransitionConfig;return t.useEffect((function(){var e=function(e){r&&["ArrowUp","ArrowDown","End","Home","PageUp","PageDown"].includes(e.key)&&e.preventDefault()},t=function(e){if(r)switch(e.key){case"ArrowLeft":c();break;case"ArrowRight":l();break;case"Escape":i();break;default:e.preventDefault()}};return document.addEventListener("keyup",t),document.addEventListener("keydown",e),function(){document.removeEventListener("keyup",t),document.removeEventListener("keydown",e)}})),n.createElement(q,null,n.createElement(N,{isOpen:r,className:d,style:v,pageTransitionConfig:h},u(),n.createElement(I,{images:a,onClose:i,currentIndex:o,onPrev:c,onNext:l,renderPrevButton:f,renderNextButton:g,renderImageOverlay:p,singleClickToZoom:m}),s()))};_.defaultProps={pageTransitionConfig:null,className:null,style:null,renderHeader:function(){return null},renderFooter:function(){return null},renderPrevButton:function(){return null},renderNextButton:function(){return null},renderImageOverlay:function(){return null},singleClickToZoom:!1},module.exports=_; | ||
//# sourceMappingURL=index.cjs.js.map |
@@ -1,999 +0,2 @@ | ||
import React, { useEffect, useState, useRef } from 'react'; | ||
import _taggedTemplateLiteral from '@babel/runtime/helpers/taggedTemplateLiteral'; | ||
import styled from 'styled-components'; | ||
import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; | ||
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; | ||
import { useSpring, config, to, animated, useSprings, useTransition } from '@react-spring/web'; | ||
import { useGesture } from 'react-use-gesture'; | ||
import _defineProperty from '@babel/runtime/helpers/defineProperty'; | ||
import _classCallCheck from '@babel/runtime/helpers/classCallCheck'; | ||
import _createClass from '@babel/runtime/helpers/createClass'; | ||
import _assertThisInitialized from '@babel/runtime/helpers/assertThisInitialized'; | ||
import _inherits from '@babel/runtime/helpers/inherits'; | ||
import _possibleConstructorReturn from '@babel/runtime/helpers/possibleConstructorReturn'; | ||
import _getPrototypeOf from '@babel/runtime/helpers/getPrototypeOf'; | ||
import ReactDOM from 'react-dom'; | ||
/** | ||
* Calculates the the translate(x,y) coordinates needed to zoom-in | ||
* to a point in an image. | ||
* | ||
* @param {ref} imageRef The image dom node used as a reference to calculate translate offsets | ||
* @param {number} scale The current transform scale of image | ||
* @param {number} pinchDelta The amount of change in the new transform scale | ||
* @param {array} touchOrigin The [x,y] coordinates of the zoom origin | ||
* @param {array} currentTranslate The current [x,y] translate values of image | ||
* | ||
* @returns {array} The next [x,y] translate values to apply to image | ||
*/ | ||
var getTranslateOffsetsFromScale = function getTranslateOffsetsFromScale(_ref) { | ||
var imageRef = _ref.imageRef, | ||
scale = _ref.scale, | ||
pinchDelta = _ref.pinchDelta, | ||
_ref$touchOrigin = _slicedToArray(_ref.touchOrigin, 2), | ||
touchOriginX = _ref$touchOrigin[0], | ||
touchOriginY = _ref$touchOrigin[1], | ||
_ref$currentTranslate = _slicedToArray(_ref.currentTranslate, 2), | ||
translateX = _ref$currentTranslate[0], | ||
translateY = _ref$currentTranslate[1]; | ||
var _imageRef$current$get = imageRef.current.getBoundingClientRect(), | ||
imageTopLeftY = _imageRef$current$get.top, | ||
imageTopLeftX = _imageRef$current$get.left, | ||
imageWidth = _imageRef$current$get.width, | ||
imageHeight = _imageRef$current$get.height; // Get the (x,y) touch position relative to image origin at the current scale | ||
var imageCoordX = (touchOriginX - imageTopLeftX - imageWidth / 2) / scale; | ||
var imageCoordY = (touchOriginY - imageTopLeftY - imageHeight / 2) / scale; // Calculate translateX/Y offset at the next scale to zoom to touch position | ||
var newTranslateX = -imageCoordX * pinchDelta + translateX; | ||
var newTranslateY = -imageCoordY * pinchDelta + translateY; | ||
return [newTranslateX, newTranslateY]; | ||
}; | ||
/** | ||
* Determines if the provided image is within the viewport | ||
* | ||
* @param {ref} imageRef The image dom node to measure against the viewport | ||
* | ||
* @returns {boolean} True if image needs to be resized to fit viewport, otherwise false | ||
*/ | ||
var imageIsOutOfBounds = function imageIsOutOfBounds(imageRef) { | ||
var _imageRef$current$get = imageRef.current.getBoundingClientRect(), | ||
topLeftY = _imageRef$current$get.top, | ||
topLeftX = _imageRef$current$get.left, | ||
bottomRightY = _imageRef$current$get.bottom, | ||
bottomRightX = _imageRef$current$get.right; | ||
var _window = window, | ||
windowHeight = _window.innerHeight, | ||
windowWidth = _window.innerWidth; | ||
if (topLeftX > windowWidth * (1 / 2) || topLeftY > windowHeight * (1 / 2) || bottomRightX < windowWidth * (1 / 2) || bottomRightY < windowHeight * (1 / 2)) return true; | ||
return false; | ||
}; | ||
/** | ||
* React Hook that returns the current window size | ||
* and report updates from the 'resize' window event | ||
* | ||
* @param {node} ref Dom node to watch for double clicks | ||
* @param {number} [latency=300] The amount of time (in milliseconds) to wait before differentiating a single from a double click | ||
* @param {function} onSingleClick A callback function for single click events | ||
* @param {function} onDoubleClick A callback function for double click events | ||
*/ | ||
var useDoubleClick = function useDoubleClick(_ref) { | ||
var ref = _ref.ref, | ||
_ref$latency = _ref.latency, | ||
latency = _ref$latency === void 0 ? 300 : _ref$latency, | ||
_ref$onSingleClick = _ref.onSingleClick, | ||
onSingleClick = _ref$onSingleClick === void 0 ? function () { | ||
return null; | ||
} : _ref$onSingleClick, | ||
_ref$onDoubleClick = _ref.onDoubleClick, | ||
onDoubleClick = _ref$onDoubleClick === void 0 ? function () { | ||
return null; | ||
} : _ref$onDoubleClick; | ||
useEffect(function () { | ||
var clickRef = ref.current; | ||
var clickCount = 0; | ||
var handleClick = function handleClick(e) { | ||
clickCount += 1; | ||
setTimeout(function () { | ||
if (clickCount === 1) onSingleClick(e);else if (clickCount === 2) onDoubleClick(e); | ||
clickCount = 0; | ||
}, latency); | ||
}; // Add event listener for click events | ||
clickRef.addEventListener('click', handleClick); // Remove event listener | ||
return function () { | ||
clickRef.removeEventListener('click', handleClick); | ||
}; | ||
}); | ||
}; | ||
/** | ||
* React Hook that returns the current window size | ||
* and report updates from the 'resize' window event | ||
* | ||
* @typedef {WindowSize} WindowSize | ||
* @property {number} width Window width | ||
* @property {number} height Window height | ||
* @returns {WindowSize} An object container the window width and height | ||
*/ | ||
var useWindowSize = function useWindowSize() { | ||
var _useState = useState({ | ||
width: window.innerWidth, | ||
height: window.innerHeight | ||
}), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
windowSize = _useState2[0], | ||
setWindowSize = _useState2[1]; | ||
useEffect(function () { | ||
var handleResize = function handleResize() { | ||
if (window.innerHeight !== windowSize.height || window.innerWidth !== windowSize.width) { | ||
setWindowSize({ | ||
width: window.innerWidth, | ||
height: window.innerHeight | ||
}); | ||
} | ||
}; // Add event listener for window resize events | ||
window.addEventListener('resize', handleResize); | ||
window.addEventListener('orientationchange', handleResize); // Remove event listener | ||
return function () { | ||
window.removeEventListener('resize', handleResize); | ||
window.addEventListener('orientationchange', handleResize); | ||
}; | ||
}); | ||
return windowSize; | ||
}; | ||
function _templateObject() { | ||
var data = _taggedTemplateLiteral(["\n width: auto;\n max-width: 100%;\n user-select: none;\n ::selection {\n background: none;\n }\n"]); | ||
_templateObject = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
/** | ||
* Animates pinch-zoom + panning on image using spring physics | ||
* | ||
* @param {string} src The source URL of this image | ||
* @param {string} alt The alt attribute for this image | ||
* @param {boolean} isCurrentImage True if this image is currently shown in pager, otherwise false | ||
* @param {function} setDisableDrag Function that can be called to disable dragging in the pager | ||
* @param {number} pagerHeight Fixed height of the image stage, used to restrict maximum height of images | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
* @param {boolean} pagerIsDragging Indicates parent ImagePager is in a state of dragging, if true click to zoom is disabled | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
* @see https://github.com/react-spring/react-spring | ||
*/ | ||
var Image = function Image(_ref) { | ||
var _useDoubleClick; | ||
var src = _ref.src, | ||
alt = _ref.alt, | ||
pagerHeight = _ref.pagerHeight, | ||
isCurrentImage = _ref.isCurrentImage, | ||
setDisableDrag = _ref.setDisableDrag, | ||
singleClickToZoom = _ref.singleClickToZoom, | ||
pagerIsDragging = _ref.pagerIsDragging; | ||
var _useState = useState(false), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
isPanningImage = _useState2[0], | ||
setIsPanningImage = _useState2[1]; | ||
var imageRef = useRef(); | ||
var defaultImageTransform = function defaultImageTransform() { | ||
return { | ||
scale: 1, | ||
translateX: 0, | ||
translateY: 0, | ||
config: _objectSpread(_objectSpread({}, config["default"]), {}, { | ||
precision: 0.01 | ||
}) | ||
}; | ||
}; | ||
/** | ||
* Animates scale and translate offsets of Image as they change in gestures | ||
* | ||
* @see https://www.react-spring.io/docs/hooks/use-spring | ||
*/ | ||
var _useSpring = useSpring(function () { | ||
return _objectSpread(_objectSpread({}, defaultImageTransform()), {}, { | ||
onFrame: function onFrame(f) { | ||
if (f.scale < 1 || !f.pinching) set(defaultImageTransform); // Prevent dragging image out of viewport | ||
if (f.scale > 1 && imageIsOutOfBounds(imageRef)) set(defaultImageTransform()); | ||
}, | ||
// Enable dragging in ImagePager if image is at the default size | ||
onRest: function onRest(f) { | ||
if (f.scale === 1) setDisableDrag(false); | ||
} | ||
}); | ||
}), | ||
_useSpring2 = _slicedToArray(_useSpring, 2), | ||
_useSpring2$ = _useSpring2[0], | ||
scale = _useSpring2$.scale, | ||
translateX = _useSpring2$.translateX, | ||
translateY = _useSpring2$.translateY, | ||
set = _useSpring2[1]; // Reset scale of this image when dragging to new image in ImagePager | ||
useEffect(function () { | ||
if (!isCurrentImage) set(defaultImageTransform); | ||
}); | ||
/** | ||
* Update Image scale and translate offsets during pinch/pan gestures | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture#usegesture-hook-supporting-multiple-gestures-at-once | ||
*/ | ||
var bind = useGesture({ | ||
onPinch: function onPinch(_ref2) { | ||
var _ref2$movement = _slicedToArray(_ref2.movement, 1), | ||
xMovement = _ref2$movement[0], | ||
_ref2$origin = _slicedToArray(_ref2.origin, 2), | ||
touchOriginX = _ref2$origin[0], | ||
touchOriginY = _ref2$origin[1], | ||
event = _ref2.event, | ||
ctrlKey = _ref2.ctrlKey, | ||
last = _ref2.last, | ||
cancel = _ref2.cancel; | ||
// Prevent ImagePager from registering isDragging | ||
setDisableDrag(true); // Disable click to zoom during pinch | ||
if (xMovement && !isPanningImage) setIsPanningImage(true); // Don't calculate new translate offsets on final frame | ||
if (last) { | ||
cancel(); | ||
return; | ||
} // Speed up pinch zoom when using mouse versus touch | ||
var SCALE_FACTOR = ctrlKey ? 1000 : 250; | ||
var pinchScale = scale.value + xMovement / SCALE_FACTOR; | ||
var pinchDelta = pinchScale - scale.value; | ||
var clientX = event.clientX, | ||
clientY = event.clientY; // Calculate the amount of x, y translate offset needed to | ||
// zoom-in to point as image scale grows | ||
var _getTranslateOffsetsF = getTranslateOffsetsFromScale({ | ||
imageRef: imageRef, | ||
scale: scale.value, | ||
pinchDelta: pinchDelta, | ||
currentTranslate: [translateX.value, translateY.value], | ||
// Use the [x, y] coords of mouse if a trackpad or ctrl + wheel event | ||
// Otherwise use touch origin | ||
touchOrigin: ctrlKey ? [clientX, clientY] : [touchOriginX, touchOriginY] | ||
}), | ||
_getTranslateOffsetsF2 = _slicedToArray(_getTranslateOffsetsF, 2), | ||
newTranslateX = _getTranslateOffsetsF2[0], | ||
newTranslateY = _getTranslateOffsetsF2[1]; // Restrict the amount of zoom between half and 3x image size | ||
if (pinchScale < 0.5) set({ | ||
scale: 0.5, | ||
pinching: true | ||
});else if (pinchScale > 3.0) set({ | ||
scale: 3.0, | ||
pinching: true | ||
});else set({ | ||
scale: pinchScale, | ||
translateX: newTranslateX, | ||
translateY: newTranslateY, | ||
pinching: true | ||
}); | ||
}, | ||
onPinchEnd: function onPinchEnd() { | ||
if (scale.value > 1) setDisableDrag(true);else set(defaultImageTransform); | ||
setIsPanningImage(false); | ||
}, | ||
onDragEnd: function onDragEnd() { | ||
return setIsPanningImage(false); | ||
}, | ||
onDrag: function onDrag(_ref3) { | ||
var _ref3$movement = _slicedToArray(_ref3.movement, 2), | ||
xMovement = _ref3$movement[0], | ||
yMovement = _ref3$movement[1], | ||
pinching = _ref3.pinching, | ||
event = _ref3.event, | ||
cancel = _ref3.cancel, | ||
first = _ref3.first, | ||
_ref3$memo = _ref3.memo, | ||
memo = _ref3$memo === void 0 ? { | ||
initialTranslateX: 0, | ||
initialTranslateY: 0 | ||
} : _ref3$memo; | ||
// Disable click to zoom during drag | ||
if (xMovement && yMovement && !isPanningImage) setIsPanningImage(true); | ||
if (event.touches && event.touches.length > 1) return; | ||
if (pinching || scale.value <= 1) return; // Prevent dragging image out of viewport | ||
if (scale.value > 1 && imageIsOutOfBounds(imageRef)) cancel();else { | ||
if (first) { | ||
return { | ||
initialTranslateX: translateX.value, | ||
initialTranslateY: translateY.value | ||
}; | ||
} // Translate image from dragging | ||
set({ | ||
translateX: memo.initialTranslateX + xMovement, | ||
translateY: memo.initialTranslateY + yMovement | ||
}); | ||
return memo; | ||
} | ||
} | ||
}, | ||
/** | ||
* useGesture config | ||
* @see https://github.com/react-spring/react-use-gesture#usegesture-config | ||
*/ | ||
{ | ||
domTarget: imageRef, | ||
event: { | ||
passive: false | ||
} | ||
}); | ||
/** | ||
* @see https://github.com/react-spring/react-use-gesture#adding-gestures-to-dom-nodes | ||
*/ | ||
useEffect(bind, [bind]); // Handle click/tap on image | ||
useDoubleClick((_useDoubleClick = {}, _defineProperty(_useDoubleClick, singleClickToZoom ? 'onSingleClick' : 'onDoubleClick', function (e) { | ||
if (pagerIsDragging || isPanningImage) { | ||
e.stopPropagation(); | ||
return; | ||
} // If tapped while already zoomed-in, zoom out to default scale | ||
if (scale.value !== 1) { | ||
set(defaultImageTransform); | ||
return; | ||
} // Zoom-in to origin of click on image | ||
var touchOriginX = e.clientX, | ||
touchOriginY = e.clientY; | ||
var pinchScale = scale.value + 1; | ||
var pinchDelta = pinchScale - scale.value; // Calculate the amount of x, y translate offset needed to | ||
// zoom-in to point as image scale grows | ||
var _getTranslateOffsetsF3 = getTranslateOffsetsFromScale({ | ||
imageRef: imageRef, | ||
scale: scale.value, | ||
pinchDelta: pinchDelta, | ||
currentTranslate: [translateX.value, translateY.value], | ||
touchOrigin: [touchOriginX, touchOriginY] | ||
}), | ||
_getTranslateOffsetsF4 = _slicedToArray(_getTranslateOffsetsF3, 2), | ||
newTranslateX = _getTranslateOffsetsF4[0], | ||
newTranslateY = _getTranslateOffsetsF4[1]; // Disable dragging in pager | ||
setDisableDrag(true); | ||
set({ | ||
scale: pinchScale, | ||
translateX: newTranslateX, | ||
translateY: newTranslateY, | ||
pinching: true | ||
}); | ||
}), _defineProperty(_useDoubleClick, "ref", imageRef), _defineProperty(_useDoubleClick, "latency", singleClickToZoom ? 0 : 200), _useDoubleClick)); | ||
return /*#__PURE__*/React.createElement(AnimatedImage, { | ||
ref: imageRef, | ||
className: "lightbox-image", | ||
style: _objectSpread({ | ||
transform: to([scale, translateX, translateY], function (s, x, y) { | ||
return "translate(".concat(x, "px, ").concat(y, "px) scale(").concat(s, ")"); | ||
}), | ||
maxHeight: pagerHeight | ||
}, isCurrentImage && { | ||
willChange: 'transform' | ||
}), | ||
src: src, | ||
alt: alt, | ||
draggable: "false", | ||
onDragStart: function onDragStart(e) { | ||
// Disable image ghost dragging in firefox | ||
e.preventDefault(); | ||
}, | ||
onClick: function onClick(e) { | ||
// Don't close lighbox when clicking image | ||
e.stopPropagation(); | ||
e.nativeEvent.stopImmediatePropagation(); | ||
} | ||
}); | ||
}; | ||
var AnimatedImage = styled(animated.img)(_templateObject()); | ||
function _templateObject4() { | ||
var data = _taggedTemplateLiteral(["\n position: relative;\n touch-action: none;\n user-select: none;\n"]); | ||
_templateObject4 = function _templateObject4() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject3() { | ||
var data = _taggedTemplateLiteral(["\n position: absolute;\n top: 0px;\n left: 0px;\n right: 0px;\n bottom: 0px;\n height: 100%;\n width: 100%;\n will-change: transform;\n touch-action: none;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n"]); | ||
_templateObject3 = function _templateObject3() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject2() { | ||
var data = _taggedTemplateLiteral(["\n width: 100%;\n display: flex;\n justify-content: center;\n"]); | ||
_templateObject2 = function _templateObject2() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject$1() { | ||
var data = _taggedTemplateLiteral(["\n display: flex;\n justify-content: center;\n align-items: center;\n"]); | ||
_templateObject$1 = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
* Gesture controlled surface that animates prev/next page changes via spring physics. | ||
* | ||
* @param {array} images Array of image objects to be shown in Lightbox | ||
* @param {number} currentIndex Index of image in images array that is currently shown | ||
* @param {function} onPrev True if this image is currently shown in pager, otherwise false | ||
* @param {function} onNext Function that can be called to disable dragging in the pager | ||
* @param {function} onClose Function that closes the Lightbox | ||
* @param {function} renderImageOverlay A React component that renders inside the image stage, useful for making overlays over the image | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
* @see https://github.com/react-spring/react-spring | ||
*/ | ||
var ImagePager = function ImagePager(_ref) { | ||
var images = _ref.images, | ||
currentIndex = _ref.currentIndex, | ||
onPrev = _ref.onPrev, | ||
onNext = _ref.onNext, | ||
onClose = _ref.onClose, | ||
renderImageOverlay = _ref.renderImageOverlay, | ||
singleClickToZoom = _ref.singleClickToZoom; | ||
var firstRender = useRef(true); | ||
var imageStageRef = useRef(_toConsumableArray(Array(images.length)).map(function () { | ||
return React.createRef(); | ||
})); | ||
var _useWindowSize = useWindowSize(), | ||
windowHeight = _useWindowSize.height, | ||
pageWidth = _useWindowSize.width; | ||
var _useState = useState(false), | ||
_useState2 = _slicedToArray(_useState, 2), | ||
disableDrag = _useState2[0], | ||
setDisableDrag = _useState2[1]; | ||
var _useState3 = useState('100%'), | ||
_useState4 = _slicedToArray(_useState3, 2), | ||
pagerHeight = _useState4[0], | ||
setPagerHeight = _useState4[1]; | ||
var _useState5 = useState(false), | ||
_useState6 = _slicedToArray(_useState5, 2), | ||
isDragging = _useState6[0], | ||
setIsDragging = _useState6[1]; // Generate page positions based on current index | ||
var getPagePositions = function getPagePositions(i) { | ||
var down = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
var xDelta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | ||
var x = (i - currentIndex) * pageWidth + (down ? xDelta : 0); | ||
if (i < currentIndex - 1 || i > currentIndex + 1) return { | ||
x: x, | ||
display: 'none' | ||
}; | ||
return { | ||
x: x, | ||
display: 'flex' | ||
}; | ||
}; | ||
/** | ||
* Animates translateX of all images at the same time | ||
* | ||
* @see https://www.react-spring.io/docs/hooks/use-springs | ||
*/ | ||
var _useSprings = useSprings(images.length, getPagePositions), | ||
_useSprings2 = _slicedToArray(_useSprings, 2), | ||
props = _useSprings2[0], | ||
set = _useSprings2[1]; // Determine the absolute height of the image pager | ||
useEffect(function () { | ||
var currPagerHeight = imageStageRef.current[currentIndex].current.clientHeight - 50; | ||
if (pagerHeight !== currPagerHeight) setPagerHeight(currPagerHeight); | ||
}, [currentIndex, pagerHeight, windowHeight]); // Animate page change if currentIndex changes | ||
useEffect(function () { | ||
// No need to set page position for initial render | ||
if (firstRender.current) { | ||
firstRender.current = false; | ||
return; | ||
} // Update page positions after prev/next page state change | ||
set(getPagePositions); | ||
}); | ||
/** | ||
* Update each Image's visibility and translateX offset during dragging | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
*/ | ||
var bind = useGesture({ | ||
onWheel: function onWheel(_ref2) { | ||
var distance = _ref2.distance, | ||
velocity = _ref2.velocity, | ||
_ref2$direction = _slicedToArray(_ref2.direction, 2), | ||
xDir = _ref2$direction[0], | ||
yDir = _ref2$direction[1], | ||
ctrlKey = _ref2.ctrlKey; | ||
// Disable drag if Image has been zoomed in to allow for panning | ||
if (ctrlKey || disableDrag || velocity === 0) return; | ||
var draggedFarEnough = distance > pageWidth / 3; | ||
var draggedFastEnough = velocity > 1.5 && distance <= pageWidth / 3; // Handle next/prev image from valid drag | ||
if (draggedFarEnough || draggedFastEnough) { | ||
var goToIndex = xDir + yDir > 0 ? -1 : 1; | ||
if (goToIndex > 0) onNext();else if (goToIndex < 0) onPrev(); | ||
} | ||
}, | ||
onWheelEnd: function onWheelEnd() { | ||
set(function (i) { | ||
return getPagePositions(i, false, 0); | ||
}); | ||
setIsDragging(false); | ||
}, | ||
onDrag: function onDrag(_ref3) { | ||
var down = _ref3.down, | ||
_ref3$movement = _slicedToArray(_ref3.movement, 1), | ||
xMovement = _ref3$movement[0], | ||
_ref3$direction = _slicedToArray(_ref3.direction, 1), | ||
xDir = _ref3$direction[0], | ||
velocity = _ref3.velocity, | ||
distance = _ref3.distance, | ||
cancel = _ref3.cancel, | ||
touches = _ref3.touches; | ||
// Disable drag if Image has been zoomed in to allow for panning | ||
if (disableDrag || xMovement === 0) return; | ||
if (!isDragging) setIsDragging(true); | ||
var isHorizontalDrag = Math.abs(xDir) > 0.7; | ||
var draggedFarEnough = down && isHorizontalDrag && distance > pageWidth / 3.5; | ||
var draggedFastEnough = down && isHorizontalDrag && velocity > 2; // Handle next/prev image from valid drag | ||
if (draggedFarEnough || draggedFastEnough) { | ||
var goToIndex = xDir > 0 ? -1 : 1; // Cancel gesture animation | ||
cancel(); | ||
if (goToIndex > 0) onNext();else if (goToIndex < 0) onPrev(); | ||
} // Don't move pager during two+ finger touch events, i.e. pinch-zoom | ||
if (touches > 1) return; // Update page x-coordinates for single finger/mouse gestures | ||
set(function (i) { | ||
return getPagePositions(i, down, xMovement); | ||
}); | ||
}, | ||
onDragEnd: function onDragEnd() { | ||
return setIsDragging(false); | ||
} | ||
}, | ||
/** | ||
* useGesture config | ||
* @see https://github.com/react-spring/react-use-gesture#usegesture-config | ||
*/ | ||
{ | ||
domTarget: imageStageRef.current[currentIndex], | ||
event: { | ||
passive: true, | ||
capture: false | ||
} | ||
}); | ||
/** | ||
* @see https://github.com/react-spring/react-use-gesture#adding-gestures-to-dom-nodes | ||
*/ | ||
useEffect(bind, [bind, currentIndex]); | ||
return props.map(function (_ref4, i) { | ||
var x = _ref4.x, | ||
display = _ref4.display; | ||
return /*#__PURE__*/React.createElement(AnimatedImagePager, { | ||
role: "presentation", | ||
ref: imageStageRef.current[i], | ||
key: i, | ||
className: "lightbox-image-pager", | ||
style: { | ||
display: display, | ||
transform: x.to(function (xInterp) { | ||
return "translateX(".concat(xInterp, "px)"); | ||
}) | ||
}, | ||
onClick: function onClick() { | ||
return Math.abs(x.value) < 1 && !disableDrag && onClose(); | ||
} | ||
}, /*#__PURE__*/React.createElement(PagerContentWrapper, null, /*#__PURE__*/React.createElement(PagerInnerContentWrapper, null, /*#__PURE__*/React.createElement(ImageContainer, { | ||
onClick: function onClick(e) { | ||
e.stopPropagation(); | ||
e.nativeEvent.stopImmediatePropagation(); | ||
} | ||
}, /*#__PURE__*/React.createElement(Image, { | ||
setDisableDrag: setDisableDrag, | ||
src: images[i].src, | ||
alt: images[i].alt, | ||
pagerHeight: pagerHeight, | ||
isCurrentImage: i === currentIndex, | ||
pagerIsDragging: isDragging, | ||
singleClickToZoom: singleClickToZoom | ||
}), renderImageOverlay())))); | ||
}); | ||
}; | ||
var PagerInnerContentWrapper = styled.div(_templateObject$1()); | ||
var PagerContentWrapper = styled.div(_templateObject2()); | ||
var AnimatedImagePager = styled(animated.div)(_templateObject3()); | ||
var ImageContainer = styled.div(_templateObject4()); | ||
function _templateObject$2() { | ||
var data = _taggedTemplateLiteral(["\n flex-grow: 1;\n position: relative;\n height: 100%;\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n"]); | ||
_templateObject$2 = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
* Containing element for ImagePager and prev/next button controls | ||
* | ||
* @param {array} images Array of image objects to be shown in Lightbox | ||
* @param {number} currentIndex Index of image in images array that is currently shown | ||
* @param {function} onPrev True if this image is currently shown in pager, otherwise false | ||
* @param {function} onNext Function that can be called to disable dragging in the pager | ||
* @param {function} renderPrevButton A React component that is used for previous button in image pager | ||
* @param {function} renderNextButton A React component that is used for next button in image pager | ||
* @param {function} renderImageOverlay A React component that renders inside the image stage, useful for making overlays over the image | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
*/ | ||
var ImageStage = function ImageStage(_ref) { | ||
var images = _ref.images, | ||
currentIndex = _ref.currentIndex, | ||
onPrev = _ref.onPrev, | ||
onNext = _ref.onNext, | ||
onClose = _ref.onClose, | ||
renderPrevButton = _ref.renderPrevButton, | ||
renderNextButton = _ref.renderNextButton, | ||
renderImageOverlay = _ref.renderImageOverlay, | ||
singleClickToZoom = _ref.singleClickToZoom; | ||
// Extra sanity check that the next/prev image exists before moving to it | ||
var canPrev = currentIndex > 0; | ||
var canNext = currentIndex + 1 < images.length; | ||
return /*#__PURE__*/React.createElement(ImageStageContainer, { | ||
className: "lightbox-image-stage" | ||
}, renderPrevButton({ | ||
canPrev: canPrev | ||
}), /*#__PURE__*/React.createElement(ImagePager, { | ||
images: images, | ||
currentIndex: currentIndex, | ||
onClose: onClose, | ||
onNext: onNext, | ||
onPrev: onPrev, | ||
renderImageOverlay: renderImageOverlay, | ||
singleClickToZoom: singleClickToZoom | ||
}), renderNextButton({ | ||
canNext: canNext | ||
})); | ||
}; | ||
var ImageStageContainer = styled.div(_templateObject$2()); | ||
function _templateObject$3() { | ||
var data = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n position: fixed;\n z-index: 400;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n"]); | ||
_templateObject$3 = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$1(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } | ||
/** | ||
* Animates the lightbox as it opens/closes | ||
* | ||
* @param {ReactNode} children All child components of Lightbox | ||
* @param {boolean} isOpen Flag that dictates if the lightbox is open or closed | ||
* @param {string} className Classes are applied to the root lightbox component | ||
* @param {object} style Inline styles are applied to the root lightbox component | ||
* @param {object} pageTransitionConfig React-Spring useTransition config for page open/close animation | ||
* | ||
* @see https://www.react-spring.io/docs/hooks/use-transition | ||
*/ | ||
var PageContainer = function PageContainer(_ref) { | ||
var children = _ref.children, | ||
isOpen = _ref.isOpen, | ||
className = _ref.className, | ||
style = _ref.style, | ||
pageTransitionConfig = _ref.pageTransitionConfig; | ||
var defaultTransition = { | ||
from: { | ||
transform: 'scale(0.75)', | ||
opacity: 0 | ||
}, | ||
enter: { | ||
transform: 'scale(1)', | ||
opacity: 1 | ||
}, | ||
leave: { | ||
transform: 'scale(0.75)', | ||
opacity: 0 | ||
}, | ||
config: _objectSpread$1(_objectSpread$1({}, config["default"]), {}, { | ||
mass: 1, | ||
tension: 320, | ||
friction: 32 | ||
}) | ||
}; | ||
var transitions = useTransition(isOpen, null, _objectSpread$1(_objectSpread$1({}, defaultTransition), pageTransitionConfig)); | ||
return transitions.map(function (_ref2) { | ||
var item = _ref2.item, | ||
key = _ref2.key, | ||
props = _ref2.props; | ||
return item && /*#__PURE__*/React.createElement(AnimatedPageContainer, { | ||
key: key, | ||
className: "lightbox-container".concat(className ? " ".concat(className) : ''), | ||
style: _objectSpread$1(_objectSpread$1({}, props), style) | ||
}, children); | ||
}); | ||
}; | ||
var AnimatedPageContainer = styled(animated.div)(_templateObject$3()); | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
/** | ||
* Creates a SSR + next.js friendly React Portal inside <body /> | ||
* | ||
* Child components are rendered on the client side only | ||
* | ||
* @param {array|node} children Child components will be rendered to the portal | ||
* @see https://reactjs.org/docs/portals.html | ||
*/ | ||
var CreatePortal = /*#__PURE__*/function (_React$Component) { | ||
_inherits(CreatePortal, _React$Component); | ||
var _super = _createSuper(CreatePortal); | ||
function CreatePortal() { | ||
var _this; | ||
_classCallCheck(this, CreatePortal); | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this = _super.call.apply(_super, [this].concat(args)); | ||
_defineProperty(_assertThisInitialized(_this), "preventWheel", function (e) { | ||
return e.preventDefault(); | ||
}); | ||
return _this; | ||
} | ||
_createClass(CreatePortal, [{ | ||
key: "componentDidMount", | ||
// Only executes on the client-side | ||
value: function componentDidMount() { | ||
// Get the document body | ||
this.body = document.body; // Create a container <div /> for React Portal | ||
this.portalContainer = document.createElement('div'); | ||
this.portalContainer.setAttribute('class', 'lightbox-portal'); // Append the container to the document body | ||
this.body.appendChild(this.portalContainer); // Force a re-render as we're on the client side now | ||
// children prop will render to portalContainer | ||
this.forceUpdate(); // Add event listener to prevent trackpad/ctrl+mousewheel zooming of lightbox | ||
// Zooming is handled specifically within /ImageStage/components/Image | ||
this.portalContainer.addEventListener('wheel', this.preventWheel); | ||
} | ||
}, { | ||
key: "componentWillUnmount", | ||
value: function componentWillUnmount() { | ||
// Remove wheel event listener | ||
this.portalContainer.removeEventListener('wheel', this.preventWheel); // Cleanup Portal from DOM | ||
this.body.removeChild(this.portalContainer); | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
// Return null during SSR | ||
if (this.portalContainer === undefined) return null; | ||
var children = this.props.children; | ||
return ReactDOM.createPortal(children, this.portalContainer); | ||
} | ||
}]); | ||
return CreatePortal; | ||
}(React.Component); | ||
/** | ||
* Gesture controlled lightbox that interpolates animations with spring physics. | ||
* | ||
* @param {boolean} isOpen Flag that dictates if the lightbox is open or closed | ||
* @param {function} onClose Function that closes the Lightbox | ||
* @param {function} onPrev Function that changes currentIndex to previous image in images | ||
* @param {function} onNext Function that changes currentIndex to next image in images | ||
* @param {number} currentIndex Index of image in images array that is currently shown | ||
* @param {function} renderHeader A React component that renders above the image pager | ||
* @param {function} renderFooter A React component that renders below the image pager | ||
* @param {function} renderImageOverlay A React component that renders inside the image stage, useful for making overlays over the image | ||
* @param {function} renderPrevButton A React component that is used for previous button in image pager | ||
* @param {function} renderNextButton A React component that is used for next button in image pager | ||
* @param {array} images Array of image objects to be shown in Lightbox | ||
* @param {string} className Classes are applied to the root lightbox component | ||
* @param {object} style Inline styles are applied to the root lightbox component | ||
* @param {object} pageTransitionConfig React-Spring useTransition config for page open/close animation | ||
* @param {boolean} singleClickToZoom Overrides the default behavior of double clicking causing an image zoom to a single click | ||
* | ||
* @see https://github.com/react-spring/react-use-gesture | ||
* @see https://github.com/react-spring/react-spring | ||
*/ | ||
var Lightbox = function Lightbox(_ref) { | ||
var isOpen = _ref.isOpen, | ||
onClose = _ref.onClose, | ||
images = _ref.images, | ||
currentIndex = _ref.currentIndex, | ||
onPrev = _ref.onPrev, | ||
onNext = _ref.onNext, | ||
renderHeader = _ref.renderHeader, | ||
renderFooter = _ref.renderFooter, | ||
renderPrevButton = _ref.renderPrevButton, | ||
renderNextButton = _ref.renderNextButton, | ||
renderImageOverlay = _ref.renderImageOverlay, | ||
className = _ref.className, | ||
singleClickToZoom = _ref.singleClickToZoom, | ||
style = _ref.style, | ||
pageTransitionConfig = _ref.pageTransitionConfig; | ||
// Handle event listeners for keyboard | ||
useEffect(function () { | ||
/** | ||
* Prevent keyboard from controlling background page | ||
* when lightbox is open | ||
*/ | ||
var preventBackgroundScroll = function preventBackgroundScroll(e) { | ||
var keysToIgnore = ['ArrowUp', 'ArrowDown', 'End', 'Home', 'PageUp', 'PageDown']; | ||
if (isOpen && keysToIgnore.includes(e.key)) e.preventDefault(); | ||
}; | ||
/** | ||
* Navigate images with arrow keys, close on Esc key | ||
*/ | ||
var handleKeyboardInput = function handleKeyboardInput(e) { | ||
if (isOpen) { | ||
switch (e.key) { | ||
case 'ArrowLeft': | ||
onPrev(); | ||
break; | ||
case 'ArrowRight': | ||
onNext(); | ||
break; | ||
case 'Escape': | ||
onClose(); | ||
break; | ||
default: | ||
e.preventDefault(); | ||
break; | ||
} | ||
} | ||
}; | ||
document.addEventListener('keyup', handleKeyboardInput); | ||
document.addEventListener('keydown', preventBackgroundScroll); | ||
return function () { | ||
document.removeEventListener('keyup', handleKeyboardInput); | ||
document.removeEventListener('keydown', preventBackgroundScroll); | ||
}; | ||
}); | ||
return /*#__PURE__*/React.createElement(CreatePortal, null, /*#__PURE__*/React.createElement(PageContainer, { | ||
isOpen: isOpen, | ||
className: className, | ||
style: style, | ||
pageTransitionConfig: pageTransitionConfig | ||
}, renderHeader(), /*#__PURE__*/React.createElement(ImageStage, { | ||
images: images, | ||
onClose: onClose, | ||
currentIndex: currentIndex, | ||
onPrev: onPrev, | ||
onNext: onNext, | ||
renderPrevButton: renderPrevButton, | ||
renderNextButton: renderNextButton, | ||
renderImageOverlay: renderImageOverlay, | ||
singleClickToZoom: singleClickToZoom | ||
}), renderFooter())); | ||
}; | ||
Lightbox.defaultProps = { | ||
pageTransitionConfig: null, | ||
className: null, | ||
style: null, | ||
renderHeader: function renderHeader() { | ||
return null; | ||
}, | ||
renderFooter: function renderFooter() { | ||
return null; | ||
}, | ||
renderPrevButton: function renderPrevButton() { | ||
return null; | ||
}, | ||
renderNextButton: function renderNextButton() { | ||
return null; | ||
}, | ||
renderImageOverlay: function renderImageOverlay() { | ||
return null; | ||
}, | ||
singleClickToZoom: false | ||
}; | ||
export default Lightbox; | ||
import e,{useEffect as t,useState as n,useRef as r}from"react";import i from"styled-components";import o from"@babel/runtime/helpers/slicedToArray";import a from"@babel/runtime/helpers/toConsumableArray";import{useSpring as l,config as c,to as s,animated as u,useSprings as f,useTransition as p}from"@react-spring/web";import{useGesture as m}from"react-use-gesture";import g from"@babel/runtime/helpers/defineProperty";import d from"@babel/runtime/helpers/classCallCheck";import v from"@babel/runtime/helpers/createClass";import h from"@babel/runtime/helpers/assertThisInitialized";import y from"@babel/runtime/helpers/inherits";import b from"@babel/runtime/helpers/possibleConstructorReturn";import w from"@babel/runtime/helpers/getPrototypeOf";import C from"react-dom";var P=function(e){var t=e.imageRef,n=e.scale,r=e.pinchDelta,i=o(e.touchOrigin,2),a=i[0],l=i[1],c=o(e.currentTranslate,2),s=c[0],u=c[1],f=t.current.getBoundingClientRect(),p=f.top;return[-((a-f.left-f.width/2)/n)*r+s,-((l-p-f.height/2)/n)*r+u]},O=function(e){var t=e.current.getBoundingClientRect(),n=t.top,r=t.left,i=t.bottom,o=t.right,a=window,l=a.innerHeight,c=a.innerWidth;return r>.5*c||n>.5*l||o<.5*c||i<.5*l};function x(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function k(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?x(Object(n),!0).forEach((function(t){g(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):x(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var E=function(i){var a,u=i.src,f=i.alt,p=i.pagerHeight,d=i.isCurrentImage,v=i.setDisableDrag,h=i.singleClickToZoom,y=i.pagerIsDragging,b=n(!1),w=o(b,2),C=w[0],x=w[1],E=r(),D=function(){return{scale:1,translateX:0,translateY:0,config:k(k({},c.default),{},{precision:.01})}},j=l((function(){return k(k({},D()),{},{onFrame:function(e){(e.scale<1||!e.pinching)&&A(D),e.scale>1&&O(E)&&A(D())},onRest:function(e){1===e.scale&&v(!1)}})})),T=o(j,2),N=T[0],R=N.scale,_=N.translateX,L=N.translateY,A=T[1];t((function(){d||A(D)}));var W=m({onPinch:function(e){var t=o(e.movement,1)[0],n=o(e.origin,2),r=n[0],i=n[1],a=e.event,l=e.ctrlKey,c=e.last,s=e.cancel;if(v(!0),t&&!C&&x(!0),c)s();else{var u=l?1e3:250,f=R.value+t/u,p=f-R.value,m=a.clientX,g=a.clientY,d=P({imageRef:E,scale:R.value,pinchDelta:p,currentTranslate:[_.value,L.value],touchOrigin:l?[m,g]:[r,i]}),h=o(d,2),y=h[0],b=h[1];A(f<.5?{scale:.5,pinching:!0}:f>3?{scale:3,pinching:!0}:{scale:f,translateX:y,translateY:b,pinching:!0})}},onPinchEnd:function(){R.value>1?v(!0):A(D),x(!1)},onDragEnd:function(){return x(!1)},onDrag:function(e){var t=o(e.movement,2),n=t[0],r=t[1],i=e.pinching,a=e.event,l=e.cancel,c=e.first,s=e.memo,u=void 0===s?{initialTranslateX:0,initialTranslateY:0}:s;if(n&&r&&!C&&x(!0),!(a.touches&&a.touches.length>1||i||R.value<=1))return R.value>1&&O(E)?void l():c?{initialTranslateX:_.value,initialTranslateY:L.value}:(A({translateX:u.initialTranslateX+n,translateY:u.initialTranslateY+r}),u)}},{domTarget:E,event:{passive:!1}});return t(W,[W]),function(e){var n=e.ref,r=e.latency,i=void 0===r?300:r,o=e.onSingleClick,a=void 0===o?function(){return null}:o,l=e.onDoubleClick,c=void 0===l?function(){return null}:l;t((function(){var e=n.current,t=0,r=function(e){t+=1,setTimeout((function(){1===t?a(e):2===t&&c(e),t=0}),i)};return e.addEventListener("click",r),function(){e.removeEventListener("click",r)}}))}((g(a={},h?"onSingleClick":"onDoubleClick",(function(e){if(y||C)e.stopPropagation();else if(1===R.value){var t=e.clientX,n=e.clientY,r=R.value+1,i=r-R.value,a=P({imageRef:E,scale:R.value,pinchDelta:i,currentTranslate:[_.value,L.value],touchOrigin:[t,n]}),l=o(a,2),c=l[0],s=l[1];v(!0),A({scale:r,translateX:c,translateY:s,pinching:!0})}else A(D)})),g(a,"ref",E),g(a,"latency",h?0:200),a)),e.createElement(I,{ref:E,className:"lightbox-image",style:k({transform:s([R,_,L],(function(e,t,n){return"translate(".concat(t,"px, ").concat(n,"px) scale(").concat(e,")")})),maxHeight:p},d&&{willChange:"transform"}),src:u,alt:f,draggable:"false",onDragStart:function(e){e.preventDefault()},onClick:function(e){e.stopPropagation(),e.nativeEvent.stopImmediatePropagation()}})},I=i(u.img).withConfig({displayName:"Image__AnimatedImage",componentId:"sc-1k35vrn-0"})(["width:auto;max-width:100%;user-select:none;::selection{background:none;}"]),D=function(i){var l=i.images,c=i.currentIndex,s=i.onPrev,u=i.onNext,p=i.onClose,g=i.renderImageOverlay,d=i.singleClickToZoom,v=r(!0),h=r(a(Array(l.length)).map((function(){return e.createRef()}))),y=function(){var e=n({width:window.innerWidth,height:window.innerHeight}),r=o(e,2),i=r[0],a=r[1];return t((function(){var e=function(){window.innerHeight===i.height&&window.innerWidth===i.width||a({width:window.innerWidth,height:window.innerHeight})};return window.addEventListener("resize",e),window.addEventListener("orientationchange",e),function(){window.removeEventListener("resize",e),window.addEventListener("orientationchange",e)}})),i}(),b=y.height,w=y.width,C=n(!1),P=o(C,2),O=P[0],x=P[1],k=n("100%"),I=o(k,2),D=I[0],_=I[1],L=n(!1),A=o(L,2),W=A[0],H=A[1],X=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,r=(e-c)*w+(t?n:0);return e<c-1||e>c+1?{x:r,display:"none"}:{x:r,display:"flex"}},B=f(l.length,X),S=o(B,2),Y=S[0],Z=S[1];t((function(){var e=h.current[c].current.clientHeight-50;D!==e&&_(e)}),[c,D,b]),t((function(){v.current?v.current=!1:Z(X)}));var z=m({onWheel:function(e){var t=e.distance,n=e.velocity,r=o(e.direction,2),i=r[0],a=r[1];if(!e.ctrlKey&&!O&&0!==n&&(t>w/3||n>1.5&&t<=w/3)){var l=i+a>0?-1:1;l>0?u():l<0&&s()}},onWheelEnd:function(){Z((function(e){return X(e,!1,0)})),H(!1)},onDrag:function(e){var t=e.down,n=o(e.movement,1)[0],r=o(e.direction,1)[0],i=e.velocity,a=e.distance,l=e.cancel,c=e.touches;if(!O&&0!==n){W||H(!0);var f=Math.abs(r)>.7;if(t&&f&&a>w/3.5||t&&f&&i>2){var p=r>0?-1:1;l(),p>0?u():p<0&&s()}c>1||Z((function(e){return X(e,t,n)}))}},onDragEnd:function(){return H(!1)}},{domTarget:h.current[c],event:{passive:!0,capture:!1}});return t(z,[z,c]),Y.map((function(t,n){var r=t.x,i=t.display;return e.createElement(N,{role:"presentation",ref:h.current[n],key:n,className:"lightbox-image-pager",style:{display:i,transform:r.to((function(e){return"translateX(".concat(e,"px)")}))},onClick:function(){return Math.abs(r.value)<1&&!O&&p()}},e.createElement(T,null,e.createElement(j,null,e.createElement(R,{onClick:function(e){e.stopPropagation(),e.nativeEvent.stopImmediatePropagation()}},e.createElement(E,{setDisableDrag:x,src:l[n].src,alt:l[n].alt,pagerHeight:D,isCurrentImage:n===c,pagerIsDragging:W,singleClickToZoom:d}),g()))))}))},j=i.div.withConfig({displayName:"ImagePager__PagerInnerContentWrapper",componentId:"a18fy4-0"})(["display:flex;justify-content:center;align-items:center;"]),T=i.div.withConfig({displayName:"ImagePager__PagerContentWrapper",componentId:"a18fy4-1"})(["width:100%;display:flex;justify-content:center;"]),N=i(u.div).withConfig({displayName:"ImagePager__AnimatedImagePager",componentId:"a18fy4-2"})(["position:absolute;top:0px;left:0px;right:0px;bottom:0px;height:100%;width:100%;will-change:transform;touch-action:none;display:flex;flex-direction:column;justify-content:center;align-items:center;"]),R=i.div.withConfig({displayName:"ImagePager__ImageContainer",componentId:"a18fy4-3"})(["position:relative;touch-action:none;user-select:none;"]),_=function(t){var n=t.images,r=t.currentIndex,i=t.onPrev,o=t.onNext,a=t.onClose,l=t.renderPrevButton,c=t.renderNextButton,s=t.renderImageOverlay,u=t.singleClickToZoom,f=r>0,p=r+1<n.length;return e.createElement(L,{className:"lightbox-image-stage"},l({canPrev:f}),e.createElement(D,{images:n,currentIndex:r,onClose:a,onNext:o,onPrev:i,renderImageOverlay:s,singleClickToZoom:u}),c({canNext:p}))},L=i.div.withConfig({displayName:"ImageStage__ImageStageContainer",componentId:"m6g63l-0"})(["flex-grow:1;position:relative;height:100%;width:100%;display:flex;justify-content:center;align-items:center;"]);function A(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function W(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?A(Object(n),!0).forEach((function(t){g(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):A(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var H=function(t){var n=t.children,r=t.isOpen,i=t.className,o=t.style,a=t.pageTransitionConfig,l={from:{transform:"scale(0.75)",opacity:0},enter:{transform:"scale(1)",opacity:1},leave:{transform:"scale(0.75)",opacity:0},config:W(W({},c.default),{},{mass:1,tension:320,friction:32})};return p(r,null,W(W({},l),a)).map((function(t){var r=t.item,a=t.key,l=t.props;return r&&e.createElement(X,{key:a,className:"lightbox-container".concat(i?" ".concat(i):""),style:W(W({},l),o)},n)}))},X=i(u.div).withConfig({displayName:"PageContainer__AnimatedPageContainer",componentId:"sc-19m9s01-0"})(["display:flex;flex-direction:column;position:fixed;z-index:400;top:0;bottom:0;left:0;right:0;"]);function B(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=w(e);if(t){var i=w(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return b(this,n)}}var S=function(e){y(n,e);var t=B(n);function n(){var e;d(this,n);for(var r=arguments.length,i=new Array(r),o=0;o<r;o++)i[o]=arguments[o];return e=t.call.apply(t,[this].concat(i)),g(h(e),"preventWheel",(function(e){return e.preventDefault()})),e}return v(n,[{key:"componentDidMount",value:function(){this.body=document.body,this.portalContainer=document.createElement("div"),this.portalContainer.setAttribute("class","lightbox-portal"),this.body.appendChild(this.portalContainer),this.forceUpdate(),this.portalContainer.addEventListener("wheel",this.preventWheel)}},{key:"componentWillUnmount",value:function(){this.portalContainer.removeEventListener("wheel",this.preventWheel),this.body.removeChild(this.portalContainer)}},{key:"render",value:function(){if(void 0===this.portalContainer)return null;var e=this.props.children;return C.createPortal(e,this.portalContainer)}}]),n}(e.Component),Y=function(n){var r=n.isOpen,i=n.onClose,o=n.images,a=n.currentIndex,l=n.onPrev,c=n.onNext,s=n.renderHeader,u=n.renderFooter,f=n.renderPrevButton,p=n.renderNextButton,m=n.renderImageOverlay,g=n.className,d=n.singleClickToZoom,v=n.style,h=n.pageTransitionConfig;return t((function(){var e=function(e){r&&["ArrowUp","ArrowDown","End","Home","PageUp","PageDown"].includes(e.key)&&e.preventDefault()},t=function(e){if(r)switch(e.key){case"ArrowLeft":l();break;case"ArrowRight":c();break;case"Escape":i();break;default:e.preventDefault()}};return document.addEventListener("keyup",t),document.addEventListener("keydown",e),function(){document.removeEventListener("keyup",t),document.removeEventListener("keydown",e)}})),e.createElement(S,null,e.createElement(H,{isOpen:r,className:g,style:v,pageTransitionConfig:h},s(),e.createElement(_,{images:o,onClose:i,currentIndex:a,onPrev:l,onNext:c,renderPrevButton:f,renderNextButton:p,renderImageOverlay:m,singleClickToZoom:d}),u()))};Y.defaultProps={pageTransitionConfig:null,className:null,style:null,renderHeader:function(){return null},renderFooter:function(){return null},renderPrevButton:function(){return null},renderNextButton:function(){return null},renderImageOverlay:function(){return null},singleClickToZoom:!1};export default Y; | ||
//# sourceMappingURL=index.es.js.map |
{ | ||
"name": "react-spring-lightbox", | ||
"version": "1.4.9", | ||
"version": "1.4.10-beta.0", | ||
"description": "A flexible image gallery lightbox with native-feeling touch gestures and buttery smooth animations, built with react-spring.", | ||
@@ -77,2 +77,3 @@ "author": "Tim Ellenberger <timellenberger@gmail.com>", | ||
"babel-eslint": "10.1.0", | ||
"babel-plugin-styled-components": "1.10.5", | ||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24", | ||
@@ -99,2 +100,3 @@ "babel-polyfill": "^6.26.0", | ||
"rollup-plugin-filesize": "^8.0.2", | ||
"rollup-plugin-terser": "^5.3.0", | ||
"styled-components": "^5.0.1" | ||
@@ -101,0 +103,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
137598
35
97
2
1