@cloudscape_innovation/react-images-viewer
Version:
Create an react-images-viewer component.
472 lines (466 loc) • 19.5 kB
JavaScript
"use strict";
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); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireWildcard(require("react"));
var _aphrodite = require("aphrodite");
var _reactScrolllock = _interopRequireDefault(require("react-scrolllock"));
var _theme = _interopRequireDefault(require("./theme"));
var _Arrow = _interopRequireDefault(require("./components/Arrow"));
var _Container = _interopRequireDefault(require("./components/Container"));
var _Footer = _interopRequireDefault(require("./components/Footer"));
var _Header = _interopRequireDefault(require("./components/Header"));
var _PaginatedThumbnails = _interopRequireDefault(require("./components/PaginatedThumbnails"));
var _Portal = _interopRequireDefault(require("./components/Portal"));
var _Spinner = _interopRequireDefault(require("./components/Spinner"));
var _util = require("./utils/util");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: 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 || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
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 || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
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 _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
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: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
function normalizeSourceSet(data) {
var sourceSet = data.srcSet || data.srcset;
if (Array.isArray(sourceSet)) {
return sourceSet.join();
}
return sourceSet;
}
var ThemeContext = /*#__PURE__*/(0, _react.createContext)({
theme: _theme.default,
toggleTheme: function toggleTheme(newTheme) {}
});
var ImgsViewer = /*#__PURE__*/function (_Component) {
function ImgsViewer(props) {
var _this;
_classCallCheck(this, ImgsViewer);
_this = _callSuper(this, ImgsViewer, [props]);
_this.theme = (0, _util.deepMerge)(_theme.default, _this.props.theme);
_this.classes = _aphrodite.StyleSheet.create((0, _util.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
};
_util.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 (!_util.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__*/(0, _jsxRuntime.jsx)(_Arrow.default, {
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__*/(0, _jsxRuntime.jsx)(_Arrow.default, {
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__*/(0, _jsxRuntime.jsx)("span", {}, "closed");
var offsetThumbnails = showThumbnails ? this.theme.thumbnail.size + this.theme.container.gutter.vertical : 0;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ThemeContext.Consumer, {
children: function children(_ref) {
var theme = _ref.theme,
toggleTheme = _ref.toggleTheme;
theme = newState.theme;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Container.default, {
theme: theme,
onClick: backdropCloseable && _this2.closeBackdrop,
onTouchEnd: backdropCloseable && _this2.closeBackdrop,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, {
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
className: (0, _aphrodite.css)(_this2.classes.content),
style: {
marginBottom: offsetThumbnails,
maxWidth: width
},
children: [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__*/(0, _jsxRuntime.jsx)(_reactScrolllock.default, {})]
})
}, "open");
}
});
}
}, {
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__*/(0, _jsxRuntime.jsx)("figure", {
className: (0, _aphrodite.css)(this.classes.figure),
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
className: (0, _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__*/(0, _jsxRuntime.jsx)(_PaginatedThumbnails.default, {
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__*/(0, _jsxRuntime.jsx)(_Header.default, {
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__*/(0, _jsxRuntime.jsx)(_Footer.default, {
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__*/(0, _jsxRuntime.jsx)("div", {
className: (0, _aphrodite.css)(this.classes.spinner, !imgLoaded && this.classes.spinnerActive),
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Spinner, {
color: spinnerColor,
size: spinnerSize
})
});
}
}, {
key: "render",
value: function render() {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ThemeContext.Provider, {
value: this.state,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Portal.default, {
children: [" ", this.renderDialog(this.state), " "]
})
});
}
}]);
}(_react.Component);
ImgsViewer.propTypes = {
backdropCloseable: _propTypes.default.bool,
closeBtnTitle: _propTypes.default.string,
currImg: _propTypes.default.number,
customControls: _propTypes.default.arrayOf(_propTypes.default.node),
enableKeyboardInput: _propTypes.default.bool,
imgCountSeparator: _propTypes.default.string,
imgs: _propTypes.default.arrayOf(_propTypes.default.shape({
src: _propTypes.default.string.isRequired,
srcSet: _propTypes.default.oneOfType([_propTypes.default.array, _propTypes.default.string]),
caption: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.element]),
thumbnail: _propTypes.default.string
})).isRequired,
isOpen: _propTypes.default.bool,
leftArrowTitle: _propTypes.default.string,
onClickImg: _propTypes.default.func,
onClickNext: _propTypes.default.func,
onClickPrev: _propTypes.default.func,
onClickThumbnail: _propTypes.default.func,
onClose: _propTypes.default.func.isRequired,
preloadNextImg: _propTypes.default.bool,
preventScroll: _propTypes.default.bool,
rightArrowTitle: _propTypes.default.string,
showCloseBtn: _propTypes.default.bool,
showImgCount: _propTypes.default.bool,
showThumbnails: _propTypes.default.bool,
spinnerDisabled: _propTypes.default.bool,
spinner: _propTypes.default.func,
spinnerColor: _propTypes.default.string,
spinnerSize: _propTypes.default.number,
theme: _propTypes.default.object,
thumbnailOffset: _propTypes.default.number,
width: _propTypes.default.number
};
ImgsViewer.defaultProps = {
currImg: 0,
enableKeyboardInput: true,
imgCountSeparator: " / ",
onClickShowNextImg: true,
preloadNextImg: true,
preventScroll: true,
showCloseBtn: true,
showImgCount: true,
spinnerDisabled: false,
spinner: _Spinner.default,
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
}
};
var _default = exports.default = ImgsViewer;