69 lines
1.8 KiB
Vue
69 lines
1.8 KiB
Vue
|
<template>
|
||
|
<transition name="fade">
|
||
|
<div
|
||
|
v-if="modelValue"
|
||
|
v-block-scroll="modelValue"
|
||
|
class="top-0 left-0 z-[100] fixed flex justify-center items-center bg-gray-900 opacity-80 w-screen h-screen"
|
||
|
@click.self="emitClose"
|
||
|
>
|
||
|
<div class="bg-gray-100 dark:bg-gray-700 border rounded w-1/2">
|
||
|
<div class="flex justify-between p-2 border-b">
|
||
|
<h2 class="pr-4 font-bold">Header title</h2>
|
||
|
<button class="border" @click="emitClose">
|
||
|
<XMarkIcon class="size-6"></XMarkIcon>
|
||
|
</button>
|
||
|
</div>
|
||
|
<div class="p-4">
|
||
|
<slot>
|
||
|
<RocketLaunchIcon class="float-left mr-4 text-green-600 size-20"></RocketLaunchIcon>
|
||
|
<p>content</p>
|
||
|
</slot>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</transition>
|
||
|
</template>
|
||
|
|
||
|
<script setup lang="ts">
|
||
|
import { RocketLaunchIcon, XMarkIcon } from '@heroicons/vue/24/solid';
|
||
|
import type { ModalProps } from './types';
|
||
|
const props = withDefaults(defineProps<ModalProps>(), {});
|
||
|
|
||
|
onMounted(() => {
|
||
|
document.addEventListener('keydown', closeModalKeyBoardEvent);
|
||
|
});
|
||
|
|
||
|
onBeforeUnmount(() => {
|
||
|
document.removeEventListener('keydown', closeModalKeyBoardEvent);
|
||
|
});
|
||
|
|
||
|
function closeModalKeyBoardEvent(event: KeyboardEvent) {
|
||
|
if (event.key === 'Escape') {
|
||
|
emitClose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const emit = defineEmits<{
|
||
|
(name: 'update:modelValue', info: boolean): void;
|
||
|
}>();
|
||
|
|
||
|
function emitClose() {
|
||
|
emit('update:modelValue', false);
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style scoped>
|
||
|
.fade-enter-active {
|
||
|
transition: all 0.2s ease-out;
|
||
|
}
|
||
|
|
||
|
.fade-leave-active {
|
||
|
transition: all 0.4s ease-in;
|
||
|
}
|
||
|
|
||
|
.fade-enter-from,
|
||
|
.fade-leave-to {
|
||
|
opacity: 0;
|
||
|
}
|
||
|
</style>
|