<commit_before>import React, { PureComponent, CSSProperties } from 'react';
import classnames from 'classnames';
import { REFRESH_STATE, LOAD_STATE, PullAction, PropsType } from './PropsType';
import Events from '../utils/events';
import Throttle from '../utils/throttle';
import Drag from '../drag';
import ActivityIndicator from '../activity-indicator';
import Icon from '../icon';

export interface PullProps extends PropsType {
  prefixCls?: string;
  className?: string;
}

export default class Pull extends PureComponent<PullProps, any> {
  private pull;

  private wrap;

  private throttledScroll;

  private wrapTouchstartY;

  static defaultProps: PullProps = {
    prefixCls: 'za-pull',
    refresh: {
      state: REFRESH_STATE.normal,
      startDistance: 30,
      distance: 50,
    },
    load: {
      state: LOAD_STATE.normal,
      distance: 0,
    },
    animationDuration: 400,
    stayTime: 1000,
  };

  constructor(props) {
    super(props);
    this.state = {
      offsetY: 0,
      animationDuration: 0,
      refreshState: props.refresh.state,
      loadState: props.load.state,
    };
    this.throttledScroll = Throttle(this.onScroll, 250);
  }

  componentDidMount() {
    this.addScrollEvent();
    Events.on(this.wrap, 'touchstart', this.wrapTouchstart);
    Events.on(this.wrap, 'touchmove', this.wrapTouchmove);
    Events.on(this.wrap, 'touchend', this.wrapTouchEnd);
  }

  static getDerivedStateFromProps(nextProps, state) {
    const { load, refresh } = nextProps;
    const { prevLoad = {}, prevRefresh = {} } = state;
    if ('load' in nextProps && load.state !== prevLoad.state) {
      return {
        loadState: load.state,
        prevLoad: load,
      };
    }

    if ('refresh' in nextProps && refresh.state !== prevRefresh.state) {
      return {
        refreshState: refresh.state,
        prevRefresh: refresh,
      };
    }
    return null;
  }

  componentDidUpdate(prevProps) {
    if (this.wrap !== this.getScrollContainer()) {
      this.addScrollEvent();
    }

    const { load, refresh } = this.props;
    if (prevProps.load!.state !== load!.state) {
      this.doLoadAction(load!.state);
    }
    if (prevProps.refresh!.state !== refresh!.state) {
      this.doRefreshAction(refresh!.state);
    }
  }

  componentWillUnmount() {
    const scroller = (this.wrap === document.documentElement) ? window : this.wrap;
    Events.off(scroller, 'scroll', this.throttledScroll);
    Events.off(this.wrap, 'touchstart', this.wrapTouchstart);
    Events.off(this.wrap, 'touchmove', this.wrapTouchmove);
    Events.off(this.wrap, 'touchend', this.wrapTouchEnd);
  }

  wrapTouchstart = (event) => {
    const touch = event.touches[0];
    this.wrapTouchstartY = touch.pageY;
  };

  wrapTouchmove = (event) => {
    const touch = event.touches[0];
    const currentY = touch.pageY;
    if (currentY - this.wrapTouchstartY > 0 && event.cancelable && this.wrap.scrollTop === 0) {
      event.preventDefault();
    }
  };

  wrapTouchEnd = () => {
    this.wrapTouchstartY = 0;
  };

  getScrollContainer = () => {
    return ((node) => {
      while (node && node.parentNode && node.parentNode !== document.body) {
        const style = window.getComputedStyle(node);
        if (
          // overflow 或者 overflowY 值为 scroll/auto
          (['scroll', 'auto'].indexOf(style.overflowY!) > -1 || ['scroll', 'auto'].indexOf(style.overflow!) > -1)
          // height 或者 max-height 值大于 0
          && (parseInt(style.height!, 10) > 0 || parseInt(style.maxHeight!, 10) > 0)
        ) {
          return node;
        }
        node = node.parentNode;
      }
    })(this.pull) || document.documentElement;
  };

  addScrollEvent = () => {
    this.wrap = this.getScrollContainer();
    const scroller = (this.wrap === document.documentElement) ? window : this.wrap;
    Events.on(scroller, 'scroll', this.throttledScroll);
  };

  onScroll = () => {
    const { refreshState, loadState } = this.state;
    const { scrollHeight, scrollTop, clientHeight } = this.wrap;
    const load: PullAction = { ...Pull.defaultProps.load, ...this.props.load };
    const { handler, distance } = load;

    if (
      typeof handler !== 'function'
      || refreshState !== REFRESH_STATE.normal
      || loadState !== LOAD_STATE.normal
      || scrollHeight <= clientHeight

      // 内容高度 - 偏移值 - 修正距离 <= 容器可见高度
      || scrollHeight - (scrollTop + document.body.scrollTop) - distance! > clientHeight
    ) {
      return;
    }
    handler();
  };

  onDragMove = (event, { offsetY }) => {
    const { handler } = this.props.refresh!;
    if (
      // 未设置刷新事件
      !handler

      // 上拉
      || offsetY <= 0

      // 未滚动到顶部
      || (offsetY > 0 && (this.wrap.scrollTop + document.body.scrollTop) > 0)

      // 已经触发过加载状态
      || this.state.refreshState >= REFRESH_STATE.loading
    ) {
      return false;
    }

    // 解决低端安卓系统只触发一次touchmove事件的bug
    event.preventDefault();

    const refresh: PullAction = { ...Pull.defaultProps.refresh, ...this.props.refresh };
    const { startDistance, distance } = refresh;
    const offset = offsetY / 2; // 移动距离为拖动距离的一半

    // 判断是否达到释放立即刷新的条件
    const action = ((offset - startDistance!) < distance!)
      ? REFRESH_STATE.pull
      : REFRESH_STATE.drop;

    this.doRefreshAction(action, offset);
    return true;
  };

  onDragEnd = (_event, { offsetY }) => {
    // 没有产生位移
    if (!offsetY) {
      return;
    }

    // 当前状态为下拉状态时
    const { refreshState } = this.state;<commit_msg>fix: fix #471<commit_after>import React, { PureComponent, CSSProperties } from 'react';
import classnames from 'classnames';
import { REFRESH_STATE, LOAD_STATE, PullAction, PropsType } from './PropsType';
import Events from '../utils/events';
import Throttle from '../utils/throttle';
import Drag from '../drag';
import ActivityIndicator from '../activity-indicator';
import Icon from '../icon';

export interface PullProps extends PropsType {
  prefixCls?: string;
  className?: string;
}

export default class Pull extends PureComponent<PullProps, any> {
  private pull;

  private wrap;

  private throttledScroll;

  private wrapTouchstartY;

  static defaultProps: PullProps = {
    prefixCls: 'za-pull',
    refresh: {
      state: REFRESH_STATE.normal,
      startDistance: 30,
      distance: 50,
    },
    load: {
      state: LOAD_STATE.normal,
      distance: 0,
    },
    animationDuration: 400,
    stayTime: 1000,
  };

  constructor(props) {
    super(props);
    this.state = {
      offsetY: 0,
      animationDuration: 0,
      refreshState: props.refresh.state,
      loadState: props.load.state,
    };
    this.throttledScroll = Throttle(this.onScroll, 250);
  }

  componentDidMount() {
    this.addScrollEvent();
    Events.on(this.wrap, 'touchstart', this.wrapTouchstart);
    Events.on(this.wrap, 'touchmove', this.wrapTouchmove);
    Events.on(this.wrap, 'touchend', this.wrapTouchEnd);
  }

  static getDerivedStateFromProps(nextProps, state) {
    const { load, refresh } = nextProps;
    const { prevLoad = {}, prevRefresh = {} } = state;
    if ('load' in nextProps && load.state !== prevLoad.state) {
      return {
        loadState: load.state,
        prevLoad: load,
      };
    }

    if ('refresh' in nextProps && refresh.state !== prevRefresh.state) {
      return {
        refreshState: refresh.state,
        prevRefresh: refresh,
      };
    }
    return null;
  }

  componentDidUpdate(prevProps) {
    if (this.wrap !== this.getScrollContainer()) {
      this.addScrollEvent();
    }

    const { load, refresh } = this.props;
    if (prevProps.load!.state !== load!.state) {
      this.doLoadAction(load!.state);
    }
    if (prevProps.refresh!.state !== refresh!.state) {
      this.doRefreshAction(refresh!.state);
    }
  }

  componentWillUnmount() {
    const scroller = (this.wrap === document.documentElement) ? window : this.wrap;
    Events.off(scroller, 'scroll', this.throttledScroll);
    Events.off(this.wrap, 'touchstart', this.wrapTouchstart);
    Events.off(this.wrap, 'touchmove', this.wrapTouchmove);
    Events.off(this.wrap, 'touchend', this.wrapTouchEnd);
  }

  wrapTouchstart = (event) => {
    const touch = event.touches[0];
    this.wrapTouchstartY = touch.pageY;
  };

  wrapTouchmove = (event) => {
    const touch = event.touches[0];
    const currentY = touch.pageY;
    if (currentY - this.wrapTouchstartY > 0 && event.cancelable && this.getWrapScrollTop() === 0) {
      event.preventDefault();
    }
  };

  wrapTouchEnd = () => {
    this.wrapTouchstartY = 0;
  };

  getWrapScrollTop = () => {
    const el = (this.wrap === document.documentElement) ? (document.scrollingElement || document.documentElement) : this.wrap;
    return el.scrollTop;
  };

  getScrollContainer = () => {
    return ((node) => {
      while (node && node.parentNode && node.parentNode !== document.body) {
        const style = window.getComputedStyle(node);
        if (
          // overflow 或者 overflowY 值为 scroll/auto
          (['scroll', 'auto'].indexOf(style.overflowY!) > -1 || ['scroll', 'auto'].indexOf(style.overflow!) > -1)
          // height 或者 max-height 值大于 0
          && (parseInt(style.height!, 10) > 0 || parseInt(style.maxHeight!, 10) > 0)
        ) {
          return node;
        }
        node = node.parentNode;
      }
    })(this.pull) || document.documentElement;
    // 由于在ios12版本中，document.scrollingElement.clientHeight计算结果与document.documentElement.clientHeight不一致，故无法替换。
  };

  addScrollEvent = () => {
    this.wrap = this.getScrollContainer();
    const scroller = (this.wrap === document.documentElement) ? window : this.wrap;
    Events.on(scroller, 'scroll', this.throttledScroll);
  };

  onScroll = () => {
    const { refreshState, loadState } = this.state;
    const { scrollHeight, clientHeight } = this.wrap;
    const load: PullAction = { ...Pull.defaultProps.load, ...this.props.load };
    const { handler, distance } = load;

    if (
      typeof handler !== 'function'
      || refreshState !== REFRESH_STATE.normal
      || loadState !== LOAD_STATE.normal
      || scrollHeight <= clientHeight

      // 内容高度 - 偏移值 - 修正距离 <= 容器可见高度
      || scrollHeight - this.getWrapScrollTop() - distance! > clientHeight
    ) {
      return;
    }
    handler();
  };

  onDragMove = (event, { offsetY }) => {
    const { handler } = this.props.refresh!;
    if (
      // 未设置刷新事件
      !handler

      // 上拉
      || offsetY <= 0

      // 未滚动到顶部
      || (offsetY > 0 && this.getWrapScrollTop() > 0)

      // 已经触发过加载状态
      || this.state.refreshState >= REFRESH_STATE.loading
    ) {
      return false;
    }

    // 解决低端安卓系统只触发一次touchmove事件的bug
    event.preventDefault();

    const refresh: PullAction = { ...Pull.defaultProps.refresh, ...this.props.refresh };
    const { startDistance, distance } = refresh;
    const offset = offsetY / 2; // 移动距离为拖动距离的一半

    // 判断是否达到释放立即刷新的条件
    const action = ((offset - startDistance!) < distance!)
      ? REFRESH_STATE.pull
      : REFRESH_STATE.drop;

    this.doRefreshAction(action, offset);
    return true;
  };

  onDragEnd = (_event, { offsetY }) => {
    // 没有产生位移
    if (!offsetY) {
      return;
    }

    // 当前状态为下拉状态时
    const { refreshState } = this.state;
{'fraction_non_alphanumeric': 0.14315056260046438, 'fraction_numerical': 0.00669762457581711, 'mean_word_length': 2.6984808454425364, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '84208', 'n_tokens_mistral': 3768, 'n_tokens_neox': 3700, 'n_words': 725}