<template>
  <div
    @touchstart="onPanStart"
    @mousedown="onPanStart"
    @touchmove="onPanMove"
    @mousemove="onPanMove"
    @touchend="onPanEnd"
    @mouseup="onPanEnd"
    @touchcancel="onPanEnd"
    :class="prefixCls"
  >
    <div :class="[prefixCls+'-button-grounp',prefixCls+'-button-left']" v-show="distX >= 0">
      <slot name="left-button"></slot>
    </div>
    <div :class="[prefixCls+'-button-grounp',prefixCls+'-button-right']" v-show="distX <= 0">
      <slot name="right-button"></slot>
    </div>
    <div
      :class="prefixCls+'-content'"
      :style="styles"
      ref="content"
      @mousedown="onContentClick"
      @touchstart="onContentClick"
    >
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  name: 'swipeout',
  props: {
    disabled: Boolean,
    prefixCls: {
      type: String,
      default: 'vui-swipeout'
    },
    threshold: {
      type: Number,
      default: 0.3
    }
  },
  data () {
    return {
      pageX: undefined, // 记录start开始时到x位置
      pageY: undefined, // 记录start开始时到y位置
      distX: 0, // 拖动到x距离
      distY: 0, // 拖动到x距离
      styles: {
        transform: 'translate3d(0px, 0, 0)'
      },
      opened: false, // swipe打开状态
      initDirection: '', // 初始化swipe方向
      leftWidth: 0,
      rightWidth: 0,
      needShowLeft: false,
      needShowRight: false,
      isScrolling: undefined // 判断滑动的轴（x || y）
    }
  },
  watch: {
    disabled (newVal, oldVal) {
      if (newVal === true && !oldVal) {
        this.close()
      }
    }
  },
  computed: {
    buttonWidth () {
      if (this.leftWidth <= 0 && this.rightWidth > 0) {
        return this.rightWidth
      }
      if (this.leftWidth > 0 && this.rightWidth <= 0) {
        return this.leftWidth
      }
      if (this.leftWidth > 0 && this.rightWidth > 0) {
        return this.distX < 0 ? this.rightWidth : this.leftWidth
      }
    }
  },
  mounted () {
    this.$nextTick(() => {
      if (this.$slots['left-button']) {
        this.needShowLeft = true
        this.caculateButtonWidth('left')
      }
      if (this.$slots['right-button']) {
        this.needShowRight = true
        this.caculateButtonWidth('right')
      }
    })
  },
  methods: {
    // 计算两侧按钮的总宽度
    caculateButtonWidth (direction) {
      const list = this.$slots[`${direction}-button`][0].children.filter(
        node => node.tag
      )
      let width = 0
      list.forEach(node => (width += node.elm.clientWidth))
      this[`${direction}Width`] = width
    },
    // 点击内容区域关闭
    onContentClick () {
      if (this.styles.transform.indexOf('(0px, 0, 0)') === -1) this.close()
    },
    // 点击按钮
    onButtonClick () {
      this.close()
    },
    onPanStart (e) {
      if (this.disabled || this.opened) {
        return
      }
      // 关闭已经打开的swipe
      const openedSwipe = this.$parent.$children.filter(
        item =>
          item.$options._componentTag === 'swipeout' &&
          item.$data.styles.transform.indexOf('(0px, 0, 0)') === -1
      )
      if (openedSwipe.length > 0) {
        openedSwipe.forEach(item => {
          item.close()
          item._setStyle(true)
        })
        e.preventDefault()
        return
      }

      const touch = e.touches ? e.touches[0] : e
      this.pageX = touch.pageX
      this.pageY = touch.pageY
    },
    onPanMove (e) {
      if (this.disabled) {
        return
      }

      if (this.pageX === undefined) {
        e.preventDefault()
        return
      }

      const touch = e.touches ? e.touches[0] : e
      this.distX = touch.pageX - this.pageX
      this.distY = touch.pageY - this.pageY

      // 如果移动的x大于y说明是横向的，否则return
      if (typeof this.isScrolling === 'undefined') {
        this.isScrolling = !!(
          this.isScrolling || Math.abs(this.distY) > Math.abs(this.distX)
        )
      }
      if (this.isScrolling) {
        return
      }

      // 设置初始方向值
      if (!this.initDirection) {
        this.initDirection = this.distX > 0 ? 'toRight' : 'toLeft'
      }

      if (
        (this.distX > 0 && this.needShowLeft) ||
        (this.distX < 0 && this.needShowRight)
      ) {
        this._setStyle()
        if (Math.abs(this.distX) <= this.buttonWidth) {
          this._setOffset(this.distX)
        } else {
          const extra = (Math.abs(this.distX) - this.buttonWidth) * 0.5
          const offset = (this.buttonWidth + extra) * (this.distX < 0 ? -1 : 1)
          this._setOffset(offset)
        }
      }
      e.preventDefault()
    },
    onPanEnd () {
      if (this.disabled || this.isScrolling) {
        return
      }
      if (this.initDirection === 'toRight' && this.distX > 0) {
        if (this.distX > this.threshold * this.leftWidth) {
          this._setOffset(this.leftWidth)
          this.$emit('on-open')
          this.opened = true
        } else {
          this.close()
        }
      } else if (this.initDirection === 'toLeft' && this.distX < 0) {
        if (Math.abs(this.distX) > this.threshold * this.rightWidth) {
          this._setOffset(-this.rightWidth)
          this.$emit('on-open')
          this.opened = true
        } else {
          this.close()
        }
      }
      this._setStyle(true)
      this.pageX = this.pageY = undefined
      this.initDirection = ''
      this.isScrolling = undefined
    },
    open (position) {
      this.distX = position === 'right' ? -this.rightWidth : this.leftWidth
      this._setOffset(this.distX)
      this.opened = true
      this._setStyle(true)
    },
    close () {
      this._setOffset(0)
      this.$emit('on-close')
      setTimeout(() => {
        this.opened = false
      }, 100)
      this.distX = 0
    },
    _setStyle (animated) {
      if (animated) {
        this.$refs.content.classList.add('vui-swipeout-content-animated')
      } else {
        this.$refs.content.classList.remove('vui-swipeout-content-animated')
      }
    },
    _setOffset (x) {
      // 临界以后
      if (
        (this.initDirection === 'toRight' && x < 0) ||
        (this.initDirection === 'toLeft' && x > 0)
      ) {
        x = 0
      }
      this.styles.transform = 'translate3d(' + x + 'px, 0, 0)'
    }
  }
}
</script>
<style>
.vui-swipeout {
  width: 100%;
  overflow: hidden;
  position: relative;
}
.vui-swipeout-button-grounp {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: #eee;
}
.vui-swipeout-button-grounp > div {
  display: -webkit-flex;
  display: flex;
  -webkit-align-items: stretch;
  align-items: stretch;
  height: 100%;
}
.vui-swipeout-button-left > div {
  justify-content: flex-start;
  -webkit-justify-content: flex-start;
}
.vui-swipeout-button-right > div {
  justify-content: flex-end;
  -webkit-justify-content: flex-end;
}
.vui-swipeout-content {
  position: relative;
  background: #fff;
}
.vui-swipeout-content-animated {
  -webkit-transition: -webkit-transform 0.2s;
  transition: -webkit-transform 0.2s;
  transition: transform 0.2s;
  transition: transform 0.2s, -webkit-transform 0.2s;
}
</style>
