// @ts-nocheck import React, { Component } from "react"; import { View, Text, StyleSheet, Animated, TouchableWithoutFeedback, } from "react-native"; const STEP_STATUS = { CURRENT: "current", FINISHED: "finished", UNFINISHED: "unfinished", }; interface CustomStyles { stepIndicatorSize?: number; currentStepIndicatorSize?: number; separatorStrokeWidth?: number; separatorStrokeUnfinishedWidth?: number; separatorStrokeFinishedWidth?: number; currentStepStrokeWidth: number; stepStrokeWidth?: number; stepStrokeCurrentColor?: string; stepStrokeFinishedColor: string; stepStrokeUnFinishedColor: string; separatorFinishedColor: string; separatorUnFinishedColor: string; stepIndicatorFinishedColor: string; stepIndicatorUnFinishedColor: string; stepIndicatorCurrentColor: string; stepIndicatorLabelFontSize: number; currentStepIndicatorLabelFontSize?: number; stepIndicatorLabelCurrentColor: string; stepIndicatorLabelFinishedColor: string; stepIndicatorLabelUnFinishedColor: string; labelColor?: string; labelSize?: number; labelAlign?: string; currentStepLabelColor?: string; labelFontFamily?: string; } type Props = { stepCount: number; currentPosition: number; customStyles: CustomStyles; }; type State = { width: number; height: number; progressBarSize: number; customStyles: CustomStyles; }; export default class StepIndicator extends Component { constructor(props: Props) { super(props); const defaultStyles = { stepIndicatorSize: 30, currentStepIndicatorSize: 40, separatorStrokeWidth: 3, separatorStrokeUnfinishedWidth: 0, separatorStrokeFinishedWidth: 0, currentStepStrokeWidth: 5, stepStrokeWidth: 0, stepStrokeCurrentColor: "#4aae4f", stepStrokeFinishedColor: "#4aae4f", stepStrokeUnFinishedColor: "#4aae4f", separatorFinishedColor: "#4aae4f", separatorUnFinishedColor: "#a4d4a5", stepIndicatorFinishedColor: "#4aae4f", stepIndicatorUnFinishedColor: "#a4d4a5", stepIndicatorCurrentColor: "#ffffff", stepIndicatorLabelFontSize: 15, currentStepIndicatorLabelFontSize: 15, stepIndicatorLabelCurrentColor: "#000000", stepIndicatorLabelFinishedColor: "#ffffff", stepIndicatorLabelUnFinishedColor: "rgba(255,255,255,0.5)", labelColor: "#000000", labelSize: 13, labelAlign: "center", currentStepLabelColor: "#4aae4f", }; const customStyles = Object.assign(defaultStyles, props.customStyles); this.state = { width: 0, height: 1, progressBarSize: -2, customStyles, }; this.progressAnim = new Animated.Value(0); this.sizeAnim = new Animated.Value( this.state.customStyles.stepIndicatorSize ); this.borderRadiusAnim = new Animated.Value( this.state.customStyles.stepIndicatorSize / 2 ); } stepPressed(position) { if (this.props.onPress) { this.props.onPress(position); } } render() { const { labels, direction } = this.props; return ( {this.state.width !== 0 && this.renderProgressBarBackground()} {this.state.width !== 0 && this.renderProgressBar()} {this.renderStepIndicator()} {labels && this.renderStepLabels()} ); } componentDidUpdate(prevProps) { // if (prevProps.customStyles !== this.props.customStyles) { // this.setState((state) => ({ // customStyles: Object.assign( // state.customStyles, // this.props.customStyles // ), // })); // } if (prevProps.currentPosition !== this.props.currentPosition) { this.onCurrentPositionChanged(this.props.currentPosition); } } renderProgressBarBackground = () => { const { stepCount, direction } = this.props; let progressBarBackgroundStyle; if (direction === "vertical") { progressBarBackgroundStyle = { backgroundColor: this.state.customStyles.separatorUnFinishedColor, position: "absolute", left: (this.state.width - this.state.customStyles.separatorStrokeWidth) / 2, top: this.state.height / (2 * stepCount), bottom: this.state.height / (2 * stepCount), width: this.state.customStyles.separatorStrokeUnfinishedWidth === 0 ? this.state.customStyles.separatorStrokeWidth : this.state.customStyles.separatorStrokeUnfinishedWidth, }; } else { progressBarBackgroundStyle = { backgroundColor: this.state.customStyles.separatorUnFinishedColor, position: "absolute", top: (this.state.height - this.state.customStyles.separatorStrokeWidth) / 2, left: this.state.width / (2 * stepCount), right: this.state.width / (2 * stepCount), height: this.state.customStyles.separatorStrokeUnfinishedWidth === 0 ? this.state.customStyles.separatorStrokeWidth : this.state.customStyles.separatorStrokeUnfinishedWidth, }; } return ( { if (direction === "vertical") { this.setState( { progressBarSize: event.nativeEvent.layout.height }, () => { this.onCurrentPositionChanged(this.props.currentPosition); } ); } else { this.setState( { progressBarSize: event.nativeEvent.layout.width }, () => { this.onCurrentPositionChanged(this.props.currentPosition); } ); } }} style={progressBarBackgroundStyle} /> ); }; renderProgressBar = () => { const { stepCount, direction } = this.props; let progressBarStyle; if (direction === "vertical") { progressBarStyle = { backgroundColor: this.state.customStyles.separatorFinishedColor, position: "absolute", left: (this.state.width - this.state.customStyles.separatorStrokeWidth) / 2, top: this.state.height / (2 * stepCount), bottom: this.state.height / (2 * stepCount), width: this.state.customStyles.separatorStrokeFinishedWidth === 0 ? this.state.customStyles.separatorStrokeWidth : this.state.customStyles.separatorStrokeFinishedWidth, height: this.progressAnim, }; } else { progressBarStyle = { backgroundColor: this.state.customStyles.separatorFinishedColor, position: "absolute", top: (this.state.height - this.state.customStyles.separatorStrokeWidth) / 2, left: this.state.width / (2 * stepCount), right: this.state.width / (2 * stepCount), height: this.state.customStyles.separatorStrokeFinishedWidth === 0 ? this.state.customStyles.separatorStrokeWidth : this.state.customStyles.separatorStrokeFinishedWidth, width: this.progressAnim, }; } return ; }; renderStepIndicator = () => { let steps = []; const { stepCount, direction } = this.props; for (let position = 0; position < stepCount; position++) { steps.push( this.stepPressed(position)} > {this.renderStep(position)} ); } return ( this.setState({ width: event.nativeEvent.layout.width, height: event.nativeEvent.layout.height, }) } style={[ styles.stepIndicatorContainer, direction === "vertical" ? { flexDirection: "column", width: this.state.customStyles.currentStepIndicatorSize, } : { flexDirection: "row", height: this.state.customStyles.currentStepIndicatorSize, }, ]} > {steps} ); }; renderStepLabels = () => { const { labels, direction, currentPosition, renderLabel } = this.props; var labelViews = labels.map((label, index) => { const selectedStepLabelStyle = index === currentPosition ? { color: this.state.customStyles.currentStepLabelColor } : { color: this.state.customStyles.labelColor }; return ( this.stepPressed(index)} > {renderLabel ? ( renderLabel({ position: index, stepStatus: this.getStepStatus(index), label, currentPosition, }) ) : ( {label} )} ); }); return ( {labelViews} ); }; renderStep = (position) => { const { renderStepIndicator } = this.props; let stepStyle; let indicatorLabelStyle; switch (this.getStepStatus(position)) { case STEP_STATUS.CURRENT: { stepStyle = { backgroundColor: this.state.customStyles.stepIndicatorCurrentColor, borderWidth: this.state.customStyles.currentStepStrokeWidth, borderColor: this.state.customStyles.stepStrokeCurrentColor, height: this.sizeAnim, width: this.sizeAnim, borderRadius: this.borderRadiusAnim, }; indicatorLabelStyle = { fontSize: this.state.customStyles.currentStepIndicatorLabelFontSize, color: this.state.customStyles.stepIndicatorLabelCurrentColor, }; break; } case STEP_STATUS.FINISHED: { stepStyle = { backgroundColor: this.state.customStyles.stepIndicatorFinishedColor, borderWidth: this.state.customStyles.stepStrokeWidth, borderColor: this.state.customStyles.stepStrokeFinishedColor, height: this.state.customStyles.stepIndicatorSize, width: this.state.customStyles.stepIndicatorSize, borderRadius: this.state.customStyles.stepIndicatorSize / 2, }; indicatorLabelStyle = { fontSize: this.state.customStyles.stepIndicatorLabelFontSize, color: this.state.customStyles.stepIndicatorLabelFinishedColor, }; break; } case STEP_STATUS.UNFINISHED: { stepStyle = { backgroundColor: this.state.customStyles.stepIndicatorUnFinishedColor, borderWidth: this.state.customStyles.stepStrokeWidth, borderColor: this.state.customStyles.stepStrokeUnFinishedColor, height: this.state.customStyles.stepIndicatorSize, width: this.state.customStyles.stepIndicatorSize, borderRadius: this.state.customStyles.stepIndicatorSize / 2, }; indicatorLabelStyle = { overflow: "hidden", fontSize: this.state.customStyles.stepIndicatorLabelFontSize, color: this.state.customStyles.stepIndicatorLabelUnFinishedColor, }; break; } default: } return ( {renderStepIndicator ? ( renderStepIndicator({ position, stepStatus: this.getStepStatus(position), }) ) : ( {`${position + 1}`} )} ); }; getStepStatus = (stepPosition) => { const { currentPosition } = this.props; if (stepPosition === currentPosition) { return STEP_STATUS.CURRENT; } else if (stepPosition < currentPosition) { return STEP_STATUS.FINISHED; } else { return STEP_STATUS.UNFINISHED; } }; onCurrentPositionChanged = (position) => { let { stepCount } = this.props; if (position > stepCount - 1) { position = stepCount - 1; } const animateToPosition = (this.state.progressBarSize / (stepCount - 1)) * position; this.sizeAnim.setValue(this.state.customStyles.stepIndicatorSize); this.borderRadiusAnim.setValue( this.state.customStyles.stepIndicatorSize / 2 ); Animated.sequence([ Animated.timing(this.progressAnim, { toValue: animateToPosition, duration: 200, }), Animated.parallel([ Animated.timing(this.sizeAnim, { toValue: this.state.customStyles.currentStepIndicatorSize, duration: 100, }), Animated.timing(this.borderRadiusAnim, { toValue: this.state.customStyles.currentStepIndicatorSize / 2, duration: 100, }), ]), ]).start(); }; } const styles = StyleSheet.create({ container: { backgroundColor: "transparent", }, stepIndicatorContainer: { flexDirection: "row", alignItems: "center", justifyContent: "space-around", backgroundColor: "transparent", }, stepLabelsContainer: { justifyContent: "space-around", }, step: { alignItems: "center", justifyContent: "center", zIndex: 2, }, stepContainer: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", }, stepLabel: { fontSize: 12, textAlign: "center", fontWeight: "500", }, stepLabelItem: { flex: 1, alignItems: "center", justifyContent: "center", }, }); StepIndicator.defaultProps = { currentPosition: 0, stepCount: 5, customStyles: {}, direction: "horizontal", };