fix. 버튼 컴포넌트 제작
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/* 커스텀 컴포넌트 스타일 */
|
||||
|
||||
@@ -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>
|
||||
|
||||
19
layers/types/components/button.ts
Normal file
19
layers/types/components/button.ts
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user