@rubixscript/react-native-onboarding 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/components/NavigationButtons.d.ts +23 -0
  2. package/dist/components/NavigationButtons.d.ts.map +1 -0
  3. package/dist/components/NavigationButtons.js +106 -0
  4. package/dist/components/Onboarding.d.ts +11 -0
  5. package/dist/components/Onboarding.d.ts.map +1 -0
  6. package/dist/components/Onboarding.js +219 -0
  7. package/dist/components/Pagination.d.ts +5 -0
  8. package/dist/components/Pagination.d.ts.map +1 -0
  9. package/dist/components/Pagination.js +269 -0
  10. package/dist/components/index.d.ts +5 -0
  11. package/dist/components/index.d.ts.map +1 -0
  12. package/dist/components/index.js +4 -0
  13. package/dist/index.d.ts +7 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +10 -0
  16. package/dist/presets/index.d.ts +27 -0
  17. package/dist/presets/index.d.ts.map +1 -0
  18. package/dist/presets/index.js +373 -0
  19. package/dist/slides/FormSlide.d.ts +12 -0
  20. package/dist/slides/FormSlide.d.ts.map +1 -0
  21. package/dist/slides/FormSlide.js +227 -0
  22. package/dist/slides/IconSlide.d.ts +10 -0
  23. package/dist/slides/IconSlide.d.ts.map +1 -0
  24. package/dist/slides/IconSlide.js +133 -0
  25. package/dist/slides/ImageSlide.d.ts +10 -0
  26. package/dist/slides/ImageSlide.d.ts.map +1 -0
  27. package/dist/slides/ImageSlide.js +99 -0
  28. package/dist/slides/VideoSlide.d.ts +10 -0
  29. package/dist/slides/VideoSlide.d.ts.map +1 -0
  30. package/dist/slides/VideoSlide.js +101 -0
  31. package/dist/slides/index.d.ts +14 -0
  32. package/dist/slides/index.d.ts.map +1 -0
  33. package/dist/slides/index.js +25 -0
  34. package/dist/themes/index.d.ts +35 -0
  35. package/dist/themes/index.d.ts.map +1 -0
  36. package/dist/themes/index.js +545 -0
  37. package/dist/types/index.d.ts +191 -0
  38. package/dist/types/index.d.ts.map +1 -0
  39. package/dist/types/index.js +1 -0
  40. package/package.json +10 -4
  41. package/src/components/Onboarding.tsx +8 -11
  42. package/src/components/Pagination.tsx +1 -1
  43. package/src/slides/FormSlide.tsx +1 -1
  44. package/src/slides/IconSlide.tsx +1 -1
  45. package/src/slides/ImageSlide.tsx +1 -1
  46. package/src/slides/VideoSlide.tsx +1 -1
  47. package/src/slides/{index.ts → index.tsx} +0 -7
  48. package/src/types/index.ts +3 -3
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { NavigationButtonProps } from '../types';
3
+ export declare const NavigationButton: React.FC<NavigationButtonProps>;
4
+ interface NavigationButtonsProps {
5
+ currentIndex: number;
6
+ totalSlides: number;
7
+ theme: any;
8
+ config: any;
9
+ onNext: () => void;
10
+ onBack: () => void;
11
+ onSkip: () => void;
12
+ canGoNext?: boolean;
13
+ isLastSlide?: boolean;
14
+ isLoading?: boolean;
15
+ darkMode?: boolean;
16
+ }
17
+ export declare const NavigationButtons: React.FC<NavigationButtonsProps>;
18
+ declare const _default: {
19
+ NavigationButton: React.FC<NavigationButtonProps>;
20
+ NavigationButtons: React.FC<NavigationButtonsProps>;
21
+ };
22
+ export default _default;
23
+ //# sourceMappingURL=NavigationButtons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigationButtons.d.ts","sourceRoot":"","sources":["../../src/components/NavigationButtons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAMvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAIjD,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+D5D,CAAC;AAEF,UAAU,sBAAsB;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,EAAE,GAAG,CAAC;IACZ,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAgF9D,CAAC;;;;;AA4BF,wBAAuD"}
@@ -0,0 +1,106 @@
1
+ import React, { useMemo } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
3
+ import { BlurView } from 'expo-blur';
4
+ import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
5
+ const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity);
6
+ export const NavigationButton = ({ label, onPress, theme, variant = 'primary', disabled = false, loading = false, style, textStyle, }) => {
7
+ const buttonStyle = useMemo(() => {
8
+ const base = {
9
+ ...styles.button,
10
+ minHeight: 48,
11
+ paddingHorizontal: 24,
12
+ borderRadius: theme.borderRadius.full,
13
+ alignItems: 'center',
14
+ justifyContent: 'center',
15
+ flexDirection: 'row',
16
+ };
17
+ switch (variant) {
18
+ case 'primary':
19
+ base.backgroundColor = theme.colors.primary;
20
+ break;
21
+ case 'secondary':
22
+ base.backgroundColor = theme.colors.secondary;
23
+ break;
24
+ case 'ghost':
25
+ base.backgroundColor = 'transparent';
26
+ base.borderWidth = 1.5;
27
+ base.borderColor = theme.colors.border;
28
+ break;
29
+ }
30
+ return base;
31
+ }, [theme, variant]);
32
+ const buttonText = useMemo(() => {
33
+ const base = {
34
+ ...styles.buttonText,
35
+ color: variant === 'ghost' ? theme.colors.text.primary : theme.colors.text.inverse,
36
+ };
37
+ return base;
38
+ }, [theme, variant]);
39
+ return (<AnimatedTouchableOpacity style={[buttonStyle, style]} onPress={onPress} disabled={disabled || loading} activeOpacity={0.8} entering={FadeIn} exiting={FadeOut}>
40
+ {loading ? (<ActivityIndicator color={variant === 'ghost' ? theme.colors.text.primary : theme.colors.text.inverse}/>) : (<Text style={[buttonText, textStyle]}>{label}</Text>)}
41
+ </AnimatedTouchableOpacity>);
42
+ };
43
+ export const NavigationButtons = ({ currentIndex, totalSlides, theme, config, onNext, onBack, onSkip, canGoNext = true, isLastSlide = false, isLoading = false, darkMode = false, }) => {
44
+ const { showSkip = true, showBack = true, skipLabel = 'Skip', backLabel = 'Back', nextLabel = 'Next', doneLabel = 'Get Started', position = 'bottom' } = config;
45
+ const containerStyle = useMemo(() => {
46
+ const base = {
47
+ paddingHorizontal: theme.spacing.lg,
48
+ paddingVertical: theme.spacing.md,
49
+ gap: theme.spacing.sm,
50
+ };
51
+ if (position === 'floating') {
52
+ base.position = 'absolute';
53
+ base.bottom = theme.spacing.xl;
54
+ base.left = theme.spacing.lg;
55
+ base.right = theme.spacing.lg;
56
+ }
57
+ return base;
58
+ }, [theme, position]);
59
+ const isAtStart = currentIndex === 0;
60
+ const buttons = (<>
61
+ {/* Back Button */}
62
+ {showBack && !isAtStart && (<NavigationButton label={backLabel} onPress={onBack} theme={theme} variant="ghost"/>)}
63
+
64
+ {/* Skip Button */}
65
+ {showSkip && !isLastSlide && !isLoading && (<TouchableOpacity style={styles.skipButton} onPress={onSkip} activeOpacity={0.7}>
66
+ <Text style={[styles.skipButtonText, { color: theme.colors.text.secondary }]}>
67
+ {skipLabel}
68
+ </Text>
69
+ </TouchableOpacity>)}
70
+
71
+ {/* Next/Done Button */}
72
+ <NavigationButton label={isLastSlide ? doneLabel : nextLabel} onPress={onNext} theme={theme} variant="primary" disabled={!canGoNext} loading={isLoading}/>
73
+ </>);
74
+ if (position === 'floating') {
75
+ return (<BlurView intensity={80} tint={darkMode ? 'dark' : 'light'} style={containerStyle}>
76
+ <View style={styles.buttonRow}>{buttons}</View>
77
+ </BlurView>);
78
+ }
79
+ return <View style={containerStyle}>{buttons}</View>;
80
+ };
81
+ const styles = StyleSheet.create({
82
+ button: {
83
+ minWidth: 120,
84
+ },
85
+ buttonText: {
86
+ fontSize: 16,
87
+ fontWeight: '600',
88
+ letterSpacing: 0.5,
89
+ },
90
+ buttonRow: {
91
+ flexDirection: 'row',
92
+ justifyContent: 'space-between',
93
+ alignItems: 'center',
94
+ },
95
+ skipButton: {
96
+ alignSelf: 'flex-end',
97
+ paddingVertical: 8,
98
+ paddingHorizontal: 4,
99
+ marginRight: 8,
100
+ },
101
+ skipButtonText: {
102
+ fontSize: 15,
103
+ fontWeight: '500',
104
+ },
105
+ });
106
+ export default { NavigationButton, NavigationButtons };
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { OnboardingProps } from '../types';
3
+ export declare const Onboarding: React.FC<OnboardingProps>;
4
+ export declare const useOnboarding: (storageKey?: string) => {
5
+ hasCompletedOnboarding: boolean | null;
6
+ isLoading: boolean;
7
+ markComplete: () => Promise<void>;
8
+ reset: () => Promise<void>;
9
+ };
10
+ export default Onboarding;
11
+ //# sourceMappingURL=Onboarding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Onboarding.d.ts","sourceRoot":"","sources":["../../src/components/Onboarding.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAC;AAgBzE,OAAO,EAAE,eAAe,EAAoB,MAAM,UAAU,CAAC;AAQ7D,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA4PhD,CAAC;AAGF,eAAO,MAAM,aAAa,GAAI,aAAY,MAA+B;;;;;CAuCxE,CAAC;AAkBF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,219 @@
1
+ import React, { useState, useCallback, useMemo, useEffect } from 'react';
2
+ import { View, StyleSheet, SafeAreaView, } from 'react-native';
3
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
4
+ import FlatList from 'react-native-reanimated';
5
+ import { LinearGradient } from 'expo-linear-gradient';
6
+ import AsyncStorage from '@react-native-async-storage/async-storage';
7
+ import { mergeTheme, getPreset } from '../themes';
8
+ import { SlideRenderer } from '../slides';
9
+ import { Pagination, NavigationButtons } from './index';
10
+ // Type assertion for AnimatedFlatList from react-native-reanimated
11
+ const AnimatedFlatListImplemented = FlatList;
12
+ export const Onboarding = ({ visible, slides = [], theme: customTheme, navigation: customNavigation, animation: customAnimation, storage, onboardingComplete, onSlideChange, onSkip, initialSlide = 0, swipeEnabled = true, containerStyle, safeAreaEnabled = true, darkMode = false, }) => {
13
+ // State
14
+ const [currentIndex, setCurrentIndex] = useState(initialSlide);
15
+ const [isSubmitting, setIsSubmitting] = useState(false);
16
+ const [formData, setFormData] = useState({});
17
+ //Refs
18
+ const flatListRef = React.useRef(null);
19
+ // Merge theme with preset
20
+ const preset = useMemo(() => {
21
+ if (!customTheme)
22
+ return getPreset('modern');
23
+ const presetKey = Object.keys(getPreset('modern')).find(key => customTheme[key]);
24
+ return presetKey ? getPreset(presetKey) : getPreset('modern');
25
+ }, [customTheme]);
26
+ const theme = useMemo(() => mergeTheme(preset.theme, customTheme), [preset, customTheme]);
27
+ const navigationConfig = useMemo(() => ({ ...preset.navigation, ...customNavigation }), [preset, customNavigation]);
28
+ const animationConfig = useMemo(() => ({ ...preset.animation, ...customAnimation }), [preset, customAnimation]);
29
+ // Check storage on mount
30
+ useEffect(() => {
31
+ if (storage?.enabled) {
32
+ checkOnboardingStatus();
33
+ }
34
+ }, [storage]);
35
+ const checkOnboardingStatus = async () => {
36
+ try {
37
+ const key = storage?.key || '@onboarding_complete';
38
+ const completed = await AsyncStorage.getItem(key);
39
+ if (completed && !visible) {
40
+ // Already completed, don't show
41
+ return;
42
+ }
43
+ }
44
+ catch (error) {
45
+ console.warn('Error checking onboarding status:', error);
46
+ }
47
+ };
48
+ const saveOnboardingComplete = async () => {
49
+ if (storage?.enabled) {
50
+ try {
51
+ const key = storage?.key || '@onboarding_complete';
52
+ await AsyncStorage.setItem(key, 'true');
53
+ if (storage.onComplete) {
54
+ await storage.onComplete();
55
+ }
56
+ }
57
+ catch (error) {
58
+ console.warn('Error saving onboarding status:', error);
59
+ }
60
+ }
61
+ };
62
+ // Handlers
63
+ const handleNext = useCallback(async () => {
64
+ const currentSlide = slides[currentIndex];
65
+ // Check if form slide and validate
66
+ if (currentSlide.type === 'form') {
67
+ const formSlide = currentSlide;
68
+ const requiredFields = formSlide.fields.filter((f) => f.required);
69
+ const isValid = requiredFields.every((field) => formData[field.key]);
70
+ if (!isValid) {
71
+ return; // Form validation failed
72
+ }
73
+ // Submit form data
74
+ if (formSlide.onSubmit) {
75
+ setIsSubmitting(true);
76
+ try {
77
+ await formSlide.onSubmit(formData);
78
+ }
79
+ catch (error) {
80
+ console.warn('Form submit error:', error);
81
+ }
82
+ setIsSubmitting(false);
83
+ }
84
+ }
85
+ if (currentIndex < slides.length - 1) {
86
+ flatListRef.current?.scrollToIndex({ index: currentIndex + 1, animated: true });
87
+ }
88
+ else {
89
+ // Complete onboarding
90
+ await saveOnboardingComplete();
91
+ if (onboardingComplete) {
92
+ await onboardingComplete(formData);
93
+ }
94
+ }
95
+ }, [currentIndex, slides, formData, onboardingComplete, storage]);
96
+ const handleBack = useCallback(() => {
97
+ if (currentIndex > 0) {
98
+ flatListRef.current?.scrollToIndex({ index: currentIndex - 1, animated: true });
99
+ }
100
+ }, [currentIndex]);
101
+ const handleSkip = useCallback(async () => {
102
+ await saveOnboardingComplete();
103
+ if (onSkip) {
104
+ await onSkip();
105
+ }
106
+ else if (onboardingComplete) {
107
+ await onboardingComplete();
108
+ }
109
+ }, [onSkip, onboardingComplete, storage]);
110
+ const handleMomentumScrollEnd = useCallback((event) => {
111
+ const index = Math.round(event.nativeEvent.contentOffset.x / event.nativeEvent.layoutMeasurement.width);
112
+ if (index !== currentIndex) {
113
+ setCurrentIndex(index);
114
+ if (onSlideChange) {
115
+ onSlideChange(index);
116
+ }
117
+ }
118
+ }, [currentIndex, onSlideChange]);
119
+ const handleFormDataChange = useCallback((data) => {
120
+ setFormData(prev => ({ ...prev, ...data }));
121
+ }, []);
122
+ // Renderers
123
+ const renderSlide = useCallback(({ item, index }) => {
124
+ return (<View style={[styles.slide, { width: '100%' }]}>
125
+ <SlideRenderer data={item} theme={theme} darkMode={darkMode} onSubmit={handleFormDataChange} isSubmitting={isSubmitting}/>
126
+ </View>);
127
+ }, [theme, darkMode, isSubmitting, handleFormDataChange]);
128
+ const getKey = useCallback((item, index) => item.id || `slide-${index}`, []);
129
+ // Computed values
130
+ const isLastSlide = currentIndex === slides.length - 1;
131
+ const currentSlide = slides[currentIndex];
132
+ // Determine if we should show navigation (not for form slides that handle their own)
133
+ const showNavigation = currentSlide?.type !== 'form';
134
+ if (!visible)
135
+ return null;
136
+ const content = (<View style={[styles.container, containerStyle]}>
137
+ {/* Background Gradient if applicable */}
138
+ {currentSlide?.gradientColors && currentSlide.gradientColors.length > 0 && (<LinearGradient colors={currentSlide.gradientColors} style={StyleSheet.absoluteFillObject}/>)}
139
+
140
+ {/* Pagination */}
141
+ {showNavigation && (<Pagination currentIndex={currentIndex} totalSlides={slides.length} theme={theme} config={navigationConfig}/>)}
142
+
143
+ {/* Slides */}
144
+ <AnimatedFlatListImplemented ref={flatListRef} data={slides} renderItem={renderSlide} keyExtractor={getKey} horizontal pagingEnabled showsHorizontalScrollIndicator={false} scrollEventThrottle={32} onMomentumScrollEnd={handleMomentumScrollEnd} scrollEnabled={swipeEnabled} bounces={false} initialScrollIndex={initialSlide} onScrollToIndexFailed={(info) => {
145
+ // Retry if scroll to index fails
146
+ setTimeout(() => {
147
+ flatListRef.current?.scrollToIndex({
148
+ index: info.index,
149
+ animated: true,
150
+ });
151
+ }, 100);
152
+ }}/>
153
+
154
+ {/* Navigation Buttons */}
155
+ {showNavigation && (<NavigationButtons currentIndex={currentIndex} totalSlides={slides.length} theme={theme} config={navigationConfig} onNext={handleNext} onBack={handleBack} onSkip={handleSkip} isLastSlide={isLastSlide} isLoading={isSubmitting} darkMode={darkMode}/>)}
156
+ </View>);
157
+ if (safeAreaEnabled) {
158
+ return (<SafeAreaView style={styles.safeArea}>
159
+ <SafeAreaProvider>{content}</SafeAreaProvider>
160
+ </SafeAreaView>);
161
+ }
162
+ return content;
163
+ };
164
+ // Hook for checking onboarding status
165
+ export const useOnboarding = (storageKey = '@onboarding_complete') => {
166
+ const [hasCompletedOnboarding, setHasCompletedOnboarding] = React.useState(null);
167
+ const [isLoading, setIsLoading] = React.useState(true);
168
+ React.useEffect(() => {
169
+ checkStatus();
170
+ }, []);
171
+ const checkStatus = async () => {
172
+ try {
173
+ const completed = await AsyncStorage.getItem(storageKey);
174
+ setHasCompletedOnboarding(completed === 'true');
175
+ }
176
+ catch (error) {
177
+ console.warn('Error checking onboarding:', error);
178
+ setHasCompletedOnboarding(false);
179
+ }
180
+ finally {
181
+ setIsLoading(false);
182
+ }
183
+ };
184
+ const markComplete = async () => {
185
+ try {
186
+ await AsyncStorage.setItem(storageKey, 'true');
187
+ setHasCompletedOnboarding(true);
188
+ }
189
+ catch (error) {
190
+ console.warn('Error marking onboarding complete:', error);
191
+ }
192
+ };
193
+ const reset = async () => {
194
+ try {
195
+ await AsyncStorage.removeItem(storageKey);
196
+ setHasCompletedOnboarding(false);
197
+ }
198
+ catch (error) {
199
+ console.warn('Error resetting onboarding:', error);
200
+ }
201
+ };
202
+ return { hasCompletedOnboarding, isLoading, markComplete, reset };
203
+ };
204
+ const styles = StyleSheet.create({
205
+ safeArea: {
206
+ flex: 1,
207
+ backgroundColor: '#FFFFFF',
208
+ },
209
+ container: {
210
+ flex: 1,
211
+ backgroundColor: '#FFFFFF',
212
+ },
213
+ slide: {
214
+ flex: 1,
215
+ justifyContent: 'center',
216
+ alignItems: 'center',
217
+ },
218
+ });
219
+ export default Onboarding;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import { PaginationProps } from '../types';
3
+ export declare const Pagination: React.FC<PaginationProps>;
4
+ export default Pagination;
5
+ //# sourceMappingURL=Pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/components/Pagination.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAIvC,OAAO,EAAE,eAAe,EAAmB,MAAM,UAAU,CAAC;AAmK5D,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2ChD,CAAC;AA8HF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,269 @@
1
+ import React, { useMemo } from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import Animated, { useAnimatedStyle, withSpring, useDerivedValue } from 'react-native-reanimated';
4
+ import { LinearGradient } from 'expo-linear-gradient';
5
+ // DOTS PAGINATION
6
+ const DotsPagination = ({ currentIndex, totalSlides, theme, style }) => {
7
+ const animatedIndex = useDerivedValue(() => withSpring(currentIndex, { damping: 15, stiffness: 150 }));
8
+ return (<View style={[styles.dotsContainer, style]}>
9
+ {Array.from({ length: totalSlides }).map((_, index) => {
10
+ const animatedStyle = useAnimatedStyle(() => ({
11
+ width: index === animatedIndex.value ? 24 : 8,
12
+ opacity: withSpring(index === animatedIndex.value ? 1 : 0.4, { damping: 15, stiffness: 150 }),
13
+ }));
14
+ return (<Animated.View key={index} style={[
15
+ styles.dot,
16
+ {
17
+ backgroundColor: theme.colors.primary,
18
+ },
19
+ animatedStyle,
20
+ ]}/>);
21
+ })}
22
+ </View>);
23
+ };
24
+ // PROGRESS BAR PAGINATION
25
+ const ProgressBarPagination = ({ currentIndex, totalSlides, theme, style }) => {
26
+ const progress = useMemo(() => (currentIndex + 1) / totalSlides, [currentIndex, totalSlides]);
27
+ return (<View style={[styles.progressContainer, style]}>
28
+ <View style={[styles.progressBackground, { backgroundColor: theme.colors.border }]}>
29
+ <Animated.View style={[
30
+ styles.progressFill,
31
+ {
32
+ width: `${progress * 100}%`,
33
+ backgroundColor: theme.colors.primary,
34
+ },
35
+ ]}/>
36
+ </View>
37
+ <Animated.Text style={[styles.progressText, { color: theme.colors.text.secondary }]}>
38
+ {currentIndex + 1} / {totalSlides}
39
+ </Animated.Text>
40
+ </View>);
41
+ };
42
+ // STEPS PAGINATION
43
+ const StepsPagination = ({ currentIndex, totalSlides, theme, style }) => {
44
+ return (<View style={[styles.stepsContainer, style]}>
45
+ {Array.from({ length: totalSlides }).map((_, index) => {
46
+ const isCompleted = index < currentIndex;
47
+ const isCurrent = index === currentIndex;
48
+ return (<View key={index} style={styles.stepItem}>
49
+ <View style={[
50
+ styles.stepCircle,
51
+ {
52
+ backgroundColor: isCompleted ? theme.colors.primary : isCurrent ? theme.colors.primary : theme.colors.border,
53
+ borderColor: theme.colors.border,
54
+ },
55
+ ]}>
56
+ {isCompleted ? (<View style={styles.stepCheckmark}/>) : (<Animated.Text style={[
57
+ styles.stepNumber,
58
+ { color: isCurrent ? theme.colors.text.inverse : theme.colors.text.secondary },
59
+ ]}>
60
+ {index + 1}
61
+ </Animated.Text>)}
62
+ </View>
63
+ {index < totalSlides - 1 && (<View style={[
64
+ styles.stepLine,
65
+ { backgroundColor: isCompleted ? theme.colors.primary : theme.colors.border },
66
+ ]}/>)}
67
+ </View>);
68
+ })}
69
+ </View>);
70
+ };
71
+ // NUMBERS PAGINATION
72
+ const NumbersPagination = ({ currentIndex, totalSlides, theme, style }) => {
73
+ return (<View style={[styles.numbersContainer, style]}>
74
+ {Array.from({ length: totalSlides }).map((_, index) => {
75
+ const isCurrent = index === currentIndex;
76
+ return (<Animated.View key={index} style={[
77
+ styles.numberCircle,
78
+ {
79
+ backgroundColor: isCurrent ? theme.colors.primary : 'transparent',
80
+ borderColor: theme.colors.border,
81
+ },
82
+ ]}>
83
+ <Animated.Text style={[
84
+ styles.numberText,
85
+ { color: isCurrent ? theme.colors.text.inverse : theme.colors.text.secondary },
86
+ ]}>
87
+ {index + 1}
88
+ </Animated.Text>
89
+ </Animated.View>);
90
+ })}
91
+ </View>);
92
+ };
93
+ // FLOATING DOTS PAGINATION
94
+ const FloatingDotsPagination = ({ currentIndex, totalSlides, theme, style }) => {
95
+ return (<View style={[styles.floatingContainer, style]}>
96
+ <LinearGradient colors={[theme.colors.surface + 'CC', theme.colors.surface + 'CC']} style={styles.floatingBackground}>
97
+ <View style={styles.floatingDots}>
98
+ {Array.from({ length: totalSlides }).map((_, index) => (<Animated.View key={index} style={[
99
+ styles.floatingDot,
100
+ {
101
+ width: index === currentIndex ? 28 : 10,
102
+ backgroundColor: index === currentIndex ? theme.colors.primary : theme.colors.border,
103
+ },
104
+ ]}/>))}
105
+ </View>
106
+ </LinearGradient>
107
+ </View>);
108
+ };
109
+ // MAIN PAGINATION COMPONENT
110
+ export const Pagination = ({ currentIndex, totalSlides, theme, config, style }) => {
111
+ const { style: navStyle, position } = config || { style: 'dots', position: 'bottom' };
112
+ const containerStyle = useMemo(() => {
113
+ const base = {};
114
+ if (position === 'top') {
115
+ base.position = 'absolute';
116
+ base.top = 0;
117
+ base.left = 0;
118
+ base.right = 0;
119
+ base.paddingTop = 20;
120
+ }
121
+ else if (position === 'bottom') {
122
+ base.position = 'absolute';
123
+ base.bottom = 0;
124
+ base.left = 0;
125
+ base.right = 0;
126
+ base.paddingBottom = 20;
127
+ }
128
+ return base;
129
+ }, [position]);
130
+ const renderPagination = () => {
131
+ switch (navStyle) {
132
+ case 'dots':
133
+ return <DotsPagination currentIndex={currentIndex} totalSlides={totalSlides} theme={theme} style={style}/>;
134
+ case 'progress-bar':
135
+ return <ProgressBarPagination currentIndex={currentIndex} totalSlides={totalSlides} theme={theme} style={style}/>;
136
+ case 'steps':
137
+ return <StepsPagination currentIndex={currentIndex} totalSlides={totalSlides} theme={theme} style={style}/>;
138
+ case 'numbers':
139
+ return <NumbersPagination currentIndex={currentIndex} totalSlides={totalSlides} theme={theme} style={style}/>;
140
+ case 'none':
141
+ return null;
142
+ default:
143
+ return <DotsPagination currentIndex={currentIndex} totalSlides={totalSlides} theme={theme} style={style}/>;
144
+ }
145
+ };
146
+ if (navStyle === 'none')
147
+ return null;
148
+ return <View style={containerStyle}>{renderPagination()}</View>;
149
+ };
150
+ const styles = StyleSheet.create({
151
+ // Dots
152
+ dotsContainer: {
153
+ flexDirection: 'row',
154
+ alignItems: 'center',
155
+ justifyContent: 'center',
156
+ gap: 8,
157
+ paddingVertical: 16,
158
+ },
159
+ dot: {
160
+ height: 8,
161
+ borderRadius: 4,
162
+ },
163
+ // Progress Bar
164
+ progressContainer: {
165
+ paddingHorizontal: 24,
166
+ paddingVertical: 16,
167
+ alignItems: 'center',
168
+ },
169
+ progressBackground: {
170
+ width: '100%',
171
+ height: 4,
172
+ borderRadius: 2,
173
+ overflow: 'hidden',
174
+ marginBottom: 8,
175
+ },
176
+ progressFill: {
177
+ height: '100%',
178
+ borderRadius: 2,
179
+ },
180
+ progressText: {
181
+ fontSize: 13,
182
+ fontWeight: '500',
183
+ },
184
+ // Steps
185
+ stepsContainer: {
186
+ flexDirection: 'row',
187
+ alignItems: 'center',
188
+ paddingHorizontal: 24,
189
+ paddingVertical: 16,
190
+ },
191
+ stepItem: {
192
+ flexDirection: 'row',
193
+ alignItems: 'center',
194
+ flex: 1,
195
+ },
196
+ stepCircle: {
197
+ width: 32,
198
+ height: 32,
199
+ borderRadius: 16,
200
+ borderWidth: 2,
201
+ alignItems: 'center',
202
+ justifyContent: 'center',
203
+ },
204
+ stepNumber: {
205
+ fontSize: 14,
206
+ fontWeight: '600',
207
+ },
208
+ stepCheckmark: {
209
+ width: 12,
210
+ height: 6,
211
+ borderBottomWidth: 2,
212
+ borderLeftWidth: 2,
213
+ borderColor: '#FFFFFF',
214
+ transform: [{ rotate: '-45deg' }, { translateY: -2 }],
215
+ },
216
+ stepLine: {
217
+ flex: 1,
218
+ height: 2,
219
+ minWidth: 8,
220
+ },
221
+ // Numbers
222
+ numbersContainer: {
223
+ flexDirection: 'row',
224
+ alignItems: 'center',
225
+ justifyContent: 'center',
226
+ gap: 16,
227
+ paddingVertical: 16,
228
+ },
229
+ numberCircle: {
230
+ width: 36,
231
+ height: 36,
232
+ borderRadius: 18,
233
+ borderWidth: 2,
234
+ alignItems: 'center',
235
+ justifyContent: 'center',
236
+ },
237
+ numberText: {
238
+ fontSize: 14,
239
+ fontWeight: '600',
240
+ },
241
+ // Floating
242
+ floatingContainer: {
243
+ position: 'absolute',
244
+ bottom: 32,
245
+ left: 0,
246
+ right: 0,
247
+ alignItems: 'center',
248
+ },
249
+ floatingBackground: {
250
+ paddingHorizontal: 20,
251
+ paddingVertical: 12,
252
+ borderRadius: 24,
253
+ shadowColor: '#000',
254
+ shadowOffset: { width: 0, height: 4 },
255
+ shadowOpacity: 0.15,
256
+ shadowRadius: 12,
257
+ elevation: 8,
258
+ },
259
+ floatingDots: {
260
+ flexDirection: 'row',
261
+ alignItems: 'center',
262
+ gap: 8,
263
+ },
264
+ floatingDot: {
265
+ height: 10,
266
+ borderRadius: 5,
267
+ },
268
+ });
269
+ export default Pagination;
@@ -0,0 +1,5 @@
1
+ export { Pagination } from './Pagination';
2
+ export { NavigationButton, NavigationButtons } from './NavigationButtons';
3
+ export { Onboarding, useOnboarding } from './Onboarding';
4
+ export { default } from './Onboarding';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Pagination } from './Pagination';
2
+ export { NavigationButton, NavigationButtons } from './NavigationButtons';
3
+ export { Onboarding, useOnboarding } from './Onboarding';
4
+ export { default } from './Onboarding';
@@ -0,0 +1,7 @@
1
+ export { Onboarding, useOnboarding } from './components/Onboarding';
2
+ export { ImageSlide, IconSlide, FormSlide, VideoSlide, SlideRenderer, } from './slides';
3
+ export { Pagination, NavigationButton, NavigationButtons, } from './components';
4
+ export { onepageTheme, zaprecipeTheme, pomodoTheme, modernTheme, minimalTheme, gradientTheme, onepagePreset, zaprecipePreset, pomodoPreset, modernPreset, minimalPreset, gradientPreset, getTheme, getPreset, mergeTheme, } from './themes';
5
+ export type { SlideType, BaseSlideData, ImageSlideData, IconSlideData, FormSlideData, VideoSlideData, CustomSlideData, SlideData, OnboardingTheme, NavigationStyle, NavigationConfig, AnimationType, AnimationConfig, StorageConfig, OnboardingConfig, OnboardingProps, FormSlideCustomProps, PaginationProps, NavigationButtonProps, OnboardingPreset, PresetConfig, } from './types';
6
+ export { default } from './components/Onboarding';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,aAAa,GACd,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,aAAa,EACb,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,QAAQ,EACR,SAAS,EACT,UAAU,GACX,MAAM,UAAU,CAAC;AAGlB,YAAY,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,SAAS,EACT,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,GACb,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC"}