@rubixscript/react-native-onboarding 1.0.0 → 1.1.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.
Files changed (59) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +383 -383
  3. package/dist/components/NavigationButtons.d.ts +23 -0
  4. package/dist/components/NavigationButtons.d.ts.map +1 -0
  5. package/dist/components/NavigationButtons.js +106 -0
  6. package/dist/components/Onboarding.d.ts +11 -0
  7. package/dist/components/Onboarding.d.ts.map +1 -0
  8. package/dist/components/Onboarding.js +219 -0
  9. package/dist/components/Pagination.d.ts +5 -0
  10. package/dist/components/Pagination.d.ts.map +1 -0
  11. package/dist/components/Pagination.js +269 -0
  12. package/dist/components/SimpleOnboardingScreen.d.ts +54 -0
  13. package/dist/components/SimpleOnboardingScreen.d.ts.map +1 -0
  14. package/dist/components/SimpleOnboardingScreen.js +184 -0
  15. package/dist/components/index.d.ts +7 -0
  16. package/dist/components/index.d.ts.map +1 -0
  17. package/dist/components/index.js +5 -0
  18. package/dist/index.d.ts +9 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +12 -0
  21. package/dist/presets/index.d.ts +27 -0
  22. package/dist/presets/index.d.ts.map +1 -0
  23. package/dist/presets/index.js +370 -0
  24. package/dist/slides/FormSlide.d.ts +12 -0
  25. package/dist/slides/FormSlide.d.ts.map +1 -0
  26. package/dist/slides/FormSlide.js +227 -0
  27. package/dist/slides/IconSlide.d.ts +10 -0
  28. package/dist/slides/IconSlide.d.ts.map +1 -0
  29. package/dist/slides/IconSlide.js +133 -0
  30. package/dist/slides/ImageSlide.d.ts +10 -0
  31. package/dist/slides/ImageSlide.d.ts.map +1 -0
  32. package/dist/slides/ImageSlide.js +99 -0
  33. package/dist/slides/VideoSlide.d.ts +10 -0
  34. package/dist/slides/VideoSlide.d.ts.map +1 -0
  35. package/dist/slides/VideoSlide.js +101 -0
  36. package/dist/slides/index.d.ts +14 -0
  37. package/dist/slides/index.d.ts.map +1 -0
  38. package/dist/slides/index.js +25 -0
  39. package/dist/themes/index.d.ts +35 -0
  40. package/dist/themes/index.d.ts.map +1 -0
  41. package/dist/themes/index.js +547 -0
  42. package/dist/types/index.d.ts +191 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +1 -0
  45. package/package.json +73 -60
  46. package/src/components/NavigationButtons.tsx +198 -198
  47. package/src/components/Onboarding.tsx +337 -340
  48. package/src/components/Pagination.tsx +337 -337
  49. package/src/components/SimpleOnboardingScreen.tsx +266 -0
  50. package/src/components/index.ts +7 -5
  51. package/src/index.ts +69 -65
  52. package/src/presets/index.ts +391 -394
  53. package/src/slides/FormSlide.tsx +314 -314
  54. package/src/slides/IconSlide.tsx +166 -166
  55. package/src/slides/ImageSlide.tsx +132 -132
  56. package/src/slides/VideoSlide.tsx +146 -146
  57. package/src/slides/{index.ts → index.tsx} +37 -44
  58. package/src/themes/index.ts +576 -574
  59. package/src/types/index.ts +247 -247
@@ -1,314 +1,314 @@
1
- import React, { useMemo, useState } from 'react';
2
- import {
3
- View,
4
- Text,
5
- TextInput,
6
- TouchableOpacity,
7
- StyleSheet,
8
- ScrollView,
9
- KeyboardAvoidingView,
10
- Platform,
11
- ViewStyle,
12
- } from 'react-native';
13
- import Animated, { FadeIn } from 'react-native-reanimated';
14
- import { LinearGradient } from 'expo-linear-gradient';
15
- import { Ionicons } from '@expo/vector-icons';
16
- import { FormSlideData, OnboardingTheme, FormFieldConfig } from '../types';
17
-
18
- interface FormSlideProps {
19
- data: FormSlideData;
20
- theme: OnboardingTheme;
21
- onSubmit: (formData: Record<string, any>) => void;
22
- darkMode?: boolean;
23
- isSubmitting?: boolean;
24
- }
25
-
26
- export const FormSlide: React.FC<FormSlideProps> = ({
27
- data,
28
- theme,
29
- onSubmit,
30
- darkMode,
31
- isSubmitting = false,
32
- }) => {
33
- const { title, description, fields, submitLabel = 'Get Started', gradientColors } = data;
34
- const [formData, setFormData] = useState<Record<string, any>>({});
35
- const [errors, setErrors] = useState<Record<string, string>>({});
36
-
37
- const containerStyle = useMemo(() => {
38
- const styles: any = {
39
- flex: 1,
40
- };
41
-
42
- if (data.backgroundColor) {
43
- styles.backgroundColor = data.backgroundColor;
44
- } else if (gradientColors && gradientColors.length > 0) {
45
- // Will use LinearGradient
46
- } else {
47
- styles.backgroundColor = theme.colors.background;
48
- }
49
-
50
- return styles;
51
- }, [data, theme]);
52
-
53
- const updateField = (key: string, value: any) => {
54
- setFormData(prev => ({ ...prev, [key]: value }));
55
- // Clear error when user starts typing
56
- if (errors[key]) {
57
- setErrors(prev => {
58
- const newErrors = { ...prev };
59
- delete newErrors[key];
60
- return newErrors;
61
- });
62
- }
63
- };
64
-
65
- const validateForm = (): boolean => {
66
- const newErrors: Record<string, string> = {};
67
-
68
- fields.forEach(field => {
69
- if (field.required && !formData[field.key]) {
70
- newErrors[field.key] = `${field.label} is required`;
71
- } else if (field.validation && formData[field.key]) {
72
- const validationResult = field.validation(formData[field.key]);
73
- if (typeof validationResult === 'string') {
74
- newErrors[field.key] = validationResult;
75
- } else if (!validationResult) {
76
- newErrors[field.key] = `${field.label} is invalid`;
77
- }
78
- }
79
- });
80
-
81
- setErrors(newErrors);
82
- return Object.keys(newErrors).length === 0;
83
- };
84
-
85
- const handleSubmit = () => {
86
- if (validateForm()) {
87
- onSubmit(formData);
88
- }
89
- };
90
-
91
- const renderField = (field: FormFieldConfig) => {
92
- const value = formData[field.key];
93
- const error = errors[field.key];
94
-
95
- if (field.type === 'select') {
96
- return (
97
- <View key={field.key} style={styles.fieldContainer}>
98
- <Text style={styles.label}>{field.label}</Text>
99
- <View style={styles.optionsContainer}>
100
- {field.options?.map(option => (
101
- <TouchableOpacity
102
- key={option.value}
103
- style={[
104
- styles.optionCard,
105
- value === option.value && styles.optionCardActive,
106
- { borderColor: theme.colors.border },
107
- ]}
108
- onPress={() => updateField(field.key, option.value)}
109
- >
110
- {option.icon && (
111
- <Ionicons
112
- name={option.icon as any}
113
- size={20}
114
- color={value === option.value ? theme.colors.text.inverse : theme.colors.text.secondary}
115
- style={{ marginRight: 8 }}
116
- />
117
- )}
118
- <Text
119
- style={[
120
- styles.optionLabel,
121
- value === option.value && { color: theme.colors.text.inverse },
122
- ]}
123
- >
124
- {option.label}
125
- </Text>
126
- </TouchableOpacity>
127
- ))}
128
- </View>
129
- {error && <Text style={styles.errorText}>{error}</Text>}
130
- </View>
131
- );
132
- }
133
-
134
- return (
135
- <View key={field.key} style={styles.fieldContainer}>
136
- <Text style={styles.label}>{field.label}</Text>
137
- <TextInput
138
- style={[
139
- styles.input,
140
- { borderColor: error ? theme.colors.text.secondary : theme.colors.border },
141
- ]}
142
- placeholder={field.placeholder}
143
- placeholderTextColor={theme.colors.text.secondary}
144
- value={value}
145
- onChangeText={text => updateField(field.key, text)}
146
- keyboardType={field.type === 'number' ? 'numeric' : field.type === 'email' ? 'email-address' : 'default'}
147
- secureTextEntry={field.type === 'password'}
148
- multiline={field.multiline}
149
- numberOfLines={field.numberOfLines}
150
- />
151
- {error && <Text style={styles.errorText}>{error}</Text>}
152
- </View>
153
- );
154
- };
155
-
156
- const headerStyle = useMemo(() => ({
157
- ...styles.header,
158
- backgroundColor: 'transparent',
159
- }), []);
160
-
161
- const titleStyle = useMemo(() => ({
162
- ...styles.title,
163
- color: gradientColors?.length ? theme.colors.text.primary : theme.colors.text.primary,
164
- }), [gradientColors, theme]);
165
-
166
- const descriptionStyle = useMemo(() => ({
167
- ...styles.description,
168
- color: gradientColors?.length ? theme.colors.text.secondary : theme.colors.text.secondary,
169
- }), [gradientColors, theme]);
170
-
171
- const content = (
172
- <KeyboardAvoidingView
173
- style={styles.keyboardContainer}
174
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
175
- keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
176
- >
177
- <ScrollView
178
- style={styles.scrollView}
179
- contentContainerStyle={styles.scrollContent}
180
- showsVerticalScrollIndicator={false}
181
- >
182
- {/* Header */}
183
- <Animated.View entering={FadeIn.duration(300)} style={headerStyle}>
184
- {title && <Text style={titleStyle}>{title}</Text>}
185
- {description && <Text style={descriptionStyle}>{description}</Text>}
186
- </Animated.View>
187
-
188
- {/* Form Fields */}
189
- <View style={styles.formContainer}>
190
- {fields.map(renderField)}
191
- </View>
192
-
193
- {/* Submit Button */}
194
- <Animated.View entering={FadeIn.delay(300).duration(300)} style={styles.buttonContainer}>
195
- <TouchableOpacity
196
- style={[styles.submitButton, { backgroundColor: theme.colors.primary }]}
197
- onPress={handleSubmit}
198
- disabled={isSubmitting}
199
- activeOpacity={0.8}
200
- >
201
- <Text style={styles.submitButtonText}>{isSubmitting ? 'Submitting...' : submitLabel}</Text>
202
- </TouchableOpacity>
203
- </Animated.View>
204
- </ScrollView>
205
- </KeyboardAvoidingView>
206
- );
207
-
208
- if (gradientColors && gradientColors.length > 0) {
209
- return (
210
- <LinearGradient colors={gradientColors} style={StyleSheet.absoluteFillObject}>
211
- <View style={containerStyle}>{content}</View>
212
- </LinearGradient>
213
- );
214
- }
215
-
216
- return <View style={containerStyle}>{content}</View>;
217
- };
218
-
219
- const styles = StyleSheet.create({
220
- keyboardContainer: {
221
- flex: 1,
222
- },
223
- scrollView: {
224
- flex: 1,
225
- },
226
- scrollContent: {
227
- padding: 24,
228
- paddingBottom: 40,
229
- },
230
- header: {
231
- marginBottom: 32,
232
- alignItems: 'center',
233
- },
234
- title: {
235
- fontSize: 28,
236
- fontWeight: '700',
237
- textAlign: 'center',
238
- marginBottom: 12,
239
- },
240
- description: {
241
- fontSize: 15,
242
- textAlign: 'center',
243
- paddingHorizontal: 20,
244
- lineHeight: 22,
245
- },
246
- formContainer: {
247
- width: '100%',
248
- gap: 20,
249
- },
250
- fieldContainer: {
251
- width: '100%',
252
- },
253
- label: {
254
- fontSize: 14,
255
- fontWeight: '600',
256
- marginBottom: 8,
257
- marginLeft: 4,
258
- },
259
- input: {
260
- borderWidth: 1.5,
261
- borderRadius: 12,
262
- paddingHorizontal: 16,
263
- paddingVertical: 14,
264
- fontSize: 16,
265
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
266
- },
267
- optionsContainer: {
268
- flexDirection: 'row',
269
- flexWrap: 'wrap',
270
- gap: 12,
271
- },
272
- optionCard: {
273
- flexDirection: 'row',
274
- alignItems: 'center',
275
- paddingHorizontal: 16,
276
- paddingVertical: 12,
277
- borderRadius: 12,
278
- borderWidth: 1.5,
279
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
280
- minWidth: 100,
281
- },
282
- optionCardActive: {
283
- backgroundColor: '#6B4EFF',
284
- borderColor: '#6B4EFF',
285
- },
286
- optionLabel: {
287
- fontSize: 14,
288
- fontWeight: '500',
289
- },
290
- errorText: {
291
- fontSize: 12,
292
- color: '#EF4444',
293
- marginTop: 4,
294
- marginLeft: 4,
295
- },
296
- buttonContainer: {
297
- marginTop: 24,
298
- alignItems: 'center',
299
- },
300
- submitButton: {
301
- paddingHorizontal: 48,
302
- paddingVertical: 16,
303
- borderRadius: 9999,
304
- minWidth: 200,
305
- alignItems: 'center',
306
- },
307
- submitButtonText: {
308
- color: '#FFFFFF',
309
- fontSize: 16,
310
- fontWeight: '600',
311
- },
312
- });
313
-
314
- export default FormSlide;
1
+ import React, { useMemo, useState } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ TouchableOpacity,
7
+ StyleSheet,
8
+ ScrollView,
9
+ KeyboardAvoidingView,
10
+ Platform,
11
+ ViewStyle,
12
+ } from 'react-native';
13
+ import Animated, { FadeIn } from 'react-native-reanimated';
14
+ import { LinearGradient } from 'expo-linear-gradient';
15
+ import { Ionicons } from '@expo/vector-icons';
16
+ import { FormSlideData, OnboardingTheme, FormFieldConfig } from '../types';
17
+
18
+ interface FormSlideProps {
19
+ data: FormSlideData;
20
+ theme: OnboardingTheme;
21
+ onSubmit: (formData: Record<string, any>) => void;
22
+ darkMode?: boolean;
23
+ isSubmitting?: boolean;
24
+ }
25
+
26
+ export const FormSlide: React.FC<FormSlideProps> = ({
27
+ data,
28
+ theme,
29
+ onSubmit,
30
+ darkMode,
31
+ isSubmitting = false,
32
+ }) => {
33
+ const { title, description, fields, submitLabel = 'Get Started', gradientColors } = data;
34
+ const [formData, setFormData] = useState<Record<string, any>>({});
35
+ const [errors, setErrors] = useState<Record<string, string>>({});
36
+
37
+ const containerStyle = useMemo(() => {
38
+ const styles: any = {
39
+ flex: 1,
40
+ };
41
+
42
+ if (data.backgroundColor) {
43
+ styles.backgroundColor = data.backgroundColor;
44
+ } else if (gradientColors && gradientColors.length > 0) {
45
+ // Will use LinearGradient
46
+ } else {
47
+ styles.backgroundColor = theme.colors.background;
48
+ }
49
+
50
+ return styles;
51
+ }, [data, theme]);
52
+
53
+ const updateField = (key: string, value: any) => {
54
+ setFormData(prev => ({ ...prev, [key]: value }));
55
+ // Clear error when user starts typing
56
+ if (errors[key]) {
57
+ setErrors(prev => {
58
+ const newErrors = { ...prev };
59
+ delete newErrors[key];
60
+ return newErrors;
61
+ });
62
+ }
63
+ };
64
+
65
+ const validateForm = (): boolean => {
66
+ const newErrors: Record<string, string> = {};
67
+
68
+ fields.forEach(field => {
69
+ if (field.required && !formData[field.key]) {
70
+ newErrors[field.key] = `${field.label} is required`;
71
+ } else if (field.validation && formData[field.key]) {
72
+ const validationResult = field.validation(formData[field.key]);
73
+ if (typeof validationResult === 'string') {
74
+ newErrors[field.key] = validationResult;
75
+ } else if (!validationResult) {
76
+ newErrors[field.key] = `${field.label} is invalid`;
77
+ }
78
+ }
79
+ });
80
+
81
+ setErrors(newErrors);
82
+ return Object.keys(newErrors).length === 0;
83
+ };
84
+
85
+ const handleSubmit = () => {
86
+ if (validateForm()) {
87
+ onSubmit(formData);
88
+ }
89
+ };
90
+
91
+ const renderField = (field: FormFieldConfig) => {
92
+ const value = formData[field.key];
93
+ const error = errors[field.key];
94
+
95
+ if (field.type === 'select') {
96
+ return (
97
+ <View key={field.key} style={styles.fieldContainer}>
98
+ <Text style={styles.label}>{field.label}</Text>
99
+ <View style={styles.optionsContainer}>
100
+ {field.options?.map(option => (
101
+ <TouchableOpacity
102
+ key={option.value}
103
+ style={[
104
+ styles.optionCard,
105
+ value === option.value && styles.optionCardActive,
106
+ { borderColor: theme.colors.border },
107
+ ]}
108
+ onPress={() => updateField(field.key, option.value)}
109
+ >
110
+ {option.icon && (
111
+ <Ionicons
112
+ name={option.icon as any}
113
+ size={20}
114
+ color={value === option.value ? theme.colors.text.inverse : theme.colors.text.secondary}
115
+ style={{ marginRight: 8 }}
116
+ />
117
+ )}
118
+ <Text
119
+ style={[
120
+ styles.optionLabel,
121
+ value === option.value && { color: theme.colors.text.inverse },
122
+ ]}
123
+ >
124
+ {option.label}
125
+ </Text>
126
+ </TouchableOpacity>
127
+ ))}
128
+ </View>
129
+ {error && <Text style={styles.errorText}>{error}</Text>}
130
+ </View>
131
+ );
132
+ }
133
+
134
+ return (
135
+ <View key={field.key} style={styles.fieldContainer}>
136
+ <Text style={styles.label}>{field.label}</Text>
137
+ <TextInput
138
+ style={[
139
+ styles.input,
140
+ { borderColor: error ? theme.colors.text.secondary : theme.colors.border },
141
+ ]}
142
+ placeholder={field.placeholder}
143
+ placeholderTextColor={theme.colors.text.secondary}
144
+ value={value}
145
+ onChangeText={text => updateField(field.key, text)}
146
+ keyboardType={field.type === 'number' ? 'numeric' : field.type === 'email' ? 'email-address' : 'default'}
147
+ secureTextEntry={field.type === 'password'}
148
+ multiline={field.multiline}
149
+ numberOfLines={field.numberOfLines}
150
+ />
151
+ {error && <Text style={styles.errorText}>{error}</Text>}
152
+ </View>
153
+ );
154
+ };
155
+
156
+ const headerStyle = useMemo(() => ({
157
+ ...styles.header,
158
+ backgroundColor: 'transparent',
159
+ }), []);
160
+
161
+ const titleStyle = useMemo(() => ({
162
+ ...styles.title,
163
+ color: gradientColors?.length ? theme.colors.text.primary : theme.colors.text.primary,
164
+ }), [gradientColors, theme]);
165
+
166
+ const descriptionStyle = useMemo(() => ({
167
+ ...styles.description,
168
+ color: gradientColors?.length ? theme.colors.text.secondary : theme.colors.text.secondary,
169
+ }), [gradientColors, theme]);
170
+
171
+ const content = (
172
+ <KeyboardAvoidingView
173
+ style={styles.keyboardContainer}
174
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
175
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
176
+ >
177
+ <ScrollView
178
+ style={styles.scrollView}
179
+ contentContainerStyle={styles.scrollContent}
180
+ showsVerticalScrollIndicator={false}
181
+ >
182
+ {/* Header */}
183
+ <Animated.View entering={FadeIn.duration(300)} style={headerStyle}>
184
+ {title && <Text style={titleStyle}>{title}</Text>}
185
+ {description && <Text style={descriptionStyle}>{description}</Text>}
186
+ </Animated.View>
187
+
188
+ {/* Form Fields */}
189
+ <View style={styles.formContainer}>
190
+ {fields.map(renderField)}
191
+ </View>
192
+
193
+ {/* Submit Button */}
194
+ <Animated.View entering={FadeIn.delay(300).duration(300)} style={styles.buttonContainer}>
195
+ <TouchableOpacity
196
+ style={[styles.submitButton, { backgroundColor: theme.colors.primary }]}
197
+ onPress={handleSubmit}
198
+ disabled={isSubmitting}
199
+ activeOpacity={0.8}
200
+ >
201
+ <Text style={styles.submitButtonText}>{isSubmitting ? 'Submitting...' : submitLabel}</Text>
202
+ </TouchableOpacity>
203
+ </Animated.View>
204
+ </ScrollView>
205
+ </KeyboardAvoidingView>
206
+ );
207
+
208
+ if (gradientColors && gradientColors.length > 0) {
209
+ return (
210
+ <LinearGradient colors={gradientColors as any} style={StyleSheet.absoluteFillObject}>
211
+ <View style={containerStyle}>{content}</View>
212
+ </LinearGradient>
213
+ );
214
+ }
215
+
216
+ return <View style={containerStyle}>{content}</View>;
217
+ };
218
+
219
+ const styles = StyleSheet.create({
220
+ keyboardContainer: {
221
+ flex: 1,
222
+ },
223
+ scrollView: {
224
+ flex: 1,
225
+ },
226
+ scrollContent: {
227
+ padding: 24,
228
+ paddingBottom: 40,
229
+ },
230
+ header: {
231
+ marginBottom: 32,
232
+ alignItems: 'center',
233
+ },
234
+ title: {
235
+ fontSize: 28,
236
+ fontWeight: '700',
237
+ textAlign: 'center',
238
+ marginBottom: 12,
239
+ },
240
+ description: {
241
+ fontSize: 15,
242
+ textAlign: 'center',
243
+ paddingHorizontal: 20,
244
+ lineHeight: 22,
245
+ },
246
+ formContainer: {
247
+ width: '100%',
248
+ gap: 20,
249
+ },
250
+ fieldContainer: {
251
+ width: '100%',
252
+ },
253
+ label: {
254
+ fontSize: 14,
255
+ fontWeight: '600',
256
+ marginBottom: 8,
257
+ marginLeft: 4,
258
+ },
259
+ input: {
260
+ borderWidth: 1.5,
261
+ borderRadius: 12,
262
+ paddingHorizontal: 16,
263
+ paddingVertical: 14,
264
+ fontSize: 16,
265
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
266
+ },
267
+ optionsContainer: {
268
+ flexDirection: 'row',
269
+ flexWrap: 'wrap',
270
+ gap: 12,
271
+ },
272
+ optionCard: {
273
+ flexDirection: 'row',
274
+ alignItems: 'center',
275
+ paddingHorizontal: 16,
276
+ paddingVertical: 12,
277
+ borderRadius: 12,
278
+ borderWidth: 1.5,
279
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
280
+ minWidth: 100,
281
+ },
282
+ optionCardActive: {
283
+ backgroundColor: '#6B4EFF',
284
+ borderColor: '#6B4EFF',
285
+ },
286
+ optionLabel: {
287
+ fontSize: 14,
288
+ fontWeight: '500',
289
+ },
290
+ errorText: {
291
+ fontSize: 12,
292
+ color: '#EF4444',
293
+ marginTop: 4,
294
+ marginLeft: 4,
295
+ },
296
+ buttonContainer: {
297
+ marginTop: 24,
298
+ alignItems: 'center',
299
+ },
300
+ submitButton: {
301
+ paddingHorizontal: 48,
302
+ paddingVertical: 16,
303
+ borderRadius: 9999,
304
+ minWidth: 200,
305
+ alignItems: 'center',
306
+ },
307
+ submitButtonText: {
308
+ color: '#FFFFFF',
309
+ fontSize: 16,
310
+ fontWeight: '600',
311
+ },
312
+ });
313
+
314
+ export default FormSlide;