fix. scrollTop 버튼 수정
This commit is contained in:
@@ -12,6 +12,11 @@
|
|||||||
@apply outline-none;
|
@apply outline-none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* 라이트 테마 색상 */
|
/* 라이트 테마 색상 */
|
||||||
[data-theme='light'] {
|
[data-theme='light'] {
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -1,31 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
|
||||||
parentRef: HTMLElement | null
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
|
|
||||||
const { y: windowY } = useWindowScroll({ behavior: 'smooth' })
|
const { y: windowY } = useWindowScroll({ behavior: 'smooth' })
|
||||||
const { height: viewportH } = useWindowSize()
|
|
||||||
|
|
||||||
const parentEl = toRef(props, 'parentRef')
|
|
||||||
const { bottom: parentBottom } = useElementBounding(parentEl)
|
|
||||||
|
|
||||||
const showBtn = computed(() => windowY.value > 0)
|
const showBtn = computed(() => windowY.value > 0)
|
||||||
|
|
||||||
const offsetY = 12
|
|
||||||
|
|
||||||
const pinToParent = computed(() => {
|
|
||||||
if (!parentBottom.value) return false
|
|
||||||
return parentBottom.value <= viewportH.value - offsetY
|
|
||||||
})
|
|
||||||
|
|
||||||
const positionClasses = computed(() =>
|
|
||||||
pinToParent.value
|
|
||||||
? 'absolute bottom-[12px] right-[12px] md:bottom-[40px] md:right-[40px]'
|
|
||||||
: 'fixed bottom-[12px] right-[12px] md:bottom-[40px] md:right-[40px]'
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleScrollToTop = () => {
|
const handleScrollToTop = () => {
|
||||||
windowY.value = 0
|
windowY.value = 0
|
||||||
}
|
}
|
||||||
@@ -33,11 +10,7 @@ const handleScrollToTop = () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<button
|
<button v-if="showBtn" class="btn-top" @click="handleScrollToTop">
|
||||||
v-if="showBtn"
|
|
||||||
:class="['btn-top', positionClasses]"
|
|
||||||
@click="handleScrollToTop"
|
|
||||||
>
|
|
||||||
<AtomsIconsTopLine class="icon-top" />
|
<AtomsIconsTopLine class="icon-top" />
|
||||||
<span class="sr-only">top</span>
|
<span class="sr-only">top</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -46,7 +19,7 @@ const handleScrollToTop = () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.btn-top {
|
.btn-top {
|
||||||
@apply rounded-full flex items-center justify-center bg-black/20 z-[100]
|
@apply relative rounded-full flex items-center justify-center bg-black/20 z-[100]
|
||||||
w-[40px] h-[40px] md:w-[48px] md:h-[48px]
|
w-[40px] h-[40px] md:w-[48px] md:h-[48px]
|
||||||
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 before:transition-all before:duration-300 before:ease-in-out
|
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 before:transition-all before:duration-300 before:ease-in-out
|
||||||
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;
|
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;
|
||||||
|
|||||||
@@ -10,25 +10,25 @@ interface Props {
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
|
alt: 'image',
|
||||||
})
|
})
|
||||||
|
|
||||||
const imagePaths = computed(() => {
|
const imagePaths = computed(() => {
|
||||||
if (!props.resourcesData?.res_path) return null
|
if (!props.resourcesData?.res_path) return null
|
||||||
|
|
||||||
const pcPath =
|
const pc =
|
||||||
props.resourcesData.res_path.path_pc ?? props.resourcesData.res_path.path_mo
|
props.resourcesData.res_path.path_pc ?? props.resourcesData.res_path.path_mo
|
||||||
const moPath =
|
const mo =
|
||||||
props.resourcesData.res_path.path_mo ?? props.resourcesData.res_path.path_pc
|
props.resourcesData.res_path.path_mo ?? props.resourcesData.res_path.path_pc
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pc: pcPath ? getResolvedHost(pcPath) : '',
|
pc: pc ? getResolvedHost(pc) : '',
|
||||||
mo: moPath ? getResolvedHost(moPath) : '',
|
mo: mo ? getResolvedHost(mo) : '',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<picture v-if="imagePaths">
|
<picture v-if="imagePaths" ref="pictureRef">
|
||||||
<source media="(min-width: 1024px)" :srcset="imagePaths.pc" />
|
<source media="(min-width: 1024px)" :srcset="imagePaths.pc" />
|
||||||
<source media="(max-width: 1023px)" :srcset="imagePaths.mo" />
|
<source media="(max-width: 1023px)" :srcset="imagePaths.mo" />
|
||||||
<img
|
<img
|
||||||
@@ -39,24 +39,3 @@ const imagePaths = computed(() => {
|
|||||||
/>
|
/>
|
||||||
</picture>
|
</picture>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* 이미지 깨짐 시 보더 및 아이콘 제거 */
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 깨진 이미지 아이콘과 alt 텍스트 숨김 */
|
|
||||||
img::before,
|
|
||||||
img::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* alt 텍스트 영역 숨김 */
|
|
||||||
img[alt] {
|
|
||||||
text-indent: -9999px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
28
layers/components/atoms/icons/ShareLine.vue
Normal file
28
layers/components/atoms/icons/ShareLine.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
interface Props {
|
||||||
|
size?: number | string
|
||||||
|
color?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
withDefaults(defineProps<Props>(), {
|
||||||
|
size: 24,
|
||||||
|
color: 'white',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
:width="size"
|
||||||
|
:height="size"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M16.75 2C14.6789 2 13 3.67893 13 5.75C13 6.04651 13.0344 6.33499 13.0995 6.61165C13.06 6.6295 13.0213 6.65017 12.9836 6.67375L8.98359 9.17375C8.93046 9.20696 8.88163 9.24445 8.83728 9.2855C8.16435 8.64391 7.25318 8.25 6.25 8.25C4.17893 8.25 2.5 9.92893 2.5 12C2.5 14.0711 4.17893 15.75 6.25 15.75C7.25317 15.75 8.16434 15.3561 8.83728 14.7145C8.88163 14.7556 8.93046 14.793 8.98359 14.8263L12.9836 17.3263C12.996 17.334 13.0086 17.3415 13.0212 17.3486C13.0072 17.4805 13 17.6144 13 17.75C13 19.8211 14.6789 21.5 16.75 21.5C18.8211 21.5 20.5 19.8211 20.5 17.75C20.5 15.6789 18.8211 14 16.75 14C15.521 14 14.43 14.5912 13.7461 15.5048L10.0164 13.1737C9.95959 13.1382 9.90054 13.1093 9.84012 13.0867C9.9441 12.7428 10 12.3779 10 12C10 11.6221 9.9441 11.2572 9.84012 10.9133C9.90054 10.8907 9.95959 10.8618 10.0164 10.8263L14.0164 8.32626L14.0218 8.32286C14.7056 9.04763 15.675 9.5 16.75 9.5C18.8211 9.5 20.5 7.82107 20.5 5.75C20.5 3.67893 18.8211 2 16.75 2ZM15 5.75C15 4.7835 15.7835 4 16.75 4C17.7165 4 18.5 4.7835 18.5 5.75C18.5 6.7165 17.7165 7.5 16.75 7.5C15.7835 7.5 15 6.7165 15 5.75ZM6.25 10.25C5.2835 10.25 4.5 11.0335 4.5 12C4.5 12.9665 5.2835 13.75 6.25 13.75C7.2165 13.75 8 12.9665 8 12C8 11.0335 7.2165 10.25 6.25 10.25ZM16.75 16C15.7835 16 15 16.7835 15 17.75C15 18.7165 15.7835 19.5 16.75 19.5C17.7165 19.5 18.5 18.7165 18.5 17.75C18.5 16.7835 17.7165 16 16.75 16Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
39
layers/components/blocks/UtileContainer.vue
Normal file
39
layers/components/blocks/UtileContainer.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
interface Props {
|
||||||
|
parentRef: HTMLElement | null
|
||||||
|
isShowTopBtn: boolean
|
||||||
|
isShowSnsBtn: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const { height: viewportH } = useWindowSize()
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
isShowTopBtn: false,
|
||||||
|
isShowSnsBtn: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const parentEl = toRef(props, 'parentRef')
|
||||||
|
const { bottom: parentBottom } = useElementBounding(parentEl)
|
||||||
|
|
||||||
|
const pinToParent = computed(() => {
|
||||||
|
if (!parentBottom.value) return false
|
||||||
|
return parentBottom.value <= viewportH.value
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['utile-container', { 'is-fixed': pinToParent }]">
|
||||||
|
<AtomsButtonScrollTop v-if="props.isShowTopBtn" />
|
||||||
|
<!-- <AtomsButtonSns v-if="props.isShowSnsBtn" /> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.utile-container {
|
||||||
|
@apply fixed flex flex-col
|
||||||
|
bottom-[12px] right-[12px] gap-2 md:bottom-[40px] md:right-[40px] md:gap-3;
|
||||||
|
}
|
||||||
|
.utile-container.is-fixed {
|
||||||
|
@apply absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -67,9 +67,10 @@ watchEffect(() => {
|
|||||||
:page-ver-tmpl-seq="template.page_ver_tmpl_seq"
|
:page-ver-tmpl-seq="template.page_ver_tmpl_seq"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<AtomsButtonScrollTop
|
<BlocksUtileContainer
|
||||||
v-if="pageData.use_top_btn ?? false"
|
|
||||||
:parent-ref="mainRef"
|
:parent-ref="mainRef"
|
||||||
|
:is-show-top-btn="pageData.use_top_btn ?? false"
|
||||||
|
:is-show-sns-btn="pageData.use_sns_btn ?? false"
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SplideSlide } from '@splidejs/vue-splide'
|
import { getComponentGroup } from '#layers/utils/dataUtil'
|
||||||
import {
|
|
||||||
getComponentContainer,
|
|
||||||
getComponentGroup,
|
|
||||||
} from '#layers/utils/dataUtil'
|
|
||||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
||||||
|
import AtomsImg from '#layers/components/atoms/Img.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
@@ -13,9 +10,6 @@ interface Props {
|
|||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const { locale } = useI18n()
|
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
|
|
||||||
const backgroundData = computed(() =>
|
const backgroundData = computed(() =>
|
||||||
getComponentGroup(props.components, 'background')
|
getComponentGroup(props.components, 'background')
|
||||||
)
|
)
|
||||||
@@ -28,21 +22,12 @@ const subTitleData = computed(() =>
|
|||||||
const descriptionData = computed(() =>
|
const descriptionData = computed(() =>
|
||||||
getComponentGroup(props.components, 'description')
|
getComponentGroup(props.components, 'description')
|
||||||
)
|
)
|
||||||
const slideData = computed(() => {
|
const imgListData = computed(() => {
|
||||||
return getComponentContainer(props.components, 'group_sets')
|
return getComponentGroupAry(props.components, 'imgList')
|
||||||
})
|
})
|
||||||
const buttonListData = computed(() => {
|
const buttonListData = computed(() => {
|
||||||
return getComponentGroupAry(props.components, 'buttonList')
|
return getComponentGroupAry(props.components, 'buttonList')
|
||||||
})
|
})
|
||||||
const paginationData = computed(() => {
|
|
||||||
return getComponentGroupAry(props.components, 'pagination')
|
|
||||||
})
|
|
||||||
|
|
||||||
const onArrowClick = (direction, targetIndex) => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -59,51 +44,15 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
:resources-data="subTitleData"
|
:resources-data="subTitleData"
|
||||||
class="title-sm mt-2 mx-[20px] sm:mx-[40px]"
|
class="title-sm mt-2 mx-[20px] sm:mx-[40px]"
|
||||||
/>
|
/>
|
||||||
<template v-if="slideData">
|
<div v-if="imgListData" class="img-container">
|
||||||
<div v-if="slideData.length <= 2" class="img-container">
|
<div v-for="(item, index) in imgListData" :key="index" class="img-item">
|
||||||
<div
|
<AtomsImg
|
||||||
v-for="(item, index) in slideData"
|
:resources-data="item"
|
||||||
:key="index"
|
object-fit="contain"
|
||||||
:class="[{ 'slide-2': slideData.length === 2 }]"
|
:alt="item?.group_label"
|
||||||
>
|
/>
|
||||||
<AtomsImg
|
|
||||||
:resources-data="getComponentGroup(item, 'imgList')"
|
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
:alt="getComponentGroup(item, 'subTitle')?.display?.text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<BlocksSlideDefault
|
</div>
|
||||||
v-else
|
|
||||||
:type="slideData.length === 3 ? 'slide' : 'loop'"
|
|
||||||
:slide-item-length="slideData?.length"
|
|
||||||
:arrows="slideData.length === 3 ? false : true"
|
|
||||||
:pagination="slideData.length === 3 ? false : true"
|
|
||||||
:pagination-data="paginationData"
|
|
||||||
:breakpoints="{
|
|
||||||
1023: {
|
|
||||||
type: 'loop',
|
|
||||||
pagination: false,
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
class="mt-[32px]"
|
|
||||||
@arrow-click="onArrowClick"
|
|
||||||
>
|
|
||||||
<SplideSlide
|
|
||||||
v-for="(item, index) in slideData"
|
|
||||||
:key="index"
|
|
||||||
class="mr-4"
|
|
||||||
>
|
|
||||||
<div class="slide-inner w-[295px] sm:w-[304px]">
|
|
||||||
<AtomsImg
|
|
||||||
:resources-data="getComponentGroup(item, 'imgList')"
|
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
:alt="getComponentGroup(item, 'subTitle')?.display?.text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</SplideSlide>
|
|
||||||
</BlocksSlideDefault>
|
|
||||||
</template>
|
|
||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="buttonListData"
|
v-if="buttonListData"
|
||||||
:resources-data="buttonListData"
|
:resources-data="buttonListData"
|
||||||
@@ -121,26 +70,8 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.img-container {
|
.img-container {
|
||||||
@apply flex flex-wrap justify-center gap-4 box-content mx-auto mt-[32px]
|
@apply flex flex-wrap items-center justify-center gap-4 box-content mx-auto mt-[32px]
|
||||||
max-w-[688px] px-[20px]
|
max-w-[688px] px-[20px]
|
||||||
md:max-w-[944px] md:px-[40px];
|
md:max-w-[944px] md:px-[40px];
|
||||||
}
|
}
|
||||||
.splide {
|
|
||||||
@apply md:max-w-[1088px];
|
|
||||||
}
|
|
||||||
.splide:deep(.splide__track) {
|
|
||||||
@apply md:w-[944px] md:mx-auto;
|
|
||||||
}
|
|
||||||
.splide:deep(.splide__track) {
|
|
||||||
@apply !px-[20px] sm:!px-[40px] md:!px-[0];
|
|
||||||
}
|
|
||||||
.splide:deep(.arrow-next) {
|
|
||||||
@apply md:-right-[0];
|
|
||||||
}
|
|
||||||
.splide:deep(.arrow-prev) {
|
|
||||||
@apply md:-left-[0];
|
|
||||||
}
|
|
||||||
.slide-2 {
|
|
||||||
@apply max-w-[335px] md:max-w-[464px];
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 216 B |
Reference in New Issue
Block a user