UNPKG

react-trilogo-images

Version:

A simple, responsive lightbox component for displaying an array of images with React.js with extended features

1,821 lines (1,571 loc) 52.1 kB
import PropTypes from 'prop-types'; import React, { Children, Component } from 'react'; import { StyleSheet, css } from 'aphrodite'; import ScrollLock from 'react-scrolllock'; import { StyleSheet as StyleSheet$1, css as css$1 } from 'aphrodite/no-important'; import { CSSTransitionGroup } from 'react-transition-group'; import { render, unmountComponentAtNode } from 'react-dom'; // ============================== // THEME // ============================== var theme = {}; // container theme.container = { background: 'rgba(0, 0, 0, 0.8)', gutter: { horizontal: 10, vertical: 10 }, zIndex: 2001 }; // header theme.header = { height: 40 }; theme.close = { fill: 'white' }; // footer theme.footer = { color: 'white', count: { color: 'rgba(255, 255, 255, 0.75)', fontSize: '0.85em' }, height: 'auto', gutter: { horizontal: 0, vertical: 5 } }; // thumbnails theme.thumbnail = { activeBorderColor: 'white', size: 50, gutter: 2 }; // arrow theme.arrow = { background: 'none', fill: 'white', height: 120 }; // toolbar action theme.toolbarAction = { background: 'none', fill: 'white', height: 60 }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var asyncGenerator = function () { function AwaitValue(value) { this.value = value; } function AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; if (value instanceof AwaitValue) { Promise.resolve(value.value).then(function (arg) { resume("next", arg); }, function (arg) { resume("throw", arg); }); } else { settle(result.done ? "return" : "normal", result.value); } } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } } if (typeof Symbol === "function" && Symbol.asyncIterator) { AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; } AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); }; AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); }; return { wrap: function (fn) { return function () { return new AsyncGenerator(fn.apply(this, arguments)); }; }, await: function (value) { return new AwaitValue(value); } }; }(); var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var objectWithoutProperties = function (obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); function deepMerge(target) { var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var extended = Object.assign({}, target); Object.keys(source).forEach(function (key) { if (_typeof(source[key]) !== 'object' || !source[key]) { extended[key] = source[key]; } else { if (!target[key]) { extended[key] = source[key]; } else { extended[key] = deepMerge(target[key], source[key]); } } }); return extended; } var arrowLeft = (function (fill) { return "<svg fill=\"" + fill + "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" xml:space=\"preserve\">\n\t\t<path d=\"M213.7,256L213.7,256L213.7,256L380.9,81.9c4.2-4.3,4.1-11.4-0.2-15.8l-29.9-30.6c-4.3-4.4-11.3-4.5-15.5-0.2L131.1,247.9 c-2.2,2.2-3.2,5.2-3,8.1c-0.1,3,0.9,5.9,3,8.1l204.2,212.7c4.2,4.3,11.2,4.2,15.5-0.2l29.9-30.6c4.3-4.4,4.4-11.5,0.2-15.8 L213.7,256z\"/>\n\t</svg>"; }); var arrowRight = (function (fill) { return "<svg fill=\"" + fill + "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" xml:space=\"preserve\">\n\t\t<path d=\"M298.3,256L298.3,256L298.3,256L131.1,81.9c-4.2-4.3-4.1-11.4,0.2-15.8l29.9-30.6c4.3-4.4,11.3-4.5,15.5-0.2l204.2,212.7 c2.2,2.2,3.2,5.2,3,8.1c0.1,3-0.9,5.9-3,8.1L176.7,476.8c-4.2,4.3-11.2,4.2-15.5-0.2L131.3,446c-4.3-4.4-4.4-11.5-0.2-15.8 L298.3,256z\"/>\n\t</svg>"; }); var close = (function (fill) { return "<svg fill=\"" + fill + "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" style=\"enable-background:new 0 0 512 512;\" xml:space=\"preserve\">\n\t\t<path d=\"M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4 L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1 c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1 c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z\"/>\n\t</svg>"; }); var zoomIn = (function (fill) { return "<svg fill=\"" + fill + "\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" x=\"0px\" y=\"0px\" width=\"40px\" height=\"40px\" viewBox=\"0 0 150 100\">\n\t<g>\n\t\t<path d=\"M51.6,96.7c11,0,21-3.9,28.8-10.5l35,35c0.8,0.8,1.8,1.2,2.9,1.2c1,0,2.1-0.4,2.9-1.2c1.6-1.6,1.6-4.2,0-5.8l-35-35 c6.5-7.8,10.5-17.9,10.5-28.8c0-24.9-20.2-45.1-45.1-45.1C26.8,6.5,6.5,26.8,6.5,51.6C6.5,76.5,26.8,96.7,51.6,96.7z M51.6,14.7 c20.4,0,36.9,16.6,36.9,36.9C88.5,72,72,88.5,51.6,88.5c-20.4,0-36.9-16.6-36.9-36.9C14.7,31.3,31.3,14.7,51.6,14.7z\"/>\n\t\t<path d=\"m47.5,78.3c0,2.3 1.8,4.1 4.1,4.1 2.3,0 4.1-1.8 4.1-4.1v-22.6h22.6c2.3,0 4.1-1.8 4.1-4.1s-1.8-4.1-4.1-4.1h-22.6v-22.6c0-2.3-1.8-4.1-4.1-4.1-2.3,0-4.1,1.8-4.1,4.1v22.6h-22.6c-2.3,0-4.1,1.8-4.1,4.1s1.8,4.1 4.1,4.1h22.6v22.6z\"/>\n\t</g>\n</svg>"; }); var zoomOut = (function (fill) { return "<svg fill=\"" + fill + "\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" x=\"0px\" y=\"0px\" width=\"40px\" height=\"40px\" viewBox=\"0 0 150 100\" xml:space=\"preserve\" class=\"\">\n\t<g>\n <path d=\"m51.6,6.5c-24.9,0-45.1,20.2-45.1,45.1 0,24.9 20.2,45.1 45.1,45.1 11,0 21-3.9 28.9-10.5l35.1,35.1c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2 1.6-1.6 1.6-4.2 0-5.8l-35.1-35c6.5-7.8 10.5-17.9 10.5-28.9-0.1-24.9-20.3-45.1-45.2-45.1zm0,82.1c-20.4,0-37-16.6-37-37 0-20.4 16.6-37 37-37 20.4,0 37,16.6 37,37 0,20.4-16.6,37-37,37z\" />\n <path d=\"m78.3,47.5h-53.4c-2.3,0-4.1,1.8-4.1,4.1s1.8,4.1 4.1,4.1h53.4c2.3,0 4.1-1.8 4.1-4.1s-1.8-4.1-4.1-4.1z\" />\n </g>\n</svg>"; }); var rotateLeft = (function (fill) { return "<svg fill=\"" + fill + "\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" x=\"0px\" y=\"0px\" width=\"40px\" height=\"40px\" viewBox=\"0 0 600 400\">\n\t<g><path d=\"M261.397,17.983c-88.909,0-167.372,51.302-203.909,129.073L32.072,94.282L0,109.73l52.783,109.565l109.565-52.786 l-15.451-32.066L89.82,161.934c30.833-65.308,96.818-108.353,171.577-108.353c104.668,0,189.818,85.154,189.818,189.821 s-85.15,189.824-189.818,189.824c-61.631,0-119.663-30.109-155.228-80.539l-29.096,20.521 c42.241,59.87,111.143,95.613,184.324,95.613c124.286,0,225.407-101.122,225.407-225.419S385.684,17.983,261.397,17.983z\" /></g>\n\t\t</svg>"; }); var rotateRight = (function (fill) { return "<svg fill=\"" + fill + "\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" x=\"0px\" y=\"0px\" width=\"40px\" height=\"40px\" viewBox=\"0 0 100 70\">\n\t\t<g><path d=\"M37.242,2.971c14.689,0,27.651,8.476,33.688,21.325l4.201-8.719l5.298,2.552l-8.721,18.102L53.605,27.51l2.553-5.298 l9.43,4.542C60.494,15.964,49.594,8.853,37.242,8.853C19.95,8.853,5.88,22.921,5.88,40.214s14.07,31.362,31.362,31.362 c10.183,0,19.769-4.975,25.647-13.307l4.805,3.391c-6.979,9.892-18.361,15.797-30.452,15.797C16.707,77.458,0,60.75,0,40.214 S16.707,2.971,37.242,2.971z\"/></g>\n\t\t</svg>"; }); var save = (function (fill) { return "<svg fill=\"" + fill + "\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" x=\"0px\" y=\"0px\" width=\"40px\" height=\"40px\" viewBox=\"0 0 650 400\">\n\t<g>\n\t\t<path d=\"M473.7,485.75c6.8,0,12.3-5.5,12.3-12.3v-359.8c0-3.6-1.6-7-4.3-9.3L363,2.85c-0.2-0.2-0.4-0.3-0.6-0.4 c-0.3-0.2-0.5-0.4-0.8-0.6c-0.4-0.2-0.7-0.4-1.1-0.6c-0.3-0.1-0.6-0.3-0.9-0.4c-0.4-0.2-0.9-0.3-1.3-0.4c-0.3-0.1-0.6-0.2-0.9-0.2 c-0.8-0.1-1.5-0.2-2.3-0.2H12.3C5.5,0.05,0,5.55,0,12.35v461.3c0,6.8,5.5,12.3,12.3,12.3h461.4V485.75z M384.5,461.25h-283v-184.1 c0-3.7,3-6.6,6.6-6.6h269.8c3.7,0,6.6,3,6.6,6.6V461.25z M161.8,24.45h180.9v127.8c0,0.8-0.6,1.4-1.4,1.4h-178 c-0.8,0-1.4-0.7-1.4-1.4V24.45H161.8z M24.6,24.45h112.8v127.8c0,14.3,11.6,25.9,25.9,25.9h178c14.3,0,25.9-11.6,25.9-25.9V38.75 l94.2,80.6v341.9H409v-184.1c0-17.2-14-31.1-31.1-31.1H108.1c-17.2,0-31.1,14-31.1,31.1v184.2H24.6V24.45z\" />\n\t\t<path d=\"M227.4,77.65h53.8v32.6c0,6.8,5.5,12.3,12.3,12.3s12.3-5.5,12.3-12.3v-44.8c0-6.8-5.5-12.3-12.3-12.3h-66.1 c-6.8,0-12.3,5.5-12.3,12.3S220.7,77.65,227.4,77.65z\" />\n\t\t<path d=\"M304.5,322.85h-123c-6.8,0-12.3,5.5-12.3,12.3s5.5,12.3,12.3,12.3h123c6.8,0,12.3-5.5,12.3-12.3 S311.3,322.85,304.5,322.85z\" />\n\t\t<path d=\"M304.5,387.75h-123c-6.8,0-12.3,5.5-12.3,12.3s5.5,12.3,12.3,12.3h123c6.8,0,12.3-5.5,12.3-12.3 S311.3,387.75,304.5,387.75z\" />\n\t</g>\n</svg>"; }); var icons = { arrowLeft: arrowLeft, arrowRight: arrowRight, close: close, zoomIn: zoomIn, zoomOut: zoomOut, rotateLeft: rotateLeft, rotateRight: rotateRight, save: save }; var Icon = function Icon(_ref) { var fill = _ref.fill, type = _ref.type, props = objectWithoutProperties(_ref, ['fill', 'type']); var icon = icons[type]; return React.createElement('span', _extends({ dangerouslySetInnerHTML: { __html: icon(fill) } }, props)); }; Icon.propTypes = { fill: PropTypes.string, type: PropTypes.any }; Icon.defaultProps = { fill: 'white' }; var ActionType = { zoomIn: 1, zoomOut: 2, prev: 3, next: 4, rotateLeft: 5, rotateRight: 6, reset: 7, close: 8, scaleX: 9, scaleY: 10, save: 11 }; function Arrow(_ref, _ref2) { var theme$$1 = _ref2.theme; var direction = _ref.direction, icon = _ref.icon, onClick = _ref.onClick, size = _ref.size, props = objectWithoutProperties(_ref, ['direction', 'icon', 'onClick', 'size']); var classes = StyleSheet$1.create(deepMerge(defaultStyles$1, theme$$1)); return React.createElement( 'button', _extends({ type: 'button', className: css$1(classes.arrow, classes['arrow__direction__' + direction], size && classes['arrow__size__' + size]), onClick: onClick, onTouchEnd: onClick }, props), React.createElement(Icon, { fill: !!theme$$1.arrow && theme$$1.arrow.fill || theme.arrow.fill, type: icon }) ); } Arrow.propTypes = { direction: PropTypes.oneOf(['left', 'right']), icon: PropTypes.string, onClick: PropTypes.func.isRequired, size: PropTypes.oneOf(['medium', 'small']).isRequired }; Arrow.defaultProps = { size: 'medium' }; Arrow.contextTypes = { theme: PropTypes.object.isRequired }; var defaultStyles$1 = { arrow: { background: 'none', border: 'none', borderRadius: 4, cursor: 'pointer', outline: 'none', padding: 10, // increase hit area position: 'absolute', top: '50%', // disable user select WebkitTouchCallout: 'none', userSelect: 'none' }, // sizes arrow__size__medium: { height: theme.arrow.height, marginTop: theme.arrow.height / -2, width: 40, '@media (min-width: 768px)': { width: 70 } }, arrow__size__small: { height: theme.thumbnail.size, marginTop: theme.thumbnail.size / -2, width: 30, '@media (min-width: 500px)': { width: 40 } }, // direction arrow__direction__right: { right: theme.container.gutter.horizontal }, arrow__direction__left: { left: theme.container.gutter.horizontal } }; function Container(_ref, _ref2) { var theme$$1 = _ref2.theme; var props = objectWithoutProperties(_ref, []); var classes = StyleSheet$1.create(deepMerge(defaultStyles$2, theme$$1)); return React.createElement('div', _extends({ id: 'lightboxBackdrop', className: css$1(classes.container) }, props)); } Container.contextTypes = { theme: PropTypes.object.isRequired }; var defaultStyles$2 = { container: { alignItems: 'center', backgroundColor: theme.container.background, boxSizing: 'border-box', display: 'flex', height: '100%', justifyContent: 'center', left: 0, paddingBottom: theme.container.gutter.vertical, paddingLeft: theme.container.gutter.horizontal, paddingRight: theme.container.gutter.horizontal, paddingTop: theme.container.gutter.vertical, position: 'fixed', top: 0, width: '100%', zIndex: theme.container.zIndex } }; function ToolbarAction(_ref, _ref2) { var theme$$1 = _ref2.theme; var icon = _ref.icon, onClick = _ref.onClick, props = objectWithoutProperties(_ref, ['icon', 'onClick']); var classes = StyleSheet$1.create(deepMerge(defaultStyles$4, theme$$1)); return React.createElement( 'button', _extends({ type: 'button', className: css$1(classes.actionButton), onClick: onClick, onTouchEnd: onClick }, props), React.createElement(Icon, { fill: !!theme$$1.toolbarAction && theme$$1.toolbarAction.fill || theme.toolbarAction.fill, type: icon }) ); } ToolbarAction.propTypes = { icon: PropTypes.string, onClick: PropTypes.func.isRequired, type: PropTypes.oneOf(['zoomIn', 'zoomOut', 'rotateLeft', 'rotateRight']) }; ToolbarAction.contextTypes = { theme: PropTypes.object.isRequired }; var defaultStyles$4 = { actionButton: { background: 'none', border: 'none', borderRadius: 4, cursor: 'pointer', outline: 'none', padding: 10, // increase hit area // disable user select WebkitTouchCallout: 'none', userSelect: 'none' } }; var Footer = function (_React$Component) { inherits(Footer, _React$Component); function Footer(props) { classCallCheck(this, Footer); return possibleConstructorReturn(this, (Footer.__proto__ || Object.getPrototypeOf(Footer)).call(this, props)); } createClass(Footer, [{ key: 'handleAction', value: function handleAction(type) { this.props.onAction(type); } }, { key: 'render', value: function render$$1() { var _this2 = this; var _props = this.props, caption = _props.caption, countCurrent = _props.countCurrent, countSeparator = _props.countSeparator, countTotal = _props.countTotal, showCount = _props.showCount, zoomable = _props.zoomable, rotatable = _props.rotatable, savable = _props.savable; if (!caption && !showCount) return null; var classes = StyleSheet$1.create(deepMerge(defaultStyles$3, this.context.theme)); var imageCount = showCount ? React.createElement( 'div', { className: css$1(classes.footerCount) }, countCurrent, countSeparator, countTotal ) : React.createElement('span', null); var featureNodeArr = []; if (zoomable) { featureNodeArr = featureNodeArr.concat([React.createElement( 'li', { key: 'zoomIn', className: css$1(classes.footerActionBtn) }, React.createElement(ToolbarAction, { icon: 'zoomIn', onClick: function onClick() { _this2.handleAction(ActionType.zoomIn); } }) ), React.createElement( 'li', { key: 'zoomOut', className: css$1(classes.footerActionBtn) }, React.createElement(ToolbarAction, { icon: 'zoomOut', onClick: function onClick() { _this2.handleAction(ActionType.zoomOut); } }) )]); } if (rotatable) { featureNodeArr = featureNodeArr.concat([React.createElement( 'li', { key: 'rotateLeft', className: css$1(classes.footerActionBtn) }, React.createElement(ToolbarAction, { icon: 'rotateLeft', onClick: function onClick() { _this2.handleAction(ActionType.rotateLeft); } }) ), React.createElement( 'li', { key: 'rotateRight', className: css$1(classes.footerActionBtn) }, React.createElement(ToolbarAction, { icon: 'rotateRight', onClick: function onClick() { _this2.handleAction(ActionType.rotateRight); } }) )]); } if (savable) { featureNodeArr = featureNodeArr.concat([React.createElement( 'li', { key: 'save', className: css$1(classes.footerActionBtn) }, React.createElement(ToolbarAction, { icon: 'save', onClick: function onClick() { _this2.handleAction(ActionType.save); } }) )]); } return React.createElement( 'div', { className: css$1(classes.footer) }, React.createElement( 'div', { className: css$1(classes.footerInfos) }, caption ? React.createElement( 'figcaption', { className: css$1(classes.footerCaption) }, caption ) : React.createElement('span', { className: css$1(classes.footerCaption) }), React.createElement( 'ul', { className: css$1(classes.actions) }, featureNodeArr ), imageCount ), React.createElement('div', { className: css$1(classes.footerActions) }) ); } }]); return Footer; }(React.Component); Footer.propTypes = { caption: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), countCurrent: PropTypes.number, countSeparator: PropTypes.string, countTotal: PropTypes.number, onAction: PropTypes.func, rotatable: PropTypes.bool, showCount: PropTypes.bool, zoomable: PropTypes.bool }; Footer.contextTypes = { theme: PropTypes.object.isRequired }; var defaultStyles$3 = { footer: { position: 'absolute', bottom: 35, width: '100%', height: '60px' }, footerInfos: { boxSizing: 'border-box', color: theme.footer.color, cursor: 'auto', display: 'flex', justifyContent: 'space-between', left: 0, lineHeight: 1.3, paddingBottom: theme.footer.gutter.vertical, paddingLeft: theme.footer.gutter.horizontal, paddingRight: theme.footer.gutter.horizontal, paddingTop: theme.footer.gutter.vertical, zIndex: 1100, alignItems: 'center' }, footerCount: { color: theme.footer.count.color, fontSize: theme.footer.count.fontSize, paddingLeft: '1em', // add a small gutter for the caption flex: 1, textAlign: 'right' }, footerCaption: { flex: 1 }, footerActions: { position: 'relative', flex: 1 }, actions: { display: 'flex', listStyle: 'none', justifyContent: 'center', padding: 0, margin: 0 } }; function Header(_ref, _ref2) { var theme$$1 = _ref2.theme; var customControls = _ref.customControls, onClose = _ref.onClose, showCloseButton = _ref.showCloseButton, closeButtonTitle = _ref.closeButtonTitle, props = objectWithoutProperties(_ref, ['customControls', 'onClose', 'showCloseButton', 'closeButtonTitle']); var classes = StyleSheet$1.create(deepMerge(defaultStyles$5, theme$$1)); return React.createElement( 'div', _extends({ className: css$1(classes.header) }, props), customControls ? customControls : React.createElement('span', null), !!showCloseButton && React.createElement( 'button', { title: closeButtonTitle, className: css$1(classes.close), onClick: onClose }, React.createElement(Icon, { fill: !!theme$$1.close && theme$$1.close.fill || theme.close.fill, type: 'close' }) ) ); } Header.propTypes = { customControls: PropTypes.array, onClose: PropTypes.func.isRequired, showCloseButton: PropTypes.bool }; Header.contextTypes = { theme: PropTypes.object.isRequired }; var defaultStyles$5 = { header: { display: 'flex', justifyContent: 'space-between', height: theme.header.height }, close: { background: 'none', border: 'none', cursor: 'pointer', outline: 'none', position: 'relative', top: 0, verticalAlign: 'bottom', zIndex: 1, // increase hit area height: 40, marginRight: -10, padding: 10, width: 40 } }; function Thumbnail(_ref, _ref2) { var index = _ref.index, src = _ref.src, thumbnail = _ref.thumbnail, active = _ref.active, _onClick = _ref.onClick; var theme$$1 = _ref2.theme; var url = thumbnail ? thumbnail : src; var classes = StyleSheet$1.create(deepMerge(defaultStyles$6, theme$$1)); return React.createElement('div', { className: css$1(classes.thumbnail, active && classes.thumbnail__active), onClick: function onClick(e) { e.preventDefault(); e.stopPropagation(); _onClick(index); }, style: { backgroundImage: 'url("' + url + '")' } }); } Thumbnail.propTypes = { active: PropTypes.bool, index: PropTypes.number, onClick: PropTypes.func.isRequired, src: PropTypes.string, thumbnail: PropTypes.string }; Thumbnail.contextTypes = { theme: PropTypes.object.isRequired }; var defaultStyles$6 = { thumbnail: { backgroundPosition: 'center', backgroundSize: 'cover', borderRadius: 2, boxShadow: 'inset 0 0 0 1px hsla(0,0%,100%,.2)', cursor: 'pointer', display: 'inline-block', height: theme.thumbnail.size, margin: theme.thumbnail.gutter, overflow: 'hidden', width: theme.thumbnail.size }, thumbnail__active: { boxShadow: 'inset 0 0 0 2px ' + theme.thumbnail.activeBorderColor } }; var classes = StyleSheet$1.create({ paginatedThumbnails: { bottom: theme.container.gutter.vertical, height: theme.thumbnail.size, padding: '0 50px', position: 'absolute', textAlign: 'center', whiteSpace: 'nowrap', left: '50%', transform: 'translateX(-50%)' } }); var arrowStyles = { height: theme.thumbnail.size + theme.thumbnail.gutter * 2, width: 40 }; var PaginatedThumbnails = function (_Component) { inherits(PaginatedThumbnails, _Component); function PaginatedThumbnails(props) { classCallCheck(this, PaginatedThumbnails); var _this = possibleConstructorReturn(this, (PaginatedThumbnails.__proto__ || Object.getPrototypeOf(PaginatedThumbnails)).call(this, props)); _this.state = { hasCustomPage: false }; _this.gotoPrev = _this.gotoPrev.bind(_this); _this.gotoNext = _this.gotoNext.bind(_this); return _this; } createClass(PaginatedThumbnails, [{ key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { // Component should be controlled, flush state when currentImage changes if (nextProps.currentImage !== this.props.currentImage) { this.setState({ hasCustomPage: false }); } } // ============================== // METHODS // ============================== }, { key: 'getFirst', value: function getFirst() { var _props = this.props, currentImage = _props.currentImage, offset = _props.offset; if (this.state.hasCustomPage) { return this.clampFirst(this.state.first); } return this.clampFirst(currentImage - offset); } }, { key: 'setFirst', value: function setFirst(event, newFirst) { var first = this.state.first; if (event) { event.preventDefault(); event.stopPropagation(); } if (first === newFirst) return; this.setState({ hasCustomPage: true, first: newFirst }); } }, { key: 'gotoPrev', value: function gotoPrev(event) { this.setFirst(event, this.getFirst() - this.props.offset); } }, { key: 'gotoNext', value: function gotoNext(event) { this.setFirst(event, this.getFirst() + this.props.offset); } }, { key: 'clampFirst', value: function clampFirst(value) { var _props2 = this.props, images = _props2.images, offset = _props2.offset; var totalCount = 2 * offset + 1; // show $offset extra thumbnails on each side if (value < 0) { return 0; } else if (value + totalCount > images.length) { // Too far return images.length - totalCount; } else { return value; } } // ============================== // RENDERERS // ============================== }, { key: 'renderArrowPrev', value: function renderArrowPrev() { if (this.getFirst() <= 0) return null; return React.createElement(Arrow, { direction: 'left', size: 'small', icon: 'arrowLeft', onClick: this.gotoPrev, style: arrowStyles, title: 'Previous (Left arrow key)', type: 'button' }); } }, { key: 'renderArrowNext', value: function renderArrowNext() { var _props3 = this.props, offset = _props3.offset, images = _props3.images; var totalCount = 2 * offset + 1; if (this.getFirst() + totalCount >= images.length) return null; return React.createElement(Arrow, { direction: 'right', size: 'small', icon: 'arrowRight', onClick: this.gotoNext, style: arrowStyles, title: 'Next (Right arrow key)', type: 'button' }); } }, { key: 'render', value: function render$$1() { var _props4 = this.props, images = _props4.images, currentImage = _props4.currentImage, onClickThumbnail = _props4.onClickThumbnail, offset = _props4.offset; var totalCount = 2 * offset + 1; // show $offset extra thumbnails on each side var thumbnails = []; var baseOffset = 0; if (images.length <= totalCount) { thumbnails = images; } else { // Try to center current image in list baseOffset = this.getFirst(); thumbnails = images.slice(baseOffset, baseOffset + totalCount); } return React.createElement( 'div', { className: css$1(classes.paginatedThumbnails) }, this.renderArrowPrev(), thumbnails.map(function (img, idx) { return React.createElement(Thumbnail, _extends({ key: baseOffset + idx }, img, { index: baseOffset + idx, onClick: onClickThumbnail, active: baseOffset + idx === currentImage })); }), this.renderArrowNext() ); } }]); return PaginatedThumbnails; }(Component); PaginatedThumbnails.propTypes = { currentImage: PropTypes.number, images: PropTypes.array, offset: PropTypes.number, onClickThumbnail: PropTypes.func.isRequired }; // Pass the Lightbox context through to the Portal's descendents // StackOverflow discussion http://goo.gl/oclrJ9 var PassContext = function (_Component) { inherits(PassContext, _Component); function PassContext() { classCallCheck(this, PassContext); return possibleConstructorReturn(this, (PassContext.__proto__ || Object.getPrototypeOf(PassContext)).apply(this, arguments)); } createClass(PassContext, [{ key: 'getChildContext', value: function getChildContext() { return this.props.context; } }, { key: 'render', value: function render$$1() { return Children.only(this.props.children); } }]); return PassContext; }(Component); PassContext.propTypes = { context: PropTypes.object.isRequired }; PassContext.childContextTypes = { theme: PropTypes.object }; var Portal = function (_Component) { inherits(Portal, _Component); function Portal() { classCallCheck(this, Portal); var _this = possibleConstructorReturn(this, (Portal.__proto__ || Object.getPrototypeOf(Portal)).call(this)); _this.portalElement = null; return _this; } createClass(Portal, [{ key: 'componentDidMount', value: function componentDidMount() { var p = document.createElement('div'); document.body.appendChild(p); this.portalElement = p; this.componentDidUpdate(); } }, { key: 'componentDidUpdate', value: function componentDidUpdate() { // Animate fade on mount/unmount var duration = 200; var styles = '\n\t\t\t\t.fade-enter { opacity: 0.01; }\n\t\t\t\t.fade-enter.fade-enter-active { opacity: 1; transition: opacity ' + duration + 'ms; }\n\t\t\t\t.fade-leave { opacity: 1; }\n\t\t\t\t.fade-leave.fade-leave-active { opacity: 0.01; transition: opacity ' + duration + 'ms; }\n\t\t'; render(React.createElement( PassContext, { context: this.context }, React.createElement( 'div', null, React.createElement( 'style', null, styles ), React.createElement(CSSTransitionGroup, _extends({ component: 'div', transitionName: 'fade', transitionEnterTimeout: duration, transitionLeaveTimeout: duration }, this.props)) ) ), this.portalElement); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { unmountComponentAtNode(this.portalElement); document.body.removeChild(this.portalElement); } }, { key: 'render', value: function render$$1() { return null; } }]); return Portal; }(Component); Portal.contextTypes = { theme: PropTypes.object.isRequired }; var Spinner = function Spinner(props) { var classes = StyleSheet$1.create(styles(props)); return React.createElement( 'div', { className: css$1(classes.spinner) }, React.createElement('div', { className: css$1(classes.ripple) }) ); }; Spinner.propTypes = { color: PropTypes.string, size: PropTypes.number }; var rippleKeyframes = { '0%': { top: '50%', left: '50%', width: 0, height: 0, opacity: 1 }, '100%': { top: 0, left: 0, width: '100%', height: '100%', opacity: 0 } }; var styles = function styles(_ref) { var color = _ref.color, size = _ref.size; return { spinner: { display: 'inline-block', position: 'relative', width: size, height: size }, ripple: { position: 'absolute', border: '4px solid ' + color, opacity: 1, borderRadius: '50%', animationName: rippleKeyframes, animationDuration: '1s', animationTimingFunction: 'cubic-bezier(0, 0.2, 0.8, 1)', animationIterationCount: 'infinite' } }; }; /** Bind multiple component methods: * @param {this} context * @param {Array} functions constructor() { ... bindFunctions.call(this, ['handleClick', 'handleOther']); } */ function bindFunctions(functions) { var _this = this; functions.forEach(function (f) { return _this[f] = _this[f].bind(_this); }); } // Return true if window + document var canUseDom = !!(typeof window !== 'undefined' && window.document && window.document.createElement); // consumers sometimes provide incorrect type or casing function normalizeSourceSet(data) { var sourceSet = data.srcSet || data.srcset; if (Array.isArray(sourceSet)) { return sourceSet.join(); } return sourceSet; } var Lightbox = function (_Component) { inherits(Lightbox, _Component); function Lightbox(props) { classCallCheck(this, Lightbox); var _this = possibleConstructorReturn(this, (Lightbox.__proto__ || Object.getPrototypeOf(Lightbox)).call(this, props)); _this.theme = deepMerge(theme, props.theme); _this.classes = StyleSheet.create(deepMerge(defaultStyles, _this.theme)); _this.state = { imageLoaded: false, left: null, top: 15, width: 0, height: 0, rotate: 0, imageWidth: 0, imageHeight: 0, scaleX: 1, scaleY: 1 }; _this.containerWidth = 0; _this.containerHeight = 0; _this.footerHeight = 84; _this.setContainerWidthHeight(); bindFunctions.call(_this, ['gotoNext', 'gotoPrev', 'closeBackdrop', 'handleKeyboardInput', 'handleImageLoaded', 'handleAction', 'getImageCenterXY', 'handleZoom', 'handleRotate']); return _this; } createClass(Lightbox, [{ key: 'getChildContext', value: function getChildContext() { return { theme: this.theme }; } }, { key: 'componentDidMount', value: function componentDidMount() { if (this.props.isOpen) { if (this.props.enableKeyboardInput) { window.addEventListener('keydown', this.handleKeyboardInput); } if (typeof this.props.currentImage === 'number') { this.preloadImage(this.props.currentImage); } } } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (!canUseDom) return; if (this.props.isOpen && nextProps.isOpen) { this.preloadImage(this.props.currentImage, nextProps); } // preload current image if (this.props.currentImage !== nextProps.currentImage || !this.props.isOpen && nextProps.isOpen) { var img = this.preloadImage(nextProps.currentImage, nextProps); if (img && img.complete) { this.setState({ imageLoaded: img.complete }); } } // add/remove event listeners if (!this.props.isOpen && nextProps.isOpen && nextProps.enableKeyboardInput) { window.addEventListener('keydown', this.handleKeyboardInput); } if (!nextProps.isOpen && nextProps.enableKeyboardInput) { window.removeEventListener('keydown', this.handleKeyboardInput); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { if (this.props.enableKeyboardInput) { window.removeEventListener('keydown', this.handleKeyboardInput); } } // ============================== // METHODS // ============================== }, { key: 'setContainerWidthHeight', value: function setContainerWidthHeight() { this.containerWidth = window.innerWidth; this.containerHeight = window.innerHeight; } }, { key: 'getImageCenterXY', value: function getImageCenterXY() { return { x: this.state.left + this.state.width / 2, y: this.state.top + this.state.height / 2 }; } }, { key: 'getImgWidthHeight', value: function getImgWidthHeight(imgWidth, imgHeight) { var width = 0; var height = 0; var maxWidth = this.containerWidth * 0.8; var maxHeight = (this.containerHeight - this.footerHeight) * 0.8; width = Math.min(maxWidth, imgWidth); height = width / imgWidth * imgHeight; if (height > maxHeight) { height = maxHeight; width = height / imgHeight * imgWidth; } return [width, height]; } }, { key: 'handleAction', value: function handleAction(type) { switch (type) { case ActionType.prev: if (this.state.activeIndex - 1 >= 0) { this.handleChangeImg(this.state.activeIndex - 1); } break; case ActionType.next: if (this.state.activeIndex + 1 < this.props.images.length) { this.handleChangeImg(this.state.activeIndex + 1); } break; case ActionType.zoomIn: var imgCenterXY = this.getImageCenterXY(); this.handleZoom(imgCenterXY.x, imgCenterXY.y, 1, 0.1); this.props.onZoomIn && this.props.onZoomIn(); break; case ActionType.zoomOut: var imgCenterXY2 = this.getImageCenterXY(); this.handleZoom(imgCenterXY2.x, imgCenterXY2.y, -1, 0.1); this.props.onZoomOut && this.props.onZoomOut(); break; case ActionType.rotateLeft: this.handleRotate(); this.props.onRotateLeft && this.props.onRotateLeft(); break; case ActionType.rotateRight: this.handleRotate(true); this.props.onRotateRight && this.props.onRotateRight(); break; case ActionType.reset: this.loadImg(this.state.activeIndex); break; case ActionType.scaleX: this.handleScaleX(-1); break; case ActionType.scaleY: this.handleScaleY(-1); break; case ActionType.save: this.props.onSave && this.props.onSave(this.props.currentImage, { zoom: this.state.scaleX, rotation: this.state.rotate }); break; default: break; } } }, { key: 'handleZoom', value: function handleZoom(targetX, targetY, direct, scale) { var imgCenterXY = this.getImageCenterXY(); var diffX = targetX - imgCenterXY.x; var diffY = targetY - imgCenterXY.y; // when image width is 0, set original width var top = 0; var left = 0; var width = 0; var height = 0; var scaleX = 0; var scaleY = 0; if (this.state.width === 0) { var _getImgWidthHeight = this.getImgWidthHeight(this.state.imageWidth, this.state.imageHeight), _getImgWidthHeight2 = slicedToArray(_getImgWidthHeight, 2), imgWidth = _getImgWidthHeight2[0], imgHeight = _getImgWidthHeight2[1]; left = (this.containerWidth - imgWidth) / 2; top = (this.containerHeight - this.footerHeight - imgHeight) / 2; width = this.state.width + imgWidth; height = this.state.height + imgHeight; scaleX = scaleY = 1; } else { var directX = this.state.scaleX > 0 ? 1 : -1; var directY = this.state.scaleY > 0 ? 1 : -1; scaleX = this.state.scaleX + scale * direct * directX; scaleY = this.state.scaleY + scale * direct * directY; if (Math.abs(scaleX) < 0.1 || Math.abs(scaleY) < 0.1) { return; } top = this.state.top + -direct * diffY / this.state.scaleX * scale * directX; left = this.state.left + -direct * diffX / this.state.scaleY * scale * directY; width = this.state.width; height = this.state.height; } this.setState({ width: width, scaleX: Math.floor(scaleX * 10) / 10, scaleY: Math.floor(scaleY * 10) / 10, height: height, top: top, left: left, loading: false }); } }, { key: 'handleRotate', value: function handleRotate() { var isRight = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this.setState({ rotate: (this.state.rotate + 90 * (isRight ? 1 : -1)) % 360 }); } }, { key: 'preloadImage', value: function preloadImage(idx, nextProps) { var _this2 = this; var data = nextProps ? nextProps.images[idx] : this.props.images[idx]; if (!data) return; var img = new Image(); var sourceSet = normalizeSourceSet(data); // TODO: add error handling for missing images img.onerror = function () { _this2.setState({ imageLoaded: true }); }; img.onload = function () { var imgWidth = img.width; var imgHeight = img.height; var _getImgWidthHeight3 = _this2.getImgWidthHeight(imgWidth, imgHeight), _getImgWidthHeight4 = slicedToArray(_getImgWidthHeight3, 2), width = _getImgWidthHeight4[0], height = _getImgWidthHeight4[1]; var left = (_this2.containerWidth - width) / 2; var top = (_this2.containerHeight - height - _this2.footerHeight) / 2; _this2.setState({ width: width, height: height, left: left, top: top, imageWidth: imgWidth, imageHeight: imgHeight, rotate: data.initialRotation || 0, scaleX: data.initialZoom || 1, scaleY: data.initialZoom || 1, imageLoaded: true }); }; img.src = data.src; if (sourceSet) img.srcset = sourceSet; return img; } }, { key: 'gotoNext', value: function gotoNext(event) { var _props = this.props, currentImage = _props.currentImage, images = _props.images; var imageLoaded = this.state.imageLoaded; if (!imageLoaded || currentImage === images.length - 1) return; if (event) { event.preventDefault(); event.stopPropagation(); } this.props.onClickNext(); } }, { key: 'gotoPrev', value: function gotoPrev(event) { var currentImage = this.props.currentImage; var imageLoaded = this.state.imageLoaded; if (!imageLoaded || currentImage === 0) return; if (event) { event.preventDefault(); event.stopPropagation(); } this.props.onClickPrev(); } }, { key: 'closeBackdrop', value: function closeBackdrop(event) { // make sure event only happens if they click the backdrop // and if the caption is widening the figure element let that respond too if (event.target.id === 'lightboxBackdrop' || event.target.tagName === 'FIGURE') { this.props.onClose(); } } }, { key: 'handleKeyboardInput', value: function handleKeyboardInput(event) { if (event.keyCode === 37) { // left this.gotoPrev(event); return true; } else if (event.keyCode === 39) { // right this.gotoNext(event); return true; } else if (event.keyCode === 27) { // esc this.props.onClose(); return true; } return false; } }, { key: 'handleImageLoaded', value: function handleImageLoaded() { this.setState({ imageLoaded: true }); } // ============================== // RENDERERS // ============================== }, { key: 'renderArrowPrev', value: function renderArrowPrev() { if (this.props.currentImage === 0) return null; return React.createElement(Arrow, { direction: 'left', icon: 'arrowLeft', onClick: this.gotoPrev, title: this.props.leftArrowTitle, type: 'button' }); } }, { key: 'renderArrowNext', value: function renderArrowNext() { if (this.props.currentImage === this.props.images.length - 1) return null; return React.createElement(Arrow, { direction: 'right', icon: 'arrowRight', onClick: this.gotoNext, title: this.props.rightArrowTitle, type: 'button' }); } }, { key: 'renderDialog', value: function renderDialog() { var _props2 = this.props, backdropClosesModal = _props2.backdropClosesModal, isOpen = _props2.isOpen, showThumbnails = _props2.showThumbnails, width = _props2.width; var imageLoaded = this.state.imageLoaded; if (!isOpen) return React.createElement('span', { key: 'closed' }); var offsetThumbnails = 0; if (showThumbnails) { offsetThumbnails = this.theme.thumbnail.size + this.theme.container.gutter.vertical; } return React.createElement( Container, { key: 'open', onClick: backdropClosesModal && this.closeBackdrop, onTouchEnd: backdropClosesModal && this.closeBackdrop }, React.createElement( 'div', null, React.createElement( 'div', { className: css(this.classes.content), style: { marginBottom: offsetThumbnails, maxWidth: width } }, imageLoaded && this.renderHeader(), this.renderImages(), this.renderSpinner(), imageLoaded && this.renderFooter() ), imageLoaded && this.renderThumbnails(), imageLoaded && this.renderArrowPrev(), imageLoaded && this.renderArrowNext(), this.props.preventScroll && React.createElement(ScrollLock, null) ) ); } }, { key: 'renderImages', value: function renderImages() { var _this3 = this; var _props3 = this.props, currentImage = _props3.currentImage, images = _props3.images, onClickImage = _props3.onClickImage; var imageLoaded = this.state.imageLoaded; if (!images || !images.length) return null; var image = images[currentImage]; var sourceSet = normalizeSourceSet(image); var sizes = sourceSet ? '100vw' : null; var imgStyle = { width: this.state.width + 'px', height: 'auto', transform: 'rotate(' + this.state.rotate + 'deg) scaleX(' + this.state.scaleX + ') scaleY(' + this.state.scaleY + ')' }; return React.createElement( 'figure', { className: css(this.classes.figure) }, React.createElement('img', { className: css(this.classes.image, imageLoaded && this.classes.imageLoaded), onClick: onClickImage, ref: function ref(image) { _this3.image = image; }, sizes: sizes, alt: image.alt, src: image.src, srcSet: sourceSet, style: imgStyle }) ); } }, { key: 'renderThumbnails', value: function renderThumbnails() { var _props4 = this.props, images = _props4.images, currentImage = _props4.currentImage, onClickThumbnail = _props4.onClickThumbnail, showThumbnails = _props4.showThumbnails, thumbnailOffset = _props4.thumbnailOffset; if (!showThumbnails) return; return React.createElement(PaginatedThumbnails, { currentImage: currentImage, images: images, offset: thumbnailOffset, onClickThumbnail: onClickThumbnail }); } }, { key: 'renderHeader', value: function renderHeader() { var _props5 = this.props, closeButtonTitle = _props5.closeButtonTitle, customControls = _props5.customControls, onClose = _props5.onClose, showCloseButton = _props5.showCloseButton; return React.createElement(Header, { customControls: customControls, onClose: onClose, showCloseButton: showCloseButton, closeButtonTitle: closeButtonTitle }); } }, { key: 'renderFooter', value: function renderFooter() { var _props6 = this.props, currentImage = _props6.currentImage, images = _props6.images, imageCountSeparator = _props6.imageCountSeparator, showImageCount = _props6.showImageCount, rotatable = _props6.rotatable, zoomable = _props6.zoomable, onSave = _props6.onSave; if (!images || !images.length) return null; return React.createElement(Footer, { caption: images[currentImage].caption, countCurrent: currentImage + 1, countSeparator: imageCountSeparator, countTotal: images.length, onAction: this.handleAction, rotatable: rotatable, showCount: showImageCount, zoomable: zoomable, savable: !!onSave }); } }, { key: 'renderSpinner', value: function renderSpinner() { var _props7 = this.props, spinner = _props7.spinner, spinnerColor = _props7.spinnerColor, spinnerSize = _props7.spinnerSize; var imageLoaded = this.state.imageLoaded; var Spinner$$1 = spinner; return React.createElement( 'div', { className: css(this.classes.spinner, !imageLoaded && this.classes.spinnerActive) }, React.createElement(Spinner$$1, { color: spinnerColor, size: spinnerSize }) ); } }, { key: 'render', value: function render$$1() { return React.createElement( Portal, null, this.renderDialog() ); } }]); return Lightbox; }(Component); Lightbox.propTypes = { backdropClosesModal: PropTypes.bool, closeButtonTitle: PropTypes.string, currentImage: PropTypes.number, customControls: PropTypes.arrayOf(PropTypes.node), enableKeyboardInput: PropTypes.bool, imageCountSeparator: PropTypes.string, images: PropTypes.arrayOf(PropTypes.shape({ src: PropTypes.string.isRequired, srcSet: PropTypes.array, caption: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), thumbnail: PropTypes.string })).i