UNPKG

@jdcfe/yep-react

Version:

一套移动端的React组件库

383 lines (297 loc) 13.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.forceCheck = exports.default = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var React = _interopRequireWildcard(require("react")); var ReactDom = _interopRequireWildcard(require("react-dom")); var _events = require("../_utils/events"); var _scrollParent = _interopRequireDefault(require("../_utils/scrollParent")); var _throttle = _interopRequireDefault(require("lodash/throttle")); var _debounce = _interopRequireDefault(require("lodash/debounce")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } var defaultBoundingClientRect = { top: 0, right: 0, bottom: 0, left: 0, width: 0, height: 0 }; var LISTEN_FLAG = 'data-lazyload-listened'; var listeners = []; var pending = []; // try to handle passive events var passiveEventSupported = false; try { var opts = Object.defineProperty({}, 'passive', { get: function get() { passiveEventSupported = true; } }); window.addEventListener('test', function () {}, opts); } catch (e) {} // if they are supported, setup the optional params // IMPORTANT: FALSE doubles as the default CAPTURE value! var passiveEvent = passiveEventSupported ? { capture: false, passive: true } : false; /** * Check if `component` is visible in overflow container `parent` * @param {node} component React component * @param {node} parent component's scroll parent * @return {bool} */ var checkOverflowVisible = function checkOverflowVisible(component, parent) { var node = ReactDom.findDOMNode(component); var parentTop; var parentHeight; try { var _parent$getBoundingCl = parent.getBoundingClientRect(); parentTop = _parent$getBoundingCl.top; parentHeight = _parent$getBoundingCl.height; } catch (e) { parentTop = defaultBoundingClientRect.top; parentHeight = defaultBoundingClientRect.height; } var windowInnerHeight = window.innerHeight || document.documentElement.clientHeight; // calculate top and height of the intersection of the element's scrollParent and viewport var intersectionTop = Math.max(parentTop, 0); // intersection's top relative to viewport var intersectionHeight = Math.min(windowInnerHeight, parentTop + parentHeight) - intersectionTop; // height // check whether the element is visible in the intersection var top; var height; try { var _node$getBoundingClie = node.getBoundingClientRect(); top = _node$getBoundingClie.top; height = _node$getBoundingClie.height; } catch (e) { top = defaultBoundingClientRect.top; height = defaultBoundingClientRect.height; } var offsetTop = top - intersectionTop; // element's top relative to intersection var offsets = Array.isArray(component.props.offset) ? component.props.offset : [component.props.offset, component.props.offset]; // Be compatible with previous API return offsetTop - offsets[0] <= intersectionHeight && offsetTop + height + offsets[1] >= 0; }; /** * Check if `component` is visible in document * @param {node} component React component * @return {bool} */ var checkNormalVisible = function checkNormalVisible(component) { var node = ReactDom.findDOMNode(component); // If this element is hidden by css rules somehow, it's definitely invisible if (!(node.offsetWidth || node.offsetHeight || node.getClientRects().length)) return false; var top; var elementHeight; try { var _node$getBoundingClie2 = node.getBoundingClientRect(); top = _node$getBoundingClie2.top; elementHeight = _node$getBoundingClie2.height; } catch (e) { top = defaultBoundingClientRect.top; elementHeight = defaultBoundingClientRect.height; } var windowInnerHeight = window.innerHeight || document.documentElement.clientHeight; var offsets = Array.isArray(component.props.offset) ? component.props.offset : [component.props.offset, component.props.offset]; // Be compatible with previous API return top - offsets[0] <= windowInnerHeight && top + elementHeight + offsets[1] >= 0; }; /** * Detect if element is visible in viewport, if so, set `visible` state to true. * If `once` prop is provided true, remove component as listener after checkVisible * * @param {React} component React component that respond to scroll and resize */ var checkVisible = function checkVisible(component) { var node = ReactDom.findDOMNode(component); if (!(node instanceof HTMLElement)) { return; } var parent = (0, _scrollParent.default)(node); var isOverflow = component.props.overflow && parent !== node.ownerDocument && parent !== document && parent !== document.documentElement; var visible = isOverflow ? checkOverflowVisible(component, parent) : checkNormalVisible(component); if (visible) { // Avoid extra render if previously is visible if (!component.visible) { if (component.props.once) { pending.push(component); } component.visible = true; component.forceUpdate(); } } else if (!(component.props.once && component.visible)) { component.visible = false; if (component.props.unmountIfInvisible) { component.forceUpdate(); } } }; var purgePending = function purgePending() { pending.forEach(function (component) { var index = listeners.indexOf(component); if (index !== -1) { listeners.splice(index, 1); } }); pending = []; }; var lazyLoadHandler = function lazyLoadHandler() { for (var i = 0; i < listeners.length; ++i) { var listener = listeners[i]; checkVisible(listener); } // Remove `once` component in listeners purgePending(); }; // Depending on component's props exports.forceCheck = lazyLoadHandler; var delayType; var finalLazyLoadHandler = null; var isString = function isString(str) { return typeof str === 'string'; }; var LazyLoad = /*#__PURE__*/function (_React$Component) { (0, _inherits2.default)(LazyLoad, _React$Component); var _super = _createSuper(LazyLoad); function LazyLoad(props) { var _this; (0, _classCallCheck2.default)(this, LazyLoad); _this = _super.call(this, props); _this.visible = false; return _this; } (0, _createClass2.default)(LazyLoad, [{ key: "componentDidMount", value: function componentDidMount() { // It's unlikely to change delay type on the fly, this is mainly // designed for tests var scrollport = window; var scrollContainer = this.props.scrollContainer; if (scrollContainer) { if (isString(scrollContainer)) { scrollport = scrollport.document.querySelector(scrollContainer); } } var needResetFinalLazyLoadHandler = this.props.debounce !== undefined && delayType === 'throttle' || delayType === 'debounce' && this.props.debounce === undefined; if (needResetFinalLazyLoadHandler) { (0, _events.off)(scrollport, 'scroll', finalLazyLoadHandler, passiveEvent); (0, _events.off)(window, 'resize', finalLazyLoadHandler, passiveEvent); finalLazyLoadHandler = null; } if (!finalLazyLoadHandler) { if (this.props.debounce !== undefined) { finalLazyLoadHandler = (0, _debounce.default)(lazyLoadHandler, typeof this.props.debounce === 'number' ? this.props.debounce : 300); delayType = 'debounce'; } else if (this.props.throttle !== undefined) { finalLazyLoadHandler = (0, _throttle.default)(lazyLoadHandler, typeof this.props.throttle === 'number' ? this.props.throttle : 300); delayType = 'throttle'; } else { finalLazyLoadHandler = lazyLoadHandler; } } if (this.props.overflow) { var parent = (0, _scrollParent.default)(ReactDom.findDOMNode(this)); if (parent && typeof parent.getAttribute === 'function') { var listenerCount = 1 + +parent.getAttribute(LISTEN_FLAG); if (listenerCount === 1) { parent.addEventListener('scroll', finalLazyLoadHandler, passiveEvent); } parent.setAttribute(LISTEN_FLAG, listenerCount); } } else if (listeners.length === 0 || needResetFinalLazyLoadHandler) { var _this$props = this.props, scroll = _this$props.scroll, resize = _this$props.resize; if (scroll) { (0, _events.on)(scrollport, 'scroll', finalLazyLoadHandler, passiveEvent); } if (resize) { (0, _events.on)(window, 'resize', finalLazyLoadHandler, passiveEvent); } } listeners.push(this); checkVisible(this); } }, { key: "shouldComponentUpdate", value: function shouldComponentUpdate() { return this.visible; } }, { key: "componentWillUnmount", value: function componentWillUnmount() { if (this.props.overflow) { var parent = (0, _scrollParent.default)(ReactDom.findDOMNode(this)); if (parent && typeof parent.getAttribute === 'function') { var listenerCount = +parent.getAttribute(LISTEN_FLAG) - 1; if (listenerCount === 0) { parent.removeEventListener('scroll', finalLazyLoadHandler, passiveEvent); parent.removeAttribute(LISTEN_FLAG); } else { parent.setAttribute(LISTEN_FLAG, listenerCount); } } } var index = listeners.indexOf(this); if (index !== -1) { listeners.splice(index, 1); } if (listeners.length === 0 && typeof window !== 'undefined') { (0, _events.off)(window, 'resize', finalLazyLoadHandler, passiveEvent); (0, _events.off)(window, 'scroll', finalLazyLoadHandler, passiveEvent); } } }, { key: "render", value: function render() { return this.visible ? this.props.children : this.props.placeholder ? this.props.placeholder : /*#__PURE__*/React.createElement("div", { style: { height: this.props.height }, className: "lazyload-placeholder" }); } }]); return LazyLoad; }(React.Component); LazyLoad.defaultProps = { once: false, offset: 0, overflow: false, resize: false, scroll: true, unmountIfInvisible: false }; var getDisplayName = function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; }; var decorator = function decorator(options) { return function lazyload(WrappedComponent) { return /*#__PURE__*/function (_React$Component2) { (0, _inherits2.default)(LazyLoadDecorated, _React$Component2); var _super2 = _createSuper(LazyLoadDecorated); function LazyLoadDecorated() { var _this2; (0, _classCallCheck2.default)(this, LazyLoadDecorated); _this2 = _super2.apply(this, arguments); _this2.displayName = "LazyLoad".concat(getDisplayName(WrappedComponent)); return _this2; } (0, _createClass2.default)(LazyLoadDecorated, [{ key: "render", value: function render() { return /*#__PURE__*/React.createElement(LazyLoad, options, /*#__PURE__*/React.createElement(WrappedComponent, this.props)); } }]); return LazyLoadDecorated; }(React.Component); }; }; LazyLoad.lazyload = decorator; var _default = LazyLoad; exports.default = _default;