UNPKG

react-image-fork

Version:

React Image is an <img> tag replacement for react, featuring preloader and multiple image fallback support

300 lines (238 loc) 10.4 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var _objectWithoutProperties = _interopDefault(require('@babel/runtime/helpers/objectWithoutProperties')); var _classCallCheck = _interopDefault(require('@babel/runtime/helpers/classCallCheck')); var _createClass = _interopDefault(require('@babel/runtime/helpers/createClass')); var _possibleConstructorReturn = _interopDefault(require('@babel/runtime/helpers/possibleConstructorReturn')); var _getPrototypeOf = _interopDefault(require('@babel/runtime/helpers/getPrototypeOf')); var _assertThisInitialized = _interopDefault(require('@babel/runtime/helpers/assertThisInitialized')); var _inherits = _interopDefault(require('@babel/runtime/helpers/inherits')); var _defineProperty = _interopDefault(require('@babel/runtime/helpers/defineProperty')); var React = require('react'); var React__default = _interopDefault(React); var propTypes = require('prop-types'); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var cache = {}; var imgPropTypes = { loader: propTypes.node, unloader: propTypes.node, decode: propTypes.bool, crossorigin: propTypes.string, src: propTypes.oneOfType([propTypes.string, propTypes.array]), imageRef: propTypes.func, container: propTypes.func, loaderContainer: propTypes.func, unloaderContainer: propTypes.func, minSize: propTypes.number }; var Img = /*#__PURE__*/ function (_Component) { _inherits(Img, _Component); function Img(props) { var _this; _classCallCheck(this, Img); _this = _possibleConstructorReturn(this, _getPrototypeOf(Img).call(this, props)); // default loader/unloader container to just container. If no container was set // this will be a noop _defineProperty(_assertThisInitialized(_this), "srcToArray", function (src) { return (Array.isArray(src) ? src : [src]).filter(function (x) { return x; }); }); _defineProperty(_assertThisInitialized(_this), "onLoad", function () { cache[_this.sourceList[_this.state.currentIndex]] = true; /* istanbul ignore else */ if (_this.i) { var minSize = _this.props.minSize; if (minSize) { if (_this.i.naturalWidth < minSize || _this.i.naturalHeight < minSize) { _this.onError(); return; } } _this.setState({ isLoaded: true }); } }); _defineProperty(_assertThisInitialized(_this), "onError", function () { cache[_this.sourceList[_this.state.currentIndex]] = false; // if the current image has already been destroyed, we are probably no longer mounted // no need to do anything then /* istanbul ignore else */ if (!_this.i) return false; // before loading the next image, check to see if it was ever loaded in the past for (var nextIndex = _this.state.currentIndex + 1; nextIndex < _this.sourceList.length; nextIndex++) { // get next img var src = _this.sourceList[nextIndex]; // if we have never seen it, its the one we want to try next if (!(src in cache)) { _this.setState({ currentIndex: nextIndex }); break; } // if we know it exists, use it! if (cache[src] === true) { _this.setState({ currentIndex: nextIndex, isLoading: false, isLoaded: true }); return true; } // if we know it doesn't exist, skip it! /* istanbul ignore else */ if (cache[src] === false) continue; } // currentIndex is zero bases, length is 1 based. // if we have no more sources to try, return - we are done if (nextIndex === _this.sourceList.length) return _this.setState({ isLoading: false }); // otherwise, try the next img _this.loadImg(); }); _defineProperty(_assertThisInitialized(_this), "loadImg", function () { { _this.i = new Image(); } if (_this.props.crossorigin) { _this.i.crossOrigin = _this.props.crossorigin; } _this.i.src = _this.sourceList[_this.state.currentIndex]; if (_this.props.decode && _this.i.decode) { _this.i.decode().then(_this.onLoad)["catch"](_this.onError); } else { _this.i.onload = _this.onLoad; _this.i.onerror = _this.onError; } }); _defineProperty(_assertThisInitialized(_this), "unloadImg", function () { _this.i.onerror = null; _this.i.onload = null; // abort any current downloads https://github.com/mbrevda/react-image/pull/223 _this.i.src = ''; try { delete _this.i.src; } catch (e) {// On Safari in Strict mode this will throw an exception, // - https://github.com/mbrevda/react-image/issues/187 // We don't need to do anything about it. } delete _this.i; }); _this.loaderContainer = props.loaderContainer || props.container; _this.unloaderContainer = props.unloaderContainer || props.container; _this.sourceList = _this.srcToArray(_this.props.src); // check cache to decide at which index to start for (var i = 0; i < _this.sourceList.length; i++) { // if we've never seen this image before, the cache wont help. // no need to look further, just start loading /* istanbul ignore else */ if (!(_this.sourceList[i] in cache)) break; // if we have loaded this image before, just load it again /* istanbul ignore else */ if (cache[_this.sourceList[i]] === true) { _this.state = { currentIndex: i, isLoading: false, isLoaded: true }; return _possibleConstructorReturn(_this); } } _this.state = _this.sourceList.length ? // 'normal' opperation: start at 0 and try to load { currentIndex: 0, isLoading: true, isLoaded: false } : // if we dont have any sources, jump directly to unloaded { isLoading: false, isLoaded: false }; return _this; } _createClass(Img, [{ key: "componentDidMount", value: function componentDidMount() { // kick off process /* istanbul ignore else */ if (this.state.isLoading) this.loadImg(); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { // ensure that we dont leave any lingering listeners /* istanbul ignore else */ if (this.i) this.unloadImg(); } }, { key: "UNSAFE_componentWillReceiveProps", value: function UNSAFE_componentWillReceiveProps(nextProps) { var _this2 = this; this.loaderContainer = nextProps.loaderContainer || nextProps.container; this.unloaderContainer = nextProps.unloaderContainer || nextProps.container; var src = this.srcToArray(nextProps.src); var srcAdded = src.filter(function (s) { return _this2.sourceList.indexOf(s) === -1; }); var srcRemoved = this.sourceList.filter(function (s) { return src.indexOf(s) === -1; }); // if src prop changed, restart the loading process if (srcAdded.length || srcRemoved.length) { this.sourceList = src; // if we dont have any sources, jump directly to unloader if (!src.length) return this.setState({ isLoading: false, isLoaded: false }); this.setState({ currentIndex: 0, isLoading: true, isLoaded: false }, this.loadImg); } } }, { key: "render", value: function render() { // set img props as rest var _this$props = this.props, container = _this$props.container, loader = _this$props.loader, unloader = _this$props.unloader, src = _this$props.src, crossorigin = _this$props.crossorigin, imageRef = _this$props.imageRef, decode = _this$props.decode, loaderContainer = _this$props.loaderContainer, unloaderContainer = _this$props.unloaderContainer, mockImage = _this$props.mockImage, minSize = _this$props.minSize, rest = _objectWithoutProperties(_this$props, ["container", "loader", "unloader", "src", "crossorigin", "imageRef", "decode", "loaderContainer", "unloaderContainer", "mockImage", "minSize"]); //eslint-disable-line // if we have loaded, show img if (this.state.isLoaded) { var imgProps = _objectSpread({}, rest, {}, crossorigin ? { crossOrigin: crossorigin } : {}, {}, { src: this.sourceList[this.state.currentIndex] }, {}, imageRef ? { ref: imageRef } : {}); return container(React__default.createElement("img", imgProps)); } // if we are still trying to load, show img and a loader if requested if (!this.state.isLoaded && this.state.isLoading) { return loader ? this.loaderContainer(loader) : null; } // if we have given up on loading, show a place holder if requested, or nothing /* istanbul ignore else */ if (!this.state.isLoaded && !this.state.isLoading) { return unloader ? this.unloaderContainer(unloader) : null; } } }]); return Img; }(React.Component); _defineProperty(Img, "defaultProps", { loader: false, unloader: false, decode: true, src: [], // by default, just return what gets sent in. Can be used for advanced rendering // such as animations container: function container(x) { return x; } }); Img.propTypes = {}; module.exports = Img;