282 lines
7.5 KiB
Vue
282 lines
7.5 KiB
Vue
<script setup lang="ts">
|
|
import type { CSSProperties, Component } from 'vue'
|
|
import type { PlatformTransformType } from '#layers/types/api/gameData'
|
|
import type {
|
|
DownloadButtonType,
|
|
ButtonVariant,
|
|
Platform,
|
|
} from '#layers/types/components/button'
|
|
|
|
interface Props {
|
|
platform: Platform
|
|
type?: DownloadButtonType
|
|
variant?: ButtonVariant
|
|
backgroundColor?: string
|
|
textColor?: string
|
|
iconComponent?: Component
|
|
iconProps?: Record<string, any>
|
|
disabled?: boolean
|
|
useGameFont?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
type: 'default',
|
|
variant: 'filled',
|
|
disabled: false,
|
|
useGameFont: false,
|
|
})
|
|
|
|
const runtimeConfig = useRuntimeConfig()
|
|
const { tm } = useI18n()
|
|
const device = useDevice()
|
|
const gameDataStore = useGameDataStore()
|
|
const modalStore = useModalStore()
|
|
const { isProcessing, validateLauncher } = useCheckGameStart()
|
|
|
|
const { gameName, platformType, osType, marketJson, fontFamily } =
|
|
storeToRefs(gameDataStore)
|
|
|
|
const PLATFORM_ICON_MAP: Record<Platform, string> = {
|
|
google_play: 'AtomsIconsLogoGoogle',
|
|
app_store: 'AtomsIconsLogoApple',
|
|
pc: 'AtomsIconsLogoWindow',
|
|
stove: 'AtomsIconsLogoStove',
|
|
} as const
|
|
|
|
const DUP_IMAGE_MAP: Record<Platform, string> = {
|
|
google_play: '/images/common/btn_launcher/btn_logo-google.svg',
|
|
app_store: '/images/common/btn_launcher/btn_logo-app.svg',
|
|
pc: '/images/common/btn_launcher/btn_logo-pc.svg',
|
|
stove: '/images/common/btn_launcher/btn_logo-stove.svg',
|
|
} as const
|
|
|
|
const componentTag = computed(() => {
|
|
if (props.platform === 'stove' && props.type !== 'duplication') {
|
|
return 'a'
|
|
}
|
|
return 'button'
|
|
})
|
|
const isSingle = computed(() => props.type === 'single')
|
|
const supportedPlatforms = computed(
|
|
() =>
|
|
getSupportedPlatforms(
|
|
platformType.value,
|
|
osType.value
|
|
) as PlatformTransformType[]
|
|
)
|
|
const platformIcon = computed(() => PLATFORM_ICON_MAP[props.platform])
|
|
const buttonStyle = computed<CSSProperties>(() => {
|
|
const style: CSSProperties = {}
|
|
|
|
if (props.backgroundColor) {
|
|
style.backgroundColor = props.backgroundColor
|
|
}
|
|
if (props.type === 'duplication') {
|
|
style.backgroundImage = `url(${formatPathHost(DUP_IMAGE_MAP[props.platform], { imageType: 'common' })})`
|
|
}
|
|
|
|
return style
|
|
})
|
|
const textStyle = computed<CSSProperties>(() => {
|
|
const style: CSSProperties = {}
|
|
if (props.textColor) {
|
|
style.color = props.textColor
|
|
}
|
|
if (props.useGameFont && fontFamily.value) {
|
|
style.fontFamily = fontFamily.value
|
|
}
|
|
|
|
return style
|
|
})
|
|
|
|
const highlight = (text: string) => `<span class="highlight">${text}</span>`
|
|
|
|
const tmWithGameName = (key: string): string => {
|
|
const raw = tm(key)
|
|
if (typeof raw !== 'string') return ''
|
|
|
|
const withName = raw.replace(/%게임명%/g, highlight(gameName.value))
|
|
const platformLines = supportedPlatforms.value
|
|
.map(platform => highlight(formatSnakeToTitle(platform)))
|
|
.filter(Boolean)
|
|
|
|
return platformLines.length
|
|
? `${withName}<br><br>${platformLines.join('<br>')}`
|
|
: withName
|
|
}
|
|
|
|
const handleClick = () => {
|
|
if (props.platform === 'stove') {
|
|
if (props.type === 'duplication') return
|
|
|
|
const stoveClientDownloadUrl = runtimeConfig.public.stoveClientDownloadUrl
|
|
location.href = stoveClientDownloadUrl
|
|
return
|
|
}
|
|
|
|
if (props.platform === 'pc') {
|
|
if (device.isDesktop && platformType.value !== '2') {
|
|
// 디바이스 pc 환경이면서 모바일 스토어 전용이 아니면 런처 실행
|
|
validateLauncher()
|
|
return
|
|
} else {
|
|
const target = device.isAndroid
|
|
? 'google_play'
|
|
: device.isApple
|
|
? 'app_store'
|
|
: null
|
|
|
|
if (!target || !supportedPlatforms.value.includes(target)) {
|
|
modalStore.handleOpenAlert({
|
|
contentText: tmWithGameName('Alert_Not_SupportedOS'),
|
|
})
|
|
return
|
|
}
|
|
|
|
const url = marketJson.value?.[target]?.url ?? ''
|
|
window.open(url, '_blank')
|
|
return
|
|
}
|
|
}
|
|
|
|
const url = marketJson.value?.[props.platform]?.url ?? ''
|
|
if (url) window.open(url, '_blank')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<component
|
|
:is="componentTag"
|
|
v-bind="$attrs"
|
|
:class="[
|
|
'btn-base',
|
|
props.type,
|
|
{ 'no-text': isSingle && !$slots.default },
|
|
]"
|
|
:data-variant="props.variant"
|
|
:data-platform="props.platform"
|
|
:style="buttonStyle"
|
|
:disabled="disabled || isProcessing"
|
|
@click="handleClick"
|
|
>
|
|
<span class="btn-content">
|
|
<component
|
|
:is="platformIcon"
|
|
v-if="props.type !== 'duplication'"
|
|
class="icon-platform"
|
|
/>
|
|
<span class="text" :style="textStyle">
|
|
<slot />
|
|
</span>
|
|
<component
|
|
:is="props.iconComponent"
|
|
v-if="props.iconComponent"
|
|
v-bind="props.iconProps"
|
|
/>
|
|
<span
|
|
v-if="props.platform === 'pc' && props.type === 'default'"
|
|
class="icon-download"
|
|
>
|
|
<AtomsIconsDownloadLine />
|
|
</span>
|
|
</span>
|
|
</component>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.btn-base {
|
|
@apply overflow-hidden inline-flex relative font-medium rounded-lg cursor-pointer;
|
|
}
|
|
.btn-content {
|
|
@apply relative flex items-center w-full z-[1];
|
|
}
|
|
.icon-platform {
|
|
@apply w-5 h-5 flex-shrink-0;
|
|
}
|
|
|
|
.btn-base[data-variant='filled'] {
|
|
@apply bg-[#383838] text-[#ffffff]
|
|
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10 before:rounded-lg
|
|
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0 after:rounded-lg
|
|
hover:after:opacity-20;
|
|
}
|
|
|
|
.btn-base[data-variant='outlined'] {
|
|
@apply bg-white text-[#1F1F1F]
|
|
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-black/10 before:rounded-lg;
|
|
}
|
|
.btn-base[data-variant='outlined'][data-platform='app_store'] svg,
|
|
.btn-base[data-variant='outlined'][data-platform='pc'] svg,
|
|
.btn-base[data-variant='outlined'][data-platform='stove'] svg {
|
|
@apply fill-[#1F1F1F];
|
|
}
|
|
|
|
/* default */
|
|
.btn-base.default {
|
|
@apply justify-center py-3.5 px-5
|
|
md:py-4 md:px-6;
|
|
}
|
|
.btn-base.default .btn-content {
|
|
@apply justify-center;
|
|
}
|
|
.btn-base.default .text {
|
|
@apply line-clamp-2 text-[14px]
|
|
md:text-[16px];
|
|
}
|
|
.btn-base.default .icon-platform + .text {
|
|
@apply pl-2;
|
|
}
|
|
.btn-base.default .icon-download {
|
|
@apply ml-auto pl-4;
|
|
}
|
|
|
|
.btn-base.default[data-variant='outlined'] .icon-download {
|
|
@apply border-black/10;
|
|
}
|
|
.btn-base.default[data-variant='outlined'] .icon-download svg {
|
|
@apply fill-[#1F1F1F];
|
|
}
|
|
|
|
/* duplication */
|
|
.btn-base.duplication {
|
|
@apply bg-[16px_50%] bg-[length:auto_28px] bg-no-repeat backdrop-blur-[15px]
|
|
pt-[25px] pl-[47px] pr-[22px] pb-[7px] text-[11px]
|
|
md:h-[64px] md:pt-[30px] md:pl-[64px] md:pr-[28px] md:pb-[11px] md:text-[12px] md:bg-[20px_50%] md:bg-[length:auto_40px];
|
|
}
|
|
.btn-base.duplication[data-platform='google_play'] {
|
|
@apply min-w-[148px] md:min-w-[194px];
|
|
}
|
|
.btn-base.duplication[data-platform='app_store'] {
|
|
@apply min-w-[132px] md:min-w-[174px];
|
|
}
|
|
.btn-base.duplication[data-platform='pc'] {
|
|
@apply min-w-[113px] md:min-w-[150px];
|
|
}
|
|
.btn-base.duplication[data-platform='stove'] {
|
|
@apply min-w-[113px] md:min-w-[150px];
|
|
}
|
|
.btn-base.duplication .text {
|
|
@apply line-clamp-1;
|
|
}
|
|
|
|
/* single */
|
|
.btn-base.single {
|
|
@apply justify-center items-center text-[14px]
|
|
h-[40px] px-3.5
|
|
md:h-[48px];
|
|
}
|
|
.btn-base.single .btn-content {
|
|
@apply justify-center;
|
|
}
|
|
.btn-base.single.no-text {
|
|
@apply min-w-[40px] px-0 md:min-w-[48px];
|
|
}
|
|
.btn-base.single.no-text .icon-platform {
|
|
@apply mx-auto;
|
|
}
|
|
|
|
.btn-base[data-variant='outlined'] {
|
|
@apply hover:before:border-[#999];
|
|
}
|
|
</style>
|