<template>
  <div
    class="bx-input"
    :class="{ 'bx-input--textarea': inputAttrs.type === 'textarea','bx-input--center': $slots.button, 'bx-input--link': isLink }"
    >
    <div class="bx-input-inner">
      <div
        class="bx-input-label"
        :class="isLabelMoreThanThreshold && 'bx-input-label--lg'"
        v-if="label"
        >
        <span>{{ label }}</span>
      </div>
      <div class="bx-input-value">
        <div class="bx-input-body">
          <div
            class="bx-input-textarea"
            v-if="inputAttrs.type === 'textarea'"
            >
            <textarea
              rows="1"
              ref="input"
              v-bind="inputAttrs"
              v-on="inputListeners"
              class="bx-input-control"
              :class="[inputAlignClass]"
              />
            <div
              v-if="showTextareaWordLimit"
              class="bx-input-textarea--limit"
              >
              {{ remainingWords }}
            </div>
          </div>
          <input
            v-else
            v-bind="inputAttrs"
            v-on="inputListeners"
            ref="input"
            class="bx-input-control"
            :class="[inputAlignClass]"
            >
          <div
            class="bx-input-icon"
            :class="isShowClearIcon && iconName && 'bx-input-icon--withPadding'"
            v-if="isShowClearIcon"
            @touchstart.prevent="clearInput"
            >
            <bxs-icon
              class="bx-input-icon--clearable"
              name="close-solid"
              />
          </div>
          <div
            class="bx-input-icon"
            :class="isShowClearIcon && 'bx-input-icon--withoutPadding'"
            v-if="iconName"
            @touchstart.prevent="clickIcon"
            >
            <bxs-icon
              :name="iconName"
              :color="iconColor"
              :size="iconSize"
              />
          </div>
          <slot name="button" />
        </div>
        <div
          class="bx-input-extra"
          v-if="hasExtraSlot"
          >
          <slot name="extra" />
        </div>
      </div>
    </div>
    <div
      class="bx-input-error"
      v-if="errorMessage"
      >
      {{ errorMessage }}
    </div>
  </div>
</template>
<script>
import Icon from '../icon/index'
import { create } from '../utils'
import Env from '../../src/utils/env'
import Emitter from '../mixins/emitter'

const [LABEL_THRESHOLD, ICON_LINK_NAME] = [5, 'arrow-right']
export default create({
  name: 'input',
  // 组件的根元素不继承特性，配合$attrs使用
  // 不会影响 style 和 class 的绑定
  inheritAttrs: false,
  mixins: [Emitter],
  props: {
    label: {
      type: String,
      default: '',
    },
    value: {
      type: [String, Number],
      default: '',
    },
    clearable: {
      type: Boolean,
      default: true,
    },
    // 暂时无用
    error: {
      type: Boolean,
      default: false,
    },
    errorMessage: {
      type: String,
      default: '',
    },
    // 是否显示右侧右箭头
    isLink: {
      type: Boolean,
      default: false,
    },
    rightIcon: {
      type: String,
      default: '',
    },
    iconColor: {
      type: String,
      default: '#CCC',
    },
    iconSize: {
      type: String,
      default: '18px',
    },
    inputAlign: {
      type: String,
      default: '',
      validator: value =>
        ['', 'bx-center', 'bx-right', 'center', 'right'].includes(value),
    },
    autosize: {
      type: [Boolean, Object],
      default: false,
    },
    // 是否显示剩余字数统计
    showWordLimit: {
      type: Boolean,
      default: false,
    },
    // 开始进行剩余字数统计阈值
    wordLimitThreshold: {
      type: [Number, String],
      default: 50,
    },
  },
  data() {
    return {
      isFocus: false,
    }
  },
  mounted() {
    this.resizeArea()
  },
  watch: {
    currentValue() {
      this.$nextTick(this.resizeArea)
    },
  },
  computed: {
    currentValue: {
      get() {
        return this.value
      },
      set(value) {
        this.$emit('input', value)
      },
    },
    inputAlignClass() {
      let alignClass = ''
      // bx-center 和 bx-right 是为了兼容旧版本
      if (['bx-center', 'center'].includes(this.inputAlign)) {
        alignClass = 'bx-input-center'
      }
      if (['bx-right', 'right'].includes(this.inputAlign)) {
        alignClass = 'bx-input-right'
      }
      return alignClass
    },
    iconName() {
      return this.isLink ? ICON_LINK_NAME : this.rightIcon
    },
    // label 文案是否超过阈值LABEL_THRESHOLD=5
    isLabelMoreThanThreshold() {
      return this.label.length >= LABEL_THRESHOLD
    },
    // 是否显示剩余字数统计
    showTextareaWordLimit() {
      const maxLength = this.$attrs.maxlength
      return (
        this.showWordLimit &&
        maxLength > 0 &&
        this.remainingWords <= this.wordLimitThreshold
      )
    },
    // 剩余字数
    remainingWords() {
      const maxLength = this.$attrs.maxlength
      let currentLength = this.currentValue.length
      if (currentLength >= maxLength) {
        currentLength = maxLength
      }
      return maxLength - currentLength
    },
    // 属性透传
    inputAttrs() {
      return Object.assign({}, this.$attrs, {
        value: this.currentValue,
      })
    },
    // 事件透传
    inputListeners() {
      const vm = this
      return Object.assign({}, this.$listeners, {
        // 与 属性value 合并成v-model语法
        input(event) {
          let { value } = event.target
          const { type, maxlength } = vm.$attrs
          // 解决number类型maxlength不生效问题
          if (type === 'number' && maxlength) {
            value = value.slice(0, maxlength)
          }
          vm.$emit('input', value.trim())
        },
        focus: this.onFocus,
        blur: this.onBlur,
        change: this.onChange,
      })
    },
    // 是否显示清除图标
    isShowClearIcon() {
      return this.currentValue && this.isFocus && this.clearable
    },
    hasExtraSlot() {
      return this.$slots.extra
    },
  },
  methods: {
    getRootScrollTop() {
      return (
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop ||
        0
      )
    },
    resizeArea() {
      const { input } = this.$refs
      const { autosize, $attrs } = this

      if (!($attrs.type === 'textarea' && autosize)) {
        return
      }

      input.style.height = 'auto'

      let height = input.scrollHeight
      if (!(typeof autosize === 'object')) {
        if (height) {
          input.style.height = `${height}px`
        }
        return
      }

      const { minHeight, maxHeight } = autosize
      if (maxHeight) {
        height = Math.min(height, maxHeight)
      }
      if (minHeight) {
        height = Math.max(height, minHeight)
      }
      input.style.height = `${height}px`
    },
    onFocus(event) {
      this.isFocus = true
      this.$emit('focus', event)
      this.dispatch('bxs-form-item', 'form.item.reset')
      // ios手机，input元素加readonly属性
      // 点击后会有光标出现在框中，且会有不完整输入键盘出现
      const { input } = this.$refs
      if (this.isReadonly(this.$attrs) && input) {
        input.blur()
      }
    },
    onBlur(event) {
      this.isFocus = false
      this.$emit('blur', event)
      if (!this.isReadonly(this.$attrs)) {
        this.dispatch('bxs-form-item', 'form.item.validate')
      }
      if (Env.isIos) {
        window.scrollTo(0, this.getRootScrollTop())
      }
    },
    onChange(event) {
      this.$emit('change', event)
      this.dispatch('bxs-form-item', 'form.item.validate')
    },
    clearInput() {
      this.currentValue = ''
      this.$emit('clear')
    },
    clickIcon() {
      this.$emit('click-icon')
    },
    isReadonly(attrs) {
      if (
        {}.hasOwnProperty.call(attrs, 'readonly') &&
        attrs.readonly !== false &&
        attrs.readonly !== null &&
        attrs.readonly !== undefined
      ) {
        return true
      }
      return false
    },
  },
  components: {
    [Icon.name]: Icon,
  },
})
</script>
<style lang="postcss">
@import './index.css';
</style>
