UNPKG

chowa

Version:

UI component library based on React

122 lines (121 loc) 4.79 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 PropTypes = require("prop-types"); const resize_observer_polyfill_1 = require("resize-observer-polyfill"); const utils_1 = require("../utils"); const classNames = require("classnames"); const anchor_link_1 = require("./anchor-link"); const affix_1 = require("../affix"); class Anchor extends React.PureComponent { constructor(props) { super(props); const links = []; React.Children.forEach(props.children, (child) => { if (utils_1.isReactElement(child) && child.type === anchor_link_1.default) { links.push(child.props); } }); this.state = { links, activeIndex: 0, anchorTops: [] }; [ 'onScrollHandler', 'computedAnchorTops' ].forEach((fn) => { this[fn] = this[fn].bind(this); }); } componentDidMount() { this.resizeObserver = new resize_observer_polyfill_1.default(this.computedAnchorTops); this.resizeObserver.observe(document.body); } componentWillUnmount() { this.resizeObserver.unobserve(document.body); this.resizeObserver.disconnect(); } computedAnchorTops() { const anchorTops = []; this.state.links.forEach((link) => { const anchorEle = document.getElementById(`${link.href}`); if (anchorEle === null) { return anchorTops.push(99999); } const { top } = utils_1.doms.offset(anchorEle); anchorTops.push(top); }); this.setState({ anchorTops }, () => { this.onScrollHandler(); }); } onScrollHandler() { const { bounds, scrollTarget } = this.props; const targetNode = scrollTarget(); const scrollTop = utils_1.doms.scrollTop(targetNode); const targetTop = targetNode === window ? 0 : utils_1.doms.offset(targetNode).top; const { anchorTops } = this.state; let activeIndex = -1; while (++activeIndex < anchorTops.length) { const nextTop = anchorTops[activeIndex + 1] || Infinity; if (scrollTop + targetTop + bounds < nextTop) { break; } } this.setState({ activeIndex }); } render() { const { offsetTop, offsetBottom, scrollTarget, onSelect, className, style, affixed } = this.props; const { links, activeIndex, anchorTops } = this.state; const wrapperClass = classNames({ [utils_1.preClass('anchor-wrapper')]: true, [className]: utils_1.isExist(className) }); if (!utils_1.isExist(links)) { return null; } const catalogNode = (React.createElement("div", { style: style, className: wrapperClass }, React.createElement("ul", { className: utils_1.preClass('anchor') }, links.map((link, key) => { const { title, href, target, className: anchorClass, style: anchorStyle } = link; const realHref = anchorTops[key] === 99999 ? href : `#${href}`; const itemClass = classNames({ [utils_1.preClass('anchor-link')]: true, [anchorClass]: utils_1.isExist(anchorClass) }); return (React.createElement("li", { key: key, className: itemClass, style: anchorStyle, onClick: onSelect ? onSelect.bind(this, link) : null }, React.createElement("a", { href: realHref, title: title, target: target }, title))); })), React.createElement("span", { className: utils_1.preClass('anchor-active-line'), style: { top: activeIndex * 22 } }))); if (affixed) { return (React.createElement(affix_1.default, { onTargetScroll: this.onScrollHandler, onTargetResize: this.computedAnchorTops, offsetTop: offsetTop, offsetBottom: offsetBottom, target: scrollTarget }, catalogNode)); } return catalogNode; } } Anchor.propTypes = { className: PropTypes.string, style: PropTypes.object, affixed: PropTypes.bool, offsetTop: PropTypes.number, offsetBottom: PropTypes.number, scrollTarget: PropTypes.func, onSelect: PropTypes.func, bounds: PropTypes.number }; Anchor.defaultProps = { affixed: true, offsetTop: 20, bounds: 5, scrollTarget: () => window }; Anchor.Link = anchor_link_1.default; exports.default = Anchor;