import React from 'react'
import PropTypes from 'prop-types'
import {findDOMNode} from 'react-dom'
import Component from '../utils/Component'
import classnames from 'classnames'
import {setPhPrefix, getClientHeight, getScrollTop, getDocumentHeight, preventDefault} from '../utils/Tool'
import Logger from '../utils/logger'
import Button from '../button'
import Icon from '../icon'
import '../style'
import 'phoenix-styles/less/modules/pullup.less'
/**
* 加载更多组件<br/>
* - 书写时PullDown组件在可加载列表的前面。
* - 可通过loadCallback设置下拉的执行回调。
* - 如果当前列表存在自定义的滚动条,需要通过getTarget传递滚动的目标,且滚动元素的子元素必须只有一个。
* - 只有getTarget的元素(默认window)滚到最顶端的时候,才能触发下拉。
*
* 主要属性和接口:
* - loadCallback:点击按钮加载或滑到底部自动加载的回调函数。
* - getTarget: 如果当前列表存在自定义的滚动条,需要传递滚动的目标。
*
* 示例:
* ```code
* <div style={{height:'300px',overflow:'auto'}} ref={(list)=>this.list=list}> // 用到getTarget需要保证只有一个子元素,包裹住滚动的所有内容
* <div>
* <List>...</List> // 可加载列表的位置
* <PullDown loadCallback={this.loadCallback.bind(this)}
* getTarget={()=>{return list;}} />
* </div>
* </div>
* ```
*
* @class PullDown
* @module 操作类组件
* @extends Component
* @constructor
* @since 3.3.0
* @demo pulldown|pulldown.js {展示}
* @show true
* */
const MAX_HEIGHT = 800,
MAX_DISTANCE = 200/2
export default class PullDown extends Component{
static propTypes = {
/**
* 样式前缀
* @property classPrefix
* @type String
* @default 'pulldown'
* */
classPrefix: PropTypes.string,
/**
* 滑到底部自动加载的回调函数,用户在该函数内自定义请求
* @method loadCallback
* @type Function
* @default null
**/
loadCallback: PropTypes.func,
/**
* 如果当前列表存在自定义的滚动条,需要传递滚动的目标
* @method getTarget
* @type Function
* @default null
* @return {object} 目标元素的ref
**/
getTarget: PropTypes.func
}
static defaultProps ={
classPrefix:'pulldown',
classMapping : {}
}
constructor(props,context){ // 记得做数据没有触底的判断
super(props,context)
new Logger('PullDown')
this.touchTop = true
this.distanceY = 0
this.start = false
this.scrollHandle = this.scrollHandle.bind(this)
this.scrollElem = window
}
scrollHandle(e){
let {getTarget} = this.props,
offsetTop = this.nextElem.offsetTop,
scrollTop = getScrollTop()
if(getTarget){
scrollTop = target.scrollTop
}
// console.log('offsetTop', offsetTop)
// console.log('scrollTop', scrollTop)
if(scrollTop>0){
this.touchTop = false
}else{
this.touchTop = true
}
}
componentDidMount(){
let pullDownElem = findDOMNode(this.pullDown)
this.dragElem = pullDownElem.parentNode
this.nextElem = pullDownElem.nextElementSibling
this.addClass(this.dragElem, 'animated')
// this.addClass(this.nextElem, 'hardware')
this.dragEventHandle(this.dragElem)
if(this.props.getTarget){
setTimeout(()=>{
this.scrollElem = this.props.getTarget()
this.scrollElem.addEventListener('scroll', this.scrollHandle, false)
},0);
}else{
this.scrollElem.addEventListener('scroll', this.scrollHandle, false)
}
}
dragEventHandle(elem){
this.touchStartHandle = this.touchStartHandle.bind(this)
elem.addEventListener('touchstart', this.touchStartHandle, false)
this.touchMoveHandle = this.touchMoveHandle.bind(this)
elem.addEventListener('touchmove', this.touchMoveHandle, false)
this.touchEndHandle = this.touchEndHandle.bind(this)
elem.addEventListener('touchend', this.touchEndHandle, false)
this.touchCancelHandle = this.touchCancelHandle.bind(this)
elem.addEventListener('touchcancel', this.touchCancelHandle, false)
}
loadCallback(){
let {loadCallback} = this.props
if(loadCallback) loadCallback()
}
touchStartHandle(e){
if(!this.touchTop) return
this.start = true
this.distanceY = 0
this.starY = event.touches[0].pageY
}
touchMoveHandle(e){
if(!this.touchTop || !this.start) return
this.moveY = event.touches[0].pageY
this.distanceY = this.moveY - this.starY
if(this.distanceY<=0) return
else preventDefault(e)
this.distanceY = Math.abs(this.distanceY)
if(this.distanceY >= MAX_DISTANCE) this.distanceY = MAX_DISTANCE
this.transform = Math.min(1, MAX_HEIGHT/this.distanceY) * Math.min(MAX_HEIGHT, this.distanceY)
this.dragElem.style.marginTop = this.transform+'px'
}
touchEndHandle(){
if(!this.touchTop || !this.start) return
this.starY = this.moveY
this.resetDragElem()
if(Math.abs(this.distanceY) <= 80 || this.distanceY<=0) return
this.loadCallback()
this.start = false
}
touchCancelHandle(){
this.resetDragElem()
}
resetDragElem(){
this.dragElem.style.marginTop = '0'
}
componentWillUnmount(){
this.scrollElem.removeEventListener('scroll', this.scrollHandle, false)
this.dragElem.removeEventListener('touchstart', this.touchStartHandle, false)
this.dragElem.removeEventListener('touchmove', this.touchMoveHandle, false)
this.dragElem.removeEventListener('touchend', this.touchEndHandle, false)
}
renderPullDown(){
return (
<div {...this.otherProps}
ref={(pullDown)=>{this.pullDown=pullDown}}
className={classnames(this.getProperty(true), this.props.className)}
>
<div className={setPhPrefix('pulldown-tip')}>
<Icon className='gfs-icon-loading' phIcon='loading-gray' phSize='sm' />
</div>
</div>
)
}
render(){
return this.renderPullDown()
}
}