I am facing weird behavior with HeadlessUI's Modal component (via TailwindUI), used in Vue3. The Modal will correctly Fade Out when clicking the "close" button, pressing "Esc" or clicking outside the Modal's body, but it won't Fade In as soon as I put an HTML "button" inside the Modal's content.
Example
This will work:
<template>
<TransitionRoot as="template" :show="open">
<Dialog as="div" class="relative z-40 transition">
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0" enter-to="opacity-100" leave="ease-in duration-200" leave-from="opacity-100" leave-to="opacity-0">
<div class="fixed inset-0 bg-black/80 transition-opacity backdrop-blur-sm"/>
</TransitionChild>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enter-to="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200" leave-from="opacity-100 translate-y-0 sm:scale-100" leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
<DialogPanel
:class="[
this.lg
? 'sm:max-w-6xl max-h-[90vh] lg:max-h-[75vh]'
: 'sm:max-w-lg max-h-[75vh] lg:max-h-[50vh]'
]"
@scroll="onScroll($event)"
class="relative w-full transform overflow-hidden rounded bg-lighter shadow-xl px-4 pt-5 pb-4 text-left border border-black/10 transition-all sm:my-8 sm:p-6 overflow-y-auto"
>
<div class="text-center sm:text-left relative">
<DialogTitle as="h3" class="text-md mb-8 text-center leading-6 font-title text-black relative">
<slot name="title"></slot>
</DialogTitle>
<div>
<div @click="$emit('close')"> <!-- Works fine with a div -->
<svg
class="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</div>
</div>
</div>
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
This WON'T work
<template>
<TransitionRoot as="template" :show="open">
<Dialog as="div" class="relative z-40 transition">
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0" enter-to="opacity-100" leave="ease-in duration-200" leave-from="opacity-100" leave-to="opacity-0">
<div class="fixed inset-0 bg-black/80 transition-opacity backdrop-blur-sm"/>
</TransitionChild>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enter-to="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200" leave-from="opacity-100 translate-y-0 sm:scale-100" leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
<DialogPanel
:class="[
this.lg
? 'sm:max-w-6xl max-h-[90vh] lg:max-h-[75vh]'
: 'sm:max-w-lg max-h-[75vh] lg:max-h-[50vh]'
]"
@scroll="onScroll($event)"
class="relative w-full transform overflow-hidden rounded bg-lighter shadow-xl px-4 pt-5 pb-4 text-left border border-black/10 transition-all sm:my-8 sm:p-6 overflow-y-auto"
>
<div class="text-center sm:text-left relative">
<DialogTitle as="h3" class="text-md mb-8 text-center leading-6 font-title text-black relative">
<slot name="title"></slot>
</DialogTitle>
<div>
<button type="button" @click="$emit('close')"> <!-- But not with a button -->
<svg
class="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</button>
</div>
</div>
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
The script part of the component:
<script>
import {Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot} from '@headlessui/vue'
import {ExclamationIcon} from '@heroicons/vue/outline'
import ButtonSimple from "../UI/Buttons/ButtonSimple.vue";
export default {
components: {
ButtonSimple,
Dialog,
DialogPanel,
DialogTitle,
TransitionChild,
TransitionRoot,
ExclamationIcon,
},
emits: ['bottomScrollReached'],
props: {
open: Boolean,
lg: {
type: Boolean,
default: false,
}
},
data() {
return {
scrollHeight: null,
}
},
methods: {
onScroll: function (event) {
console.log(event.target.scrollTop + event.target.offsetHeight, event.target.scrollHeight)
if (event.target.scrollTop + event.target.offsetHeight >= event.target.scrollHeight) {
this.$emit('bottomScrollReached');
}
},
}
}
</script>