Resolve merge conflicts in ButtonList.vue and pageData.ts

This commit is contained in:
“hyeonggkim”
2025-10-17 11:15:48 +09:00
81 changed files with 1090 additions and 802 deletions

View File

@@ -1,9 +1,7 @@
<script setup lang="ts">
import { useNuxtApp } from 'nuxt/app'
import LoadingFull from '#layers/components/blocks/loading/Full.vue'
import LoadingLocal from '#layers/components/blocks/loading/Local.vue'
import BlocksModalYouTube from '#layers/components/blocks/modal/YouTube.vue'
import type { GameDataMetaTag, GameDataValue } from '#layers/types/api/gameData'
import { getResolvedHost } from '#layers/utils/styleUtil'
const nuxtApp = useNuxtApp()
@@ -11,9 +9,9 @@ const gameDataStore = useGameDataStore()
const modalStore = useModalStore()
const scrollStore = useScrollStore()
const { youtube, handleResetYoutube } = modalStore
const { setGameData } = gameDataStore
const { gameData } = storeToRefs(gameDataStore)
const { youtube, confirm, alert, handleResetYoutube } = modalStore
const metaData = ref<GameDataMetaTag | null>(null)
@@ -113,6 +111,7 @@ if (serverGameData) {
setGameData(serverGameData)
setupMetaData(serverGameData)
}
const { gtag, initialize } = useGtag()
initialize(gameData.value?.ga_code)
gtag('event', 'screen_view', {
@@ -122,6 +121,14 @@ gtag('event', 'screen_view', {
onMounted(() => {
useEventListener('scroll', scrollStore.updateScrollValue)
if (gameData.value?.comm_img) {
gameData.value?.comm_img.groups.forEach(group => {
const cssVarName = `--${group.img_name}`
const imageUrl = `url(${getResolvedHost(group.img_path.comm)})`
document.documentElement.style.setProperty(cssVarName, imageUrl)
})
}
})
onBeforeUnmount(() => {
@@ -135,15 +142,36 @@ onBeforeUnmount(() => {
<!-- 공통 모달 컴포넌트 -->
<BlocksModalYouTube
:is-open="youtube.storeIsOpen"
v-model:is-open="youtube.storeIsOpen"
:youtube-url="youtube.storeYoutubeUrl"
:class-name="youtube.storeClassName"
:is-outside-close="youtube.storeIsOutsideClose"
:modal-name="youtube.storeModalName"
@close-button-event="handleResetYoutube"
/>
<BlocksModalConfirm
v-model:is-open="confirm.storeIsOpen"
:is-show-dimmed="confirm.storeIsShowDimmed"
:content-text="confirm.storeContentText"
:confirm-button-text="confirm.storeConfirmButtonText"
:cancel-button-text="confirm.storeCancelButtonText"
:is-outside-close="confirm.storeIsOutsideClose"
:modal-name="confirm.storeModalName"
@confirm-button-event="confirm.storeConfirmButtonEvent"
@cancel-button-event="confirm.storeCancelButtonEvent"
/>
<BlocksModalAlert
v-model:is-open="alert.storeIsOpen"
:is-show-dimmed="alert.storeIsShowDimmed"
:content-text="alert.storeContentText"
:confirm-button-text="alert.storeConfirmButtonText"
:is-outside-close="alert.storeIsOutsideClose"
:modal-name="alert.storeModalName"
@confirm-button-event="alert.storeConfirmButtonEvent"
/>
<!-- 로딩 컴포넌트 -->
<LoadingFull />
<LoadingLocal />
<BlocksLoadingFull />
<BlocksLoadingLocal />
</template>
<style>

View File

@@ -1,9 +1,11 @@
@import './base/_theme.css';
@import './base/_reset.css';
@import './base/_transition.css';
@import './components/_splide.css';
@import './components/_button.css';
@import './components/_layout.css';
@import './components/_modal.css';
@import './components/_splide.css';
@import '@splidejs/vue-splide/css';

View File

@@ -1,21 +1,21 @@
/* CSS 리셋 및 기본 스타일 */
@layer base {
body {
background-color: #000;
@apply min-w-[320px] bg-black;
}
body.scroll-lock {
overflow: hidden;
@apply overflow-hidden;
}
button,
a {
outline: none;
@apply outline-none;
}
/* 라이트 테마 색상 */
[data-theme='light'] {
body {
background-color: #fff;
@apply bg-white;
}
}
}

View File

@@ -1,4 +1,5 @@
:root {
/* 다크 테마 색상 */
--foreground: #191919;
--foreground-10: #292929;

View File

@@ -0,0 +1,19 @@
/* page-fade */
.page-fade-enter-active,
.page-fade-leave-active {
transition: opacity 0.6s cubic-bezier(0.33, 1, 0.68, 1);
}
.page-fade-enter-from,
.page-fade-leave-to {
opacity: 0;
}
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease-in-out;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

View File

@@ -1,34 +1,22 @@
/* Button Size Classes */
@layer components {
.btn-base {
@apply overflow-hidden relative inline-flex items-center justify-center font-medium cursor-pointer
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:rounded-lg after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
/* 기본 크기: size-medium */
px-10 h-14 text-base rounded-lg;
}
.btn-base:hover {
@apply after:opacity-20;
}
.btn-base:disabled {
@apply cursor-default
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
}
.size-large {
@apply px-10 h-16 text-lg;
@apply px-10 h-16 text-lg rounded-lg
before:rounded after:rounded;
}
.size-medium {
@apply px-10 h-14 text-base;
@apply px-10 h-14 text-base rounded-lg
before:rounded after:rounded;
}
.size-small {
@apply px-10 h-12 text-sm;
@apply px-10 h-12 text-sm rounded-lg
before:rounded after:rounded;
}
.size-extra-small {
@apply before:rounded after:rounded
px-6 h-10 text-sm rounded;
@apply px-6 h-10 text-sm rounded
before:rounded after:rounded;
}
}

View File

@@ -0,0 +1,22 @@
/* Button Size Classes */
@layer components {
.modal-wrap {
@apply fixed inset-0 flex p-5 z-[500];
}
.modal-wrap.dimmed {
@apply bg-black/60;
}
.modal-area {
@apply relative w-full m-auto bg-white;
}
.content-btns {
@apply flex gap-2 mt-6;
}
.content-text {
@apply text-center text-[15px] text-[#333333] leading-6 tracking-[-0.45px];
}
}

View File

@@ -1,102 +1,46 @@
/* 페이지네이션 버튼 - 모바일 퍼스트 */
.splide-pagination-bullets {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
gap: 12px;
margin-top: 24px;
z-index: 5;
}
.splide-pagination-bullets.type-full {
position: absolute;
bottom: 32px;
left: 0;
}
.splide-pagination-bullet {
position: relative;
width: 8px;
height: 8px;
background: var(--primary);
border-radius: 50%;
opacity: 1;
}
.splide-pagination-bullet:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background: rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease;
}
.splide-pagination-bullet.is-active:after {
opacity: 0;
}
/* 네비게이션 버튼 - 모바일 퍼스트 */
.splide-arrow {
display: none;
/* position: absolute;
top: 50%;
width: 40px;
height: 40px;
transform: translateY(-50%);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
z-index: 5;
background-color: var(--primary); */
}
/* .type-full .arrow-prev {
left: 20px;
}
.type-full.arrow-next {
right: 20px;
} */
/* 데스크톱 스타일 */
@media (min-width: 1024px) {
@layer components {
.splide-pagination-bullets {
gap: 24px;
margin-top: 32px;
@apply w-full flex justify-center items-center gap-3 mt-6 z-[5] md:mt-8 md:gap-6;
}
.splide-pagination-bullets.position-absolute {
bottom: 48px;
.splide-pagination-bullets.type-full {
@apply absolute bottom-8 left-0;
}
.splide-pagination-bullet {
width: 12px;
height: 12px;
@apply relative w-2 h-2 rounded-full bg-[var(--primary)] opacity-100 md:w-3 md:h-3
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:rounded-full after:bg-[rgba(0,0,0,0.5)] after:transition-opacity after:duration-300 after:ease-in-out after:opacity-100;
}
.splide-pagination-bullet.is-active {
@apply after:opacity-0;
}
.splide-arrows {
@apply hidden md:block;
}
.splide-arrow {
display: block;
position: absolute;
top: 50%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-color: var(--primary);
transform: translateY(-50%);
cursor: pointer;
z-index: 5;
@apply absolute top-1/2 w-[48px] h-[48px] bg-cover bg-center bg-no-repeat -translate-y-1/2 cursor-pointer z-[5]
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:rounded-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
hover:after:opacity-10;
}
.arrow-prev {
@apply bg-[image:var(--arrow-prev)];
}
.arrow-next {
@apply bg-[image:var(--arrow-next)];
}
.type-full .arrow-prev {
left: 40px;
@apply left-10;
}
.type-full .arrow-next {
right: 40px;
@apply right-10;
}
.splide-arrow svg {
@apply hidden;
}
}

View File

@@ -1,28 +1,19 @@
<script setup lang="ts">
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
const props = defineProps<{ resourcesData: PageDataResourceGroup }>()
const emit = defineEmits<{
(e: 'click'): void
}>()
const bgStyles = getResponsiveSrc(props.resourcesData?.res_path, {
resourcesType: 'bg',
})
</script>
<template>
<button
v-if="resourcesData && bgStyles"
class="relative group bg-cover bg-center bg-no-repeat w-[66px] h-[66px] md:w-[100px] md:h-[100px]"
:class="getResponsiveClass()"
:style="bgStyles"
@click="emit('click')"
>
<span
class="absolute inset-0 m-[10px] bg-white opacity-0 group-hover:opacity-10 transition-opacity duration-300 ease-in-out rounded-[50%]"
/>
<button class="btn-play" @click="emit('click')">
<span class="sr-only">Play</span>
</button>
</template>
<style scoped>
.btn-play {
@apply relative w-[66px] h-[66px] bg-[image:var(--video-play)] bg-cover bg-center bg-no-repeat md:w-[100px] md:h-[100px]
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-[50%] after:opacity-0 after:transition-opacity after:duration-300 after:ease-in-out
hover:after:opacity-10;
}
</style>

View File

@@ -0,0 +1,44 @@
<script setup lang="ts">
import type { ButtonVariant } from '#layers/types/components/button'
interface props {
variant?: ButtonVariant
disabled?: boolean
}
const props = withDefaults(defineProps<props>(), {
variant: 'filled',
disabled: false,
})
</script>
<template>
<button :class="['btn-base', props.variant]" :disabled="props.disabled">
<slot />
</button>
</template>
<style scoped>
.btn-base {
@apply relative w-full py-[14px] px-5 text-sm font-medium rounded-lg
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 before:transition-all before:duration-300 before:ease-in-out;
}
.btn-base.filled {
@apply bg-[var(--primary)] text-[var(--text-secondary)]
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-lg after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
hover:after:opacity-20;
}
.btn-base.outlined {
@apply bg-white text-[#333333]
hover:before:border-[#999];
}
.btn-base:disabled {
@apply cursor-default bg-[#EBEBEB] text-[#999]
before:border-[#D9D9D9]
after:hidden
hover:before:border-[#D9D9D9];
}
</style>

View File

@@ -1,45 +1,25 @@
<script setup lang="ts">
import type { ButtonType } from '#layers/types/components/button'
interface ButtonProps {
interface props {
type?: ButtonType
icon?: string
buttonSize?: string
target?: '_self' | '_blank'
href?: string
rel?: string
backgroundColor?: string
backgroundImage?: string
textColor?: string
disabled?: boolean
class?: string
}
const props = withDefaults(defineProps<ButtonProps>(), {
const props = withDefaults(defineProps<props>(), {
type: 'action',
buttonSize: 'size-extra-small md:size-large',
backgroundColor: 'var(--primary)',
textColor: 'var(--alternative-02)',
disabled: false,
})
const buttonClasses = computed(() =>
['btn-base group', props.class].filter(Boolean)
)
const buttonStyles = computed(() => {
const styles: Record<string, string> = {
backgroundColor: props.backgroundColor,
color: props.textColor,
'--text-color': props.textColor,
}
if (props.backgroundImage) {
styles.backgroundImage = `url(${props.backgroundImage})`
styles.backgroundSize = 'contain'
styles.backgroundPosition = 'center'
styles.backgroundRepeat = 'no-repeat'
}
return styles
})
const componentTag = computed((): string => {
switch (props.type) {
case 'download':
@@ -77,13 +57,41 @@ const componentProps = computed(() => {
<template>
<component
:is="componentTag"
v-bind="componentProps"
:class="buttonClasses"
:style="buttonStyles"
v-bind="{ ...componentProps }"
:class="['btn-base', props.buttonSize]"
:style="{
backgroundColor: props.backgroundColor,
color: props.textColor,
}"
>
<span class="relative flex items-center gap-2 z-[1]">
<span class="btn-content">
<slot />
<span v-if="props.icon" class="flex-shrink-0" v-html="props.icon" />
<AtomsIconsLongArrowRightLine v-if="props.type === 'internal'" />
<AtomsIconsWebLinkLine
v-if="props.type === 'external'"
size="24"
color="#ebebeb"
/>
<AtomsIconsDownloadLine v-if="props.type === 'download'" />
</span>
</component>
</template>
<style scoped>
.btn-base {
@apply overflow-hidden relative inline-flex items-center justify-center font-medium cursor-pointer
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10
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;
}
.btn-base:hover {
@apply after:opacity-20;
}
.btn-base:disabled {
@apply cursor-default
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
}
.btn-base .btn-content {
@apply relative flex items-center gap-1 z-[1];
}
</style>

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 12,
color: 'var(--foreground-gray-500)',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 12 12"
:fill="color"
:class="className"
>
<path
d="M5.29499 7.715L2.39999 4.875C2.07499 4.555 2.29999 4 2.75999 4L9.23499 4C9.69499 4 9.91999 4.555 9.59499 4.875L6.69999 7.715C6.30999 8.095 5.68999 8.095 5.29999 7.715H5.29499Z"

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 32,
color: '#EBEBEB',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 32 32"
fill="none"
:class="className"
>
<path
d="M26.2768 8.10947C26.7975 7.58877 26.7975 6.74455 26.2768 6.22385C25.7561 5.70315 24.9119 5.70315 24.3912 6.22385L16.0007 14.6144L7.61013 6.22385C7.08943 5.70315 6.24521 5.70315 5.72451 6.22385C5.20381 6.74455 5.20381 7.58877 5.72451 8.10947L14.115 16.5L5.72451 24.8905C5.20381 25.4112 5.20381 26.2554 5.72451 26.7761C6.24521 27.2968 7.08943 27.2968 7.61013 26.7761L16.0007 18.3856L24.3912 26.7761C24.9119 27.2968 25.7561 27.2968 26.2768 26.7761C26.7975 26.2554 26.7975 25.4112 26.2768 24.8905L17.8863 16.5L26.2768 8.10947Z"

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
interface Props {
size?: number | string
color?: string
}
withDefaults(defineProps<Props>(), {
size: 24,
color: '#EBEBEB',
})
</script>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="size"
:height="size"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M10.75 3.25L10.75 12.2322L6.88391 8.36611C6.39576 7.87796 5.6043 7.87796 5.11615 8.36611C4.62799 8.85427 4.62799 9.64573 5.11615 10.1339L11.1161 16.1339C11.3506 16.3683 11.6685 16.5 12 16.5C12.3316 16.5 12.6495 16.3683 12.8839 16.1339L18.8839 10.1339C19.3721 9.64573 19.3721 8.85427 18.8839 8.36611C18.3958 7.87796 17.6043 7.87796 17.1161 8.36611L13.25 12.2322L13.25 3.25C13.25 2.55964 12.6904 2 12 2C11.3097 2 10.75 2.55964 10.75 3.25Z"
:fill="color"
/>
<path
d="M20 21C20.6904 21 21.25 20.4404 21.25 19.75L21.25 17.75C21.25 17.0596 20.6904 16.5 20 16.5C19.3097 16.5 18.75 17.0596 18.75 17.75L18.75 18.5L5.25003 18.5L5.25003 17.75C5.25003 17.0596 4.69039 16.5 4.00003 16.5C3.30967 16.5 2.75003 17.0596 2.75003 17.75L2.75003 19.75C2.75003 20.4404 3.30967 21 4.00003 21L20 21Z"
:fill="color"
/>
</svg>
</template>

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
interface Props {
size?: number | string
color?: string
}
withDefaults(defineProps<Props>(), {
size: 24,
color: '#EBEBEB',
})
</script>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="size"
:height="size"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M11.7929 18.2929C11.4024 18.6834 11.4024 19.3166 11.7929 19.7071C12.1834 20.0976 12.8166 20.0976 13.2071 19.7071L20.2071 12.7071C20.5976 12.3166 20.5976 11.6834 20.2071 11.2929L13.2071 4.29289C12.8166 3.90237 12.1834 3.90237 11.7929 4.29289C11.4024 4.68342 11.4024 5.31658 11.7929 5.70711L17.0858 11L4.5 11C3.94771 11 3.5 11.4477 3.5 12C3.5 12.5523 3.94771 13 4.5 13L17.0858 13L11.7929 18.2929Z"
:fill="color"
/>
</svg>
</template>

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 24,
color: 'var(--foreground-reversal)',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 24 24"
fill="none"
:class="className"
>
<path
d="M20 16C20.7594 16 21.375 16.6156 21.375 17.375C21.375 18.1344 20.7594 18.75 20 18.75H4C3.24061 18.75 2.625 18.1344 2.625 17.375C2.625 16.6156 3.24061 16 4 16H20ZM20 10.5C20.7594 10.5 21.375 11.1156 21.375 11.875C21.375 12.6344 20.7594 13.25 20 13.25H4C3.24061 13.25 2.625 12.6344 2.625 11.875C2.625 11.1156 3.24061 10.5 4 10.5H20ZM20 5C20.7594 5 21.375 5.61561 21.375 6.375C21.375 7.13439 20.7594 7.75 20 7.75H4C3.24061 7.75 2.625 7.13439 2.625 6.375C2.625 5.61561 3.24061 5 4 5H20Z"

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 16,
color: 'var(--foreground-reversal)',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 16 16"
fill="none"
:class="className"
>
<path
d="M15.7071 1.70711C16.0976 1.31658 16.0976 0.683417 15.7071 0.292893C15.3166 -0.0976311 14.6834 -0.0976311 14.2929 0.292893L8 6.58579L1.70711 0.292893C1.31658 -0.0976311 0.683417 -0.0976311 0.292894 0.292893C-0.0976304 0.683417 -0.0976304 1.31658 0.292894 1.70711L6.58579 8L0.292893 14.2929C-0.0976311 14.6834 -0.0976311 15.3166 0.292893 15.7071C0.683417 16.0976 1.31658 16.0976 1.70711 15.7071L8 9.41421L14.2929 15.7071C14.6834 16.0976 15.3166 16.0976 15.7071 15.7071C16.0976 15.3166 16.0976 14.6834 15.7071 14.2929L9.41421 8L15.7071 1.70711Z"

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 24,
color: 'var(--foreground-reversal)',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 24 24"
fill="none"
:class="className"
>
<path
d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z"

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 12,
color: '#FD3886',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 12 12"
fill="none"
:class="className"
>
<path
d="M6.37364 3.24966C6.22658 2.91678 5.77344 2.91678 5.62638 3.24966L4.95508 4.76916L3.36352 4.96618C3.01484 5.00934 2.87482 5.4593 3.1326 5.7082L4.30928 6.84431L3.99693 8.48558C3.9285 8.84514 4.2951 9.12323 4.60148 8.94417L6.00001 8.12684L7.39853 8.94417C7.70491 9.12323 8.07151 8.84514 8.00308 8.48558L7.69074 6.84431L8.8674 5.70819C9.12518 5.4593 8.98515 5.00934 8.63648 4.96618L7.04492 4.76916L6.37364 3.24966Z"

View File

@@ -2,13 +2,11 @@
interface Props {
size?: number | string
color?: string
className?: string
}
withDefaults(defineProps<Props>(), {
size: 16,
color: 'var(--foreground-gray-500)',
className: '',
})
</script>
@@ -19,7 +17,6 @@ withDefaults(defineProps<Props>(), {
:height="size"
viewBox="0 0 16 16"
fill="none"
:class="className"
>
<path
d="M3.63636 3.33333C3.469 3.33333 3.33333 3.469 3.33333 3.63636L3.33333 12.3636C3.33333 12.531 3.469 12.6667 3.63636 12.6667H12.3636C12.531 12.6667 12.6667 12.531 12.6667 12.3636V9.93939C12.6667 9.5712 12.9651 9.27273 13.3333 9.27273C13.7015 9.27273 14 9.5712 14 9.93939V12.3636C14 13.2674 13.2674 14 12.3636 14H3.63636C2.73262 14 2 13.2674 2 12.3636L2 3.63636C2 2.73263 2.73262 2 3.63636 2L6.06061 2C6.4288 2 6.72727 2.29848 6.72727 2.66667C6.72727 3.03486 6.4288 3.33333 6.06061 3.33333H3.63636Z"

View File

@@ -0,0 +1,85 @@
<script setup>
const {
isProcessing,
isShowCheckLauncher,
isShowDownloadLauncher,
validateLauncher,
downloadLauncher,
} = useCheckGameStart()
</script>
<template>
<AtomsButton
:class="$attrs?.class"
:disabled="isProcessing"
@click="validateLauncher"
>
<slot />
</AtomsButton>
<ClientOnly>
<Teleport to="#teleports">
<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>
</Teleport>
</ClientOnly>
</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>

View File

@@ -33,7 +33,7 @@ const componentProps = computed(() => {
</script>
<template>
<component :is="componentTag" v-bind="{ ...$attrs, ...componentProps }">
<component :is="componentTag" v-bind="{ ...componentProps }">
<slot />
</component>
</template>

View File

@@ -1,10 +1,8 @@
<template>
<div id="stove-wrapper" class="relative z-[5]" />
</template>
<script setup lang="ts">
import { useGameDataStore } from '#layers/stores/useGameDataStore'
let cpHeader: any = null
const runtimeConfig = useRuntimeConfig()
const { locale, availableLocales } = useI18n()
const { gameData } = useGameDataStore()
@@ -22,7 +20,7 @@ const languageCodes = computed(() => {
return [locale]
})
function loadGnb(locale: string) {
const loadGnb = (locale: string) => {
locale = locale.toLowerCase()
const gnbOption = {
@@ -51,11 +49,22 @@ function loadGnb(locale: string) {
},
}
const cpHeader = new (window as any).cp.Header(gnbOption)
cpHeader = new (window as any).cp.Header(gnbOption)
cpHeader.render()
}
onMounted(() => {
loadGnb(locale.value)
})
onBeforeUnmount(() => {
if (cpHeader && typeof cpHeader.destroy === 'function') {
cpHeader.destroy()
}
cpHeader = null
})
</script>
<template>
<div id="stove-wrapper" class="relative z-[5]" />
</template>

View 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>

View 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>

View 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>

View File

@@ -1,19 +1,61 @@
<script setup lang="ts">
import { getYouTubeEmbedUrl } from '#layers/utils/youtube'
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
enter-active-class="transition duration-300 ease-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition duration-200 ease-in"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<Transition name="fade">
<div
v-if="isOpen"
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75"
@click="handleBackdropClick"
:class="props.modalName"
@click="handleOutsideClick"
>
<div
v-if="isOpen"
class="relative mx-4 my-4"
style="
width: min(896px, 90vw, calc((90vh - 2rem) * 16 / 9));
@@ -23,11 +65,7 @@
>
<!-- 헤더 -->
<div class="flex justify-end mb-3 md:mb-4">
<button
class="text-white rounded-full transition-colors"
aria-label="모달 닫기"
@click="closeModal"
>
<button type="button" @click="handleCloseModal">
<AtomsIconsCloseLine />
</button>
</div>
@@ -48,61 +86,3 @@
</div>
</Transition>
</template>
<script setup lang="ts">
import { getYouTubeEmbedUrl } from '#layers/utils/youtube'
interface Props {
isOpen: boolean
youtubeUrl: string
title?: string
description?: string
closeOnBackdrop?: boolean
}
interface Emits {
(e: 'closeButtonEvent'): void
}
const props = withDefaults(defineProps<Props>(), {
isOpen: false,
youtubeUrl: '',
title: '',
description: '',
closeOnBackdrop: true,
})
const emit = defineEmits<Emits>()
const embedUrl = computed(() => {
return getYouTubeEmbedUrl(props.youtubeUrl)
})
// ESC 키로 모달 닫기
const handleKeydown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && props.isOpen) {
closeModal()
}
}
// 배경 클릭으로 모달 닫기
const handleBackdropClick = () => {
if (props.closeOnBackdrop) {
closeModal()
}
}
// 모달 닫기 함수
const closeModal = () => {
emit('closeButtonEvent')
}
// 키보드 이벤트 리스너 등록/해제
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
</script>

View File

@@ -125,19 +125,27 @@ const handleMoved = (
width: var(--banner-width-mo);
height: var(--banner-height-mo-active);
margin-right: var(--banner-gap-mo);
opacity: 0.5;
}
.center-highlight:deep(.splide__slide) .slide-inner {
width: var(--banner-width-mo);
height: var(--banner-height-mo);
opacity: 0.5;
}
.center-highlight:deep(.splide__slide.is-active) {
width: var(--banner-width-mo-container);
opacity: 1;
}
.center-highlight:deep(.splide__slide.is-active) .slide-inner {
width: var(--banner-width-mo-active);
height: var(--banner-height-mo-active);
opacity: 1;
transition: all 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.center-highlight:deep(.splide__slide.is-next),
.center-highlight:deep(.splide__slide.is-prev) {
opacity: 1;
}
/* PC 스타일 */
@media (min-width: 1024px) {

View File

@@ -1,15 +1,19 @@
<script setup lang="ts">
import { Splide, SplideSlide } from '@splidejs/vue-splide'
import { getFirstGroup, isTypeVideo } from '#layers/utils/dataUtil'
import { getMediaSrc, getYouTubeEmbedUrl } from '#layers/utils/youtube'
import { getComponentGroup, isTypeVideo } from '#layers/utils/dataUtil'
import {
getMediaSrc,
getYouTubeEmbedUrl,
getMediaImgSrc,
} from '#layers/utils/youtube'
import type { Splide as SplideType, Options } from '@splidejs/splide'
import type {
PageDataResourceGroups,
PageDataTemplateComponentSet,
PageDataResourceGroup,
} from '#layers/types/api/pageData'
interface Props {
slideData: { media: PageDataResourceGroups; set_order: number }[]
slideData: PageDataTemplateComponentSet[]
videoPlay?: PageDataResourceGroup
arrows?: boolean
pagination?: boolean
@@ -19,6 +23,9 @@ interface Props {
const props = defineProps<Props>()
let mainInst: SplideType | null = null
let thumbsInst: SplideType | null = null
const mainRef = ref<InstanceType<typeof Splide> | null>(null)
const thumbsRef = ref<InstanceType<typeof Splide> | null>(null)
const playingSlideIndex = ref<number | null>(null)
@@ -33,6 +40,7 @@ const mainOptions = computed<Options>(() => ({
pagination: false,
drag: false,
}))
const thumbOptions = computed<Options>(() => ({
type: 'slide',
rewind: true,
@@ -51,39 +59,46 @@ const thumbOptions = computed<Options>(() => ({
},
}))
const isPassVideo = (groups: PageDataResourceGroups, index: number) => {
const firstGroup = getFirstGroup(groups)
return (
firstGroup &&
isTypeVideo(firstGroup?.resource_type) &&
index !== playingSlideIndex.value
)
const getMediaComponent = (item: PageDataTemplateComponentSet) => {
return getComponentGroup(item, 'media')
}
const getMediaImgSrcFromItem = (item: PageDataTemplateComponentSet) => {
const mediaComponent = getMediaComponent(item)
return mediaComponent ? getMediaImgSrc(mediaComponent) : ''
}
const getYouTubeEmbedUrlFromMedia = (item: PageDataTemplateComponentSet) => {
const mediaComponent = getMediaComponent(item)
if (!mediaComponent) return ''
const mediaSrc = getMediaSrc(mediaComponent)
return mediaSrc ? getYouTubeEmbedUrl(mediaSrc, true) : ''
}
const isPassVideo = (item: PageDataTemplateComponentSet, index: number) => {
const mediaComponent = getMediaComponent(item)
const isNotPlaying = index !== playingSlideIndex.value
const isVideoType =
mediaComponent && isTypeVideo(mediaComponent?.resource_type)
return isVideoType && isNotPlaying
}
const handleVideoClick = (index: number) => {
playingSlideIndex.value = index
}
const getYouTubeEmbedUrlFromMedia = (
resourceGroups: PageDataResourceGroup[]
) => {
const resourceGroup = getFirstGroup(resourceGroups)
const mediaSrc = getMediaSrc(resourceGroup)
return mediaSrc ? getYouTubeEmbedUrl(mediaSrc, true) : ''
const stopVideo = () => {
playingSlideIndex.value = null
}
let mainInst: SplideType | null = null
let thumbsInst: SplideType | null = null
onMounted(() => {
mainInst = mainRef.value?.splide ?? null
thumbsInst = thumbsRef.value?.splide ?? null
if (mainInst && thumbsInst) {
mainInst.sync(thumbsInst)
mainInst.on('moved', () => {
playingSlideIndex.value = null
})
mainInst.on('moved', stopVideo)
}
})
@@ -103,21 +118,21 @@ onBeforeUnmount(() => {
class="main-slide"
>
<img
:src="getMediaImgSrc(item.media)"
:src="getMediaImgSrcFromItem(item)"
alt="main image"
class="slide-image"
:class="{ 'opacity-0': playingSlideIndex === index }"
/>
<AtomsButtonPlay
v-if="isPassVideo(item.media, index)"
v-if="isPassVideo(item, index)"
:resources-data="videoPlay"
class="btn-play"
@click="handleVideoClick(index)"
/>
<iframe
v-if="playingSlideIndex === index"
:src="getYouTubeEmbedUrlFromMedia(item.media)"
class="absolute top-0 left-0 w-full h-full"
:src="getYouTubeEmbedUrlFromMedia(item)"
class="video-iframe"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
@@ -133,7 +148,7 @@ onBeforeUnmount(() => {
class="thumbnail-slide"
>
<img
:src="getMediaImgSrc(item.media)"
:src="getMediaImgSrcFromItem(item)"
alt="thumbnail image"
class="slide-image"
/>
@@ -143,8 +158,6 @@ onBeforeUnmount(() => {
</template>
<style scoped>
/* 비디오 iframe 전환 애니메이션 */
.thumbnail-carousel {
@apply w-full md:max-w-[944px];
}
@@ -161,6 +174,9 @@ onBeforeUnmount(() => {
.btn-play {
@apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2;
}
.video-iframe {
@apply absolute top-0 left-0 w-full h-full;
}
.thumbnail-splide {
@apply overflow-hidden flex justify-center w-screen mt-[20px] mx-[-20px] sm:mx-[-40px] md:w-auto md:mx-0 md:px-[120px] md:mt-[28px];

View File

@@ -59,7 +59,7 @@ const handleMenuClose = () => (isMenuOpen.value = false)
// navAreaRef의 넓이를 구하는 함수
const calculateNavWidth = () => {
if (!navAreaRef.value) return 0
if (!navAreaRef.value || !gnbData) return 0
const navAreaWidth = navAreaRef.value.offsetWidth
const moreWidth = 72 // 더보기 버튼 넓이 + 마진
@@ -68,7 +68,7 @@ const calculateNavWidth = () => {
// startRef의 넓이를 구하는 함수
const calculateStartWidth = () => {
if (!startRef.value) return 0
if (!startRef.value || !gnbData) return 0
const startWidth = startRef.value.offsetWidth
const headerRightPadding = 40 // 헤더 오른쪽 마진
@@ -98,7 +98,7 @@ const calculateOfficialItemWidths = () => {
// 오버플로우 계산 함수
const calculateOverflow = () => {
if (!navAreaRef.value) return
if (!navAreaRef.value || !startRef.value) return
const totalNavWidth = navWidth.value + startWidth.value
const screenWidth = width.value
@@ -129,7 +129,10 @@ const calculateOverflow = () => {
}
}
onClickOutside(navAreaRef, () => (isMenuOpen.value = false))
const stopClickOutside = onClickOutside(
navAreaRef,
() => (isMenuOpen.value = false)
)
// 화면 크기 변경 시 오버플로우 재계산
watch(width, () => {
@@ -147,6 +150,12 @@ onMounted(() => {
}
})
})
onBeforeUnmount(() => {
if (stopClickOutside) {
stopClickOutside()
}
})
</script>
<template>
@@ -171,7 +180,7 @@ onMounted(() => {
>
<div ref="navAreaRef" class="nav-area">
<div class="nav-logo">
<AtomsLocaleLink to="/brand">
<AtomsLocaleLink to="/brand" @click="handleMenuClose">
<img
:src="gnbData?.bi_path"
:alt="gameData?.game_name"
@@ -203,24 +212,26 @@ onMounted(() => {
class="hidden md:block"
/>
</BlocksHybridLink>
<div v-if="gnbItem.children" class="nav-2depth">
<ul>
<li
v-for="child in gnbItem.children"
:key="child.menu_name"
>
<BlocksHybridLink
:to="child.url_path"
:target="child.link_target"
<Transition name="fade">
<div v-if="gnbItem.children" class="nav-2depth">
<ul>
<li
v-for="child in gnbItem.children"
:key="child.menu_name"
>
<span>{{ child.menu_name }}</span>
<AtomsIconsWebLinkLine
v-if="child.link_target === '_blank'"
/>
</BlocksHybridLink>
</li>
</ul>
</div>
<BlocksHybridLink
:to="child.url_path"
:target="child.link_target"
>
<span>{{ child.menu_name }}</span>
<AtomsIconsWebLinkLine
v-if="child.link_target === '_blank'"
/>
</BlocksHybridLink>
</li>
</ul>
</div>
</Transition>
</div>
</div>
<div v-if="gnbData?.menus && overflowNam > 0" class="more">
@@ -283,9 +294,9 @@ onMounted(() => {
</div>
</nav>
<div ref="startRef" class="btn-start">
<AtomsButton size="small" class="w-full md:w-auto">
<BlocksButtonLuncher class="w-full md:w-auto">
게임 시작
</AtomsButton>
</BlocksButtonLuncher>
</div>
<button class="btn-close" @click="handleMenuClose">
<AtomsIconsMenuCloseLine class="mx-auto" />
@@ -341,8 +352,8 @@ onMounted(() => {
}
.nav-area {
@apply flex flex-col w-[360px] bg-theme-foreground-10 translate-x-[-100%]
md:inline-flex md:flex-row md:w-auto md:h-full md:pl-[40px] md:items-center md:bg-transparent md:transform-none;
@apply flex flex-col w-[100vw] max-w-[360px] min-w-[320px] bg-theme-foreground-10 translate-x-[-100%]
md:inline-flex md:flex-row md:w-auto md:max-w-none md:h-full md:pl-[40px] md:items-center md:bg-transparent md:transform-none;
}
.nav-logo {

View File

@@ -1,9 +1,8 @@
<script setup lang="ts">
import { templateRegistry } from '#layers/registry'
import { useTemplateRegistry } from '#layers/composables/useTemplateRegistry'
import type {
PageDataValue,
PageDataTemplate,
PageDataComponent,
PageDataMetaTag,
} from '#layers/types/api/pageData'
@@ -13,11 +12,8 @@ interface Props {
const props = defineProps<Props>()
// 템플릿 레지스트리 타입 캐스팅
const registry = templateRegistry as unknown as Record<
string,
{ component: PageDataComponent }
>
// 템플릿 레지스트리 사용
const { getTemplateComponent } = useTemplateRegistry()
// 개별 메타 태그 표시 여부 확인
const shouldShowMetaTag = computed(() => props.pageData.meta_tag_type === 2)
@@ -63,7 +59,7 @@ watchEffect(() => {
:key="template.template_code ?? index"
>
<component
:is="registry[template.template_code]?.component"
:is="getTemplateComponent(template.template_code)"
:components="template.components"
:page-ver-tmpl-seq="template.page_ver_tmpl_seq.toString()"
/>

View File

@@ -1,10 +1,10 @@
<script setup lang="ts">
import { SplideSlide } from '@splidejs/vue-splide'
import type { ListOperateGroupItem } from '#layers/types/api/resourcesData'
import type { OperateGroupItem } from '#layers/types/api/resourcesData'
import type { SlideItemSize } from '#layers/types/components/slide'
interface BannerListProps {
resourcesData: ListOperateGroupItem[]
resourcesData: OperateGroupItem[]
slideItemSize: SlideItemSize
arrows?: boolean
pagination?: boolean

View File

@@ -2,6 +2,7 @@
import type {
PageDataResourceGroup,
PageDataResourceGroupBtnInfo,
PageDataTracking,
} from '#layers/types/api/pageData'
import type { ButtonType } from '#layers/types/components/button'
@@ -11,8 +12,11 @@ interface ButtonListProps {
}
const props = defineProps<ButtonListProps>()
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
const {locale} = useI18n()
const { gameData } = useGameDataStore()
// 상수 정의
const BUTTON_TYPE_MAP = {
URL: {
_self: 'internal' as const,
@@ -37,25 +41,36 @@ const getButtonType = (btnInfo: PageDataResourceGroupBtnInfo): ButtonType => {
return DEFAULT_BUTTON_TYPE
}
const getButtonProps = (button: PageDataResourceGroup) => ({
type: getButtonType(button.btn_info),
target: button.btn_info?.detail?.action?.link_target,
href: button.btn_info?.detail?.action?.url,
rel: button.btn_info?.detail?.action?.rel,
backgroundColor: getColorCode({
colorName: button.btn_info?.color_name_btn,
colorCode: button.btn_info?.color_code_btn,
}),
textColor: getColorCode({
colorName: button.btn_info?.color_name_txt,
colorCode: button.btn_info?.color_code_txt,
}),
disabled: button.btn_info?.disabled,
text: button.btn_info?.txt_btn_name,
tracking: button.tracking,
})
const getButtonBackgroundImage = (
btnInfo: PageDataResourceGroupBtnInfo
): string => {
const marketType = btnInfo?.detail?.market_type
const marketImageMap: Record<string, string> = {
google_play: '/images/common/btn_logo-google.svg',
app_store: '/images/common/btn_logo-app.svg',
pc: '/images/common/btn_logo-pc.svg',
}
const { useAnalyticsLogDataDirect } = useAnalytics()
if (marketType && marketImageMap[marketType]) {
return marketImageMap[marketType]
}
return ''
}
const handleButtonClick = (btnInfo: PageDataResourceGroupBtnInfo) => {
const marketType = btnInfo?.detail?.market_type
if (marketType) {
const url = gameData?.market[marketType]?.url
window.open(url, '_blank')
return
}
// sendLog(locale.value, useAnalyticsLogDataDirect(btnInfo, props.pageVerTmplSeq))
// v-analytics="useAnalyticsLogDataDirect(props.resourcesData[0].tracking, props.pageVerTmplSeq)"
}
</script>
<template>
@@ -66,11 +81,39 @@ const { useAnalyticsLogDataDirect } = useAnalytics()
<AtomsButton
v-for="(button, index) in props.resourcesData"
:key="index"
v-bind="getButtonProps(button)"
v-analytics="useAnalyticsLogDataDirect(getButtonProps(button), props.pageVerTmplSeq)"
class="size-extra-small md:size-medium"
v-analytics="useAnalyticsLogDataDirect(props.resourcesData[index].tracking, props.pageVerTmplSeq)"
:type="getButtonType(button.btn_info)"
:target="button.btn_info?.detail?.action?.link_target"
:href="button.btn_info?.detail?.action?.url"
:rel="button.btn_info?.detail?.action?.rel"
:background-color="
getColorCode({
colorName: button.btn_info?.color_name_btn,
colorCode: button.btn_info?.color_code_btn,
})
"
:text-color="
getColorCode({
colorName: button.btn_info?.color_name_txt,
colorCode: button.btn_info?.color_code_txt,
})
"
:disabled="button.btn_info?.disabled"
:class="button.btn_info?.detail?.market_type ? 'btn-market' : ''"
:style="{
backgroundImage: `url(${getButtonBackgroundImage(button.btn_info)})`,
}"
@click="handleButtonClick(button.btn_info)"
>
{{ button.btn_info?.txt_btn_name }}
</AtomsButton>
</div>
</template>
<style scoped>
:deep(.btn-market) {
@apply flex items-start bg-[16px_50%] bg-[length:auto_34px] bg-no-repeat
min-w-[113px] pt-[23px] pl-[44px] pr-[22px] text-[11px]
md:min-w-[150px] md:pt-[30px] md:pl-[64px] md:pr-[28px] md:text-[12px] md:bg-[20px_50%] md:bg-[length:auto_40px];
}
</style>

View File

@@ -7,136 +7,46 @@ export const useCheckGameStart = () => {
const modalStore = useModalStore()
const runtimeConfig = useRuntimeConfig()
const disabledDoubleClick = ref(false) // 연속 호출 클릭 방지
const isCheckLauncher = ref(false) // 런처 실행 로딩 상태
const isProcessing = ref(false) // 연속 클릭 방지
const isShowCheckLauncher = ref(false) // 런처 실행 로딩 표시
const isShowDownloadLauncher = ref(false) // 런처 다운로드 표시
const customerService = { title: '확인', link: 'https://www.google.com' } //[TODO]
const customerService = { title: '확인', link: 'https://www.google.com' } //[TODO] 고객센터 링크
// 로그인 모달 표시
const showLoginModal = () => {
modalStore.handleOpenAlert({
contentText: '로그인',
modalStore.handleOpenConfirm({
contentText: '로그인이 필요합니다.',
confirmButtonText: '스토브 로그인',
className: 'modal-login',
modalName: 'modal-login',
confirmButtonEvent: () => {
modalStore.handleResetAlert()
csrGoStoveLogin()
},
closeButtonEvent: () => {
modalStore.handleResetAlert()
disabledDoubleClick.value = false
},
})
}
// 모든 런처 버튼 비활성화
const setLauncherButtonDisabled = (disabled: boolean) => {
const launcherButton = document.querySelectorAll(
'#btn-launcher'
) as NodeListOf<HTMLButtonElement>
launcherButton.forEach(button => {
button.disabled = disabled
})
}
// 런처 실행 로딩 시작 UI 처리
const startLoadingForLauncher = () => {
if (import.meta.client) {
setLauncherButtonDisabled(true)
isCheckLauncher.value = true
setTimeout(() => {
if (isCheckLauncher.value) {
isShowDownloadLauncher.value = true
}
}, 5000)
}
}
// 런처 실행 로딩 종료 UI 처리
const stopLoadingForLauncher = () => {
if (import.meta.client) {
setLauncherButtonDisabled(false)
isCheckLauncher.value = false
isShowDownloadLauncher.value = false
}
}
// 런처 호출
const runLauncher = async () => {
// 클라이언트에서만 실행
if (!import.meta.client) return
const gameDataStore = useGameDataStore()
const stoveGameId = gameDataStore.gameData?.game_id || ''
const accessTokenSub = useCookie('SUAT')
const nationCookie = useCookie('NNTO').value
const localeCookie = useCookie('LOCALE').value
const isAgent = true
disabledDoubleClick.value = true
window.stoveJsService = window.stoveJsService || {}
// 토큰 유효성 체크
const { validateToken } = useTokenValidation()
const validateTokenResult = await validateToken(accessTokenSub.value || '')
if (validateTokenResult) {
startLoadingForLauncher()
window.stoveJsService.launcher
.run({
gameId: stoveGameId,
nation: nationCookie,
lang: localeCookie,
isSkipMaintenance: isAgent,
})
.then(() => {
// 런처 실행 성공 시 처리
stopLoadingForLauncher()
})
.catch((error: any) => {
// 런처 실행 실패시 처리
if (error.code !== 601) {
stopLoadingForLauncher()
}
errorHandler(error.code)
})
.finally(() => {
disabledDoubleClick.value = false
})
} else {
showLoginModal()
}
}
// PC 클라이언트 설치 전 (에러 처리)
// 에러 처리
const errorHandler = (errorCode: number) => {
switch (errorCode) {
case 601: // PC 클라이언트 미설치
break
case 40101: // 로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요.
modalStore.handleOpenAlert({
modalStore.handleOpenConfirm({
contentText:
'로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요.',
confirmButtonText: '스토브 로그인',
className: 'modal-login',
modalName: 'modal-login',
confirmButtonEvent: () => {
modalStore.handleResetAlert()
csrGoStoveLogin()
},
})
break
case 40103: // 로그인 정보가 만료되었습니다. 재로그인 후 다시 이용해 주세요.
modalStore.handleOpenAlert({
modalStore.handleOpenConfirm({
contentText:
'로그인 정보가 만료되었습니다. 재로그인 후 다시 이용해 주세요.',
confirmButtonText: '스토브 로그인',
className: 'modal-login',
modalName: 'modal-login',
confirmButtonEvent: () => {
modalStore.handleResetAlert()
csrGoStoveLogin()
},
})
@@ -155,7 +65,6 @@ export const useCheckGameStart = () => {
confirmButtonText: customerService.title,
cancelButtonText: '취소',
confirmButtonEvent: () => {
modalStore.handleResetConfirm()
window.open(customerService.link, '_blank')
},
})
@@ -163,29 +72,102 @@ export const useCheckGameStart = () => {
}
}
// 런처 실행 로딩 시작 UI 처리
const startLoadingForLauncher = () => {
if (import.meta.client) {
isShowCheckLauncher.value = true
isShowDownloadLauncher.value = false
setTimeout(() => {
if (isShowCheckLauncher.value) {
isShowDownloadLauncher.value = true
}
}, 5000)
}
}
// 런처 실행 로딩 종료 UI 처리
const stopLoadingForLauncher = () => {
if (import.meta.client) {
isShowCheckLauncher.value = false
isShowDownloadLauncher.value = false
}
}
// 런처 호출
const runLauncher = async () => {
// 클라이언트에서만 실행
if (!import.meta.client) return
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const accessTokenSub = useCookie('SUAT')
const stoveGameId = gameData.value?.game_id || ''
const nationCookie = useCookie('NNTO').value
const localeCookie = useCookie('LOCALE').value
window.stoveJsService = window.stoveJsService || {}
// 토큰 유효성 체크
const { validateToken } = useTokenValidation()
const validateTokenResult = await validateToken(accessTokenSub.value || '')
// 토큰 유효성 체크 실패 시 로그인 모달 표시
if (!validateTokenResult) {
showLoginModal()
isProcessing.value = false
return
}
// 런처 실행 로딩 시작
startLoadingForLauncher()
window.stoveJsService.launcher
.run({
gameId: stoveGameId,
nation: nationCookie,
lang: localeCookie,
isSkipMaintenance: true,
})
.then(() => {
// 런처 실행 성공 시 처리
stopLoadingForLauncher()
})
.catch((error: any) => {
// 런처 실행 실패시 처리
if (error.code !== 601) {
stopLoadingForLauncher()
}
errorHandler(error.code)
})
.finally(() => {
isProcessing.value = false
})
}
// 디바운스 설정
const debounceHandler = useDebounceFn(runLauncher, 500)
// 런처 상태 검사
const validateLauncher = () => {
if (!disabledDoubleClick.value) {
debounceHandler()
}
if (isProcessing.value) return
isProcessing.value = true
debounceHandler()
}
// 런처 다운로드 함수
const downloadLauncher = () => {
const stoveClientDownloadUrl = runtimeConfig.public.stoveClientDownloadUrl
location.href = stoveClientDownloadUrl
disabledDoubleClick.value = false
isProcessing.value = false
}
return {
disabledDoubleClick, // 연속 클릭 방지
isCheckLauncher, // 런처 실행 로딩 상태
isProcessing, // 연속 클릭 방지
isShowCheckLauncher, // 런처 실행 로딩 표시
isShowDownloadLauncher, // 런처 다운로드 표시
validateLauncher, // 런처 검사 함수
downloadLauncher, // 런처 실행 함수
stopLoadingForLauncher, // 런처 실행 로딩 종료 함수
}
}

View File

@@ -1,18 +1,18 @@
import type {
GetResourcesDataParams,
ResourcesDataResponse,
ResourcesDataValue,
OperateComponents,
} from '#layers/types/api/resourcesData'
export const useResourcesData = () => {
const getResourcesData = async (
params: GetResourcesDataParams
): Promise<ResourcesDataValue | null> => {
): Promise<OperateComponents | null> => {
const { pageSeq, pageVer, pageVerTmplSeq, langCode, q, qc } = params
const config = useRuntimeConfig()
const stoveApiBaseUrl = config.public.stoveApiUrl
const apiUrl = `${stoveApiBaseUrl}/pub-comm/v1.0/template/resources`
const apiUrl = `${stoveApiBaseUrl}/pub-comm/v1.0/template/operateResources`
const queryParams: Record<string, string> = {
page_seq: pageSeq,

View File

@@ -4,13 +4,8 @@ import GrVisual03 from '#layers/templates/GrVisual03/index.vue'
import GrGallery01 from '#layers/templates/GrGallery01/index.vue'
import GrGallery02 from '#layers/templates/GrGallery02/index.vue'
import GrGallery03 from '#layers/templates/GrGallery03/index.vue'
// import GrBoard01 from "#layers/templates/GrBoard01/index.vue";
// import GrDetail01 from "#layers/templates/GrDetail01/index.vue";
// import GrDetail02 from "#layers/templates/GrDetail02/index.vue";
// import GrDetail03 from "#layers/templates/GrDetail03/index.vue";
// import GrContents01 from "#layers/templates/GrContents01/index.vue";
export const templateRegistry = {
const templateRegistry = {
GR_VISUAL_01: { component: GrVisual01 },
GR_VISUAL_02: { component: GrVisual02 },
GR_VISUAL_03: { component: GrVisual03 },
@@ -24,4 +19,13 @@ export const templateRegistry = {
// GR_CONTENTS_01: { component: GrContents01 },
} as const
export type TemplateKey = keyof typeof templateRegistry
type TemplateKey = keyof typeof templateRegistry
export const useTemplateRegistry = () => {
return {
templateRegistry,
getTemplateComponent: (templateCode: string) => {
return templateRegistry[templateCode as TemplateKey]?.component
},
}
}

View File

@@ -63,15 +63,9 @@ export default defineEventHandler(async event => {
query: queryParams,
})) as GameDataResponse | null
const gaId = (response as any).value?.ga_code
if (gaId) {
// 환경변수에 동적 설정
event.context.googleAnalyticsId = gaId
}
if (response?.code === 0 && 'value' in response) {
event.context.gameData = response.value
event.context.googleAnalyticsId = response.value?.ga_code
}
} catch (error) {
console.error('gameData load error:', error)

View File

@@ -1,4 +1,4 @@
import { defineStore } from 'pinia'
import { defineStore, skipHydrate } from 'pinia'
import type {
DialogParams,
YoutubeParams,
@@ -7,25 +7,13 @@ import type {
const createModalState = () => ({
storeIsOpen: ref(false),
storeIsShowDimmed: ref(false),
storeClassName: ref(''),
storeIsOutsideClose: ref(true),
storeModalName: ref(''),
storeIsOutsideClose: ref(false),
storeContentText: ref(''),
storeConfirmButtonText: ref(''),
storeConfirmButtonEvent: ref(() => {}),
storeCloseButtonEvent: ref(() => {}),
storeConfirmButtonEvent: skipHydrate(ref<() => void>(undefined)),
})
const resetModalState = (type: ReturnType<typeof createModalState>) => {
type.storeIsOpen.value = false
type.storeIsShowDimmed.value = false
type.storeClassName.value = ''
type.storeIsOutsideClose.value = true
type.storeContentText.value = ''
type.storeConfirmButtonText.value = ''
type.storeConfirmButtonEvent.value = () => {}
type.storeCloseButtonEvent.value = () => {}
}
export const useModalStore = defineStore('modalStore', () => {
const scrollStore = useScrollStore()
@@ -36,83 +24,74 @@ export const useModalStore = defineStore('modalStore', () => {
const handleOpenAlert = ({
isShowDimmed = false,
className = '',
isOutsideClose = true,
modalName = '',
isOutsideClose = false,
contentText,
confirmButtonText = '',
confirmButtonEvent,
closeButtonEvent,
confirmButtonEvent = undefined,
}: DialogParams) => {
alert.storeIsOpen.value = true
alert.storeIsShowDimmed.value = isShowDimmed
alert.storeClassName.value = className
alert.storeModalName.value = modalName
alert.storeContentText.value = contentText
alert.storeConfirmButtonText.value = confirmButtonText
alert.storeIsOutsideClose.value = isOutsideClose
alert.storeConfirmButtonEvent.value = confirmButtonEvent ?? handleResetAlert
alert.storeCloseButtonEvent.value = closeButtonEvent ?? handleResetAlert
}
const handleResetAlert = () => {
resetModalState(alert)
alert.storeConfirmButtonEvent.value = confirmButtonEvent
}
// confirm ------------------
const confirm = {
...createModalState(),
storeCancelButtonText: ref(''),
storeCancelButtonEvent: ref(() => {}),
storeCancelButtonEvent: skipHydrate(ref<() => void>(undefined)),
}
const handleOpenConfirm = ({
isShowDimmed = false,
className = '',
isOutsideClose = true,
modalName = '',
isOutsideClose = false,
contentText,
confirmButtonText = '',
cancelButtonText = '',
confirmButtonEvent,
cancelButtonEvent,
closeButtonEvent,
confirmButtonEvent = undefined,
cancelButtonEvent = undefined,
}: DialogParams) => {
confirm.storeIsOpen.value = true
confirm.storeIsShowDimmed.value = isShowDimmed
confirm.storeClassName.value = className
confirm.storeModalName.value = modalName
confirm.storeContentText.value = contentText
confirm.storeConfirmButtonText.value = confirmButtonText
confirm.storeCancelButtonText.value = cancelButtonText
confirm.storeIsOutsideClose.value = isOutsideClose
confirm.storeConfirmButtonEvent.value =
confirmButtonEvent ?? handleResetConfirm
confirm.storeCancelButtonEvent.value =
cancelButtonEvent ?? handleResetConfirm
confirm.storeCloseButtonEvent.value = closeButtonEvent ?? handleResetConfirm
}
const handleResetConfirm = () => {
resetModalState(confirm)
confirm.storeCancelButtonText.value = ''
confirm.storeCancelButtonEvent.value = () => {}
confirm.storeConfirmButtonEvent.value = confirmButtonEvent
confirm.storeCancelButtonEvent.value = cancelButtonEvent
}
// youtube ------------------
const youtube = {
storeIsOpen: ref(false),
storeYoutubeUrl: ref(''),
storeClassName: ref(''),
storeIsOutsideClose: ref(false),
storeModalName: ref(''),
}
const handleOpenYoutube = ({ youtubeUrl, className = '' }: YoutubeParams) => {
const handleOpenYoutube = ({
youtubeUrl,
isOutsideClose = false,
modalName = '',
}: YoutubeParams) => {
youtube.storeIsOpen.value = true
youtube.storeYoutubeUrl.value = youtubeUrl
youtube.storeClassName.value = className
youtube.storeIsOutsideClose.value = isOutsideClose
youtube.storeModalName.value = modalName
scrollStore.controlScrollLock(true)
}
const handleResetYoutube = () => {
youtube.storeIsOpen.value = false
youtube.storeYoutubeUrl.value = ''
youtube.storeClassName.value = ''
youtube.storeIsOutsideClose.value = false
youtube.storeModalName.value = ''
scrollStore.controlScrollLock(false)
}
@@ -122,8 +101,6 @@ export const useModalStore = defineStore('modalStore', () => {
youtube,
handleOpenAlert,
handleOpenConfirm,
handleResetAlert,
handleResetConfirm,
handleOpenYoutube,
handleResetYoutube,
}

View File

@@ -1,8 +1,12 @@
<script setup lang="ts">
import { getComponentGroup } from '#layers/utils/dataUtil'
import {
getComponentGroup,
ensureMinimumSlideData,
} from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
interface Props {
components: Record<string, any>
components: PageDataTemplateComponents
pageVerTmplSeq: string
}
@@ -15,11 +19,7 @@ const mainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitle')
)
const slideData = computed(() => {
if (props.components.group_sets.length < 3) {
return [...props.components.group_sets, ...props.components.group_sets]
}
return props.components.group_sets
return ensureMinimumSlideData(props.components)
})
const videoPlayData = computed(() =>
getComponentGroup(props.components, 'videoPlay')

View File

@@ -1,10 +1,14 @@
<script setup lang="ts">
import { SplideSlide } from '@splidejs/vue-splide'
import { getComponentGroup } from '#layers/utils/dataUtil'
import {
getComponentGroup,
ensureMinimumSlideData,
} from '#layers/utils/dataUtil'
import type { Splide as SplideType } from '@splidejs/splide'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
interface Props {
components: Record<string, any>
components: PageDataTemplateComponents
pageVerTmplSeq: string
}
@@ -17,12 +21,9 @@ const mainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitle')
)
const slideData = computed(() => {
if (props.components.group_sets.length < 3) {
return [...props.components.group_sets, ...props.components.group_sets]
}
return props.components.group_sets
return ensureMinimumSlideData(props.components)
})
const buttonListData = ref(
getComponentGroupAry(slideData?.value[0], 'buttonList')
)
@@ -91,3 +92,10 @@ const handleChange = (
</div>
</section>
</template>
<style scoped>
.section-container {
@apply before:hidden md:before:block before:content-[''] before:absolute before:top-0 before:left-0 before:w-[104px] before:h-full before:bg-gradient-to-l from-transparent to-[rgba(0,0,0,0.7)]
after:hidden md:after:block after:content-[''] after:absolute after:top-0 after:right-0 after:w-[104px] after:h-full after:bg-gradient-to-r from-transparent to-[rgba(0,0,0,0.7)];
}
</style>

View File

@@ -1,10 +1,14 @@
<script setup lang="ts">
import { SplideSlide } from '@splidejs/vue-splide'
import { getComponentGroup } from '#layers/utils/dataUtil'
import {
getComponentGroup,
ensureMinimumSlideData,
} from '#layers/utils/dataUtil'
import type { Splide as SplideType } from '@splidejs/splide'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
interface Props {
components: Record<string, any>
components: PageDataTemplateComponents
pageVerTmplSeq: string
}
@@ -17,11 +21,7 @@ const mainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitle')
)
const slideData = computed(() => {
if (props.components.group_sets.length < 3) {
return [...props.components.group_sets, ...props.components.group_sets]
}
return props.components.group_sets
return ensureMinimumSlideData(props.components)
})
const subTitleData = ref(getComponentGroup(slideData?.value[0], 'subTitle'))
const descriptionData = ref(

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import { getComponentGroup, getComponentGroupAry } from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
interface Props {
components: Record<string, any>
components: PageDataTemplateComponents
pageVerTmplSeq: string
}
@@ -36,12 +37,12 @@ const buttonListData = computed(() =>
<WidgetsMainTitle
v-if="mainTitleData"
:resources-data="mainTitleData"
class="w-[355px] md:w-[944px]"
class="w-full max-w-[355px] md:max-w-[944px]"
/>
<WidgetsDescription
v-if="descriptionData"
:resources-data="descriptionData"
class="w-[355px] md:w-[944px]"
class="w-full max-w-[355px] md:max-w-[944px]"
/>
<client-only>
<WidgetsVideoPlay

View File

@@ -1,9 +1,14 @@
<script setup lang="ts">
import { SplideSlide } from '@splidejs/vue-splide'
import { getComponentGroup, getComponentGroupAry } from '#layers/utils/dataUtil'
import {
getComponentGroup,
getComponentGroupAry,
ensureMinimumSlideOperateData,
} from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
interface Props {
components: Record<string, any>
components: PageDataTemplateComponents
pageVerTmplSeq: string
}
@@ -48,20 +53,10 @@ const { data: resourcesData } = await useLazyAsyncData(
)
const slideData = computed(() => {
const operateComponents = resourcesData.value?.operate_components
if (!resourcesData.value) return []
if (!operateComponents) {
return []
}
const firstKey = Object.keys(operateComponents)[0]
const data = operateComponents[firstKey]?.list_operate_groups || []
if (data.length < 3) {
return [...data, ...data]
}
return data
const data = getComponentGroupAry(resourcesData.value, 'bannerList')
return ensureMinimumSlideOperateData(data)
})
const slideItemSize = {
@@ -89,12 +84,12 @@ const slideItemSize = {
<WidgetsMainTitle
v-if="mainTitleData"
:resources-data="mainTitleData"
class="w-[355px] md:w-[944px]"
class="w-full max-w-[355px] md:max-w-[944px]"
/>
<WidgetsDescription
v-if="descriptionData"
:resources-data="descriptionData"
class="w-[355px] md:w-[944px]"
class="w-full max-w-[355px] md:max-w-[944px]"
/>
<WidgetsVideoPlay
v-if="videoPlayData"

View File

@@ -5,9 +5,10 @@ import {
getComponentGroup,
getComponentGroupAry,
} from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
interface Props {
components: Record<string, any>
components: PageDataTemplateComponents
pageVerTmplSeq: string
}

View File

@@ -39,6 +39,8 @@ export interface GameDataValue {
meta_tag: GameDataMetaTag
sns: GameDataSns
footer: string // JSON 문자열로 변경
comm_img: GameDataCommImg
market: Record<string, { url: string }>
}
// ===== 세부 데이터 타입들 =====
@@ -97,6 +99,20 @@ export interface GameDataSns {
tiktok: GameDataSnsItem
}
// 공통 이미지 그룹 타입
export interface GameDataCommImgGroup {
img_name: string
img_path: {
comm: string
}
group_label: string
}
// 공통 이미지 타입
export interface GameDataCommImg {
groups: GameDataCommImgGroup[]
}
// Global 설정 타입
export interface GameDataGlobal {
system_font: string // JSON 문자열로 변경

View File

@@ -1,10 +1,3 @@
// Tracking 타입
export interface PageDataTracking {
click_item: string
action_type: string
click_sarea: string
}
// API 요청 파라미터 타입
export interface PageDataRequest {
game_alias: string
@@ -57,7 +50,7 @@ export interface PageDataLnbMenu {
menu_name: string
target_type: number
page_ver_tmpl_name_en: string
tracking: string
// tracking: Record<string, PageDataTracking>
}
// 메타 태그 타입
@@ -110,12 +103,15 @@ export interface PageDataResourceGroup {
export type PageDataResourceGroups = PageDataResourceGroup[]
// 컴포넌트 타입
export interface PageDataComponent {
// 리소스 컨테이너 타입
export interface PageDataResourceContainer {
groups: PageDataResourceGroups
}
export type PageDataTemplateComponent = Record<string, PageDataComponent>
export type PageDataTemplateComponent = Record<
string,
PageDataResourceContainer
>
// 그룹 세트 아이템 타입
export type PageDataTemplateComponentSet = PageDataTemplateComponent & {
@@ -169,3 +165,9 @@ export interface PageDataApiResult {
data: PageDataResponse | null
error: string | null
}
// Tracking 타입
export interface PageDataTracking {
click_item: string
action_type: string
click_sarea: string
}

View File

@@ -3,8 +3,9 @@
*/
// 리스트 운영 그룹 아이템
export interface ListOperateGroupItem {
export interface OperateGroupItem {
seq: number
flag_type?: number
title: string
img_path: string
url: string
@@ -16,36 +17,19 @@ export interface ListOperateGroupItem {
option03: string
}
// 플래그 운영 그룹 아이템
export interface FlagOperateGroupItem {
seq: number
flag_type: number
option01: number
option02: number
option03: string
export interface OperateGroupList {
groups: OperateGroupItem[]
}
// 운영 컴포넌트 그룹
export interface OperateComponentGroup {
list_operate_groups: ListOperateGroupItem[]
flag_operate_groups: FlagOperateGroupItem[]
}
// 운영 컴포넌트 목록 (동적 키)
export interface OperateComponents {
[key: string]: OperateComponentGroup
}
// Resources Data 응답 값
export interface ResourcesDataValue {
operate_components: OperateComponents
[key: string]: OperateGroupList
}
// Resources Data API 응답
export interface ResourcesDataResponse {
code: number
message: string
value: ResourcesDataValue
value: OperateComponents
}
// getResourcesData 함수 파라미터

View File

@@ -1,2 +1,3 @@
export type ButtonType = 'internal' | 'external' | 'download' | 'action'
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'
export type ButtonVariant = 'filled' | 'outlined'

View File

@@ -1,6 +1,6 @@
export interface DialogParams {
isShowDimmed?: boolean
className?: string
modalName?: string
isOutsideClose?: boolean
contentText: string
confirmButtonText?: string
@@ -12,5 +12,6 @@ export interface DialogParams {
export interface YoutubeParams {
youtubeUrl: string
className?: string
isOutsideClose?: boolean
modalName?: string
}

View File

@@ -5,9 +5,15 @@
import type {
PageDataValue,
PageDataComponent,
PageDataResourceContainer,
PageDataTemplateComponents,
PageDataTemplateComponentSet,
PageDataResourceGroupType,
} from '#layers/types/api/pageData'
import type {
OperateComponents,
OperateGroupItem,
} from '#layers/types/api/resourcesData'
/**
* 페이지 데이터를 기반으로 레이아웃 타입을 결정합니다.
@@ -60,52 +66,105 @@ export const isTypeButton = (type: PageDataResourceGroupType): boolean => {
/**
* 그룹의 첫 번째 데이터를 반환합니다.
* @param source props.components 또는 group 객체
* @param container 리소스 컨테이너
* @returns 첫 번째 그룹 데이터 또는 null
*/
export const getFirstGroup = (source: any) => {
if (!source) return null
return source.groups?.[0] || null
export const getFirstGroup = (container: PageDataResourceContainer) => {
if (!container) return null
return container.groups?.[0] || null
}
/**
* 컴포넌트 그룹에 데이터가 존재하는지 확인합니다.
* @param source props.components 또는 group 객체
* @param components props.components 또는 group 객체
* @param componentName 컴포넌트 이름
* @returns 데이터 존재 여부
*/
export const hasComponentGroup = (
source: any,
components: PageDataTemplateComponents,
componentName: string
): boolean => {
if (!source) return false
if (!components) return false
const component = source[componentName] as PageDataComponent
const component = components[componentName] as PageDataResourceContainer
return component?.groups && component.groups.length > 0
}
/**
* 컴포넌트 그룹의 첫 번째 데이터를 반환합니다.
* @param source props.components 또는 group 객체
* @param components props.components 또는 group 객체
* @param componentName 컴포넌트 이름
* @returns 첫 번째 그룹 데이터 또는 null
*/
export const getComponentGroup = (source: any, componentName: string) => {
if (!source) return null
export const getComponentGroup = (
components: PageDataTemplateComponents | OperateComponents,
componentName: string
) => {
if (!components) return null
return getFirstGroup(source[componentName])
const component = components[componentName] as PageDataResourceContainer
return getFirstGroup(component)
}
/**
* 컴포넌트 그룹의 모든 데이터를 반환합니다.
* @param source props.components 또는 group 객체
* @param components props.components 또는 group 객체
* @param componentName 컴포넌트 이름
* @returns 그룹 배열 데이터
*/
export const getComponentGroupAry = (source: any, componentName: string) => {
if (!source) return []
export const getComponentGroupAry = (
components: PageDataTemplateComponents | OperateComponents,
componentName: string
) => {
if (!components) return []
return source[componentName]?.groups || []
return components[componentName]?.groups || []
}
/**
* 슬라이드 데이터를 최소 개수로 보장합니다. (페이지 데이터용)
* @param components 원본 데이터 배열 또는 객체
* @param minCount 최소 보장할 개수 (기본값: 3)
* @returns 최소 개수가 보장된 데이터 배열
*/
export const ensureMinimumSlideData = (
components: PageDataTemplateComponents,
minCount: number = 3
): PageDataTemplateComponentSet[] => {
if (!components) return []
const arrayData = Array.isArray(components.group_sets)
? components.group_sets
: []
// 빈 배열이거나 이미 최소 개수를 만족하면 그대로 반환
if (arrayData.length === 0 || arrayData.length >= minCount) {
return arrayData
}
// 최소 개수를 보장하기 위해 데이터 반복
const repeatTimes = Math.ceil(minCount / arrayData.length)
return Array(repeatTimes).fill(arrayData).flat()
}
/**
* 슬라이드 데이터를 최소 개수로 보장합니다. (운영 그룹용)
* @param data 원본 데이터 배열
* @param minCount 최소 보장할 개수 (기본값: 3)
* @returns 최소 개수가 보장된 데이터 배열
*/
export const ensureMinimumSlideOperateData = (
data: OperateGroupItem[],
minCount: number = 3
): OperateGroupItem[] => {
// 빈 배열이거나 이미 최소 개수를 만족하면 그대로 반환
if (data.length === 0 || data.length >= minCount) {
return data
}
// 최소 개수를 보장하기 위해 데이터 반복
const repeatTimes = Math.ceil(minCount / data.length)
return Array(repeatTimes).fill(data).flat()
}
/**

View File

@@ -3,11 +3,8 @@
* @description 유튜브 관련 유틸리티 함수를 제공합니다.
*/
import { getFirstGroup, isTypeVideo } from '#layers/utils/dataUtil'
import type {
PageDataResourceGroups,
PageDataResourceGroup,
} from '#layers/types/api/pageData'
import { isTypeVideo } from '#layers/utils/dataUtil'
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
/**
* 유튜브 URL에서 비디오 ID를 추출합니다.
@@ -114,14 +111,13 @@ export const getMediaSrc = (resourceGroup: PageDataResourceGroup): string => {
* @returns 미디어 이미지 소스
*/
export const getMediaImgSrc = (
resourceGroups: PageDataResourceGroups,
resourceGroups: PageDataResourceGroup,
quality: 'default' | 'medium' | 'high' | 'standard' | 'maxres' = 'high'
): string => {
if (!resourceGroups) return ''
const resourceGroup = getFirstGroup(resourceGroups)
const mediaSrc = getMediaSrc(resourceGroup)
const mediaType = resourceGroup?.resource_type
const mediaSrc = getMediaSrc(resourceGroups)
const mediaType = resourceGroups?.resource_type
if (isTypeVideo(mediaType) && mediaSrc) {
const videoId = getYouTubeVideoId(mediaSrc)

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,20 +0,0 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15070_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15070)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.2929 18.2929C23.9024 18.6834 23.9024 19.3166 24.2929 19.7071L30.5858 26L24.2929 32.2929C23.9024 32.6834 23.9024 33.3166 24.2929 33.7071C24.6834 34.0976 25.3166 34.0976 25.7071 33.7071L32.7071 26.7071C33.0976 26.3166 33.0976 25.6834 32.7071 25.2929L25.7071 18.2929C25.3166 17.9024 24.6834 17.9024 24.2929 18.2929Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3300_15070" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15070"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15070" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15070_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,20 +0,0 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15069_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15069)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.7071 18.2929C32.0976 18.6834 32.0976 19.3166 31.7071 19.7071L25.4142 26L31.7071 32.2929C32.0976 32.6834 32.0976 33.3166 31.7071 33.7071C31.3166 34.0976 30.6834 34.0976 30.2929 33.7071L23.2929 26.7071C22.9024 26.3166 22.9024 25.6834 23.2929 25.2929L30.2929 18.2929C30.6834 17.9024 31.3166 17.9024 31.7071 18.2929Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3300_15069" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15069"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15069" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15069_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,21 +0,0 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15073_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15073)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path d="M29 23.4142L33.2929 27.7071C33.6834 28.0976 34.3166 28.0976 34.7071 27.7071C35.0976 27.3166 35.0976 26.6834 34.7071 26.2929L28.7078 20.2936C28.5289 20.1143 28.2822 20.0026 28.0094 20C28.0063 20 28.0032 20 28 20C27.9968 20 27.9937 20 27.9906 20C27.7269 20.0025 27.4877 20.1069 27.3104 20.2758C27.3045 20.2815 27.2987 20.2871 27.2929 20.2929L21.2929 26.2929C20.9024 26.6834 20.9024 27.3166 21.2929 27.7071C21.6834 28.0976 22.3166 28.0976 22.7071 27.7071L27 23.4142L27 34C27 34.5523 27.4477 35 28 35C28.5523 35 29 34.5523 29 34L29 23.4142Z" fill="white" fill-opacity="0.5"/>
<path d="M35.5 18C35.5 18.5523 35.0523 19 34.5 19L21.5 19C20.9477 19 20.5 18.5523 20.5 18C20.5 17.4477 20.9477 17 21.5 17L34.5 17C35.0523 17 35.5 17.4477 35.5 18Z" fill="white" fill-opacity="0.5"/>
</g>
<defs>
<filter id="filter0_d_3300_15073" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15073"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15073" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15073_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -1,13 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5105_3183)">
<path d="M9.86253 9.63379L2.8042 17.013C3.03337 17.7921 3.7667 18.388 4.63753 18.388C5.0042 18.388 5.32503 18.2963 5.60003 18.113L13.575 13.5755L9.86253 9.63379Z" fill="#EA4335"/>
<path d="M17.0126 8.35023L13.5751 6.37939L9.7251 9.77106L13.6209 13.5752L17.0584 11.6502C17.6543 11.3294 18.0668 10.6877 18.0668 10.0002C18.0209 9.31273 17.6084 8.67106 17.0126 8.35023Z" fill="#FBBC04"/>
<path d="M2.80426 2.98779C2.75842 3.12529 2.75842 3.30863 2.75842 3.49196V16.5545C2.75842 16.7378 2.75842 16.8753 2.80426 17.0586L10.1376 9.86279L2.80426 2.98779Z" fill="#4285F4"/>
<path d="M9.90836 10.0003L13.575 6.37946L5.64587 1.88779C5.37087 1.70446 5.0042 1.61279 4.63753 1.61279C3.7667 1.61279 2.98753 2.20863 2.8042 2.98779L9.90836 10.0003Z" fill="#34A853"/>
</g>
<defs>
<clipPath id="clip0_5105_3183">
<rect width="18.3333" height="18.3333" fill="white" transform="translate(0.833374 0.833496)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 517 B

View File

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.91082 3.57757C6.58539 3.90301 6.58539 4.43065 6.91082 4.75609L12.1549 10.0002L6.91082 15.2442C6.58539 15.5697 6.58539 16.0973 6.91082 16.4228C7.23626 16.7482 7.7639 16.7482 8.08934 16.4228L13.9227 10.5894C14.2481 10.264 14.2481 9.73634 13.9227 9.41091L8.08934 3.57757C7.7639 3.25214 7.23626 3.25214 6.91082 3.57757Z" fill="#7F7F7F"/>
</svg>

Before

Width:  |  Height:  |  Size: 489 B

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7071 5.70711C20.0976 5.31658 20.0976 4.68342 19.7071 4.29289C19.3166 3.90237 18.6834 3.90237 18.2929 4.29289L12 10.5858L5.70711 4.29289C5.31658 3.90237 4.68342 3.90237 4.29289 4.29289C3.90237 4.68342 3.90237 5.31658 4.29289 5.70711L10.5858 12L4.29289 18.2929C3.90237 18.6834 3.90237 19.3166 4.29289 19.7071C4.68342 20.0976 5.31658 20.0976 5.70711 19.7071L12 13.4142L18.2929 19.7071C18.6834 20.0976 19.3166 20.0976 19.7071 19.7071C20.0976 19.3166 20.0976 18.6834 19.7071 18.2929L13.4142 12L19.7071 5.70711Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 640 B

View File

@@ -1,3 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.29499 8.215L2.39999 5.375C2.07499 5.055 2.29999 4.5 2.75999 4.5L9.23499 4.5C9.69499 4.5 9.91999 5.055 9.59499 5.375L6.69999 8.215C6.30999 8.595 5.68999 8.595 5.29999 8.215H5.29499Z" fill="#7F7F7F"/>
</svg>

Before

Width:  |  Height:  |  Size: 314 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.63636 3.33333C3.469 3.33333 3.33333 3.469 3.33333 3.63636L3.33333 12.3636C3.33333 12.531 3.469 12.6667 3.63636 12.6667H12.3636C12.531 12.6667 12.6667 12.531 12.6667 12.3636V9.93939C12.6667 9.5712 12.9651 9.27273 13.3333 9.27273C13.7015 9.27273 14 9.5712 14 9.93939V12.3636C14 13.2674 13.2674 14 12.3636 14H3.63636C2.73262 14 2 13.2674 2 12.3636L2 3.63636C2 2.73263 2.73262 2 3.63636 2L6.06061 2C6.4288 2 6.72727 2.29848 6.72727 2.66667C6.72727 3.03486 6.4288 3.33333 6.06061 3.33333H3.63636Z" fill="#7F7F7F"/>
<path d="M12.6667 4.27614V6.54545C12.6667 6.91364 12.9651 7.21212 13.3333 7.21212C13.7015 7.21212 14 6.91364 14 6.54545V2.66667C14 2.29848 13.7015 2 13.3333 2L9.45455 2C9.08636 2 8.78788 2.29848 8.78788 2.66667C8.78788 3.03486 9.08636 3.33333 9.45455 3.33333L11.7239 3.33333L7.28616 7.77103C7.02581 8.03138 7.02581 8.45349 7.28616 8.71384C7.54651 8.97419 7.96862 8.97419 8.22897 8.71384L12.6667 4.27614Z" fill="#7F7F7F"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,4 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.237 3.76397C12.7632 3.09926 13.1113 2.17037 13.0142 1.25C12.2613 1.28409 11.3545 1.77836 10.8121 2.44307C10.3264 3.03109 9.90542 3.97702 10.0188 4.88035C10.8526 4.94852 11.7108 4.42868 12.237 3.76397Z" fill="#EBEBEB"/>
<path d="M13.4325 5.21408L13.248 5.19858C12.5336 5.14288 11.8799 5.34976 11.3277 5.55942L10.7675 5.77674C10.5095 5.87346 10.2861 5.9429 10.1039 5.9429C9.90183 5.9429 9.66519 5.87108 9.40315 5.77314L8.73016 5.51131C8.31461 5.35663 7.86368 5.2224 7.40258 5.23403C6.01207 5.25175 4.73671 6.04037 4.01931 7.28977C2.57567 9.79743 3.64733 13.5013 5.05555 15.5393L5.29985 15.8859L5.46886 16.1166L5.64325 16.343C6.20438 17.0495 6.8521 17.6387 7.64171 17.6128C8.06839 17.5946 8.38546 17.4772 8.69864 17.3454L8.96859 17.2308C9.33383 17.0787 9.73346 16.9394 10.3253 16.9394C10.8395 16.9394 11.2021 17.0535 11.5273 17.186L11.934 17.3589C12.2483 17.4894 12.5744 17.5972 13.0266 17.5862C13.964 17.5713 14.6139 16.8687 15.2021 16.0508L15.3687 15.8144L15.6157 15.4515C15.6967 15.3285 15.7724 15.2063 15.8431 15.0862L15.9778 14.8492C15.9992 14.8102 16.02 14.7716 16.0402 14.7334L16.1554 14.5088C16.1735 14.4723 16.1911 14.4361 16.2082 14.4006L16.3046 14.1935L16.3888 14.0008L16.4613 13.8249L16.5485 13.5971L16.6111 13.4194L16.6667 13.2443L16.6024 13.2177L16.4408 13.1398L16.27 13.0449L16.138 12.9632L15.9949 12.8661C15.3552 12.4107 14.4871 11.5037 14.4702 9.93034C14.4579 8.4852 15.2845 7.56519 15.8136 7.12531L15.9441 7.02142C15.9645 7.00591 15.9841 6.9913 16.0029 6.97759L16.1467 6.87828L16.2416 6.82014C15.5921 5.86906 14.7104 5.48115 14.0399 5.31771L13.8455 5.27508L13.6665 5.24388L13.5054 5.22208C13.4803 5.21912 13.4559 5.21647 13.4325 5.21408Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 16C20.7594 16 21.375 16.6156 21.375 17.375C21.375 18.1344 20.7594 18.75 20 18.75H4C3.24061 18.75 2.625 18.1344 2.625 17.375C2.625 16.6156 3.24061 16 4 16H20ZM20 10.5C20.7594 10.5 21.375 11.1156 21.375 11.875C21.375 12.6344 20.7594 13.25 20 13.25H4C3.24061 13.25 2.625 12.6344 2.625 11.875C2.625 11.1156 3.24061 10.5 4 10.5H20ZM20 5C20.7594 5 21.375 5.61561 21.375 6.375C21.375 7.13439 20.7594 7.75 20 7.75H4C3.24061 7.75 2.625 7.13439 2.625 6.375C2.625 5.61561 3.24061 5 4 5H20Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 613 B

View File

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1017 0.90918L10.4045 0.913291C11.9002 0.952388 14.4916 1.28038 15.5523 2.32323C15.9914 2.75485 16.2077 3.28804 16.2077 3.97366C16.2077 5.04013 15.4683 5.83518 14.2801 5.83518C14.0487 5.83518 13.7651 5.77777 13.4329 5.69509L12.6484 5.49098L12.2949 5.40343C11.6265 5.24464 10.8475 5.10286 9.97258 5.10286L9.84545 5.10354L9.59838 5.10924C9.51773 5.11217 9.43885 5.11618 9.36194 5.12139L9.13735 5.14078C8.33708 5.22611 7.80063 5.47324 7.80063 6.05628C7.80063 6.21288 7.84333 6.36401 7.96719 6.54628C8.01569 6.61772 8.07818 6.68849 8.15471 6.75733L8.24073 6.82937C8.3729 6.93934 8.54119 7.03723 8.73902 7.12717L8.91566 7.20241L9.10586 7.27433C9.13863 7.28607 9.17194 7.2977 9.20575 7.30922L9.41453 7.37725L9.63457 7.44341L9.98368 7.54039L10.4799 7.66855L11.4122 7.90261L11.8286 8.0118L12.1095 8.08899L12.392 8.17038L12.6751 8.25655C12.7223 8.27134 12.7695 8.28635 12.8166 8.3016L13.099 8.39601L13.3797 8.49664C15.4299 9.25882 17.2725 10.5694 17.2725 13.4541C17.2725 17.2883 13.9244 19.091 9.76596 19.091C9.56983 19.091 9.36794 19.0859 9.16197 19.0755L8.85017 19.0561L8.53352 19.0289C8.4273 19.0185 8.32048 19.0068 8.21327 18.9937L7.8907 18.9506C5.35803 18.5819 2.72705 17.478 2.72705 15.5361C2.72705 14.419 3.45993 13.4555 4.64808 13.4555C4.99219 13.4555 5.3796 13.5796 5.83145 13.7526L6.71895 14.1005C7.63152 14.4483 8.76701 14.807 10.2294 14.807C11.7902 14.807 12.5281 14.4752 12.5281 13.7841C12.5281 13.4541 12.3989 13.266 12.1923 13.0628C11.9581 12.8431 11.6124 12.6589 11.1911 12.4893L10.9743 12.4055L10.7458 12.3237L10.5067 12.2431L10.1303 12.1233L8.76156 11.7101L8.32963 11.5744L7.89382 11.4298L7.60276 11.3275L7.31244 11.2198C7.0711 11.1276 6.83156 11.0292 6.59641 10.9231L6.31649 10.7919C4.55912 9.93633 3.11456 8.59871 3.11456 6.11604C3.11456 2.89126 5.99488 0.90918 10.1017 0.90918Z" fill="#FC4420"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.7929 18.2929C11.4024 18.6834 11.4024 19.3166 11.7929 19.7071C12.1834 20.0976 12.8166 20.0976 13.2071 19.7071L20.2071 12.7071C20.5976 12.3166 20.5976 11.6834 20.2071 11.2929L13.2071 4.29289C12.8166 3.90237 12.1834 3.90237 11.7929 4.29289C11.4024 4.68342 11.4024 5.31658 11.7929 5.70711L17.0858 11L4.5 11C3.94771 11 3.5 11.4477 3.5 12C3.5 12.5523 3.94771 13 4.5 13L17.0858 13L11.7929 18.2929Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 525 B

View File

@@ -1,4 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.45455 5C5.20351 5 5 5.20351 5 5.45455V18.5455C5 18.7965 5.20351 19 5.45455 19H18.5455C18.7965 19 19 18.7965 19 18.5455V14.9091C19 14.3568 19.4477 13.9091 20 13.9091C20.5523 13.9091 21 14.3568 21 14.9091V18.5455C21 19.9011 19.9011 21 18.5455 21H5.45455C4.09894 21 3 19.9011 3 18.5455L3 5.45455C3 4.09894 4.09894 3 5.45455 3L9.09091 3C9.64319 3 10.0909 3.44772 10.0909 4C10.0909 4.55228 9.64319 5 9.09091 5H5.45455Z" fill="#EBEBEB"/>
<path d="M19 6.41421V9.81818C19 10.3705 19.4477 10.8182 20 10.8182C20.5523 10.8182 21 10.3705 21 9.81818V4C21 3.44772 20.5523 3 20 3H14.1818C13.6295 3 13.1818 3.44772 13.1818 4C13.1818 4.55228 13.6295 5 14.1818 5L17.5858 5L10.9292 11.6565C10.5387 12.0471 10.5387 12.6802 10.9292 13.0708C11.3198 13.4613 11.9529 13.4613 12.3435 13.0708L19 6.41421Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 912 B

View File

@@ -1,4 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.75 3.25L10.75 12.2322L6.88391 8.36611C6.39576 7.87796 5.6043 7.87796 5.11615 8.36611C4.62799 8.85427 4.62799 9.64573 5.11615 10.1339L11.1161 16.1339C11.3506 16.3683 11.6685 16.5 12 16.5C12.3316 16.5 12.6495 16.3683 12.8839 16.1339L18.8839 10.1339C19.3721 9.64573 19.3721 8.85427 18.8839 8.36611C18.3958 7.87796 17.6043 7.87796 17.1161 8.36611L13.25 12.2322L13.25 3.25C13.25 2.55964 12.6904 2 12 2C11.3097 2 10.75 2.55964 10.75 3.25Z" fill="#EBEBEB"/>
<path d="M20 21C20.6904 21 21.25 20.4404 21.25 19.75L21.25 17.75C21.25 17.0596 20.6904 16.5 20 16.5C19.3097 16.5 18.75 17.0596 18.75 17.75L18.75 18.5L5.25003 18.5L5.25003 17.75C5.25003 17.0596 4.69039 16.5 4.00003 16.5C3.30967 16.5 2.75003 17.0596 2.75003 17.75L2.75003 19.75C2.75003 20.4404 3.30967 21 4.00003 21L20 21Z" fill="#EBEBEB"/>
</svg>

Before

Width:  |  Height:  |  Size: 906 B

View File

@@ -1,13 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5105_5120)">
<path d="M9.86253 9.63379L2.8042 17.013C3.03337 17.7921 3.7667 18.388 4.63753 18.388C5.0042 18.388 5.32503 18.2963 5.60003 18.113L13.575 13.5755L9.86253 9.63379Z" fill="#EA4335"/>
<path d="M17.0126 8.35023L13.5751 6.37939L9.7251 9.77106L13.6209 13.5752L17.0584 11.6502C17.6543 11.3294 18.0668 10.6877 18.0668 10.0002C18.0209 9.31273 17.6084 8.67106 17.0126 8.35023Z" fill="#FBBC04"/>
<path d="M2.80413 2.98779C2.7583 3.12529 2.7583 3.30863 2.7583 3.49196V16.5545C2.7583 16.7378 2.7583 16.8753 2.80413 17.0586L10.1375 9.86279L2.80413 2.98779Z" fill="#4285F4"/>
<path d="M9.90836 10.0003L13.575 6.37946L5.64587 1.88779C5.37087 1.70446 5.0042 1.61279 4.63753 1.61279C3.7667 1.61279 2.98753 2.20863 2.8042 2.98779L9.90836 10.0003Z" fill="#34A853"/>
</g>
<defs>
<clipPath id="clip0_5105_5120">
<rect width="18.3333" height="18.3333" fill="white" transform="translate(0.833496 0.833496)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z" fill="#1F1F1F"/>
</svg>

Before

Width:  |  Height:  |  Size: 517 B

View File

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.91058 3.57709C6.58514 3.90252 6.58514 4.43016 6.91058 4.7556L12.1547 9.99967L6.91058 15.2438C6.58514 15.5692 6.58514 16.0968 6.91058 16.4223C7.23602 16.7477 7.76366 16.7477 8.08909 16.4223L13.9224 10.5889C14.2479 10.2635 14.2479 9.73586 13.9224 9.41042L8.08909 3.57709C7.76366 3.25165 7.23602 3.25165 6.91058 3.57709Z" fill="#999999"/>
</svg>

Before

Width:  |  Height:  |  Size: 491 B

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7071 5.70711C20.0976 5.31658 20.0976 4.68342 19.7071 4.29289C19.3166 3.90237 18.6834 3.90237 18.2929 4.29289L12 10.5858L5.70711 4.29289C5.31658 3.90237 4.68342 3.90237 4.29289 4.29289C3.90237 4.68342 3.90237 5.31658 4.29289 5.70711L10.5858 12L4.29289 18.2929C3.90237 18.6834 3.90237 19.3166 4.29289 19.7071C4.68342 20.0976 5.31658 20.0976 5.70711 19.7071L12 13.4142L18.2929 19.7071C18.6834 20.0976 19.3166 20.0976 19.7071 19.7071C20.0976 19.3166 20.0976 18.6834 19.7071 18.2929L13.4142 12L19.7071 5.70711Z" fill="#1F1F1F"/>
</svg>

Before

Width:  |  Height:  |  Size: 640 B

View File

@@ -1,3 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.29499 8.215L2.39999 5.375C2.07499 5.055 2.29999 4.5 2.75999 4.5L9.23499 4.5C9.69499 4.5 9.91999 5.055 9.59499 5.375L6.69999 8.215C6.30999 8.595 5.68999 8.595 5.29999 8.215H5.29499Z" fill="#999999"/>
</svg>

Before

Width:  |  Height:  |  Size: 314 B

View File

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.63636 3.33333C3.469 3.33333 3.33333 3.469 3.33333 3.63636L3.33333 12.3636C3.33333 12.531 3.469 12.6667 3.63636 12.6667H12.3636C12.531 12.6667 12.6667 12.531 12.6667 12.3636V9.93939C12.6667 9.5712 12.9651 9.27273 13.3333 9.27273C13.7015 9.27273 14 9.5712 14 9.93939V12.3636C14 13.2674 13.2674 14 12.3636 14H3.63636C2.73262 14 2 13.2674 2 12.3636L2 3.63636C2 2.73263 2.73262 2 3.63636 2L6.06061 2C6.4288 2 6.72727 2.29848 6.72727 2.66667C6.72727 3.03486 6.4288 3.33333 6.06061 3.33333H3.63636Z" fill="#999999"/>
<path d="M12.6667 4.27614V6.54545C12.6667 6.91364 12.9651 7.21212 13.3333 7.21212C13.7015 7.21212 14 6.91364 14 6.54545V2.66667C14 2.29848 13.7015 2 13.3333 2L9.45455 2C9.08636 2 8.78788 2.29848 8.78788 2.66667C8.78788 3.03486 9.08636 3.33333 9.45455 3.33333L11.7239 3.33333L7.28616 7.77103C7.02581 8.03138 7.02581 8.45349 7.28616 8.71384C7.54651 8.97419 7.96862 8.97419 8.22897 8.71384L12.6667 4.27614Z" fill="#999999"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,4 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.2371 3.76397C12.7633 3.09926 13.1114 2.17037 13.0143 1.25C12.2614 1.28409 11.3547 1.77836 10.8123 2.44307C10.3265 3.03109 9.90554 3.97702 10.0189 4.88035C10.8527 4.94852 11.7109 4.42868 12.2371 3.76397Z" fill="#1F1F1F"/>
<path d="M13.4326 5.21408L13.2481 5.19858C12.5337 5.14288 11.88 5.34976 11.3278 5.55942L10.7676 5.77674C10.5096 5.87346 10.2862 5.9429 10.104 5.9429C9.90196 5.9429 9.66532 5.87108 9.40327 5.77314L8.73028 5.51131C8.31473 5.35663 7.8638 5.2224 7.4027 5.23403C6.01219 5.25175 4.73683 6.04037 4.01943 7.28977C2.57579 9.79743 3.64745 13.5013 5.05567 15.5393L5.29997 15.8859L5.46899 16.1166L5.64337 16.343C6.2045 17.0495 6.85222 17.6387 7.64183 17.6128C8.06852 17.5946 8.38558 17.4772 8.69876 17.3454L8.96871 17.2308C9.33396 17.0787 9.73358 16.9394 10.3254 16.9394C10.8396 16.9394 11.2022 17.0535 11.5274 17.186L11.9341 17.3589C12.2484 17.4894 12.5745 17.5972 13.0267 17.5862C13.9641 17.5713 14.614 16.8687 15.2022 16.0508L15.3688 15.8144L15.6159 15.4515C15.6968 15.3285 15.7725 15.2063 15.8432 15.0862L15.9779 14.8492C15.9993 14.8102 16.0201 14.7716 16.0404 14.7334L16.1555 14.5088C16.1737 14.4723 16.1913 14.4361 16.2083 14.4006L16.3047 14.1935L16.389 14.0008L16.4614 13.8249L16.5487 13.5971L16.6113 13.4194L16.6668 13.2443L16.6025 13.2177L16.441 13.1398L16.2701 13.0449L16.1381 12.9632L15.995 12.8661C15.3553 12.4107 14.4872 11.5037 14.4704 9.93034C14.458 8.4852 15.2846 7.56519 15.8137 7.12531L15.9442 7.02142C15.9646 7.00591 15.9843 6.9913 16.003 6.97759L16.1469 6.87828L16.2417 6.82014C15.5922 5.86906 14.7105 5.48115 14.04 5.31771L13.8456 5.27508L13.6666 5.24388L13.5056 5.22208C13.4804 5.21912 13.456 5.21647 13.4326 5.21408Z" fill="#1F1F1F"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 16C20.7594 16 21.375 16.6156 21.375 17.375C21.375 18.1344 20.7594 18.75 20 18.75H4C3.24061 18.75 2.625 18.1344 2.625 17.375C2.625 16.6156 3.24061 16 4 16H20ZM20 10.5C20.7594 10.5 21.375 11.1156 21.375 11.875C21.375 12.6344 20.7594 13.25 20 13.25H4C3.24061 13.25 2.625 12.6344 2.625 11.875C2.625 11.1156 3.24061 10.5 4 10.5H20ZM20 5C20.7594 5 21.375 5.61561 21.375 6.375C21.375 7.13439 20.7594 7.75 20 7.75H4C3.24061 7.75 2.625 7.13439 2.625 6.375C2.625 5.61561 3.24061 5 4 5H20Z" fill="#1F1F1F"/>
</svg>

Before

Width:  |  Height:  |  Size: 613 B

View File

@@ -1,3 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1017 0.90918L10.4045 0.913291C11.9002 0.952388 14.4916 1.28038 15.5523 2.32323C15.9914 2.75485 16.2077 3.28804 16.2077 3.97366C16.2077 5.04013 15.4683 5.83518 14.2801 5.83518C14.0487 5.83518 13.7651 5.77777 13.4329 5.69509L12.6484 5.49098L12.2949 5.40343C11.6265 5.24464 10.8475 5.10286 9.97258 5.10286L9.84545 5.10354L9.59838 5.10924C9.51773 5.11217 9.43885 5.11618 9.36194 5.12139L9.13735 5.14078C8.33708 5.22611 7.80063 5.47324 7.80063 6.05628C7.80063 6.21288 7.84333 6.36401 7.96719 6.54628C8.01569 6.61772 8.07818 6.68849 8.15471 6.75733L8.24073 6.82937C8.3729 6.93934 8.54119 7.03723 8.73902 7.12717L8.91566 7.20241L9.10586 7.27433C9.13863 7.28607 9.17194 7.2977 9.20575 7.30922L9.41453 7.37725L9.63457 7.44341L9.98368 7.54039L10.4799 7.66855L11.4122 7.90261L11.8286 8.0118L12.1095 8.08899L12.392 8.17038L12.6751 8.25655C12.7223 8.27134 12.7695 8.28635 12.8166 8.3016L13.099 8.39601L13.3797 8.49664C15.4299 9.25882 17.2725 10.5694 17.2725 13.4541C17.2725 17.2883 13.9244 19.091 9.76596 19.091C9.56983 19.091 9.36794 19.0859 9.16197 19.0755L8.85017 19.0561L8.53352 19.0289C8.4273 19.0185 8.32048 19.0068 8.21327 18.9937L7.8907 18.9506C5.35803 18.5819 2.72705 17.478 2.72705 15.5361C2.72705 14.419 3.45993 13.4555 4.64808 13.4555C4.99219 13.4555 5.3796 13.5796 5.83145 13.7526L6.71895 14.1005C7.63152 14.4483 8.76701 14.807 10.2294 14.807C11.7902 14.807 12.5281 14.4752 12.5281 13.7841C12.5281 13.4541 12.3989 13.266 12.1923 13.0628C11.9581 12.8431 11.6124 12.6589 11.1911 12.4893L10.9743 12.4055L10.7458 12.3237L10.5067 12.2431L10.1303 12.1233L8.76156 11.7101L8.32963 11.5744L7.89382 11.4298L7.60276 11.3275L7.31244 11.2198C7.0711 11.1276 6.83156 11.0292 6.59641 10.9231L6.31649 10.7919C4.55912 9.93633 3.11456 8.59871 3.11456 6.11604C3.11456 2.89126 5.99488 0.90918 10.1017 0.90918Z" fill="#FC4420"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@@ -1,20 +1,20 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15076_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15076)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="black" fill-opacity="0.2" shape-rendering="crispEdges"/>
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15070_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15070)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.2929 18.2929C23.9024 18.6834 23.9024 19.3166 24.2929 19.7071L30.5858 26L24.2929 32.2929C23.9024 32.6834 23.9024 33.3166 24.2929 33.7071C24.6834 34.0976 25.3166 34.0976 25.7071 33.7071L32.7071 26.7071C33.0976 26.3166 33.0976 25.6834 32.7071 25.2929L25.7071 18.2929C25.3166 17.9024 24.6834 17.9024 24.2929 18.2929Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3300_15076" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<filter id="filter0_d_3300_15070" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15076"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15076" result="shape"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15070"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15070" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15076_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
<clipPath id="bgblur_0_3300_15070_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,20 +1,20 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15075_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15075)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="black" fill-opacity="0.2" shape-rendering="crispEdges"/>
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15069_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15069)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.7071 18.2929C32.0976 18.6834 32.0976 19.3166 31.7071 19.7071L25.4142 26L31.7071 32.2929C32.0976 32.6834 32.0976 33.3166 31.7071 33.7071C31.3166 34.0976 30.6834 34.0976 30.2929 33.7071L23.2929 26.7071C22.9024 26.3166 22.9024 25.6834 23.2929 25.2929L30.2929 18.2929C30.6834 17.9024 31.3166 17.9024 31.7071 18.2929Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_3300_15075" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<filter id="filter0_d_3300_15069" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15075"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15075" result="shape"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15069"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15069" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15075_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
<clipPath id="bgblur_0_3300_15069_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,21 +1,21 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15079_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15079)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="black" fill-opacity="0.2" shape-rendering="crispEdges"/>
<foreignObject x="-26" y="-28" width="108" height="108"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(15px);clip-path:url(#bgblur_0_3300_15073_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_d_3300_15073)" data-figma-bg-blur-radius="30">
<path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z" fill="white" fill-opacity="0.1" shape-rendering="crispEdges"/>
<path d="M28 2.5C40.9787 2.5 51.5 13.0213 51.5 26C51.5 38.9787 40.9787 49.5 28 49.5C15.0213 49.5 4.5 38.9787 4.5 26C4.5 13.0213 15.0213 2.5 28 2.5Z" stroke="white" stroke-opacity="0.06" shape-rendering="crispEdges"/>
<path d="M29 23.4142L33.2929 27.7071C33.6834 28.0976 34.3166 28.0976 34.7071 27.7071C35.0976 27.3166 35.0976 26.6834 34.7071 26.2929L28.7078 20.2936C28.5289 20.1143 28.2822 20.0026 28.0094 20C28.0063 20 28.0032 20 28 20C27.9968 20 27.9937 20 27.9906 20C27.7269 20.0025 27.4877 20.1069 27.3104 20.2758C27.3045 20.2815 27.2987 20.2871 27.2929 20.2929L21.2929 26.2929C20.9024 26.6834 20.9024 27.3166 21.2929 27.7071C21.6834 28.0976 22.3166 28.0976 22.7071 27.7071L27 23.4142L27 34C27 34.5523 27.4477 35 28 35C28.5523 35 29 34.5523 29 34L29 23.4142Z" fill="white" fill-opacity="0.5"/>
<path d="M35.5 18C35.5 18.5523 35.0523 19 34.5 19L21.5 19C20.9477 19 20.5 18.5523 20.5 18C20.5 17.4477 20.9477 17 21.5 17L34.5 17C35.0523 17 35.5 17.4477 35.5 18Z" fill="white" fill-opacity="0.5"/>
</g>
<defs>
<filter id="filter0_d_3300_15079" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<filter id="filter0_d_3300_15073" x="-26" y="-28" width="108" height="108" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15079"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15079" result="shape"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3300_15073"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3300_15073" result="shape"/>
</filter>
<clipPath id="bgblur_0_3300_15079_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
<clipPath id="bgblur_0_3300_15073_clip_path" transform="translate(26 28)"><path d="M4 26C4 12.7452 14.7452 2 28 2C41.2548 2 52 12.7452 52 26C52 39.2548 41.2548 50 28 50C14.7452 50 4 39.2548 4 26Z"/>
</clipPath></defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB