@cloudscape_innovation/react-images-viewer
Version:
Create an react-images-viewer component.
1,222 lines (1,184 loc) • 41.7 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('prop-types'), require('react'), require('aphrodite'), require('react-scrolllock'), require('aphrodite/no-important'), require('react-transition-group'), require('react-dom')) :
typeof define === 'function' && define.amd ? define(['prop-types', 'react', 'aphrodite', 'react-scrolllock', 'aphrodite/no-important', 'react-transition-group', 'react-dom'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ImgsViewer = factory(global.PropTypes, global.React, global.aphrodite, global.ScrollLock, global.aphrodite, global.ReactTransitionGroup, global.ReactDOM));
})(this, (function (PropTypes, React, aphrodite, ScrollLock, noImportant, reactTransitionGroup, reactDom) { 'use strict';
function _assertThisInitialized(e) {
if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return e;
}
function _callSuper(t, o, e) {
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
}
function _classCallCheck(a, n) {
if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", {
writable: false
}), e;
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function (n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
}
return n;
}, _extends.apply(null, arguments);
}
function _getPrototypeOf(t) {
return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
return t.__proto__ || Object.getPrototypeOf(t);
}, _getPrototypeOf(t);
}
function _inherits(t, e) {
if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
t.prototype = Object.create(e && e.prototype, {
constructor: {
value: t,
writable: true,
configurable: true
}
}), Object.defineProperty(t, "prototype", {
writable: false
}), e && _setPrototypeOf(t, e);
}
function _isNativeReflectConstruct() {
try {
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
} catch (t) {}
return (_isNativeReflectConstruct = function () {
return !!t;
})();
}
function _objectWithoutProperties(e, t) {
if (null == e) return {};
var o,
r,
i = _objectWithoutPropertiesLoose(e, t);
if (Object.getOwnPropertySymbols) {
var n = Object.getOwnPropertySymbols(e);
for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
}
return i;
}
function _objectWithoutPropertiesLoose(r, e) {
if (null == r) return {};
var t = {};
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
if (-1 !== e.indexOf(n)) continue;
t[n] = r[n];
}
return t;
}
function _possibleConstructorReturn(t, e) {
if (e && ("object" == typeof e || "function" == typeof e)) return e;
if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
return _assertThisInitialized(t);
}
function _setPrototypeOf(t, e) {
return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
return t.__proto__ = e, t;
}, _setPrototypeOf(t, e);
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r);
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (String )(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
// ===================
// Theme
// ===================
var theme = {};
// container
theme.container = {
background: 'rgba(0, 0, 0, .8)',
gutter: {
horizontal: 10,
vertical: 10
},
zIndex: 2001
};
// header
theme.header = {
height: 40
};
theme.close = {
fill: 'white'
};
// footer
theme.footer = {
color: '#fff',
count: {
color: 'rgba(255, 255, 255, .75)',
fontSize: '.85em'
},
height: 40,
gutter: {
horizontal: 0,
vertical: 5
}
};
// thumbnails
theme.thumbnail = {
activeBorderColor: '#fff',
size: 50,
gutter: 2
};
// arrow
theme.arrow = {
background: 'none',
fill: '#fff',
height: 120
};
function deepMerge(source) {
var target = 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;
}
// export function deepMerge(source, target = {}) {
// // initialize with source styles
// const styles = { ...source }
// // massage in target styles
// Object.keys(target).forEach(key => {
// if (source[key]) {
// styles[key] = (rsCss, props) => {
// return target[key](source[key](rsCss, props), props)
// }
// } else {
// styles[key] = target[key]
// }
// })
// return styles
// }
var canUseDom = !!(typeof window !== "undefined" && window.document && window.document.createElement);
/**
* Bind multiple conponent 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);
});
}
var arrowLeft = (function (fill) {
return "<svg fill=\"".concat(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=\"".concat(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=\"".concat(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 _excluded$3 = ["fill", "type"];
var icons = {
arrowLeft: arrowLeft,
arrowRight: arrowRight,
close: close
};
var Icon = function Icon(_ref) {
var fill = _ref.fill,
type = _ref.type,
props = _objectWithoutProperties(_ref, _excluded$3);
var icon = icons[type];
return /*#__PURE__*/React.createElement("span", _extends({
dangerouslySetInnerHTML: {
__html: icon(fill)
}
}, props));
};
Icon.propTypes = {
fill: PropTypes.string,
type: PropTypes.oneOf(Object.keys(icons))
};
Icon.defaultProps = {
fill: "#fff"
};
var _excluded$2 = ["direction", "icon", "onClick", "size", "theme"];
function Arrow(_ref) {
var direction = _ref.direction,
icon = _ref.icon,
onClick = _ref.onClick,
size = _ref.size,
theme$1 = _ref.theme,
props = _objectWithoutProperties(_ref, _excluded$2);
var classes = noImportant.StyleSheet.create(deepMerge(defaultStyles$5, theme$1));
return /*#__PURE__*/React.createElement("button", _extends({
type: "button" // default: submit
,
className: noImportant.css(classes.arrow, classes["arrow__direction__" + direction], size && classes["arrow__size__" + size]),
onClick: onClick,
onTouchEnd: onClick
}, props), /*#__PURE__*/React.createElement(Icon, {
fill: !!theme$1.arrow && theme$1.arrow.fill || theme.arrow.fill,
type: icon
}));
}
Arrow.propTypes = {
theme: PropTypes.object,
direction: PropTypes.oneOf(["left", "right"]),
icon: PropTypes.string,
onClick: PropTypes.func.isRequired,
size: PropTypes.oneOf(["medium", "small"]).isRequired
};
Arrow.defaultProps = {
size: "medium"
};
var defaultStyles$5 = {
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
}
},
// direciton
arrow__direction__right: {
right: theme.container.gutter.horizontal
},
arrow__direction__left: {
left: theme.container.gutter.horizontal
}
};
function Container(props) {
var classes = noImportant.StyleSheet.create(deepMerge(defaultStyles$4, props.theme));
return /*#__PURE__*/React.createElement("div", _extends({
id: "viewerBackdrop",
className: noImportant.css(classes.container)
}, props));
}
Container.propTypes = {
theme: PropTypes.object
};
var defaultStyles$4 = {
container: {
alignItems: 'center',
backdropColor: theme.container.background,
boxSizing: 'border-box',
display: 'flex',
height: '100%',
justifyContent: 'center',
left: 0,
paddingTop: theme.container.gutter.vertical,
paddingRight: theme.container.gutter.horizontal,
paddingBottom: theme.container.gutter.vertical,
paddingLeft: theme.container.gutter.horizontal,
position: 'fixed',
top: 0,
width: '100%',
zIndex: theme.container.zIndex
}
};
var _excluded$1 = ["caption", "countCurr", "countSeparator", "countTotal", "showCount", "theme"];
function Footer(_ref) {
var caption = _ref.caption,
countCurr = _ref.countCurr,
countSeparator = _ref.countSeparator,
countTotal = _ref.countTotal,
showCount = _ref.showCount,
theme = _ref.theme,
props = _objectWithoutProperties(_ref, _excluded$1);
if (!caption && !showCount) return null;
var classes = noImportant.StyleSheet.create(deepMerge(defaultStyles$3, theme));
var imgCount = showCount ? /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.footerCount)
}, countCurr, countSeparator, countTotal) : /*#__PURE__*/React.createElement("span", null);
return /*#__PURE__*/React.createElement("div", _extends({
className: noImportant.css(classes.footer)
}, props), caption ? /*#__PURE__*/React.createElement("figcaption", {
className: noImportant.css(classes.footerCaption)
}, caption) : /*#__PURE__*/React.createElement("span", null), imgCount);
}
Footer.propTypes = {
theme: PropTypes.object,
caption: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
countCurr: PropTypes.number,
countSeparator: PropTypes.string,
countTotal: PropTypes.number,
showCount: PropTypes.bool
};
var defaultStyles$3 = {
footer: {
boxSizing: 'border-box',
color: theme.footer.color,
cursor: 'auto',
display: 'flex',
justifyContent: 'space-between',
left: 0,
lineHeight: 1.3,
paddingTop: theme.footer.gutter.vertical,
paddingRight: theme.footer.gutter.horizontal,
paddingBottom: theme.footer.gutter.vertical,
paddingLeft: theme.footer.gutter.horizontal
},
footerCount: {
color: theme.footer.count.color,
fontSize: theme.footer.count.fontSize,
paddingLeft: '1em' // add a small gutter for the caption
},
footerCaption: {
flex: '1 1 0'
}
};
var _excluded = ["customControls", "onClose", "showCloseBtn", "closeBtnTitle", "theme"];
function Header(_ref) {
var customControls = _ref.customControls,
onClose = _ref.onClose,
showCloseBtn = _ref.showCloseBtn,
closeBtnTitle = _ref.closeBtnTitle,
theme$1 = _ref.theme,
props = _objectWithoutProperties(_ref, _excluded);
var classes = noImportant.StyleSheet.create(deepMerge(defaultStyles$2, theme$1));
return /*#__PURE__*/React.createElement("div", _extends({
className: noImportant.css(classes.header)
}, props), customControls ? customControls : /*#__PURE__*/React.createElement("span", null), !!showCloseBtn && /*#__PURE__*/React.createElement("button", {
title: closeBtnTitle,
className: noImportant.css(classes.close),
onClick: onClose
}, /*#__PURE__*/React.createElement(Icon, {
fill: !!theme$1.close && theme$1.close.fill || theme.close.fill,
type: "close"
})));
}
Header.propTypes = {
theme: PropTypes.object,
customControls: PropTypes.array,
onClose: PropTypes.func.isRequired,
showCloseBtn: PropTypes.bool,
closeBtnTitle: PropTypes.string
};
var defaultStyles$2 = {
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) {
var index = _ref.index,
src = _ref.src,
thumbnail = _ref.thumbnail,
active = _ref.active,
_onClick = _ref.onClick,
theme = _ref.theme;
var url = thumbnail || src;
var classes = noImportant.StyleSheet.create(deepMerge(defaultStyles$1, theme));
return /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.thumbnail, active && classes.thumbnail__active),
onClick: function onClick(e) {
e.preventDefault();
e.stopPropagation();
_onClick(index);
},
style: {
backgroundImage: "url(\"".concat(url, "\")")
}
});
}
Thumbnail.propTypes = {
theme: PropTypes.object,
active: PropTypes.bool,
index: PropTypes.number,
onClick: PropTypes.func.isRequired,
src: PropTypes.string,
thumbnail: PropTypes.string
};
var defaultStyles$1 = {
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 ".concat(theme.thumbnail.activeBorderColor)
}
};
var classes = noImportant.StyleSheet.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 = /*#__PURE__*/function (_Component) {
function PaginatedThumbnails(props) {
var _this;
_classCallCheck(this, PaginatedThumbnails);
_this = _callSuper(this, PaginatedThumbnails, [props]);
_this.state = {
hasCustomPage: false
};
_this.gotoPrev = _this.gotoPrev.bind(_this);
_this.gotoNext = _this.gotoNext.bind(_this);
return _this;
}
_inherits(PaginatedThumbnails, _Component);
return _createClass(PaginatedThumbnails, [{
key: "UNSAFE_componentWillReceiveProps",
value: function UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.currImg !== this.props.currImg) {
this.setState({
hasCustomPage: false
});
}
}
// ====================
// Methods
// ====================
}, {
key: "getFirst",
value: function getFirst() {
var _this$props = this.props,
currImg = _this$props.currImg,
offset = _this$props.offset;
if (this.state.hasCustomPage) {
return this.clampFirst(this.state.first);
}
return this.clampFirst(currImg - 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 _this$props2 = this.props,
imgs = _this$props2.imgs,
offset = _this$props2.offset;
var totalCount = 2 * offset + 1; // show $offset extra thumbnails on each side
if (value < 0) {
return 0;
} else if (value + totalCount > imgs.length) {
// Too far
return imgs.length - totalCount;
} else {
return value;
}
}
// ====================
// Renderers
// ====================
}, {
key: "renderArrowPrev",
value: function renderArrowPrev(theme) {
var leftTitle = this.props.leftTitle;
if (this.getFirst() <= 0) return null;
return /*#__PURE__*/React.createElement(Arrow, {
theme: theme,
direction: "left",
size: "small",
icon: "arrowLeft",
onClick: this.gotoPrev,
style: arrowStyles,
title: leftTitle,
type: "button"
});
}
}, {
key: "renderArrowNext",
value: function renderArrowNext(theme) {
var _this$props3 = this.props,
offset = _this$props3.offset,
imgs = _this$props3.imgs,
rightTitle = _this$props3.rightTitle;
var totalCount = 2 * offset + 1;
if (this.getFirst() + totalCount >= imgs.length) return null;
return /*#__PURE__*/React.createElement(Arrow, {
theme: theme,
direction: "right",
size: "small",
icon: "arrowRight",
onClick: this.gotoNext,
style: arrowStyles,
title: rightTitle,
type: "button"
});
}
}, {
key: "render",
value: function render() {
var _this$props4 = this.props,
imgs = _this$props4.imgs,
currImg = _this$props4.currImg,
onClickThumbnail = _this$props4.onClickThumbnail,
offset = _this$props4.offset,
theme = _this$props4.theme;
var totalCount = 2 * offset + 1; // show $offset extra thumbnails on each side
var thumbnails = [];
var baseOffset = 0;
if (imgs.length <= totalCount) {
thumbnails = imgs;
} else {
// Try to center current image in list
baseOffset = this.getFirst();
thumbnails = imgs.slice(baseOffset, baseOffset + totalCount);
}
return /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.paginatedThumbnails)
}, this.renderArrowPrev(theme), thumbnails.map(function (img, idx) {
return /*#__PURE__*/React.createElement(Thumbnail, _extends({
theme: theme,
key: baseOffset + idx
}, img, {
index: baseOffset + idx,
onClick: onClickThumbnail,
active: baseOffset + idx === currImg
}));
}), this.renderArrowNext(theme));
}
}]);
}(React.Component);
PaginatedThumbnails.propTypes = {
theme: PropTypes.object,
leftTitle: PropTypes.string,
rightTitle: PropTypes.string,
currImg: PropTypes.number,
imgs: PropTypes.array,
offset: PropTypes.number,
onClickThumbnail: PropTypes.func.isRequired
};
var Portal = /*#__PURE__*/function (_Component) {
function Portal() {
var _this;
_classCallCheck(this, Portal);
_this = _callSuper(this, Portal);
_this.portalElement = null;
return _this;
}
_inherits(Portal, _Component);
return _createClass(Portal, [{
key: "componentDidMount",
value: function componentDidMount() {
var div = document.createElement('div');
document.body.appendChild(div);
this.portalElement = div;
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 ".concat(duration, "ms; }\n\t\t\t\t.fade-leave { opacity: 1; }\n\t\t\t\t.fade-leave.fade-leave-active { opacity: .01; transition: opacity ").concat(duration, "ms; }\n\t\t");
reactDom.render(/*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("style", null, styles), /*#__PURE__*/React.createElement(reactTransitionGroup.TransitionGroup, this.props, /*#__PURE__*/React.createElement(reactTransitionGroup.CSSTransition, {
timeout: {
enter: duration,
exit: duration
},
className: "fade"
}, /*#__PURE__*/React.createElement("div", null, this.props.children)))), this.portalElement);
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
reactDom.unmountComponentAtNode(this.portalElement);
document.body.removeChild(this.portalElement);
}
}, {
key: "render",
value: function render() {
return null;
}
}]);
}(React.Component);
Portal.propTypes = {
children: PropTypes.arrayOf(PropTypes.any)
};
var Spinner = function Spinner(props) {
var classes = noImportant.StyleSheet.create(styles(props));
return /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.bouncingLoader)
}, /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.child)
}), /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.child, classes.child2)
}), /*#__PURE__*/React.createElement("div", {
className: noImportant.css(classes.child, classes.child3)
}));
};
Spinner.propTypes = {
color: PropTypes.string,
size: PropTypes.number
};
var bouncingKeyframes = function bouncingKeyframes(size) {
return {
'0%': {
opacity: 1,
transform: 'translateY(0)'
},
'100%': {
opacity: .1,
transform: "translateY(-".concat(size, "px)")
}
};
};
var styles = function styles(_ref) {
var color = _ref.color,
size = _ref.size;
return {
bouncingLoader: {
display: 'flex',
justifyContent: 'center'
},
child: {
width: size,
height: size,
margin: "".concat(3 * size, "px ").concat(.2 * size, "px"),
background: color,
borderRadius: '50%',
animationName: bouncingKeyframes(size),
animationDuration: '.6s',
animationDirection: 'alternate',
animationIterationCount: 'infinite'
},
child2: {
animationDelay: '0.2s'
},
child3: {
animationDelay: '0.4s'
}
};
};
function normalizeSourceSet(data) {
var sourceSet = data.srcSet || data.srcset;
if (Array.isArray(sourceSet)) {
return sourceSet.join();
}
return sourceSet;
}
var ThemeContext = /*#__PURE__*/React.createContext({
theme: theme,
toggleTheme: function toggleTheme(newTheme) {}
});
var ImgsViewer = /*#__PURE__*/function (_Component) {
function ImgsViewer(props) {
var _this;
_classCallCheck(this, ImgsViewer);
_this = _callSuper(this, ImgsViewer, [props]);
_this.theme = deepMerge(theme, _this.props.theme);
_this.classes = aphrodite.StyleSheet.create(deepMerge(defaultStyles, _this.props.theme));
_this.toggleTheme = function (theme) {
_this.setState(function () {
return {
theme: theme
};
});
};
_this.state = {
imgLoaded: false,
theme: _this.theme,
toggleTheme: _this.toggleTheme
};
bindFunctions.call(_this, ["gotoNext", "gotoPrev", "closeBackdrop", "handleKeyboardInput", "handleImgLoaded"]);
return _this;
}
_inherits(ImgsViewer, _Component);
return _createClass(ImgsViewer, [{
key: "componentDidMount",
value: function componentDidMount() {
if (this.props.isOpen) {
if (this.props.enableKeyboardInput) {
window.addEventListener("keydown", this.handleKeyboardInput);
}
if (typeof this.props.currImg === "number") {
this.preloadImg(this.props.currImg, this.handleImgLoaded);
}
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (!canUseDom) return;
// always to preload imgs with both directions
// then when user changs direction, img also show quickly
if (this.props.preloadNextImg) {
var nextIdx = this.props.currImg + 1;
var prevIdx = this.props.currImg - 1;
this.preloadImg(prevIdx);
this.preloadImg(nextIdx);
}
// preload currImg
if (prevProps.currImg !== this.props.currImg || !prevProps.isOpen && this.props.isOpen) {
var img = this.preloadImgData(this.props.imgs[this.props.currImg], this.handleImgLoaded);
if (img) this.setState({
imgLoaded: img.complete
});
}
// add/remove event listeners
if (!prevProps.isOpen && this.props.isOpen && this.props.enableKeyboardInput) {
window.addEventListener("keydown", this.handleKeyboardInput);
}
if (!this.props.isOpen && this.props.enableKeyboardInput) {
window.removeEventListener("keydown", this.handleKeyboardInput);
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.props.enableKeyboardInput) {
window.removeEventListener("keydown", this.handleKeyboardInput);
}
}
// ====================
// Methods
// ====================
}, {
key: "preloadImg",
value: function preloadImg(idx, onload) {
return this.preloadImgData(this.props.imgs[idx], onload);
}
}, {
key: "preloadImgData",
value: function preloadImgData(data, onload) {
if (!data) return;
var img = new Image();
var sourceSet = normalizeSourceSet(data);
// Todo: add error handling for missing imgs
img.onerror = onload;
img.onload = onload;
img.src = data.src;
if (sourceSet) img.srcset = sourceSet;
return img;
}
}, {
key: "gotoNext",
value: function gotoNext(event) {
var _this$props = this.props,
currImg = _this$props.currImg,
imgs = _this$props.imgs;
var imgLoaded = this.state.imgLoaded;
if (!imgLoaded || currImg === imgs.length - 1) return;
if (event) {
event.preventDefault();
event.stopPropagation();
}
this.props.onClickNext();
}
}, {
key: "gotoPrev",
value: function gotoPrev(event) {
var currImg = this.props.currImg;
var imgLoaded = this.state.imgLoaded;
if (!imgLoaded || currImg === 0) return;
if (event) {
event.preventDefault();
event.stopPropagation();
}
this.props.onClickPrev();
}
}, {
key: "closeBackdrop",
value: function closeBackdrop(event) {
if (event.target.id === "viewerBackdrop" || event.target.tagName === "FIGURE") {
this.props.onClose();
}
}
}, {
key: "handleKeyboardInput",
value: function handleKeyboardInput(event) {
var keyCode = event.keyCode;
if (keyCode === 37 || keyCode === 33 || keyCode === 38) {
// left, pageup, up
this.gotoPrev(event);
return true;
} else if (keyCode === 39 || keyCode === 34 || keyCode === 40) {
// right, pagedown, down
this.gotoNext(event);
return true;
} else if (keyCode === 27 || keyCode === 32) {
// esc, space
this.props.onClose();
return true;
}
return false;
}
}, {
key: "handleImgLoaded",
value: function handleImgLoaded() {
this.setState({
imgLoaded: true
});
}
// ====================
// Renderers
// ====================
}, {
key: "renderArrowPrev",
value: function renderArrowPrev(theme) {
if (this.props.currImg === 0) return null;
return /*#__PURE__*/React.createElement(Arrow, {
theme: theme,
direction: "left",
icon: "arrowLeft",
onClick: this.gotoPrev,
title: this.props.leftArrowTitle,
type: "button"
});
}
}, {
key: "renderArrowNext",
value: function renderArrowNext(theme) {
if (this.props.currImg === this.props.imgs.length - 1) return null;
return /*#__PURE__*/React.createElement(Arrow, {
theme: theme,
direction: "right",
icon: "arrowRight",
onClick: this.gotoNext,
title: this.props.rightArrowTitle,
type: "button"
});
}
}, {
key: "renderDialog",
value: function renderDialog(newState) {
var _this2 = this;
var _this$props2 = this.props,
backdropCloseable = _this$props2.backdropCloseable,
isOpen = _this$props2.isOpen,
showThumbnails = _this$props2.showThumbnails,
width = _this$props2.width;
var imgLoaded = this.state.imgLoaded;
if (!isOpen) return /*#__PURE__*/React.createElement("span", {
key: "closed"
});
var offsetThumbnails = showThumbnails ? this.theme.thumbnail.size + this.theme.container.gutter.vertical : 0;
return /*#__PURE__*/React.createElement(ThemeContext.Consumer, null, function (_ref) {
var theme = _ref.theme;
_ref.toggleTheme;
theme = newState.theme;
return /*#__PURE__*/React.createElement(Container, {
theme: theme,
key: "open",
onClick: backdropCloseable && _this2.closeBackdrop,
onTouchEnd: backdropCloseable && _this2.closeBackdrop
}, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
className: aphrodite.css(_this2.classes.content),
style: {
marginBottom: offsetThumbnails,
maxWidth: width
}
}, imgLoaded && _this2.renderHeader(theme), " ", _this2.renderImgs(theme), _this2.renderSpinner(), " ", imgLoaded && _this2.renderFooter(theme)), imgLoaded && _this2.renderThumbnails(theme), imgLoaded && _this2.renderArrowPrev(theme), imgLoaded && _this2.renderArrowNext(theme), _this2.props.preventScroll && /*#__PURE__*/React.createElement(ScrollLock, null)));
});
}
}, {
key: "renderImgs",
value: function renderImgs(theme) {
var _this$props3 = this.props,
currImg = _this$props3.currImg,
imgs = _this$props3.imgs,
onClickImg = _this$props3.onClickImg,
showThumbnails = _this$props3.showThumbnails;
var imgLoaded = this.state.imgLoaded;
if (!imgs || !imgs.length) return null;
var img = imgs[currImg];
var sourceSet = normalizeSourceSet(img);
var sizes = sourceSet ? "100vw" : null;
var thumbnailsSize = showThumbnails ? theme.thumbnail.size : 0;
var heightOffset = "".concat(theme.header.height + theme.footer.height + thumbnailsSize + theme.container.gutter.vertical, "px");
return /*#__PURE__*/React.createElement("figure", {
className: aphrodite.css(this.classes.figure)
}, /*#__PURE__*/React.createElement("img", {
className: aphrodite.css(this.classes.img, imgLoaded && this.classes.imgLoaded),
onClick: onClickImg,
sizes: sizes,
alt: img.alt,
src: img.src,
srcSet: sourceSet,
style: {
cursor: onClickImg ? "pointer" : "auto",
maxHeight: "calc(100vh - ".concat(heightOffset)
}
}));
}
}, {
key: "renderThumbnails",
value: function renderThumbnails(theme) {
var _this$props4 = this.props,
imgs = _this$props4.imgs,
currImg = _this$props4.currImg,
leftArrowTitle = _this$props4.leftArrowTitle,
rightArrowTitle = _this$props4.rightArrowTitle,
onClickThumbnail = _this$props4.onClickThumbnail,
showThumbnails = _this$props4.showThumbnails,
thumbnailOffset = _this$props4.thumbnailOffset;
if (!showThumbnails) return null;
return /*#__PURE__*/React.createElement(PaginatedThumbnails, {
theme: theme,
leftTitle: leftArrowTitle,
rightTitle: rightArrowTitle,
currImg: currImg,
imgs: imgs,
offset: thumbnailOffset,
onClickThumbnail: onClickThumbnail
});
}
}, {
key: "renderHeader",
value: function renderHeader(theme) {
var _this$props5 = this.props,
closeBtnTitle = _this$props5.closeBtnTitle,
customControls = _this$props5.customControls,
onClose = _this$props5.onClose,
showCloseBtn = _this$props5.showCloseBtn;
return /*#__PURE__*/React.createElement(Header, {
theme: theme,
customControls: customControls,
onClose: onClose,
showCloseBtn: showCloseBtn,
closeBtnTitle: closeBtnTitle
});
}
}, {
key: "renderFooter",
value: function renderFooter(theme) {
var _this$props6 = this.props,
currImg = _this$props6.currImg,
imgs = _this$props6.imgs,
imgCountSeparator = _this$props6.imgCountSeparator,
showImgCount = _this$props6.showImgCount;
if (!imgs || !imgs.length) return null;
return /*#__PURE__*/React.createElement(Footer, {
theme: theme,
caption: imgs[currImg].caption,
countCurr: currImg + 1,
countSeparator: imgCountSeparator,
countTotal: imgs.length,
showCount: showImgCount
});
}
}, {
key: "renderSpinner",
value: function renderSpinner() {
var _this$props7 = this.props,
spinner = _this$props7.spinner,
spinnerDisabled = _this$props7.spinnerDisabled,
spinnerColor = _this$props7.spinnerColor,
spinnerSize = _this$props7.spinnerSize;
var imgLoaded = this.state.imgLoaded;
var Spinner = spinner;
if (spinnerDisabled) return null;
return /*#__PURE__*/React.createElement("div", {
className: aphrodite.css(this.classes.spinner, !imgLoaded && this.classes.spinnerActive)
}, /*#__PURE__*/React.createElement(Spinner, {
color: spinnerColor,
size: spinnerSize
}));
}
}, {
key: "render",
value: function render() {
return /*#__PURE__*/React.createElement(ThemeContext.Provider, {
value: this.state
}, /*#__PURE__*/React.createElement(Portal, null, " ", this.renderDialog(this.state), " "));
}
}]);
}(React.Component);
ImgsViewer.propTypes = {
backdropCloseable: PropTypes.bool,
closeBtnTitle: PropTypes.string,
currImg: PropTypes.number,
customControls: PropTypes.arrayOf(PropTypes.node),
enableKeyboardInput: PropTypes.bool,
imgCountSeparator: PropTypes.string,
imgs: PropTypes.arrayOf(PropTypes.shape({
src: PropTypes.string.isRequired,
srcSet: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
caption: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
thumbnail: PropTypes.string
})).isRequired,
isOpen: PropTypes.bool,
leftArrowTitle: PropTypes.string,
onClickImg: PropTypes.func,
onClickNext: PropTypes.func,
onClickPrev: PropTypes.func,
onClickThumbnail: PropTypes.func,
onClose: PropTypes.func.isRequired,
preloadNextImg: PropTypes.bool,
preventScroll: PropTypes.bool,
rightArrowTitle: PropTypes.string,
showCloseBtn: PropTypes.bool,
showImgCount: PropTypes.bool,
showThumbnails: PropTypes.bool,
spinnerDisabled: PropTypes.bool,
spinner: PropTypes.func,
spinnerColor: PropTypes.string,
spinnerSize: PropTypes.number,
theme: PropTypes.object,
thumbnailOffset: PropTypes.number,
width: PropTypes.number
};
ImgsViewer.defaultProps = {
currImg: 0,
enableKeyboardInput: true,
imgCountSeparator: " / ",
onClickShowNextImg: true,
preloadNextImg: true,
preventScroll: true,
showCloseBtn: true,
showImgCount: true,
spinnerDisabled: false,
spinner: Spinner,
spinnerColor: "#fff",
spinnerSize: 50,
theme: {},
thumbnailOffset: 2,
width: 1024
};
var defaultStyles = {
content: {
position: "relative"
},
figure: {
margin: 0 // remove browser default
},
img: {
display: "block",
// removes browser default gutter
height: "auto",
margin: "0 auto",
// main center on very short screens or very narrow img
maxWidth: "100%",
// disable user select
WebkitTouchCallout: "none",
userSelect: "none",
// opacity animation on image load
opacity: 0,
transition: "opacity .3s"
},
imgLoaded: {
opacity: 1
},
spinner: {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
// opacity animation to make spinner appear with delay
opacity: 0,
transition: "opacity .3s",
pointerEvents: "none"
},
spinnerActive: {
opacity: 1
}
};
return ImgsViewer;
}));