vueform / multiselect

Vue 3 multiselect component with single select, multiselect and tagging options (+Tailwind CSS support).
https://vueform.com
MIT License
807 stars 150 forks source link

Why does the VueMultiselect is always open by default when using within HeadlessUI Dialog/modal #355

Closed Aravinda93 closed 1 year ago

Aravinda93 commented 1 year ago

I am using the VueMultiselect within my Nuxt 3 application. I am using it within my Nuxt Headless UI Transition Dialog modal. When I open the modal the multiselect is open by default.

I have 2 of them and one of them is always open. If I just keep one then that is open but if there is 2 then first one is open.

components/MultiSelect.vue:

<template>
  <label class="w-1/6 font-semibold text-base mb-1">{{ label }}</label>
  <div class="flex items-center dark:text-white">
    <VueMultiselect
      v-model="modelValue"
      :label="label"
      :placeholder="label"
      :options="options.map((i) => i.value)"
      :custom-label="(opt) => options.find((x) => x.value == opt).text"
      :searchable="true"
      :close-on-select="false"
      :allow-empty="false"
      :multiple="true"
      :preselect-first="false"
      :open-direction="'above'"
      :prevent-autofocus="true"
    >
    </VueMultiselect>
  </div>
</template>

<script setup>
import VueMultiselect from "vue-multiselect";

const props = defineProps({
  label: {
    type: String,
    required: false,
  },
  modelValue: {
    type: Array,
    required: false,
  },
  options: {
    type: Array,
    required: true,
  },
});

const emits = defineEmits(["update:modelValue"]);

const modelValue = ref(props.modelValue);

// Emit the updated modelValue when the selectedOption changes
const updateModelValue = () => {
  emits("update:modelValue", modelValue.value);
};

// Emit the updated modelValue when the selectedOption changes
watchEffect(() => {
  updateModelValue();
});
</script>

<style src="vue-multiselect/dist/vue-multiselect.css"></style>

Following is my uage on Dialog components/MyDialog.vue:

<template>
    <div class="flex-col items-center">
      <div class="mb-2">
        <button
          type="button"
          @click="openModal($event)"
          title="$t('pages.modal.sensor-title')"
          class="text-cyan-700 border-cyan-700 focus:ring-cyan-300 hover:bg-cyan-800 dark:hover:bg-cyan-600 dark:focus:ring-cyan-800 dark:border-cyan-500 dark:text-cyan-500 block rounded-full hover:text-white border focus:ring-4 focus:outline-none font-medium text-sm px-5 py-2.5 text-center mx-auto dark:hover:text-white"
        >
          Open
        </button>
      </div>
    </div>
    <TransitionRoot appear :show="showModal" as="template">
      <Dialog as="div" @close="closeModal" class="relative z-10">
        <TransitionChild
          as="template"
          enter="duration-300 ease-out"
          enter-from="opacity-0"
          enter-to="opacity-100"
          leave="duration-200 ease-in"
          leave-from="opacity-100"
          leave-to="opacity-0"
        >
          <div class="fixed inset-0 bg-black bg-opacity-25" />
        </TransitionChild>
        <div
          class="fixed h-fit inset-0 overflow-y-auto flex items-center justify-center"
        >
          <div
            class="fixed inset-0 overflow-y-auto flex items-center justify-center"
          >
            <TransitionChild
              as="template"
              enter="duration-300 ease-out"
              enter-from="opacity-0 scale-95"
              enter-to="opacity-100 scale-100"
              leave="duration-200 ease-in"
              leave-from="opacity-100 scale-100"
              leave-to="opacity-0 scale-95"
            >
              <DialogPanel
                class="flex-grow w-full h-fit transform overflow-visible rounded-2xl bg-gray-200 dark:bg-slate-800 p-6 text-left align-middle shadow-xl transition-all max-w-[70vw] sm:max-w-[70vw] md:max-w-[70vw] lg:max-w-[70vw] xl:max-w-[70vw] 2xl:max-w-[70vw]"
              >  
                <form @submit="onSubmit($event)">
                  <div class="flex items-center mt-10">
                    <div class="w-2/5">
                      <MultiSelect
                        :label="'Option1'"
                        v-model="selectedOption1"
                        :options="options1"
                      />
                    </div>

                    <div class="w-2/5 ml-5">
                      <MultiSelect
                        :label="'Option 2'"
                        v-model="selectionOption2"
                        :options="options2"
                      />
                    </div>
                  </div>

                  <!-- Submit/Cancel button for the modal -->
                  <button
                    type="button"
                    class="text-red-700 border-red-700 focus:ring-red-300 hover:bg-red-700 dark:hover:bg-red-500 dark:focus:ring-red-800 dark:border-red-500 dark:text-red-500 block rounded-full hover:text-white border focus:ring-4 focus:outline-none font-medium text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:hover:text-white"
                    @click="closeModal"
                  >
                    Submit
                  </button>
                </form>
              </DialogPanel>
            </TransitionChild>
          </div>
        </div>
      </Dialog>
    </TransitionRoot>
  </template>

  <script setup>
  import { ref, defineProps, onBeforeMount } from "vue";
  import {
    TransitionRoot,
    TransitionChild,
    Dialog,
    DialogPanel,
    DialogTitle,
    Switch,
  } from "@headlessui/vue";

  //Import static values for dropdowns
  import {
    options1,
    options2
  } from "~/public/Static/DefaultValues";

  const showModal = ref(false);
  const selectionOption1 = ref([]);
  const selectionOption2 = ref([]);

  // Open the modal on click of the button
  const openModal = () => {
    showModal.value = true;
  };

  // Close the modal on click of the button
  const closeModal = () => {
    showModal.value = false;
  };
  </script>

<style>
</style>

Pages/View.vue:

As soon as the Dialog open after clicking on the button I see that the 1st multiselect dropdown is open by default. How to fix this?

hellojessicagraham commented 1 year ago

I believe it's because the dialogue component will focus on the first focusable element https://headlessui.com/react/dialog#managing-initial-focus

Aravinda93 commented 1 year ago

@hellojessicagraham

Yes it was because of that only. Thanks a lot for pointing it out. I have changed the initial focus element to some button so it wont be opened any more. Thanks again :)