UNPKG

chowa

Version:

UI component library based on React

192 lines (191 loc) 6.97 kB
/** * @license chowa v1.1.3 * * Copyright (c) Chowa Techonlogies Co.,Ltd.(http://www.chowa.cn). * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const React = require("react"); const ReactDom = require("react-dom"); const PropTypes = require("prop-types"); const resize_observer_polyfill_1 = require("resize-observer-polyfill"); const classNames = require("classnames"); const utils_1 = require("../utils"); class Affix extends React.PureComponent { constructor(props) { super(props); this.state = { contentTop: 0, contentLeft: 0, contentWidth: 0, contentHeight: 0, targetTop: 0, targetHeight: 0, targetLeft: 0, affixTop: 0, affixBottom: 0, affixLeft: 0, position: 'static', affixed: false, isBlock: false, isWindow: true }; [ 'updatePosition', 'onScrollHandler', 'onResizeHandler' ].forEach((fn) => { this[fn] = this[fn].bind(this); }); } componentDidMount() { const affixwrapperNode = ReactDom.findDOMNode(this); this.node = affixwrapperNode.firstElementChild; const { target } = this.props; const listeners = () => { const targetEle = target(); utils_1.doms.on(targetEle, 'scroll', this.onScrollHandler); utils_1.doms.on(window, 'resize', this.onResizeHandler); this.resizeObserver = new resize_observer_polyfill_1.default(this.onResizeHandler); this.resizeObserver.observe(targetEle === window ? document.body : targetEle); }; if (!target()) { setTimeout(listeners, 200); } else { listeners(); } } componentWillUnmount() { const targetEle = this.props.target(); utils_1.doms.off(targetEle, 'scroll', this.onScrollHandler); utils_1.doms.off(window, 'resize', this.onResizeHandler); this.resizeObserver.unobserve(targetEle === window ? document.body : targetEle); this.resizeObserver.disconnect(); } componentDidUpdate(preProps) { if (preProps.offsetTop !== this.props.offsetTop || preProps.offsetBottom !== this.props.offsetBottom) { this.updatePosition(); } } onScrollHandler() { this.updatePosition(); if (this.props.onTargetScroll) { this.props.onTargetScroll(); } } onResizeHandler() { this.setState({ affixed: false }, () => { const { top, left, width, height } = utils_1.doms.offset(this.node); const position = utils_1.doms.css(this.node, 'position'); const display = utils_1.doms.css(this.node, 'display'); const isWindow = this.props.target() === window; let targetTop = 0; let targetLeft = 0; let targetHeight = window.innerHeight; if (!isWindow) { const { top: realTop, height: realHeight, left: realLeft } = utils_1.doms.offset(this.props.target()); targetTop = realTop; targetHeight = realHeight; targetLeft = realLeft; } this.setState({ contentTop: top, contentLeft: left, contentWidth: width, contentHeight: height, targetTop, targetHeight, targetLeft, position: position, isBlock: display === 'block' || display === 'flex', isWindow }, () => { this.updatePosition(); }); }); if (this.props.onTargetResize) { this.props.onTargetResize(); } } updatePosition() { const { offsetTop, offsetBottom, target, onChange } = this.props; const { contentTop, targetTop, isWindow, targetHeight, contentLeft, targetLeft } = this.state; const targetNode = target(); const scrollTop = utils_1.doms.scrollTop(targetNode); let affixed = false; if (offsetBottom > 0) { if (scrollTop + targetHeight - offsetBottom <= contentTop - targetTop) { affixed = true; } } else { if (scrollTop >= contentTop - targetTop - offsetTop) { affixed = true; } } if (affixed !== this.state.affixed) { if (onChange) { onChange(affixed); } } this.setState({ affixed, affixTop: isWindow ? offsetTop : scrollTop + offsetTop, affixBottom: isWindow ? offsetBottom : targetNode.scrollHeight - scrollTop - targetHeight + offsetBottom, affixLeft: isWindow ? contentLeft : contentLeft - targetLeft }); } render() { const { children, offsetBottom, style, className } = this.props; const { contentWidth, contentHeight, affixed, isBlock, position, isWindow, affixTop, affixBottom, affixLeft } = this.state; if (!utils_1.isReactElement(children)) { throw new Error('Affix elements must be wrapped in an enclosing tag'); } const wrapperStyle = Object.assign({ position, display: isBlock ? 'block' : 'inline-block' }, position !== 'absolute' && position !== 'fixed' ? { contentWidth, contentHeight } : {}); const offsetStyle = Object.assign({ position: isWindow ? 'fixed' : 'absolute', left: affixLeft, width: contentWidth, height: contentHeight, zIndex: 999 }, utils_1.isExist(offsetBottom) ? { bottom: affixBottom } : { top: affixTop }); const renderChild = React.cloneElement(children, { style: Object.assign({}, children.props.style, affixed ? offsetStyle : {}) }); const componentClassName = classNames({ [utils_1.preClass('affix')]: true, [className]: !!className }); return (React.createElement("div", { style: Object.assign({}, style, affixed ? wrapperStyle : {}), className: componentClassName }, renderChild)); } } Affix.propTypes = { children: PropTypes.node, offsetTop: PropTypes.number, offsetBottom: PropTypes.number, target: PropTypes.func, onTargetScroll: PropTypes.func, onTargetResize: PropTypes.func, onChange: PropTypes.func, style: PropTypes.object, className: PropTypes.string }; Affix.defaultProps = { offsetTop: 0, target: () => window }; exports.default = Affix;