カスタム風の選択入力と風を構築する


要素UIを使ったことがありますか?私は現在、生産アプリケーションでこれを使用して、それは非常にモバイルフレンドリーではない、ということに気づいた!私は検索機能と日付/時刻ピッカーで選択入力のようなもののカップルのためにそれを使用している.両方ともモバイルデバイスで惨めに失敗します、そして、私のユーザーが私にそれを報告し始めたので、私はこれを見つけました.
それで、私は自分のカスタムVueコンポーネントを構築することを決めました.私は、彼らがモバイルフレンドリーであることを保証することができます.
私は自動的にコンポーネントを起動することを決めた.
コンポーネントは次のようになります.
<auto-complete
  :data="data"
  v-model.trim="formData.client"
  @chosen="handleChosen"
  placeholder="Search for state..."
></auto-complete>
私の目標はそれをシンプルに保つことでしたが、カスタマイズできるように誰か他の人は、彼らは好みに合わせてカスタマイズすることができますそれを使用したい場合.以下を含む.
私はもっとカスタマイズできるようにもう少し追加するつもりだと思います.

よし、いい部分、コードに行こう!
<template>
  <div class="relative" v-click-outside="clickedOutside">
    <input
      :value="value"
      @input="handleInput"
      :placeholder="placeholder"
      ref="input"
      tabindex="0"
      :class="inputClass"
    />
    <span
      v-if="value"
      @click.prevent="reset()"
      class="absolute inset-y-0 right-0 pr-3 flex items-center cursor-pointer"
    >
      x
    </span>
    <div
      v-show="value && showOptions"
      @click.self="handleSelf()"
      @focusout="showOptions = false"
      tabindex="0"
      :class="dropdownClass"
    >
      <ul class="py-1">
        <li
          v-for="(item, index) in searchResults"
          :key="index"
          @click="handleClick(item)"
          class="px-3 py-2 cursor-pointer hover:bg-gray-200"
        >
          {{ item.name }}
        </li>
        <li v-if="!searchResults.length" class="px-3 py-2 text-center">
          No Matching Results
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      required: false,
    },
    placeholder: {
      type: String,
      required: false,
      default: "Enter text here.",
    },
    data: {
      type: Array,
      required: true,
    },
    inputClass: {
      type: String,
      required: false,
      default:
        "border border-gray-300 py-2 px-3 rounded-md focus:outline-none focus:shadow-outline",
    },
    dropdownClass: {
      type: String,
      required: false,
      default:
        "absolute w-full z-50 bg-white border border-gray-300 mt-1 mh-48 overflow-hidden overflow-y-scroll rounded-md shadow-md",
    },
  },

  data() {
    return {
      showOptions: false,
      chosenOption: "",
      searchTerm: "",
    };
  },

  computed: {
    searchResults() {
      return this.data.filter((item) => {
        return item.name.toLowerCase().includes(this.searchTerm.toLowerCase());
      });
    },
  },

  methods: {
    reset() {
      this.$emit("input", "");
      this.chosenOption = "";
    },

    handleInput(evt) {
      this.$emit("input", evt.target.value);
      this.searchTerm = evt.target.value;
      this.showOptions = true;
    },

    handleClick(item) {
      this.$emit("input", item.name);
      this.$emit("chosen", item);
      this.chosenOption = item.name;
      this.showOptions = false;
      this.$refs.input.focus();
    },

    clickedOutside() {
      this.showOptions = false;

      if (!this.chosenOption) {
        this.$emit("input", "");
      }
    },
  },
};
</script>

<style scoped>
.mh-48 {
  max-height: 10rem;
}
</style>
任意の改善提案がある場合は下記コメントしてください.私はあなたのフィードバックに感謝します!