@umituz/react-native-design-system 2.6.128 → 2.7.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 (64) hide show
  1. package/package.json +4 -2
  2. package/src/exports/onboarding.ts +6 -0
  3. package/src/index.ts +5 -0
  4. package/src/onboarding/domain/entities/OnboardingOptions.ts +104 -0
  5. package/src/onboarding/domain/entities/OnboardingQuestion.ts +165 -0
  6. package/src/onboarding/domain/entities/OnboardingSlide.ts +152 -0
  7. package/src/onboarding/domain/entities/OnboardingUserData.ts +43 -0
  8. package/src/onboarding/hooks/useOnboardingFlow.ts +50 -0
  9. package/src/onboarding/index.ts +108 -0
  10. package/src/onboarding/infrastructure/hooks/__tests__/useOnboardingAnswers.test.ts +163 -0
  11. package/src/onboarding/infrastructure/hooks/__tests__/useOnboardingNavigation.test.ts +121 -0
  12. package/src/onboarding/infrastructure/hooks/useOnboardingAnswers.ts +69 -0
  13. package/src/onboarding/infrastructure/hooks/useOnboardingNavigation.ts +75 -0
  14. package/src/onboarding/infrastructure/services/SlideManager.ts +53 -0
  15. package/src/onboarding/infrastructure/services/ValidationManager.ts +127 -0
  16. package/src/onboarding/infrastructure/storage/OnboardingStore.ts +99 -0
  17. package/src/onboarding/infrastructure/storage/OnboardingStoreActions.ts +50 -0
  18. package/src/onboarding/infrastructure/storage/OnboardingStoreSelectors.ts +25 -0
  19. package/src/onboarding/infrastructure/storage/OnboardingStoreState.ts +22 -0
  20. package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +85 -0
  21. package/src/onboarding/infrastructure/storage/actions/answerActions.ts +47 -0
  22. package/src/onboarding/infrastructure/storage/actions/completeAction.ts +45 -0
  23. package/src/onboarding/infrastructure/storage/actions/index.ts +22 -0
  24. package/src/onboarding/infrastructure/storage/actions/initializeAction.ts +40 -0
  25. package/src/onboarding/infrastructure/storage/actions/resetAction.ts +37 -0
  26. package/src/onboarding/infrastructure/storage/actions/skipAction.ts +46 -0
  27. package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +60 -0
  28. package/src/onboarding/infrastructure/utils/arrayUtils.ts +28 -0
  29. package/src/onboarding/infrastructure/utils/backgroundUtils.ts +38 -0
  30. package/src/onboarding/infrastructure/utils/layouts/collageLayout.ts +81 -0
  31. package/src/onboarding/infrastructure/utils/layouts/gridLayouts.ts +78 -0
  32. package/src/onboarding/infrastructure/utils/layouts/honeycombLayout.ts +36 -0
  33. package/src/onboarding/infrastructure/utils/layouts/index.ts +12 -0
  34. package/src/onboarding/infrastructure/utils/layouts/layoutTypes.ts +37 -0
  35. package/src/onboarding/infrastructure/utils/layouts/masonryLayout.ts +37 -0
  36. package/src/onboarding/infrastructure/utils/layouts/scatteredLayout.ts +34 -0
  37. package/src/onboarding/infrastructure/utils/layouts/screenDimensions.ts +11 -0
  38. package/src/onboarding/infrastructure/utils/layouts/tilesLayout.ts +34 -0
  39. package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +90 -0
  40. package/src/onboarding/presentation/components/BackgroundVideo.tsx +24 -0
  41. package/src/onboarding/presentation/components/BaseSlide.tsx +47 -0
  42. package/src/onboarding/presentation/components/OnboardingBackground.tsx +91 -0
  43. package/src/onboarding/presentation/components/OnboardingFooter.tsx +151 -0
  44. package/src/onboarding/presentation/components/OnboardingHeader.tsx +92 -0
  45. package/src/onboarding/presentation/components/OnboardingResetSetting.tsx +70 -0
  46. package/src/onboarding/presentation/components/OnboardingScreenContent.tsx +146 -0
  47. package/src/onboarding/presentation/components/OnboardingSlide.tsx +124 -0
  48. package/src/onboarding/presentation/components/QuestionRenderer.tsx +60 -0
  49. package/src/onboarding/presentation/components/QuestionSlide.tsx +67 -0
  50. package/src/onboarding/presentation/components/QuestionSlideHeader.tsx +75 -0
  51. package/src/onboarding/presentation/components/questions/MultipleChoiceQuestion.tsx +74 -0
  52. package/src/onboarding/presentation/components/questions/QuestionOptionItem.tsx +115 -0
  53. package/src/onboarding/presentation/components/questions/RatingQuestion.tsx +66 -0
  54. package/src/onboarding/presentation/components/questions/SingleChoiceQuestion.tsx +117 -0
  55. package/src/onboarding/presentation/components/questions/TextInputQuestion.tsx +71 -0
  56. package/src/onboarding/presentation/hooks/__tests__/useOnboardingContainerStyle.test.ts +96 -0
  57. package/src/onboarding/presentation/hooks/useOnboardingContainerStyle.ts +37 -0
  58. package/src/onboarding/presentation/hooks/useOnboardingGestures.ts +45 -0
  59. package/src/onboarding/presentation/hooks/useOnboardingScreenHandlers.ts +114 -0
  60. package/src/onboarding/presentation/hooks/useOnboardingScreenState.ts +146 -0
  61. package/src/onboarding/presentation/providers/OnboardingProvider.tsx +51 -0
  62. package/src/onboarding/presentation/screens/OnboardingScreen.tsx +189 -0
  63. package/src/onboarding/presentation/types/OnboardingProps.ts +46 -0
  64. package/src/onboarding/presentation/types/OnboardingTheme.ts +27 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * useOnboardingScreenState Hook
3
+ * Single Responsibility: Coordinate onboarding screen state
4
+ */
5
+
6
+ import { useMemo, useEffect } from "react";
7
+ import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
8
+ import { useOnboarding } from "../../infrastructure/storage/OnboardingStore";
9
+ import { useOnboardingNavigation } from "../../infrastructure/hooks/useOnboardingNavigation";
10
+ import { useOnboardingAnswers } from "../../infrastructure/hooks/useOnboardingAnswers";
11
+ import { useOnboardingContainerStyle } from "./useOnboardingContainerStyle";
12
+ import { useOnboardingScreenHandlers } from "./useOnboardingScreenHandlers";
13
+ import { SlideManager } from "../../infrastructure/services/SlideManager";
14
+ import { ValidationManager } from "../../infrastructure/services/ValidationManager";
15
+ import { shouldUseCustomBackground } from "../../infrastructure/utils/backgroundUtils";
16
+
17
+ export interface UseOnboardingScreenStateProps {
18
+ slides: OnboardingSlide[] | undefined;
19
+ storageKey?: string;
20
+ onComplete?: () => void | Promise<void>;
21
+ onSkip?: () => void | Promise<void>;
22
+ globalUseCustomBackground?: boolean;
23
+ }
24
+
25
+ export interface UseOnboardingScreenStateReturn {
26
+ filteredSlides: OnboardingSlide[];
27
+ currentSlide: OnboardingSlide | undefined;
28
+ currentIndex: number;
29
+ isFirstSlide: boolean;
30
+ isLastSlide: boolean;
31
+ currentAnswer: unknown;
32
+ isAnswerValid: boolean;
33
+ useCustomBackground: boolean;
34
+ containerStyle: unknown;
35
+ handleNext: () => Promise<void>;
36
+ handlePrevious: () => void;
37
+ handleSkip: () => Promise<void>;
38
+ setCurrentAnswer: (value: unknown) => void;
39
+ }
40
+
41
+ export function useOnboardingScreenState({
42
+ slides,
43
+ storageKey,
44
+ onComplete,
45
+ onSkip,
46
+ globalUseCustomBackground = false,
47
+ }: UseOnboardingScreenStateProps): UseOnboardingScreenStateReturn {
48
+ const onboardingStore = useOnboarding();
49
+
50
+ const filteredSlides = useMemo(() => {
51
+ if (!slides || !Array.isArray(slides) || slides.length === 0) {
52
+ return [];
53
+ }
54
+ const userData = onboardingStore.userData;
55
+ return SlideManager.filterSlides(slides, userData);
56
+ }, [slides, onboardingStore.userData]);
57
+
58
+ const {
59
+ currentIndex,
60
+ goToNext,
61
+ goToPrevious,
62
+ complete: completeOnboarding,
63
+ skip: skipOnboarding,
64
+ isLastSlide,
65
+ isFirstSlide,
66
+ } = useOnboardingNavigation(
67
+ filteredSlides.length,
68
+ async () => {
69
+ await onboardingStore.complete(storageKey);
70
+ if (onComplete) {
71
+ await onComplete();
72
+ }
73
+ },
74
+ async () => {
75
+ await onboardingStore.skip(storageKey);
76
+ if (onSkip) {
77
+ await onSkip();
78
+ }
79
+ }
80
+ );
81
+
82
+ const currentSlide = useMemo(
83
+ () => SlideManager.getSlideAtIndex(filteredSlides, currentIndex),
84
+ [filteredSlides, currentIndex]
85
+ );
86
+
87
+ const {
88
+ currentAnswer,
89
+ setCurrentAnswer,
90
+ loadAnswerForSlide,
91
+ saveCurrentAnswer,
92
+ } = useOnboardingAnswers(currentSlide);
93
+
94
+ const { handleNext, handlePrevious, handleSkip } = useOnboardingScreenHandlers(
95
+ {
96
+ filteredSlides,
97
+ currentSlide,
98
+ currentIndex,
99
+ isLastSlide,
100
+ saveCurrentAnswer,
101
+ completeOnboarding,
102
+ goToNext,
103
+ goToPrevious,
104
+ skipOnboarding,
105
+ loadAnswerForSlide,
106
+ }
107
+ );
108
+
109
+ const useCustomBackground = shouldUseCustomBackground(currentSlide, globalUseCustomBackground);
110
+
111
+ const isAnswerValid = useMemo(() => {
112
+ if (!currentSlide?.question) {
113
+ return true;
114
+ }
115
+ return ValidationManager.validateAnswer(
116
+ currentSlide.question,
117
+ currentAnswer
118
+ );
119
+ }, [currentSlide, currentAnswer]);
120
+
121
+ const { containerStyle } = useOnboardingContainerStyle({ useCustomBackground });
122
+
123
+ useEffect(() => {
124
+ return () => {
125
+ if (__DEV__) {
126
+ console.log("[useOnboardingScreenState] Cleanup completed");
127
+ }
128
+ };
129
+ }, []);
130
+
131
+ return {
132
+ filteredSlides,
133
+ currentSlide,
134
+ currentIndex,
135
+ isFirstSlide,
136
+ isLastSlide,
137
+ currentAnswer,
138
+ isAnswerValid,
139
+ useCustomBackground,
140
+ containerStyle,
141
+ handleNext,
142
+ handlePrevious,
143
+ handleSkip,
144
+ setCurrentAnswer,
145
+ };
146
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Onboarding Provider
3
+ *
4
+ * Central manager for onboarding theme and configuration.
5
+ * All values are passed from the main application.
6
+ */
7
+
8
+ import React, { createContext, useContext, useMemo } from "react";
9
+ import type { OnboardingTheme, OnboardingColors } from "../types/OnboardingTheme";
10
+
11
+ interface OnboardingProviderValue {
12
+ theme: OnboardingTheme;
13
+ }
14
+
15
+ const OnboardingScope = createContext<OnboardingProviderValue | undefined>(undefined);
16
+
17
+ export interface OnboardingProviderProps {
18
+ children: React.ReactNode;
19
+ useCustomBackground: boolean;
20
+ colors: OnboardingColors;
21
+ }
22
+
23
+ export const OnboardingProvider = ({
24
+ children,
25
+ useCustomBackground,
26
+ colors,
27
+ }: OnboardingProviderProps) => {
28
+ const value = useMemo(
29
+ () => ({
30
+ theme: {
31
+ colors,
32
+ useCustomBackground,
33
+ },
34
+ }),
35
+ [colors, useCustomBackground]
36
+ );
37
+
38
+ return (
39
+ <OnboardingScope.Provider value={value}>
40
+ {children}
41
+ </OnboardingScope.Provider>
42
+ );
43
+ };
44
+
45
+ export const useOnboardingProvider = (): OnboardingProviderValue => {
46
+ const scope = useContext(OnboardingScope);
47
+ if (!scope) {
48
+ throw new Error("useOnboardingProvider must be used within OnboardingProvider");
49
+ }
50
+ return scope;
51
+ };
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Onboarding Screen
3
+ *
4
+ * Main onboarding screen component with theme-aware colors
5
+ * Generic and reusable across hundreds of apps
6
+ *
7
+ * This component only handles UI coordination - no business logic
8
+ */
9
+
10
+ import React, { useMemo } from "react";
11
+ import { StyleSheet } from "react-native";
12
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
13
+ import type { OnboardingOptions } from "../../domain/entities/OnboardingOptions";
14
+ import { useOnboardingScreenState } from "../hooks/useOnboardingScreenState";
15
+ import { OnboardingScreenContent } from "../components/OnboardingScreenContent";
16
+ import { OnboardingProvider } from "../providers/OnboardingProvider";
17
+ import type { OnboardingColors } from "../types/OnboardingTheme";
18
+
19
+ export interface OnboardingScreenProps extends OnboardingOptions {
20
+ /**
21
+ * Optional custom header component
22
+ */
23
+ renderHeader?: (props: {
24
+ isFirstSlide: boolean;
25
+ onBack: () => void;
26
+ onSkip: () => void;
27
+ }) => React.ReactNode;
28
+
29
+ /**
30
+ * Optional custom footer component
31
+ */
32
+ renderFooter?: (props: {
33
+ currentIndex: number;
34
+ totalSlides: number;
35
+ isLastSlide: boolean;
36
+ onNext: () => void;
37
+ onUpgrade?: () => void;
38
+ showPaywallOnComplete?: boolean;
39
+ }) => React.ReactNode;
40
+
41
+ /**
42
+ * Optional custom slide component
43
+ */
44
+ renderSlide?: (slide: OnboardingOptions["slides"][0]) => React.ReactNode;
45
+
46
+ /**
47
+ * Optional upgrade callback for premium features
48
+ * Called when user wants to upgrade from onboarding
49
+ */
50
+ onUpgrade?: () => void;
51
+
52
+ /**
53
+ * Show paywall modal on onboarding completion (default: false)
54
+ * When true, shows premium paywall before completing onboarding
55
+ */
56
+ showPaywallOnComplete?: boolean;
57
+
58
+ /**
59
+ * Theme colors for the onboarding (Optional - will use design tokens if not provided)
60
+ */
61
+ themeColors?: OnboardingColors;
62
+ }
63
+
64
+ export const OnboardingScreen = ({
65
+ slides,
66
+ onComplete,
67
+ onSkip,
68
+ skipButtonText,
69
+ nextButtonText,
70
+ getStartedButtonText,
71
+ showSkipButton = true,
72
+ showBackButton = true,
73
+ showProgressBar = true,
74
+ showDots = true,
75
+ showProgressText = true,
76
+ storageKey,
77
+ autoComplete: _autoComplete = false,
78
+ renderHeader,
79
+ renderFooter,
80
+ renderSlide,
81
+ onUpgrade,
82
+ showPaywallOnComplete = false,
83
+ useCustomBackground: globalUseCustomBackground = false,
84
+ themeVariant = "default",
85
+ themeColors: providedThemeColors,
86
+ }: OnboardingScreenProps) => {
87
+ if (__DEV__) {
88
+ console.log("[OnboardingScreen] Rendering with slides:", slides?.length);
89
+ }
90
+
91
+ const tokens = useAppDesignTokens();
92
+
93
+ const themeColors = useMemo(
94
+ () =>
95
+ providedThemeColors ?? {
96
+ iconColor: tokens.colors.primary,
97
+ textColor: tokens.colors.textPrimary,
98
+ subTextColor: tokens.colors.textSecondary,
99
+ buttonBg: tokens.colors.primary,
100
+ buttonTextColor: tokens.colors.onPrimary,
101
+ progressBarBg: tokens.colors.surfaceSecondary,
102
+ progressFillColor: tokens.colors.primary,
103
+ dotColor: tokens.colors.surfaceSecondary,
104
+ activeDotColor: tokens.colors.primary,
105
+ progressTextColor: tokens.colors.textSecondary,
106
+ headerButtonBg: tokens.colors.surface,
107
+ headerButtonBorder: tokens.colors.borderLight,
108
+ iconBg: tokens.colors.surfaceSecondary,
109
+ iconBorder: tokens.colors.borderLight,
110
+ errorColor: tokens.colors.error,
111
+ featureItemBg: tokens.colors.surfaceSecondary,
112
+ },
113
+ [providedThemeColors, tokens]
114
+ );
115
+
116
+ const {
117
+ filteredSlides,
118
+ currentSlide,
119
+ currentIndex,
120
+ isFirstSlide,
121
+ isLastSlide,
122
+ currentAnswer,
123
+ isAnswerValid,
124
+ useCustomBackground,
125
+ containerStyle,
126
+ handleNext,
127
+ handlePrevious,
128
+ handleSkip,
129
+ setCurrentAnswer,
130
+ } = useOnboardingScreenState({
131
+ slides,
132
+ storageKey,
133
+ onComplete,
134
+ onSkip,
135
+ globalUseCustomBackground,
136
+ });
137
+
138
+ if (__DEV__) {
139
+ console.log("[OnboardingScreen] filteredSlides:", filteredSlides?.length);
140
+ }
141
+
142
+ // Early return if no slides - prevents rendering empty/broken screen
143
+ if (filteredSlides.length === 0) {
144
+ if (__DEV__) {
145
+ console.log("[OnboardingScreen] No slides, returning null");
146
+ }
147
+ return null;
148
+ }
149
+
150
+ return (
151
+ <OnboardingProvider useCustomBackground={useCustomBackground} colors={themeColors}>
152
+ <OnboardingScreenContent
153
+ containerStyle={[styles.container, containerStyle]}
154
+ useCustomBackground={useCustomBackground}
155
+ currentSlide={currentSlide}
156
+ isFirstSlide={isFirstSlide}
157
+ isLastSlide={isLastSlide}
158
+ currentIndex={currentIndex}
159
+ totalSlides={filteredSlides.length}
160
+ currentAnswer={currentAnswer}
161
+ isAnswerValid={isAnswerValid}
162
+ showBackButton={showBackButton}
163
+ showSkipButton={showSkipButton}
164
+ showProgressBar={showProgressBar}
165
+ showDots={showDots}
166
+ showProgressText={showProgressText}
167
+ skipButtonText={skipButtonText}
168
+ nextButtonText={nextButtonText}
169
+ getStartedButtonText={getStartedButtonText}
170
+ onBack={handlePrevious}
171
+ onSkip={handleSkip}
172
+ onNext={handleNext}
173
+ onAnswerChange={setCurrentAnswer}
174
+ renderHeader={renderHeader}
175
+ renderFooter={renderFooter}
176
+ renderSlide={renderSlide}
177
+ onUpgrade={onUpgrade}
178
+ showPaywallOnComplete={showPaywallOnComplete}
179
+ variant={themeVariant}
180
+ />
181
+ </OnboardingProvider>
182
+ );
183
+ };
184
+
185
+ const styles = StyleSheet.create({
186
+ container: {
187
+ flex: 1,
188
+ },
189
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Onboarding Screen Content Props
3
+ */
4
+
5
+ import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
6
+
7
+ export interface OnboardingScreenContentProps {
8
+ containerStyle?: any;
9
+ useCustomBackground: boolean;
10
+ currentSlide: OnboardingSlide | undefined;
11
+ isFirstSlide: boolean;
12
+ isLastSlide: boolean;
13
+ currentIndex: number;
14
+ totalSlides: number;
15
+ currentAnswer: any;
16
+ isAnswerValid: boolean;
17
+ showBackButton: boolean;
18
+ showSkipButton: boolean;
19
+ showProgressBar: boolean;
20
+ showDots: boolean;
21
+ showProgressText: boolean;
22
+ skipButtonText?: string;
23
+ nextButtonText?: string;
24
+ getStartedButtonText?: string;
25
+ onBack: () => void;
26
+ onSkip: () => void;
27
+ onNext: () => void;
28
+ onAnswerChange: (value: any) => void;
29
+ renderHeader?: (props: {
30
+ isFirstSlide: boolean;
31
+ onBack: () => void;
32
+ onSkip: () => void;
33
+ }) => React.ReactNode;
34
+ renderFooter?: (props: {
35
+ currentIndex: number;
36
+ totalSlides: number;
37
+ isLastSlide: boolean;
38
+ onNext: () => void;
39
+ onUpgrade?: () => void;
40
+ showPaywallOnComplete?: boolean;
41
+ }) => React.ReactNode;
42
+ renderSlide?: (slide: OnboardingSlide) => React.ReactNode;
43
+ onUpgrade?: () => void;
44
+ showPaywallOnComplete?: boolean;
45
+ variant?: "default" | "card" | "minimal" | "fullscreen";
46
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Onboarding Theme Types
3
+ */
4
+
5
+ export interface OnboardingColors {
6
+ iconColor: string;
7
+ textColor: string;
8
+ subTextColor: string;
9
+ buttonBg: string;
10
+ buttonTextColor: string;
11
+ progressBarBg: string;
12
+ progressFillColor: string;
13
+ dotColor: string;
14
+ activeDotColor: string;
15
+ progressTextColor: string;
16
+ headerButtonBg: string;
17
+ headerButtonBorder: string;
18
+ iconBg: string;
19
+ iconBorder: string;
20
+ errorColor: string;
21
+ featureItemBg: string;
22
+ }
23
+
24
+ export interface OnboardingTheme {
25
+ colors: OnboardingColors;
26
+ useCustomBackground: boolean;
27
+ }