fix. 버튼 컴포넌트 제작

This commit is contained in:
clkim
2025-09-17 11:14:59 +09:00
parent 525b350ae4
commit d9c26e651d
3 changed files with 74 additions and 89 deletions

View File

@@ -9,12 +9,12 @@
--foreground-reversal-40: rgba(0, 0, 0, 0.4);
--foreground-reversal-70: #666666; /* gray-700 */
/* 게임별 동적 색상 기본값 (API 로드 실패 시 fallback) */
--game-primary: #3b82f6;
--game-alternative-01: #64748b;
--game-alternative-02: #64748b;
--game-text-primary: #1f2937;
--game-text-secondary: #6b7280;
/* 게임별 동적 색상 기본값 */
--game-primary: transparent;
--game-alternative-01: transparent;
--game-alternative-02: transparent;
--game-text-primary: transparent;
--game-text-secondary: transparent;
}
/* 다크 테마 색상 */
@@ -28,12 +28,12 @@
--foreground-reversal-40: rgba(255, 255, 255, 0.4);
--foreground-reversal-70: #b2b2b2; /* gray-300 */
/* 다크 테마 게임별 동적 색상 기본값 (API 로드 실패 시 fallback) */
--game-primary: #60a5fa;
--game-alternative-01: #94a3b8;
--game-alternative-02: #94a3b8;
--game-text-primary: #f9fafb;
--game-text-secondary: #d1d5db;
/* 게임별 동적 색상 기본값 */
--game-primary: transparent;
--game-alternative-01: transparent;
--game-alternative-02: transparent;
--game-text-primary: transparent;
--game-text-secondary: transparent;
}
/* 커스텀 컴포넌트 스타일 */

View File

@@ -1,29 +1,21 @@
<script setup lang="ts">
type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'
type ButtonVariant = 'primary' | 'secondary'
import type {
ButtonSize,
ButtonConfig,
ButtonProps,
} from '#layers/types/components/button'
interface Props {
size?: ButtonSize
variant?: ButtonVariant
disabled?: boolean
icon?: string
backgroundColor?: string
textColor?: string
}
const props = withDefaults(defineProps<Props>(), {
// Props 정의
const props = withDefaults(defineProps<ButtonProps>(), {
size: 'medium',
variant: 'primary',
disabled: false,
backgroundColor: 'var(--game-primary)',
textColor: 'var(--game-text-primary)',
icon: '',
backgroundColor: '',
textColor: '',
disabled: false,
})
const attrs = useAttrs()
// 버튼 크기별 설정
const buttonConfig = {
// 버튼 크기별 설정 상수
const BUTTON_CONFIGS: Record<ButtonSize, ButtonConfig> = {
large: {
padding: 'px-10',
height: 'h-16',
@@ -50,72 +42,46 @@ const buttonConfig = {
},
} as const
// 버튼 변형별 기본 스타일
const variantStyles = {
primary: 'bg-game-primary text-game-text-primary',
secondary: 'bg-[rgba(255, 255, 255, 0.5)] text-white',
} as const
// 크기별 클래스 생성
const getSizeClasses = (size: ButtonSize) => {
const config = buttonConfig[size]
return `${config.padding} ${config.height} ${config.text} ${config.rounded}`
}
// 상태별 클래스 생성
const getStateClasses = (disabled: boolean) => {
if (disabled) {
return 'bg-white/10 text-gray-400 cursor-not-allowed'
}
return 'cursor-pointer'
}
// 호버 효과 클래스 생성
const getHoverEffectClasses = (
size: ButtonSize,
disabled: boolean,
variant: ButtonVariant
) => {
const config = buttonConfig[size]
if (disabled) {
// disabled 상태에서 variant에 따라 다른 오버레이 색상
const overlayColor = variant === 'primary' ? 'bg-black' : 'bg-white'
return `absolute inset-0 -m-px ${overlayColor} opacity-20 transition-opacity duration-200 z-10 ${config.rounded}`
}
return `absolute inset-0 -m-px bg-white opacity-0 group-hover:opacity-20 transition-opacity duration-200 ${config.rounded}`
}
const currentConfig = computed(() => BUTTON_CONFIGS[props.size])
const buttonClasses = computed(() => [
'group relative inline-flex items-center justify-center font-medium border border-gray-600/30 overflow-hidden',
`${currentConfig.value.padding} ${currentConfig.value.height} ${currentConfig.value.text} ${currentConfig.value.rounded}`,
props.disabled ? 'cursor-default' : 'cursor-pointer',
])
const buttonStyles = computed(() => ({
backgroundColor: props.backgroundColor,
color: props.textColor,
}))
const overlayClasses = computed(() => [
'absolute inset-0 -m-px transition-opacity duration-200',
props.disabled
? 'opacity-20 z-10'
: 'bg-white opacity-0 group-hover:opacity-20',
currentConfig.value.rounded,
])
const overlayDisabledStyles = computed(
() =>
props.disabled && {
backgroundColor: props.textColor,
}
)
const contentDisabledStyles = computed(() => props.disabled && { opacity: 0.2 })
</script>
<template>
<button
:class="[
// 기본 스타일
'group relative inline-flex items-center justify-center font-medium transition-all duration-200 border border-gray-600/30 overflow-hidden',
// 크기별 스타일
getSizeClasses(props.size),
// 변형별 스타일 (disabled 상태가 아닐 때만 적용, 커스텀 색상이 없을 때만)
!props.disabled && !props.backgroundColor
? variantStyles[props.variant]
: '',
// 상태별 스타일
getStateClasses(props.disabled),
// 외부 클래스 (최우선 적용)
attrs.class,
]"
:style="{
backgroundColor: props.backgroundColor || undefined,
color: props.textColor || undefined,
}"
:class="buttonClasses"
:style="buttonStyles"
:disabled="props.disabled"
v-bind="attrs"
>
<!-- 호버 효과 / Disabled 오버레이 -->
<span
:class="getHoverEffectClasses(props.size, props.disabled, props.variant)"
/>
<span :class="overlayClasses" :style="overlayDisabledStyles" />
<!-- 버튼 내용 -->
<span class="relative flex items-center gap-2 z-20">
<span
class="relative flex items-center gap-2"
:style="contentDisabledStyles"
>
<slot />
<span v-if="props.icon" class="flex-shrink-0" v-html="props.icon" />
</span>

View File

@@ -0,0 +1,19 @@
// 버튼 크기 타입
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'
// 버튼 설정 인터페이스
export interface ButtonConfig {
padding: string
height: string
text: string
rounded: string
}
// Button 컴포넌트 Props 인터페이스
export interface ButtonProps {
size?: ButtonSize
backgroundColor?: string
textColor?: string
icon?: string
disabled?: boolean
}