Merge branch 'feature/202501107-all' into feature/20251001-gil
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const showSnsList = ref(false)
|
||||
|
||||
const { tm } = useI18n()
|
||||
const gameDataStore = useGameDataStore()
|
||||
const modalStore = useModalStore()
|
||||
|
||||
@@ -33,9 +34,9 @@ const handleCopy = async () => {
|
||||
try {
|
||||
const url = window.location.href
|
||||
await navigator.clipboard.writeText(url)
|
||||
handleOpenToast('복사 성공')
|
||||
handleOpenToast(tm('Alert_Copy_Complete'))
|
||||
} catch (error) {
|
||||
console.error('복사 실패:', error)
|
||||
console.error('[handleCopy] Error:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -58,10 +58,6 @@ const handlePagination = (page: number) => {
|
||||
currentPage.value = page
|
||||
emits('update:page', page)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log(blocks.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -80,16 +80,3 @@ onBeforeUnmount(() => {
|
||||
<template>
|
||||
<div id="stove-wrap" class="relative h-[48px] z-[5]" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
[data-theme='light'] {
|
||||
#stove-wrap {
|
||||
@apply bg-white;
|
||||
}
|
||||
}
|
||||
[data-theme='dark'] {
|
||||
#stove-wrap {
|
||||
@apply bg-black;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
interface props {
|
||||
isShowDimmed?: boolean
|
||||
contentText?: string
|
||||
confirmButtonText?: string
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
}
|
||||
import type { DialogParams } from '#layers/types/components/modal'
|
||||
|
||||
const props = withDefaults(defineProps<props>(), {
|
||||
const props = withDefaults(defineProps<DialogParams>(), {
|
||||
isShowDimmed: true,
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
interface props {
|
||||
isShowDimmed?: boolean
|
||||
contentText?: string
|
||||
confirmButtonText?: string
|
||||
cancelButtonText?: string
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
}
|
||||
import type { DialogParams } from '#layers/types/components/modal'
|
||||
|
||||
const props = withDefaults(defineProps<props>(), {
|
||||
const props = withDefaults(defineProps<DialogParams>(), {
|
||||
isShowDimmed: true,
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
165
layers/components/blocks/modal/Content.vue
Normal file
165
layers/components/blocks/modal/Content.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<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-visible': 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;
|
||||
}
|
||||
.content-tex::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
.content-tex::-webkit-scrollbar {
|
||||
@apply w-5;
|
||||
}
|
||||
.content-tex::-webkit-scrollbar-thumb {
|
||||
@apply w-1 bg-[#D9D9D9] rounded-full bg-clip-padding border-solid border-transparent border-8;
|
||||
}
|
||||
</style>
|
||||
@@ -6,16 +6,14 @@ const { toast } = modalStore
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div v-if="toast.storeIsOpen" class="toast-container">
|
||||
<p class="toast-text">
|
||||
{{ toast.storeContentText }}
|
||||
</p>
|
||||
<p v-dompurify-html="toast.storeContentText" class="toast-text"></p>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toast-container {
|
||||
@apply fixed left-1/2 max-w-[328px] py-3 px-6 rounded-[8px] bg-[rgba(85,85,85,0.4)] backdrop-blur-[25px] -translate-x-1/2 bottom-[20px] md:bottom-[40px] z-[800]
|
||||
@apply fixed left-1/2 max-w-[328px] py-3 px-6 rounded-[8px] bg-[rgba(85,85,85,0.4)] backdrop-blur-[25px] -translate-x-1/2 bottom-[20px] md:bottom-[40px] z-[900]
|
||||
before:content-[''] before:absolute before:inset-0 before:border before:border-white/10 before:rounded-[8px];
|
||||
}
|
||||
.toast-text {
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { getYouTubeEmbedUrl } from '@/layers/utils/youtubeUtil'
|
||||
import type { YoutubeParams } from '#layers/types/components/modal'
|
||||
|
||||
interface Props {
|
||||
youtubeUrl: string
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<YoutubeParams>(), {
|
||||
youtubeUrl: '',
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user