UNPKG

@alifd/next

Version:

A configurable component library for web built on React.

353 lines (284 loc) 12.5 kB
'use strict'; exports.__esModule = true; var _extends2 = require('babel-runtime/helpers/extends'); var _extends3 = _interopRequireDefault(_extends2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _class, _temp; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _classnames2 = require('classnames'); var _classnames3 = _interopRequireDefault(_classnames2); var _reactDom = require('react-dom'); var _reactLifecyclesCompat = require('react-lifecycles-compat'); var _resizeObserverPolyfill = require('resize-observer-polyfill'); var _resizeObserverPolyfill2 = _interopRequireDefault(_resizeObserverPolyfill); var _util = require('../util'); var _configProvider = require('../config-provider'); var _configProvider2 = _interopRequireDefault(_configProvider); var _util2 = require('./util'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** Affix */ var Affix = (_temp = _class = function (_React$Component) { (0, _inherits3.default)(Affix, _React$Component); Affix._getAffixMode = function _getAffixMode(nextProps) { var affixMode = { top: false, bottom: false, offset: 0 }; if (!nextProps) { return affixMode; } var offsetTop = nextProps.offsetTop, offsetBottom = nextProps.offsetBottom; if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') { // set default affixMode.top = true; } else if (typeof offsetTop === 'number') { affixMode.top = true; affixMode.bottom = false; affixMode.offset = offsetTop; } else if (typeof offsetBottom === 'number') { affixMode.bottom = true; affixMode.top = false; affixMode.offset = offsetBottom; } return affixMode; }; function Affix(props, context) { (0, _classCallCheck3.default)(this, Affix); var _this = (0, _possibleConstructorReturn3.default)(this, _React$Component.call(this, props, context)); _this._clearContainerEvent = function () { if (_this.timeout) { clearTimeout(_this.timeout); _this.timeout = null; } var container = _this.props.container; _this._removeEventHandlerForContainer(container); }; _this.updatePosition = function () { _this._updateNodePosition(); }; _this._updateNodePosition = function () { var affixMode = _this.state.affixMode; var _this$props = _this.props, container = _this$props.container, useAbsolute = _this$props.useAbsolute; var affixContainer = container(); if (!affixContainer || !_this.affixNode) { return false; } var containerScrollTop = (0, _util2.getScroll)(affixContainer, true); // 容器在垂直位置上的滚动 offset var affixOffset = _this._getOffset(_this.affixNode, affixContainer); // 目标节点当前相对于容器的 offset var containerHeight = (0, _util2.getNodeHeight)(affixContainer); // 容器的高度 var affixHeight = _this.affixNode.offsetHeight; var containerRect = (0, _util2.getRect)(affixContainer); var affixChildHeight = _this.affixChildNode.offsetHeight; var affixStyle = { width: affixOffset.width }; var containerStyle = { width: affixOffset.width, height: affixChildHeight }; var positionStyle = null; if (affixMode.top && containerScrollTop > affixOffset.top - affixMode.offset) { // affix top if (useAbsolute) { affixStyle.position = 'absolute'; affixStyle.top = containerScrollTop - (affixOffset.top - affixMode.offset); positionStyle = 'relative'; } else { affixStyle.position = 'fixed'; affixStyle.top = affixMode.offset + containerRect.top; } _this._setAffixStyle(affixStyle, true); _this._setContainerStyle(containerStyle); } else if (affixMode.bottom && containerScrollTop < affixOffset.top + affixHeight + affixMode.offset - containerHeight) { // affix bottom affixStyle.height = affixHeight; if (useAbsolute) { affixStyle.position = 'absolute'; affixStyle.top = containerScrollTop - (affixOffset.top + affixHeight + affixMode.offset - containerHeight); positionStyle = 'relative'; } else { affixStyle.position = 'fixed'; affixStyle.bottom = affixMode.offset; } _this._setAffixStyle(affixStyle, true); _this._setContainerStyle(containerStyle); } else { _this._setAffixStyle(null); _this._setContainerStyle(null); } if (_this.state.positionStyle !== positionStyle) { _this.setState({ positionStyle: positionStyle }); } }; _this._affixNodeRefHandler = function (ref) { _this.affixNode = ref; }; _this._affixChildNodeRefHandler = function (ref) { _this.affixChildNode = ref; }; _this.state = { style: null, containerStyle: null, positionStyle: null, affixMode: Affix._getAffixMode(props) }; _this.resizeObserver = new _resizeObserverPolyfill2.default(_this._updateNodePosition); return _this; } Affix.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { if ('offsetTop' in nextProps || 'offsetBottom' in nextProps) { return { affixMode: Affix._getAffixMode(nextProps) }; } return null; }; Affix.prototype.componentDidMount = function componentDidMount() { var _this2 = this; var container = this.props.container; // wait for parent rendered this.timeout = setTimeout(function () { _this2._updateNodePosition(); _this2._setEventHandlerForContainer(container); }); }; Affix.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState, snapshot) { var _this3 = this; if (prevProps.container() !== this.props.container()) { this._clearContainerEvent(); this.timeout = setTimeout(function () { _this3._setEventHandlerForContainer(_this3.props.container); }); } setTimeout(this._updateNodePosition); }; Affix.prototype.componentWillUnmount = function componentWillUnmount() { this._clearContainerEvent(); }; Affix.prototype._setEventHandlerForContainer = function _setEventHandlerForContainer(getContainer) { var container = getContainer(); if (!container) { return; } _util.events.on(container, 'scroll', this._updateNodePosition, false); this.resizeObserver.observe(this.affixNode); }; Affix.prototype._removeEventHandlerForContainer = function _removeEventHandlerForContainer(getContainer) { var container = getContainer(); if (container) { _util.events.off(container, 'scroll', this._updateNodePosition); this.resizeObserver.disconnect(); } }; Affix.prototype._setAffixStyle = function _setAffixStyle(affixStyle) { var affixed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (_util.obj.shallowEqual(affixStyle, this.state.style)) { return; } this.setState({ style: affixStyle }); var onAffix = this.props.onAffix; if (affixed) { setTimeout(function () { return onAffix(true); }); } else if (!affixStyle) { setTimeout(function () { return onAffix(false); }); } }; Affix.prototype._setContainerStyle = function _setContainerStyle(containerStyle) { if (_util.obj.shallowEqual(containerStyle, this.state.containerStyle)) { return; } this.setState({ containerStyle: containerStyle }); }; Affix.prototype._getOffset = function _getOffset(affixNode, affixContainer) { var affixRect = affixNode.getBoundingClientRect(); // affix 元素 相对浏览器窗口的位置 var containerRect = (0, _util2.getRect)(affixContainer); // affix 容器 相对浏览器窗口的位置 var containerScrollTop = (0, _util2.getScroll)(affixContainer, true); var containerScrollLeft = (0, _util2.getScroll)(affixContainer, false); return { top: affixRect.top - containerRect.top + containerScrollTop, left: affixRect.left - containerRect.left + containerScrollLeft, width: affixRect.width, height: affixRect.height }; }; Affix.prototype.render = function render() { var _classnames; var _state = this.state, affixMode = _state.affixMode, positionStyle = _state.positionStyle; var _props = this.props, prefix = _props.prefix, className = _props.className, style = _props.style, children = _props.children; var state = this.state; var classNames = (0, _classnames3.default)((_classnames = {}, _classnames[prefix + 'affix'] = state.style, _classnames[prefix + 'affix-top'] = !state.style && affixMode.top, _classnames[prefix + 'affix-bottom'] = !state.style && affixMode.bottom, _classnames[className] = className, _classnames)); var wrapperStyle = (0, _extends3.default)({}, style, { position: positionStyle }); return _react2.default.createElement( 'div', { ref: this._affixNodeRefHandler, style: wrapperStyle }, state.style && _react2.default.createElement('div', { style: state.containerStyle, 'aria-hidden': 'true' }), _react2.default.createElement( 'div', { ref: this._affixChildNodeRefHandler, className: classNames, style: state.style }, children ) ); }; return Affix; }(_react2.default.Component), _class.propTypes = { prefix: _propTypes2.default.string, /** * 设置 Affix 需要监听滚动事件的容器元素 * @return {ReactElement} 目标容器元素的实例 */ container: _propTypes2.default.func, /** * 距离窗口顶部达到指定偏移量后触发 */ offsetTop: _propTypes2.default.number, /** * 距离窗口底部达到制定偏移量后触发 */ offsetBottom: _propTypes2.default.number, /** * 当元素的样式发生固钉样式变化时触发的回调函数 * @param {Boolean} affixed 元素是否被固钉 */ onAffix: _propTypes2.default.func, /** * 是否启用绝对布局实现 affix * @param {Boolean} 是否启用绝对布局 */ useAbsolute: _propTypes2.default.bool, className: _propTypes2.default.string, style: _propTypes2.default.object, children: _propTypes2.default.any }, _class.defaultProps = { prefix: 'next-', container: function container() { return window; }, onAffix: _util.func.noop }, _temp); Affix.displayName = 'Affix'; exports.default = _configProvider2.default.config((0, _reactLifecyclesCompat.polyfill)(Affix)); module.exports = exports['default'];