feat. GR_GALLERY_03 템플릿 제작
This commit is contained in:
@@ -3,14 +3,18 @@ import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
||||
|
||||
interface Props {
|
||||
resourcesData?: PageDataResourceGroup
|
||||
objectFit?: 'contain' | 'cover'
|
||||
alt?: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
objectFit: 'contain',
|
||||
})
|
||||
|
||||
const breakpoints = useResponsiveBreakpointsReliable()
|
||||
|
||||
// 텍스트 데이터 추출
|
||||
// [TODO] txt 대신 text 사용
|
||||
const displayText = computed(() => {
|
||||
return props.resourcesData?.display?.txt || ''
|
||||
return props.resourcesData?.display?.text || 'image'
|
||||
})
|
||||
const imageSrc = computed(() => {
|
||||
return getResponsiveSrc(props.resourcesData?.res_path)
|
||||
@@ -21,12 +25,17 @@ const colorName = computed(() => {
|
||||
const colorCode = computed(() => {
|
||||
return props.resourcesData?.display?.color_code
|
||||
})
|
||||
const currentImageSrc = computed(() => {
|
||||
if (!imageSrc.value) return ''
|
||||
return breakpoints.value.isMobile
|
||||
? imageSrc.value.mobileSrc || ''
|
||||
: imageSrc.value.pcSrc || ''
|
||||
})
|
||||
|
||||
// HTML 콘텐츠 정리 (줄바꿈 처리)
|
||||
const sanitizedContent = computed(() => {
|
||||
return displayText.value?.replace(/\n/g, '<br/>') || ''
|
||||
})
|
||||
|
||||
// 이미지가 있는지 확인
|
||||
const hasImage = computed(() => {
|
||||
return imageSrc.value && (imageSrc.value.mobileSrc || imageSrc.value.pcSrc)
|
||||
@@ -34,25 +43,16 @@ const hasImage = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 이미지가 있는 경우 -->
|
||||
<template v-if="hasImage">
|
||||
<!-- 모바일 이미지 (md 미만) -->
|
||||
<img
|
||||
v-if="imageSrc.mobileSrc"
|
||||
:src="imageSrc.mobileSrc"
|
||||
:alt="displayText"
|
||||
class="md:hidden w-full h-full object-contain"
|
||||
/>
|
||||
<!-- PC 이미지 (md 이상) -->
|
||||
<img
|
||||
v-if="imageSrc.pcSrc"
|
||||
:src="imageSrc.pcSrc"
|
||||
:alt="displayText"
|
||||
class="hidden md:block w-full h-full object-contain"
|
||||
/>
|
||||
</template>
|
||||
<!-- 이미지 -->
|
||||
<img
|
||||
v-if="hasImage && currentImageSrc"
|
||||
:src="currentImageSrc"
|
||||
:alt="alt || displayText"
|
||||
:class="`w-full h-full object-${objectFit}`"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<!-- 텍스트가 있는 경우 -->
|
||||
<!-- 텍스트 -->
|
||||
<span
|
||||
v-else-if="displayText"
|
||||
v-dompurify-html="sanitizedContent"
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { SlideItemSize } from '#layers/types/components/slide'
|
||||
|
||||
interface Props {
|
||||
slideItemSize: SlideItemSize
|
||||
type?: 'loop' | 'slide'
|
||||
slideItemLength?: number
|
||||
autoplay?: boolean | string
|
||||
arrows?: boolean
|
||||
pagination?: boolean
|
||||
@@ -13,22 +13,27 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'loop',
|
||||
autoplay: false,
|
||||
arrows: true,
|
||||
pagination: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['mounted', 'move', 'moved'])
|
||||
|
||||
const isMultipleItems = computed(() => {
|
||||
return props.slideItemLength > 1
|
||||
})
|
||||
|
||||
const options = computed((): ResponsiveOptions => {
|
||||
return {
|
||||
type: props.type,
|
||||
type: isMultipleItems.value ? 'loop' : 'slide',
|
||||
focus: 'center',
|
||||
autoWidth: true,
|
||||
autoHeight: true,
|
||||
speed: 400,
|
||||
updateOnMove: true,
|
||||
arrows: props.arrows,
|
||||
pagination: props.pagination,
|
||||
arrows: props.arrows && isMultipleItems.value,
|
||||
pagination: props.pagination && isMultipleItems.value,
|
||||
autoplay: props.autoplay,
|
||||
classes: {
|
||||
arrows: 'splide-arrows',
|
||||
@@ -71,13 +76,37 @@ const style = computed(() => {
|
||||
})
|
||||
|
||||
const handleSplideMounted = (splide: SplideType) => {
|
||||
emit('mounted', splide)
|
||||
splide.refresh()
|
||||
}
|
||||
|
||||
const handleMove = (
|
||||
splide: SplideType,
|
||||
newIndex: number,
|
||||
oldIndex: number,
|
||||
destIndex: number
|
||||
) => {
|
||||
emit('move', splide, newIndex, oldIndex, destIndex)
|
||||
}
|
||||
|
||||
const handleMoved = (
|
||||
splide: SplideType,
|
||||
newIndex: number,
|
||||
oldIndex: number,
|
||||
destIndex: number
|
||||
) => {
|
||||
emit('moved', splide, newIndex, oldIndex, destIndex)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="`center-highlight ${props.class || ''}`" :style="style">
|
||||
<Splide :options="options" @splide:mounted="handleSplideMounted">
|
||||
<Splide
|
||||
:options="options"
|
||||
@splide:mounted="handleSplideMounted"
|
||||
@splide:move="handleMove"
|
||||
@splide:moved="handleMoved"
|
||||
>
|
||||
<slot />
|
||||
</Splide>
|
||||
</div>
|
||||
@@ -93,9 +122,18 @@ const handleSplideMounted = (splide: SplideType) => {
|
||||
height: var(--banner-height-mo-active);
|
||||
margin-right: var(--banner-gap-mo);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide) .slide-inner {
|
||||
width: var(--banner-width-mo);
|
||||
height: var(--banner-height-mo);
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) {
|
||||
width: var(--banner-width-mo-container);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) .slide-inner {
|
||||
width: var(--banner-width-mo-active);
|
||||
height: var(--banner-height-mo-active);
|
||||
}
|
||||
|
||||
/* PC 스타일 */
|
||||
@media (min-width: 1024px) {
|
||||
@@ -104,9 +142,17 @@ const handleSplideMounted = (splide: SplideType) => {
|
||||
height: var(--banner-height-pc-active);
|
||||
margin-right: var(--banner-gap-pc);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide) .slide-inner {
|
||||
width: var(--banner-width-pc);
|
||||
height: var(--banner-height-pc);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) {
|
||||
width: var(--banner-width-pc-container);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) .slide-inner {
|
||||
width: var(--banner-width-pc-active);
|
||||
height: var(--banner-height-pc-active);
|
||||
}
|
||||
.center-highlight:deep(.splide-arrow) {
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
@@ -15,63 +15,33 @@ const props = withDefaults(defineProps<BannerListProps>(), {
|
||||
arrows: true,
|
||||
pagination: true,
|
||||
})
|
||||
|
||||
const isMultipleItems = computed(() => {
|
||||
return props.resourcesData.length > 1
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BlocksSlideCenterHighlight
|
||||
:slide-item-size="props.slideItemSize"
|
||||
:type="isMultipleItems ? 'loop' : 'slide'"
|
||||
:arrows="isMultipleItems ? true : false"
|
||||
:slide-item-length="props.resourcesData.length"
|
||||
:pagination="false"
|
||||
class="mt-[36px] md:mt-[60px]"
|
||||
>
|
||||
<SplideSlide
|
||||
v-for="(item, index) in props.resourcesData"
|
||||
:key="index"
|
||||
class="splide-slide"
|
||||
>
|
||||
<SplideSlide v-for="(item, index) in props.resourcesData" :key="index">
|
||||
<BlocksCardNews
|
||||
:title="item.title"
|
||||
:description="item.option01"
|
||||
:img-path="getResolvedHost(item.img_path)"
|
||||
:url="item.url"
|
||||
:link-target="item.link_target"
|
||||
class="news-center-highlight"
|
||||
class="slide-inner"
|
||||
/>
|
||||
</SplideSlide>
|
||||
</BlocksSlideCenterHighlight>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.center-highlight:deep(.splide__slide.is-active .news-center-highlight) {
|
||||
width: var(--banner-width-mo-active);
|
||||
height: var(--banner-height-mo-active);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active .card-link) {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.news-center-highlight {
|
||||
width: var(--banner-width-mo);
|
||||
height: var(--banner-height-mo);
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide .card-link) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* PC 스타일 */
|
||||
@media (min-width: 1024px) {
|
||||
.center-highlight:deep(.splide__slide.is-active .news-center-highlight) {
|
||||
width: var(--banner-width-pc-active);
|
||||
height: var(--banner-height-pc-active);
|
||||
}
|
||||
.news-center-highlight {
|
||||
width: var(--banner-width-pc);
|
||||
height: var(--banner-height-pc);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user