<template>
  <div class="co-drawer-wrap" ref="wrap">
      <div class="co-drawer-container" ref="container">
          <div class="co-drawer" ref="menu">
              <slot name="menu"></slot>
          </div>
          <div class="co-drawer-content"
            @touchstart="startDrag"
            @touchmove="onDrag"
            @touchend="endDrag"
            ref="content">
              <slot name="content"></slot>
              <div ref="vmodal" v-show="modal" class="co-swipe-modal"></div>
          </div>
      </div>
  </div>
</template>

<style lang="css">
  @component-namespace co {
    @component drawer {
      overflow: hidden;
      height: 100%;

      @descendent wrap {
        overflow-x: hidden;
        width: 100%;
      }

      @descendent container {
        display: flex;
        flex-wrap: nowrap;
      }

      @descendent content {
        position: relative;
      }
    }

    @component swipe-modal {
      position:absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
      background: #000;
      display: none;
      transform: 150ms ease-in-out;
    }

  }


</style>

<script>
/**
 * co-drawer
 * 抽屉 - 支持滑动和点击触发
 * slot name="menu" 定制菜单的内容
 * slot name="content" 定制主要区域的内容
 *
 * @param {boolean} [opened] - 菜单是否开启的状态 true 表示开启  false 表示关闭
 * @param {string} [direction] - 菜单滑出的位置 'left'表示左侧  'right'表示右侧
 * @param {Number} [distance] - 菜单弹射的距离 0~1之间的小数 页面宽度的百分比
 * @param {function} [togglemenu] - 外部按钮控制菜单状态的方法
 *
 * @example
 * <co-drawer :opened="isopen" direction="right" @togglemenu="togglemenu">
 *     <div slot="menu">
 *          <span @click="togglemenu(false)">关闭</span>
 *     </div>
 *     <div slot="content">
 *          <span @click="togglemenu(true)">打开</span>
 *     </div>
 * </co-drawer>
 */

export default {
  name: 'co-drawer',

  props: {
    opened: {
      type: Boolean,
      default: false
    },
    direction: {
      type: String,
      default: 'left'
    },
    distance: {
      type: Number,
      default: 0.4
    },
    modal: {
      type: Boolean,
      default: false
    }
  },

  data() {
    return {
      start: { x: 0, y: 0 }
    };
  },

  watch: {
    opened(val) {
      if (val === false) {
        if (this.direction === 'left') {
          this.container.style.webkitTransform = this.translate3d(-this.pageWidth * this.distance);
        } else if (this.direction === 'right') {
          this.container.style.webkitTransform = this.translate3d(0);
        }
        this.swipeLeave = false;
        this.swiping = false;
      } else {
        if (this.direction === 'left') {
          this.container.style.webkitTransform = this.translate3d(0);
        } else if (this.direction === 'right') {
          this.container.style.webkitTransform = this.translate3d(-this.pageWidth * this.distance);
        }
        this.swipeLeave = false;
        this.swiping = false;
      }
    },

    direction(val) {
      this.container.style.webkitTransition = '';
    },

    distance(val) {
      this.container.style.webkitTransition = '';
    }
  },

  mounted() {
    this.wrap = this.$refs.wrap;
    this.container = this.$refs.container;
    this.menu = this.$refs.menu;
    this.content = this.$refs.content;
    this.vmodal = this.$refs.vmodal;

    this.pageWidth = this.wrap.clientWidth;
    this.content.style.width = this.pageWidth + 'px';

    this.init();
  },

  methods: {
    translate3d(offset) {
      if (this.modal === true) {
        if (Math.abs(offset) >= this.pageWidth * this.distance) {
          offset = offset > 0 ? this.pageWidth * this.distance : -this.pageWidth * this.distance;
        }
        if (this.direction === 'left') {
          if (offset !== -this.pageWidth * this.distance) {
            this.vmodal.style.display = 'block';
          } else {
            this.vmodal.style.display = 'none';
          }
          this.vmodal.style.opacity = Math.abs((this.pageWidth * this.distance + offset) * 0.5 / (this.pageWidth * this.distance));
        }
        if (this.direction === 'right') {
          if (offset !== 0) {
            this.vmodal.style.display = 'block';
          } else {
            this.vmodal.style.display = 'none';
          }
          this.vmodal.style.opacity = Math.abs(offset * 0.5 / (this.pageWidth * this.distance));
        }
      }
      return `translate3d(${offset}px, 0, 0)`;
    },

    swipeMove(offset = 0) {
      this.container.style.webkitTransform = this.translate3d(offset);
      this.swiping = true;
    },

    swipeLeaveTransition(dir) {
      setTimeout(() => {
        this.swipeLeave = true;
        this.dragging = false;

        if (this.direction === 'left') {
          if (dir > 0) {
            if (this.offsetLeft > this.pageWidth * this.distance * 0.5) {
              this.swipeMove(0);
              this.$emit('togglemenu', true);
            } else {
              this.swipeMove(-this.pageWidth * this.distance);
              this.$emit('togglemenu', false);
            }
            return;
          } else if (dir < 0) {
            if (-this.offsetLeft > this.pageWidth * this.distance * 0.5) {
              this.swipeMove(-this.pageWidth * this.distance);
              this.$emit('togglemenu', false);
            } else {
              this.swipeMove(0);
              this.$emit('togglemenu', true);
            }
            return;
          }
        } else if (this.direction === 'right') {
          if (dir > 0) {
            if (this.offsetLeft > this.pageWidth * this.distance * 0.5) {
              this.swipeMove(0);
              this.$emit('togglemenu', false);
            } else {
              this.swipeMove(-this.pageWidth * this.distance);
              this.$emit('togglemenu', true);
            }
            return;
          } else if (dir < 0) {
            if (-this.offsetLeft > this.pageWidth * this.distance * 0.5) {
              this.swipeMove(-this.pageWidth * this.distance);
              this.$emit('togglemenu', true);
            } else {
              this.swipeMove(0);
              this.$emit('togglemenu', false);
            }
            return;
          }
        }
      }, 0);
    },

    startDrag(evt) {
      evt = evt.changedTouches ? evt.changedTouches[0] : evt;
      this.start.x = evt.pageX;
      this.start.y = evt.pageY;

      if (this.direction === 'left') {
        if (!this.opened && (this.start.x > this.pageWidth * this.distance)) {
          return;
        }
        if (this.opened && (this.start.x < this.pageWidth * this.distance)) {
          return;
        }
      } else if (this.direction === 'right') {
        if (!this.opened && (this.start.x < this.pageWidth * this.distance)) {
          return;
        }
        if (this.opened && (this.start.x > this.pageWidth * this.distance)) {
          return;
        }
      }

      this.dragging = true;
    },

    onDrag(evt) {
      if (!this.dragging) return;
      let swiping;
      const e = evt.changedTouches ? evt.changedTouches[0] : evt;
      const offsetTop = e.pageY - this.start.y;
      const offsetLeft = this.offsetLeft = e.pageX - this.start.x;

      if (offsetLeft <= -this.pageWidth * this.distance || offsetLeft >= this.pageWidth * this.distance) {
        return;
      }

      const y = Math.abs(offsetTop);
      const x = Math.abs(offsetLeft);

      swiping = !(x < 5 || (x >= 5 && y >= x * 1.73));

      if (!swiping) return;
      evt.preventDefault();
      if (this.direction === 'left') {
        if (offsetLeft > 0 && !this.opened) {
          this.swipeMove(offsetLeft - this.pageWidth * this.distance);
        }
        if (offsetLeft < 0 && this.opened) {
          this.swipeMove(offsetLeft);
        }
      } else if (this.direction === 'right') {
        if (offsetLeft < 0 && !this.opened) {
          this.swipeMove(offsetLeft);
        }
        if (offsetLeft > 0 && this.opened) {
          this.swipeMove(offsetLeft - this.pageWidth * this.distance);
        }
      }
    },

    endDrag() {
      if (!this.swiping) return;
      this.swipeLeaveTransition(this.offsetLeft > 0 ? 1 : -1);
    },

    init() {
      this.menu.style.width = this.pageWidth * this.distance + 'px';
      this.container.style.width = this.pageWidth * (this.distance + 1) + 'px';

      if (this.direction !== 'left' && this.direction !== 'right') {
        throw new Error("props direction must be 'left' or 'right'");
      }

      if (!this.opened) {
        if (this.direction === 'right') {
          this.container.style.flexDirection = 'row-reverse';
          this.container.style.webkitTransform = this.translate3d(0);
        } else {
          this.container.style.flexDirection = '';
          this.container.style.webkitTransform = this.translate3d(-this.pageWidth * this.distance);
        }
        setTimeout(_=>{
          this.container.style.webkitTransition = '-webkit-transform 150ms ease-in-out';
        }, 0);
      }
    }
  },

  updated() {
    this.init();
  }

};

</script>
