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.
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
.
Minimal reproduction repo in CodeSandBox
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/index.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"
@close="closeModal"
@click.native.stop.prevent
>
<Dialog
:initialFocus="focusRef"
as="div"
@click.native.stop.prevent
class="relative z-50"
>
<div class="fixed inset-0 overflow-y-auto">
<div
class="flex min-h-full items-center justify-center p-4 text-center"
>
<DialogPanel
@click.native.stop.prevent
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
<span class="text-black dark:text-white">Field-1</span>
<input
ref="focusRef"
v-model="test.one"
type="text"
class="relative w-full h-full bg-gray-50 dark:text-black dark:bg-gray-700 dark:border-gray-600 border-gray-300 border rounded text-center block overflow-hidden"
/>
<span class="text-black dark:text-white">Field-2</span>
<input
v-model="test.two"
type="text"
class="relative w-full h-full bg-gray-50 dark:text-white dark:bg-gray-700 dark:border-gray-600 border-gray-300 border rounded text-center block overflow-hidden"
/>
<button class="mt-4" @click="closeModal">Close</button>
</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"]);
const focusRef = ref(null);
//Reference variables
const modal2 = ref(props.modal2);
const test = ref({});
watch(
() => props.modal2,
async (newValue) => {
modal2.value = newValue;
}
);
const closeModal = () => {
console.log("CLOSE MODAL-2");
modal2.value = false;
emits("closeModal");
};
</script>
Why clicking anywhere on DialogPanel2.vue
automatically closes the previous Dialog DialogPanel1.vue
? I tried removing the @close="closeModal"
from the DialogPanel1.vue
that would prevent the closing of the DialogPanel1.vue
but Its also making that I am unable to edit any of the fields in DialogPanel2.vue
meaning the focus kind of still remains on the DialogPanel1.vue
due to which I am unable to get the access or focus in DialogPanel2.vue
. Following changes to DialogPanel1.vue
:
<TransitionRoot appear :show="modal1" as="template">
but after this when I am unable to access the Input fields in DialogPanel2.vue
.
I am still unable to identify the root cause and fix it, any suggestions would be really helpful.
Issue: Codesandbox
Following issue my issue GIF:
I can't read the full code, but it seems like the dialog uses something like "outside click" which will consider any click that is not inside the element or its child as "outside click" and it will trigger the close function. In your case, your <DialogPanel2>
is placed outside of <DialogPanel1>
, so when the <DialogPanel1>
is opened and the <DialogPanel2>
is also opened, anything that is not children of <DialogPanel1>
will be considered as outside of it (in this case, <DialogPanel2>
is considered as outside of <DialogPanel1>
), thus the click will trigger the close of <DialogPanel1>
.
The easiest fix (depending on your use case) is to move the <DialogPanel2>
to the inside of <DialogPanel1>
so that it's not considered as outside click, like this:
<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>