Merge branch 'feature/202501107-all' into feature/20251001-gil

This commit is contained in:
“hyeonggkim”
2025-11-19 19:45:30 +09:00
31 changed files with 476 additions and 333 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,

View File

@@ -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()

View File

@@ -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>

View File

@@ -50,7 +50,6 @@ const mainOptions = computed<Options>(() => ({
const thumbOptions = computed<Options>(() => ({
type: 'slide',
rewind: true,
// focus: 'center',
autoWidth: true,
perMove: 1,
arrows: true,

View File

@@ -6,9 +6,6 @@ import type {
const { locale } = useI18n()
const gameDomain = useGetGameDomain()
const scrollStore = useScrollStore()
const { isPassedStoveGnb } = storeToRefs(scrollStore)
const isEventNavigationOpen = ref(true)
const eventNavigationList = ref<Record<string, EventNavigation>>({})
@@ -52,7 +49,6 @@ onMounted(async () => {
class="event-navigation"
:class="{
'is-closed': !isEventNavigationOpen,
'is-fixed': isPassedStoveGnb,
}"
>
<div class="navigation-wrapper">
@@ -90,10 +86,7 @@ onMounted(async () => {
<style scoped>
.event-navigation {
@apply absolute top-0 left-0 bottom-0 mt-[48px] md:mt-[64px] z-[100] transition-transform duration-300 ease-in-out;
}
.event-navigation.is-fixed {
@apply fixed;
@apply fixed top-0 left-0 bottom-0 mt-[calc(var(--scroll-position,48px)+48px)] md:mt-[calc(var(--scroll-position,64px)+64px)] z-[100] transition-transform duration-300 ease-in-out;
}
.navigation-wrapper {
@apply relative h-full p-3 sm:p-5 sm:pr-3

View File

@@ -1,5 +1,4 @@
<script setup lang="ts">
import { onClickOutside, useWindowSize } from '@vueuse/core'
import { useGameDataStore } from '#layers/stores/useGameDataStore'
import type {
GameDataMenu,
@@ -9,6 +8,14 @@ import type {
PlatformTransformType,
} from '#layers/types/api/gameData'
const MORE_WIDTH = 72
const START_WIDTH_MARGIN = 40
const PLATFORM_LABEL_KEY: Record<PlatformTransformType, string> = {
pc: 'PC',
google_play: 'Google Play',
app_store: 'App Store',
}
const route = useRoute()
const { tm } = useI18n()
const { width } = useWindowSize()
@@ -21,21 +28,23 @@ const modalStore = useModalStore()
const { gameData } = storeToRefs(gameDataStore)
const { isPassedStoveGnb } = storeToRefs(scrollStore)
const navAreaRef = ref<HTMLElement>()
const startRef = ref<HTMLElement>()
const navAreaRef = ref<HTMLElement | null>(null)
const startRef = ref<HTMLElement | null>(null)
const gnbData = gameData.value?.gnb
const { width: startWidth } = useElementSize(startRef)
const isMounted = ref(false)
const isMenuOpen = ref(false)
const navWidth = ref(0)
const startWidth = ref(0)
const officialItemWidths = ref<number[]>([])
const overflowNam = ref<number>(0)
const gnbData = computed(() => gameData.value?.gnb)
const gnb1depthButtonData = computed(
() => gnbData?.buttons[0]?.button_json as GameDataResourceGroup
() => gnbData.value?.buttons[0]?.button_json as GameDataResourceGroup
)
const gnb2depthButtonData = computed(
() => gnbData?.buttons[1]?.button_json as GameDataResourceGroupSet
() => gnbData.value?.buttons[1]?.button_json as GameDataResourceGroupSet
)
const currentPath = computed(() => formatPathWithoutLocale(route.path))
const supportedPlatforms = computed(
@@ -47,7 +56,7 @@ const supportedPlatforms = computed(
const pathMatches = (base: string, current: string) => {
if (!base || base === '/') return current === '/'
return current === base || current.startsWith(base + '/')
return current === base
}
/** 자식 중 활성 링크 존재 여부 */
@@ -76,20 +85,10 @@ const isNavItemActive = (gnbItem: GameDataMenu): boolean => {
// navAreaRef의 넓이를 구하는 함수
const calculateNavWidth = () => {
if (!navAreaRef.value || !gnbData) return 0
if (!navAreaRef.value || !gnbData.value) return 0
const navAreaWidth = navAreaRef.value.offsetWidth
const moreWidth = 72 // 더보기 버튼 넓이 + 마진
return navAreaWidth + moreWidth
}
// startRef의 넓이를 구하는 함수
const calculateStartWidth = () => {
if (!startRef.value || !gnbData) return 0
const startWidth = startRef.value.offsetWidth
const headerRightPadding = 40 // 헤더 오른쪽 마진
return startWidth + headerRightPadding
navWidth.value = navAreaWidth + MORE_WIDTH
}
// official 자식들의 넓이를 구하는 함수
@@ -108,24 +107,20 @@ const calculateOfficialItemWidths = () => {
}
officialItemWidths.value = widths
// 해상도 체크 및 오버플로우 계산
calculateOverflow()
}
// 오버플로우 계산 함수
const calculateOverflow = () => {
if (!navAreaRef.value || !startRef.value) return
const totalNavWidth = navWidth.value + startWidth.value
const screenWidth = width.value
// 모바일(1024px 미만)에서는 overflowNam을 0으로 설정
if (screenWidth < 1024) {
if (breakpoints.value.isMobile) {
overflowNam.value = 0
return
}
const screenWidth = width.value
const totalNavWidth = navWidth.value + startWidth.value + START_WIDTH_MARGIN
// 해상도가 navWidth + startWidth보다 작은 경우
if (screenWidth < totalNavWidth) {
let removedCount = 0
@@ -166,12 +161,6 @@ const has2depthButton = (gnbItem: GameDataMenu) => {
const highlight = (text: string) => `<span class="highlight">${text}</span>`
const PLATFORM_LABEL_KEY: Record<PlatformTransformType, string> = {
pc: 'PC',
google_play: 'Google Play',
app_store: 'App Store',
}
const tmWithGameName = (key: string): string => {
const raw = tm(key)
if (typeof raw !== 'string') return ''
@@ -215,7 +204,10 @@ const handleStartClick = () => {
window.open(url, '_blank')
}
const stopClickOutside = onClickOutside(navAreaRef, () => handleMenuClose())
watchEffect(() => {
if (!startWidth.value) return // 0, null, undefined면 스킵
calculateOverflow()
})
// 화면 크기 변경 시 오버플로우 재계산
watch(width, () => {
@@ -224,51 +216,47 @@ watch(width, () => {
onMounted(() => {
overflowNam.value = 0
isMounted.value = true
// 초기 계산 시도
nextTick(() => {
if (navAreaRef.value && startRef.value) {
navWidth.value = calculateNavWidth()
startWidth.value = calculateStartWidth()
calculateOfficialItemWidths()
}
calculateNavWidth()
calculateOfficialItemWidths()
calculateOverflow()
})
})
onBeforeUnmount(() => {
if (stopClickOutside) {
stopClickOutside()
}
})
</script>
<template>
<header class="header">
<BlocksStoveGnbNew class="h-[48px]" />
<div :class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]">
<NuxtLinkLocale to="/brand" class="mx-auto md:hidden router-link-active router-link-exact-active">
<AtomsLocaleLink to="/" class="mx-auto md:hidden">
<img
:src="getImageHost(gnbData?.bi_path)"
:alt="gameData?.game_name"
class="h-[30px]"
/>
</NuxtLinkLocale>
</atomslocalelink>
<button class="btn-open" @click="handleMenuOpen">
<AtomsIconsMenuBoldLine class="mx-auto" />
<span class="sr-only">menu open</span>
</button>
<div :class="['nav-wrap', { 'is-open': isMenuOpen }]">
<div ref="navAreaRef" class="nav-area">
<div
:class="['nav-wrap', { 'is-open': isMenuOpen }]"
@click="handleMenuClose"
>
<div ref="navAreaRef" class="nav-area" @click.stop>
<div class="nav-logo">
<NuxtLinkLocale to="/brand" class="router-link-active router-link-exact-active" @click="handleMenuClose">
<AtomsLocaleLink to="/" @click="handleMenuClose">
<img
:src="getImageHost(gnbData?.bi_path)"
:alt="gameData?.game_name"
class="h-[30px]"
/>
</NuxtLinkLocale>
</div>
<nav class="nav-list">
<div v-if="gnbData?.menus" class="official">
</AtomsLocaleLink>
<nav :class="['nav-list', { 'is-mounted': isMounted }]">
<div v-if="gnbData?.menus" class="official custom-theme-scrollbar">
<div
v-for="(gnbItem, key) in gnbData?.menus"
:key="key"
@@ -376,45 +364,48 @@ onBeforeUnmount(() => {
class="nav-1depth text-gradient-pink"
>
<AtomsIconsStarFill />
<span>이벤트</span>
<span>{{ tm('Gnb_Event') }}</span>
<AtomsIconsStarFill />
</AtomsLocaleLink>
</div>
</div>
</nav>
<div ref="startRef" class="btn-start">
<ClientOnly>
<component
:is="
breakpoints.isDesktop ? 'BlocksButtonLauncher' : 'AtomsButton'
"
type="custom"
platform="pc"
:background-color="
getColorCodeFromData(gnb1depthButtonData?.btn_info, 'btn')
"
:text-color="
getColorCodeFromData(gnb1depthButtonData?.btn_info, 'txt')
"
@click="handleStartClick"
>
{{ gnb1depthButtonData?.btn_info?.txt_btn_name }}
</component>
<div
v-if="breakpoints.isDesktop && gnb2depthButtonData"
class="nav-2depth"
>
<ul>
<li v-for="(item, key) in gnb2depthButtonData" :key="key">
<BlocksButtonLauncher type="custom" :platform="key">
{{ item.btn_info?.txt_btn_name }}
</BlocksButtonLauncher>
</li>
</ul>
</div>
</ClientOnly>
</div>
<ClientOnly>
<div ref="startRef" class="btn-start">
<template v-if="gnb1depthButtonData">
<component
:is="
breakpoints.isDesktop
? 'BlocksButtonLauncher'
: 'AtomsButton'
"
type="custom"
platform="pc"
:background-color="
getColorCodeFromData(gnb1depthButtonData?.btn_info, 'btn')
"
:text-color="
getColorCodeFromData(gnb1depthButtonData?.btn_info, 'txt')
"
@click="handleStartClick"
>
{{ gnb1depthButtonData?.btn_info?.txt_btn_name }}
</component>
<div
v-if="breakpoints.isDesktop && gnb2depthButtonData"
class="nav-2depth"
>
<ul>
<li v-for="(item, key) in gnb2depthButtonData" :key="key">
<BlocksButtonLauncher type="custom" :platform="key">
{{ item.btn_info?.txt_btn_name }}
</BlocksButtonLauncher>
</li>
</ul>
</div>
</template>
</div>
</ClientOnly>
<button class="btn-close" @click="handleMenuClose">
<AtomsIconsCloseLine
size="24"
@@ -426,12 +417,12 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
</header>
</div></header>
</template>
<style scoped>
.header {
@apply bg-theme-foreground text-theme-foreground-reversal relative z-[200];
@apply bg-theme-foreground text-theme-foreground-reversal relative font-[500] tracking-[-0.48px] z-[200];
}
.game-wrap {
@apply absolute flex w-full h-[48px] items-center whitespace-nowrap px-[52px] bg-theme-foreground sm:px-[72px] md:h-16 md:pl-0 md:pr-[40px]
@@ -471,7 +462,7 @@ onBeforeUnmount(() => {
.nav-area {
@apply flex flex-col w-[100vw] max-w-[360px] min-w-[320px] bg-theme-foreground-10 translate-x-[-100%]
md:inline-flex md:flex-row md:w-auto md:max-w-none md:h-full md:pl-[40px] md:items-center md:bg-transparent md:transform-none;
md:inline-flex md:flex-row md:w-auto md:max-w-[100%] md:h-full md:pl-[40px] md:items-center md:bg-transparent md:transform-none;
}
.nav-logo {
@@ -480,7 +471,10 @@ onBeforeUnmount(() => {
.nav-list {
@apply overflow-hidden flex flex-col order-1 h-full mt-2 mb-4 px-2
md:flex-row md:order-none md:h-full md:my-0 md:ml-10 md:mr-6 md:px-0 md:overflow-visible;
md:flex-row md:order-none md:h-full md:my-0 md:ml-10 md:mr-6 md:px-0;
}
.nav-list.is-mounted {
@apply md:overflow-visible;
}
.nav-item {
@@ -528,6 +522,12 @@ onBeforeUnmount(() => {
.official {
@apply overflow-x-hidden overflow-y-auto pb-2 md:flex md:items-center md:space-x-8 md:pb-0 md:overflow-visible;
}
.custom-theme-scrollbar::-webkit-scrollbar {
@apply w-1;
}
.custom-theme-scrollbar::-webkit-scrollbar-thumb {
@apply border-0;
}
.more {
@apply relative hidden ml-[32px] pt-[11px] md:block;

View File

@@ -14,6 +14,6 @@ const props = defineProps<{
<style scoped>
.description {
@apply line-clamp-4 text-[15px] font-[400] leading-[24px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:line-clamp-3 md:text-[20px] md:leading-[30px];
@apply line-clamp-4 text-[15px] font-[400] tracking-[-0.45px] leading-[24px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:tracking-[-0.6px] md:line-clamp-3 md:text-[20px] md:leading-[30px];
}
</style>

View File

@@ -41,13 +41,13 @@ const componentProps = computed(() => {
</script>
<template>
<div class="flex flex-wrap items-end justify-between mb-[16px] md:mb-[24px]">
<div class="flex items-end justify-between mb-[16px] md:mb-[24px]">
<h3
class="text-[#1F1F1F] text-[18px] font-bold leading-[26px] tracking-[-0.54px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px] shrink-0"
>
<span>{{ props.title }}</span>
</h3>
<div class="flex items-center justify-between w-full">
<div class="flex items-center justify-between">
<slot />
<p
v-if="props.description && !props.link"

View File

@@ -280,7 +280,7 @@ defineExpose({
class="absolute left-0 right-0 top-0 bg-gradient-to-b from-[#292929] to-transparent z-[1] h-[24px] md:h-[32px]"
></div>
<div
class="overflow-y-auto h-full py-[24px] px-5 md:py-[32px] md:px-10"
class="overflow-y-auto h-full py-[24px] px-5 md:py-[32px] md:px-10 custom-theme-scrollbar"
>
<div class="px-3 py-4 md:px-6">
<div class="flex cursor-pointer items-center gap-3 md:gap-4">
@@ -308,7 +308,7 @@ defineExpose({
<!-- Marketing Detail Content -->
<div
v-if="isExpandedMarketing"
class="mt-4 max-h-[160px] overflow-y-auto rounded-lg bg-white/[0.04] px-4 py-3"
class="mt-4 max-h-[160px] overflow-y-auto rounded-lg bg-white/[0.04] px-4 py-3 custom-theme-scrollbar"
>
<p
v-dompurify-html="tmWithGameName('Preregist_Agree_News_Info')"
@@ -426,27 +426,4 @@ defineExpose({
.modal-wrap:deep(.modal-close) svg {
@apply fill-white;
}
/* Custom scrollbar for accordion content */
:deep(.overflow-y-auto) {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
}
:deep(.overflow-y-auto::-webkit-scrollbar) {
width: 4px;
}
:deep(.overflow-y-auto::-webkit-scrollbar-track) {
background: transparent;
}
:deep(.overflow-y-auto::-webkit-scrollbar-thumb) {
background: rgba(255, 255, 255, 0.15);
border-radius: 999px;
}
:deep(.overflow-y-auto::-webkit-scrollbar-thumb:hover) {
background: rgba(255, 255, 255, 0.25);
}
</style>