feat: 공용 Spinner 컴포넌트 추가
Co-Authored-By: Claude Opus 5
@e34e171a86ef0b8d9ce5d25c55d6834181e924eb
+++ components/ui/spinner.tsx
... | ... | @@ -0,0 +1,38 @@ |
| 1 | +type SpinnerSize = "sm" | "md" | "lg"; | |
| 2 | +type SpinnerTone = "primary" | "inverse" | "muted"; | |
| 3 | + | |
| 4 | +const SIZE_CLASSES: Record<SpinnerSize, string> = { | |
| 5 | + sm: "size-4 border-2", | |
| 6 | + md: "size-6 border-2", | |
| 7 | + lg: "size-8 border-4", | |
| 8 | +}; | |
| 9 | + | |
| 10 | +const TONE_CLASSES: Record<SpinnerTone, string> = { | |
| 11 | + primary: "text-primary", | |
| 12 | + inverse: "text-on-primary", | |
| 13 | + muted: "text-foreground-subtle", | |
| 14 | +}; | |
| 15 | + | |
| 16 | +interface SpinnerProps { | |
| 17 | + size?: SpinnerSize; | |
| 18 | + tone?: SpinnerTone; | |
| 19 | + /** 스크린리더 전용 안내 문구. 기본 '불러오는 중'. */ | |
| 20 | + label?: string; | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** 로딩 인디케이터. 상태 없는 프레젠테이션 컴포넌트 — 표시 여부는 호출부 책임. */ | |
| 24 | +export function Spinner({ | |
| 25 | + size = "md", | |
| 26 | + tone = "primary", | |
| 27 | + label = "불러오는 중", | |
| 28 | +}: SpinnerProps) { | |
| 29 | + return ( | |
| 30 | + <span role="status" className="inline-flex items-center"> | |
| 31 | + <span | |
| 32 | + aria-hidden="true" | |
| 33 | + className={`inline-block animate-spin rounded-full border-current border-t-transparent ${SIZE_CLASSES[size]} ${TONE_CLASSES[tone]}`} | |
| 34 | + /> | |
| 35 | + <span className="sr-only">{label}</span> | |
| 36 | + </span> | |
| 37 | + ); | |
| 38 | +} |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?