152 lines
4.3 KiB
Vue
152 lines
4.3 KiB
Vue
<script setup lang="ts">
|
|
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 { snsJson } = storeToRefs(gameDataStore)
|
|
const { handleOpenToast } = modalStore
|
|
|
|
const analytics = {
|
|
action_type: 'click',
|
|
click_sarea: 'SNS',
|
|
}
|
|
|
|
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 handleSnsSendLog = (clickItem: string) => {
|
|
sendLog(locale.value, { ...analytics, click_item: clickItem })
|
|
}
|
|
|
|
const handleCopy = async () => {
|
|
if (!import.meta.client) return
|
|
try {
|
|
await navigator.clipboard.writeText(window.location.href)
|
|
handleOpenToast({ contentText: tm('Alert_Copy_Complete') })
|
|
sendLog(locale.value, { ...analytics, click_item: 'URL복사' })
|
|
} catch {
|
|
// clipboard API 실패 시 무시 (e.g. 보안 컨텍스트, 권한)
|
|
}
|
|
}
|
|
|
|
const handleControlForce = () => {
|
|
isExpanded.value = !isExpanded.value
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<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)"
|
|
>
|
|
<span class="sr-only">{{ key }}</span>
|
|
</a>
|
|
<button
|
|
type="button"
|
|
sr-only="copy"
|
|
class="btn-sns community"
|
|
:style="getSnsButtonStyle('community')"
|
|
@click="handleCopy"
|
|
>
|
|
<span class="sr-only">copy</span>
|
|
</button>
|
|
</div>
|
|
<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 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 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-list.is-expanded {
|
|
@apply max-w-[1000px];
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.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>
|