Merge branch 'feature/202501107-all' into feature/20251001-gil
This commit is contained in:
@@ -1,26 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
const scrollStore = useScrollStore()
|
||||
|
||||
const { isPassedStoveGnb } = storeToRefs(scrollStore)
|
||||
</script>
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<AtomsButtonCircle
|
||||
sr-only="home"
|
||||
type="link"
|
||||
to="/"
|
||||
:class="['btn-home', { 'is-fixed': isPassedStoveGnb }]"
|
||||
>
|
||||
<AtomsButtonCircle sr-only="home" type="link" to="/" class="btn-home">
|
||||
<AtomsIconsHomeFill />
|
||||
</AtomsButtonCircle>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.btn-home {
|
||||
@apply absolute top-3 right-3 mt-[48px] bg-black/20 shadow-[0_1.667px_3.333px_0_rgba(0,0,0,0.06)] backdrop-blur-[12.5px] z-[100]
|
||||
sm:top-5 md:top-6 md:right-8 md:mt-[64px];
|
||||
}
|
||||
.btn-home.is-fixed {
|
||||
@apply fixed;
|
||||
@apply fixed top-3 right-3 mt-[calc(var(--scroll-position,48px)+48px)] bg-black/20 shadow-[0_1.667px_3.333px_0_rgba(0,0,0,0.06)] backdrop-blur-[12.5px] z-[100]
|
||||
sm:top-5 md:top-6 md:right-8 md:mt-[calc(var(--scroll-position,64px)+64px)];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { PageDataLnbMenu } from '#layers/types/api/pageData'
|
||||
|
||||
const { y: windowY } = useWindowScroll({ behavior: 'smooth' })
|
||||
const { y: windowY, directions } = useWindowScroll({ behavior: 'smooth' })
|
||||
const pageDataStore = usePageDataStore()
|
||||
const breakpoints = useResponsiveBreakpoints()
|
||||
|
||||
const { pageData } = storeToRefs(pageDataStore)
|
||||
|
||||
const observerOptions = {
|
||||
// 상수 정의
|
||||
const HEADER_HEIGHT = 64
|
||||
const OBSERVER_OPTIONS = {
|
||||
root: null,
|
||||
rootMargin: '-20% 0px -60% 0px', // 상단 20%, 하단 60% 마진
|
||||
threshold: 0,
|
||||
}
|
||||
} as const
|
||||
|
||||
const isShowLnbWithScroll = ref(false)
|
||||
const activeSection = ref<string>('')
|
||||
|
||||
const lnbList = computed<Record<string, PageDataLnbMenu>>(
|
||||
@@ -39,8 +43,9 @@ const is1DepthActive = (lnbItem: PageDataLnbMenu): boolean => {
|
||||
}
|
||||
|
||||
// children 중 하나가 활성화된 경우
|
||||
if (lnbItem.children && Object.keys(lnbItem.children).length > 0) {
|
||||
return Object.values(lnbItem.children).some(
|
||||
const children = lnbItem.children
|
||||
if (children && Object.keys(children).length > 0) {
|
||||
return Object.values(children).some(
|
||||
child => activeSection.value === child.page_ver_tmpl_name_en
|
||||
)
|
||||
}
|
||||
@@ -48,54 +53,73 @@ const is1DepthActive = (lnbItem: PageDataLnbMenu): boolean => {
|
||||
return false
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
// IntersectionObserver 콜백: 교차하는 섹션들 중 가장 위에 있는 것을 활성화
|
||||
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
|
||||
if (import.meta.server) return
|
||||
// 교차하는 섹션들 중 가장 위에 있는 것을 활성화
|
||||
|
||||
const visibleEntries = entries
|
||||
.filter(entry => entry.isIntersecting)
|
||||
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
|
||||
|
||||
if (visibleEntries.length > 0) {
|
||||
activeSection.value = visibleEntries[0].target.id
|
||||
const topEntry = visibleEntries[0]
|
||||
activeSection.value = topEntry.target.id
|
||||
}
|
||||
}, observerOptions)
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(handleIntersection, OBSERVER_OPTIONS)
|
||||
|
||||
// 요소 관찰 헬퍼 함수
|
||||
const observeElement = (elementId: string) => {
|
||||
const element = document.getElementById(elementId)
|
||||
if (element) {
|
||||
observer.observe(element)
|
||||
}
|
||||
}
|
||||
|
||||
// 섹션들을 관찰 시작
|
||||
const observeSections = () => {
|
||||
if (import.meta.server) return
|
||||
|
||||
Object.values(lnbList.value).forEach(lnbItem => {
|
||||
// 1depth 관찰
|
||||
const el = document.getElementById(lnbItem.page_ver_tmpl_name_en)
|
||||
if (el) {
|
||||
observer.observe(el)
|
||||
}
|
||||
observeElement(lnbItem.page_ver_tmpl_name_en)
|
||||
|
||||
// 2depth 관찰
|
||||
if (lnbItem.children && Object.keys(lnbItem.children).length > 0) {
|
||||
Object.values(lnbItem.children).forEach(childItem => {
|
||||
const childEl = document.getElementById(childItem.page_ver_tmpl_name_en)
|
||||
if (childEl) {
|
||||
observer.observe(childEl)
|
||||
}
|
||||
const children = lnbItem.children
|
||||
if (children && Object.keys(children).length > 0) {
|
||||
Object.values(children).forEach(childItem => {
|
||||
observeElement(childItem.page_ver_tmpl_name_en)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// LNB 클릭 핸들러: 해당 섹션으로 스크롤
|
||||
const handleLnbClick = (lnbItem: PageDataLnbMenu) => {
|
||||
if (import.meta.server) return
|
||||
|
||||
const id = lnbItem.page_ver_tmpl_name_en
|
||||
const el = document.getElementById(id)
|
||||
if (!el) return
|
||||
const targetId =
|
||||
lnbItem.page_ver_tmpl_name_en === ''
|
||||
? lnbItem?.children?.['1']?.page_ver_tmpl_name_en
|
||||
: lnbItem.page_ver_tmpl_name_en
|
||||
|
||||
const elementTop = el.getBoundingClientRect().top
|
||||
const targetElement = document.getElementById(targetId)
|
||||
if (!targetElement) return
|
||||
|
||||
// 헤더 높이를 고려한 스크롤 위치 계산
|
||||
const elementTop = targetElement.getBoundingClientRect().top
|
||||
const currentScrollY = window.scrollY
|
||||
const headerHeight = 64
|
||||
const targetScrollY = currentScrollY + elementTop - headerHeight
|
||||
const targetScrollY = currentScrollY + elementTop - HEADER_HEIGHT
|
||||
|
||||
windowY.value = targetScrollY
|
||||
}
|
||||
|
||||
watch(directions, newVal => {
|
||||
// 스크롤 업일 때만 표시, 다운이거나 멈춘 상태에서는 숨김
|
||||
isShowLnbWithScroll.value = newVal.top === true
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
observeSections()
|
||||
})
|
||||
@@ -106,79 +130,109 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="isShowLnb"
|
||||
class="lnb-wrap"
|
||||
:style="{
|
||||
'--lnb-active-color': activeColor,
|
||||
'--lnb-disable-color': disableColor,
|
||||
}"
|
||||
>
|
||||
<ul class="main-list">
|
||||
<li v-for="lnbItem in lnbList" :key="lnbItem.path_code">
|
||||
<button
|
||||
v-dompurify-html="lnbItem.menu_name"
|
||||
type="button"
|
||||
:class="['btn-1depth', { 'is-active': is1DepthActive(lnbItem) }]"
|
||||
@click="handleLnbClick(lnbItem)"
|
||||
></button>
|
||||
<ul
|
||||
v-if="Object.keys(lnbItem.children || {}).length > 0"
|
||||
class="sub-list"
|
||||
>
|
||||
<li v-for="subItem in lnbItem.children" :key="subItem.path_code">
|
||||
<button
|
||||
v-dompurify-html="subItem.menu_name"
|
||||
type="button"
|
||||
:class="[
|
||||
'btn-2depth',
|
||||
{
|
||||
'is-active': activeSection === subItem.page_ver_tmpl_name_en,
|
||||
},
|
||||
]"
|
||||
@click="handleLnbClick(subItem)"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<template v-if="isShowLnb">
|
||||
<div
|
||||
:class="['lnb-wrap', { 'is-hidden': !isShowLnbWithScroll }]"
|
||||
:style="{
|
||||
'--lnb-active-color': activeColor,
|
||||
'--lnb-disable-color': disableColor,
|
||||
}"
|
||||
>
|
||||
<ul class="lnb-main">
|
||||
<li v-for="lnbItem in lnbList" :key="lnbItem.path_code">
|
||||
<button
|
||||
v-dompurify-html="lnbItem.menu_name"
|
||||
type="button"
|
||||
:class="['btn-1depth', { 'is-active': is1DepthActive(lnbItem) }]"
|
||||
@click="handleLnbClick(lnbItem)"
|
||||
></button>
|
||||
<ul
|
||||
v-if="Object.keys(lnbItem.children || {}).length > 0"
|
||||
class="lnb-sub"
|
||||
>
|
||||
<li v-for="subItem in lnbItem.children" :key="subItem.path_code">
|
||||
<button
|
||||
v-dompurify-html="subItem.menu_name"
|
||||
type="button"
|
||||
:class="[
|
||||
'btn-2depth',
|
||||
{
|
||||
'is-active':
|
||||
activeSection === subItem.page_ver_tmpl_name_en,
|
||||
},
|
||||
]"
|
||||
@click="handleLnbClick(subItem)"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.lnb-wrap {
|
||||
@apply fixed top-1/2 right-0 py-12 pr-[42px] text-right -translate-y-1/2 bg-[radial-gradient(100%_50%_at_100%_50%,rgba(0,0,0,0.5)_25%,rgba(0,0,0,0)_100%)] z-50;
|
||||
@apply fixed top-0 right-0 mt-[calc(var(--scroll-position,48px)+64px)] py-8 pr-10 bg-[radial-gradient(100%_50%_at_100%_50%,rgba(0,0,0,0.4)_25%,rgba(0,0,0,0)_100%)] transition-transform duration-[400ms] ease-in-out z-50;
|
||||
}
|
||||
.main-list {
|
||||
.lnb-wrap:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -5%;
|
||||
width: 105%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(5px);
|
||||
mask-image: radial-gradient(
|
||||
circle at right center,
|
||||
#000 0%,
|
||||
#000 40%,
|
||||
transparent 80%
|
||||
);
|
||||
mask-size: 100% 100%;
|
||||
mask-repeat: no-repeat;
|
||||
z-index: -1;
|
||||
}
|
||||
.lnb-wrap.is-hidden {
|
||||
@apply translate-x-[110%] delay-[800ms];
|
||||
}
|
||||
.lnb-main {
|
||||
@apply flex flex-col gap-4 items-end;
|
||||
}
|
||||
.lnb-main > li {
|
||||
@apply flex flex-col items-end;
|
||||
}
|
||||
.btn-1depth {
|
||||
@apply text-[15px] leading-[26px] tracking-[-0.54px];
|
||||
}
|
||||
.sub-list {
|
||||
@apply flex flex-col gap-2 items-end mt-4 mb-1 pr-[46px];
|
||||
.lnb-sub {
|
||||
@apply flex flex-col gap-2 items-end mt-4 mb-1 pr-[16px];
|
||||
}
|
||||
.btn-2depth {
|
||||
@apply text-[14px] leading-[20px] tracking-[-0.42px];
|
||||
}
|
||||
|
||||
button {
|
||||
@apply relative font-[500] text-[var(--lnb-disable-color)] transition-all duration-300 ease-in-out;
|
||||
@apply flex items-center font-[500] text-[var(--lnb-disable-color)] transition-all duration-300 ease-in-out;
|
||||
}
|
||||
button:hover,
|
||||
button.is-active {
|
||||
@apply text-[var(--lnb-active-color)];
|
||||
}
|
||||
button::before {
|
||||
@apply content-[''] absolute top-1/2 rounded-full -translate-y-1/2 bg-transparent transition-all duration-300 ease-in-out;
|
||||
button::after {
|
||||
@apply content-[''] rounded-full ml-2 bg-[var(--lnb-disable-color)] transition-all duration-300 ease-in-out;
|
||||
}
|
||||
button.is-active::before {
|
||||
button.is-active::after {
|
||||
@apply bg-[var(--lnb-active-color)];
|
||||
}
|
||||
.btn-1depth::before {
|
||||
.btn-1depth::after {
|
||||
@apply -right-4 w-1.5 h-1.5;
|
||||
}
|
||||
.btn-2depth::before {
|
||||
.btn-2depth::after {
|
||||
@apply -right-3.5 w-1 h-1;
|
||||
}
|
||||
|
||||
.main-promotion .lnb-wrap {
|
||||
@apply mt-[calc(var(--scroll-position,48px)+64px+72px)];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,7 +28,7 @@ const loadGnb = (locale: string) => {
|
||||
const gnbOption = {
|
||||
wrapper: '#stove-wrap',
|
||||
isResponsive: true,
|
||||
skin: stoveGnbData?.skin_type || 'gnb-dark-mini',
|
||||
skin: 'gnb-dark-mini',
|
||||
widget: {
|
||||
gameListAndService: false,
|
||||
languageSelect: false,
|
||||
|
||||
@@ -22,8 +22,8 @@ onMounted(() => {
|
||||
mobile: '',
|
||||
},
|
||||
widget: {
|
||||
notification: true,
|
||||
stoveDownload: true,
|
||||
notification: stoveGnbData?.notify_icon_visible || true,
|
||||
stoveDownload: stoveGnbData?.stove_install_button_visible || true,
|
||||
languageSelect: false,
|
||||
themeSelect: false,
|
||||
stoveMenu: {
|
||||
@@ -40,9 +40,8 @@ onMounted(() => {
|
||||
},
|
||||
mode: {
|
||||
theme: {
|
||||
support: ['light', 'dark'],
|
||||
support: designTheme === 1 ? ['light'] : ['dark'],
|
||||
default: designTheme === 1 ? 'light' : 'dark',
|
||||
// support: designTheme === 1 ? ['light'] : ['dark'],
|
||||
},
|
||||
mini: true,
|
||||
layout: 'wide',
|
||||
@@ -54,7 +53,7 @@ onMounted(() => {
|
||||
stoveGnbOptions
|
||||
)
|
||||
}
|
||||
if(mountedInstance){
|
||||
if (mountedInstance) {
|
||||
//Stove GNB에서도 쿠키를 굽고 있어 소문자로 통일
|
||||
//LOCALE 쿠키를 가져온 후 소문자로 변경해서 다시 LOCALE 쿠키를 설정
|
||||
nextTick(() => {
|
||||
@@ -66,10 +65,8 @@ onMounted(() => {
|
||||
localeCookie.value = localeCookie.value.toLowerCase()
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (mountedInstance && typeof mountedInstance.destroy === 'function') {
|
||||
mountedInstance.destroy()
|
||||
|
||||
@@ -93,7 +93,7 @@ watch(isOpen, newVal => {
|
||||
:class="[
|
||||
'content-tex',
|
||||
'use-base',
|
||||
{ 'is-visible': isVisible(index) },
|
||||
{ 'is-hidden': !isVisible(index) },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
@@ -153,13 +153,4 @@ watch(isOpen, newVal => {
|
||||
.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>
|
||||
|
||||
@@ -50,7 +50,6 @@ const mainOptions = computed<Options>(() => ({
|
||||
const thumbOptions = computed<Options>(() => ({
|
||||
type: 'slide',
|
||||
rewind: true,
|
||||
// focus: 'center',
|
||||
autoWidth: true,
|
||||
perMove: 1,
|
||||
arrows: true,
|
||||
|
||||
Reference in New Issue
Block a user