import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import {Pressable, StyleSheet, Text, View} from 'react-native'; import {renderIcon} from '../../helpers/renderIcon'; import {useTheme} from '../../theme/ThemeProvider'; import type {A11yProps, IconProp} from '../../types'; import {CheckGlyph} from '../glyphs/Glyphs'; export type Step = { label?: string; /** Replaces the step number. */ icon?: IconProp; }; export type StepperProps = A11yProps & { steps: Step[]; /** Index of the active step (0-based). Steps before it are completed. */ current: number; orientation?: 'horizontal' | 'vertical'; activeColor?: string; completedColor?: string; pendingColor?: string; textColor?: string; /** Icon drawn on completed steps. Defaults to a built-in check. */ completedIcon?: IconProp; /** Makes the steps pressable. */ onStepPress?: (index: number) => void; size?: number; style?: StyleProp; labelStyle?: StyleProp; }; export const Stepper = ({ accessibilityLabel = 'Progress steps', activeColor, completedColor, completedIcon = CheckGlyph, current, labelStyle, onStepPress, orientation = 'horizontal', pendingColor, size = 28, steps, style, testID, textColor, }: StepperProps) => { const {colors} = useTheme(); const active = activeColor ?? colors.primary; const completed = completedColor ?? active; const pending = pendingColor ?? colors.border; const horizontal = orientation === 'horizontal'; return ( {steps.map((step, i) => { const isCompleted = i < current; const isActive = i === current; const stepColor = isCompleted ? completed : isActive ? active : pending; const isLast = i === steps.length - 1; return ( {horizontal && !isLast && ( )} onStepPress?.(i)} style={{ width: size, height: size, borderRadius: size / 2, borderWidth: 2, borderColor: stepColor, backgroundColor: isCompleted || isActive ? stepColor : 'transparent', alignItems: 'center', justifyContent: 'center', }}> {isCompleted && !step.icon ? ( renderIcon(completedIcon, { size: size * 0.6, color: '#FFFFFF', }) ) : step.icon ? ( renderIcon(step.icon, { size: size * 0.55, color: isCompleted || isActive ? '#FFFFFF' : pending, }) ) : ( {i + 1} )} {!horizontal && !isLast && ( )} {!!step.label && ( {step.label} )} ); })} ); }; const styles = StyleSheet.create({ row: { flexDirection: 'row', alignItems: 'flex-start', }, column: { flexDirection: 'column', }, hStep: { flex: 1, alignItems: 'center', }, hCircle: { zIndex: 1, }, hLine: { position: 'absolute', left: '50%', right: '-50%', height: 2, }, hLabel: { textAlign: 'center', paddingHorizontal: 2, }, vStep: { flexDirection: 'row', alignItems: 'flex-start', }, vCircleCol: { alignItems: 'center', }, vLine: { width: 2, height: 32, marginVertical: 4, }, label: { fontSize: 12, marginTop: 6, }, activeLabel: { fontWeight: '700', }, });