@rubixscript/react-native-onboarding 1.0.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RubixScript Team
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.
package/README.md ADDED
@@ -0,0 +1,383 @@
1
+ # @rubixscript/react-native-onboarding
2
+
3
+ A comprehensive React Native onboarding library with customizable slide types, animations, and themes. Built for React Native 0.70+ with Expo.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Slide Types**: Image, Icon, Form, Video, and Custom slides
8
+ - **Pre-built Themes**: onepage, zaprecipe, pomodo, modern, minimal, gradient presets
9
+ - **Customizable Navigation**: Dots, progress bar, steps, numbers, or no pagination
10
+ - **Smooth Animations**: Slide, fade, scale, parallax transitions with react-native-reanimated
11
+ - **Storage Integration**: AsyncStorage persistence for onboarding completion
12
+ - **Form Support**: Built-in form validation and data collection
13
+ - **Dark Mode**: Full dark mode support
14
+ - **TypeScript**: Fully typed for better developer experience
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @rubixscript/react-native-onboarding
20
+ # or
21
+ yarn add @rubixscript/react-native-onboarding
22
+ ```
23
+
24
+ ### Peer Dependencies
25
+
26
+ Ensure you have these installed:
27
+
28
+ ```bash
29
+ npm install react-native-reanimated @react-native-async-storage/async-storage expo-linear-gradient expo-blur @expo/vector-icons
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### Basic Usage
35
+
36
+ ```tsx
37
+ import React, { useState } from 'react';
38
+ import { View } from 'react-native';
39
+ import { Onboarding, useOnboarding } from '@rubixscript/react-native-onboarding';
40
+
41
+ const App = () => {
42
+ const { hasCompletedOnboarding, isLoading } = useOnboarding('@myapp:onboarding_complete');
43
+ const [showOnboarding, setShowOnboarding] = useState(!hasCompletedOnboarding);
44
+
45
+ if (isLoading) return null;
46
+
47
+ return (
48
+ <>
49
+ {showOnboarding ? (
50
+ <Onboarding
51
+ visible={showOnboarding}
52
+ slides={mySlides}
53
+ onboardingComplete={(data) => {
54
+ setShowOnboarding(false);
55
+ console.log('Onboarding data:', data);
56
+ }}
57
+ />
58
+ ) : (
59
+ <YourMainApp />
60
+ )}
61
+ </>
62
+ );
63
+ };
64
+ ```
65
+
66
+ ### Using Presets
67
+
68
+ ```tsx
69
+ import { Onboarding } from '@rubixscript/react-native-onboarding';
70
+ import { onepageConfig, onepagePreset } from '@rubixscript/react-native-onboarding/presets';
71
+
72
+ // Use a complete preset
73
+ <Onboarding
74
+ visible={true}
75
+ {...onepageConfig}
76
+ theme={onepagePreset.theme}
77
+ navigation={onepagePreset.navigation}
78
+ animation={onepagePreset.animation}
79
+ onboardingComplete={() => console.log('Done!')}
80
+ />
81
+
82
+ // Or just use the theme with custom slides
83
+ <Onboarding
84
+ visible={true}
85
+ slides={myCustomSlides}
86
+ theme={onepagePreset.theme}
87
+ onboardingComplete={() => console.log('Done!')}
88
+ />
89
+ ```
90
+
91
+ ## Slide Types
92
+
93
+ ### Image Slide
94
+
95
+ ```tsx
96
+ {
97
+ id: 'welcome',
98
+ type: 'image',
99
+ title: 'Welcome',
100
+ description: 'Your description here',
101
+ image: require('./assets/welcome.png'),
102
+ imageResizeMode: 'cover',
103
+ gradientColors: ['#667EEA', '#764BA2'],
104
+ overlayIcon: {
105
+ name: 'heart',
106
+ size: 40,
107
+ color: '#FFFFFF'
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### Icon Slide
113
+
114
+ ```tsx
115
+ {
116
+ id: 'features',
117
+ type: 'icon',
118
+ title: 'Powerful Features',
119
+ description: 'Discover what we offer',
120
+ icon: {
121
+ name: 'rocket',
122
+ type: 'ionicons', // or 'material', 'material-community', 'font-awesome', 'octicons', 'feather'
123
+ size: 64,
124
+ color: '#FFFFFF',
125
+ backgroundColor: '#8B5CF6',
126
+ backgroundSize: 160
127
+ },
128
+ backgroundColor: '#F6F6F6'
129
+ }
130
+ ```
131
+
132
+ ### Form Slide
133
+
134
+ ```tsx
135
+ {
136
+ id: 'profile',
137
+ type: 'form',
138
+ title: 'Tell us about yourself',
139
+ description: 'We\'ll personalize your experience',
140
+ fields: [
141
+ {
142
+ key: 'name',
143
+ label: 'Your Name',
144
+ placeholder: 'Enter your name',
145
+ type: 'text',
146
+ required: true
147
+ },
148
+ {
149
+ key: 'level',
150
+ label: 'Experience Level',
151
+ placeholder: 'Select level',
152
+ type: 'select',
153
+ required: true,
154
+ options: [
155
+ { value: 'beginner', label: 'Beginner', icon: 'leaf' },
156
+ { value: 'advanced', label: 'Advanced', icon: 'star' }
157
+ ]
158
+ }
159
+ ],
160
+ submitLabel: 'Get Started',
161
+ onSubmit: async (data) => {
162
+ console.log('Form data:', data);
163
+ // Save to backend, etc.
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### Video Slide
169
+
170
+ ```tsx
171
+ {
172
+ id: 'intro',
173
+ type: 'video',
174
+ title: 'Watch Our Story',
175
+ description: 'A quick introduction',
176
+ videoSource: require('./assets/intro.mp4'),
177
+ autoPlay: true,
178
+ loop: true,
179
+ muted: true,
180
+ poster: require('./assets/poster.png')
181
+ }
182
+ ```
183
+
184
+ ## Customization
185
+
186
+ ### Theme
187
+
188
+ ```tsx
189
+ import { mergeTheme, modernTheme } from '@rubixscript/react-native-onboarding';
190
+
191
+ const customTheme = mergeTheme(modernTheme, {
192
+ colors: {
193
+ primary: '#FF6B6B',
194
+ secondary: '#4ECDC4',
195
+ },
196
+ typography: {
197
+ title: {
198
+ fontSize: 32,
199
+ fontWeight: '800',
200
+ }
201
+ }
202
+ });
203
+
204
+ <Onboarding theme={customTheme} {...otherProps} />
205
+ ```
206
+
207
+ ### Navigation
208
+
209
+ ```tsx
210
+ <Onboarding
211
+ navigation={{
212
+ style: 'dots', // 'dots' | 'progress-bar' | 'steps' | 'numbers' | 'none'
213
+ position: 'bottom', // 'top' | 'bottom' | 'floating'
214
+ showSkip: true,
215
+ showBack: true,
216
+ skipLabel: 'Skip',
217
+ backLabel: 'Back',
218
+ nextLabel: 'Continue',
219
+ doneLabel: 'Get Started',
220
+ }}
221
+ {...otherProps}
222
+ />
223
+ ```
224
+
225
+ ### Animation
226
+
227
+ ```tsx
228
+ <Onboarding
229
+ animation={{
230
+ type: 'slide', // 'slide' | 'fade' | 'scale' | 'parallax' | 'cube' | 'none'
231
+ duration: 300,
232
+ parallaxFactor: 0.3
233
+ }}
234
+ {...otherProps}
235
+ />
236
+ ```
237
+
238
+ ### Storage
239
+
240
+ ```tsx
241
+ <Onboarding
242
+ storage={{
243
+ key: '@myapp:onboarding_complete',
244
+ enabled: true,
245
+ onComplete: async () => {
246
+ // Called after saving completion status
247
+ console.log('Onboarding saved!');
248
+ }
249
+ }}
250
+ {...otherProps}
251
+ />
252
+ ```
253
+
254
+ ## Hooks
255
+
256
+ ### useOnboarding
257
+
258
+ ```tsx
259
+ import { useOnboarding } from '@rubixscript/react-native-onboarding';
260
+
261
+ const MyComponent = () => {
262
+ const {
263
+ hasCompletedOnboarding, // boolean | null
264
+ isLoading, // boolean
265
+ markComplete, // () => Promise<void>
266
+ reset // () => Promise<void>
267
+ } = useOnboarding('@myapp:onboarding_key');
268
+
269
+ if (isLoading) return <LoadingScreen />;
270
+
271
+ return hasCompletedOnboarding ? <App /> : <Onboarding ... />;
272
+ };
273
+ ```
274
+
275
+ ## Available Presets
276
+
277
+ | Preset | Style | Best For |
278
+ |--------|-------|----------|
279
+ | `onepage` | Gradient backgrounds, images, forms | Reading apps, content apps |
280
+ | `zaprecipe` | Rich images, overlay icons, gradients | Lifestyle, food, travel apps |
281
+ | `pomodo` | Clean icons, minimal design | Productivity, utility apps |
282
+ | `modern` | Contemporary, rounded, vibrant | Modern consumer apps |
283
+ | `minimal` | Simple, monochrome, clean | Professional, business apps |
284
+ | `gradient` | Full-screen gradients, immersive | Creative, artistic apps |
285
+
286
+ ## Integration Examples
287
+
288
+ ### Expo Router
289
+
290
+ ```tsx
291
+ // app/onboarding.tsx
292
+ import { View } from 'react-native';
293
+ import { Onboarding } from '@rubixscript/react-native-onboarding';
294
+ import { router } from 'expo-router';
295
+ import { onepageConfig } from '@rubixscript/react-native-onboarding/presets';
296
+
297
+ export default function OnboardingScreen() {
298
+ return (
299
+ <Onboarding
300
+ visible={true}
301
+ {...onepageConfig}
302
+ onboardingComplete={() => router.replace('/(tabs)')}
303
+ />
304
+ );
305
+ }
306
+
307
+ // app/_layout.tsx
308
+ import { useOnboarding } from '@rubixscript/react-native-onboarding';
309
+ import { router } from 'expo-router';
310
+
311
+ export default function Layout() {
312
+ const { hasCompletedOnboarding, isLoading } = useOnboarding();
313
+
314
+ useEffect(() => {
315
+ if (!isLoading && !hasCompletedOnboarding) {
316
+ router.replace('/onboarding');
317
+ }
318
+ }, [hasCompletedOnboarding, isLoading]);
319
+
320
+ return <Slot />;
321
+ }
322
+ ```
323
+
324
+ ### React Navigation
325
+
326
+ ```tsx
327
+ import { Onboarding, useOnboarding } from '@rubixscript/react-native-onboarding';
328
+
329
+ const OnboardingScreen = ({ navigation }: any) => (
330
+ <Onboarding
331
+ visible={true}
332
+ slides={slides}
333
+ onboardingComplete={() => navigation.navigate('Home')}
334
+ />
335
+ );
336
+
337
+ const RootNavigator = () => {
338
+ const { hasCompletedOnboarding, isLoading } = useOnboarding();
339
+
340
+ if (isLoading) return null;
341
+
342
+ return (
343
+ <Stack.Navigator screenOptions={{ headerShown: false }}>
344
+ {!hasCompletedOnboarding && (
345
+ <Stack.Screen name="Onboarding" component={OnboardingScreen} />
346
+ )}
347
+ <Stack.Screen name="Home" component={HomeScreen} />
348
+ </Stack.Navigator>
349
+ );
350
+ };
351
+ ```
352
+
353
+ ## API Reference
354
+
355
+ ### Onboarding Component
356
+
357
+ | Prop | Type | Default | Description |
358
+ |------|------|---------|-------------|
359
+ | `visible` | `boolean` | *required* | Controls visibility of onboarding |
360
+ | `slides` | `SlideData[]` | `[]` | Array of slide configurations |
361
+ | `theme` | `Partial<OnboardingTheme>` | `modernTheme` | Custom theme configuration |
362
+ | `navigation` | `Partial<NavigationConfig>` | See below | Navigation options |
363
+ | `animation` | `Partial<AnimationConfig>` | See below | Animation settings |
364
+ | `storage` | `Partial<StorageConfig>` | See below | AsyncStorage configuration |
365
+ | `onboardingComplete` | `(data?) => void \| Promise<void>` | - | Called when onboarding completes |
366
+ | `onSlideChange` | `(index: number) => void` | - | Called on slide change |
367
+ | `onSkip` | `() => void \| Promise<void>` | - | Called when user skips |
368
+ | `initialSlide` | `number` | `0` | Starting slide index |
369
+ | `swipeEnabled` | `boolean` | `true` | Enable swipe navigation |
370
+ | `safeAreaEnabled` | `boolean` | `true` | Use SafeAreaView |
371
+ | `darkMode` | `boolean` | `false` | Enable dark mode styling |
372
+
373
+ ## License
374
+
375
+ MIT
376
+
377
+ ## Author
378
+
379
+ RubixScript Team
380
+
381
+ ---
382
+
383
+ For more examples and updates, visit [GitHub](https://github.com/rubixscript/react-native-onboarding).
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@rubixscript/react-native-onboarding",
3
+ "version": "1.0.0",
4
+ "description": "A comprehensive React Native onboarding library with customizable slide types, animations, and themes",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "scripts": {
8
+ "test": "jest",
9
+ "lint": "eslint src --ext .ts,.tsx",
10
+ "type-check": "tsc --noEmit"
11
+ },
12
+ "keywords": [
13
+ "react-native",
14
+ "onboarding",
15
+ "walkthrough",
16
+ "tutorial",
17
+ "user-onboarding",
18
+ "expo",
19
+ "react",
20
+ "slides",
21
+ "carousel"
22
+ ],
23
+ "author": "RubixScript Team",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/rubixscript/react-native-onboarding.git"
28
+ },
29
+ "peerDependencies": {
30
+ "expo": ">=48.0.0",
31
+ "react": ">=18.0.0",
32
+ "react-native": ">=0.70.0",
33
+ "react-native-reanimated": ">=3.0.0"
34
+ },
35
+ "dependencies": {
36
+ "@expo/vector-icons": "^15.0.2",
37
+ "@react-native-async-storage/async-storage": ">=1.19.0",
38
+ "expo-blur": "~15.0.7",
39
+ "expo-linear-gradient": "~15.0.7"
40
+ },
41
+ "devDependencies": {
42
+ "@testing-library/react-native": "^12.0.0",
43
+ "@types/jest": "^29.5.0",
44
+ "@types/react": "~19.1.10",
45
+ "@types/react-native": "^0.73.0",
46
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
47
+ "@typescript-eslint/parser": "^6.0.0",
48
+ "eslint": "^8.0.0",
49
+ "jest": "^29.0.0",
50
+ "typescript": "~5.9.2"
51
+ },
52
+ "files": [
53
+ "src",
54
+ "README.md",
55
+ "LICENSE"
56
+ ],
57
+ "engines": {
58
+ "node": ">=16.0.0"
59
+ }
60
+ }
@@ -0,0 +1,198 @@
1
+ import React, { useMemo } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator, ViewStyle, TextStyle, Platform } from 'react-native';
3
+ import { BlurView } from 'expo-blur';
4
+ import { LinearGradient } from 'expo-linear-gradient';
5
+ import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
6
+ import { Ionicons } from '@expo/vector-icons';
7
+ import { NavigationButtonProps } from '../types';
8
+
9
+ const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity);
10
+
11
+ export const NavigationButton: React.FC<NavigationButtonProps> = ({
12
+ label,
13
+ onPress,
14
+ theme,
15
+ variant = 'primary',
16
+ disabled = false,
17
+ loading = false,
18
+ style,
19
+ textStyle,
20
+ }) => {
21
+ const buttonStyle = useMemo(() => {
22
+ const base: ViewStyle = {
23
+ ...styles.button,
24
+ minHeight: 48,
25
+ paddingHorizontal: 24,
26
+ borderRadius: theme.borderRadius.full,
27
+ alignItems: 'center',
28
+ justifyContent: 'center',
29
+ flexDirection: 'row',
30
+ };
31
+
32
+ switch (variant) {
33
+ case 'primary':
34
+ base.backgroundColor = theme.colors.primary;
35
+ break;
36
+ case 'secondary':
37
+ base.backgroundColor = theme.colors.secondary;
38
+ break;
39
+ case 'ghost':
40
+ base.backgroundColor = 'transparent';
41
+ base.borderWidth = 1.5;
42
+ base.borderColor = theme.colors.border;
43
+ break;
44
+ }
45
+
46
+ return base;
47
+ }, [theme, variant]);
48
+
49
+ const buttonText = useMemo(() => {
50
+ const base: TextStyle = {
51
+ ...styles.buttonText,
52
+ color: variant === 'ghost' ? theme.colors.text.primary : theme.colors.text.inverse,
53
+ };
54
+
55
+ return base;
56
+ }, [theme, variant]);
57
+
58
+ return (
59
+ <AnimatedTouchableOpacity
60
+ style={[buttonStyle, style]}
61
+ onPress={onPress}
62
+ disabled={disabled || loading}
63
+ activeOpacity={0.8}
64
+ entering={FadeIn}
65
+ exiting={FadeOut}
66
+ >
67
+ {loading ? (
68
+ <ActivityIndicator color={variant === 'ghost' ? theme.colors.text.primary : theme.colors.text.inverse} />
69
+ ) : (
70
+ <Text style={[buttonText, textStyle]}>{label}</Text>
71
+ )}
72
+ </AnimatedTouchableOpacity>
73
+ );
74
+ };
75
+
76
+ interface NavigationButtonsProps {
77
+ currentIndex: number;
78
+ totalSlides: number;
79
+ theme: any;
80
+ config: any;
81
+ onNext: () => void;
82
+ onBack: () => void;
83
+ onSkip: () => void;
84
+ canGoNext?: boolean;
85
+ isLastSlide?: boolean;
86
+ isLoading?: boolean;
87
+ darkMode?: boolean;
88
+ }
89
+
90
+ export const NavigationButtons: React.FC<NavigationButtonsProps> = ({
91
+ currentIndex,
92
+ totalSlides,
93
+ theme,
94
+ config,
95
+ onNext,
96
+ onBack,
97
+ onSkip,
98
+ canGoNext = true,
99
+ isLastSlide = false,
100
+ isLoading = false,
101
+ darkMode = false,
102
+ }) => {
103
+ const { showSkip = true, showBack = true, skipLabel = 'Skip', backLabel = 'Back', nextLabel = 'Next', doneLabel = 'Get Started', position = 'bottom' } = config;
104
+
105
+ const containerStyle: ViewStyle = useMemo(() => {
106
+ const base: ViewStyle = {
107
+ paddingHorizontal: theme.spacing.lg,
108
+ paddingVertical: theme.spacing.md,
109
+ gap: theme.spacing.sm,
110
+ };
111
+
112
+ if (position === 'floating') {
113
+ base.position = 'absolute';
114
+ base.bottom = theme.spacing.xl;
115
+ base.left = theme.spacing.lg;
116
+ base.right = theme.spacing.lg;
117
+ }
118
+
119
+ return base;
120
+ }, [theme, position]);
121
+
122
+ const isAtStart = currentIndex === 0;
123
+
124
+ const buttons = (
125
+ <>
126
+ {/* Back Button */}
127
+ {showBack && !isAtStart && (
128
+ <NavigationButton
129
+ label={backLabel}
130
+ onPress={onBack}
131
+ theme={theme}
132
+ variant="ghost"
133
+ />
134
+ )}
135
+
136
+ {/* Skip Button */}
137
+ {showSkip && !isLastSlide && !isLoading && (
138
+ <TouchableOpacity
139
+ style={styles.skipButton}
140
+ onPress={onSkip}
141
+ activeOpacity={0.7}
142
+ >
143
+ <Text style={[styles.skipButtonText, { color: theme.colors.text.secondary }]}>
144
+ {skipLabel}
145
+ </Text>
146
+ </TouchableOpacity>
147
+ )}
148
+
149
+ {/* Next/Done Button */}
150
+ <NavigationButton
151
+ label={isLastSlide ? doneLabel : nextLabel}
152
+ onPress={onNext}
153
+ theme={theme}
154
+ variant="primary"
155
+ disabled={!canGoNext}
156
+ loading={isLoading}
157
+ />
158
+ </>
159
+ );
160
+
161
+ if (position === 'floating') {
162
+ return (
163
+ <BlurView intensity={80} tint={darkMode ? 'dark' : 'light'} style={containerStyle}>
164
+ <View style={styles.buttonRow}>{buttons}</View>
165
+ </BlurView>
166
+ );
167
+ }
168
+
169
+ return <View style={containerStyle}>{buttons}</View>;
170
+ };
171
+
172
+ const styles = StyleSheet.create({
173
+ button: {
174
+ minWidth: 120,
175
+ },
176
+ buttonText: {
177
+ fontSize: 16,
178
+ fontWeight: '600',
179
+ letterSpacing: 0.5,
180
+ },
181
+ buttonRow: {
182
+ flexDirection: 'row',
183
+ justifyContent: 'space-between',
184
+ alignItems: 'center',
185
+ },
186
+ skipButton: {
187
+ alignSelf: 'flex-end',
188
+ paddingVertical: 8,
189
+ paddingHorizontal: 4,
190
+ marginRight: 8,
191
+ },
192
+ skipButtonText: {
193
+ fontSize: 15,
194
+ fontWeight: '500',
195
+ },
196
+ });
197
+
198
+ export default { NavigationButton, NavigationButtons };