Skip to content

Commit 0ced1fc

Browse files
authored
Merge pull request #301 from prgrms-web-devcourse-final-project/refactor/300-button
[refactor] 버튼 컴포넌트 리펙토링
2 parents 1311d4a + 78b5893 commit 0ced1fc

File tree

13 files changed

+91
-56
lines changed

13 files changed

+91
-56
lines changed

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"@types/node": "^22.13.4",
1919
"@types/youtube": "^0.1.0",
2020
"axios": "^1.7.9",
21+
"class-variance-authority": "^0.7.1",
22+
"clsx": "^2.1.1",
2123
"dayjs": "^1.11.13",
2224
"event-source-polyfill": "^1.0.31",
2325
"framer-motion": "^12.4.7",

src/components/InputAuthCode.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import React from 'react';
44
import { twMerge } from 'tailwind-merge';
55

66
type ButtonHandler = {
7-
buttonEnabled: boolean;
8-
buttonText: string;
9-
isPending: boolean;
7+
disable: boolean;
8+
text: string;
9+
isLoading: boolean;
1010
onClick: () => void;
1111
};
1212

src/components/button/Button.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,35 @@
1-
import { twMerge } from 'tailwind-merge';
21
import { forwardRef } from 'react';
2+
import { cva } from 'class-variance-authority';
3+
import { cn } from '@/utils';
34

45
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
56
variant?: 'primary' | 'secondary' | 'disabled';
67
}
78

8-
const buttonStyle = {
9-
primary: 'bg-primary-normal hover:bg-primary-hover',
10-
secondary: 'bg-white border border-primary-active text-primary-active hover:bg-gray-5',
11-
disabled: 'bg-gray-30 cursor-not-allowed',
12-
};
9+
export const buttonVariants = cva(
10+
'flex justify-center items-center w-full rounded-lg h-[38px] text-white transition body-m cursor-pointer',
11+
{
12+
variants: {
13+
variant: {
14+
primary: 'bg-primary-normal hover:bg-primary-hover',
15+
secondary: 'bg-white border border-primary-active text-primary-active hover:bg-gray-5',
16+
disabled: 'bg-gray-30 cursor-not-allowed',
17+
},
18+
},
19+
defaultVariants: {
20+
variant: 'primary',
21+
},
22+
},
23+
);
1324

1425
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
15-
({ children, variant = 'primary', type = 'button', className, disabled, ...props }, ref) => {
26+
({ children, variant = 'primary', type = 'button', disabled, className, ...props }, ref) => {
1627
return (
1728
<button
1829
ref={ref}
1930
type={type}
20-
className={twMerge(
21-
'flex justify-center items-center w-full rounded-lg h-[38px] text-white transition body-m cursor-pointer',
22-
buttonStyle[variant],
23-
className,
24-
)}
25-
disabled={variant === 'disabled'}
31+
disabled={variant === 'disabled' || disabled}
32+
className={cn(buttonVariants({ variant }), className)}
2633
{...props}
2734
>
2835
{children}
@@ -31,9 +38,6 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
3138
},
3239
);
3340

34-
export default Button;
41+
Button.displayName = 'Button';
3542

36-
// 사용 예시
37-
// <Button variant="secondary" className="w-40 py-3 text-lg">
38-
// Secondary 버튼
39-
// </Button>
43+
export default Button;

src/components/button/LoadingSpinnerButton.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
11
import Button from '@/components/button/Button';
22
import SpinLoading from '@/components/loading/SpinLoading';
3-
import useDelayedLoading from '@/hooks/useDelayedLoading';
3+
import useDelayedLoading from '@/hooks/button/useDelayedLoading';
44

55
interface LoadingSpinnerButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6-
isPending: boolean; // pending 되었는지 유무
7-
buttonText: string; // 버튼 text
8-
buttonEnabled: boolean; // 버튼 활성화 유무
6+
isLoading: boolean;
7+
text: string;
8+
disabled?: boolean;
99
}
1010

1111
// 로딩 스피너 있는 버튼
1212
const LoadingSpinnerButton = ({
13-
isPending,
14-
buttonText,
15-
buttonEnabled,
16-
onClick,
13+
isLoading,
14+
text,
15+
disabled,
16+
className,
1717
...props
1818
}: LoadingSpinnerButtonProps) => {
19-
const showLoading = useDelayedLoading({ isPending });
19+
const showLoading = useDelayedLoading({ isLoading });
20+
21+
const variant = disabled ? 'disabled' : 'primary';
2022

2123
return (
22-
<Button
23-
variant={buttonEnabled ? 'primary' : 'disabled'}
24-
disabled={isPending}
25-
onClick={onClick}
26-
{...props}
27-
>
28-
{showLoading ? <SpinLoading /> : buttonText}
24+
<Button variant={variant} disabled={isLoading || disabled} className={className} {...props}>
25+
{showLoading ? <SpinLoading /> : text}
2926
</Button>
3027
);
3128
};

src/hooks/useDelayedLoading.ts renamed to src/hooks/button/useDelayedLoading.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import { useEffect, useState } from 'react';
22

33
interface UseDelayedLoadingProps {
4-
isPending: boolean;
4+
isLoading: boolean;
55
delay?: number; // 기본값: 100ms
66
}
77

88
//로딩 상태가 pending일 때 delay 후 로딩 UI를 표시합니다.
9-
const useDelayedLoading = ({ isPending, delay = 100 }: UseDelayedLoadingProps) => {
9+
const useDelayedLoading = ({ isLoading, delay = 100 }: UseDelayedLoadingProps) => {
1010
const [showLoading, setShowLoading] = useState(false);
1111

1212
useEffect(() => {
1313
let loadingTimeout: NodeJS.Timeout;
14-
if (isPending) {
14+
if (isLoading) {
1515
loadingTimeout = setTimeout(() => setShowLoading(true), delay);
1616
} else {
1717
setShowLoading(false);
1818
}
1919
return () => clearTimeout(loadingTimeout);
20-
}, [isPending, delay]);
20+
}, [isLoading, delay]);
2121

2222
return showLoading;
2323
};

src/pages/signup/components/AuthCodeInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ function AuthCodeInput({ email, authcodeValidity, setValidity }: AuthCodeInputPr
9696
};
9797

9898
const buttonHandler = {
99-
buttonEnabled: validationMessage.success && !authcodeValidity,
100-
buttonText: '인증확인',
101-
isPending: isPending,
99+
disable: validationMessage.success && !authcodeValidity,
100+
text: '인증확인',
101+
isLoading: isPending,
102102
onClick: verifyEmail,
103103
};
104104

src/pages/signup/components/EmailInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ function EmailInput({
107107
disabled={authcodeValidity} // input disabled
108108
actionButton={
109109
<LoadingSpinnerButton
110-
isPending={isCheckingEmail && isRequestingEmailVerification}
110+
isLoading={isCheckingEmail && isRequestingEmailVerification}
111111
className="w-[65px]"
112-
buttonText="인증요청"
113-
buttonEnabled={validationStatus.isValid && !emailValidity}
112+
text="인증요청"
113+
disabled={!validationStatus.isValid || emailValidity}
114114
onClick={() => checkEmailAvailability()}
115115
/>
116116
}

src/pages/signup/components/IdInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ const IdInput = ({ changeFormID, setValidity }: IdInputProps) => {
6666
isValid={validationStatus.isValid}
6767
actionButton={
6868
<LoadingSpinnerButton
69-
isPending={isPending}
69+
isLoading={isPending}
7070
className="w-[65px]"
71-
buttonText="중복확인"
72-
buttonEnabled={validationStatus.isValid}
71+
text="중복확인"
72+
disabled={!validationStatus.isValid}
7373
onClick={() => mutate()}
7474
/>
7575
}

src/pages/signup/components/NicknameInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ function NicknameInput({ initialText = '', changeFormNickname, setValidity }: Ni
7676
isValid={validationStatus.isValid}
7777
actionButton={
7878
<LoadingSpinnerButton
79-
isPending={isPending}
79+
isLoading={isPending}
8080
className="w-[65px]"
81-
buttonText="중복확인"
82-
buttonEnabled={validationStatus.isValid}
81+
text="중복확인"
82+
disabled={!validationStatus.isValid}
8383
onClick={() => mutate()}
8484
/>
8585
}

0 commit comments

Comments
 (0)