import * as React from 'react' ; import ClassNames from 'classnames' ; import { AffixProps } from './interface' ; import { getPrefixCls } from '../utils' ; interface ChangeProps { setAffixed : React . Dispatch < React . SetStateAction < boolean > > ; setStyle : React . Dispatch < React . SetStateAction < { } > > ; affixed : boolean ; } interface ScrollProps extends ChangeProps { props : AffixProps ; wrapperRef : React . RefObject < HTMLDivElement > ; setOuterStyle : React . Dispatch < React . SetStateAction < { } > > ; } interface UseAffixProps extends ChangeProps { onChange : ( affixed : boolean ) => void ; style : React . CSSProperties ; } const noop = ( ) => { } ; const { useRef , useState , useEffect } = React ; const defaultProps = { onChange : noop , useSticky : false , prefixCls : 'cp-ui-affix' } ; const getScrollTarget = ( { container } ) => { if ( container === undefined ) return window ; return container ( ) ; } ; const getClassNames = ( { useSticky , prefixCls } ) => { return ClassNames ( prefixCls , { [ getPrefixCls ( prefixCls , 'use-sticky' ) ] : useSticky } ) ; } ; const getStyle = ( { useSticky , offsetTop , offsetBottom } , style ) => { let styleCopy = { } ; if ( useSticky ) return style ; styleCopy . position = 'sticky' ; if ( offsetBottom === undefined ) { styleCopy . top = offsetTop ; } else { styleCopy . bottom = offsetBottom ; } return styleCopy ; } ; const setEventListener = ( props , listener ) => { const container = getScrollTarget ( props ) ; container . addEventListener ( 'scroll' , listener ) ; return container ; } ; const handleChange = ( { onChange , setAffixed , setStyle , affixed , style } ) => { onChange ( affixed ) ; setAffixed ( affixed ) ; setStyle ( style ) ; } ; const getOffsetTop = ( offsetTop ? , offsetBottom ? ) => { let _offsetTop = 0 ; if ( offsetBottom === undefined && offsetTop === undefined ) { offsetTop = 0 ; } else { _offsetTop = offsetTop ?? 0 ; } return _offsetTop ; } ; const handleScroll = ( { props , setStyle , wrapperRef , affixed , setAffixed , setOuterStyle } ) => { const { offsetBottom } = props ; wrapperScollStyle ( { props , setStyle , wrapperRef , affixed , setAffixed , setOuterStyle } , offsetBottom !== undefined ) ; } ; const wrapperScollStyle = ( { props , setStyle , wrapperRef , affixed , setAffixed , setOuterStyle } , isButtom = false ) => { let style = { } ; const { offsetTop , offsetBottom , onChange } = props ; const rect = ( wrapperRef . current as any ) . getBoundingClientRect ( ) ; const { top , bottom , left , width , height } = rect ; let scrollPos = top ; if ( isButtom ) { scrollPos = bottom ; } if ( scrollPos > getOffsetTop ( props . offsetTop , props . offsetBottom ) ) { if ( affixed ) { style . position = 'relative' ; handleChange ( { onChange , setAffixed , setStyle , affixed : false , style } ) ; setOuterStyle ( { } ) ; } } else { if ( ! affixed ) { style = { position : 'fixed' , left : left } ; if ( ! isButtom ) { style . top = offsetTop ; } else { style . bottom = offsetBottom ; } handleChange ( { onChange , setAffixed , setStyle , affixed : true , style } ) ; setOuterStyle ( { width , height } ) ; } } } ; const Affix = props => { const { useSticky } = props ; const wrapperRef = useRef ( null ) ; const targetRef = useRef ( null ) ; const [ style , setStyle ] = useState ( { } ) ; const [ outerStyle , setOuterStyle ] = useState ( { } ) ; const [ affixed , setAffixed ] = useState ( false ) ; const wrapperStyle = getStyle ( props , style ) ; useEffect ( ( ) => { if ( useSticky ) { const listener = ( ) => { handleScroll ( { props , setStyle , wrapperRef , affixed , setAffixed , setOuterStyle } ) ; } ; const container = setEventListener ( props , listener ) ; return ( ) => { container . removeEventListener ( 'scroll' , listener ) ; } ; } return ( ) => { } ; } , [ useSticky ] ) ; const classStr = getClassNames ( props ) ; return ( < div ref = { wrapperRef } className = { classStr } style = { useSticky ? outerStyle : wrapperStyle } > < div ref = { targetRef } style = { wrapperStyle } > { props . children } < /div> < /div>); } ; Affix . defaultProps = defaultProps ; export default Affix ;