Last active
October 16, 2018 17:38
-
-
Save hypervillain/3db4e68a5d6d6dbdaf454d22c671f01f to your computer and use it in GitHub Desktop.
Generic React Affix component, using react-measure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import PropTypes from 'prop-types'; | |
import Measure from 'react-measure'; | |
import classnames from 'classnames'; | |
// replace this if you don't use CSS modules | |
import cls from './affix.module.scss'; | |
class Affix extends Component { | |
static propTypes = { | |
children: PropTypes.node.isRequired, | |
FixedComponent: PropTypes.node || PropTypes.string, | |
offset: PropTypes.number, | |
} | |
static defaultProps = { | |
offset: document.documentElement.scrollTop || document.body.scrollTop || 0, | |
FixedComponent: 'div', | |
}; | |
state = { | |
affix: false, | |
}; | |
componentDidMount() { | |
window.addEventListener('scroll', this.handleScroll); | |
} | |
componentWillUnmount() { | |
window.removeEventListener('scroll', this.handleScroll); | |
} | |
handleScroll = () => { | |
const { affix } = this.state; | |
const { offset } = this.props; | |
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; | |
if (!affix && scrollTop >= offset) { | |
this.setState({ | |
affix: true, | |
}); | |
} | |
if (affix && scrollTop < offset) { | |
this.setState({ | |
affix: false, | |
}); | |
} | |
}; | |
render() { | |
const isAffix = this.state.affix; | |
const { | |
className, | |
FixedComponent, | |
wrapperStyle, | |
style, | |
...rest | |
} = this.props; | |
return ( | |
<Measure | |
bounds | |
onResize={contentRect => | |
this.setState({ parentWidth: contentRect.bounds.width }) | |
} | |
> | |
{({ measureRef }) => ( | |
<div className={cls.wrapper} ref={measureRef} style={wrapperStyle}> | |
<FixedComponent | |
{...rest} | |
className={classnames(isAffix ? cls.affix : null, className)} | |
style={{ width: this.state.parentWidth, ...style }} | |
> | |
{this.props.children} | |
</FixedComponent> | |
</div> | |
)} | |
</Measure> | |
); | |
} | |
} | |
Affix.displayName = 'Affix'; | |
export default Affix; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$pagePadding: 60px; | |
.wrapper { | |
width: 100%; | |
z-index: 1; | |
} | |
.affix { | |
position: fixed; | |
// if children.offsetHeight may be > 100vh | |
// allow an overflow + define some bottom padding | |
overflow: auto; | |
max-height: 95%; | |
padding-bottom: $pagePadding; | |
z-index: 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment