feat: 로딩 스피너 컴포넌트 추가
- LoadingSpinner: 전체 화면 로딩 스피너 - InlineSpinner: 인라인 로딩 스피너 (버튼 내부용)
This commit is contained in:
109
components/ui/loading-spinner.tsx
Normal file
109
components/ui/loading-spinner.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/**
|
||||
* [로딩 스피너 컴포넌트]
|
||||
*
|
||||
* 전역적으로 사용 가능한 로딩 스피너입니다.
|
||||
* - 크기 조절 가능 (sm, md, lg)
|
||||
* - 색상 커스터마이징 가능
|
||||
* - 텍스트와 함께 사용 가능
|
||||
*
|
||||
* @example
|
||||
* // 기본 사용
|
||||
* <LoadingSpinner />
|
||||
*
|
||||
* @example
|
||||
* // 크기 및 텍스트 지정
|
||||
* <LoadingSpinner size="lg" text="로딩 중..." />
|
||||
*
|
||||
* @example
|
||||
* // 버튼 내부에서 사용
|
||||
* <Button disabled={isLoading}>
|
||||
* {isLoading ? <LoadingSpinner size="sm" /> : "제출"}
|
||||
* </Button>
|
||||
*/
|
||||
|
||||
interface LoadingSpinnerProps {
|
||||
/** 스피너 크기 */
|
||||
size?: "sm" | "md" | "lg";
|
||||
/** 스피너와 함께 표시할 텍스트 */
|
||||
text?: string;
|
||||
/** 추가 CSS 클래스 */
|
||||
className?: string;
|
||||
/** 스피너 색상 (Tailwind 클래스) */
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export function LoadingSpinner({
|
||||
size = "md",
|
||||
text,
|
||||
className,
|
||||
color = "border-gray-900 dark:border-white",
|
||||
}: LoadingSpinnerProps) {
|
||||
// 크기별 스타일 매핑
|
||||
const sizeClasses = {
|
||||
sm: "h-4 w-4 border-2",
|
||||
md: "h-8 w-8 border-3",
|
||||
lg: "h-12 w-12 border-4",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn("flex items-center justify-center gap-2", className)}>
|
||||
{/* ========== 회전 스피너 ========== */}
|
||||
<div
|
||||
className={cn(
|
||||
"animate-spin rounded-full border-solid border-t-transparent",
|
||||
sizeClasses[size],
|
||||
color,
|
||||
)}
|
||||
role="status"
|
||||
aria-label="로딩 중"
|
||||
/>
|
||||
{/* ========== 로딩 텍스트 (선택적) ========== */}
|
||||
{text && (
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{text}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* [인라인 스피너 컴포넌트]
|
||||
*
|
||||
* 버튼 내부나 작은 공간에서 사용하기 적합한 미니 스피너입니다.
|
||||
*
|
||||
* @example
|
||||
* <Button disabled={isLoading}>
|
||||
* {isLoading && <InlineSpinner />}
|
||||
* 로그인
|
||||
* </Button>
|
||||
*/
|
||||
export function InlineSpinner({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
className={cn("h-4 w-4 animate-spin", className)}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
aria-label="로딩 중"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
/>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user