On gists
Modal - composable (toRaw ...)
Vue.js
useModal.js
Raw
#
import { ref } from 'vue'
// keep track of component to render
const component = ref();
// keep track of whether to show modal
const show = ref(false);
export function useModal() {
return {
component,
show,
// methods to show/hide modal
showModal: () => show.value = true,
hideModal: () => show.value = false,
}
}
App.vue
Raw
#
<script setup lang="ts">
import { useModal } from './composables/useModal';
import { markRaw } from 'vue';
import ModalConfirm from './components/ModalConfirm.vue';
import ModalOverview from './components/ModalOverview.vue';
const modal = useModal();
const openConfirm = () => {
modal.component.value = markRaw(ModalConfirm);
modal.showModal();
};
const openOverview = () => {
modal.component.value = markRaw(ModalOverview);
modal.showModal();
};
</script>
<template>
<div class="flex justify-center items-center min-h-screen">
<Teleport to="#modal">
<Transition>
<component :is="modal.component.value" v-if="modal.show.value" @close="modal.hideModal" />
</Transition>
</Teleport>
<button @click="openConfirm">Open Confirm Modal</button>
<span class="mx-4"></span>
<button @click="openOverview">Open Overview Modal</button>
</div>
<div class="absolute top-20 bg-red-500 p-2 w-full left-0">This will make things ugly</div>
</template>
<style scoped>
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>
ModalConfirm.vue
Raw
#
<template>
<div
class="absolute inset-0 overflow-y-auto bg-black bg-opacity-50"
>
<div
class="flex items-start justify-center min-h-screen mt-24 text-center"
>
<div
class="bg-white text-black rounded-lg text-center shadow-xl p-6 w-64"
role="dialog"
aria-modal="true"
>
<h3>Do you confirm?</h3>
<div class="flex justify-center py-4 text-white">
<!-- We will handle these emits later -->
<button @click="$emit('close')" class="border border-black bg-white text-black mr-4">No</button>
<button @click="$emit('close')">Yes</button>
</div>
</div>
</div>
</div>
</template>
ModalOverview.vue
Raw
#
<template>
<div
class="absolute inset-0 bg-black bg-opacity-50"
>
<div
class="flex items-start justify-center min-h-screen mt-24 text-center"
>
<div
class="bg-white text-black rounded-lg text-center shadow-xl p-6 w-64"
role="dialog"
aria-modal="true"
>
<h2 class="text-lg font-bold">Overview of items:</h2>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
<div class="flex justify-center py-4 text-white">
<button @click="$emit('close')">Close</button>
</div>
</div>
</div>
</div>
</template>