react-native-multistep
Version:
Create multi-step forms with ease in your React Native app
121 lines (118 loc) • 4.18 kB
JavaScript
"use strict";
import React, { useState, forwardRef, useImperativeHandle } from 'react';
import { View, Text, TouchableOpacity, Animated } from 'react-native';
import { BACK, DONE, NEXT } from "./constants.js";
import { styles } from "./styles.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export const MultiStepForm = /*#__PURE__*/forwardRef(({
stepsContent,
onStepChange,
onStepForward,
onStepBackward,
backButtonLabel = BACK,
nextButtonLabel = NEXT,
doneButtonLabel = DONE,
onComplete,
style,
nextButtonStyle,
previousButtonStyle,
doneButtonStyle,
stepsContainerStyle,
activeStepStyle,
inactiveStepStyle,
contentContainerStyle,
buttonsContainerStyle,
lineColor,
activeLineColor
}, ref) => {
const totalSteps = stepsContent.length;
const [step, setStep] = useState(1);
if (totalSteps === 0 || !stepsContent) {
throw new Error('Missing steps content');
}
// Step transition animations
const [scaleAnim] = useState(new Animated.Value(1));
const changeStep = direction => {
setStep(prevStep => {
const newStep = direction === 'next' ? Math.min(prevStep + 1, totalSteps) : Math.max(prevStep - 1, 1);
Animated.timing(scaleAnim, {
toValue: 1.1,
duration: 200,
useNativeDriver: true
}).start(() => {
Animated.timing(scaleAnim, {
toValue: 1,
duration: 200,
useNativeDriver: true
}).start();
});
direction === 'next' ? onStepForward?.(newStep) : onStepBackward?.(newStep);
onStepChange?.(newStep);
if (newStep === totalSteps) onComplete?.(newStep);
return newStep;
});
};
// Expose methods to parent component via ref
useImperativeHandle(ref, () => ({
goToStep: targetStep => {
setStep(Math.min(Math.max(targetStep, 1), totalSteps));
onStepChange?.(targetStep);
}
}));
const stepIndicators = React.useMemo(() => {
return Array.from({
length: totalSteps
}, (_, i) => {
const isActive = i + 1 <= step;
const isCurrent = i + 1 === step;
return /*#__PURE__*/_jsxs(View, {
style: [styles.stepContainer, stepsContainerStyle],
children: [/*#__PURE__*/_jsx(Animated.View, {
style: [styles.stepIndicator, isActive ? [styles.activeStep, activeStepStyle, {
transform: [{
scale: isCurrent ? scaleAnim : 1
}]
}] : inactiveStepStyle],
children: /*#__PURE__*/_jsx(Text, {
style: [styles.stepText, isActive && styles.activeStepText],
children: i + 1
})
}), i + 1 < totalSteps && /*#__PURE__*/_jsx(View, {
style: [styles.line, {
backgroundColor: lineColor
}, i + 1 < step && [styles.activeLine, {
backgroundColor: activeLineColor
}]]
})]
}, i);
});
}, [totalSteps, step, stepsContainerStyle, activeStepStyle, scaleAnim, inactiveStepStyle, lineColor, activeLineColor]);
return /*#__PURE__*/_jsxs(View, {
style: [styles.container, style],
children: [/*#__PURE__*/_jsx(View, {
style: styles.indicatorContainer,
children: stepIndicators
}), /*#__PURE__*/_jsx(Animated.View, {
style: [styles.contentContainer, contentContainerStyle],
children: stepsContent[step - 1]
}), /*#__PURE__*/_jsxs(View, {
style: [styles.fixedFooter, buttonsContainerStyle],
children: [step > 1 && /*#__PURE__*/_jsx(TouchableOpacity, {
onPress: () => changeStep('previous'),
style: [styles.button, styles.backButton, previousButtonStyle],
children: /*#__PURE__*/_jsx(Text, {
style: styles.backButtonText,
children: backButtonLabel
})
}), /*#__PURE__*/_jsx(TouchableOpacity, {
onPress: () => changeStep('next'),
style: [styles.button, styles.nextButton, step < totalSteps ? nextButtonStyle : doneButtonStyle],
children: /*#__PURE__*/_jsx(Text, {
style: styles.nextButtonText,
children: step < totalSteps ? nextButtonLabel : doneButtonLabel
})
})]
})]
});
});
//# sourceMappingURL=index.js.map