fix. [SWV-798] GNB SNS 버튼 직관성 및 시인성 향상
@@ -1,167 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import type { TrackingObject, ColorObject } from '#layers/types/api/common'
|
||||
|
||||
const showSnsList = ref(false)
|
||||
const SNS_KEY_ORDER = [
|
||||
'kakao',
|
||||
'twitter',
|
||||
'discord',
|
||||
'youtube',
|
||||
'instagram',
|
||||
'facebook',
|
||||
'tiktok',
|
||||
] as const
|
||||
const SNS_KEY_RANK: Record<string, number> = Object.fromEntries(
|
||||
SNS_KEY_ORDER.map((key, index) => [key, index])
|
||||
)
|
||||
const SNS_DISPLAY_LIMIT = 3
|
||||
|
||||
const { locale, tm } = useI18n()
|
||||
const gameDataStore = useGameDataStore()
|
||||
const modalStore = useModalStore()
|
||||
const { sendLog } = useAnalytics()
|
||||
|
||||
const { gameData, snsJson } = storeToRefs(gameDataStore)
|
||||
const { snsJson } = storeToRefs(gameDataStore)
|
||||
const { handleOpenToast } = modalStore
|
||||
|
||||
const analytics = {
|
||||
action_type: 'click',
|
||||
click_sarea: 'SNS',
|
||||
} as TrackingObject
|
||||
}
|
||||
|
||||
const snsBackgroundColor = computed(() => {
|
||||
const colorData = gameData.value?.comm_sns_bg_color_json
|
||||
?.display as ColorObject
|
||||
const colorCode = getColorCodeFromData(colorData, 'none')
|
||||
return colorCode
|
||||
const isExpanded = ref(true)
|
||||
|
||||
const hasSnsData = computed(() => Object.keys(snsJson.value).length > 0)
|
||||
const enabledSnsKeys = computed(() => {
|
||||
const keys = Object.keys(snsJson.value).filter(
|
||||
key => snsJson.value[key].use_yn === 1 && snsJson.value[key].url
|
||||
)
|
||||
|
||||
return keys.sort((a, b) => {
|
||||
const aRank = SNS_KEY_RANK[a] ?? Number.POSITIVE_INFINITY
|
||||
const bRank = SNS_KEY_RANK[b] ?? Number.POSITIVE_INFINITY
|
||||
return aRank - bRank
|
||||
})
|
||||
})
|
||||
const isMoreThanThree = computed(
|
||||
() => enabledSnsKeys.value.length > SNS_DISPLAY_LIMIT
|
||||
)
|
||||
|
||||
const getSnsButtonStyle = (key: string) => ({
|
||||
'--bg-default': `url(${formatPathHost(`/images/common/btn_sns/sns-${key}_default.png`, { imageType: 'public' })})`,
|
||||
'--bg-active': `url(${formatPathHost(`/images/common/btn_sns/sns-${key}_active.png`, { imageType: 'public' })})`,
|
||||
})
|
||||
|
||||
const handleControlForce = (state: boolean) => {
|
||||
showSnsList.value = state
|
||||
const handleSnsSendLog = (clickItem: string) => {
|
||||
sendLog(locale.value, { ...analytics, click_item: clickItem })
|
||||
}
|
||||
|
||||
const handleCopy = async () => {
|
||||
if (!import.meta.client) return
|
||||
|
||||
try {
|
||||
const url = window.location.href
|
||||
await navigator.clipboard.writeText(url)
|
||||
await navigator.clipboard.writeText(window.location.href)
|
||||
handleOpenToast({ contentText: tm('Alert_Copy_Complete') })
|
||||
sendLog(locale.value, { ...analytics, click_item: 'URL복사' })
|
||||
} catch (error) {
|
||||
console.error('[handleCopy] Error:', error)
|
||||
} catch {
|
||||
// clipboard API 실패 시 무시 (e.g. 보안 컨텍스트, 권한)
|
||||
}
|
||||
}
|
||||
|
||||
const handleControlForce = () => {
|
||||
isExpanded.value = !isExpanded.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="Object.keys(snsJson).length > 0" class="sns-wrap">
|
||||
<transition name="fade">
|
||||
<AtomsButtonCircle
|
||||
v-show="!showSnsList"
|
||||
:background-color="snsBackgroundColor"
|
||||
sr-only="sns"
|
||||
class="btn-more"
|
||||
@click="handleControlForce(true)"
|
||||
<div v-if="hasSnsData" class="sns-wrap">
|
||||
<div :class="['sns-list', { 'is-expanded': isExpanded }]">
|
||||
<a
|
||||
v-for="key in enabledSnsKeys"
|
||||
:key="key"
|
||||
:href="snsJson[key].url"
|
||||
target="_blank"
|
||||
:class="`btn-sns ${key}`"
|
||||
:style="getSnsButtonStyle(key)"
|
||||
@click="handleSnsSendLog(key)"
|
||||
>
|
||||
<AtomsIconsShareLine class="icon-share" />
|
||||
</AtomsButtonCircle>
|
||||
</transition>
|
||||
<transition name="fade">
|
||||
<div v-show="showSnsList" class="sns-list">
|
||||
<template v-for="(item, key) in snsJson" :key="key">
|
||||
<AtomsButtonCircle
|
||||
v-if="item.use_yn === 1 && item.url"
|
||||
type="external"
|
||||
:href="item.url"
|
||||
:sr-only="key"
|
||||
:class="['btn-sns', key]"
|
||||
@click="sendLog(locale, { ...analytics, click_item: key })"
|
||||
>
|
||||
<img
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="
|
||||
formatPathHost(`/images/common/ic-v2-logo-${key}-fill.png`, {
|
||||
imageType: 'public',
|
||||
})
|
||||
"
|
||||
:alt="key"
|
||||
/>
|
||||
</AtomsButtonCircle>
|
||||
</template>
|
||||
<AtomsButtonCircle
|
||||
type="action"
|
||||
sr-only="copy"
|
||||
class="btn-sns link"
|
||||
@click="handleCopy"
|
||||
>
|
||||
<img
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="
|
||||
formatPathHost('/images/common/ic-v2-community-link-line.png', {
|
||||
imageType: 'public',
|
||||
})
|
||||
"
|
||||
alt="copy"
|
||||
/>
|
||||
</AtomsButtonCircle>
|
||||
<div class="close-container">
|
||||
<span class="sr-only">{{ key }}</span>
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="opacity-50 z-[1] hover:opacity-100"
|
||||
@click="handleControlForce(false)"
|
||||
sr-only="copy"
|
||||
class="btn-sns community"
|
||||
:style="getSnsButtonStyle('community')"
|
||||
@click="handleCopy"
|
||||
>
|
||||
<span class="sr-only">close</span>
|
||||
<AtomsIconsCloseLine size="24" />
|
||||
<span class="sr-only">copy</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<AtomsButtonCircle
|
||||
v-if="isMoreThanThree"
|
||||
:sr-only="isExpanded ? 'Collapse SNS links' : 'Expand SNS links'"
|
||||
class="btn-control"
|
||||
:class="{ 'is-expanded': isExpanded }"
|
||||
@click="handleControlForce"
|
||||
>
|
||||
<AtomsIconsArrowRightLine color="rgba(255,255,255,0.7)" />
|
||||
</AtomsButtonCircle>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sns-wrap {
|
||||
@apply fixed bottom-3 left-3 h-[40px] md:bottom-8 md:left-8 md:h-[48px] z-[100];
|
||||
}
|
||||
|
||||
.btn-more:hover .icon-share {
|
||||
@apply fill-white;
|
||||
}
|
||||
.btn-sns {
|
||||
@apply bg-center bg-no-repeat bg-[rgba(255,255,255,0.6)] after:hidden;
|
||||
}
|
||||
.btn-sns.kakao:hover {
|
||||
@apply bg-[#FAE100];
|
||||
}
|
||||
.btn-sns.kakao:hover img {
|
||||
filter: brightness(0) saturate(100%) invert(13%) sepia(2%) saturate(0%)
|
||||
hue-rotate(0deg) brightness(100%) contrast(100%);
|
||||
}
|
||||
.btn-sns.tiktok:hover {
|
||||
@apply bg-[#000];
|
||||
}
|
||||
.btn-sns.discord:hover {
|
||||
@apply bg-[#000];
|
||||
}
|
||||
.btn-sns.twitter:hover {
|
||||
@apply bg-[#000];
|
||||
}
|
||||
.btn-sns.youtube:hover {
|
||||
@apply bg-[#FF0000];
|
||||
}
|
||||
.btn-sns.facebook:hover {
|
||||
@apply bg-[#1977F2];
|
||||
}
|
||||
.btn-sns.instagram:hover {
|
||||
@apply bg-[#000];
|
||||
}
|
||||
.btn-sns.link:hover {
|
||||
@apply bg-[#000];
|
||||
@apply fixed bottom-3 left-3 flex gap-1.5 h-[40px] z-[100] sm:bottom-5 sm:left-5 md:bottom-8 md:left-8 md:h-[48px] md:gap-2;
|
||||
}
|
||||
|
||||
.sns-list {
|
||||
@apply absolute bottom-0 left-0 flex items-center justify-center gap-4 rounded-full h-full pl-4 pr-3;
|
||||
@apply overflow-hidden relative flex items-center gap-1.5 rounded-full max-w-[110px] p-2.5 md:gap-2 md:p-3 md:max-w-[136px] transition-[max-width] duration-300
|
||||
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-[rgba(255,255,255,0.06)] before:rounded-full;
|
||||
}
|
||||
.sns-item {
|
||||
@apply w-[24px] h-[24px] bg-center bg-cover bg-no-repeat opacity-50 z-[1]
|
||||
hover:opacity-100;
|
||||
|
||||
.sns-list.is-expanded {
|
||||
@apply max-w-[1000px];
|
||||
}
|
||||
.sns-item:hover {
|
||||
|
||||
.btn-sns {
|
||||
@apply relative flex items-center justify-center shrink-0 rounded-full w-[26px] h-[26px] bg-center bg-no-repeat bg-contain
|
||||
md:w-8 md:h-8;
|
||||
background-image: var(--bg-default);
|
||||
}
|
||||
.btn-sns::after {
|
||||
@apply content-[''] absolute inset-0 rounded-full bg-center bg-no-repeat bg-contain opacity-0 pointer-events-none transition-opacity duration-300;
|
||||
background-image: var(--bg-active);
|
||||
}
|
||||
.btn-sns:hover::after {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.close-container {
|
||||
@apply relative flex pl-4
|
||||
before:content-[''] before:absolute before:top-1/2 before:left-0 before:w-[1px] before:h-[20px] before:bg-[rgba(255,255,255,0.1)] before:translate-y-[-50%];
|
||||
.btn-control.is-expanded {
|
||||
@apply rotate-180;
|
||||
}
|
||||
.btn-control:hover :deep(.icon) {
|
||||
@apply translate-x-[3px];
|
||||
}
|
||||
|
||||
[data-theme='dark'] .sns-list,
|
||||
[data-theme='dark'] .btn-control {
|
||||
@apply bg-[rgba(0,0,0,0.2)] backdrop-blur-[15px];
|
||||
}
|
||||
|
||||
[data-theme='light'] .sns-list,
|
||||
[data-theme='light'] .btn-control {
|
||||
@apply bg-[rgba(255,255,255,0.1)] backdrop-blur-[15px];
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -114,7 +114,7 @@ onMounted(async () => {
|
||||
@apply mt-[var(--scroll-position,48px)];
|
||||
}
|
||||
.sns-wrap ~ .navigation-wrap {
|
||||
@apply pb-[78px];
|
||||
@apply pb-[60px] md:pb-[80px];
|
||||
}
|
||||
|
||||
.navigation-wrap {
|
||||
@@ -146,7 +146,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.navigation-wrap.is-closed {
|
||||
@apply translate-x-[calc(-100%+20px)] sm:translate-x-[calc(-100%+40px)];
|
||||
@apply translate-x-[calc(-100%+12px)] sm:translate-x-[calc(-100%+20px)] md:translate-x-[calc(-100%+32px)];
|
||||
}
|
||||
.navigation-wrap.is-closed .btn-control {
|
||||
@apply rotate-0;
|
||||
|
||||
@@ -29,9 +29,6 @@ export interface GameDataValue {
|
||||
design_theme: number
|
||||
lang_codes: string[]
|
||||
key_color_json: GameDataKeyColors
|
||||
comm_sns_bg_color_json: {
|
||||
display: ColorObject
|
||||
}
|
||||
comm_multilang_filename?: string
|
||||
footer_dev_ci_img_yn: boolean
|
||||
footer_dev_ci_img_path: string
|
||||
|
||||
BIN
public/images/common/btn_sns/sns-community_active.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/images/common/btn_sns/sns-community_default.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/images/common/btn_sns/sns-discord_active.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/images/common/btn_sns/sns-discord_default.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/images/common/btn_sns/sns-facebook_active.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/images/common/btn_sns/sns-facebook_default.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/common/btn_sns/sns-instagram_active.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/images/common/btn_sns/sns-instagram_default.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/images/common/btn_sns/sns-kakao_active.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/images/common/btn_sns/sns-kakao_default.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/images/common/btn_sns/sns-tiktok_active.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/images/common/btn_sns/sns-tiktok_default.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/images/common/btn_sns/sns-twitter_active.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/common/btn_sns/sns-twitter_default.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/images/common/btn_sns/sns-youtube_active.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/images/common/btn_sns/sns-youtube_default.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 387 B |
|
Before Width: | Height: | Size: 389 B |
|
Before Width: | Height: | Size: 293 B |
|
Before Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 348 B |
|
Before Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 373 B |
|
Before Width: | Height: | Size: 370 B |