Home > Mobile >  Custom Dropdown input not triggering blur event
Custom Dropdown input not triggering blur event

Time:08-26

I have a custom dropup component that I am building and I find myself running into some very weird issues with it. The biggest one being the blur event, which doesnt seem to be firing when I click outside of the input.

Basically I just want the options elements to hide on blur, if there is no localValue then reset the component back to the Initial View and if there IS a localValue set it to the Selected State, keeping the value in there. I'm really not sure where I am going wrong here.

---Designs---

Initial State

enter image description here

Toggled State

enter image description here

Selected State

enter image description here

I've made a enter image description here

SelectModelInput

<template>
  <div :key="buttonKey">
    <div
      style="position: relative; width: 100%"
      tabindex="-1"
      @blur="() => closeDropdown()"
    >
      <div  @click="() => toggleDropup()">
        <p
          v-if="selectModelVersionIsVisible"
          
        >
          Select Model
        </p>
        <div v-if="!selectModelVersionIsVisible" >
          <p >OP-REAL</p>
          <input
            v-model="localValue"
            :
            type="text"
            placeholder="Type to Search"
          />
          <button
            v-if="localValue"
            :
            @click="() => clearLocalVal()"
          >
            <svg
              style="width: 18px"
              xmlns="http://www.w3.org/2000/svg"
              width="100%"
              height="100%"
              viewBox="0 0 24 24"
              fill="none"
              stroke="#A5B0CB"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              
            >
              <line x1="18" y1="6" x2="6" y2="18"></line>
              <line x1="6" y1="6" x2="18" y2="18"></line>
            </svg>
          </button>
        </div>
      </div>
      <Transition name="fade">
        <div
          v-if="optionsAreVisible && filteredOptions.length !== 0"
          
        >
          <a
            v-for="option in filteredOptions"
            :key="option.model"
            
          >
            <div  @click="selectOption(option.model)">
              <p >{{ option.model }}</p>
              <p >{{ option.date }}</p>
            </div>
          </a>
        </div>
      </Transition>
    </div>
  </div>
</template>
<script>
import { computed, defineComponent, ref, watch } from "vue";

export default defineComponent({
  emit: ["input"],
  props: {
    value: { type: String },
    models: {
      type: Array,
      default: () => [
        { model: "Test v1", date: "Jul.5.2022" },
        { model: "Test v2", date: "Jul.6.2022" },
        { model: "Test v3", date: "Jul.8.2022" },
      ],
    },
  },
  setup(props, { emit }) {
    const localValue = ref(props.value);
    watch(localValue, (newVal) => {
      emit("input", newVal);
    });
    const buttonKey = ref(0);
    const optionsAreVisible = ref(false);
    const selectModelVersionIsVisible = ref(true);
    const options = ref(props.models);
    const filteredOptions = computed(() => {
      const filteredOptions = options.value.filter((option) =>
        option.model.toLowerCase().includes(localValue.value?.toLowerCase())
      );
      return filteredOptions;
    });
    function toggleDropup() {
      console.log("toggle Dropup");
      optionsAreVisible.value = true;
      selectModelVersionIsVisible.value = false;
    }
    function selectOption(option) {
      console.log(`option → `, option);
      localValue.value = option;
      optionsAreVisible.value = false;
      selectModelVersionIsVisible.value = false;
    }
    function closeDropdown() {
      console.log(`localValue → `, localValue.value);
      console.log("ping");
      optionsAreVisible.value = true
    }
    function clearLocalVal() {
      buttonKey.value  ;
      selectModelVersionIsVisible.value = true;
      optionsAreVisible.value = false;
      localValue.value = "";
    }
    return {
      options,
      optionsAreVisible,
      selectModelVersionIsVisible,
      toggleDropup,
      filteredOptions,
      localValue,
      selectOption,
      clearLocalVal,
      buttonKey,
      closeDropdown,
    };
  },
});
</script>
<style lang="sass" scoped>
button
  background: none
  border: none
p
  padding: 0
  margin: 0
::placeholder
  font-style: italic
  line-height: 0
  color: #A5B0CB
._input
  all: unset
  position: relative
  display: flex
  justify-content: center
  align-items: center
  min-height: 2rem
  min-width: 212px
  background: #161a24
  border-radius: 1rem
  &:hover,
  &:focus
    box-shadow: 0 0 0 1px #3867D0

  &-active
    display: flex
    justify-content: center
    align-items: center
    // padding-right: 2rem
    &-select-model
      // padding: 6px 0
      color: #A5B0CB
      font-size: 1rem
      font-weight: 300
    &-op-real
      display: flex
      justify-content: center
      align-items: center
      height: 100%
      border-top-left-radius: 1rem
      border-bottom-left-radius: 1rem
      padding: 0 10px
      background: #202634
      font-size: 12px
      font-weight: bold
      letter-spacing: 0.96px
      padding: 8px 10px
      user-select: none
    &-input
      all: unset
      font-size: 14px
      text-align: center
      color: #A5B0CB
      padding: 6px 0px
      padding-right: 2rem
      flex: 1
      border-top-right-radius: 1rem
      border-bottom-right-radius: 1rem
      &:focus::placeholder
        color: transparent
    &-delete
      position: absolute
      bottom: 50%
      transform: translateY(50%)
      right: 0
      display: flex
      justify-content: center
      padding: 0
      padding-right: 8px

._bg-change
  background-color: #202634
._hide
  visibility: hidden
._options
  position: absolute
  bottom: 100%
  left: 30%
  z-index: 100
  display: flex
  flex-direction: column
  list-style-type: none
  padding: 0.5rem 1rem
  gap: 0.25rem
  background-color: #2C2B30
  border: 1px solid #727275
  border-radius: 0.5rem
._option
  background-color: transparent
  font-size: 0.875rem
  line-height: 1.25rem
  font-weight: 400
  white-space: nowrap
  width: 100%
  &-inner
    width: 100%
    display: flex
    justify-content: space-between
    padding: 0.25rem
    cursor: pointer
    &:hover
      background-color: #3582F5
      border-radius: 0.25rem
    &-model
      font-size: 14px
      color: #fff
      margin-right: 2.5rem
    &-date
      font-size: 12px
      color: #B2B4B9

.fade-enter-active,
.fade-leave-active
  transition: opacity 300ms

.fade-enter,
.fade-leave-to
  opacity: 0
</style>

App.vue

<template>
  <div >
    <SelectModelInput
      v-model="text"
      :models="modelsFromDev"
    />
    <br />
    <span style="color: white">{{ text }}</span>
  </div>
</template>

<script>
import { defineComponent, ref } from "vue";
import SelectModelInput from "./components/SelectModelInput.vue";
export default defineComponent({
  setup() {
    const text = ref("");
    const modelsFromDev = [
      { model: "Test v4", date: "Jul.5.2022" },
      { model: "Test v5", date: "Jul.6.2022" },
      { model: "Test v6", date: "Jul.8.2022" },
    ];
    return { text, modelsFromDev };
  },
  components: { SelectModelInput },
});
</script>

<style lang="sass" scoped>
body
  padding: 0
  margin: 0
._wrapper
  color: white
  display: flex
  flex-direction: column
  justify-content: center
  align-items: center
  width: 100vw
  height: 100vh
  background: #283044
</style>

CodePudding user response:

Showing the options should be tied to clicking on the "Type to Search" <input>. When this <input> loses focus then set options visible to false. Using your code I also had to make functions to separately toggle selectModelVersionIsVisible and optionsAreVisible: codesandbox

Also regarding one of your other comments, clicking the "X" button can be made to clear localValue by just setting localValue = '' on click which is also in my sandbox. Don't need to use :key

  • Related