tailwindlabs / headlessui

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
https://headlessui.com
MIT License
26.13k stars 1.08k forks source link

Opening Healdess UI Dialog (Modal) via another Dialog (Modal) closes the previous Dialog (Modal) automatically? #3193

Closed Aravinda93 closed 5 months ago

Aravinda93 commented 6 months ago

I am using the HeadlessUI/Vue component in my Nuxt 3 application to build the Dialog/Modals. I am using the latest version "@headlessui/vue": "^1.7.22" and developing in Chrome latest browser.

Minimal reproduction repo in CodeSandBox

I am creating a DialogPanel1.vue using the headlessui/vue. I have a button in DialogPanel1.vue on clicking it I am opening the DialogPanel2.vue but within DialogPanel2.vue when I click anywhere then it automatically closes the DialogPanel1.vue which is behind the DialogPanel2.vue.

Why is it closing DialogPanel1 automatically? I want to ensure that DialogPanel1 is closed only when I click the close button in DialogPanel1.vue not for any click on DialogPanel2. I tried adding the @click.stop or @mousedown.stop to prevent the propagation of the event but it's still not working. Can anyone please let me know the issue?

Following is my complete code /pages/Test.vue:

<template>
  <DialogPanel1 />
</template>

<script setup>
</script>

Following are my components /component/DialogPanel1.vue:

<template>
  <div class="mb-2">
    <button
      type="button"
      @click="openModal1"
      class="flex secondary-button text-secondary dark:bg-transparent dark:hover:text-secondary dark:hover:bg-slate-700 mx-auto justify-center items-center rounded border focus:ring-0 focus:outline-none font-medium px-5 py-2.5 text-center min-w-[8rem] sm:min-w-[10rem] lg:min-w-[12rem]"
    >
      <span class="pr-1"> Open Modal-1 </span>
    </button>
  </div>

  <TransitionRoot appear :show="modal1" as="template">
    <Dialog as="div" @close="closeModal" class="relative z-30">
      <div class="fixed inset-0 overflow-y-auto">
        <div
          class="flex min-h-full items-center justify-center p-4 text-center"
        >
          <DialogPanel
            class="dark:bg-slate-800 w-full transform overflow-auto rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all z-20"
          >
            THIS IS MODAL-1
            <button
              class="flex secondary-button text-secondary dark:bg-transparent dark:hover:text-secondary dark:hover:bg-slate-700 mx-auto justify-center items-center rounded border focus:ring-0 focus:outline-none font-medium px-5 py-2.5 text-center min-w-[8rem] sm:min-w-[10rem] lg:min-w-[12rem]"
              @click="openModal2"
            >
              MODAL-2
            </button>
          </DialogPanel>
        </div>
      </div>
    </Dialog>
  </TransitionRoot>

  <DialogPanel2 :modal2="modal2" @closeModal="hideModal2" />
</template>

<script setup>
import { TransitionRoot, Dialog, DialogPanel } from "@headlessui/vue";

const modal1 = ref(false);
const modal2 = ref(false);

//Function to open Modal-1
const openModal1 = () => {
  console.log("Opening Modal-1");
  modal1.value = true;
};

//Close the modal on click of the button
const closeModal = () => {
  console.log("CLOSE MODAL-1");
  modal1.value = false;
};

//Function to open Modal-2
const openModal2 = () => {
  console.log("Open MODAL-2");
  modal2.value = true;
};

const hideModal2 = () => {
  console.log("Closing MODAL-2");
  modal2.value = false;
};
</script>

Following is my /components/DialogPanel2.vue:

<template>
  <TransitionRoot appear :show="modal2" as="template">
    <Dialog
      as="div"
      @close="closeModal"
      class="relative z-50"
      :initialFocus="completeButtonRef"
    >
      <div class="fixed inset-0 overflow-y-auto">
        <div
          class="flex min-h-full items-center justify-center p-4 text-center"
        >
          <DialogPanel
            class="pt-5 w-full max-w-xl overflow-y-auto transform overflow-visible rounded-2xl bg-white dark:bg-slate-800 p-6 align-middle shadow-xl transition-all"
          >
            THIS IS MODAL-2
          </DialogPanel>
        </div>
      </div>
    </Dialog>
  </TransitionRoot>
</template>

  <script setup>
import { TransitionRoot, Dialog, DialogPanel } from "@headlessui/vue";

const props = defineProps({
  modal2: {
    type: Boolean, //Show/hide the modal based on the flag
    required: false,
  },
});

const emits = defineEmits(["closeModal"]);

//Reference variables
const modal2 = ref(props.modal2);

watch(
  () => props.modal2,
  async (newValue) => {
    modal2.value = newValue;
  }
);

const closeModal = () => {
  console.log("CLOSE MODAL-2");
  modal2.value = false;
  emits("closeModal");
};

const completeButtonRef = ref({}); // to avoid opening of the multiselect by default & initial focus
</script>

Why clicking anywhere on DialogPanel2.vue automatically closes the previous Dialog DialogPanel1.vue?

Following issue my issue GIF: ModalCosure

boring-joey commented 5 months ago

I got the same exact issue in headlessui/react v2+ now.

Aravinda93 commented 5 months ago

@boring-joey

I solved it by placing the DialogPanel2.vue inside the DialogPanel1.vue:

  <TransitionRoot appear :show="modal1" as="template" @close="closeModal">
    <Dialog as="div" class="relative z-30">
      <div class="fixed inset-0 overflow-y-auto">
        <div
          class="flex min-h-full items-center justify-center p-4 text-center"
        >
          <DialogPanel
            class="dark:bg-slate-800 w-full transform overflow-auto rounded-2xl bg-gray-200 p-6 text-left align-middle shadow-xl transition-all z-20"
          >
            THIS IS MODAL-1
            <button
              class="flex secondary-button text-secondary dark:bg-transparent dark:hover:text-secondary dark:hover:bg-slate-700 mx-auto justify-center items-center rounded border focus:ring-0 focus:outline-none font-medium px-5 py-2.5 text-center min-w-[8rem] sm:min-w-[10rem] lg:min-w-[12rem]"
              @click="openModal2"
            >
              MODAL-2
            </button>
            <DialogPanel2 :modal2="modal2" @closeModal="hideModal2" /><!-- move it here-->
          </DialogPanel>
        </div>
      </div>
    </Dialog>
  </TransitionRoot>
carlosvaldesweb commented 2 weeks ago

This issue is still happening. I've opened an issue in Nuxt ui: https://github.com/nuxt/ui/issues/2431

In my case i need only 1 Modal to show Pricing in the page, but sometimes another modal opens this modal, so i can't to add inside of other modals. Also some features are paid in my web, so i cannot add 3 times this modal in the same Page.

@RobinMalfait