fix. 버튼 컴포넌트 링크 적용, 코드 리팩토링
This commit is contained in:
@@ -1,44 +1,34 @@
|
|||||||
/* Button Size Classes */
|
/* Button Size Classes */
|
||||||
@layer components {
|
@layer components {
|
||||||
.btn-base {
|
.btn-base {
|
||||||
@apply relative inline-flex items-center justify-center font-medium border border-gray-600/30 overflow-hidden;
|
@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 */
|
/* 기본 크기: size-medium */
|
||||||
--btn-padding: theme('spacing.10');
|
px-10 h-14 text-base rounded-lg;
|
||||||
--btn-height: theme('spacing.14');
|
|
||||||
--btn-text: theme('fontSize.base');
|
|
||||||
--btn-radius: theme('borderRadius.lg');
|
|
||||||
@apply px-10 h-14 text-base rounded-lg;
|
|
||||||
}
|
}
|
||||||
|
.btn-base:hover {
|
||||||
.size-extra-small {
|
@apply after:opacity-20;
|
||||||
--btn-padding: theme('spacing.6');
|
|
||||||
--btn-height: theme('spacing.10');
|
|
||||||
--btn-text: theme('fontSize.sm');
|
|
||||||
--btn-radius: theme('borderRadius.DEFAULT');
|
|
||||||
@apply px-6 h-10 text-sm rounded;
|
|
||||||
}
|
}
|
||||||
|
.btn-base:disabled {
|
||||||
.size-small {
|
@apply cursor-default
|
||||||
--btn-padding: theme('spacing.10');
|
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
|
||||||
--btn-height: theme('spacing.12');
|
|
||||||
--btn-text: theme('fontSize.sm');
|
|
||||||
--btn-radius: theme('borderRadius.lg');
|
|
||||||
@apply px-10 h-12 text-sm rounded-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.size-medium {
|
|
||||||
--btn-padding: theme('spacing.10');
|
|
||||||
--btn-height: theme('spacing.14');
|
|
||||||
--btn-text: theme('fontSize.base');
|
|
||||||
--btn-radius: theme('borderRadius.lg');
|
|
||||||
@apply px-10 h-14 text-base rounded-lg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.size-large {
|
.size-large {
|
||||||
--btn-padding: theme('spacing.10');
|
@apply px-10 h-16 text-lg;
|
||||||
--btn-height: theme('spacing.16');
|
}
|
||||||
--btn-text: theme('fontSize.lg');
|
|
||||||
--btn-radius: theme('borderRadius.lg');
|
.size-medium {
|
||||||
@apply px-10 h-16 text-lg rounded-lg;
|
@apply px-10 h-14 text-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-small {
|
||||||
|
@apply px-10 h-12 text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size-extra-small {
|
||||||
|
@apply before:rounded after:rounded
|
||||||
|
px-6 h-10 text-sm rounded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,34 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { GameDataKeyCodeCodes } from '#layers/types/api/gameData'
|
import type { ButtonType } from '#layers/types/components/button'
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
backgroundColor?: string
|
type?: ButtonType
|
||||||
textColor?: string
|
|
||||||
icon?: string
|
icon?: string
|
||||||
disabled?: boolean
|
target?: '_self' | '_blank'
|
||||||
|
href?: string
|
||||||
|
rel?: string
|
||||||
|
backgroundColor?: string
|
||||||
backgroundImage?: string
|
backgroundImage?: string
|
||||||
|
textColor?: string
|
||||||
|
disabled?: boolean
|
||||||
|
class?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||||
|
type: 'action',
|
||||||
backgroundColor: 'var(--primary)',
|
backgroundColor: 'var(--primary)',
|
||||||
textColor: 'var(--alternative-02)',
|
textColor: 'var(--alternative-02)',
|
||||||
icon: '',
|
|
||||||
disabled: false,
|
disabled: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 색상 코드 키 목록 key_code_codes
|
const buttonClasses = computed(() =>
|
||||||
const PARSED_KEY_CODE_CODES_KEYS: (keyof GameDataKeyCodeCodes)[] = [
|
['btn-base group', props.class].filter(Boolean)
|
||||||
'primary',
|
)
|
||||||
'text-primary',
|
|
||||||
'text-secondary',
|
|
||||||
'alternative-01',
|
|
||||||
'alternative-02',
|
|
||||||
]
|
|
||||||
|
|
||||||
// 색상 값을 CSS 변수로 변환하는 헬퍼 함수
|
|
||||||
const getColorValue = (color: string) =>
|
|
||||||
PARSED_KEY_CODE_CODES_KEYS.includes(color as keyof GameDataKeyCodeCodes)
|
|
||||||
? `var(--${color})`
|
|
||||||
: color
|
|
||||||
|
|
||||||
const buttonClasses = computed(() => [
|
|
||||||
'btn-base group relative inline-flex items-center justify-center font-medium border border-gray-600/30 overflow-hidden',
|
|
||||||
props.disabled ? 'cursor-default' : 'cursor-pointer',
|
|
||||||
])
|
|
||||||
|
|
||||||
const buttonStyles = computed(() => {
|
const buttonStyles = computed(() => {
|
||||||
const styles: Record<string, string> = {
|
const styles: Record<string, string> = {
|
||||||
backgroundColor: getColorValue(props.backgroundColor),
|
backgroundColor: props.backgroundColor,
|
||||||
color: getColorValue(props.textColor),
|
color: props.textColor,
|
||||||
|
'--text-color': props.textColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.backgroundImage) {
|
if (props.backgroundImage) {
|
||||||
@@ -51,40 +40,50 @@ const buttonStyles = computed(() => {
|
|||||||
|
|
||||||
return styles
|
return styles
|
||||||
})
|
})
|
||||||
|
const componentTag = computed((): string => {
|
||||||
|
switch (props.type) {
|
||||||
|
case 'download':
|
||||||
|
case 'external':
|
||||||
|
return 'a'
|
||||||
|
case 'internal':
|
||||||
|
return 'AtomsLocaleLink'
|
||||||
|
default:
|
||||||
|
return 'button'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const componentProps = computed(() => {
|
||||||
|
const baseProps = { disabled: props.disabled }
|
||||||
|
|
||||||
const overlayClasses = computed(() => [
|
if (props.type === 'external') {
|
||||||
'absolute inset-0 -m-px transition-opacity duration-200',
|
return {
|
||||||
props.disabled
|
...baseProps,
|
||||||
? 'opacity-20 z-10'
|
href: props.href,
|
||||||
: 'bg-white opacity-0 group-hover:opacity-20',
|
target: props.target,
|
||||||
])
|
rel: props.rel,
|
||||||
|
|
||||||
const overlayDisabledStyles = computed(
|
|
||||||
() =>
|
|
||||||
props.disabled && {
|
|
||||||
backgroundColor: props.textColor,
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
const contentDisabledStyles = computed(() => props.disabled && { opacity: 0.2 })
|
if (props.type === 'internal') {
|
||||||
|
return {
|
||||||
|
...baseProps,
|
||||||
|
to: props.href,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseProps
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<component
|
||||||
|
:is="componentTag"
|
||||||
|
v-bind="componentProps"
|
||||||
:class="buttonClasses"
|
:class="buttonClasses"
|
||||||
:style="buttonStyles"
|
:style="buttonStyles"
|
||||||
:disabled="props.disabled"
|
|
||||||
>
|
>
|
||||||
<!-- 호버 효과 / Disabled 오버레이 -->
|
<span class="relative flex items-center gap-2 z-[1]">
|
||||||
<span :class="overlayClasses" :style="overlayDisabledStyles" />
|
|
||||||
|
|
||||||
<!-- 버튼 내용 -->
|
|
||||||
<span
|
|
||||||
class="relative flex items-center gap-2"
|
|
||||||
:style="contentDisabledStyles"
|
|
||||||
>
|
|
||||||
<slot />
|
<slot />
|
||||||
<span v-if="props.icon" class="flex-shrink-0" v-html="props.icon" />
|
<span v-if="props.icon" class="flex-shrink-0" v-html="props.icon" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -9,19 +9,29 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
target: '',
|
target: '',
|
||||||
class: '',
|
class: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const componentTag = computed(() => {
|
||||||
|
return props.target === '_blank' ? 'a' : 'AtomsLocaleLink'
|
||||||
|
})
|
||||||
|
|
||||||
|
const componentProps = computed(() => {
|
||||||
|
if (props.target === '_blank') {
|
||||||
|
return {
|
||||||
|
href: props.to,
|
||||||
|
target: props.target,
|
||||||
|
class: props.class,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
to: props.to,
|
||||||
|
class: props.class,
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a
|
<component :is="componentTag" v-bind="{ ...$attrs, ...componentProps }">
|
||||||
v-if="props.target === '_blank'"
|
|
||||||
v-bind="$attrs"
|
|
||||||
:href="props.to"
|
|
||||||
:target="props.target"
|
|
||||||
:class="props.class"
|
|
||||||
>
|
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</component>
|
||||||
<AtomsLocaleLink v-else v-bind="$attrs" :to="props.to" :class="props.class">
|
|
||||||
<slot />
|
|
||||||
</AtomsLocaleLink>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getResponsiveSrc } from '#layers/utils/dataUtil'
|
|
||||||
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -13,39 +12,14 @@ const props = defineProps<Props>()
|
|||||||
const displayText = computed(() => {
|
const displayText = computed(() => {
|
||||||
return props.resourcesData?.display?.txt || ''
|
return props.resourcesData?.display?.txt || ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// 이미지 소스 추출
|
|
||||||
const imageSrc = computed(() => {
|
const imageSrc = computed(() => {
|
||||||
return getResponsiveSrc(props.resourcesData?.res_path)
|
return getResponsiveSrc(props.resourcesData?.res_path)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 색상 코드 추출 (우선순위: color_code_txt > color_code)
|
|
||||||
const colorCode = computed(() => {
|
|
||||||
return (
|
|
||||||
props.resourcesData?.display?.color_code_txt ||
|
|
||||||
props.resourcesData?.display?.color_code
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 색상 이름 추출 (우선순위: color_name_txt > color_name)
|
|
||||||
const colorName = computed(() => {
|
const colorName = computed(() => {
|
||||||
return (
|
return props.resourcesData?.display?.color_name
|
||||||
props.resourcesData?.display?.color_name_txt ||
|
|
||||||
props.resourcesData?.display?.color_name
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
const colorCode = computed(() => {
|
||||||
// 색상 스타일 계산
|
return props.resourcesData?.display?.color_code
|
||||||
const textStyles = computed(() => {
|
|
||||||
const styles: Record<string, string> = {}
|
|
||||||
|
|
||||||
if (colorName.value) {
|
|
||||||
styles.color = `var(--${colorName.value})`
|
|
||||||
} else if (colorCode.value) {
|
|
||||||
styles.color = colorCode.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return styles
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// HTML 콘텐츠 정리 (줄바꿈 처리)
|
// HTML 콘텐츠 정리 (줄바꿈 처리)
|
||||||
@@ -82,7 +56,7 @@ const hasImage = computed(() => {
|
|||||||
<span
|
<span
|
||||||
v-else-if="displayText"
|
v-else-if="displayText"
|
||||||
v-dompurify-html="sanitizedContent"
|
v-dompurify-html="sanitizedContent"
|
||||||
:style="textStyles"
|
:style="{ color: getColorCode({ colorName, colorCode }) }"
|
||||||
class="block"
|
class="block"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getResponsiveClass, getResponsiveSrc } from '#layers/utils/dataUtil'
|
|
||||||
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -1,25 +1,69 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
import type {
|
||||||
|
PageDataResourceGroup,
|
||||||
|
PageDataResourceGroupBtnInfo,
|
||||||
|
} from '#layers/types/api/pageData'
|
||||||
|
import type { ButtonType } from '#layers/types/components/button'
|
||||||
|
|
||||||
const props = defineProps<{
|
interface ButtonListProps {
|
||||||
resourcesData: PageDataResourceGroup[]
|
resourcesData: PageDataResourceGroup[]
|
||||||
buttonType?: string
|
}
|
||||||
}>()
|
|
||||||
|
const props = defineProps<ButtonListProps>()
|
||||||
|
|
||||||
|
// 상수 정의
|
||||||
|
const BUTTON_TYPE_MAP = {
|
||||||
|
URL: {
|
||||||
|
_self: 'internal' as const,
|
||||||
|
_blank: 'external' as const,
|
||||||
|
},
|
||||||
|
DOWNLOAD: 'download' as const,
|
||||||
|
} as const
|
||||||
|
const DEFAULT_BUTTON_TYPE: ButtonType = 'action'
|
||||||
|
|
||||||
|
const getButtonType = (btnInfo: PageDataResourceGroupBtnInfo): ButtonType => {
|
||||||
|
const btnType = btnInfo?.detail?.btn_type
|
||||||
|
const btnTarget = btnInfo?.detail?.action?.link_target
|
||||||
|
|
||||||
|
if (btnType === 'URL' && btnTarget) {
|
||||||
|
return BUTTON_TYPE_MAP.URL[btnTarget] || DEFAULT_BUTTON_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnType === 'DOWNLOAD') {
|
||||||
|
return BUTTON_TYPE_MAP.DOWNLOAD
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="props.resourcesData"
|
v-if="props.resourcesData?.length"
|
||||||
class="flex flex-wrap justify-center gap-3 md:gap-4"
|
class="flex flex-wrap justify-center gap-3 md:gap-4"
|
||||||
>
|
>
|
||||||
<AtomsButton
|
<AtomsButton
|
||||||
v-for="button in props.resourcesData"
|
v-for="(button, index) in props.resourcesData"
|
||||||
:key="button.group_code"
|
:key="`${button.group_code}-${index}`"
|
||||||
:button-type="props.buttonType"
|
v-bind="getButtonProps(button)"
|
||||||
class="size-extra-small md:size-medium"
|
class="size-extra-small md:size-medium"
|
||||||
:background-color="button.btn_info?.color_code_btn"
|
|
||||||
:text-color="button.btn_info?.color_code_txt"
|
|
||||||
:disabled="button.btn_info?.disabled"
|
|
||||||
>
|
>
|
||||||
{{ button.btn_info?.txt_btn_name }}
|
{{ button.btn_info?.txt_btn_name }}
|
||||||
</AtomsButton>
|
</AtomsButton>
|
||||||
|
|||||||
@@ -60,25 +60,25 @@ export interface PageDataResourceGroupResPath {
|
|||||||
path_pc?: string
|
path_pc?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PageDataResourceGroupBtnInfo {
|
||||||
|
color_code_btn: string
|
||||||
|
color_name_btn: string
|
||||||
|
color_code_txt: string
|
||||||
|
color_name_txt: string
|
||||||
|
disabled: boolean
|
||||||
|
txt_btn_name: string
|
||||||
|
detail: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
// 리소스 그룹 타입
|
// 리소스 그룹 타입
|
||||||
export interface PageDataResourceGroup {
|
export interface PageDataResourceGroup {
|
||||||
group_type?: string
|
group_type?: string
|
||||||
group_code?: string
|
group_code?: string
|
||||||
res_path?: PageDataResourceGroupResPath
|
res_path?: PageDataResourceGroupResPath
|
||||||
btn_info?: {
|
btn_info?: PageDataResourceGroupBtnInfo
|
||||||
color_code_btn: string
|
|
||||||
color_code_txt: string
|
|
||||||
disabled: boolean
|
|
||||||
txt_btn_name: string
|
|
||||||
detail: Record<string, any>
|
|
||||||
}
|
|
||||||
display?: {
|
display?: {
|
||||||
text: string
|
text: string
|
||||||
txt: string
|
txt: string
|
||||||
color_code_btn?: string
|
|
||||||
color_name_btn?: string
|
|
||||||
color_code_txt?: string
|
|
||||||
color_name_txt?: string
|
|
||||||
color_code?: string
|
color_code?: string
|
||||||
color_name?: string
|
color_name?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,2 @@
|
|||||||
// 버튼 크기 타입
|
export type ButtonType = 'internal' | 'external' | 'download' | 'action'
|
||||||
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'
|
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'
|
||||||
|
|
||||||
// 버튼 설정 인터페이스
|
|
||||||
export interface ButtonConfig {
|
|
||||||
padding: string
|
|
||||||
height: string
|
|
||||||
text: string
|
|
||||||
rounded: string
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
PageDataValue,
|
PageDataValue,
|
||||||
PageDataResourceGroupResPath,
|
|
||||||
PageDataComponent,
|
PageDataComponent,
|
||||||
} from '#layers/types/api/pageData'
|
} from '#layers/types/api/pageData'
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 페이지 데이터 관련 유틸리티
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 페이지 데이터를 기반으로 레이아웃 타입을 결정합니다.
|
* 페이지 데이터를 기반으로 레이아웃 타입을 결정합니다.
|
||||||
* @param pageData 페이지 데이터
|
* @param pageData 페이지 데이터
|
||||||
@@ -19,10 +14,6 @@ export const getLayoutType = (
|
|||||||
return pageData?.page_type === 1 ? 'default' : 'promotion'
|
return pageData?.page_type === 1 ? 'default' : 'promotion'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 컴포넌트 데이터 접근 관련 유틸리티
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 그룹의 첫 번째 데이터를 반환합니다.
|
* 그룹의 첫 번째 데이터를 반환합니다.
|
||||||
* @param source props.components 또는 group 객체
|
* @param source props.components 또는 group 객체
|
||||||
@@ -72,72 +63,3 @@ export const getComponentGroupAry = (source: any, componentName: string) => {
|
|||||||
|
|
||||||
return source[componentName]?.groups || []
|
return source[componentName]?.groups || []
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 리소스/이미지 처리 관련 유틸리티
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이미지 경로를 완전한 호스트 URL로 변환합니다.
|
|
||||||
* @param path 이미지 경로
|
|
||||||
* @returns 완전한 이미지 URL
|
|
||||||
*/
|
|
||||||
export const getResolvedHost = (path: string): string => {
|
|
||||||
const config = useRuntimeConfig()
|
|
||||||
// const isDev = process.env.NODE_ENV === "development";
|
|
||||||
// const rootPath = isDev ? "/images" : `${config.public.staticUrl}`;
|
|
||||||
|
|
||||||
const rootPath = config.public.staticUrl
|
|
||||||
|
|
||||||
return `${rootPath}${path}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 반응형 리소스(이미지/비디오)를 처리하여 PC/모바일 버전을 반환합니다.
|
|
||||||
* @param pathArray 리소스 경로 배열
|
|
||||||
* @param options 리소스 타입 옵션
|
|
||||||
* @returns 반응형 리소스 객체 또는 null
|
|
||||||
*/
|
|
||||||
export const getResponsiveSrc = (
|
|
||||||
pathArray: PageDataResourceGroupResPath,
|
|
||||||
options: {
|
|
||||||
resourcesType?: 'image' | 'bg' | 'video'
|
|
||||||
} = {}
|
|
||||||
) => {
|
|
||||||
const { resourcesType = 'image' } = options
|
|
||||||
const pcField = resourcesType === 'video' ? 'path_vid_pc' : 'path_pc'
|
|
||||||
const mobileField = resourcesType === 'video' ? 'path_vid_mo' : 'path_mo'
|
|
||||||
|
|
||||||
if (!pathArray?.[mobileField]) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolvedImages = {
|
|
||||||
pc: getResolvedHost(pathArray[pcField] || pathArray[mobileField]),
|
|
||||||
mobile: getResolvedHost(pathArray[mobileField]),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resourcesType === 'bg') {
|
|
||||||
return {
|
|
||||||
'--pc-bg': `url(${resolvedImages.pc})`,
|
|
||||||
'--mobile-bg': `url(${resolvedImages.mobile})`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
mobileSrc: resolvedImages.mobile,
|
|
||||||
pcSrc: resolvedImages.pc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 스타일링 관련 유틸리티
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 반응형 배경 이미지를 위한 CSS 클래스를 반환합니다.
|
|
||||||
* @returns 반응형 배경 클래스 배열
|
|
||||||
*/
|
|
||||||
export const getResponsiveClass = () => {
|
|
||||||
return ['bg-[image:var(--mobile-bg)]', 'md:bg-[image:var(--pc-bg)]']
|
|
||||||
}
|
|
||||||
|
|||||||
82
layers/utils/styleUtil.ts
Normal file
82
layers/utils/styleUtil.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import type { PageDataResourceGroupResPath } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 이미지 경로를 완전한 호스트 URL로 변환합니다.
|
||||||
|
* @param path 이미지 경로
|
||||||
|
* @returns 완전한 이미지 URL
|
||||||
|
*/
|
||||||
|
export const getResolvedHost = (path: string): string => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
// const isDev = process.env.NODE_ENV === "development";
|
||||||
|
// const rootPath = isDev ? "/images" : `${config.public.staticUrl}`;
|
||||||
|
|
||||||
|
const rootPath = config.public.staticUrl
|
||||||
|
|
||||||
|
return `${rootPath}${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 반응형 리소스(이미지/비디오)를 처리하여 PC/모바일 버전을 반환합니다.
|
||||||
|
* @param pathArray 리소스 경로 배열
|
||||||
|
* @param options 리소스 타입 옵션
|
||||||
|
* @returns 반응형 리소스 객체 또는 null
|
||||||
|
*/
|
||||||
|
export const getResponsiveSrc = (
|
||||||
|
pathArray: PageDataResourceGroupResPath,
|
||||||
|
options: {
|
||||||
|
resourcesType?: 'image' | 'bg' | 'video'
|
||||||
|
} = {}
|
||||||
|
) => {
|
||||||
|
const { resourcesType = 'image' } = options
|
||||||
|
const pcField = resourcesType === 'video' ? 'path_vid_pc' : 'path_pc'
|
||||||
|
const mobileField = resourcesType === 'video' ? 'path_vid_mo' : 'path_mo'
|
||||||
|
|
||||||
|
if (!pathArray?.[mobileField]) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedImages = {
|
||||||
|
pc: getResolvedHost(pathArray[pcField] || pathArray[mobileField]),
|
||||||
|
mobile: getResolvedHost(pathArray[mobileField]),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourcesType === 'bg') {
|
||||||
|
return {
|
||||||
|
'--pc-bg': `url(${resolvedImages.pc})`,
|
||||||
|
'--mobile-bg': `url(${resolvedImages.mobile})`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
mobileSrc: resolvedImages.mobile,
|
||||||
|
pcSrc: resolvedImages.pc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 반응형 배경 이미지를 위한 CSS 클래스를 반환합니다.
|
||||||
|
* @returns 반응형 배경 클래스 배열
|
||||||
|
*/
|
||||||
|
export const getResponsiveClass = () => {
|
||||||
|
return ['bg-[image:var(--mobile-bg)]', 'md:bg-[image:var(--pc-bg)]']
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 색상값을 반환합니다.
|
||||||
|
* @param colorName 색상 이름
|
||||||
|
* @param colorCode 색상 코드
|
||||||
|
* @returns 색상 값
|
||||||
|
*/
|
||||||
|
export const getColorCode = ({
|
||||||
|
colorName,
|
||||||
|
colorCode,
|
||||||
|
}: {
|
||||||
|
colorName: string
|
||||||
|
colorCode: string
|
||||||
|
}) => {
|
||||||
|
if (colorName) {
|
||||||
|
return `var(--${colorName})`
|
||||||
|
} else if (colorCode) {
|
||||||
|
return colorCode
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user