<template>
  <div class="co-range" :class="{ 'co-range--disabled': disabled }">
    <slot name="start"></slot>
    <div class="co-range-content" ref="content">
      <div class="co-range-runway" :style="{ 'border-top-width': barHeight + 'px' }"></div>
      <div class="co-range-progress" :style="{ width: progress + '%', height: barHeight + 'px' }"></div>
      <div class="co-range-thumb" ref="thumb" :style="{ left: progress + '%' }"></div>
    </div>
    <slot name="end"></slot>
  </div>
</template>

<style>
  @component-namespace co {
    @component range {
      position: relative;
      display: flex;
      height: 1.5rem;
      line-height: 1.5rem;

      & > * {
        display: flex;
        display: -webkit-box;
      }

      & *[slot=start] {
        margin-right: 0.25rem;
      }

      & *[slot=end] {
        margin-left: 0.25rem;
      }

      @descendent content {
        position: relative;
        flex: 1;
        margin-right: 1.5rem;
      }

      @descendent runway {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        left: 0;
        right: -1.5rem;
        border-top-color: #a9acb1;
        border-top-style: solid;
      }

      @descendent thumb {
        background-color: #fff;
        position: absolute;
        left: 0;
        top: 0;
        width: 1.5rem;
        height: 1.5rem;
        border-radius: 100%;
        cursor: move;
        box-shadow: 0 0.05rem 0.15rem rgba(0,0,0,.4);
      }

      @descendent progress {
        position: absolute;
        display: block;
        background-color: #26a2ff;
        top: 50%;
        transform: translateY(-50%);
        width: 0;
      }

      @modifier disabled {
        opacity: 0.5;
      }
    }
  }
</style>

<script type="text/babel">
  import draggable from './draggable';

  export default {
    name: 'co-range',

    props: {
      min: {
        type: Number,
        default: 0
      },
      max: {
        type: Number,
        default: 100
      },
      step: {
        type: Number,
        default: 1
      },
      disabled: {
        type: Boolean,
        default: false
      },
      value: {
        type: Number
      },
      barHeight: {
        type: Number,
        default: 1
      }
    },

    computed: {
      progress() {
        const value = this.value;
        if (typeof value === 'undefined' || value === null) return 0;
        return Math.floor((value - this.min) / (this.max - this.min) * 100);
      }
    },

    mounted() {
      const thumb = this.$refs.thumb;
      const content = this.$refs.content;

      const getThumbPosition = () => {
        const contentBox = content.getBoundingClientRect();
        const thumbBox = thumb.getBoundingClientRect();

        return {
          left: thumbBox.left - contentBox.left,
          top: thumbBox.top - contentBox.top
        };
      };

      let dragState = {};
      draggable(thumb, {
        start: () => {
          if (this.disabled) return;
          const position = getThumbPosition();
          dragState = {
            thumbStartLeft: position.left,
            thumbStartTop: position.top
          };
        },
        drag: (event) => {
          if (this.disabled) return;
          const contentBox = content.getBoundingClientRect();
          const deltaX = event.pageX - contentBox.left - dragState.thumbStartLeft;
          const stepCount = Math.ceil((this.max - this.min) / this.step);
          const newPosition = (dragState.thumbStartLeft + deltaX) - (dragState.thumbStartLeft + deltaX) % (contentBox.width / stepCount);

          let newProgress = newPosition / contentBox.width;

          if (newProgress < 0) {
            newProgress = 0;
          } else if (newProgress > 1) {
            newProgress = 1;
          }

          this.$emit('input', Math.round(this.min + newProgress * (this.max - this.min)));
        },
        end: () => {
          if (this.disabled) return;
          this.$emit('change', this.value);
          dragState = {};
        }
      });
    }
  };
</script>
