├── .babelrc
├── .github
└── workflows
│ └── stale-and-close.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── assets
└── example.png
├── examples
└── example.jsx
├── package-lock.json
├── package.json
├── src
├── ProgressSteps
│ ├── ProgressStep.tsx
│ ├── ProgressSteps.tsx
│ └── StepIcon.tsx
├── index.ts
└── types.ts
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["module:metro-react-native-babel-preset"]
3 | }
4 |
--------------------------------------------------------------------------------
/.github/workflows/stale-and-close.yml:
--------------------------------------------------------------------------------
1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
2 | #
3 | # You can adjust the behavior by modifying this file.
4 | # For more information, see:
5 | # https://github.com/actions/stale
6 | name: Mark stale issues and pull requests then close
7 |
8 | on:
9 | schedule:
10 | - cron: '0 8 * * *'
11 | workflow_dispatch:
12 |
13 | jobs:
14 | stale:
15 |
16 | runs-on: ubuntu-latest
17 | permissions:
18 | issues: write
19 | pull-requests: write
20 |
21 | steps:
22 | - uses: actions/stale@v9
23 | with:
24 | close-issue-message: 'This issue is being automatically closed. The big release of v2 is finally here and addresses a lot of outstanding issues! If your problem still persists, please open another issue and we will take a look. Thanks for using the package!'
25 | close-pr-message: 'This PR is being automatically closed. The big release of v2 is finally here and a lot has changed! If you feel the PR is still relevant to the new package version, please open another and we will take a look. Thanks for contributing to the project!'
26 | days-before-close: 0
27 | operations-per-run: 75
28 |
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log
4 |
5 | # Runtime data
6 | tmp
7 | build
8 | dist
9 |
10 | # Dependency directory
11 | node_modules
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log
4 |
5 | # Dependency directory
6 | node_modules
7 |
8 | # Runtime data
9 | tmp
10 |
11 | # Examples
12 | examples
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Colby Miller
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | [](http://makeapullrequest.com)
4 |
5 | # React Native Progress Steps
6 |
7 | A simple and fully customizable React Native component that implements a progress stepper UI.
8 |
9 | - Each steps content is displayed inside of a customizable ScrollView.
10 | - Customizable buttons are displayed at the bottom of the component to move between steps.
11 |
12 | ## ✨ What's New in v2.0
13 |
14 | - 🎯 **Full TypeScript Support** - Complete type definitions for an enhanced development experience
15 | - 🔄 **Modern Component Architecture** - Refactored to use functional components and React hooks
16 | - 🎨 **Major UI/UX Improvements**
17 | - Enhanced responsiveness and layout
18 | - Modernized styling with new step icons, default colors, and button design
19 | - Improved performance
20 | - Better readability
21 | - 💫 **Smooth Step Transitions** - Added subtle animations when changing between steps
22 | - 🛠️ **Enhanced Customization** - Streamlined props with new customization options and removal of legacy features
23 | - ⛔️ **Breaking Changes** - Some props have been removed and renamed. See the [Migration Guide](#migration-guide-v1-to-v2) for more details.
24 |
25 | ## Example
26 |
27 |
29 |
30 | examples/example.jsx
31 |
32 | ## Installation
33 |
34 | ```
35 | npm i react-native-progress-steps
36 | ```
37 |
38 | ## Usage
39 |
40 | ```
41 | import { ProgressSteps, ProgressStep } from 'react-native-progress-steps';
42 | ```
43 |
44 | Simply place a `` tag for each desired step within the `` wrapper.
45 |
46 | ```
47 |
48 |
49 |
50 |
51 | This is the content within step 1!
52 |
53 |
54 |
55 |
56 | This is the content within step 2!
57 |
58 |
59 |
60 |
61 | This is the content within step 3!
62 |
63 |
64 |
65 |
66 | ```
67 |
68 | ### Button Styling Usage
69 |
70 | Navigation buttons are customizable using the various props provided to the `ProgressStep` component. See the [Progress Step Component](#progress-step-component) section for more details.
71 |
72 | Simple example to set a specific button text:
73 |
74 | ```
75 | return (
76 |
77 |
78 |
79 |
80 | This is the content within step 1!
81 |
82 |
83 |
84 |
85 | This is the content within step 2!
86 |
87 |
88 |
89 |
90 | )
91 | ```
92 |
93 | ### Hiding the Button Row
94 |
95 | To hide the button row, set the `removeBtnRow` prop to `true`. The current step can be changed without the buttons by updating and managing the `activeStep` prop on the `` component.
96 |
97 | ```
98 | const [activeStep, setActiveStep] = useState(0);
99 |
100 |
101 |
102 |
103 | This is the content within step 1!
104 |
105 |
106 |
107 | ```
108 |
109 | ### Current Step Error and Validation Handling
110 |
111 | The `errors` prop should be used if there's a need for validation and error handling when clicking the next button. If you would like to prevent the next step from being rendered, set the `errors` prop to `true`. By default, it will be `false`.
112 |
113 | Example usage of validation check:
114 |
115 | ```
116 | const [isValid, setIsValid] = useState(false);
117 | const [errors, setErrors] = useState(false);
118 |
119 | const onNextStep = () => {
120 | if (!isValid) {
121 | setErrors(true);
122 | } else {
123 | setErrors(false);
124 | }
125 | };
126 |
127 | return (
128 |
129 |
130 |
131 |
132 | This is the content within step 1!
133 |
134 |
135 |
136 |
137 | This is the content within step 2!
138 |
139 |
140 |
141 |
142 | );
143 | ```
144 |
145 | ## Documentation
146 |
147 | ### Progress Steps Component
148 |
149 | | Name | Description | Default | Type |
150 | | ------------------------- | ------------------------------------------------- | -------------- | ------- |
151 | | borderWidth | Width of the progress bar between steps | 2 | Number |
152 | | activeStepIconBorderColor | Outside border color for the active step | #2D2D2D | String |
153 | | progressBarColor | Color of the default progress bar | #EBEBE4 | String |
154 | | completedProgressBarColor | Color of the completed progress bar | #2D2D2D | String |
155 | | activeStepIconColor | Color of the active step icon | transparent | String |
156 | | completedStepIconColor | Color of the completed step icon | #2D2D2D | String |
157 | | disabledStepIconColor | Color of the disabled step icon | #EBEBE4 | String |
158 | | labelFontFamily | Font family for the step icon label | System default | String |
159 | | labelColor | Color of the default label | #D3D3D3 | String |
160 | | labelFontSize | Font size for the step icon label | 14 | Number |
161 | | activeLabelColor | Color of the active label | #2D2D2D | String |
162 | | activeLabelFontSize | Optional font size for the active step icon label | 14 | Number |
163 | | completedLabelColor | Color of the completed label | #2D2D2D | String |
164 | | activeStepNumColor | Color of the active step number | #2D2D2D | String |
165 | | completedStepNumColor | Color of the completed step number | #2D2D2D | String |
166 | | disabledStepNumColor | Color of the disabled step number | #FFFFFF | String |
167 | | completedCheckColor | Color of the completed step checkmark | #FFFFFF | String |
168 | | activeStep | Manually specify the active step | 0 | Number |
169 | | isComplete | Set all Steps to active state | false | Boolean |
170 | | topOffset | Set progress bar top offset | 60 | Number |
171 | | marginBottom | Set progress bar bottom margin | 30 | Number |
172 |
173 | ### Progress Step Component
174 |
175 | | Name | Description | Default | Type |
176 | | ----------------------- | ------------------------------------------------------------------------------------- | --------- | ------- |
177 | | label | Title of the current step to be displayed | undefined | String |
178 | | onNext | Function called when the next step button is pressed | undefined | Func |
179 | | onPrevious | Function called when the previous step button is pressed | undefined | Func |
180 | | onSubmit | Function called when the submit step button is pressed | undefined | Func |
181 | | scrollViewProps | Object to provide props to ScrollView component | undefined | Object |
182 | | scrollable | The content of the Progress Step should be scrollable | true | Boolean |
183 | | viewProps | Object to provide props to view component if scrollable is false | undefined | Object |
184 | | errors | Used to assist with current step validation. If true, the next step won't be rendered | false | Boolean |
185 | | removeBtnRow | Used to render the progress step without the button row | false | Boolean |
186 | | buttonNextText | Text to display inside the next button | Next | String |
187 | | buttonPreviousText | Text to display inside the previous button | Previous | String |
188 | | buttonFinishText | Text to display inside the button on the last step | Submit | String |
189 | | buttonNextDisabled | Value to disable/enable next button | false | Boolean |
190 | | buttonPreviousDisabled | Value to disable/enable previous button | false | Boolean |
191 | | buttonFinishDisabled | Value to disable/enable finish button | false | Boolean |
192 | | buttonTopOffset | Set button row top offset | 12 | Number |
193 | | buttonBottomOffset | Set button row bottom offset | 20 | Number |
194 | | buttonHorizontalOffset | Set button row horizontal offset | 30 | Number |
195 | | buttonFillColor | Background color for the next/finish buttons | #2D2D2D | String |
196 | | buttonBorderColor | Border color for the previous button | #2D2D2D | String |
197 | | buttonNextTextColor | Text color for the next button | #FFFFFF | String |
198 | | buttonPreviousTextColor | Text color for the previous button | #2D2D2D | String |
199 | | buttonFinishTextColor | Text color for the finish button | #FFFFFF | String |
200 | | buttonDisabledColor | Background color for disabled buttons | #CDCDCD | String |
201 | | buttonDisabledTextColor | Text color for disabled buttons | #FFFFFF | String |
202 |
203 | ## Migration Guide (v1 to v2)
204 |
205 | ### Breaking Changes
206 |
207 | #### Renamed Props
208 |
209 | The following props have been renamed for better clarity and consistency:
210 |
211 | | Old Prop Name | New Prop Name | Component |
212 | | ------------------- | ---------------------- | ------------ |
213 | | nextBtnText | buttonNextText | ProgressStep |
214 | | previousBtnText | buttonPreviousText | ProgressStep |
215 | | finishBtnText | buttonFinishText | ProgressStep |
216 | | nextBtnDisabled | buttonNextDisabled | ProgressStep |
217 | | previousBtnDisabled | buttonPreviousDisabled | ProgressStep |
218 |
219 | #### Removed Props
220 |
221 | The following props have been removed in favor of the new styling system:
222 |
223 | | Removed Prop | Alternative |
224 | | -------------------- | ------------------------------------------------------ |
225 | | nextBtnStyle | Use `buttonFillColor` and other button styling props |
226 | | nextBtnTextStyle | Use `buttonNextTextColor` instead |
227 | | previousBtnStyle | Use `buttonBorderColor` and other button styling props |
228 | | previousBtnTextStyle | Use `buttonPreviousTextColor` instead |
229 | | finishBtnStyle | Use `buttonFillColor` and other button styling props |
230 | | finishBtnTextStyle | Use `buttonFinishTextColor` instead |
231 | | borderStyle | No longer supported |
232 |
233 | #### New Button Styling System
234 |
235 | Instead of using style objects, the new version provides specific props for common button customizations:
236 |
237 | ```
238 | // Old way
239 |
243 | {/* content */}
244 |
245 |
246 | // New way
247 |
251 | {/* content */}
252 |
253 | ```
254 |
255 | See the [Progress Step Component](#progress-step-component) section for all available button styling props.
256 |
257 | ## Contributing
258 |
259 | Pull requests are always welcome! Feel free to open a new GitHub issue for any changes that can be made.
260 |
261 | **Working on your first Pull Request?** You can learn how from GitHub's [First Contributions](https://github.com/firstcontributions/first-contributions) guide or their [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) guide.
262 |
263 | ## Author
264 |
265 | Colby Miller | [https://colbymillerdev.com](https://colbymillerdev.com)
266 |
267 | ## License
268 |
269 | [MIT](./LICENSE)
270 |
--------------------------------------------------------------------------------
/assets/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/colbymillerdev/react-native-progress-steps/1d8b0e07bc147160fc9e237563afa6382ec1e1f8/assets/example.png
--------------------------------------------------------------------------------
/examples/example.jsx:
--------------------------------------------------------------------------------
1 | import { View, Text, StyleProp, ViewStyle } from 'react-native';
2 | import { ProgressStep, ProgressSteps } from 'react-native-progress-steps';
3 |
4 | export default function ExampleProgressSteps() {
5 | const defaultScrollViewProps = {
6 | keyboardShouldPersistTaps: 'handled',
7 | contentContainerStyle: {
8 | flexGrow: 1,
9 | justifyContent: 'center',
10 | },
11 | };
12 |
13 | const onPaymentStepComplete = () => {
14 | console.log('payment step completed!');
15 | };
16 |
17 | const onNextStep = () => {
18 | console.log('called next step');
19 | };
20 |
21 | const onPrevStep = () => {
22 | console.log('called previous step');
23 | };
24 |
25 | const onSubmitSteps = () => {
26 | console.log('called submit step');
27 | };
28 |
29 | return (
30 |
31 |
32 |
38 |
39 | Payment step content
40 |
41 |
42 |
48 |
49 | Shipping address step content
50 |
51 |
52 |
58 |
59 | Billing address step content
60 |
61 |
62 |
68 |
69 | Confirm order step content
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-progress-steps",
3 | "version": "2.0.3",
4 | "description": "A simple and fully customizable React Native component that implements a progress stepper UI.",
5 | "author": "Colby Miller",
6 | "license": "MIT",
7 | "main": "./dist/index.js",
8 | "types": "./dist/index.d.ts",
9 | "files": [
10 | "dist",
11 | "src"
12 | ],
13 | "scripts": {
14 | "test": "echo \"Error: no test specified\" && exit 1",
15 | "build": "tsc",
16 | "prepare": "npm run build"
17 | },
18 | "dependencies": {
19 | "lodash": "^4.17.21"
20 | },
21 | "devDependencies": {
22 | "@types/lodash": "^4.17.15",
23 | "@types/react": "^18.3.18",
24 | "metro-react-native-babel-preset": "^0.77.0",
25 | "typescript": "^5.7.3"
26 | },
27 | "peerDependencies": {
28 | "react": ">=16.8.0",
29 | "react-native": ">=0.59.0",
30 | "lucide-react-native": ">=0.475.0"
31 | },
32 | "keywords": [
33 | "react-native",
34 | "react component",
35 | "react native component",
36 | "react",
37 | "react native",
38 | "mobile",
39 | "ios",
40 | "android",
41 | "ui",
42 | "stepper",
43 | "progress",
44 | "progress steps",
45 | "typescript"
46 | ],
47 | "repository": {
48 | "type": "git",
49 | "url": "git+https://github.com/colbymillerdev/react-native-progress-steps.git"
50 | },
51 | "bugs": {
52 | "url": "https://github.com/colbymillerdev/react-native-progress-steps/issues"
53 | },
54 | "homepage": "https://github.com/colbymillerdev/react-native-progress-steps#readme"
55 | }
56 |
--------------------------------------------------------------------------------
/src/ProgressSteps/ProgressStep.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, ScrollView, Text, Pressable, ViewStyle, TextStyle } from 'react-native';
3 | import type { ProgressStepProps } from '../types';
4 |
5 | const ProgressStep = ({
6 | errors = false,
7 | removeBtnRow = false,
8 | scrollable = true,
9 | activeStep = 0,
10 | stepCount = 0,
11 | buttonNextText = 'Next',
12 | buttonPreviousText = 'Previous',
13 | buttonFinishText = 'Submit',
14 | buttonNextDisabled = false,
15 | buttonPreviousDisabled = false,
16 | buttonFinishDisabled = false,
17 | buttonBottomOffset = 20,
18 | buttonTopOffset = 12,
19 | buttonHorizontalOffset = 30,
20 | buttonFillColor = '#2D2D2D',
21 | buttonNextTextColor = '#FFFFFF',
22 | buttonPreviousTextColor = '#2D2D2D',
23 | buttonFinishTextColor = '#FFFFFF',
24 | buttonBorderColor = '#2D2D2D',
25 | buttonDisabledColor = '#CDCDCD',
26 | buttonDisabledTextColor = '#FFFFFF',
27 | ...props
28 | }: ProgressStepProps) => {
29 | const isPreviousBtnHidden = activeStep === 0;
30 | const isFirstStep = activeStep === 0;
31 |
32 | const onNextStep = (): void => {
33 | props.onNext?.();
34 |
35 | if (!errors && props.setActiveStep) {
36 | props.setActiveStep(activeStep + 1);
37 | }
38 | };
39 |
40 | const onPreviousStep = (): void => {
41 | props.onPrevious?.();
42 |
43 | if (props.setActiveStep) {
44 | props.setActiveStep(activeStep - 1);
45 | }
46 | };
47 |
48 | const baseButtonStyle: ViewStyle = {
49 | height: 48,
50 | minWidth: 120,
51 | borderRadius: 6,
52 | justifyContent: 'center',
53 | alignItems: 'center',
54 | };
55 |
56 | const baseTextStyle: TextStyle = {
57 | fontSize: 16,
58 | fontWeight: '500',
59 | };
60 |
61 | const renderNextButton = (): JSX.Element => (
62 | [
64 | baseButtonStyle,
65 | {
66 | backgroundColor: buttonFillColor,
67 | opacity: pressed ? 0.8 : 1,
68 | },
69 | buttonNextDisabled && { backgroundColor: buttonDisabledColor },
70 | ]}
71 | onPress={onNextStep}
72 | disabled={buttonNextDisabled}
73 | >
74 | {buttonNextText}
75 |
76 | );
77 |
78 | const renderPreviousButton = (): JSX.Element => (
79 | [
81 | baseButtonStyle,
82 | {
83 | borderWidth: 1,
84 | borderColor: buttonPreviousDisabled ? 'transparent' : buttonBorderColor,
85 | opacity: pressed ? 0.8 : 1,
86 | },
87 | buttonPreviousDisabled && { backgroundColor: buttonDisabledColor },
88 | ]}
89 | onPress={onPreviousStep}
90 | disabled={buttonPreviousDisabled}
91 | >
92 |
95 | {isFirstStep ? '' : buttonPreviousText}
96 |
97 |
98 | );
99 |
100 | const renderSubmitButton = (): JSX.Element => (
101 | [
103 | baseButtonStyle,
104 | {
105 | backgroundColor: buttonFillColor,
106 | opacity: pressed ? 0.8 : 1,
107 | },
108 | buttonFinishDisabled && { backgroundColor: buttonDisabledColor },
109 | ]}
110 | onPress={props.onSubmit}
111 | disabled={buttonFinishDisabled}
112 | >
113 | {buttonFinishText}
114 |
115 | );
116 |
117 | const Container = scrollable ? ScrollView : View;
118 | const containerProps = scrollable ? props.scrollViewProps : props.viewProps;
119 |
120 | return (
121 |
122 |
123 | {props.children}
124 |
125 | {!removeBtnRow && (
126 |
135 | {!isPreviousBtnHidden && renderPreviousButton()}
136 |
137 | {activeStep === stepCount - 1 ? renderSubmitButton() : renderNextButton()}
138 |
139 |
140 | )}
141 |
142 | );
143 | };
144 |
145 | export default ProgressStep;
146 |
--------------------------------------------------------------------------------
/src/ProgressSteps/ProgressSteps.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, StyleSheet } from 'react-native';
3 | import { times } from 'lodash';
4 | import StepIcon from './StepIcon';
5 | import type { ProgressStepsProps, ProgressStepsState } from '../types';
6 |
7 | const ProgressSteps = ({
8 | children,
9 | isComplete = false,
10 | activeStep: initialActiveStep = 0,
11 | topOffset = 60,
12 | marginBottom = 30,
13 | ...props
14 | }: ProgressStepsProps) => {
15 | const [stepCount, setStepCount] = React.useState(0);
16 | const [activeStep, setActiveStep] = React.useState(initialActiveStep);
17 |
18 | React.useEffect(() => {
19 | setStepCount(React.Children.count(children));
20 | }, [children]);
21 |
22 | React.useEffect(() => {
23 | setActiveStep(initialActiveStep);
24 | }, [initialActiveStep]);
25 |
26 | const handleSetActiveStep = (step: number): void => {
27 | const boundedStep = Math.min(Math.max(step, 0), stepCount - 1);
28 | setActiveStep(boundedStep);
29 | };
30 |
31 | const renderStepIcons = (): JSX.Element[] => {
32 | return times(stepCount, (i) => {
33 | const isCompletedStep = isComplete ? true : i < activeStep;
34 | const isActiveStep = isComplete ? false : i === activeStep;
35 |
36 | return (
37 |
38 |
47 |
48 | );
49 | });
50 | };
51 |
52 | return (
53 |
54 | {renderStepIcons()}
55 |
56 | {React.cloneElement(children[activeStep], {
57 | setActiveStep: handleSetActiveStep,
58 | activeStep,
59 | stepCount,
60 | })}
61 |
62 |
63 | );
64 | };
65 |
66 | const styles = StyleSheet.create({
67 | container: {
68 | flex: 1,
69 | },
70 | stepsContainer: {
71 | flexDirection: 'row',
72 | width: '100%',
73 | },
74 | stepContainer: {
75 | flex: 1,
76 | },
77 | contentContainer: {
78 | flex: 1,
79 | },
80 | });
81 |
82 | export default ProgressSteps;
83 |
--------------------------------------------------------------------------------
/src/ProgressSteps/StepIcon.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 | import { View, Text, StyleSheet, Dimensions, Animated } from 'react-native';
3 | import { Check } from 'lucide-react-native';
4 | import type { StepIconProps } from '../types';
5 |
6 | const CIRCLE_SIZE = 40;
7 | const MOBILE_BREAKPOINT = 768;
8 | const MOBILE_LINE_POSITION = 78;
9 | const DESKTOP_LINE_POSITION = 58;
10 |
11 | const StepIcon = ({
12 | borderWidth = 2,
13 | activeStepIconBorderColor = '#2D2D2D',
14 | progressBarColor = '#EBEBE4',
15 | completedProgressBarColor = '#2D2D2D',
16 | activeStepIconColor = 'transparent',
17 | completedStepIconColor = '#2D2D2D',
18 | disabledStepIconColor = '#EBEBE4',
19 | labelColor = '#D3D3D3',
20 | labelFontSize = 14,
21 | activeLabelColor = '#2D2D2D',
22 | completedLabelColor = '#2D2D2D',
23 | activeStepNumColor = '#2D2D2D',
24 | completedStepNumColor = '#2D2D2D',
25 | disabledStepNumColor = '#FFFFFF',
26 | completedCheckColor = '#FFFFFF',
27 | ...props
28 | }: StepIconProps) => {
29 | const {
30 | isActiveStep,
31 | isCompletedStep,
32 | isFirstStep,
33 | isLastStep,
34 | stepNum,
35 | label,
36 | labelFontFamily,
37 | activeLabelFontSize,
38 | } = props;
39 |
40 | const lineAnimationValue = useRef(new Animated.Value(0)).current;
41 |
42 | useEffect(() => {
43 | if (isCompletedStep || isActiveStep) {
44 | Animated.spring(lineAnimationValue, {
45 | toValue: 1,
46 | useNativeDriver: false,
47 | tension: 25,
48 | friction: 25,
49 | }).start();
50 | } else {
51 | lineAnimationValue.setValue(0);
52 | }
53 | }, [isCompletedStep, isActiveStep]);
54 |
55 | const getLinePosition = () => {
56 | const screenWidth = Dimensions.get('window').width;
57 | const isMobileWidth = screenWidth <= MOBILE_BREAKPOINT;
58 | return isMobileWidth ? MOBILE_LINE_POSITION : DESKTOP_LINE_POSITION;
59 | };
60 |
61 | const getLineColor = (isLeftLine: boolean) => {
62 | if (isLeftLine && (isCompletedStep || isActiveStep)) {
63 | return completedProgressBarColor;
64 | }
65 |
66 | if (!isLeftLine && isCompletedStep) {
67 | return completedProgressBarColor;
68 | }
69 |
70 | return progressBarColor;
71 | };
72 |
73 | const getStepColor = () => {
74 | if (isActiveStep) return activeStepIconColor;
75 | if (isCompletedStep) return completedStepIconColor;
76 | return disabledStepIconColor;
77 | };
78 |
79 | const getLabelColor = () => {
80 | if (isActiveStep) return activeLabelColor;
81 | if (isCompletedStep) return completedLabelColor;
82 | return labelColor;
83 | };
84 |
85 | const getNumberColor = () => {
86 | if (isActiveStep) return activeStepNumColor;
87 | if (isCompletedStep) return completedStepNumColor;
88 | return disabledStepNumColor;
89 | };
90 |
91 | const linePosition = getLinePosition();
92 |
93 | const renderLine = (isLeft: boolean) => (
94 |
105 |
118 |
119 | );
120 |
121 | return (
122 |
123 |
124 | {!isFirstStep && renderLine(true)}
125 |
140 | {isCompletedStep ? (
141 |
142 | ) : (
143 | {stepNum}
144 | )}
145 |
146 | {!isLastStep && renderLine(false)}
147 |
148 |
159 | {label}
160 |
161 |
162 | );
163 | };
164 |
165 | const styles = StyleSheet.create({
166 | container: {
167 | alignItems: 'center',
168 | },
169 | lineContainer: {
170 | width: '100%',
171 | flexDirection: 'row',
172 | alignItems: 'center',
173 | justifyContent: 'center',
174 | position: 'relative',
175 | },
176 | circle: {
177 | justifyContent: 'center',
178 | alignItems: 'center',
179 | zIndex: 2,
180 | },
181 | line: {
182 | zIndex: 1,
183 | },
184 | stepText: {
185 | fontSize: 16,
186 | fontWeight: '500',
187 | },
188 | label: {
189 | textAlign: 'center',
190 | marginTop: 8,
191 | maxWidth: '80%',
192 | },
193 | });
194 |
195 | export default StepIcon;
196 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as ProgressSteps } from './ProgressSteps/ProgressSteps';
2 | export { default as ProgressStep } from './ProgressSteps/ProgressStep';
3 |
4 | export type { ProgressStepsProps, ProgressStepProps } from './types';
5 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { ScrollViewProps, ViewProps } from 'react-native';
2 | export interface ProgressStepsProps
3 | extends Omit {
4 | isComplete?: boolean;
5 | activeStep?: number;
6 | topOffset?: number;
7 | marginBottom?: number;
8 | children: React.ReactElement[];
9 | }
10 |
11 | export interface ProgressStepsState {
12 | stepCount: number;
13 | activeStep: number;
14 | }
15 |
16 | export interface ProgressStepProps {
17 | // Internal props (set by parent)
18 | setActiveStep?: (step: number) => void;
19 | activeStep?: number;
20 | stepCount?: number;
21 |
22 | // User-provided props
23 | label?: string;
24 | onNext?: () => void;
25 | onPrevious?: () => void;
26 | onSubmit?: () => void;
27 | scrollViewProps?: ScrollViewProps;
28 | scrollable?: boolean;
29 | viewProps?: ViewProps;
30 | errors?: boolean;
31 | removeBtnRow?: boolean;
32 | children?: React.ReactNode;
33 | buttonNextText?: string;
34 | buttonPreviousText?: string;
35 | buttonFinishText?: string;
36 | buttonNextDisabled?: boolean;
37 | buttonPreviousDisabled?: boolean;
38 | buttonFinishDisabled?: boolean;
39 | buttonTopOffset?: number;
40 | buttonBottomOffset?: number;
41 | buttonHorizontalOffset?: number;
42 | buttonFillColor?: string;
43 | buttonBorderColor?: string;
44 | buttonNextTextColor?: string;
45 | buttonPreviousTextColor?: string;
46 | buttonFinishTextColor?: string;
47 | buttonDisabledColor?: string;
48 | buttonDisabledTextColor?: string;
49 | }
50 |
51 | export interface StepIconProps {
52 | // Required props
53 | stepNum: number;
54 | isFirstStep: boolean;
55 | isLastStep: boolean;
56 | isActiveStep: boolean;
57 | isCompletedStep: boolean;
58 | label?: string;
59 |
60 | // Style props (all optional with defaults)
61 | borderWidth?: number;
62 | activeStepIconBorderColor?: string;
63 | progressBarColor?: string;
64 | completedProgressBarColor?: string;
65 | activeStepIconColor?: string;
66 | disabledStepIconColor?: string;
67 | completedStepIconColor?: string;
68 | labelFontFamily?: string;
69 | labelColor?: string;
70 | labelFontSize?: number;
71 | activeLabelColor?: string;
72 | activeLabelFontSize?: number;
73 | completedLabelColor?: string;
74 | activeStepNumColor?: string;
75 | completedStepNumColor?: string;
76 | disabledStepNumColor?: string;
77 | completedCheckColor?: string;
78 | }
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "allowJs": true,
7 | "strict": true,
8 | "jsx": "react",
9 | "esModuleInterop": true,
10 | "resolveJsonModule": true,
11 | "skipLibCheck": true,
12 | "lib": ["esnext", "dom"],
13 | "baseUrl": ".",
14 | "allowUnreachableCode": false,
15 | "allowUnusedLabels": false,
16 | "forceConsistentCasingInFileNames": true,
17 | "noImplicitUseStrict": false,
18 | "noStrictGenericChecks": false,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "declaration": true,
22 | "declarationMap": true,
23 | "outDir": "dist",
24 | "rootDir": "src",
25 | "allowSyntheticDefaultImports": true
26 | },
27 | "include": ["src"],
28 | "exclude": ["node_modules", "dist", "examples", "**/__tests__/**"]
29 | }
30 |
--------------------------------------------------------------------------------