Files
web-temp/layers/components/molecules/modal/YouTubeModal.vue

135 lines
3.5 KiB
Vue

<template>
<Teleport to="body">
<Transition
enter-active-class="transition duration-300 ease-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition duration-200 ease-in"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div
v-if="isOpen"
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75"
@click="handleBackdropClick"
>
<Transition
enter-active-class="transition duration-300 ease-out"
enter-from-class="opacity-0 scale-95"
enter-to-class="opacity-100 scale-100"
leave-active-class="transition duration-200 ease-in"
leave-from-class="opacity-100 scale-100"
leave-to-class="opacity-0 scale-95"
>
<div v-if="isOpen" class="relative w-full max-w-4xl mx-4" @click.stop>
<!-- 헤더 -->
<div class="flex justify-end">
<button
class="p-1 text-white rounded-full transition-colors"
aria-label="모달 닫기"
@click="closeModal"
>
<svg
class="w-8 h-8"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<!-- 유튜브 영상 컨테이너 -->
<div class="relative w-full" :style="{ paddingBottom: '56.25%' }">
<iframe
v-if="youtubeId"
:src="`https://www.youtube.com/embed/${youtubeId}?autoplay=1&rel=0`"
class="absolute top-0 left-0 w-full h-full"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
title="YouTube video player"
/>
</div>
</div>
</Transition>
</div>
</Transition>
</Teleport>
</template>
<script setup lang="ts">
interface Props {
isOpen: boolean
youtubeId: string
title?: string
description?: string
closeOnBackdrop?: boolean
}
interface Emits {
(e: 'close'): void
(e: 'update:isOpen', value: boolean): void
}
const props = withDefaults(defineProps<Props>(), {
title: '',
description: '',
closeOnBackdrop: true,
})
const emit = defineEmits<Emits>()
// ESC 키로 모달 닫기
const handleKeydown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && props.isOpen) {
closeModal()
}
}
// 배경 클릭으로 모달 닫기
const handleBackdropClick = () => {
if (props.closeOnBackdrop) {
closeModal()
}
}
// 모달 닫기 함수
const closeModal = () => {
emit('close')
emit('update:isOpen', false)
}
// 키보드 이벤트 리스너 등록/해제
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
// 모달이 열릴 때 body 스크롤 방지
watch(
() => props.isOpen,
isOpen => {
if (isOpen) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
}
)
// 컴포넌트 언마운트 시 body 스크롤 복원
onUnmounted(() => {
document.body.style.overflow = ''
})
</script>