<template>
  <div class="v-select" v-clickoutside="clickOutSide">
    <v-input
      v-if="!multiple"
      v-model="value"
      :readonly="!filterable"
      :clearable="clearable"
      :disabled="disabled"
      :placeholder="placeholder"
      :arrowShow="arrowShow"
      :loading="loading"
      :remote="remote"
      componentType="select"
      @selectInput="handleInputChange"
      @clear="handleClear"
    ></v-input>
    <div
      v-if="multiple"
      :class="['v-select-input', { disabled }]"
      ref="cSelectInput"
      @mouseenter="handleMouseEnter"
      @mouseleave="handleMouseLeave"
    >
      <div class="v_multiple_box">
        <p class="placeholder" v-if="!valueArr.length && !filterable">
          {{ placeholder }}
        </p>
        <ul class="v-select-multiple">
          <template v-if="!collapseTags">
            <li v-for="(item, index) of valueArr" :key="index">
              <span>{{ item.label }}</span>
              <i
                :class="['iconfont icon-guanbi', 'v-clear']"
                @click="handleClearMultiple(item)"
              ></i>
            </li>
          </template>
          <li v-if="collapseTags && valueArr.length">
            <span>{{ valueArr[0].label }}</span>
            <i
              :class="['iconfont icon-guanbi', 'v-clear']"
              @click="handleClearMultiple(valueArr[0])"
            ></i>
          </li>
          <li v-if="collapseTags && valueArr.length > 1">
            <span>+ {{ valueArr.length - 1 }}</span>
          </li>
        </ul>
        <v-input
          v-if="filterable"
          v-model="searchValue"
          :disabled="disabled"
          :placeholder="placeholder"
          :arrowShow="arrowShow"
          :componentType="!multiple && 'select'"
          @selectInput="handleSearchInputChange"
          @clear="handleClear"
          :class="[{ v_filterable_input: filterable && multiple }]"
        ></v-input>
      </div>
      <i
        v-show="!isClear && !loading"
        :class="['iconfont icon-xiajiantou']"
        ref="cSelectArrow"
      ></i>
      <i
        v-if="loading && !isClear"
        :class="['loading', 'iconfont icon-loading']"
      ></i>
      <i
        v-if="isClear"
        @click="handleClearMultiple()"
        :class="['iconfont icon-guanbi', 'v-clear']"
      ></i>
    </div>
    <div class="v-select-box" ref="cSelectBox">
      <ul class="v-select-ul">
        <slot>
          <v-select-item
            v-for="(item, index) of coptions"
            :key="index"
            :option="item"
          />
          <p class="v-select-dropdown__empty" v-if="!coptions.length">
            暂无数据
          </p>
        </slot>
      </ul>
      <div class="v-popper__arrow" style="left: 35px;"></div>
    </div>
  </div>
</template>

<script>
import VairSelect from "../../types/select";
import directives from "../../directives/clickoutside";
import vInput from "../../components/Input/Input";
import { ref, provide, watch, onMounted } from "vue";
export default {
  name: "select",
  components: {
    vInput
  },
  directives: {
    ...directives
  },
  props: VairSelect,
  setup(props, ctx) {
    const arrowShow = ref(false);
    const clickOutSideIn = ref(false);
    const value = ref("");
    const oldValue = ref("");
    const valueArr = ref([]);
    const isClear = ref(false);
    const loading = ref(false);
    const cSelectBox = ref(null);
    const cSelectArrow = ref(null);
    const cSelectInput = ref(null);
    const searchValue = ref("");
    const coptions = ref(props.options);

    onMounted(() => {
      if (props.multiple) {
        if (Array.isArray(props.modelValue)) {
          valueArr.value = props.modelValue;
        } else {
          throw new TypeError(
            `When multiple selection is enabled, the binding value is expected to be an array, and the result is ${typeof props.modelValue}`
          );
        }
      } else {
        value.value = props.modelValue;
        oldValue.value = props.modelValue;
      }
    });

    const coptionsWatch = watch(
      () => props.options,
      newValue => {
        coptions.value = newValue;
        loading.value = false;
        if (coptions.value.length || props.modelValue) {
          arrowShow.value = true;
          cSelectArrow.value &&
            (cSelectArrow.value.style.transform = "rotate(180deg)");
          cSelectBox.value &&
            (cSelectBox.value.style.transform = "rotateX(0deg)");
        }
      }
    );

    const clickOutSide = bool => {
      if (!bool) {
        setTimeout(() => {
          value.value = oldValue.value;
        }, 100);
      }
      setTimeout(() => {
        if (clickOutSideIn.value === null) {
          clickOutSideIn.value = false;
          return;
        }
        if (props.disabled) return;
        if (props.modelValue === null && arrowShow.value === false) {
          ctx.emit("update:modelValue", "");
          return;
        } else {
          const label = props.modelValue;
          ctx.emit("update:modelValue", label === null ? "" : label);
        }
        if (props.remote && !props.modelValue && bool) {
          cSelectInput.value &&
            (cSelectInput.value.style.borderColor = "#409eff");
          cSelectBox.value &&
            (cSelectBox.value.style.transform = `rotateX(90deg)`);
          noHide();
          return;
        }
        arrowShow.value = bool && !arrowShow.value;
        cSelectInput.value &&
          (cSelectInput.value.style.borderColor = arrowShow.value
            ? "#409eff"
            : "");
        cSelectArrow.value &&
          (cSelectArrow.value.style.transform = `rotate(${
            arrowShow.value ? 180 : 0
          }deg)`);
        cSelectBox.value &&
          (cSelectBox.value.style.transform = `rotateX(${
            arrowShow.value ? 0 : 90
          }deg)`);
      }, 0);
    };

    const selectItemChange = option => {
      if (props.multiple) {
        noHide();
        searchValue.value = "";
        const find = valueArr.value.find(item => item.value === option.value);
        find ? handleClearMultiple(option) : valueArr.value.push(option);
      } else {
        if (props.modelValue === option.label) return;
        searchValue.value = option.label;
        value.value = option.label;
        oldValue.value = option.label;
      }
      ctx.emit(
        "update:modelValue",
        props.multiple ? valueArr.value : option.label
      );
      ctx.emit("change", props.multiple ? valueArr.value : option);
    };

    const noHide = () => {
      clickOutSideIn.value = null;
    };

    const handleClear = () => {
      noHide();
      ctx.emit("update:modelValue", "");
    };

    const handleClearMultiple = option => {
      noHide();
      if (props.disabled) return;
      if (option) {
        valueArr.value = valueArr.value.filter(item => {
          return item.value !== option.value;
        });
      } else {
        valueArr.value = [];
      }
      ctx.emit(
        "update:modelValue",
        props.multiple ? valueArr.value : option.label
      );
      ctx.emit("change", props.multiple ? valueArr.value : option);
    };

    const handleInputChange = data => {
      if (props.modelValue === data) return;
      if (props.remote) {
        data && (loading.value = true);
        ctx.emit("remote-method", data || null);
        if (!data) {
          noHide();
          cSelectArrow.value && (cSelectArrow.value.style.display = "none");
          cSelectArrow.value &&
            (cSelectArrow.value.style.transform = "rotate(0deg)");
          cSelectBox.value &&
            (cSelectBox.value.style.transform = "rotateX(90deg)");
        }
        return;
      }
      searchAndArrow(data);
    };

    const handleMouseEnter = () => {
      if (props.disabled) return;
      isClear.value = valueArr.value.length > 0;
    };

    const handleMouseLeave = () => {
      isClear.value = false;
    };

    const handleSearchInputChange = data => {
      if (props.remote) {
        data && (loading.value = true);
        ctx.emit("remote-method", data || null);
      } else {
        searchAndArrow(data);
      }
    };

    const searchAndArrow = data => {
      arrowShow.value = true;
      cSelectBox.value && (cSelectBox.value.style.transform = "rotateX(0deg)");
      coptions.value = props.options.filter(
        item => item.label.indexOf(data) !== -1
      );
    };

    provide("noHide", noHide);
    provide("value", value);
    provide("valueArr", valueArr);
    provide("multiple", props.multiple);
    provide("change", selectItemChange);

    return {
      value,
      noHide,
      isClear,
      loading,
      valueArr,
      coptions,
      searchValue,
      arrowShow,
      cSelectBox,
      handleClear,
      handleInputChange,
      handleSearchInputChange,
      cSelectArrow,
      cSelectInput,
      clickOutSide,
      coptionsWatch,
      handleMouseEnter,
      handleMouseLeave,
      handleClearMultiple
    };
  }
};
</script>

<style lang='less' scoped>
@import url("./index.less");
</style>