chowa
Version:
UI component library based on React
122 lines (121 loc) • 4.79 kB
JavaScript
/**
* @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.
*/
;
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;