@brijen/react-native-multistep
Version:
A lightweight multi-step view component for React Native with smooth transitions using Reanimated.
252 lines (250 loc) • 8.94 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _reactNative = require("react-native");
var _react = _interopRequireWildcard(require("react"));
var _Button = _interopRequireDefault(require("./Button.js"));
var _ProgressCircle = _interopRequireDefault(require("./ProgressCircle.js"));
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _Step = _interopRequireDefault(require("./Step.js"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
* A multi-step container for managing step-based navigation.
* It provides built-in navigation between steps with customizable buttons.
*
* @example
* ```tsx
* <MultiStep>
* <Step title="Step 1">
* <Text>Content for Step 1</Text>
* </Step>
* <Step title="Step 2">
* <Text>Content for Step 2</Text>
* </Step>
* </MultiStep>
* ```
*/
const MultiStep = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
const {
children,
prevButtonText,
nextButtonText,
prevButtonStyle,
nextButtonStyle,
prevButtonTextStyle,
nextButtonTextStyle,
prevButtonComponent,
nextButtonComponent,
tintColor,
globalStepTitleStyle,
globalNextStepTitleStyle,
progressCircleSize,
progressCircleThickness,
progressCircleColor,
progressCircleTrackColor,
progressCircleLabelStyle,
headerStyle,
globalStepContainerStyle,
fullScreenHeight,
buttonContainerStyle,
onFinalStepSubmit,
submitButtonText,
submitButtonTextStyle,
submitButtonStyle,
submitButtonComponent
} = props;
const COLOR = tintColor || '#DE3163';
const stepCount = _react.default.useMemo(() => _react.default.Children.count(children), [children]);
const [currentStep, setCurrentStep] = (0, _react.useState)(0);
const flatListRef = (0, _react.useRef)(null);
const {
width
} = (0, _reactNative.useWindowDimensions)();
const nextStep = () => {
if (currentStep < stepCount - 1) {
setCurrentStep(prev => {
const nextIndex = prev + 1;
flatListRef.current?.scrollToIndex({
index: nextIndex,
animated: true
});
return nextIndex;
});
}
};
const prevStep = () => {
if (currentStep > 0) {
setCurrentStep(prev => {
const prevIndex = prev - 1;
flatListRef.current?.scrollToIndex({
index: prevIndex,
animated: true
});
return prevIndex;
});
}
};
(0, _react.useImperativeHandle)(ref, () => ({
nextStep,
prevStep,
scrollToStep: index => {
if (index >= 0 && index < stepCount) {
setCurrentStep(index);
flatListRef.current?.scrollToIndex({
index,
animated: true
});
}
}
}));
const {
isValid,
titles
} = _react.default.useMemo(() => {
const extractedTitles = [];
let allValid = true;
_react.default.Children.forEach(children, child => {
if (! /*#__PURE__*/_react.default.isValidElement(child) || child.type !== _Step.default) {
allValid = false;
return;
}
extractedTitles.push({
title: child.props.title || '',
stepTitleStyle: child.props.stepTitleStyle || {},
nextStepTitleStyle: child.props.nextStepTitleStyle || {},
titleComponent: child.props.titleComponent
});
});
return {
isValid: allValid,
titles: extractedTitles
};
}, [children]);
if (!isValid) {
if (__DEV__) console.error('MultiStep only accepts `Step` components as direct children.');
return null;
}
if (titles.length === 0) {
if (__DEV__) console.error('MultiStep requires at least one Step component.');
return null;
}
const currentTitle = titles[currentStep];
const isFinalStep = currentStep === stepCount - 1;
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: [styles.multiStepContainer, fullScreenHeight && {
flex: 1
}],
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: [styles.navigationHeader, headerStyle],
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
style: styles.navigationItemWrapper,
entering: _reactNativeReanimated.FadeInLeft.duration(300).easing(_reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.quad)),
exiting: _reactNativeReanimated.FadeOutRight.duration(300).easing(_reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.quad)),
children: [currentTitle?.titleComponent ? currentTitle.titleComponent : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: [styles.currentStepTect, {
color: COLOR
}, globalStepTitleStyle, currentTitle?.stepTitleStyle],
children: currentTitle?.title
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: [styles.nextStepText, globalNextStepTitleStyle, currentTitle?.nextStepTitleStyle],
children: currentStep < stepCount - 1 ? `Next: ${titles[currentStep + 1]?.title}` : 'Completion'
})]
}, currentStep), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ProgressCircle.default, {
currentStep: currentStep + 1,
totalSteps: titles.length,
size: progressCircleSize,
progressCircleThickness: progressCircleThickness,
progressColor: progressCircleColor || COLOR,
trackColor: progressCircleTrackColor,
progressCircleLabelStyle: progressCircleLabelStyle
})]
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.FlatList, {
ref: flatListRef,
data: _react.default.Children.toArray(children),
horizontal: true,
pagingEnabled: true,
scrollEnabled: false,
showsHorizontalScrollIndicator: false,
keyExtractor: (_, index) => index.toString(),
renderItem: ({
item
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: [styles.stepContainer, {
width
}, globalStepContainerStyle],
children: item
}),
extraData: {
currentStep,
stepCount
},
itemLayoutAnimation: _reactNativeReanimated.LinearTransition
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: [styles.buttonGroup, buttonContainerStyle],
children: [prevButtonComponent ?? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
title: prevButtonText || 'Back',
variant: "secondary",
tintColor: COLOR,
style: prevButtonStyle,
textStyle: prevButtonTextStyle,
onPress: prevStep,
disabled: currentStep === 0
}), !isFinalStep && (nextButtonComponent ?? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
title: nextButtonText || 'Next',
variant: "primary",
tintColor: COLOR,
style: nextButtonStyle,
textStyle: nextButtonTextStyle,
onPress: nextStep
})), isFinalStep && (submitButtonComponent ?? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
title: submitButtonText || 'Submit',
variant: "primary",
tintColor: COLOR,
style: submitButtonStyle,
textStyle: submitButtonTextStyle,
onPress: onFinalStepSubmit
}))]
})]
});
});
var _default = exports.default = MultiStep;
const styles = _reactNative.StyleSheet.create({
multiStepContainer: {
gap: 15
},
navigationHeader: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 15,
width: '100%',
paddingHorizontal: 15
},
navigationItemWrapper: {
flex: 1,
gap: 10
},
stepContainer: {
paddingHorizontal: 15
},
currentStepTect: {
fontSize: 18,
fontWeight: '600'
},
nextStepText: {
color: '#45474B'
},
buttonGroup: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 15,
marginTop: 10
}
});
//# sourceMappingURL=MultiStep.js.map