<template>
  <div
    class="snt-number-field"
    :class="{
      'snt-number-input--controls': controls,
      'snt-number-input--center': center,
    }">
    <button
      v-if="controls"
      class="snt-number-input__button snt-number-input__button--minus"
      type="button"
      tabindex="-1"
      @click="decrease($event)"
    />
    <input
      ref="input"
      :value="newValue"
      :min="min"
      :max="max"
      type="number"
      class="snt-number-field__input"
      onkeypress="return event.charCode >= 48 && event.charCode <= 57"
      autocomplete="off"
      @change="change($event)"
      @paste="paste($event)" />
    <button
      v-if="controls"
      class="snt-number-input__button snt-number-input__button--plus"
      type="button"
      tabindex="-1"
      @click="increase($event)"
    />
  </div>
</template>

<script>
const REGEXP_NUMBER = /^-?(?:\d+|\d+\.\d+|\.\d+)(?:[eE][-+]?\d+)?$/
const REGEXP_DECIMALS = /\.\d*(?:0|9){10}\d*$/
const normalizeDecimalNumber = (value, times = 100000000000) => (
  REGEXP_DECIMALS.test(String(value)) ? (Math.round(value * times) / times) : value
)
export default {
  name: 'SntNumberField',
  props: {
    value: {
      type: Number,
      default: undefined
    },
    min: {
      type: Number,
      default: -Infinity
    },
    max: {
      type: Number,
      default: Infinity
    },
    step: {
      type: Number,
      default: 1
    },
    controls: Boolean,
    center: Boolean
  },
  data () {
    return {
      newValue: NaN
    }
  },
  computed: {
    increasable () {
      return this.$_.isNaN(this.value) || this.value < this.max
    },
    decreasable () {
      return this.$_.isNaN(this.value) || this.value > this.min
    }
  },
  watch: {
    value: {
      immediate: true,
      handler (newValue, oldValue) {
        if (
          // Avoid triggering change event when created
          !(this.$_.isNaN(newValue) && typeof oldValue === 'undefined') &&
          // Avoid infinite loop
          newValue !== this.newValue
        ) {
          this.setValue(newValue)
        }
      }
    }
  },
  methods: {
    change (e) {
      const value = e.target.value
      this.setValue(value)
    },
    paste (e) {
      const clipboardData = e.clipboardData || window.clipboardData
      if (clipboardData && !REGEXP_NUMBER.test(clipboardData.getData('text'))) {
        e.preventDefault()
      }
    },
    decrease () {
      if (this.decreasable) {
        let { value } = this
        if (this.$_.isNaN(value)) {
          value = 0
        }
        this.setValue(normalizeDecimalNumber(value - this.step))
      }
    },
    increase () {
      if (this.increasable) {
        let { value } = this
        if (this.$_.isNaN(value)) {
          value = 0
        }
        this.setValue(normalizeDecimalNumber(value + this.step))
      }
    },
    setValue (value) {
      const oldValue = this.newValue
      let newValue = Number(value)
      if (!this.$_.isNaN(newValue)) {
        if (this.min <= this.max) {
          newValue = Math.min(this.max, Math.max(this.min, newValue))
        } else {
          newValue = this.min
        }
      }
      this.newValue = newValue
      if (newValue === oldValue) {
        this.$refs.input.value = String(newValue)
      }
      this.$emit('input', newValue)
    }
  }
}
</script>

<style lang="scss" scoped>
  .snt-number-field {
    display: block;
    font-size: 0;
    max-width: 100%;
    overflow: hidden;
    position: relative;
  }
  .snt-number-field__input {
    -moz-appearance: textfield;
    background-color: #fff;
    border: 1px solid #ddd;
    display: block;
    font-size: 1rem;
    line-height: 1.5;
    max-width: 100%;
    min-height: 1.5rem;
    min-width: 3rem;
    padding: 0.4375rem 0.875rem;
    transition: border-color 0.15s;
    width: 100%;
    height: 100%;
    &::-webkit-outer-spin-button,
    &::-webkit-inner-spin-button {
      -webkit-appearance: none;
    }
    &:focus {
      border-color: #0074d9;
      outline: none;
    }
    &:disabled,
    &[readonly] {
      background-color: #f8f8f8;
    }
  }
  .snt-number-input--controls {
    & > input {
      padding-left: 2.275rem;
      padding-right: 2.275rem;
    }
  }
  .snt-number-input--center {
    & > input {
      text-align: center;
    }
  }
  .snt-number-input__button {
    background-color: #fff;
    border: 0;
    border-radius: 0.25rem;
    bottom: 1px;
    position: absolute;
    top: 1px;
    width: 2rem;
    z-index: 1;
    &:focus {
      outline: none;
    }
    &:hover {
      &::before,
      &::after {
        background-color: #0074d9;
      }
    }
    &:disabled {
      opacity: 0.65;
      &::before,
      &::after {
        background-color: #ddd;
      }
    }
    &::before,
    &::after {
      background-color: #111;
      content: "";
      left: 50%;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: background-color 0.15s;
    }
    &::before {
      height: 1px;
      width: 50%;
    }
    &::after {
      height: 50%;
      width: 1px;
    }
    &--minus {
      border-bottom-right-radius: 0;
      border-right: 1px solid #ddd;
      border-top-right-radius: 0;
      left: 1px;
      &::after {
        visibility: hidden;
      }
    }
    &--plus {
      border-bottom-left-radius: 0;
      border-left: 1px solid #ddd;
      border-top-left-radius: 0;
      right: 1px;
    }
  }
</style>
