@fpxfd/next
Version:
A configurable component library for web built on React.
350 lines (313 loc) • 13.7 kB
JavaScript
import _extends from 'babel-runtime/helpers/extends';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
var _class, _temp2;
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { polyfill } from 'react-lifecycles-compat';
import Affix from '../affix';
import ConfigProvider from '../config-provider';
import { events } from '../util';
import AnchorLink from './anchor-link';
import AnchorContext from './context';
import getScroll from './getScroll';
import scrollTo from './scrollTo';
function getDefaultContainer() {
return window;
}
function getOffsetTop(element, container) {
if (!element.getClientRects().length) {
return 0;
}
var rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
if (container === window) {
container = element.ownerDocument.documentElement;
return rect.top - container.clientTop;
}
return rect.top - container.getBoundingClientRect().top;
}
return rect.top;
}
var sharpMatcherRegx = /#([\S ]+)$/;
var scrollTimeout = void 0;
/**
* Anchor
*/
var Anchor = (_temp2 = _class = function (_Component) {
_inherits(Anchor, _Component);
function Anchor() {
var _temp, _this, _ret;
_classCallCheck(this, Anchor);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _Component.call.apply(_Component, [this].concat(args))), _this), _this.state = {
activeLink: null,
isOpen: _this.props.open
}, _this.wrapperRef = React.createRef(), _this.links = [], _this.registerLink = function (link) {
if (!_this.links.includes(link)) {
_this.links.push(link);
}
}, _this.unregisterLink = function (link) {
var index = _this.links.indexOf(link);
if (index !== -1) {
_this.links.splice(index, 1);
}
}, _this.getContainer = function () {
var getContainer = _this.props.getContainer;
var getFunc = getContainer || getDefaultContainer;
return getFunc();
}, _this.handleScrollTo = function (link) {
var _this$props = _this.props,
offsetTop = _this$props.offsetTop,
targetOffset = _this$props.targetOffset;
_this.setCurrentActiveLink(link);
var container = _this.getContainer();
var scrollTop = getScroll(container, true);
var sharpLinkMatch = sharpMatcherRegx.exec(link);
if (!sharpLinkMatch) {
return;
}
var targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
var eleOffsetTop = getOffsetTop(targetElement, container);
var y = scrollTop + eleOffsetTop;
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
_this.animating = true;
scrollTo(y, {
callback: function callback() {
_this.animating = false;
},
getContainer: _this.getContainer
});
}, _this.setCurrentActiveLink = function (link) {
var activeLink = _this.state.activeLink;
var _this$props2 = _this.props,
onChange = _this$props2.onChange,
getCurrentAnchor = _this$props2.getCurrentAnchor;
if (activeLink === link) {
return;
}
_this.setState({
isOpen: !_this.props.allowFold,
activeLink: typeof getCurrentAnchor === 'function' ? getCurrentAnchor(link) : link
});
onChange && onChange(link);
}, _this.handleScroll = function () {
if (_this.animating) {
return;
}
var _this$props3 = _this.props,
offsetTop = _this$props3.offsetTop,
bounds = _this$props3.bounds,
targetOffset = _this$props3.targetOffset,
allowFold = _this$props3.allowFold;
var currentActiveLink = _this.getCurrentAnchor(targetOffset !== undefined ? targetOffset : offsetTop || 0, bounds);
_this.setCurrentActiveLink(currentActiveLink);
if (allowFold) {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(function () {
// 滚动停止后,展开anchor
_this.setState({ isOpen: true });
clearTimeout(scrollTimeout);
}, 300);
}
}, _this.handlerOpen = function () {
_this.setState({ isOpen: !_this.state.isOpen });
}, _this.getMemoizedContextValue = function (link, onClickFn) {
return {
registerLink: _this.registerLink,
unregisterLink: _this.unregisterLink,
scrollTo: _this.handleScrollTo,
activeLink: link,
onClick: onClickFn
};
}, _temp), _possibleConstructorReturn(_this, _ret);
}
// scroll scope's container
Anchor.prototype.componentDidMount = function componentDidMount() {
this.scrollContainer = this.getContainer();
events.on(this.scrollContainer, 'scroll', this.handleScroll);
if (this.links) {
this.setState({ activeLink: this.links[0] });
}
};
Anchor.prototype.componentDidUpdate = function componentDidUpdate() {
events.off(this.scrollContainer, 'scroll', this.handleScroll);
events.on(this.scrollContainer, 'scroll', this.handleScroll);
};
Anchor.prototype.componentWillUnmount = function componentWillUnmount() {
events.off(this.scrollContainer, 'scroll', this.handleScroll);
};
Anchor.prototype.getCurrentAnchor = function getCurrentAnchor() {
var offsetTop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var bounds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
var linkSections = [];
var container = this.getContainer();
this.links.forEach(function (link) {
var sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
if (!sharpLinkMatch) {
return;
}
var target = document.getElementById(sharpLinkMatch[1]);
if (target) {
var top = getOffsetTop(target, container);
if (top < offsetTop + bounds) {
linkSections.push({
link: link,
top: top
});
}
}
});
if (linkSections.length) {
var maxSection = linkSections.reduce(function (prev, curr) {
return curr.top > prev.top ? curr : prev;
});
return maxSection.link;
}
return '';
};
Anchor.prototype.render = function render() {
var _classNames,
_classNames2,
_this2 = this;
var direction = this.context.direction;
var _props = this.props,
prefix = _props.prefix,
_props$className = _props.className,
className = _props$className === undefined ? '' : _props$className,
style = _props.style,
offsetTop = _props.offsetTop,
affix = _props.affix,
dataSource = _props.dataSource,
allowFold = _props.allowFold,
onClick = _props.onClick;
var _state = this.state,
activeLink = _state.activeLink,
isOpen = _state.isOpen;
var wrapperClass = classNames((allowFold ? prefix + '-wrapper ' + prefix + '-wrapper-allowFold' : prefix + '-wrapper') + ' ', (_classNames = {}, _classNames[prefix + '-rtl'] = direction === 'rtl', _classNames), className);
var anchorClass = classNames(prefix, (_classNames2 = {}, _classNames2[prefix + '-fixed'] = !affix, _classNames2));
var wrapperStyle = _extends({
maxHeight: offsetTop ? 'calc(100vh - ' + offsetTop + 'px)' : '100vh'
}, style);
var anchorContent = React.createElement(
'div',
{ ref: this.wrapperRef, className: wrapperClass, style: wrapperStyle },
React.createElement(
'div',
{ className: '' + (allowFold ? anchorClass + ' ' + anchorClass + '-allowFold' : anchorClass) },
dataSource && dataSource.map(function (item, index) {
var props = { href: item.href, title: item.title, allowFold: allowFold, isOpen: isOpen };
if (item.children) {
return React.createElement(
'div',
{ key: item.href + '-' + index },
isOpen ? React.createElement(AnchorLink, _extends({}, props, {
isParent: true,
style: {
marginBottom: 0,
borderLeftColor: '#DADFE8',
color: '#151F28'
}
})) : null,
item.children.map(function (g) {
var props = {
href: g.href,
title: g.title,
allowFold: allowFold,
isOpen: isOpen,
isSub: true
};
return React.createElement(AnchorLink, _extends({}, props, { key: g.href }));
})
);
} else {
return React.createElement(AnchorLink, _extends({}, props, { key: item.href }));
}
})
),
allowFold ? React.createElement(
'div',
{ className: prefix + '-fold', onClick: function onClick() {
return _this2.handlerOpen();
} },
React.createElement(
'span',
null,
isOpen ? React.createElement(
'svg',
{
viewBox: '0 0 1024 1024',
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg',
'p-id': '8015'
},
React.createElement('path', {
d: 'M448 672c-6.4 0-19.2 0-25.6-6.4-12.8-12.8-12.8-32 0-44.8L531.2 512 422.4 409.6c-12.8-12.8-12.8-32 0-44.8s32-12.8 44.8 0l128 128c12.8 12.8 12.8 32 0 44.8l-128 128C467.2 672 454.4 672 448 672z',
'p-id': '8016'
})
) : React.createElement(
'svg',
{
viewBox: '0 0 1024 1024',
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg',
'p-id': '8747'
},
React.createElement('path', {
d: 'M576 672c-6.4 0-19.2 0-25.6-6.4l-128-128c-12.8-12.8-12.8-32 0-44.8l128-128c12.8-12.8 32-12.8 44.8 0s12.8 32 0 44.8L492.8 512l102.4 102.4c12.8 12.8 12.8 32 0 44.8C595.2 672 582.4 672 576 672z',
'p-id': '8748'
})
)
)
) : null
);
var contextValue = this.getMemoizedContextValue(activeLink, onClick);
return React.createElement(
AnchorContext.Provider,
{ value: contextValue },
!affix ? anchorContent : React.createElement(
Affix,
{ useAbsolute: true, offsetTop: offsetTop, target: this.getContainer },
anchorContent
)
);
};
return Anchor;
}(Component), _class.propTypes = {
prefix: PropTypes.string,
className: PropTypes.string,
style: PropTypes.any,
dataSource: PropTypes.array,
allowFold: PropTypes.bool,
open: PropTypes.bool,
children: PropTypes.any,
offsetTop: PropTypes.number,
bounds: PropTypes.number,
affix: PropTypes.bool,
getContainer: PropTypes.func,
/** Return customize highlight anchor */
getCurrentAnchor: PropTypes.func,
onClick: function onClick(e, link) {
return PropTypes.void;
},
/** Scroll to target offset value, if none, it's offsetTop prop value or 0. */
targetOffset: PropTypes.number,
/** Listening event when scrolling change active link */
onChange: function onChange(currentActiveLink) {
return PropTypes.void;
}
}, _class.defaultProps = {
prefix: 'next-anchor',
affix: true,
allowFold: false,
open: false
}, _temp2);
Anchor.displayName = 'Anchor';
export default ConfigProvider.config(polyfill(Anchor));