@jdcfe/yep-react
Version:
一套移动端的React组件库
383 lines (297 loc) • 13.8 kB
JavaScript
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;
;