/ Gists / Modal - composable (toRaw ...)
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>