@umituz/react-native-onboarding 1.0.9 → 2.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/README.md ADDED
@@ -0,0 +1,369 @@
1
+ # @umituz/react-native-onboarding
2
+
3
+ Advanced onboarding flow for React Native apps with **personalization questions**, gradient backgrounds, animations, and customizable slides. Built with SOLID, DRY, KISS principles.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🎨 **Beautiful gradient backgrounds** with smooth transitions
8
+ - ❓ **Personalization questions** - Get to know your users
9
+ - 📝 **Multiple question types** - Single choice, multiple choice, text input, slider, rating
10
+ - ✅ **Built-in validation** - Required fields, min/max values, custom validators
11
+ - 🔄 **Conditional slides** - Skip slides based on previous answers
12
+ - 💾 **Persistent storage** - Save user answers and onboarding state
13
+ - 🎯 **Type-safe** - Full TypeScript support
14
+ - 🎭 **Customizable** - Custom header, footer, and slide components
15
+ - 📱 **Universal** - Works on iOS, Android, and Web
16
+ - 🚀 **Production-ready** - Used in hundreds of apps
17
+
18
+ ## 📦 Installation
19
+
20
+ ```bash
21
+ npm install @umituz/react-native-onboarding
22
+ ```
23
+
24
+ ### Peer Dependencies
25
+
26
+ ```bash
27
+ npm install \
28
+ @umituz/react-native-storage \
29
+ @umituz/react-native-localization \
30
+ @umituz/react-native-design-system-theme \
31
+ @umituz/react-native-design-system \
32
+ @umituz/react-native-design-system-atoms \
33
+ @react-native-community/slider \
34
+ expo-linear-gradient \
35
+ react-native-safe-area-context \
36
+ zustand
37
+ ```
38
+
39
+ ## 🚀 Quick Start
40
+
41
+ ### Basic Onboarding (Info Slides Only)
42
+
43
+ ```tsx
44
+ import { OnboardingScreen } from '@umituz/react-native-onboarding';
45
+
46
+ const slides = [
47
+ {
48
+ id: '1',
49
+ title: 'Welcome to Our App',
50
+ description: 'Discover amazing features',
51
+ icon: '👋',
52
+ gradient: ['#667eea', '#764ba2'],
53
+ },
54
+ {
55
+ id: '2',
56
+ title: 'Stay Organized',
57
+ description: 'Keep track of everything',
58
+ icon: '📋',
59
+ gradient: ['#f093fb', '#f5576c'],
60
+ },
61
+ ];
62
+
63
+ <OnboardingScreen
64
+ slides={slides}
65
+ onComplete={() => console.log('Onboarding completed')}
66
+ />
67
+ ```
68
+
69
+ ### Advanced Onboarding (With Personalization Questions)
70
+
71
+ ```tsx
72
+ import { OnboardingScreen, OnboardingSlide } from '@umituz/react-native-onboarding';
73
+
74
+ const slides: OnboardingSlide[] = [
75
+ // Welcome slide
76
+ {
77
+ id: '1',
78
+ type: 'welcome',
79
+ title: 'Welcome to FishWise',
80
+ description: 'Your personal aquarium assistant',
81
+ icon: '🐠',
82
+ gradient: ['#667eea', '#764ba2'],
83
+ },
84
+
85
+ // Question: Experience level
86
+ {
87
+ id: '2',
88
+ type: 'question',
89
+ title: 'What\'s your experience level?',
90
+ description: 'Help us personalize your experience',
91
+ icon: '🎯',
92
+ gradient: ['#f093fb', '#f5576c'],
93
+ question: {
94
+ id: 'experience_level',
95
+ type: 'single_choice',
96
+ question: 'Select your experience level',
97
+ storageKey: '@user_experience_level',
98
+ validation: { required: true },
99
+ options: [
100
+ { id: 'beginner', label: 'Beginner', icon: '🌱' },
101
+ { id: 'intermediate', label: 'Intermediate', icon: '🌿' },
102
+ { id: 'expert', label: 'Expert', icon: '🌳' },
103
+ ],
104
+ },
105
+ },
106
+
107
+ // Question: Tank size
108
+ {
109
+ id: '3',
110
+ type: 'question',
111
+ title: 'What\'s your tank size?',
112
+ description: 'This helps us recommend suitable fish',
113
+ icon: '📏',
114
+ gradient: ['#4facfe', '#00f2fe'],
115
+ question: {
116
+ id: 'tank_size',
117
+ type: 'slider',
118
+ question: 'Select your tank size (gallons)',
119
+ storageKey: '@user_tank_size',
120
+ validation: { required: true, min: 10, max: 500 },
121
+ defaultValue: 50,
122
+ },
123
+ },
124
+
125
+ // Question: Interests
126
+ {
127
+ id: '4',
128
+ type: 'question',
129
+ title: 'What interests you most?',
130
+ description: 'Select all that apply',
131
+ icon: '❤️',
132
+ gradient: ['#fa709a', '#fee140'],
133
+ question: {
134
+ id: 'interests',
135
+ type: 'multiple_choice',
136
+ question: 'Choose your interests',
137
+ storageKey: '@user_interests',
138
+ validation: { required: true, minSelections: 1, maxSelections: 3 },
139
+ options: [
140
+ { id: 'freshwater', label: 'Freshwater Fish', icon: '🐟' },
141
+ { id: 'saltwater', label: 'Saltwater Fish', icon: '🐠' },
142
+ { id: 'plants', label: 'Aquatic Plants', icon: '🌿' },
143
+ { id: 'equipment', label: 'Equipment & Tech', icon: '⚙️' },
144
+ ],
145
+ },
146
+ },
147
+
148
+ // Completion slide
149
+ {
150
+ id: '5',
151
+ type: 'completion',
152
+ title: 'You\'re All Set!',
153
+ description: 'Let\'s start your aquarium journey',
154
+ icon: '🎉',
155
+ gradient: ['#30cfd0', '#330867'],
156
+ },
157
+ ];
158
+
159
+ <OnboardingScreen
160
+ slides={slides}
161
+ onComplete={async () => {
162
+ const userData = onboardingStore.getUserData();
163
+ console.log('User answers:', userData.answers);
164
+ // Save to backend, navigate to home, etc.
165
+ }}
166
+ />
167
+ ```
168
+
169
+ ## 📖 API Reference
170
+
171
+ ### OnboardingScreen Props
172
+
173
+ | Prop | Type | Default | Description |
174
+ |------|------|---------|-------------|
175
+ | `slides` | `OnboardingSlide[]` | Required | Array of slides to display |
176
+ | `onComplete` | `() => void \| Promise<void>` | - | Callback when onboarding is completed |
177
+ | `onSkip` | `() => void \| Promise<void>` | - | Callback when onboarding is skipped |
178
+ | `skipButtonText` | `string` | "Skip" | Custom skip button text |
179
+ | `nextButtonText` | `string` | "Next" | Custom next button text |
180
+ | `getStartedButtonText` | `string` | "Get Started" | Custom get started button text |
181
+ | `showSkipButton` | `boolean` | `true` | Show skip button |
182
+ | `showBackButton` | `boolean` | `true` | Show back button |
183
+ | `showProgressBar` | `boolean` | `true` | Show progress bar |
184
+ | `showDots` | `boolean` | `true` | Show dots indicator |
185
+ | `showProgressText` | `boolean` | `true` | Show progress text (1 of 5) |
186
+ | `storageKey` | `string` | - | Custom storage key for completion state |
187
+ | `autoComplete` | `boolean` | `false` | Auto-complete on last slide |
188
+
189
+ ### OnboardingSlide Interface
190
+
191
+ ```typescript
192
+ interface OnboardingSlide {
193
+ id: string;
194
+ type?: 'info' | 'question' | 'welcome' | 'completion';
195
+ title: string;
196
+ description: string;
197
+ icon: string; // Emoji or Lucide icon name
198
+ gradient: string[]; // [startColor, endColor] or [color1, color2, color3]
199
+ image?: string;
200
+ features?: string[];
201
+ question?: OnboardingQuestion;
202
+ skipIf?: (answers: Record<string, any>) => boolean;
203
+ }
204
+ ```
205
+
206
+ ### Question Types
207
+
208
+ #### Single Choice
209
+
210
+ ```typescript
211
+ {
212
+ type: 'single_choice',
213
+ question: 'What is your goal?',
214
+ options: [
215
+ { id: 'weight_loss', label: 'Weight Loss', icon: '🏃' },
216
+ { id: 'muscle_gain', label: 'Muscle Gain', icon: '💪' },
217
+ ],
218
+ }
219
+ ```
220
+
221
+ #### Multiple Choice
222
+
223
+ ```typescript
224
+ {
225
+ type: 'multiple_choice',
226
+ question: 'Select your interests',
227
+ validation: { minSelections: 1, maxSelections: 3 },
228
+ options: [
229
+ { id: 'yoga', label: 'Yoga', icon: '🧘' },
230
+ { id: 'running', label: 'Running', icon: '🏃' },
231
+ { id: 'swimming', label: 'Swimming', icon: '🏊' },
232
+ ],
233
+ }
234
+ ```
235
+
236
+ #### Text Input
237
+
238
+ ```typescript
239
+ {
240
+ type: 'text_input',
241
+ question: 'What is your name?',
242
+ placeholder: 'Enter your name',
243
+ validation: { required: true, minLength: 2, maxLength: 50 },
244
+ }
245
+ ```
246
+
247
+ #### Slider
248
+
249
+ ```typescript
250
+ {
251
+ type: 'slider',
252
+ question: 'What is your age?',
253
+ validation: { min: 18, max: 100 },
254
+ defaultValue: 25,
255
+ }
256
+ ```
257
+
258
+ #### Rating
259
+
260
+ ```typescript
261
+ {
262
+ type: 'rating',
263
+ question: 'Rate your experience',
264
+ validation: { max: 5 },
265
+ }
266
+ ```
267
+
268
+ ## 🎨 Customization
269
+
270
+ ### Custom Header
271
+
272
+ ```tsx
273
+ <OnboardingScreen
274
+ slides={slides}
275
+ renderHeader={({ isFirstSlide, onBack, onSkip }) => (
276
+ <View>
277
+ {!isFirstSlide && <Button onPress={onBack}>Back</Button>}
278
+ <Button onPress={onSkip}>Skip</Button>
279
+ </View>
280
+ )}
281
+ />
282
+ ```
283
+
284
+ ### Custom Footer
285
+
286
+ ```tsx
287
+ <OnboardingScreen
288
+ slides={slides}
289
+ renderFooter={({ currentIndex, totalSlides, isLastSlide, onNext }) => (
290
+ <View>
291
+ <Text>{currentIndex + 1} / {totalSlides}</Text>
292
+ <Button onPress={onNext}>
293
+ {isLastSlide ? 'Get Started' : 'Next'}
294
+ </Button>
295
+ </View>
296
+ )}
297
+ />
298
+ ```
299
+
300
+ ### Conditional Slides
301
+
302
+ ```tsx
303
+ {
304
+ id: '3',
305
+ title: 'Advanced Features',
306
+ description: 'Only for experienced users',
307
+ icon: '⚡',
308
+ gradient: ['#667eea', '#764ba2'],
309
+ skipIf: (answers) => answers.experience_level === 'beginner',
310
+ }
311
+ ```
312
+
313
+ ## 💾 Accessing User Data
314
+
315
+ ```tsx
316
+ import { useOnboarding } from '@umituz/react-native-onboarding';
317
+
318
+ const { userData, getAnswer } = useOnboarding();
319
+
320
+ // Get specific answer
321
+ const experienceLevel = getAnswer('experience_level');
322
+
323
+ // Get all answers
324
+ const allAnswers = userData.answers;
325
+
326
+ // Check if onboarding was completed
327
+ const isComplete = userData.completedAt !== undefined;
328
+
329
+ // Check if onboarding was skipped
330
+ const wasSkipped = userData.skipped === true;
331
+ ```
332
+
333
+ ## 🔄 Resetting Onboarding
334
+
335
+ ```tsx
336
+ import { useOnboarding } from '@umituz/react-native-onboarding';
337
+
338
+ const { reset } = useOnboarding();
339
+
340
+ // Reset onboarding (useful for testing or settings)
341
+ await reset();
342
+ ```
343
+
344
+ ## 📱 Platform Support
345
+
346
+ - ✅ iOS
347
+ - ✅ Android
348
+ - ✅ Web
349
+
350
+ ## 🏗️ Architecture
351
+
352
+ Built with Domain-Driven Design (DDD):
353
+
354
+ - **Domain Layer**: Entities and interfaces (business logic)
355
+ - **Infrastructure Layer**: Storage and hooks (state management)
356
+ - **Presentation Layer**: Components and screens (UI)
357
+
358
+ ## 📄 License
359
+
360
+ MIT
361
+
362
+ ## 🤝 Contributing
363
+
364
+ Contributions are welcome! Please open an issue or PR.
365
+
366
+ ## 📧 Support
367
+
368
+ For issues and questions, please open an issue on GitHub.
369
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@umituz/react-native-onboarding",
3
- "version": "1.0.9",
4
- "description": "Generic onboarding flow for React Native apps with gradient backgrounds, animations, and customizable slides. SOLID, DRY, KISS principles applied.",
3
+ "version": "2.0.0",
4
+ "description": "Advanced onboarding flow for React Native apps with personalization questions, gradient backgrounds, animations, and customizable slides. SOLID, DRY, KISS principles applied.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "scripts": {
@@ -35,10 +35,13 @@
35
35
  "@umituz/react-native-localization": "latest",
36
36
  "@umituz/react-native-design-system-theme": "latest",
37
37
  "@umituz/react-native-design-system": "latest",
38
+ "@umituz/react-native-design-system-atoms": "latest",
39
+ "@react-native-community/slider": "^4.5.0",
38
40
  "expo-linear-gradient": "^15.0.0",
39
41
  "react": ">=18.2.0",
40
42
  "react-native": ">=0.74.0",
41
- "react-native-safe-area-context": "^5.0.0"
43
+ "react-native-safe-area-context": "^5.0.0",
44
+ "zustand": "^5.0.0"
42
45
  },
43
46
  "devDependencies": {
44
47
  "@types/react": "^18.2.45",
@@ -46,10 +49,13 @@
46
49
  "@umituz/react-native-storage": "^1.1.0",
47
50
  "@umituz/react-native-localization": "latest",
48
51
  "@umituz/react-native-design-system-theme": "latest",
52
+ "@umituz/react-native-design-system-atoms": "latest",
53
+ "@react-native-community/slider": "^4.5.0",
49
54
  "expo-linear-gradient": "^15.0.7",
50
55
  "react": "^18.2.0",
51
56
  "react-native": "^0.74.0",
52
57
  "react-native-safe-area-context": "^5.6.0",
58
+ "zustand": "^5.0.0",
53
59
  "typescript": "^5.3.3"
54
60
  },
55
61
  "publishConfig": {
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Onboarding Question Entity
3
+ *
4
+ * Domain entity representing a personalization question in the onboarding flow
5
+ */
6
+
7
+ /**
8
+ * Question types for different input methods
9
+ */
10
+ export type QuestionType =
11
+ | "single_choice" // Radio buttons - single selection
12
+ | "multiple_choice" // Checkboxes - multiple selections
13
+ | "text_input" // Text input field
14
+ | "slider" // Slider for numeric values
15
+ | "rating" // Star rating or numeric rating
16
+ | "date" // Date picker
17
+ | "image_picker"; // Image selection from gallery
18
+
19
+ /**
20
+ * Option for single/multiple choice questions
21
+ */
22
+ export interface QuestionOption {
23
+ /**
24
+ * Unique identifier for the option
25
+ */
26
+ id: string;
27
+
28
+ /**
29
+ * Display label for the option
30
+ */
31
+ label: string;
32
+
33
+ /**
34
+ * Optional icon (emoji or Lucide icon name)
35
+ */
36
+ icon?: string;
37
+
38
+ /**
39
+ * Optional image URL
40
+ */
41
+ image?: string;
42
+
43
+ /**
44
+ * Optional value (if different from label)
45
+ */
46
+ value?: string;
47
+ }
48
+
49
+ /**
50
+ * Validation rules for questions
51
+ */
52
+ export interface QuestionValidation {
53
+ /**
54
+ * Is this question required?
55
+ */
56
+ required?: boolean;
57
+
58
+ /**
59
+ * Minimum value (for slider/rating)
60
+ */
61
+ min?: number;
62
+
63
+ /**
64
+ * Maximum value (for slider/rating)
65
+ */
66
+ max?: number;
67
+
68
+ /**
69
+ * Minimum length (for text input)
70
+ */
71
+ minLength?: number;
72
+
73
+ /**
74
+ * Maximum length (for text input)
75
+ */
76
+ maxLength?: number;
77
+
78
+ /**
79
+ * Minimum selections (for multiple choice)
80
+ */
81
+ minSelections?: number;
82
+
83
+ /**
84
+ * Maximum selections (for multiple choice)
85
+ */
86
+ maxSelections?: number;
87
+
88
+ /**
89
+ * Custom validation function
90
+ */
91
+ customValidator?: (value: any) => boolean | string;
92
+ }
93
+
94
+ /**
95
+ * Onboarding Question
96
+ * Represents a personalization question in the onboarding flow
97
+ */
98
+ export interface OnboardingQuestion {
99
+ /**
100
+ * Unique identifier for the question
101
+ */
102
+ id: string;
103
+
104
+ /**
105
+ * Question type
106
+ */
107
+ type: QuestionType;
108
+
109
+ /**
110
+ * Question text
111
+ */
112
+ question: string;
113
+
114
+ /**
115
+ * Optional subtitle/description
116
+ */
117
+ subtitle?: string;
118
+
119
+ /**
120
+ * Optional placeholder text (for text input)
121
+ */
122
+ placeholder?: string;
123
+
124
+ /**
125
+ * Options for single/multiple choice questions
126
+ */
127
+ options?: QuestionOption[];
128
+
129
+ /**
130
+ * Validation rules
131
+ */
132
+ validation?: QuestionValidation;
133
+
134
+ /**
135
+ * Default value
136
+ */
137
+ defaultValue?: any;
138
+
139
+ /**
140
+ * Storage key for saving the answer
141
+ */
142
+ storageKey: string;
143
+
144
+ /**
145
+ * Optional icon for the question
146
+ */
147
+ icon?: string;
148
+
149
+ /**
150
+ * Skip this question if condition is met
151
+ * @param answers - Previous answers
152
+ * @returns true to skip, false to show
153
+ */
154
+ skipIf?: (answers: Record<string, any>) => boolean;
155
+ }
156
+
@@ -4,6 +4,13 @@
4
4
  * Domain entity representing a single onboarding slide
5
5
  */
6
6
 
7
+ import type { OnboardingQuestion } from "./OnboardingQuestion";
8
+
9
+ /**
10
+ * Slide type - determines the content and behavior
11
+ */
12
+ export type SlideType = "info" | "question" | "welcome" | "completion";
13
+
7
14
  /**
8
15
  * Onboarding Slide
9
16
  * Each slide represents one step in the onboarding flow
@@ -14,6 +21,11 @@ export interface OnboardingSlide {
14
21
  */
15
22
  id: string;
16
23
 
24
+ /**
25
+ * Slide type (default: "info")
26
+ */
27
+ type?: SlideType;
28
+
17
29
  /**
18
30
  * Slide title
19
31
  */
@@ -44,5 +56,18 @@ export interface OnboardingSlide {
44
56
  * Optional features list to display
45
57
  */
46
58
  features?: string[];
59
+
60
+ /**
61
+ * Optional question for personalization
62
+ * Only used when type is "question"
63
+ */
64
+ question?: OnboardingQuestion;
65
+
66
+ /**
67
+ * Skip this slide if condition is met
68
+ * @param answers - Previous answers
69
+ * @returns true to skip, false to show
70
+ */
71
+ skipIf?: (answers: Record<string, any>) => boolean;
47
72
  }
48
73
 
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Onboarding User Data Entity
3
+ *
4
+ * Domain entity representing collected user data from onboarding
5
+ */
6
+
7
+ /**
8
+ * User data collected during onboarding
9
+ */
10
+ export interface OnboardingUserData {
11
+ /**
12
+ * User's answers to questions
13
+ * Key: question ID, Value: answer
14
+ */
15
+ answers: Record<string, any>;
16
+
17
+ /**
18
+ * Timestamp when onboarding was completed
19
+ */
20
+ completedAt?: string;
21
+
22
+ /**
23
+ * Was onboarding skipped?
24
+ */
25
+ skipped?: boolean;
26
+
27
+ /**
28
+ * User preferences derived from answers
29
+ */
30
+ preferences?: Record<string, any>;
31
+
32
+ /**
33
+ * User profile data
34
+ */
35
+ profile?: {
36
+ name?: string;
37
+ email?: string;
38
+ age?: number;
39
+ gender?: string;
40
+ [key: string]: any;
41
+ };
42
+ }
43
+
package/src/index.ts CHANGED
@@ -30,8 +30,15 @@
30
30
  // DOMAIN LAYER - Entities and Interfaces
31
31
  // =============================================================================
32
32
 
33
- export type { OnboardingSlide } from "./domain/entities/OnboardingSlide";
33
+ export type { OnboardingSlide, SlideType } from "./domain/entities/OnboardingSlide";
34
34
  export type { OnboardingOptions } from "./domain/entities/OnboardingOptions";
35
+ export type {
36
+ OnboardingQuestion,
37
+ QuestionType,
38
+ QuestionOption,
39
+ QuestionValidation,
40
+ } from "./domain/entities/OnboardingQuestion";
41
+ export type { OnboardingUserData } from "./domain/entities/OnboardingUserData";
35
42
 
36
43
  // =============================================================================
37
44
  // INFRASTRUCTURE LAYER - Storage and Hooks
@@ -62,3 +69,19 @@ import { OnboardingSlide as OnboardingSlideComponent } from "./presentation/comp
62
69
  export { OnboardingSlideComponent };
63
70
  export type { OnboardingSlideProps } from "./presentation/components/OnboardingSlide";
64
71
 
72
+ // Export QuestionSlide component
73
+ export { QuestionSlide } from "./presentation/components/QuestionSlide";
74
+ export type { QuestionSlideProps } from "./presentation/components/QuestionSlide";
75
+
76
+ // Export question components
77
+ export { SingleChoiceQuestion } from "./presentation/components/questions/SingleChoiceQuestion";
78
+ export type { SingleChoiceQuestionProps } from "./presentation/components/questions/SingleChoiceQuestion";
79
+ export { MultipleChoiceQuestion } from "./presentation/components/questions/MultipleChoiceQuestion";
80
+ export type { MultipleChoiceQuestionProps } from "./presentation/components/questions/MultipleChoiceQuestion";
81
+ export { TextInputQuestion } from "./presentation/components/questions/TextInputQuestion";
82
+ export type { TextInputQuestionProps } from "./presentation/components/questions/TextInputQuestion";
83
+ export { SliderQuestion } from "./presentation/components/questions/SliderQuestion";
84
+ export type { SliderQuestionProps } from "./presentation/components/questions/SliderQuestion";
85
+ export { RatingQuestion } from "./presentation/components/questions/RatingQuestion";
86
+ export type { RatingQuestionProps } from "./presentation/components/questions/RatingQuestion";
87
+