UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

376 lines (359 loc) • 10.6 kB
"use strict"; import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { View, KeyboardAvoidingView, ScrollView, StatusBar, Platform } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, withTiming, runOnJS } from 'react-native-reanimated'; import { useThemeColors, createAuthStyles } from '../styles'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; // Individual animated progress dot const AnimatedProgressDot = ({ isActive, colors, styles }) => { const width = useSharedValue(isActive ? 12 : 6); const backgroundColor = useSharedValue(isActive ? colors.primary : colors.border); useEffect(() => { width.value = withTiming(isActive ? 12 : 6, { duration: 300 }); backgroundColor.value = withTiming(isActive ? colors.primary : colors.border, { duration: 300 }); }, [isActive, colors.primary, colors.border, width, backgroundColor]); const animatedStyle = useAnimatedStyle(() => ({ width: width.value, backgroundColor: backgroundColor.value })); return /*#__PURE__*/_jsx(Animated.View, { style: [styles.progressDot, animatedStyle] }); }; // Progress indicator component const ProgressIndicator = ({ currentStep, totalSteps, colors, styles }) => /*#__PURE__*/_jsx(View, { style: styles.progressContainer, children: Array.from({ length: totalSteps }, (_, index) => /*#__PURE__*/_jsx(AnimatedProgressDot, { isActive: currentStep === index, colors: colors, styles: styles }, index)) }); // Step container with animations const AnimatedStepContainer = ({ children, fadeAnim, slideAnim, scaleAnim, styles, stepKey }) => { const animatedStyle = useAnimatedStyle(() => ({ opacity: fadeAnim.value, transform: [{ translateX: slideAnim.value }, { scale: scaleAnim.value }] })); return /*#__PURE__*/_jsx(Animated.View, { style: [styles.stepContainer, animatedStyle], children: children }, stepKey); }; const StepBasedScreen = ({ steps, initialStep = 0, showProgressIndicator = true, enableAnimations = true, onStepChange, onComplete, stepData = [], navigate, goBack, onAuthenticated, theme, oxyServices }) => { const colors = useThemeColors(theme); const styles = useMemo(() => ({ ...createAuthStyles(colors, theme), // Additional styles for step components modernHeader: { alignItems: 'flex-start', width: '100%', marginBottom: 24 }, modernTitle: { fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold', fontWeight: Platform.OS === 'web' ? 'bold' : undefined, fontSize: 42, lineHeight: 50.4, // 42 * 1.2 marginBottom: 12, textAlign: 'left' }, modernSubtitle: { fontSize: 18, lineHeight: 24, textAlign: 'left', opacity: 0.8 }, modernInputContainer: { width: '100%' }, button: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: 18, paddingHorizontal: 32, borderRadius: 16, marginVertical: 8, gap: 8, width: '100%', ...Platform.select({ web: { boxShadow: '0 4px 8px rgba(0,0,0,0.3)' }, default: { shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8, elevation: 6 } }) }, buttonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', letterSpacing: 0.5 }, footerText: { fontSize: 14, lineHeight: 20 }, footerTextContainer: { flexDirection: 'row', justifyContent: 'center', marginTop: 16 }, linkText: { fontSize: 14, lineHeight: 20, fontWeight: '600', textDecorationLine: 'underline' }, progressContainer: { flexDirection: 'row', width: '100%', justifyContent: 'center', marginTop: 24, // Space for bottom sheet handle (~20px) + small buffer marginBottom: 24 // Equal spacing below dots }, progressDot: { height: 6, width: 6, borderRadius: 3, marginHorizontal: 3, backgroundColor: colors.border } }), [colors, theme]); // State management const [state, setState] = useState({ currentStep: initialStep, stepData: stepData, isTransitioning: false }); // Update state when stepData prop changes useEffect(() => { setState(prevState => ({ ...prevState, stepData: stepData })); }, [stepData]); // Animation values const fadeAnim = useSharedValue(1); const slideAnim = useSharedValue(0); const scaleAnim = useSharedValue(1); // Refs for animation callbacks const onStepChangeRef = useRef(onStepChange); const onCompleteRef = useRef(onComplete); onStepChangeRef.current = onStepChange; onCompleteRef.current = onComplete; // Update step data const updateStepData = useCallback((stepIndex, data) => { setState(prev => ({ ...prev, stepData: prev.stepData.map((item, index) => index === stepIndex ? data : item) })); }, [setState]); // Animation transition function const animateTransition = useCallback(nextStep => { if (!enableAnimations) { setState(prev => ({ ...prev, currentStep: nextStep })); onStepChangeRef.current?.(nextStep, steps.length); return; } setState(prev => ({ ...prev, isTransitioning: true })); const applyStepChange = (targetStep, totalSteps) => { setState(prev => ({ ...prev, currentStep: targetStep, isTransitioning: false })); onStepChangeRef.current?.(targetStep, totalSteps); // Prepare next step animation fadeAnim.value = 0; scaleAnim.value = 0.98; slideAnim.value = 0; fadeAnim.value = withTiming(1, { duration: 220 }); scaleAnim.value = withTiming(1, { duration: 220 }); }; // Animate current step out scaleAnim.value = withTiming(0.98, { duration: 180 }); fadeAnim.value = withTiming(0, { duration: 180 }, finished => { if (finished) { runOnJS(applyStepChange)(nextStep, steps.length); } }); }, [enableAnimations, steps.length, fadeAnim, scaleAnim, slideAnim]); // Navigation functions const nextStep = useCallback(() => { if (state.isTransitioning) return; const currentStepConfig = steps[state.currentStep]; if (currentStepConfig?.canProceed) { const stepData = state.stepData[state.currentStep]; if (!currentStepConfig.canProceed(stepData)) { return; // Step validation failed } } if (state.currentStep < steps.length - 1) { // Call onExit for current step currentStepConfig?.onExit?.(); animateTransition(state.currentStep + 1); // Call onEnter for next step const nextStepConfig = steps[state.currentStep + 1]; nextStepConfig?.onEnter?.(); } else { // Final step - call onComplete onCompleteRef.current?.(state.stepData); } }, [state.currentStep, state.stepData, state.isTransitioning, steps, animateTransition]); const prevStep = useCallback(() => { if (state.isTransitioning) return; if (state.currentStep > 0) { // Call onExit for current step const currentStepConfig = steps[state.currentStep]; currentStepConfig?.onExit?.(); animateTransition(state.currentStep - 1); // Call onEnter for previous step const prevStepConfig = steps[state.currentStep - 1]; prevStepConfig?.onEnter?.(); } else { // First step - go back goBack?.(); } }, [state.currentStep, state.isTransitioning, steps, animateTransition, goBack]); const goToStep = useCallback(stepIndex => { if (state.isTransitioning || stepIndex < 0 || stepIndex >= steps.length) return; if (stepIndex !== state.currentStep) { // Call onExit for current step const currentStepConfig = steps[state.currentStep]; currentStepConfig?.onExit?.(); animateTransition(stepIndex); // Call onEnter for target step const targetStepConfig = steps[stepIndex]; targetStepConfig?.onEnter?.(); } }, [state.currentStep, state.isTransitioning, steps, animateTransition]); // Get current step component const currentStepConfig = steps[state.currentStep]; const CurrentStepComponent = currentStepConfig?.component; // Enhanced props for the step component const stepProps = { ...currentStepConfig?.props, // Common props colors, styles, theme, navigate, goBack, onAuthenticated, oxyServices, // Step navigation nextStep, prevStep, goToStep, currentStep: state.currentStep, totalSteps: steps.length, // Step data - spread the step data properties directly as props ...state.stepData[state.currentStep], // Step data management updateStepData: data => updateStepData(state.currentStep, data), allStepData: state.stepData, // State isTransitioning: state.isTransitioning, // Animation refs (for components that need direct access) fadeAnim, slideAnim, scaleAnim }; return /*#__PURE__*/_jsxs(KeyboardAvoidingView, { style: [styles.container], behavior: undefined, children: [/*#__PURE__*/_jsx(StatusBar, { barStyle: theme === 'dark' ? 'light-content' : 'dark-content', backgroundColor: colors.background }), /*#__PURE__*/_jsxs(ScrollView, { contentContainerStyle: styles.scrollContent, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", bounces: false, alwaysBounceVertical: false, overScrollMode: "never", removeClippedSubviews: true, children: [showProgressIndicator && steps.length > 1 && /*#__PURE__*/_jsx(ProgressIndicator, { currentStep: state.currentStep, totalSteps: steps.length, colors: colors, styles: styles }), /*#__PURE__*/_jsx(AnimatedStepContainer, { fadeAnim: fadeAnim, slideAnim: slideAnim, scaleAnim: scaleAnim, styles: styles, stepKey: `step-${state.currentStep}`, children: CurrentStepComponent && /*#__PURE__*/_jsx(CurrentStepComponent, { ...stepProps }) })] })] }); }; export default StepBasedScreen; //# sourceMappingURL=StepBasedScreen.js.map