@ -6,32 +6,42 @@ import warning from 'warning';
import assign from 'object-assign' ;
import shallowequal from 'shallowequal' ;
function getScroll ( w , top? : boolean ) {
let ret = w [ ` page ${ top ? 'Y' : 'X' } Offset ` ] ;
const method = ` scroll ${ top ? 'Top' : 'Left' } ` ;
if ( typeof ret !== 'number' ) {
const d = w . document ;
// ie6,7,8 standard mode
ret = d . documentElement [ method ] ;
if ( typeof ret !== 'number' ) {
// quirks mode
ret = d . body [ method ] ;
}
function getScroll ( target , top ) {
const prop = top ? 'pageYOffset' : 'pageXOffset' ;
const method = top ? 'scrollTop' : 'scrollLeft' ;
const isWindow = target === window ;
let ret = isWindow ? target [ prop ] : target [ method ] ;
// ie6,7,8 standard mode
if ( isWindow && typeof ret !== 'number' ) {
ret = window . document . documentElement [ method ] ;
}
return ret ;
}
function getOffset ( element ) {
const rect = element . getBoundingClientRect ( ) ;
const body = document . body ;
const clientTop = element . clientTop || body . clientTop || 0 ;
const clientLeft = element . clientLeft || body . clientLeft || 0 ;
const scrollTop = getScroll ( window , true ) ;
const scrollLeft = getScroll ( window ) ;
function getTargetRect ( target ) {
return target !== window ?
target . getBoundingClientRect ( ) :
{ top : 0 , left : 0 , bottom : 0 } ;
}
function getOffset ( element , target ) {
const elemRect = element . getBoundingClientRect ( ) ;
const targetRect = getTargetRect ( target ) ;
const scrollTop = getScroll ( target , true ) ;
const scrollLeft = getScroll ( target , false ) ;
const docElem = window . document . body ;
const clientTop = docElem . clientTop || 0 ;
const clientLeft = docElem . clientLeft || 0 ;
return {
top : rect.top + scrollTop - clientTop ,
left : rect.left + scrollLeft - clientLeft ,
top : elemRect.top - targetRect . top +
scrollTop - clientTop ,
left : elemRect.left - targetRect . left +
scrollLeft - clientLeft ,
} ;
}
@ -51,9 +61,13 @@ export default class Affix extends React.Component<AffixProps, any> {
static propTypes = {
offsetTop : React.PropTypes.number ,
offsetBottom : React.PropTypes.number ,
} ;
target : React.PropTypes.func ,
}
static defaultProps = {
target() {
return window ;
} ,
onChange() { } ,
} ;
@ -73,8 +87,10 @@ export default class Affix extends React.Component<AffixProps, any> {
}
setAffixStyle ( e , affixStyle ) {
const { onChange , target } = this . props ;
const originalAffixStyle = this . state . affixStyle ;
if ( e . type === 'scroll' && originalAffixStyle && affixStyle ) {
const isWindow = target ( ) === window ;
if ( e . type === 'scroll' && originalAffixStyle && affixStyle && isWindow ) {
return ;
}
if ( shallowequal ( affixStyle , originalAffixStyle ) ) {
@ -84,7 +100,7 @@ export default class Affix extends React.Component<AffixProps, any> {
const affixed = ! ! this . state . affixStyle ;
if ( ( affixStyle && ! originalAffixStyle ) ||
( ! affixStyle && originalAffixStyle ) ) {
this . props . onChange ( affixed ) ;
onChange ( affixed ) ;
}
} ) ;
}
@ -100,24 +116,24 @@ export default class Affix extends React.Component<AffixProps, any> {
this . setState ( { placeholderStyle } ) ;
}
handleScroll = ( e ) = > {
let { offsetTop , offsetBottom , offset } = this . props ;
updatePosition = ( e ) = > {
let { offsetTop , offsetBottom , offset , target } = this . props ;
const targetNode = target ( ) ;
// Backwards support
offsetTop = offsetTop || offset ;
const scrollTop = getScroll ( window , true ) ;
const affixNode = ReactDOM . findDOMNode ( this ) as HTMLElement ;
const fixedNode = ReactDOM . findDOMNode ( this . refs . fixedNode ) as HTMLElement ;
const elemOffset = getOffset ( affixNode ) ;
const scrollTop = getScroll ( targetNode , true ) ;
const elemOffset = getOffset ( ReactDOM . findDOMNode ( this ) , targetNode ) ;
const elemSize = {
width : fixedNode.offsetWidth,
height : fixedNode.offsetHeight,
width : this.refs.fixedNode.offsetWidth ,
height : this.refs. fixedNode.offsetHeight,
} ;
const offsetMode = {
top : null as boolean ,
bottom : null as boolean ,
} ;
// Default to `offsetTop=0`.
if ( typeof offsetTop !== 'number' && typeof offsetBottom !== 'number' ) {
offsetMode . top = true ;
offsetTop = 0 ;
@ -126,26 +142,31 @@ export default class Affix extends React.Component<AffixProps, any> {
offsetMode . bottom = typeof offsetBottom === 'number' ;
}
const targetRect = getTargetRect ( targetNode ) ;
const targetInnerHeight = targetNode . innerHeight || targetNode . clientHeight ;
if ( scrollTop > elemOffset . top - offsetTop && offsetMode . top ) {
// Fixed Top
this . setAffixStyle ( e , {
position : 'fixed' ,
top : offsetTop,
left : elemOffset.left,
width : affixNode. offsetWidth,
top : targetRect.top + offsetTop,
left : targetRect.left + elemOffset . left,
width : ReactDOM.findDOMNode( this ) . offsetWidth,
} ) ;
this . setPlaceholderStyle ( e , {
width : affixNode.offsetWidth ,
height : affixNode.offsetHeight ,
} ) ;
} else if ( scrollTop < elemOffset . top + elemSize . height + offsetBottom - window . innerHeight &&
offsetMode . bottom ) {
} else if (
scrollTop < elemOffset . top + elemSize . height + offsetBottom - targetInnerHeight &&
offsetMode . bottom
) {
// Fixed Bottom
const targetBottomOffet = targetNode === window ? 0 : ( window . innerHeight - targetRect . bottom ) ;
this . setAffixStyle ( e , {
position : 'fixed' ,
bottom : offsetBottom,
left : elemOffset.left,
width : affixNode. offsetWidth,
bottom : targetBottomOffet + offsetBottom,
left : targetRect.left + elemOffset . left,
width : ReactDOM.findDOMNode( this ) . offsetWidth,
} ) ;
this . setPlaceholderStyle ( e , {
width : affixNode.offsetWidth ,
@ -159,19 +180,39 @@ export default class Affix extends React.Component<AffixProps, any> {
componentDidMount() {
warning ( ! ( 'offset' in this . props ) , '`offset` prop of Affix is deprecated, use `offsetTop` instead.' ) ;
this . scrollEvent = addEventListener ( window , 'scroll' , this . handleScroll ) ;
this . resizeEvent = addEventListener ( window , 'resize' , this . handleScroll ) ;
const target = this . props . target ;
this . setTargetEventListeners ( target ) ;
}
componentWillUnmount() {
if ( this . scrollEvent ) {
this . scrollEvent . remove ( ) ;
}
if ( this . resizeEvent ) {
this . resizeEvent . remove ( ) ;
componentWillReceiveProps ( nextProps ) {
if ( this . props . target !== nextProps . target ) {
this . clearScrollEventListeners ( ) ;
this . setTargetEventListeners ( nextProps . target ) ;
// Mock Event object.
this . updatePosition ( { } ) ;
}
}
componentWillUnmount() {
this . clearScrollEventListeners ( ) ;
}
setTargetEventListeners ( getTarget ) {
const target = getTarget ( ) ;
this . scrollEvent = addEventListener ( target , 'scroll' , this . updatePosition ) ;
this . resizeEvent = addEventListener ( target , 'resize' , this . updatePosition ) ;
}
clearScrollEventListeners() {
[ 'scrollEvent' , 'resizeEvent' ] . forEach ( ( name ) = > {
if ( this [ name ] ) {
this [ name ] . remove ( ) ;
}
} ) ;
}
render() {
const className = classNames ( {
'ant-affix' : this . state . affixStyle ,
@ -180,6 +221,7 @@ export default class Affix extends React.Component<AffixProps, any> {
const props = assign ( { } , this . props ) ;
delete props . offsetTop ;
delete props . offsetBottom ;
delete props . target ;
return (
< div { ...props } style = { this . state . placeholderStyle } >