import { Component , Prop , h , ComponentInterface , Host , State , Event , EventEmitter , Watch , Element } from '@stencil/core' ; import Taro from '@tarojs/taro' ; import classNames from 'classnames' ; function setTransform ( nodeStyle , value ) { nodeStyle . transform = value ; nodeStyle . webkitTransform = value ; nodeStyle . MozTransform = value ; } const isWebView = typeof navigator !== 'undefined' && /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i . test ( navigator . userAgent ) ; const INDICATOR = { activate : 'release' , deactivate : 'pull' , release : 'loading' , finish : 'finish' } ; let supportsPassive = false ; try { const opts = Object . defineProperty ( { } , 'passive' , { get ( ) { supportsPassive = true ; } } ) ; window . addEventListener ( 'cancel' , ( ) => ( { } ) , opts ) ; } catch ( e ) { } const willPreventDefault = supportsPassive ? { passive : false } : false ; @ Component ( { tag : 'taro-pull-to-refresh' , styleUrl : './style/index.css' } ) export class PullToRefresh implements ComponentInterface { @ Prop ( ) prefixCls = 'rmc-pull-to-refresh' ; @ Prop ( ) distanceToRefresh = 50 ; @ Prop ( ) damping = 100 ; @ Prop ( ) indicator = INDICATOR ; @ State ( ) currSt : 'activate' | 'deactivate' | 'release' | 'finish' = 'deactivate' ; @ State ( ) dragOnEdge = false ; @ Element ( ) el : HTMLElement ; @ Event ( { eventName : 'refresh' } ) onRefresh : EventEmitter ; private contentRef : HTMLElement ; private _to : Record < string , EventListener > ; private _ScreenY = 0 ; private _startScreenY = 0 ; private _lastScreenY = 0 ; private _isMounted = false ; private get scrollContainer ( ) { return document . querySelector ( '.taro-tabbar__panel' ) || document . body ; } @ Watch ( 'currSt' ) statusChange ( ) { if ( this . currSt === 'release' ) { const pageEl = this . el . closest ( '.taro_page' ) ; if ( pageEl && pageEl . __page ) { pageEl . __page . onPullDownRefresh ( ) ; } } } disconnectedCallback ( ) { this . destroy ( ) ; } componentDidLoad ( ) { this . init ( ) ; this . _isMounted = true ; Taro . eventCenter . on ( '__taroStartPullDownRefresh' , ( { successHandler , errorHandler } ) => { try { this . triggerPullDownRefresh ( true ) ; successHandler ( { errMsg : 'startPullDownRefresh: ok' } ) ; } catch ( e ) { errorHandler ( { errMsg : 'startPullDownRefresh: fail' } ) ; } } ) ; Taro . eventCenter . on ( '__taroStopPullDownRefresh' , ( { successHandler , errorHandler } ) => { try { this . triggerPullDownRefresh ( false ) ; successHandler ( { errMsg : 'stopPullDownRefresh: ok' } ) ; } catch ( e ) { errorHandler ( { errMsg : 'stopPullDownRefresh: fail' } ) ; } } ) ; } triggerPullDownRefresh = ( flag ) => { if ( ! this . dragOnEdge && this . _isMounted ) { if ( flag ) { this . _lastScreenY = this . distanceToRefresh + 1 ; this . currSt = 'release' ; this . setContentStyle ( this . _lastScreenY ) ; } else { this . currSt = 'finish' ; this . reset ( ) ; } } } ; init = ( ) => { const ele = this . scrollContainer ; this . _to = { touchstart : this . onTouchStart . bind ( this , ele ) , touchmove : this . onTouchMove . bind ( this , ele ) , touchend : this . onTouchEnd . bind ( this , ele ) , touchcancel : this . onTouchEnd . bind ( this , ele ) } ; Object . keys ( this . _to ) . forEach ( key => { ele . addEventListener ( key , this . _to [ key ] , willPreventDefault ) ; } ) ; } ; destroy = ( ) => { const ele = this . scrollContainer ; Object . keys ( this . _to ) . forEach ( key => { ele . removeEventListener ( key , this . _to [ key ] ) ; } ) ; } ; onTouchStart = ( _ , e ) => { this . _ScreenY = this . _startScreenY = e . touches [ 0 ] . screenY ; this . _lastScreenY = this . _lastScreenY || 0 ; } ; isEdge = ( ele ) => { const container = this . scrollContainer ; if ( container && container === document . body ) { const scrollNode = document . scrollingElement ? document . scrollingElement : document . body ; return scrollNode . scrollTop <= 0 ; } return ele . scrollTop <= 0 ; } ; damp = ( dy ) => { if ( Math . abs ( this . _lastScreenY ) > this . damping ) { return 0 ; } const ratio = Math . abs ( this . _ScreenY - this . _startScreenY ) / window . screen . height ; dy *= ( 1 - ratio ) * 0.6 ; return dy ; } ; onTouchMove = ( ele , e ) => { const _screenY = e . touches [ 0 ] . screenY ; if ( this . _startScreenY > _screenY ) { return ; } if ( this . isEdge ( ele ) ) { if ( ! this . dragOnEdge ) { this . _ScreenY = this . _startScreenY = e . touches [ 0 ] . screenY ; this . dragOnEdge = true ; } if ( e . cancelable ) { e . preventDefault ( ) ; } const _diff = Math . round ( _screenY - this . _ScreenY ) ; this . _ScreenY = _screenY ; this . _lastScreenY += this . damp ( _diff ) ; this . setContentStyle ( this . _lastScreenY ) ; if ( Math . abs ( this . _lastScreenY ) < this . distanceToRefresh ) { if ( this . currSt !== 'deactivate' ) { this . currSt = 'deactivate' ; } } else { if ( this . currSt === 'deactivate' ) { this . currSt = 'activate' ; } } if ( isWebView && e . changedTouches [ 0 ] . clientY < 0 ) { this . onTouchEnd ( ) ; } } } ; onTouchEnd = ( ) => { if ( this . dragOnEdge ) { this . dragOnEdge = false ; } if ( this . currSt === 'activate' ) { this . currSt = 'release' ; this . onRefresh . emit ( this ) ; this . _lastScreenY = this . distanceToRefresh + 1 ; this . setContentStyle ( this . _lastScreenY ) ; } else if ( this . currSt === 'release' ) { this . _lastScreenY = this . distanceToRefresh + 1 ; this . setContentStyle ( this . _lastScreenY ) ; } else { this . reset ( ) ; } } ; reset = ( ) => { this . _lastScreenY = 0 ; this . setContentStyle ( 0 ) ; } ; setContentStyle = ( ty ) => { if ( this . contentRef ) { if ( ty ) { setTransform ( this . contentRef . style , `translate3d(0px,${ ty }px,0)` ) ; } else { setTransform ( this . contentRef . style , '' ) ; } } } ; render ( ) { const renderRefresh = ( cls ) => { const { currSt , dragOnEdge , prefixCls } = this ; const cla = classNames ( cls , ! dragOnEdge && `${ prefixCls }-transition` ) ; const showIndicator = currSt === 'activate' || currSt === 'release' ; return ( < div class = { `${ prefixCls }-content-wrapper` } > < div class = { cla } ref = { el => { this . contentRef = el ! ; } } > { showIndicator && ( < div class = { `${ prefixCls }-indicator` } > < div /> < div / > < div /> < /div>)} < slot / > < /div> < /div>); } ; if ( this . scrollContainer ) { return renderRefresh ( `${ this . prefixCls }-content ${ this . prefixCls }-down` ) ; } return ( < Host class = { classNames ( this . prefixCls , `${ this . prefixCls }-down` ) } > { renderRefresh ( `${ this . prefixCls } - content `)}
      </Host>);
    }
}
