UNPKG

react-sticky-dynamic-header

Version:

React component that makes a header sticky with dynamic content before and after being sticky

175 lines (164 loc) 6.92 kB
import React from 'react'; import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; const elementName = '__react-sticky-dynamic-header__'; let componentOK = true; class DynamicHeader extends React.Component { constructor(props) { super(props); if (!(this.props.children) || !(this.props.children.length) || (this.props.children.length < 2)) { console.log('ERROR [react-sticky-dynamic-header]: 2 components must be passed as children!'); componentOK = false; } else if (this.props.children.length > 2) { console.log('WARNING [react-sticky-dynamic-header]: more than 2 components were passed as children, only the first 2 are used, the rest will be ignored!'); if (!this.props.children[0].type || !this.props.children[1].type) { componentOK = false; } } else if (!this.props.children[0].type || !this.props.children[1].type) componentOK = false; this.state = { headerType: 'big', bigElementHeight: 0, smallElementHeight: 0, alreadyCalculated: false, bigIndex: 0, smallIndex: 1, }; } componentDidMount() { var h1 = parseInt(this.refs[elementName+'_1'].children[0].offsetHeight); var h2 = parseInt(this.refs[elementName+'_2'].children[0].offsetHeight); this.setState({ bigElementHeight: h1, smallElementHeight: h2, alreadyCalculated: true, }); window.addEventListener('scroll', this._calcTranslation.bind(this, h1, h2)); } componentWillUnmount() { window.removeEventListener('scroll', this._calcTranslation) } _calcTranslation(h1, h2) { var _window = window; var heightDiff = parseInt(h1) - parseInt(h2); if ((heightDiff <= 0) || (!this.props.useHeadersDifference)) heightDiff = parseInt(h1); var scrollPos = _window.scrollY; if (scrollPos > heightDiff) { this.setState({ headerType: 'small', }); } else { this.setState({ headerType: 'big', }); } } render() { var tmpEle; var returnEle; if (!componentOK) { tmpBigComponent = ( <div style={{height: '50px', backgroundColor: '#666', width: '100%', position: 'relative', top: 0, zIndex: 1039, textAlign: 'center',}}> <div style={{paddingTop: '15px',}}>ERROR [react-sticky-dynamic-header]: 2 components must be passed as children!</div> </div> ); tmpSmallComponent = ( <div style={{height: '50px', backgroundColor: '#ccc', width: '100%', position: 'fixed', top: 0, zIndex: 1039, textAlign: 'center', }}> <div style={{paddingTop: '15px',}}>ERROR [react-sticky-dynamic-header]: 2 components must be passed as children!</div> </div> ); tmpEle = ( <div> <div style={{position: 'absolute', top: '-10000px', }} ref={elementName+'_1'}>{tmpBigComponent}</div> <div style={{position: 'absolute', top: '-20000px', }} ref={elementName+'_2'}>{tmpSmallComponent}</div> </div> ) } else { tmpEle = ( <div> <div style={{position: 'absolute', top: '-10000px', }} ref={elementName+'_1'}>{this.props.children[0]}</div> <div style={{position: 'absolute', top: '-20000px', }} ref={elementName+'_2'}>{this.props.children[1]}</div> </div> ) } if (this.state.alreadyCalculated) { tmpEle = ''; } var styleEle = ( <style>{'\ .'+elementName+'_1'+'-enter {\ opacity: 0.01;\ transition: opacity '+this.props.effectDuration+'ms ease-in;\ }\ .'+elementName+'_1'+'-enter.'+elementName+'_1'+'-enter-active {\ opacity: 1;\ }\ .'+elementName+'_1'+'-leave {\ opacity: 1;\ transition: opacity 0.1s ease-in;\ }\ .'+elementName+'_1'+'-leave.'+elementName+'_1'+'-leave-active {\ opacity: 0.01;\ }\ .'+elementName+'_2'+'-enter {\ opacity: 0.01;\ transition: opacity '+this.props.effectDuration+'ms ease-in;\ }\ .'+elementName+'_2'+'-enter.'+elementName+'_2'+'-enter-active {\ opacity: 1;\ }\ .'+elementName+'_2'+'-leave {\ opacity: 1;\ transition: opacity 0.1s ease-in;\ }\ .'+elementName+'_2'+'-leave.'+elementName+'_2'+'-leave-active {\ opacity: 0.01;\ }\ '}</style> ); if (this.state.headerType == 'big') { let bigHeaderComponent = ( <div key={elementName+'_1'} style={{height: '50px', backgroundColor: '#666', width: '100%', position: 'relative', top: 0, zIndex: 1039, textAlign: 'center', }}> <div style={{paddingTop: '15px',}}>ERROR [react-sticky-dynamic-header]: 2 components must be passed as children!</div> </div> ); if (componentOK) { bigHeaderComponent = React.cloneElement(this.props.children[this.state.bigIndex], {key: elementName+'_1'}); } returnEle = ( <div> {tmpEle} {styleEle} <ReactCSSTransitionGroup transitionName={elementName+'_1'} transitionAppear={false} transitionEnterTimeout={500} transitionLeaveTimeout={500} transitionLeave={false} transitionEnter={this.props.hasEffect}> {bigHeaderComponent} </ReactCSSTransitionGroup> </div> ); } else { let smallHeaderComponent = ( <div key={elementName+'_2'} style={{height: '50px', backgroundColor: '#ccc', width: '100%', position: 'fixed', top: 0, zIndex: 1039, textAlign: 'center', }}> <div style={{paddingTop: '15px',}}>ERROR [react-sticky-dynamic-header]: 2 components must be passed as children!</div> </div> ); if (componentOK) { smallHeaderComponent = React.cloneElement(this.props.children[this.state.smallIndex], {key: elementName+'_2'}); } returnEle = ( <div> {tmpEle} {styleEle} <ReactCSSTransitionGroup transitionName={elementName+'_2'} transitionAppear={false} transitionEnterTimeout={500} transitionLeaveTimeout={500} transitionLeave={false} transitionEnter={this.props.hasEffect}> {smallHeaderComponent} </ReactCSSTransitionGroup> <div style={{height: (this.state.bigElementHeight + 'px'), width: '100%', clear: 'both'}}></div> </div> ); } return returnEle; } } DynamicHeader.defaultProps = { hasEffect: true, effectDuration: 600, useHeadersDifference: false, }; export default DynamicHeader;