Support multiple <Dialog />
components
#1564
Replies: 4 comments 8 replies
-
Just hit this issue as well. In our case we have an open dialog with a button that opens another dialog. The first dialog should close and the second should open. As these happen simultaneously with transitions, the For now our solution is to wait for the first dialog to close before opening the second dialog. |
Beta Was this translation helpful? Give feedback.
-
Any suggestions on how to deal with this? I have 2 different modals on my page, one for a sign up form, and a second modal as thank you modal. What's the optimal way to implement this? |
Beta Was this translation helpful? Give feedback.
-
I have such a scenario where there is a global dialog, such as a login or registration form, which can be triggered at any time, on the page layer, in the first-level dialog, or even in a second-level dialog. If I have to use component hierarchy to implement nesting, this global dialog component will be ubiquitous, leading to a lot of redundant code. Is there any solution for this situation? |
Beta Was this translation helpful? Give feedback.
-
Hello there. Modals work fine as long as there is only one open, so I made a stack system similar to what Apple does on iPhones to track navigation. This way, I can open them in whatever combinations, and they work fine on iOS also (where if you open two modals at the same time right now, you can't scroll, at least on my env). The gist of it:
// component/molecule/Modal.vue
<script setup lang="ts">
import {Dialog, TransitionChild, TransitionRoot} from '@headlessui/vue'
import {computed, watch} from "vue";
import useModals from "@/service/modals";
const props = withDefaults(defineProps<{ open: boolean }>(), {open: false});
const emits = defineEmits<{ (e: 'close'): void }>();
// State, used to track whether this is the topmost modal (otherwise it bugs out on iOS).
const modals = useModals();
const modalId = modals.newId();
const isTopmostModal = computed(() => {
console.log('Recalculating open. modal_id=%o; is_open=%o;', modalId, modals.topmostModal.value === modalId);
return modals.topmostModal.value === modalId;
});
watch(() => props.open, (current, previous) => {
// When this changes, either we opened or closed the modal.
let opened = previous === false && current === true;
let closed = !opened;
if (opened) modals.onOpen(modalId);
if (closed) modals.onClose(modalId);
})
console.log('Creating modal. id=%o', modalId);
// --- UI Events.
function onClose() {
emits("close");
}
</script>
<template>
<TransitionRoot as="template" :show="props.open && isTopmostModal">
<Dialog as="div" class="relative z-10" @close="onClose">
<TransitionChild as="template" enter="ease-out duration-300"
enter-from="opacity-0 translate-y-4 md:translate-y-0 md:scale-95"
enter-to="opacity-100 translate-y-0 md:scale-100" leave="ease-in duration-200"
leave-from="opacity-100 translate-y-0 md:scale-100"
leave-to="opacity-0 translate-y-4 md:translate-y-0 md:scale-95">
<slot></slot>
</TransitionChild>
</Dialog>
</TransitionRoot>
</template> // service/modals.ts
import {computed, ref} from "vue";
const openModals = ref<string[]>([]);
const topmostModal = computed<string | undefined>(() => {
let open = openModals.value;
return open.length > 0 ? open[open.length - 1] : undefined;
})
function newId() {
return crypto.randomUUID();
}
/**
* Registers a modal as open (on top of everything else already opened).
* @return id of the opened modal that should be returned to onModalClose();
*/
function onOpen(id: string) {
openModals.value.push(id);
}
/**
* Removes the top modal form the list.
*/
function onClose(id: string) {
openModals.value = openModals.value.filter(openedId => openedId !== id);
}
export default function useModals() {
return {
newId,
topmostModal,
onOpen,
onClose,
}
} |
Beta Was this translation helpful? Give feedback.
-
Currently we only support a single Dialog or nested Dialog components. We also want to be able to open multiple Dialogs at the same time where typically the top most Dialog "wins" and receives and locks the focus.
If all Dialogs are closed, only then do we want to restore the scroll lock, inert others, ... which is currently not the case.
Some references:
Beta Was this translation helpful? Give feedback.
All reactions