StepIndicator.tsx
1 export interface StepIndicatorProps { 2 steps: string[] 3 currentStep: number 4 variant?: 'default' | 'prominent' | 'minimal' 5 className?: string 6 } 7 8 export function StepIndicator({ 9 steps, 10 currentStep, 11 variant = 'default', 12 className, 13 }: StepIndicatorProps) { 14 return ( 15 <div 16 className={className} 17 role="navigation" 18 aria-label="Progress steps" 19 style={{ 20 display: 'flex', 21 alignItems: 'center', 22 gap: variant === 'minimal' ? '4px' : '0', 23 }} 24 > 25 {steps.map((step, i) => { 26 const isComplete = i < currentStep 27 const isCurrent = i === currentStep 28 const isLast = i === steps.length - 1 29 30 const dotSize = variant === 'prominent' ? 28 : variant === 'minimal' ? 6 : 20 31 32 const dotColor = isComplete || isCurrent 33 ? 'var(--alpha-500)' 34 : 'var(--border-default)' 35 36 const textColor = isCurrent ? 'var(--text-primary)' : isComplete ? 'var(--text-secondary)' : 'var(--text-tertiary)' 37 38 return ( 39 <div key={step} style={{ display: 'flex', alignItems: 'center', flex: isLast ? '0 0 auto' : 1 }}> 40 {/* Step dot */} 41 <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }}> 42 <div 43 aria-current={isCurrent ? 'step' : undefined} 44 style={{ 45 width: dotSize, 46 height: dotSize, 47 borderRadius: '50%', 48 background: isComplete || isCurrent ? dotColor : 'transparent', 49 border: `2px solid ${dotColor}`, 50 display: 'flex', 51 alignItems: 'center', 52 justifyContent: 'center', 53 transition: 'all 150ms linear', 54 flexShrink: 0, 55 }} 56 > 57 {isComplete && variant !== 'minimal' && ( 58 <span style={{ fontSize: '10px', color: 'var(--neutral-1000)', fontWeight: 700 }}>OK</span> 59 )} 60 {isCurrent && variant !== 'minimal' && ( 61 <span style={{ fontSize: '10px', color: 'var(--neutral-1000)', fontWeight: 700 }}>{i + 1}</span> 62 )} 63 </div> 64 {variant !== 'minimal' && ( 65 <span style={{ fontSize: '10px', color: textColor, whiteSpace: 'nowrap', fontWeight: isCurrent ? 600 : 400 }}> 66 {step} 67 </span> 68 )} 69 </div> 70 71 {/* Connector line */} 72 {!isLast && ( 73 <div style={{ 74 flex: 1, 75 height: '2px', 76 background: isComplete ? 'var(--alpha-500)' : 'var(--border-default)', 77 margin: variant === 'minimal' ? '0 4px' : `0 4px ${variant === 'prominent' ? '22px' : '18px'}`, 78 transition: 'background 150ms linear', 79 }} /> 80 )} 81 </div> 82 ) 83 })} 84 </div> 85 ) 86 }