fix. 템플릿 수정
This commit is contained in:
@@ -146,14 +146,14 @@ onBeforeUnmount(() => {
|
||||
<NuxtPage />
|
||||
|
||||
<!-- 공통 모달 컴포넌트 -->
|
||||
<AtomsModalYouTube
|
||||
<BlocksModalYouTube
|
||||
v-model:is-open="youtube.storeIsOpen"
|
||||
:youtube-url="youtube.storeYoutubeUrl"
|
||||
:is-outside-close="youtube.storeIsOutsideClose"
|
||||
:modal-name="youtube.storeModalName"
|
||||
@close-button-event="handleResetYoutube"
|
||||
/>
|
||||
<AtomsModalConfirm
|
||||
<BlocksModalConfirm
|
||||
v-model:is-open="confirm.storeIsOpen"
|
||||
:is-show-dimmed="confirm.storeIsShowDimmed"
|
||||
:content-text="confirm.storeContentText"
|
||||
@@ -164,7 +164,7 @@ onBeforeUnmount(() => {
|
||||
@confirm-button-event="confirm.storeConfirmButtonEvent"
|
||||
@cancel-button-event="confirm.storeCancelButtonEvent"
|
||||
/>
|
||||
<AtomsModalAlert
|
||||
<BlocksModalAlert
|
||||
v-model:is-open="alert.storeIsOpen"
|
||||
:is-show-dimmed="alert.storeIsShowDimmed"
|
||||
:content-text="alert.storeContentText"
|
||||
|
||||
@@ -16,11 +16,4 @@
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 라이트 테마 색상 */
|
||||
[data-theme='light'] {
|
||||
.main {
|
||||
@apply bg-theme-foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const handleScrollToTop = () => {
|
||||
|
||||
<style scoped>
|
||||
.btn-top {
|
||||
@apply relative rounded-full bg-[image:var(--button-top)] bg-center bg-cover bg-no-repeat z-[100]
|
||||
@apply relative rounded-full bg-[image:var(--button-top)] bg-center bg-cover bg-no-repeat
|
||||
w-[40px] h-[40px] md:w-[48px] md:h-[48px]
|
||||
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-full after:opacity-0 after:transition-all after:duration-300 after:ease-in-out;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ const handleClick = () => {
|
||||
|
||||
<ClientOnly>
|
||||
<Teleport to="#teleports">
|
||||
<BlocksModalClient />
|
||||
<WidgetsModalClient />
|
||||
</Teleport>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
@@ -72,7 +72,7 @@ const handleLinkClick = (title: string) => {
|
||||
@apply absolute bottom-0 left-0 w-full pt-[14px] px-[18px] pb-[16px] flex flex-col justify-end border-t border-white/10 bg-black/40 shadow-[0_-10px_10px_0_rgba(0,0,0,0.25)] backdrop-blur-[25px] md:pt-[20px] text-left md:px-[26px] md:pb-[26px];
|
||||
}
|
||||
.card-title {
|
||||
@apply text-[14px] leading-[20px] font-medium text-white md:text-[18px] md:leading-[26px];
|
||||
@apply text-[14px] leading-[20px] font-medium text-white line-clamp-1 md:text-[18px] md:leading-[26px];
|
||||
}
|
||||
.card-description {
|
||||
@apply mt-[6px] text-[12px] leading-[18px] text-white/50 md:mt-1 md:text-[14px] md:leading-[24px];
|
||||
|
||||
65
layers/components/blocks/modal/Alert.vue
Normal file
65
layers/components/blocks/modal/Alert.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
interface props {
|
||||
isShowDimmed?: boolean
|
||||
contentText?: string
|
||||
confirmButtonText?: string
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<props>(), {
|
||||
isShowDimmed: false,
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['confirmButtonEvent'])
|
||||
|
||||
const isOpen = defineModel<boolean>('isOpen', { default: false })
|
||||
|
||||
const setButtonEvent = (event?: () => void | void) => {
|
||||
if (typeof event === 'function') {
|
||||
return event()
|
||||
}
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
if (props.isOutsideClose) {
|
||||
isOpen.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
:class="['modal-wrap', { dimmed: props.isShowDimmed }, props.modalName]"
|
||||
@click="handleOutsideClick"
|
||||
>
|
||||
<div class="modal-area" @click.stop>
|
||||
<div class="modal-content">
|
||||
<p
|
||||
v-if="props.contentText"
|
||||
v-dompurify-html="props.contentText"
|
||||
class="content-text"
|
||||
></p>
|
||||
<slot></slot>
|
||||
<div class="content-btns">
|
||||
<AtomsButtonVariant
|
||||
@click="setButtonEvent(() => emit('confirmButtonEvent'))"
|
||||
>
|
||||
{{ props.confirmButtonText || '확인' }}
|
||||
</AtomsButtonVariant>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-area {
|
||||
@apply max-w-[312px] p-6 bg-white rounded-[20px];
|
||||
}
|
||||
</style>
|
||||
72
layers/components/blocks/modal/Confirm.vue
Normal file
72
layers/components/blocks/modal/Confirm.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
interface props {
|
||||
isShowDimmed?: boolean
|
||||
contentText?: string
|
||||
confirmButtonText?: string
|
||||
cancelButtonText?: string
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<props>(), {
|
||||
isShowDimmed: false,
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['cancelButtonEvent', 'confirmButtonEvent'])
|
||||
|
||||
const isOpen = defineModel<boolean>('isOpen', { default: false })
|
||||
|
||||
const setButtonEvent = (event?: () => void) => {
|
||||
if (event) {
|
||||
event()
|
||||
}
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
if (props.isOutsideClose) {
|
||||
isOpen.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
:class="['modal-wrap', { dimmed: props.isShowDimmed }, props.modalName]"
|
||||
@click="handleOutsideClick"
|
||||
>
|
||||
<div class="modal-area" @click.stop>
|
||||
<div class="modal-content">
|
||||
<p
|
||||
v-if="props.contentText"
|
||||
v-dompurify-html="props.contentText"
|
||||
class="content-text"
|
||||
></p>
|
||||
<slot></slot>
|
||||
<div class="content-btns">
|
||||
<AtomsButtonVariant
|
||||
variant="outlined"
|
||||
@click="setButtonEvent(() => emit('cancelButtonEvent'))"
|
||||
>
|
||||
{{ props.cancelButtonText || '취소' }}
|
||||
</AtomsButtonVariant>
|
||||
<AtomsButtonVariant
|
||||
@click="setButtonEvent(() => emit('confirmButtonEvent'))"
|
||||
>
|
||||
{{ props.confirmButtonText || '확인' }}
|
||||
</AtomsButtonVariant>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-area {
|
||||
@apply max-w-[312px] p-6 bg-white rounded-[20px];
|
||||
}
|
||||
</style>
|
||||
50
layers/components/blocks/modal/Layer.vue
Normal file
50
layers/components/blocks/modal/Layer.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
interface props {
|
||||
isShowDimmed?: boolean
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
areaClass?: string
|
||||
closeClass?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<props>(), {
|
||||
isShowDimmed: false,
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
const isOpen = defineModel<boolean>('isOpen', { default: false })
|
||||
|
||||
const handleCloseModal = () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
if (props.isOutsideClose) {
|
||||
handleCloseModal()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
:class="['modal-wrap', { dimmed: props.isShowDimmed }, props.modalName]"
|
||||
@click="handleOutsideClick"
|
||||
>
|
||||
<div :class="['modal-area', props.areaClass]" @click.stop>
|
||||
<div class="modal-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
:class="['modal-close', props.closeClass]"
|
||||
@click="handleCloseModal"
|
||||
>
|
||||
<span class="sr-only">close</span>
|
||||
<AtomsIconsCloseLine size="24" color="#333333" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
88
layers/components/blocks/modal/YouTube.vue
Normal file
88
layers/components/blocks/modal/YouTube.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import { getYouTubeEmbedUrl } from '@/layers/utils/youtubeUtil'
|
||||
|
||||
interface Props {
|
||||
youtubeUrl: string
|
||||
isOutsideClose?: boolean
|
||||
modalName?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
youtubeUrl: '',
|
||||
isOutsideClose: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['closeButtonEvent'])
|
||||
|
||||
const isOpen = defineModel<boolean>('isOpen', { default: false })
|
||||
|
||||
const embedUrl = computed(() => {
|
||||
return getYouTubeEmbedUrl(props.youtubeUrl)
|
||||
})
|
||||
|
||||
const handleCloseModal = () => {
|
||||
isOpen.value = false
|
||||
emit('closeButtonEvent')
|
||||
}
|
||||
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape' && isOpen.value) {
|
||||
handleCloseModal()
|
||||
}
|
||||
}
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
if (props.isOutsideClose) {
|
||||
handleCloseModal()
|
||||
}
|
||||
}
|
||||
|
||||
// 키보드 이벤트 리스너 등록/해제
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeydown)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-75 z-[800]"
|
||||
:class="props.modalName"
|
||||
@click="handleOutsideClick"
|
||||
>
|
||||
<div
|
||||
class="relative mx-4 my-4"
|
||||
style="
|
||||
width: min(896px, 90vw, calc((90vh - 2rem) * 16 / 9));
|
||||
aspect-ratio: 16/9;
|
||||
"
|
||||
@click.stop
|
||||
>
|
||||
<!-- 헤더 -->
|
||||
<div class="flex justify-end mb-3 md:mb-4">
|
||||
<button type="button" @click="handleCloseModal">
|
||||
<AtomsIconsCloseLine />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 유튜브 영상 컨테이너 -->
|
||||
<div class="relative w-full h-full">
|
||||
<iframe
|
||||
v-if="embedUrl"
|
||||
:src="embedUrl"
|
||||
class="absolute top-0 left-0 w-full h-full"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
title="YouTube video player"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
@@ -6,6 +6,7 @@ import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||
interface Props {
|
||||
type?: ResponsiveOptions['type']
|
||||
slideItemLength?: number
|
||||
gap?: number
|
||||
autoplay?: boolean
|
||||
perPage?: number
|
||||
drag?: boolean
|
||||
@@ -16,6 +17,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
gap: 0,
|
||||
autoplay: false,
|
||||
perPage: 1,
|
||||
drag: true,
|
||||
@@ -39,6 +41,7 @@ const options = computed((): ResponsiveOptions => {
|
||||
type: slideType.value,
|
||||
autoWidth: true,
|
||||
autoHeight: true,
|
||||
gap: props.gap,
|
||||
perPage: props.perPage,
|
||||
speed: 500,
|
||||
updateOnMove: true,
|
||||
|
||||
@@ -59,7 +59,6 @@ watchEffect(() => {
|
||||
setupSeoMeta(props.pageData?.meta_tag_json)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -84,6 +83,12 @@ watchEffect(() => {
|
||||
|
||||
<style scoped>
|
||||
.main {
|
||||
@apply relative pt-[48px] md:pt-[64px];
|
||||
@apply relative min-h-[200px] pt-[48px] md:min-h-[300px] md:pt-[64px];
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
.main {
|
||||
@apply bg-theme-foreground;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
68
layers/components/widgets/modal/Client.vue
Normal file
68
layers/components/widgets/modal/Client.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
const { isShowCheckLauncher, isShowDownloadLauncher, downloadLauncher } =
|
||||
useCheckGameStart()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BlocksModalLayer
|
||||
v-model:is-open="isShowCheckLauncher"
|
||||
:is-show-dimmed="true"
|
||||
:is-outside-close="false"
|
||||
:modal-name="'launcher'"
|
||||
area-class="max-w-[480px] pt-[56px] px-[24px] pb-[24px] rounded-[8px]"
|
||||
close-class="absolute top-[16px] right-[24px]"
|
||||
>
|
||||
<span class="ico-loading"></span>
|
||||
<!-- [TODO] i18n 적용 -->
|
||||
<!-- <p class="text-check">{{ tm('Common_Message_Check_Client').txt }}</p> -->
|
||||
<p class="text-check">pc 클라이언트 실행 중...</p>
|
||||
<Transition name="fade">
|
||||
<div v-if="isShowDownloadLauncher" class="client-area">
|
||||
<!-- <p
|
||||
v-dompurify-html="tm('Common_Message_Download_Client').txt"
|
||||
class="text-info"
|
||||
></p>
|
||||
<button type="button" class="btn-download" @click="downloadLauncher">
|
||||
{{ tm('Common_Message_Install').txt }}
|
||||
</button>
|
||||
<p
|
||||
v-dompurify-html="tm('Common_Message_Download_Close').txt"
|
||||
class="text-tip"
|
||||
></p> -->
|
||||
<p class="text-info">
|
||||
PC 클라이언트가 실행되지 않나요?
|
||||
<br />
|
||||
다운로드 전이라면 다운로드 후 진행해주세요
|
||||
</p>
|
||||
<AtomsButtonVariant class="max-w-[300px]" @click="downloadLauncher">
|
||||
다운로드
|
||||
</AtomsButtonVariant>
|
||||
<p
|
||||
v-dompurify-html="
|
||||
'*PC 클라이언트가 정상 실행되었다면 팝업을 닫아 주세요.'
|
||||
"
|
||||
class="text-tip"
|
||||
></p>
|
||||
</div>
|
||||
</Transition>
|
||||
</BlocksModalLayer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ico-loading {
|
||||
@apply block mx-auto mb-4 w-[80px] h-[80px] bg-[url('/images/common/stove_loading_light.png')] bg-contain bg-center bg-no-repeat;
|
||||
}
|
||||
.text-check {
|
||||
@apply mb-6 text-center text-[20px] font-bold leading-[30px] tracking-[-0.6px] text-[#333333];
|
||||
}
|
||||
|
||||
.client-area {
|
||||
@apply pt-4 border-t border-[rgba(0,0,0,0.08)] text-center;
|
||||
}
|
||||
.text-info {
|
||||
@apply mb-3 text-[14px] font-medium leading-[24px] tracking-[-0.42px] text-[#333333];
|
||||
}
|
||||
.text-tip {
|
||||
@apply mt-4 text-[14px] leading-[20px] tracking-[-0.42px] text-[#999999];
|
||||
}
|
||||
</style>
|
||||
@@ -14,19 +14,12 @@ const props = defineProps<Props>()
|
||||
const gameDataStore = useGameDataStore()
|
||||
const pageDataStore = usePageDataStore()
|
||||
const { getCwmsArticleData } = useResourcesData()
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
const { locale } = useI18n()
|
||||
|
||||
const { gameData } = storeToRefs(gameDataStore)
|
||||
const { pageData } = storeToRefs(pageDataStore)
|
||||
|
||||
const defaultSlideItem = {
|
||||
article_id: '',
|
||||
media_thumbnail_url: '',
|
||||
title: '',
|
||||
create_datetime: 0,
|
||||
}
|
||||
|
||||
const boardId = computed(
|
||||
() => getComponentGroup(props.components, 'boardId')?.display?.text
|
||||
)
|
||||
@@ -65,28 +58,33 @@ const { data: slideData } = await useAsyncData(
|
||||
server: false,
|
||||
}
|
||||
)
|
||||
|
||||
const getResponsiveMultiple = (): number => {
|
||||
const breakpoints = useResponsiveBreakpoints()
|
||||
|
||||
if (breakpoints.value.isSm) return 1 // sm: 1개 단위
|
||||
if (breakpoints.value.isMd) return 2 // md: 2개 단위
|
||||
return 4 // 그 외: 4개 단위
|
||||
}
|
||||
|
||||
const fillToMultipleOfFour = (arr: CwmsArticleItem[]): CwmsArticleItem[] => {
|
||||
const remainderSize = getResponsiveMultiple()
|
||||
const remainder = arr.length % remainderSize
|
||||
const fillCount = remainder === 0 ? 0 : remainderSize - remainder
|
||||
return [
|
||||
...arr,
|
||||
...Array(fillCount).fill({ ...defaultSlideItem } as CwmsArticleItem),
|
||||
]
|
||||
}
|
||||
|
||||
const filledSlideData = computed(() => {
|
||||
return fillToMultipleOfFour(slideData.value)
|
||||
})
|
||||
const slideLength = computed(() => slideData.value.length)
|
||||
const slideClass = computed(() => ({
|
||||
'is-one-item': slideLength.value === 1,
|
||||
'is-two-items': slideLength.value === 2,
|
||||
'is-three-items': slideLength.value === 3,
|
||||
'is-four-items': slideLength.value === 4,
|
||||
}))
|
||||
const splideOptions = computed(() => ({
|
||||
gap: 20,
|
||||
perPage: 4,
|
||||
drag: false,
|
||||
arrows: slideLength.value > 4,
|
||||
pagination: slideLength.value > 4,
|
||||
breakpoints: {
|
||||
[BREAKPOINTS.lg - 1]: {
|
||||
perPage: 2,
|
||||
arrows: slideLength.value > 2,
|
||||
pagination: slideLength.value > 2,
|
||||
},
|
||||
[BREAKPOINTS.md - 1]: {
|
||||
drag: true,
|
||||
perPage: 1,
|
||||
arrows: false,
|
||||
pagination: false,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const getArticleUrl = (articleId: string) => {
|
||||
const communityUrl = gameData.value?.url_json?.community
|
||||
@@ -105,7 +103,7 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="relative py-[80px] md:py-[120px]"
|
||||
class="relative min-h-[483px] py-[80px] md:min-h-[684px] md:py-[120px]"
|
||||
:style="getPaginationClass(paginationData)"
|
||||
>
|
||||
<WidgetsBackground v-if="backgroundData" :resources-data="backgroundData" />
|
||||
@@ -117,37 +115,24 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
/>
|
||||
<ClientOnly>
|
||||
<BlocksSlideDefault
|
||||
v-if="slideData.length > 0"
|
||||
:slide-item-length="slideData.length"
|
||||
:per-page="4"
|
||||
:drag="false"
|
||||
:arrows="slideData.length > 4"
|
||||
:pagination="slideData.length > 4"
|
||||
:breakpoints="{
|
||||
[BREAKPOINTS.lg - 1]: {
|
||||
perPage: 2,
|
||||
arrows: slideData.length > 2,
|
||||
pagination: slideData.length > 2,
|
||||
},
|
||||
[BREAKPOINTS.md - 1]: {
|
||||
drag: true,
|
||||
perPage: 1,
|
||||
arrows: false,
|
||||
pagination: false,
|
||||
},
|
||||
}"
|
||||
:class="{ 'is-single': slideData.length === 1 }"
|
||||
v-if="slideLength > 0"
|
||||
:slide-item-length="slideLength"
|
||||
v-bind="splideOptions"
|
||||
:class="slideClass"
|
||||
@arrow-click="onArrowClick"
|
||||
>
|
||||
<SplideSlide v-for="(item, index) in filledSlideData" :key="index">
|
||||
<SplideSlide
|
||||
v-for="(item, index) in slideData"
|
||||
:key="item.article_id || `real-${index}`"
|
||||
>
|
||||
<div class="slide-inner">
|
||||
<BlocksCardNews
|
||||
:title="item.title"
|
||||
:description="
|
||||
formatTimestamp(item?.create_datetime, 'YYYY.MM.DD')
|
||||
formatTimestamp(item.create_datetime, 'YYYY.MM.DD')
|
||||
"
|
||||
:img-path="getResolvedHost(item?.media_thumbnail_url)"
|
||||
:url="getArticleUrl(item?.article_id)"
|
||||
:img-path="getResolvedHost(item.media_thumbnail_url)"
|
||||
:url="getArticleUrl(item.article_id)"
|
||||
link-target="_blank"
|
||||
/>
|
||||
</div>
|
||||
@@ -182,17 +167,24 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
.splide:deep(.arrow-next) {
|
||||
@apply top-[calc(50%-28px)] right-[0];
|
||||
}
|
||||
.splide__slide {
|
||||
@apply mr-[20px];
|
||||
}
|
||||
.slide-inner {
|
||||
@apply w-[275px] aspect-[1/1] md:w-[306px] md:box-border;
|
||||
}
|
||||
|
||||
.splide.is-single {
|
||||
@apply flex justify-center w-auto;
|
||||
.splide.is-one-item:deep(.splide__track) {
|
||||
@apply max-w-[315px] sm:max-w-[355px] mx-auto md:max-w-[306px];
|
||||
}
|
||||
.splide.is-single:deep(.splide__track) {
|
||||
@apply w-[calc(100%-20px)] md:w-auto;
|
||||
.splide.is-two-items:deep(.splide__track) {
|
||||
@apply lg:max-w-[632px];
|
||||
}
|
||||
.splide.is-two-items:deep(.splide__list) {
|
||||
@apply md:!translate-x-0;
|
||||
}
|
||||
.splide.is-three-items:deep(.splide__track) {
|
||||
@apply lg:max-w-[958px];
|
||||
}
|
||||
.splide.is-three-items:deep(.splide__list),
|
||||
.splide.is-four-items:deep(.splide__list) {
|
||||
@apply lg:!translate-x-0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { getComponentGroup } from '#layers/utils/dataUtil'
|
||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
||||
import AtomsImg from '#layers/components/atoms/Img.vue'
|
||||
|
||||
interface Props {
|
||||
components: PageDataTemplateComponents
|
||||
@@ -71,7 +70,7 @@ const buttonListData = computed(() => {
|
||||
<style scoped>
|
||||
.img-container {
|
||||
@apply flex flex-col items-center justify-center gap-4 box-content mx-auto mt-[32px]
|
||||
w-[334px]
|
||||
w-[440px]
|
||||
md:w-[944px];
|
||||
}
|
||||
.img-item {
|
||||
|
||||
@@ -158,6 +158,14 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
.thumbnail-carousel:deep(.main-splide) {
|
||||
@apply overflow-hidden rounded-lg border border-white/10 shadow-[0_4px_20px_0_rgba(0,0,0,0.5)];
|
||||
}
|
||||
.thumbnail-carousel:deep(.thumbnail-slide) {
|
||||
@apply opacity-50;
|
||||
}
|
||||
.thumbnail-carousel:deep(.thumbnail-slide:hover),
|
||||
.thumbnail-carousel:deep(.thumbnail-slide.is-active) {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.main-slide {
|
||||
@apply relative aspect-[16/9];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user