react-bootstrap
Version:
Bootstrap 3 components build with React
123 lines (98 loc) • 3.27 kB
JavaScript
/* global window, document */
import React from './react-es6';
import domUtils from './domUtils';
var AffixMixin = {
propTypes: {
offset: React.PropTypes.number,
offsetTop: React.PropTypes.number,
offsetBottom: React.PropTypes.number
},
getInitialState: function () {
return {
affixClass: 'affix-top'
};
},
getPinnedOffset: function (DOMNode) {
if (this.pinnedOffset) {
return this.pinnedOffset;
}
DOMNode.className = DOMNode.className.replace(/affix-top|affix-bottom|affix/, '');
DOMNode.className += DOMNode.className.length ? ' affix' : 'affix';
this.pinnedOffset = domUtils.getOffset(DOMNode).top - window.pageYOffset;
return this.pinnedOffset;
},
checkPosition: function () {
var DOMNode, scrollHeight, scrollTop, position, offsetTop, offsetBottom,
affix, affixType, affixPositionTop;
// TODO: or not visible
if (!this.isMounted()) {
return;
}
DOMNode = this.getDOMNode();
scrollHeight = document.documentElement.offsetHeight;
scrollTop = window.pageYOffset;
position = domUtils.getOffset(DOMNode);
offsetTop;
offsetBottom;
if (this.affixed === 'top') {
position.top += scrollTop;
}
offsetTop = this.props.offsetTop != null ?
this.props.offsetTop : this.props.offset;
offsetBottom = this.props.offsetBottom != null ?
this.props.offsetBottom : this.props.offset;
if (offsetTop == null && offsetBottom == null) {
return;
}
if (offsetTop == null) {
offsetTop = 0;
}
if (offsetBottom == null) {
offsetBottom = 0;
}
if (this.unpin != null && (scrollTop + this.unpin <= position.top)) {
affix = false;
} else if (offsetBottom != null && (position.top + DOMNode.offsetHeight >= scrollHeight - offsetBottom)) {
affix = 'bottom';
} else if (offsetTop != null && (scrollTop <= offsetTop)) {
affix = 'top';
} else {
affix = false;
}
if (this.affixed === affix) {
return;
}
if (this.unpin != null) {
DOMNode.style.top = '';
}
affixType = 'affix' + (affix ? '-' + affix : '');
this.affixed = affix;
this.unpin = affix === 'bottom' ?
this.getPinnedOffset(DOMNode) : null;
if (affix === 'bottom') {
DOMNode.className = DOMNode.className.replace(/affix-top|affix-bottom|affix/, 'affix-top');
affixPositionTop = scrollHeight - offsetBottom - DOMNode.offsetHeight - domUtils.getOffset(DOMNode).top;
}
this.setState({
affixClass: affixType,
affixPositionTop: affixPositionTop
});
},
checkPositionWithEventLoop: function () {
setTimeout(this.checkPosition, 0);
},
componentDidMount: function () {
window.addEventListener('scroll', this.checkPosition);
document.addEventListener('click', this.checkPositionWithEventLoop);
},
componentWillUnmount: function () {
window.removeEventListener('scroll', this.checkPosition);
document.addEventListener('click', this.checkPositionWithEventLoop);
},
componentDidUpdate: function (prevProps, prevState) {
if (prevState.affixClass === this.state.affixClass) {
this.checkPositionWithEventLoop();
}
}
};
export default = AffixMixin;