Files
web-temp/layers/components/blocks/modal/Content.vue

157 lines
4.3 KiB
Vue

<script setup lang="ts">
import type { ContentParams } from '#layers/types/components/modal'
interface TabItem {
title: string
desc: string
}
const props = withDefaults(defineProps<ContentParams>(), {
isOutsideClose: false,
contentTitle: '',
tabActiveIndex: 0,
tabInfo: () => [],
})
const modalStore = useModalStore()
const breakpoints = useResponsiveBreakpoints()
const isOpen = defineModel<boolean>('isOpen', { default: false })
const currentTab = ref<number>(props.tabActiveIndex)
const responsiveTransition = computed(() =>
breakpoints.value.isXs ? 'slide-up' : 'fade'
)
const tabInfo = computed<TabItem[]>(() => props.tabInfo ?? [])
const isTab = computed(() => tabInfo.value.length >= 2)
const isVisible = (index: number) => currentTab.value === index
const handleCloseModal = () => {
isOpen.value = false
}
const handleOutsideClick = () => {
if (props.isOutsideClose) handleCloseModal()
}
const handleUpdateTab = (tabNumber: number) => {
if (currentTab.value !== tabNumber) {
currentTab.value = tabNumber
}
}
watch(isOpen, newVal => {
if (newVal) {
modalStore.handleControlDimmed(true)
} else {
modalStore.handleControlDimmed(false)
}
})
</script>
<template>
<Transition :name="responsiveTransition">
<div
v-if="isOpen"
:class="['modal-wrap', { 'is-open': isOpen }, props.modalName]"
@click="handleOutsideClick"
>
<div class="modal-area" @click.stop>
<div class="modal-header">
<strong class="title">{{ props.contentTitle }}</strong>
<button type="button" class="modal-close" @click="handleCloseModal">
<span class="sr-only">close</span>
<AtomsIconsCloseLine size="24" color="#333333" />
</button>
</div>
<div class="modal-body">
<template v-if="isTab">
<div class="tab-trigger" role="tablist">
<template
v-for="(tab, index) in tabInfo"
:key="tab.title + index"
>
<button
type="button"
:class="['btn-trigger', { 'is-active': isVisible(index) }]"
role="tab"
@click="handleUpdateTab(index)"
>
{{ tab.title }}
</button>
</template>
</div>
<!-- 패널 겹침(컨테이너를 가장 패널 높이에 맞춤) -->
<div class="tab-panel grid">
<template v-for="(tab, index) in tabInfo" :key="tab.desc + index">
<div
v-dompurify-html="tab.desc"
:class="[
'content-tex',
'use-base',
{ 'is-hidden': !isVisible(index) },
]"
/>
</template>
</div>
</template>
<template v-else>
<div
v-dompurify-html="tabInfo[0]?.desc"
class="content-tex use-base"
/>
</template>
</div>
</div>
</div>
</Transition>
</template>
<style scoped>
.modal-wrap {
@apply overflow-hidden flex-col p-0 pt-[80px] sm:p-5;
}
.modal-area {
@apply overflow-hidden flex flex-col rounded-t-[20px] sm:w-[560px] sm:max-h-[680px] sm:rounded-b-[20px];
}
.modal-header {
@apply flex items-center justify-between gap-[8px] w-full py-[16px] px-[20px]
sm:pt-[20px] sm:px-[32px];
}
.modal-header .title {
@apply line-clamp-2 w-full text-[#1F1F1F] text-[16px] font-[700] leading-[24px] tracking-[-0.48px];
}
.modal-body {
@apply flex flex-col flex-1 min-h-0;
}
.tab-trigger {
@apply relative flex w-full mb-[12px] sm:mb-[24px];
}
.btn-trigger {
@apply relative w-full py-2.5 before:content-[''] before:absolute before:bottom-0 before:left-0 before:w-full before:h-[1px] before:bg-[rgba(0,0,0,0.15)] text-[#1F1F1F] text-[14px] font-[500] leading-[24px] tracking-[-0.42px];
}
.btn-trigger.is-active {
@apply before:bg-[#1F1F1F] before:h-[2px];
}
.tab-panel {
@apply overflow-hidden grid w-full h-full;
}
.tab-panel .content-tex {
@apply col-start-1 row-start-1 transition-opacity duration-200 ease-in-out;
}
.tab-panel .content-tex.is-hidden {
@apply opacity-0 invisible pointer-events-none;
}
.content-tex {
@apply overflow-y-auto mb-4 px-6 sm:mb-6 sm:px-8;
}
</style>