/ src / components / financial / StepIndicator.tsx
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  }