- 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()
- }
- }
-