vue · 2297 bytes Raw Blame History
1 <template>
2 <Teleport to="body">
3 <div
4 v-if="show"
5 class="fixed inset-0 z-50 overflow-y-auto"
6 @keydown.esc="handleClose"
7 >
8 <!-- Backdrop -->
9 <div
10 class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"
11 @click="handleClose"
12 ></div>
13
14 <!-- Modal -->
15 <div class="flex min-h-screen items-center justify-center p-4">
16 <div
17 class="relative bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden"
18 @click.stop
19 >
20 <!-- Header -->
21 <div class="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
22 <h3 class="text-lg font-semibold text-gray-900 dark:text-white">
23 <slot name="title">Modal</slot>
24 </h3>
25 <button
26 @click="handleClose"
27 class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
28 >
29 <XMarkIcon class="w-6 h-6" />
30 </button>
31 </div>
32
33 <!-- Content -->
34 <div class="p-6 overflow-y-auto max-h-[calc(90vh-120px)]">
35 <slot />
36 </div>
37
38 <!-- Footer -->
39 <div v-if="$slots.footer" class="flex justify-end space-x-3 p-6 border-t border-gray-200 dark:border-gray-700">
40 <slot name="footer" />
41 </div>
42 </div>
43 </div>
44 </div>
45 </Teleport>
46 </template>
47
48 <script setup lang="ts">
49 import { ref, onMounted, onUnmounted } from 'vue'
50 import { XMarkIcon } from '@heroicons/vue/24/outline'
51
52 interface Props {
53 show?: boolean
54 closable?: boolean
55 }
56
57 const props = withDefaults(defineProps<Props>(), {
58 show: true,
59 closable: true,
60 })
61
62 const emit = defineEmits<{
63 close: []
64 }>()
65
66 function handleClose() {
67 if (props.closable) {
68 emit('close')
69 }
70 }
71
72 // Handle escape key
73 function handleKeydown(event: KeyboardEvent) {
74 if (event.key === 'Escape' && props.closable) {
75 handleClose()
76 }
77 }
78
79 onMounted(() => {
80 document.addEventListener('keydown', handleKeydown)
81 // Prevent body scroll
82 document.body.style.overflow = 'hidden'
83 })
84
85 onUnmounted(() => {
86 document.removeEventListener('keydown', handleKeydown)
87 // Restore body scroll
88 document.body.style.overflow = ''
89 })
90 </script>