feat: 다운로드페이지-플랫폼 및 OS타입별 추가
This commit is contained in:
422
layers/components/atoms/Tooltip.vue
Normal file
422
layers/components/atoms/Tooltip.vue
Normal file
@@ -0,0 +1,422 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
type?: 'click' | 'hover'
|
||||
position?:
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'top-left'
|
||||
| 'top-right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-right'
|
||||
offset?: number
|
||||
backgroundColor?: string
|
||||
textColor?: string
|
||||
textAlign?: string
|
||||
fontSize?: string
|
||||
fontWeight?: string
|
||||
lineHeight?: string
|
||||
letterSpacing?: string
|
||||
arrow?: boolean
|
||||
teleport?: boolean
|
||||
content?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'click',
|
||||
position: 'top',
|
||||
offset: 8,
|
||||
arrow: true,
|
||||
teleport: false,
|
||||
backgroundColor: '#666666',
|
||||
textColor: '#FFFFFF',
|
||||
textAlign: 'center',
|
||||
fontSize: '12px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '18px',
|
||||
letterSpacing: '-0.24px',
|
||||
})
|
||||
|
||||
const isOpen = defineModel<boolean>('isOpen', { default: false })
|
||||
|
||||
const isLeftAligned = ref(false) // 브라우저 화면 기준으로 툴팁의 왼쪽 가장자리가 화면 밖으로 나가는 경우 좌측 정렬을 합니다.
|
||||
const isRightAligned = ref(false) // 브라우저 화면 기준으로 툴팁의 오른쪽 가장자리가 화면 밖으로 나가는 경우 우측 정렬을 합니다.
|
||||
const tooltipOffsetX = ref(18) // 툴팁 오프셋 X 값
|
||||
const tooltipArrowOffsetX = ref(26) // 툴팁 화살표 오프셋 X 값
|
||||
const tooltipPosition = ref({
|
||||
// 툴팁 위치 값
|
||||
top: null,
|
||||
bottom: null,
|
||||
left: null,
|
||||
right: null,
|
||||
})
|
||||
const tooltipTriggerRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const isPositions = computed(() => {
|
||||
return {
|
||||
isTop:
|
||||
props.position === 'top' && !isLeftAligned.value && !isRightAligned.value,
|
||||
isTopLeft:
|
||||
props.position === 'top-left' ||
|
||||
(props.position === 'top' && isRightAligned.value),
|
||||
isTopRight:
|
||||
props.position === 'top-right' ||
|
||||
(props.position === 'top' && isLeftAligned.value),
|
||||
isBottom:
|
||||
props.position === 'bottom' &&
|
||||
!isLeftAligned.value &&
|
||||
!isRightAligned.value,
|
||||
isBottomLeft:
|
||||
props.position === 'bottom-left' ||
|
||||
(props.position === 'bottom' && isRightAligned.value),
|
||||
isBottomRight:
|
||||
props.position === 'bottom-right' ||
|
||||
(props.position === 'bottom' && isLeftAligned.value),
|
||||
isLeft: props.position === 'left',
|
||||
isRight: props.position === 'right',
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 툴팁 초기화 함수입니다.
|
||||
*/
|
||||
const resetTooltipContent = () => {
|
||||
tooltipPosition.value.top = null
|
||||
tooltipPosition.value.bottom = null
|
||||
tooltipPosition.value.left = null
|
||||
tooltipPosition.value.right = null
|
||||
isOpen.value = false
|
||||
isLeftAligned.value = false
|
||||
isRightAligned.value = false
|
||||
tooltipTriggerRef.value = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 반응형 툴팁 위치 계산 함수입니다.
|
||||
* @param trigger 트리거 요소
|
||||
*/
|
||||
const calculateTooltipPosition = (trigger: HTMLElement) => {
|
||||
const triggerRect = trigger.getBoundingClientRect()
|
||||
const tooltipOffset = props.offset + 4 // 툴팁 오프셋
|
||||
const triggerCenterX = triggerRect.left + triggerRect.width / 2
|
||||
const triggerCenterY = triggerRect.top + triggerRect.height / 2
|
||||
|
||||
const isLefts =
|
||||
isPositions.value.isLeft ||
|
||||
isPositions.value.isTopLeft ||
|
||||
isPositions.value.isBottomLeft ||
|
||||
isLeftAligned.value
|
||||
const isOnlyLeft =
|
||||
isPositions.value.isLeft &&
|
||||
Object.values(isPositions.value).filter(Boolean).length === 1
|
||||
|
||||
const isRights =
|
||||
isPositions.value.isRight ||
|
||||
isPositions.value.isTopRight ||
|
||||
isPositions.value.isBottomRight ||
|
||||
isRightAligned.value
|
||||
const isOnlyRight =
|
||||
isPositions.value.isRight &&
|
||||
Object.values(isPositions.value).filter(Boolean).length === 1
|
||||
|
||||
let topPosition: number | null = null
|
||||
let bottomPosition: number | null = null
|
||||
let leftPosition: number | null = null
|
||||
let rightPosition: number | null = null
|
||||
|
||||
if (isLefts) {
|
||||
if (isOnlyLeft) {
|
||||
leftPosition = triggerRect.left - tooltipOffset
|
||||
} else {
|
||||
leftPosition = triggerRect.left + triggerRect.width + tooltipOffsetX.value
|
||||
}
|
||||
} else if (isRights) {
|
||||
if (isOnlyRight) {
|
||||
leftPosition = triggerRect.left + triggerRect.width + tooltipOffset
|
||||
} else {
|
||||
leftPosition = triggerRect.left - tooltipOffsetX.value
|
||||
}
|
||||
} else {
|
||||
leftPosition = triggerCenterX
|
||||
}
|
||||
|
||||
if (
|
||||
isPositions.value.isBottom ||
|
||||
isPositions.value.isBottomLeft ||
|
||||
isPositions.value.isBottomRight
|
||||
) {
|
||||
topPosition = triggerRect.top
|
||||
} else if (
|
||||
isPositions.value.isTop ||
|
||||
isPositions.value.isTopLeft ||
|
||||
isPositions.value.isTopRight
|
||||
) {
|
||||
topPosition = triggerRect.top - tooltipOffset
|
||||
} else {
|
||||
topPosition = triggerCenterY
|
||||
}
|
||||
|
||||
tooltipPosition.value = {
|
||||
top: topPosition,
|
||||
bottom: bottomPosition,
|
||||
left: leftPosition,
|
||||
right: rightPosition,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 좌측 정렬이 필요한지 확인 함수입니다.
|
||||
* 브라우저 화면 기준으로 툴팁의 왼쪽 가장자리가 화면 밖으로 나가는 경우 좌측 정렬을 합니다.
|
||||
*/
|
||||
const isValidLeftAligned = (trigger: HTMLElement): boolean => {
|
||||
const tooltipWidth = 280 // 툴팁 최대 너비
|
||||
const triggerRect = trigger.getBoundingClientRect()
|
||||
const tooltipOffset = props.offset + 4 // 툴팁 오프셋
|
||||
|
||||
// trigger의 중앙 위치 계산
|
||||
const triggerCenterX = triggerRect.left + triggerRect.width / 2
|
||||
|
||||
// 중앙 정렬 시 툴팁의 왼쪽 가장자리 위치
|
||||
const tooltipLeftEdge = triggerCenterX - tooltipWidth / 2
|
||||
|
||||
return tooltipLeftEdge < tooltipOffset
|
||||
}
|
||||
|
||||
/**
|
||||
* 우측 정렬이 필요한지 확인 함수입니다.
|
||||
* 브라우저 화면 기준으로 툴팁의 오른쪽 가장자리가 화면 밖으로 나가는 경우 우측 정렬을 합니다.
|
||||
*/
|
||||
const isValidRightAligned = (trigger: HTMLElement): boolean => {
|
||||
const tooltipWidth = 280 // 툴팁 최대 너비
|
||||
const triggerRect = trigger.getBoundingClientRect()
|
||||
const windowWidth = window.innerWidth
|
||||
const tooltipOffset = props.offset + 4 // 툴팁 오프셋
|
||||
|
||||
// trigger의 중앙 위치 계산
|
||||
const triggerCenterX = triggerRect.left + triggerRect.width / 2
|
||||
|
||||
// 중앙 정렬 시 툴팁의 오른쪽 가장자리 위치
|
||||
const tooltipRightEdge = triggerCenterX + tooltipWidth / 2
|
||||
|
||||
return tooltipRightEdge > windowWidth - tooltipOffset
|
||||
}
|
||||
|
||||
/**
|
||||
* 툴팁 표시 함수입니다.
|
||||
* @param event 이벤트 객체
|
||||
* @param text 툴팁 텍스트
|
||||
*/
|
||||
const handleTooltip = (event: MouseEvent, type: string) => {
|
||||
if (type !== props.type) {
|
||||
return
|
||||
}
|
||||
|
||||
const trigger = (event.currentTarget || event.target) as HTMLElement
|
||||
|
||||
isOpen.value = !isOpen.value
|
||||
|
||||
if (isOpen.value) {
|
||||
tooltipTriggerRef.value = trigger
|
||||
isLeftAligned.value = isValidLeftAligned(trigger)
|
||||
isRightAligned.value = isValidRightAligned(trigger)
|
||||
calculateTooltipPosition(trigger)
|
||||
} else {
|
||||
resetTooltipContent()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 브라우저 리사이즈 시 툴팁 위치 재계산 함수입니다.
|
||||
*/
|
||||
const handleResize = () => {
|
||||
if (isOpen.value && tooltipTriggerRef.value) {
|
||||
isLeftAligned.value = isValidLeftAligned(tooltipTriggerRef.value)
|
||||
isRightAligned.value = isValidRightAligned(tooltipTriggerRef.value)
|
||||
calculateTooltipPosition(tooltipTriggerRef.value)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="relative z-[100] inline-flex items-center justify-center"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@click="handleTooltip($event, 'click')"
|
||||
@mouseenter="handleTooltip($event, 'hover')"
|
||||
@mouseleave="handleTooltip($event, 'hover')"
|
||||
>
|
||||
<slot name="trigger" />
|
||||
|
||||
<template v-if="!$slots.trigger">
|
||||
<AtomsIconsStateInfoCircleLine :size="20" color="#7F7F7F" />
|
||||
</template>
|
||||
</button>
|
||||
<div
|
||||
v-if="!teleport && isOpen"
|
||||
:class="[
|
||||
'absolute z-[10] flex items-center justify-center w-[280px] py-[8px] px-[12px] rounded-[4px]',
|
||||
]"
|
||||
:style="[
|
||||
isPositions.isTop
|
||||
? `top: -${props.offset + 4}px; left: 50%; transform: translate(-50%, -100%)`
|
||||
: isPositions.isBottom
|
||||
? `bottom: -${props.offset + 4}px; left: 50%; transform: translate(-50%, 100%)`
|
||||
: isPositions.isLeft
|
||||
? `top: 50%; left: -${props.offset + 4}px; transform: translate(-100%, -50%)`
|
||||
: isPositions.isRight
|
||||
? `top: 50%; right: -${props.offset + 4}px; transform: translate(100%, -50%)`
|
||||
: isPositions.isTopLeft
|
||||
? `top: -${props.offset + 4}px; right: -${tooltipOffsetX}px; transform: translate(0, -100%)`
|
||||
: isPositions.isTopRight
|
||||
? `top: -${props.offset + 4}px; left: -${tooltipOffsetX}px; transform: translate(0, -100%)`
|
||||
: isPositions.isBottomLeft
|
||||
? `bottom: -${props.offset + 4}px; right: -${tooltipOffsetX}px; transform: translate(0, 100%)`
|
||||
: isPositions.isBottomRight
|
||||
? `bottom: -${props.offset + 4}px; left: -${tooltipOffsetX}px; transform: translate(0, 100%)`
|
||||
: '',
|
||||
`background-color: ${backgroundColor};`,
|
||||
]"
|
||||
>
|
||||
<slot name="panel" />
|
||||
|
||||
<p
|
||||
v-if="!$slots.panel"
|
||||
v-dompurify-html="props.content"
|
||||
:class="[
|
||||
`relative flex items-center justify-center w-full text-${props.textAlign} text-[${props.fontSize}] font-[${props.fontWeight}] leading-[${props.lineHeight}] tracking-[${props.letterSpacing}]`,
|
||||
]"
|
||||
:style="[`color: ${props.textColor};`]"
|
||||
></p>
|
||||
|
||||
<span
|
||||
v-if="arrow"
|
||||
class="absolute"
|
||||
:style="[
|
||||
isPositions.isTop
|
||||
? `bottom: -4px; left: 50%; transform: translate(-50%, 0)`
|
||||
: isPositions.isBottom
|
||||
? `top: -4px; left: 50%; transform: translate(-50%, 0) rotate(180deg)`
|
||||
: isPositions.isLeft
|
||||
? `right: -4px; top: 50%; transform: translate(0, -50%) rotate(-90deg)`
|
||||
: isPositions.isRight
|
||||
? `left: -4px; top: 50%; transform: translate(0, -50%) rotate(90deg)`
|
||||
: isPositions.isTopLeft
|
||||
? `bottom: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
|
||||
: isPositions.isTopRight
|
||||
? `bottom: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
|
||||
: isPositions.isBottomLeft
|
||||
? `top: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
|
||||
: isPositions.isBottomRight
|
||||
? `top: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
|
||||
: '',
|
||||
]"
|
||||
>
|
||||
<svg
|
||||
width="6"
|
||||
height="4"
|
||||
viewBox="0 0 6 4"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.2 2.93333L0 0H6L3.8 2.93333C3.4 3.46667 2.6 3.46667 2.2 2.93333Z"
|
||||
:fill="backgroundColor"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ClientOnly>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="teleport && isOpen"
|
||||
:class="[
|
||||
'absolute z-[100] flex items-center justify-center w-[280px] py-[8px] px-[12px] rounded-[4px] text-center text-[12px] font-[400] leading-[18px] tracking-[-0.24px] sm:w-auto sm:max-w-[280px]',
|
||||
]"
|
||||
:style="[
|
||||
isPositions.isTop
|
||||
? `transform: translate(-50%, -100%)`
|
||||
: isPositions.isBottom
|
||||
? `transform: translate(-50%, 100%)`
|
||||
: isPositions.isLeft
|
||||
? `transform: translate(0, -50%)`
|
||||
: isPositions.isRight
|
||||
? `transform: translate(0, -50%)`
|
||||
: isPositions.isTopLeft
|
||||
? `transform: translate(-100%, -100%)`
|
||||
: isPositions.isTopRight
|
||||
? `transform: translate(0, -100%)`
|
||||
: isPositions.isBottomLeft
|
||||
? `transform: translate(-100%, 100%)`
|
||||
: isPositions.isBottomRight
|
||||
? `transform: translate(0, 100%)`
|
||||
: '',
|
||||
`top: ${tooltipPosition.top !== null ? `${tooltipPosition.top}px` : 'auto'}; bottom: ${tooltipPosition.bottom !== null ? `${tooltipPosition.bottom}px` : 'auto'}; left: ${tooltipPosition.left !== null ? `${tooltipPosition.left}px` : 'auto'}; right: ${tooltipPosition.right !== null ? `${tooltipPosition.right}px` : 'auto'}; background-color: ${backgroundColor};`,
|
||||
]"
|
||||
>
|
||||
<slot name="panel" />
|
||||
|
||||
<p
|
||||
v-if="!$slots.panel"
|
||||
v-dompurify-html="props.content"
|
||||
:class="[
|
||||
`relative flex items-center justify-center w-full text-${props.textAlign} text-[${props.fontSize}] font-[${props.fontWeight}] leading-[${props.lineHeight}] tracking-[${props.letterSpacing}]`,
|
||||
]"
|
||||
:style="[`color: ${props.textColor};`]"
|
||||
></p>
|
||||
|
||||
<span
|
||||
v-if="arrow"
|
||||
class="absolute"
|
||||
:style="[
|
||||
isPositions.isTop
|
||||
? `bottom: -4px; left: 50%; transform: translate(-50%, 0)`
|
||||
: isPositions.isBottom
|
||||
? `top: -4px; left: 50%; transform: translate(-50%, 0) rotate(180deg)`
|
||||
: isPositions.isLeft
|
||||
? `right: -4px; top: 50%; transform: translate(0, -50%) rotate(-90deg)`
|
||||
: isPositions.isRight
|
||||
? `left: -4px; top: 50%; transform: translate(0, -50%) rotate(90deg)`
|
||||
: isPositions.isTopLeft
|
||||
? `bottom: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
|
||||
: isPositions.isTopRight
|
||||
? `bottom: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0)`
|
||||
: isPositions.isBottomLeft
|
||||
? `top: -4px; right: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
|
||||
: isPositions.isBottomRight
|
||||
? `top: -4px; left: ${tooltipArrowOffsetX}px; transform: translate(0, 0) rotate(180deg)`
|
||||
: '',
|
||||
]"
|
||||
>
|
||||
<svg
|
||||
width="6"
|
||||
height="4"
|
||||
viewBox="0 0 6 4"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.2 2.93333L0 0H6L3.8 2.93333C3.4 3.46667 2.6 3.46667 2.2 2.93333Z"
|
||||
:fill="backgroundColor"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</Teleport>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
36
layers/components/atoms/icons/StateInfoCircleLine.vue
Normal file
36
layers/components/atoms/icons/StateInfoCircleLine.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 12,
|
||||
color: 'var(--foreground-gray-500)',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M8.75 13.3333C8.28976 13.3333 7.91667 12.9602 7.91667 12.5L7.91667 8.125C7.91667 7.66476 8.28976 7.29167 8.75 7.29167C9.21024 7.29167 9.58333 7.66476 9.58333 8.125V12.5C9.58333 12.9602 9.21024 13.3333 8.75 13.3333Z"
|
||||
:fill="color"
|
||||
/>
|
||||
<path
|
||||
d="M8.75 4.21875C8.14594 4.21875 7.65625 4.70844 7.65625 5.3125C7.65625 5.91656 8.14594 6.40625 8.75 6.40625C9.35406 6.40625 9.84375 5.91656 9.84375 5.3125C9.84375 4.70844 9.35406 4.21875 8.75 4.21875Z"
|
||||
:fill="color"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M17.5 8.75C17.5 13.5825 13.5825 17.5 8.75 17.5C3.91751 17.5 0 13.5825 0 8.75C0 3.91751 3.91751 0 8.75 0C13.5825 0 17.5 3.91751 17.5 8.75ZM8.75 15.8333C12.662 15.8333 15.8333 12.662 15.8333 8.75C15.8333 4.83798 12.662 1.66667 8.75 1.66667C4.83798 1.66667 1.66667 4.83798 1.66667 8.75C1.66667 12.662 4.83798 15.8333 8.75 15.8333Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { SplideSlide } from '@splidejs/vue-splide'
|
||||
import { getComponentGroup, getComponentGroupAry } from '#layers/utils/dataUtil'
|
||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
||||
import type { Platform } from '#layers/types/components/button'
|
||||
@@ -16,6 +15,8 @@ const props = defineProps<Props>()
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
|
||||
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_download.json'
|
||||
const stoveClientDownloadUrl = runtimeConfig.public
|
||||
.stoveClientDownloadUrl as string
|
||||
|
||||
// Multilingual
|
||||
const resultGetMultilingual = await useGetMultilingual({
|
||||
@@ -70,13 +71,6 @@ const mobileSpecArray = ref<Array<string>>(['Android', 'Ios'])
|
||||
const mobileOSArray = ref<Array<string>>(['AOS', 'iOS'])
|
||||
|
||||
// Computed
|
||||
const platformList = computed(() => {
|
||||
if (breakpoints.value.isMobile) {
|
||||
return ['MOBILE', 'PC', 'STOVE']
|
||||
} else {
|
||||
return ['PC', 'STOVE', 'MOBILE']
|
||||
}
|
||||
})
|
||||
const driverList = computed(() =>
|
||||
driverArray.value.map(driver => ({
|
||||
id: `DRIVER_${driver}`,
|
||||
@@ -103,6 +97,7 @@ const mobileSpecList = computed(() =>
|
||||
const mobileOSList = computed(() =>
|
||||
mobileOSArray.value.map(os => ({
|
||||
id: `MO_OS_${os}`,
|
||||
osType: tm(`Download_${os}_Type`),
|
||||
osCode: os,
|
||||
osText: tm(`Download_${os}_OS`),
|
||||
platformCode: tm(`Download_${os}_Platform`),
|
||||
@@ -141,116 +136,164 @@ const handleMoveFocus = (target: 'pc' | 'mobile') => {
|
||||
<section class="section-static">
|
||||
<WidgetsFixSubTitle :title="tm('Download_Section_Platform_Title')" />
|
||||
|
||||
<BlocksSlideDefault
|
||||
:per-page="platformList.length"
|
||||
:gap="20"
|
||||
:arrows="false"
|
||||
:pagination="false"
|
||||
:drag="false"
|
||||
:breakpoints="{
|
||||
1023: {
|
||||
perPage: 'auto',
|
||||
gap: 12,
|
||||
focus: 0,
|
||||
drag: true,
|
||||
padding: { left: 0, right: 0 },
|
||||
},
|
||||
}"
|
||||
class="min-w-[320px] w-[100vw] px-[20px] ml-[-20px] sm:px-[40px] sm:ml-[-40px] md:w-full md:px-0 md:ml-0"
|
||||
<div
|
||||
class="relative flex flex-col-reverse sm:flex-row-reverse items-stretch justify-center gap-[20px] w-full md:flex-row"
|
||||
>
|
||||
<SplideSlide
|
||||
v-for="platform in platformList"
|
||||
:key="platform"
|
||||
class="flex flex-col items-center justify-between shrink-0 whitespace-normal w-[295px] h-[280px] bg-[#FFFFFF] p-[20px] rounded-[12px] text-left md:w-[calc((100%-40px)/3)] md:h-[314px] md:p-[24px] md:rounded-[16px] lg:w-[420px] lg:h-[340px] lg:p-[32px]"
|
||||
<div
|
||||
v-if="gameData?.platform_type !== '2'"
|
||||
class="relative flex flex-1 flex-col items-start justify-start h-[270px] py-[20px] rounded-[12px] bg-white sm:h-auto md:flex-[2] md:pt-[24px] md:pb-0 lg:pt-[32px]"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col items-start justify-start gap-[8px] w-full md:gap-[12px]"
|
||||
class="relative flex flex-col items-start justify-start w-full max-w-[800px] h-full px-[20px] mx-auto md:h-auto md:px-[28px] lg:px-[40px]"
|
||||
>
|
||||
<h4
|
||||
class="relative flex justify-left items-center w-full text-left text-[#1F1F1F] text-[18px] font-bold leading-[26px] tracking-[-0.54px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
|
||||
<span
|
||||
v-if="!breakpoints.isMobile"
|
||||
class="absolute top-0 right-[28px] w-[212px] h-[212px] lg:right-[40px]"
|
||||
>
|
||||
<span>{{ tm(`Download_Box_${platform}_Title`) }}</span>
|
||||
<img
|
||||
:src="
|
||||
getImageHost('/images/common/img_desktop.png', {
|
||||
imageType: 'common',
|
||||
})
|
||||
"
|
||||
:alt="tm('Download_Box_PC_Title')"
|
||||
loading="lazy"
|
||||
draggable="false"
|
||||
class="w-full h-full object-contain"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<h4
|
||||
class="relative flex justify-left items-center w-full gap-[4px] text-left text-[#1F1F1F] text-[18px] font-bold leading-[26px] tracking-[-0.54px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
|
||||
>
|
||||
<span>{{ tm('Download_Box_PC_Title') }}</span>
|
||||
|
||||
<AtomsTooltip
|
||||
position="top"
|
||||
:offset="8"
|
||||
background-color="#666666"
|
||||
text-color="#FFFFFF"
|
||||
:arrow="true"
|
||||
:content="tm('Download_Box_PC_Tooltip')"
|
||||
/>
|
||||
</h4>
|
||||
|
||||
<p
|
||||
v-if="
|
||||
tm(`Download_Box_${platform}_Description_List`).length === 1
|
||||
"
|
||||
v-dompurify-html="tm(`Download_Box_${platform}_Description01`)"
|
||||
class="text-[#999999] text-[14px] font-[400] leading-[24px] tracking-[-0.42px] md:text-[15px] md:tracking-[-0.45px]"
|
||||
></p>
|
||||
<ul
|
||||
v-else-if="
|
||||
tm(`Download_Box_${platform}_Description_List`).length > 1
|
||||
"
|
||||
<template
|
||||
v-for="(description, dIndex) in tm(
|
||||
'Download_Box_PC_Description_List'
|
||||
)"
|
||||
:key="dIndex"
|
||||
>
|
||||
<li
|
||||
v-for="description in tm(
|
||||
`Download_Box_${platform}_Description_List`
|
||||
)"
|
||||
:key="description"
|
||||
v-dompurify-html="tm(description)"
|
||||
class="relative pl-[22px] before:content-[''] before:absolute before:top-[10px] before:left-[9px] before:w-[3px] before:h-[3px] before:rounded-full before:bg-[#999999] text-[#999999] text-[14px] font-[400] leading-[24px] tracking-[-0.42px] md:text-[15px] md:tracking-[-0.45px]"
|
||||
></li>
|
||||
</ul>
|
||||
<p
|
||||
class="relative flex items-center justify-start w-full mt-[12px] text-left text-[#999999] text-[14px] font-[400] leading-[24px] tracking-[-0.42px] md:text-[15px] md:tracking-[-0.45px]"
|
||||
v-dompurify-html="tm(description as string)"
|
||||
></p>
|
||||
</template>
|
||||
|
||||
<AtomsButton
|
||||
v-if="platform !== 'STOVE'"
|
||||
type="action"
|
||||
button-size="size-small"
|
||||
background-color="transparent"
|
||||
text-color="#1F1F1F"
|
||||
class="relative w-auto h-auto px-0 text-[16px] font-[500] leading-[24px] tracking-[-0.48px] before:content-[''] before:absolute before:z-[2] before:top-p before:left-0 before:w-full before:h-full before:bg-[#FFFFFF] before:transition-opacity before:duration-300 before:ease-in-out before:opacity-0 hover:before:opacity-20"
|
||||
@click="
|
||||
handleMoveFocus(platform.toLowerCase() as 'pc' | 'mobile')
|
||||
"
|
||||
class="relative w-auto h-auto px-0 mt-[12px] text-[16px] font-[500] leading-[24px] tracking-[-0.48px] before:content-[''] before:absolute before:z-[2] before:top-p before:left-0 before:w-full before:h-full before:bg-[#FFFFFF] before:transition-opacity before:duration-300 before:ease-in-out before:opacity-0 hover:before:opacity-20"
|
||||
@click="handleMoveFocus('pc')"
|
||||
>
|
||||
<span>{{ tm(`Download_Box_${platform}_SpecCheck`) }}</span>
|
||||
<span>{{ tm('Download_Box_PC_SpecCheck') }}</span>
|
||||
<AtomsIconsLongArrowRightLine
|
||||
:size="20"
|
||||
color="#1F1F1F"
|
||||
class="relative rotate-90"
|
||||
/>
|
||||
</AtomsButton>
|
||||
|
||||
<div
|
||||
v-if="breakpoints.isMobile"
|
||||
class="relative flex items-center justify-center w-full h-auto min-h-[48px] py-[14px] px-[40px] mt-[48px] border border-solid border-[rgba(0,0,0,0.1)] rounded-[8px] bg-[#EBEBEB] text-center text-[#999999] text-[14px] font-[500] leading-[20px] tracking-[-0.42px]"
|
||||
>
|
||||
<span>{{ tm('Download_Button_PC_Mobile') }}</span>
|
||||
</div>
|
||||
|
||||
<BlocksButtonLauncher
|
||||
v-else-if="breakpoints.isMd || breakpoints.isDesktop"
|
||||
platform="pc"
|
||||
class="!w-full !max-w-[300px] mt-[32px] lg:mt-[48px]"
|
||||
>
|
||||
<span>{{ tm('Download_Button_PC') }}</span>
|
||||
</BlocksButtonLauncher>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center justify-center gap-[8px] w-full md:gap-[12px]"
|
||||
v-if="!breakpoints.isMobile"
|
||||
class="relative flex items-center justify-center w-full p-[25px] mt-[24px] rounded-b-[12px] bg-[#FAFAFA] lg:mt-[32px]"
|
||||
>
|
||||
<template v-if="platform === 'MOBILE'">
|
||||
<p
|
||||
class="relative flex items-center justify-center w-full gap-[8px] text-[#999999] text-[16px] font-[400] leading-[26px] tracking-[-0.48px]"
|
||||
>
|
||||
<span>{{ tm('Download_Text_Not_STOVE_Client') }}</span>
|
||||
<NuxtLink
|
||||
:href="stoveClientDownloadUrl"
|
||||
target="_self"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-start gap-[4px] text-[#3C75FF] text-[16px] font-[500] reading-[24px] tracking-[-0.48px]"
|
||||
>
|
||||
<span>{{ tm('Download_Button_STOVE') }}</span>
|
||||
<AtomsIconsDownloadLine color="#3C75FF" />
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="gameData?.platform_type !== '1'"
|
||||
class="relative flex flex-1 flex-col items-start justify-start h-[270px] p-[20px] rounded-[12px] bg-white sm:h-auto md:py-[24px] md:px-[28px] lg:pt-[32px] lg:pb-[40px] lg:px-[40px]"
|
||||
>
|
||||
<div
|
||||
class="relative flex flex-col items-start justify-start w-full max-w-[800px] mx-auto"
|
||||
>
|
||||
<h4
|
||||
class="relative flex justify-left items-center w-full text-left text-[#1F1F1F] text-[18px] font-bold leading-[26px] tracking-[-0.54px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
|
||||
>
|
||||
<span>{{ tm('Download_Box_MOBILE_Title') }}</span>
|
||||
</h4>
|
||||
|
||||
<AtomsButton
|
||||
type="action"
|
||||
button-size="size-small"
|
||||
background-color="transparent"
|
||||
text-color="#1F1F1F"
|
||||
class="relative w-auto h-auto px-0 mt-[8px] mb-[48px] text-[16px] font-[500] leading-[24px] tracking-[-0.48px] before:content-[''] before:absolute before:z-[2] before:top-p before:left-0 before:w-full before:h-full before:bg-[#FFFFFF] before:transition-opacity before:duration-300 before:ease-in-out before:opacity-0 hover:before:opacity-20 md:mt-[12px]"
|
||||
@click="handleMoveFocus('mobile')"
|
||||
>
|
||||
<span>{{ tm('Download_Box_MOBILE_SpecCheck') }}</span>
|
||||
<AtomsIconsLongArrowRightLine
|
||||
:size="20"
|
||||
color="#1F1F1F"
|
||||
class="relative rotate-90"
|
||||
/>
|
||||
</AtomsButton>
|
||||
|
||||
<div
|
||||
class="relative flex flex-col items-start justify-start w-full gap-[8px] mt-auto md:gap-[12px]"
|
||||
>
|
||||
<template v-for="os in mobileOSList" :key="os.id">
|
||||
<BlocksButtonLauncher
|
||||
v-if="device.isMobile ? os.isValue : true"
|
||||
v-if="
|
||||
device.isMobile
|
||||
? os.isValue
|
||||
: gameData?.os_type !== '3'
|
||||
? gameData?.os_type === os.osType
|
||||
: true
|
||||
"
|
||||
:platform="`${os.platformCode as Platform}`"
|
||||
class="!w-full"
|
||||
>
|
||||
<span>{{ os.platformText }}</span>
|
||||
</BlocksButtonLauncher>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<AtomsButton
|
||||
v-if="breakpoints.isMobile"
|
||||
type="action"
|
||||
button-size="size-small"
|
||||
background-color="#EBEBEB"
|
||||
text-color="#999999"
|
||||
:disabled="true"
|
||||
class="w-full px-0 border border-solid border-[rgba(0,0,0,0.1)] cursor-default"
|
||||
>
|
||||
<span>{{ tm(`Download_Button_${platform}_Mobile`) }}</span>
|
||||
</AtomsButton>
|
||||
<BlocksButtonLauncher
|
||||
v-else-if="breakpoints.isMd || breakpoints.isDesktop"
|
||||
:platform="`${platform.toLowerCase() as Platform}`"
|
||||
class="!w-full"
|
||||
>
|
||||
<span>{{ tm(`Download_Button_${platform}`) }}</span>
|
||||
</BlocksButtonLauncher>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</SplideSlide>
|
||||
</BlocksSlideDefault>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section ref="specPCRef" class="section-static">
|
||||
@@ -426,4 +469,8 @@ table td {
|
||||
.splide :deep(.splide__track) {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
::v-deep([data-platform='stove']) .icon-platform {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
public/images/common/img_desktop.png
Normal file
BIN
public/images/common/img_desktop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
Reference in New Issue
Block a user