<template>
    <div ref="outerWrapper" :class="wrapperClasses">
        <div v-if="isHorizontal" :class="`${prefix}-horizontal`">
            <div :style="{right: `${anotherOffset}%`}" class="left-pane" :class="paneClasses">
                <slot name="left"/>
            </div>
            <div :class="`${prefix}-trigger-con`" :style="{left: `${offset}%`}" @mousedown="handleMousedown">
                <slot name="trigger">
                    <div class="cbim-split-trigger cbim-split-trigger-vertical"></div>
                </slot>
            </div>
            <div :style="{left: `${offset}%`}" class="right-pane" :class="paneClasses">
                <slot name="right"/>
            </div>
        </div>
        <div v-else :class="`${prefix}-vertical`">
            <div :style="{bottom: `${anotherOffset}%`}" class="top-pane" :class="paneClasses">
                <slot name="top"/>
            </div>
            <div :class="`${prefix}-trigger-con`" :style="{top: `${offset}%`}" @mousedown="handleMousedown">
                <slot name="trigger">
                    <div class="cbim-split-trigger cbim-split-trigger-horizontal"></div>
                </slot>
            </div>
            <div :style="{top: `${offset}%`}" class="bottom-pane" :class="paneClasses">
                <slot name="bottom"/>
            </div>
        </div>
    </div>
</template>

<script>
// 借鉴iview Split组件
import { on, off } from 'element-ui/src/utils/dom';

export default {
  name: 'CbimSplit',
  props: {
    value: {
      type: [Number, String],
      default: 0.5
    },
    mode: {
      validator(value) {
        return value === 'horizontal' || value === 'vertical'
      },
      default: 'horizontal'
    },
    min: {
      type: [Number, String],
      default: '0px'
    },
    max: {
      type: [Number, String],
      default: '8px'
    }
  },
  data() {
    return {
      prefix: 'cbim-split',
      offset: 0,
      oldOffset: 0,
      isDragging: false,
      computedMin: 0,
      computedMax: 0,
      currentValue: 0.5
    };
  },
  computed: {
    wrapperClasses() {
      return [
        `${this.prefix}`,
        this.isDragging ? 'no-select' : ''
      ];
    },
    paneClasses() {
      return [
        `${this.prefix}-pane`,
        {
          [`${this.prefix}-pane-dragging`]: this.isDragging
        }
      ];
    },
    isHorizontal() {
      return this.mode === 'horizontal';
    },
    anotherOffset() {
      return 100 - this.offset;
    },
    valueIsPx() {
      return typeof this.value === 'string';
    },
    offsetSize() {
      return this.isHorizontal ? 'offsetWidth' : 'offsetHeight';
    }
  },
  watch: {
    value(val) {
      if (val !== this.currentValue) {
        this.currentValue = val;
        this.computeOffset();
      }
    }
  },
  methods: {
    px2percent(numerator, denominator) {
      return parseFloat(numerator) / parseFloat(denominator);
    },
    getComputedThresholdValue(type) {
      const size = this.$refs.outerWrapper[this.offsetSize];
      if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type];
      return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type];
    },
    getMin(value1, value2) {
      if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`;
      return Math.min(value1, value2);
    },
    getMax(value1, value2) {
      if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;
      return Math.max(value1, value2);
    },
    getAnotherOffset(value) {
      let res = 0;
      if (this.valueIsPx) res = `${this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)}px`;
      else res = 1 - value;
      return res;
    },
    handleMove(e) {
      const pageOffset = this.isHorizontal ? e.pageX : e.pageY;
      const offset = pageOffset - this.initOffset;
      const outerWidth = this.$refs.outerWrapper[this.offsetSize];
      let value = this.valueIsPx ? `${parseFloat(this.oldOffset) + offset}px` : (this.px2percent(outerWidth * this.oldOffset + offset, outerWidth));
      const anotherValue = this.getAnotherOffset(value);
      if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin);
      if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax));
      e.atMin = this.value === this.computedMin;
      e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : this.getAnotherOffset(this.value).toFixed(5) === this.computedMax.toFixed(5);
      this.$emit('input', value);
      this.$emit('split-dragging', e);
    },
    handleUp() {
      this.isDragging = false;
      off(document, 'mousemove', this.handleMove);
      off(document, 'mouseup', this.handleUp);
      this.$emit('split-drag-end');
    },
    handleMousedown(e) {
      this.initOffset = this.isHorizontal ? e.pageX : e.pageY;
      this.oldOffset = this.value;
      this.isDragging = true;
      on(document, 'mousemove', this.handleMove);
      on(document, 'mouseup', this.handleUp);
      this.$emit('split-drag-start');
    },
    computeOffset() {
      this.$nextTick(() => {
        this.computedMin = this.getComputedThresholdValue('min');
        this.computedMax = this.getComputedThresholdValue('max');
        let value = this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value;
        const anotherValue = this.getAnotherOffset(value);
        if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin);
        if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax));
        this.offset = value * 10000 / 100;
        this.currentValue = value;
        this.$emit('input', value);
      });
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.computeOffset();
    });
    on(window, 'resize', this.computeOffset);
  },
  beforeDestroy() {
    off(window, 'resize', this.computeOffset);
  }
}
</script>
