import idMixin from '../../mixins/id'
import formMixin from '../../mixins/form'
import formSizeMixin from '../../mixins/form-size'
import formStateMixin from '../../mixins/form-state'
import formTextMixin from '../../mixins/form-text'
import formSelectionMixin from '../../mixins/form-selection'
import formValidityMixin from '../../mixins/form-validity'
import { arrayIncludes } from '../../utils/array'
import { eventOn, eventOff } from '../../utils/dom'

// Valid supported input types
const TYPES = [
  'text',
  'password',
  'email',
  'number',
  'url',
  'tel',
  'search',
  'range',
  'color',
  'date',
  'time',
  'datetime',
  'datetime-local',
  'month',
  'week'
]

// @vue/component
export default {
  name: 'BFormInput',
  mixins: [
    idMixin,
    formMixin,
    formSizeMixin,
    formStateMixin,
    formTextMixin,
    formSelectionMixin,
    formValidityMixin
  ],
  props: {
    type: {
      type: String,
      default: 'text',
      validator: type => arrayIncludes(TYPES, type)
    },
    noWheel: {
      // Disable mousewheel to prevent wheel from changing values (i.e. number/date).
      type: Boolean,
      default: false
    },
    min: {
      type: [String, Number],
      default: null
    },
    max: {
      type: [String, Number],
      default: null
    },
    step: {
      type: [String, Number],
      default: null
    }
  },
  computed: {
    localType() {
      // We only allow certain types
      return arrayIncludes(TYPES, this.type) ? this.type : 'text'
    }
  },
  watch: {
    noWheel(newVal) {
      this.setWheelStopper(newVal)
    }
  },
  mounted() {
    this.setWheelStopper(this.noWheel)
  },
  deactivated() {
    // Turn off listeners when keep-alive component deactivated
    /* istanbul ignore next */
    this.setWheelStopper(false)
  },
  activated() {
    // Turn on listeners (if no-wheel) when keep-alive component activated
    /* istanbul ignore next */
    this.setWheelStopper(this.noWheel)
  },
  beforeDestroy() {
    /* istanbul ignore next */
    this.setWheelStopper(false)
  },
  methods: {
    setWheelStopper(on) {
      const input = this.$el
      // We use native events, so that we don't interfere with propgation
      if (on) {
        eventOn(input, 'focus', this.onWheelFocus)
        eventOn(input, 'blur', this.onWheelBlur)
      } else {
        eventOff(input, 'focus', this.onWheelFocus)
        eventOff(input, 'blur', this.onWheelBlur)
        eventOff(document, 'wheel', this.stopWheel)
      }
    },
    onWheelFocus(evt) {
      eventOn(document, 'wheel', this.stopWheel)
    },
    onWheelBlur(evt) {
      eventOff(document, 'wheel', this.stopWheel)
    },
    stopWheel(evt) {
      evt.preventDefault()
      this.$el.blur()
    }
  },
  render(h) {
    var self = this
    return h('input', {
      ref: 'input',
      class: self.computedClass,
      directives: [
        {
          name: 'model',
          rawName: 'v-model',
          value: self.localValue,
          expression: 'localValue'
        }
      ],
      attrs: {
        id: self.safeId(),
        name: self.name,
        form: self.form || null,
        type: self.localType,
        disabled: self.disabled,
        placeholder: self.placeholder,
        required: self.required,
        autocomplete: self.autocomplete || null,
        readonly: self.readonly || self.plaintext,
        min: self.min,
        max: self.max,
        step: self.step,
        'aria-required': self.required ? 'true' : null,
        'aria-invalid': self.computedAriaInvalid
      },
      domProps: {
        value: self.localValue
      },
      on: {
        ...self.$listeners,
        input: self.onInput,
        change: self.onChange,
        blur: self.onBlur
      }
    })
  }
}

@@ -887,24 +887,94 @@
 %0A  props: %7B%0A
+    value: %7B%0A      type: %5BString, Number%5D,%0A      default: null%0A    %7D,%0A
     type: %7B%0A

Allow number as value type (#2583)