I am creating a reusable Dialog modal in my application and it works fine when I use it once in my page but when I use more than once then I get the error: There are no focusable elements inside the <FocusTrap />
.
Following is my reusable Dialog component from HeadlessUI in my Vue3/Nuxt 3 application:
ExtensionModal.vue
:
<template>
<div class="flex-col items-center">
<div class="mb-2">
<button
type="button"
@click="openModal"
class="block rounded-full text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-0 focus:outline-none focus:ring-blue-300 font-medium text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-600 dark:focus:ring-blue-800"
>
{{ buttonLabel }}
</button>
</div>
</div>
<TransitionRoot appear :show="showExtensionModal" 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-[90vw] sm:max-w-[80vw] md:max-w-[70vw] lg:max-w-[60vw] xl:max-w-[50vw] 2xl:max-w-[40vw]"
>
<DialogTitle
as="h3"
class="flex text-lg font-medium leading-6 text-gray-900 justify-center dark:text-white"
>
{{ buttonTitle }}
</DialogTitle>
<form @submit="onSubmit($event)">
<div class="flex mt-5">
<div class="w-full z-40">
<select
v-model="element"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
>
<option
v-for="option in extensionTypes"
:value="option.value"
:key="option.value"
>
{{ option.text }}
</option>
</select>
</div>
</div>
<!-- Submit/Cancel button for the modal -->
<FormSubmit @submit="onSubmit" @close="closeModal" />
</form>
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
</template>
<script setup>
import {
TransitionRoot,
TransitionChild,
Dialog,
DialogPanel,
DialogTitle,
Switch,
} from "@headlessui/vue";
const props = defineProps({
buttonTitle: {
type: String, // title for the field
required: false,
},
buttonLabel: {
type: String, // label for the button
required: false,
},
});
const emits = defineEmits(["onSubmit"]);
const extension = ref({});
const showExtensionModal = useState("showExtensionModal", () => false);
const extensionTypes = useState("extensionTypes", () => [
{ value: "extensionElement", text: "Element" },
]);
const dataTypes = useState("dataTypes", () => [
{ value: "string", text: "String" },
{ value: "complex", text: "Complex" },
]);
//Open the modal on click of the button
function openModal() {
showExtensionModal.value = true;
}
//Close the modal on click of the button
function closeModal() {
showExtensionModal.value = false;
}
//Function to update the value of the object based on DropDown value change
function onItemChange(fieldName, value) {
extension.value = {};
extension.value[fieldName] = value.value;
}
//Function to store and return the value on submission
function onSubmit(event) {
event.preventDefault();
console.log("Sumitted : " + JSON.stringify(extension.value, null, 4));
emits("onSubmit", extension.value);
closeModal();
}
</script>
<style>
</style>
I am using this component twice in my application: pages/view.vue
:
<template>
<div>
<div class="my-2 mx-2 px-2 pb-0">
<div class="flex gap-2 h-16 float-right">
<ExtensionModal
:buttonTitle="$t('pages.modal.userExtension-btt-title')"
:buttonLabel="$t('pages.modal.userExtension-btt-label')"
/>
<ExtensionModal
:buttonTitle="$t('pages.modal.ilmd-btt-title')"
:buttonLabel="$t('pages.modal.ilmd-btt-label')"
/>
</div>
</div>
</div>
</template>
<script setup>
import { Icon } from "@iconify/vue";
</script>
<style>
</style>
When I click on the button to open modal then I get the error:
There are no focusable elements inside the <FocusTrap />`
The installation and everything else is correct because it works fine when I use only one instance of the child component in my view.vue
page:
<ExtensionModal
:buttonTitle="$t('pages.modal.userExtension-btt-title')"
:buttonLabel="$t('pages.modal.userExtension-btt-label')"
/>
I am getting the error only when i use it more than once in my application. Not sure what am I doing wrong here. Do I need to pass any additional parameter or add any missing information?