@umituz/react-native-design-system 2.6.128 → 2.8.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/package.json +4 -2
- package/src/exports/onboarding.ts +6 -0
- package/src/index.ts +5 -0
- package/src/molecules/navigation/utils/AppNavigation.ts +53 -28
- package/src/molecules/splash/hooks/useSplashFlow.ts +33 -7
- package/src/onboarding/domain/entities/OnboardingOptions.ts +104 -0
- package/src/onboarding/domain/entities/OnboardingQuestion.ts +165 -0
- package/src/onboarding/domain/entities/OnboardingSlide.ts +152 -0
- package/src/onboarding/domain/entities/OnboardingUserData.ts +43 -0
- package/src/onboarding/hooks/useOnboardingFlow.ts +50 -0
- package/src/onboarding/index.ts +108 -0
- package/src/onboarding/infrastructure/hooks/__tests__/useOnboardingAnswers.test.ts +163 -0
- package/src/onboarding/infrastructure/hooks/__tests__/useOnboardingNavigation.test.ts +121 -0
- package/src/onboarding/infrastructure/hooks/useOnboardingAnswers.ts +69 -0
- package/src/onboarding/infrastructure/hooks/useOnboardingNavigation.ts +75 -0
- package/src/onboarding/infrastructure/services/SlideManager.ts +53 -0
- package/src/onboarding/infrastructure/services/ValidationManager.ts +127 -0
- package/src/onboarding/infrastructure/storage/OnboardingStore.ts +99 -0
- package/src/onboarding/infrastructure/storage/OnboardingStoreActions.ts +50 -0
- package/src/onboarding/infrastructure/storage/OnboardingStoreSelectors.ts +25 -0
- package/src/onboarding/infrastructure/storage/OnboardingStoreState.ts +22 -0
- package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +85 -0
- package/src/onboarding/infrastructure/storage/actions/answerActions.ts +47 -0
- package/src/onboarding/infrastructure/storage/actions/completeAction.ts +45 -0
- package/src/onboarding/infrastructure/storage/actions/index.ts +22 -0
- package/src/onboarding/infrastructure/storage/actions/initializeAction.ts +40 -0
- package/src/onboarding/infrastructure/storage/actions/resetAction.ts +37 -0
- package/src/onboarding/infrastructure/storage/actions/skipAction.ts +46 -0
- package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +60 -0
- package/src/onboarding/infrastructure/utils/arrayUtils.ts +28 -0
- package/src/onboarding/infrastructure/utils/backgroundUtils.ts +38 -0
- package/src/onboarding/infrastructure/utils/layouts/collageLayout.ts +81 -0
- package/src/onboarding/infrastructure/utils/layouts/gridLayouts.ts +78 -0
- package/src/onboarding/infrastructure/utils/layouts/honeycombLayout.ts +36 -0
- package/src/onboarding/infrastructure/utils/layouts/index.ts +12 -0
- package/src/onboarding/infrastructure/utils/layouts/layoutTypes.ts +37 -0
- package/src/onboarding/infrastructure/utils/layouts/masonryLayout.ts +37 -0
- package/src/onboarding/infrastructure/utils/layouts/scatteredLayout.ts +34 -0
- package/src/onboarding/infrastructure/utils/layouts/screenDimensions.ts +11 -0
- package/src/onboarding/infrastructure/utils/layouts/tilesLayout.ts +34 -0
- package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +90 -0
- package/src/onboarding/presentation/components/BackgroundVideo.tsx +24 -0
- package/src/onboarding/presentation/components/BaseSlide.tsx +47 -0
- package/src/onboarding/presentation/components/OnboardingBackground.tsx +91 -0
- package/src/onboarding/presentation/components/OnboardingFooter.tsx +151 -0
- package/src/onboarding/presentation/components/OnboardingHeader.tsx +92 -0
- package/src/onboarding/presentation/components/OnboardingResetSetting.tsx +70 -0
- package/src/onboarding/presentation/components/OnboardingScreenContent.tsx +146 -0
- package/src/onboarding/presentation/components/OnboardingSlide.tsx +124 -0
- package/src/onboarding/presentation/components/QuestionRenderer.tsx +60 -0
- package/src/onboarding/presentation/components/QuestionSlide.tsx +67 -0
- package/src/onboarding/presentation/components/QuestionSlideHeader.tsx +75 -0
- package/src/onboarding/presentation/components/questions/MultipleChoiceQuestion.tsx +74 -0
- package/src/onboarding/presentation/components/questions/QuestionOptionItem.tsx +115 -0
- package/src/onboarding/presentation/components/questions/RatingQuestion.tsx +66 -0
- package/src/onboarding/presentation/components/questions/SingleChoiceQuestion.tsx +117 -0
- package/src/onboarding/presentation/components/questions/TextInputQuestion.tsx +71 -0
- package/src/onboarding/presentation/hooks/__tests__/useOnboardingContainerStyle.test.ts +96 -0
- package/src/onboarding/presentation/hooks/useOnboardingContainerStyle.ts +37 -0
- package/src/onboarding/presentation/hooks/useOnboardingGestures.ts +45 -0
- package/src/onboarding/presentation/hooks/useOnboardingScreenHandlers.ts +114 -0
- package/src/onboarding/presentation/hooks/useOnboardingScreenState.ts +146 -0
- package/src/onboarding/presentation/providers/OnboardingProvider.tsx +51 -0
- package/src/onboarding/presentation/screens/OnboardingScreen.tsx +189 -0
- package/src/onboarding/presentation/types/OnboardingProps.ts +46 -0
- package/src/onboarding/presentation/types/OnboardingTheme.ts +27 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone and
|
|
3
|
+
"version": "2.8.0",
|
|
4
|
+
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, and onboarding utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./image": "./src/image/index.ts",
|
|
23
23
|
"./timezone": "./src/timezone/index.ts",
|
|
24
24
|
"./offline": "./src/offline/index.ts",
|
|
25
|
+
"./onboarding": "./src/onboarding/index.ts",
|
|
25
26
|
"./package.json": "./package.json"
|
|
26
27
|
},
|
|
27
28
|
"scripts": {
|
|
@@ -74,6 +75,7 @@
|
|
|
74
75
|
"expo-network": ">=8.0.0",
|
|
75
76
|
"expo-secure-store": ">=14.0.0",
|
|
76
77
|
"expo-sharing": ">=12.0.0",
|
|
78
|
+
"expo-video": ">=3.0.0",
|
|
77
79
|
"react": ">=19.0.0",
|
|
78
80
|
"react-native": ">=0.81.0",
|
|
79
81
|
"react-native-gesture-handler": ">=2.20.0",
|
package/src/index.ts
CHANGED
|
@@ -101,3 +101,8 @@ export * from './exports/variants';
|
|
|
101
101
|
// UTILITIES
|
|
102
102
|
// =============================================================================
|
|
103
103
|
export * from './exports/utilities';
|
|
104
|
+
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// ONBOARDING EXPORTS
|
|
107
|
+
// =============================================================================
|
|
108
|
+
export * from './exports/onboarding';
|
|
@@ -3,58 +3,47 @@ import { NavigationContainerRef, CommonActions, StackActions } from '@react-navi
|
|
|
3
3
|
export class AppNavigation {
|
|
4
4
|
private static navigationRef: NavigationContainerRef<any> | null = null;
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Set the global navigation reference
|
|
8
|
-
*/
|
|
9
6
|
static setRef(ref: NavigationContainerRef<any> | null): void {
|
|
10
7
|
this.navigationRef = ref;
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
/**
|
|
14
|
-
* Set the global navigation reference (alias for setRef)
|
|
15
11
|
* @deprecated Use setRef instead
|
|
16
12
|
*/
|
|
17
13
|
static setNavigationRef(ref: NavigationContainerRef<any> | null): void {
|
|
18
14
|
this.setRef(ref);
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
/**
|
|
22
|
-
* Get the global navigation reference
|
|
23
|
-
*/
|
|
24
17
|
static getRef(): NavigationContainerRef<any> | null {
|
|
25
18
|
return this.navigationRef;
|
|
26
19
|
}
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
static isReady(): boolean {
|
|
22
|
+
return this.navigationRef?.isReady() ?? false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static canGoBack(): boolean {
|
|
26
|
+
return this.navigationRef?.canGoBack() ?? false;
|
|
27
|
+
}
|
|
28
|
+
|
|
31
29
|
static navigate(name: string, params?: object): void {
|
|
32
30
|
if (this.navigationRef?.isReady()) {
|
|
33
31
|
this.navigationRef.navigate(name, params);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
/**
|
|
38
|
-
* Push a route onto the stack
|
|
39
|
-
*/
|
|
40
35
|
static push(name: string, params?: object): void {
|
|
41
36
|
if (this.navigationRef?.isReady()) {
|
|
42
37
|
this.navigationRef.dispatch(StackActions.push(name, params));
|
|
43
38
|
}
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
/**
|
|
47
|
-
* Go back to the previous screen
|
|
48
|
-
*/
|
|
49
41
|
static goBack(): void {
|
|
50
42
|
if (this.navigationRef?.isReady() && this.navigationRef.canGoBack()) {
|
|
51
43
|
this.navigationRef.goBack();
|
|
52
44
|
}
|
|
53
45
|
}
|
|
54
46
|
|
|
55
|
-
/**
|
|
56
|
-
* Reset the navigation state
|
|
57
|
-
*/
|
|
58
47
|
static reset(name: string, params?: object): void {
|
|
59
48
|
if (this.navigationRef?.isReady()) {
|
|
60
49
|
this.navigationRef.dispatch(
|
|
@@ -66,27 +55,18 @@ export class AppNavigation {
|
|
|
66
55
|
}
|
|
67
56
|
}
|
|
68
57
|
|
|
69
|
-
/**
|
|
70
|
-
* Replace the current route
|
|
71
|
-
*/
|
|
72
58
|
static replace(name: string, params?: object): void {
|
|
73
59
|
if (this.navigationRef?.isReady()) {
|
|
74
60
|
this.navigationRef.dispatch(StackActions.replace(name, params));
|
|
75
61
|
}
|
|
76
62
|
}
|
|
77
63
|
|
|
78
|
-
/**
|
|
79
|
-
* Navigate to a screen in a nested navigator (e.g. Tab > Stack > Screen)
|
|
80
|
-
*/
|
|
81
64
|
static navigateToNested(parentParams: { screen: string; params?: any }): void {
|
|
82
65
|
if (this.navigationRef?.isReady()) {
|
|
83
66
|
this.navigationRef.navigate(parentParams.screen, parentParams.params);
|
|
84
67
|
}
|
|
85
68
|
}
|
|
86
69
|
|
|
87
|
-
/**
|
|
88
|
-
* Navigate to a screen in the parent navigator
|
|
89
|
-
*/
|
|
90
70
|
static navigateToParent(name: string, params?: object): void {
|
|
91
71
|
if (this.navigationRef?.isReady()) {
|
|
92
72
|
const parent = this.navigationRef.getParent();
|
|
@@ -95,4 +75,49 @@ export class AppNavigation {
|
|
|
95
75
|
}
|
|
96
76
|
}
|
|
97
77
|
}
|
|
78
|
+
|
|
79
|
+
static popToTop(): void {
|
|
80
|
+
if (this.navigationRef?.isReady()) {
|
|
81
|
+
this.navigationRef.dispatch(StackActions.popToTop());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static pop(count: number = 1): void {
|
|
86
|
+
if (this.navigationRef?.isReady()) {
|
|
87
|
+
this.navigationRef.dispatch(StackActions.pop(count));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static getCurrentRoute(): string | undefined {
|
|
92
|
+
return this.navigationRef?.getCurrentRoute()?.name;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static getCurrentParams<T extends object>(): T | undefined {
|
|
96
|
+
return this.navigationRef?.getCurrentRoute()?.params as T | undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static goToSettings(): void {
|
|
100
|
+
this.navigate('Settings');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static goToProfile(): void {
|
|
104
|
+
this.navigate('Profile');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static goToHome(): void {
|
|
108
|
+
this.navigate('Home');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static openModal(name: string, params?: object): void {
|
|
112
|
+
this.navigateToParent(name, params);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static closeModal(): void {
|
|
116
|
+
if (this.navigationRef?.isReady()) {
|
|
117
|
+
const parent = this.navigationRef.getParent();
|
|
118
|
+
if (parent?.canGoBack()) {
|
|
119
|
+
parent.goBack();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
98
123
|
}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Splash Flow Hook
|
|
3
3
|
* Manages splash screen initialization state
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: Use isAppReady prop instead of duration for real initialization tracking.
|
|
6
|
+
* Artificial delays cause poor UX - the splash should transition as soon as app is ready.
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
9
|
import { useState, useEffect } from 'react';
|
|
7
10
|
import { DeviceEventEmitter } from 'react-native';
|
|
8
11
|
|
|
9
12
|
export interface UseSplashFlowOptions {
|
|
13
|
+
/**
|
|
14
|
+
* External readiness signal from app initialization
|
|
15
|
+
* When true, splash will transition to main content
|
|
16
|
+
*/
|
|
17
|
+
isAppReady?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Use isAppReady instead. Artificial delays cause poor UX.
|
|
20
|
+
* Only use for fallback/timeout scenarios.
|
|
21
|
+
*/
|
|
10
22
|
duration?: number;
|
|
11
23
|
}
|
|
12
24
|
|
|
@@ -15,17 +27,31 @@ export interface UseSplashFlowResult {
|
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
export const useSplashFlow = (options: UseSplashFlowOptions = {}): UseSplashFlowResult => {
|
|
18
|
-
const { duration
|
|
30
|
+
const { isAppReady, duration } = options;
|
|
19
31
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
20
32
|
|
|
21
33
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
// Primary: Use external app ready signal (preferred)
|
|
35
|
+
if (isAppReady !== undefined) {
|
|
36
|
+
if (isAppReady && !isInitialized) {
|
|
37
|
+
setIsInitialized(true);
|
|
38
|
+
DeviceEventEmitter.emit('splash-ready');
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
26
42
|
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
// Fallback: Use duration timer if isAppReady not provided (legacy support)
|
|
44
|
+
if (duration !== undefined) {
|
|
45
|
+
const timer = setTimeout(() => {
|
|
46
|
+
setIsInitialized(true);
|
|
47
|
+
DeviceEventEmitter.emit('splash-ready');
|
|
48
|
+
}, duration);
|
|
49
|
+
|
|
50
|
+
return () => clearTimeout(timer);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return undefined;
|
|
54
|
+
}, [isAppReady, duration, isInitialized]);
|
|
29
55
|
|
|
30
56
|
return { isInitialized };
|
|
31
57
|
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding Options
|
|
3
|
+
*
|
|
4
|
+
* Configuration options for onboarding flow
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { OnboardingSlide } from "./OnboardingSlide";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Onboarding Options
|
|
11
|
+
* Customize the onboarding experience
|
|
12
|
+
*/
|
|
13
|
+
export interface OnboardingOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Array of slides to display
|
|
16
|
+
*/
|
|
17
|
+
slides: OnboardingSlide[];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Callback when onboarding is completed
|
|
21
|
+
*/
|
|
22
|
+
onComplete?: () => void | Promise<void>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Callback when onboarding is skipped
|
|
26
|
+
*/
|
|
27
|
+
onSkip?: () => void | Promise<void>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Custom skip button text (default: "Skip")
|
|
31
|
+
*/
|
|
32
|
+
skipButtonText?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Custom next button text (default: "Next")
|
|
36
|
+
*/
|
|
37
|
+
nextButtonText?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Custom get started button text (default: "Get Started")
|
|
41
|
+
*/
|
|
42
|
+
getStartedButtonText?: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Show skip button (default: true)
|
|
46
|
+
*/
|
|
47
|
+
showSkipButton?: boolean;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Show back button (default: true)
|
|
51
|
+
*/
|
|
52
|
+
showBackButton?: boolean;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Show progress bar (default: true)
|
|
56
|
+
*/
|
|
57
|
+
showProgressBar?: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Show dots indicator (default: true)
|
|
61
|
+
*/
|
|
62
|
+
showDots?: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Show progress text (default: true)
|
|
66
|
+
*/
|
|
67
|
+
showProgressText?: boolean;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Storage key for completion state (default: "@onboarding_completed")
|
|
71
|
+
*/
|
|
72
|
+
storageKey?: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Auto-complete onboarding on last slide (default: false)
|
|
76
|
+
*/
|
|
77
|
+
autoComplete?: boolean;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Callback when user wants to upgrade from onboarding
|
|
81
|
+
*/
|
|
82
|
+
onUpgrade?: () => void;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Show paywall modal on onboarding completion (default: false)
|
|
86
|
+
*/
|
|
87
|
+
showPaywallOnComplete?: boolean;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Use custom background for all slides (default: false)
|
|
91
|
+
* When true, all slides will use custom backgrounds if available
|
|
92
|
+
*/
|
|
93
|
+
useCustomBackground?: boolean;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Visual theme variant (default: "default")
|
|
97
|
+
* - default: Standard layout
|
|
98
|
+
* - card: Content in a card with shadow
|
|
99
|
+
* - minimal: Clean, text-focused layout
|
|
100
|
+
* - fullscreen: Immersive fullscreen layout
|
|
101
|
+
*/
|
|
102
|
+
themeVariant?: "default" | "card" | "minimal" | "fullscreen";
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
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
|
+
| "rating" // Star rating or numeric rating
|
|
15
|
+
| "date" // Date picker
|
|
16
|
+
| "image_picker"; // Image selection from gallery
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Option for single/multiple choice questions
|
|
20
|
+
*/
|
|
21
|
+
export interface QuestionOption {
|
|
22
|
+
/**
|
|
23
|
+
* Unique identifier for the option
|
|
24
|
+
*/
|
|
25
|
+
id: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Display label for the option
|
|
29
|
+
*/
|
|
30
|
+
label: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Optional icon (emoji or Ionicons name)
|
|
34
|
+
*/
|
|
35
|
+
icon?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Optional image URL
|
|
39
|
+
*/
|
|
40
|
+
image?: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Optional value (if different from label)
|
|
44
|
+
*/
|
|
45
|
+
value?: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Type of icon: 'emoji' or 'icon' (default: 'icon')
|
|
49
|
+
*/
|
|
50
|
+
iconType?: 'emoji' | 'icon';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validation rules for questions
|
|
55
|
+
*/
|
|
56
|
+
export interface QuestionValidation {
|
|
57
|
+
/**
|
|
58
|
+
* Is this question required?
|
|
59
|
+
*/
|
|
60
|
+
required?: boolean;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Minimum value (for rating)
|
|
64
|
+
*/
|
|
65
|
+
min?: number;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Maximum value (for rating)
|
|
69
|
+
*/
|
|
70
|
+
max?: number;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Minimum length (for text input)
|
|
74
|
+
*/
|
|
75
|
+
minLength?: number;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Maximum length (for text input)
|
|
79
|
+
*/
|
|
80
|
+
maxLength?: number;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Minimum selections (for multiple choice)
|
|
84
|
+
*/
|
|
85
|
+
minSelections?: number;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Maximum selections (for multiple choice)
|
|
89
|
+
*/
|
|
90
|
+
maxSelections?: number;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Custom validation function
|
|
94
|
+
*/
|
|
95
|
+
customValidator?: (value: any) => boolean | string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Onboarding Question
|
|
100
|
+
* Represents a personalization question in the onboarding flow
|
|
101
|
+
*/
|
|
102
|
+
export interface OnboardingQuestion {
|
|
103
|
+
/**
|
|
104
|
+
* Unique identifier for the question
|
|
105
|
+
*/
|
|
106
|
+
id: string;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Question type
|
|
110
|
+
*/
|
|
111
|
+
type: QuestionType;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Question text
|
|
115
|
+
*/
|
|
116
|
+
question: string;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Optional subtitle/description
|
|
120
|
+
*/
|
|
121
|
+
subtitle?: string;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Optional placeholder text (for text input)
|
|
125
|
+
*/
|
|
126
|
+
placeholder?: string;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Options for single/multiple choice questions
|
|
130
|
+
*/
|
|
131
|
+
options?: QuestionOption[];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Validation rules
|
|
135
|
+
*/
|
|
136
|
+
validation?: QuestionValidation;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Default value
|
|
140
|
+
*/
|
|
141
|
+
defaultValue?: any;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Storage key for saving the answer
|
|
145
|
+
*/
|
|
146
|
+
storageKey: string;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Optional icon for the question
|
|
150
|
+
*/
|
|
151
|
+
icon?: string;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Type of icon: 'emoji' or 'icon' (default: 'icon')
|
|
155
|
+
*/
|
|
156
|
+
iconType?: 'emoji' | 'icon';
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Skip this question if condition is met
|
|
160
|
+
* @param answers - Previous answers
|
|
161
|
+
* @returns true to skip, false to show
|
|
162
|
+
*/
|
|
163
|
+
skipIf?: (answers: Record<string, any>) => boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding Slide Entity
|
|
3
|
+
*
|
|
4
|
+
* Domain entity representing a single onboarding slide
|
|
5
|
+
*/
|
|
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
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Content position - determines where the text content is positioned
|
|
16
|
+
*/
|
|
17
|
+
export type ContentPosition = "center" | "bottom";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Onboarding Slide
|
|
21
|
+
* Each slide represents one step in the onboarding flow
|
|
22
|
+
*/
|
|
23
|
+
export interface OnboardingSlide {
|
|
24
|
+
/**
|
|
25
|
+
* Unique identifier for the slide
|
|
26
|
+
*/
|
|
27
|
+
id: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Slide type (default: "info")
|
|
31
|
+
*/
|
|
32
|
+
type?: SlideType;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Slide title
|
|
36
|
+
*/
|
|
37
|
+
title: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Slide description/body text
|
|
41
|
+
*/
|
|
42
|
+
description: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Icon to display (emoji or icon name)
|
|
46
|
+
*/
|
|
47
|
+
icon?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Type of icon: 'emoji' or 'icon' (default: 'icon')
|
|
51
|
+
*/
|
|
52
|
+
iconType?: 'emoji' | 'icon';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Hide icon even if provided (default: false)
|
|
56
|
+
*/
|
|
57
|
+
hideIcon?: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Content position: 'center' or 'bottom' (default: 'center')
|
|
61
|
+
*/
|
|
62
|
+
contentPosition?: ContentPosition;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Background color for the slide background (optional)
|
|
66
|
+
* Only used if useCustomBackground is true
|
|
67
|
+
*/
|
|
68
|
+
backgroundColor?: string;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Use custom background color instead of theme defaults (default: false)
|
|
72
|
+
* If true and backgroundColor is provided, it will be used
|
|
73
|
+
*/
|
|
74
|
+
useCustomBackground?: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Optional image URL (alternative to icon)
|
|
78
|
+
*/
|
|
79
|
+
image?: any;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Optional background image (URL or require path)
|
|
83
|
+
* Stretches to fill the screen behind content
|
|
84
|
+
*/
|
|
85
|
+
backgroundImage?: any;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Optional multiple background images (URLs or require paths)
|
|
89
|
+
* Displayed in a collage/grid pattern behind content
|
|
90
|
+
* If provided, takes precedence over single backgroundImage
|
|
91
|
+
*/
|
|
92
|
+
backgroundImages?: any[];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Layout pattern for multiple background images
|
|
96
|
+
* 'grid' - Equal sized grid (auto columns)
|
|
97
|
+
* 'dense' - Dense grid with many small images (6 columns)
|
|
98
|
+
* 'masonry' - Pinterest-style masonry layout
|
|
99
|
+
* 'collage' - Random sizes and positions
|
|
100
|
+
* 'scattered' - Small randomly placed images
|
|
101
|
+
* 'tiles' - Fixed size tiles centered
|
|
102
|
+
* 'honeycomb' - Hexagonal pattern
|
|
103
|
+
* Default: 'grid'
|
|
104
|
+
*/
|
|
105
|
+
backgroundImagesLayout?: 'grid' | 'dense' | 'masonry' | 'collage' | 'scattered' | 'tiles' | 'honeycomb';
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Number of columns for grid-based layouts
|
|
109
|
+
* Only applies to: grid, dense, masonry, tiles
|
|
110
|
+
*/
|
|
111
|
+
backgroundImagesColumns?: number;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Gap between images in pixels
|
|
115
|
+
*/
|
|
116
|
+
backgroundImagesGap?: number;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Border radius for images
|
|
120
|
+
*/
|
|
121
|
+
backgroundImagesBorderRadius?: number;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Optional background video (URL or require path)
|
|
125
|
+
* Plays in loop behind content
|
|
126
|
+
*/
|
|
127
|
+
backgroundVideo?: any;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Opacity of the overlay color on top of background media
|
|
131
|
+
* Range: 0.0 to 1.0 (Default: 0.5)
|
|
132
|
+
*/
|
|
133
|
+
overlayOpacity?: number;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Optional features list to display
|
|
137
|
+
*/
|
|
138
|
+
features?: string[];
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Optional question for personalization
|
|
142
|
+
* Only used when type is "question"
|
|
143
|
+
*/
|
|
144
|
+
question?: OnboardingQuestion;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Skip this slide if condition is met
|
|
148
|
+
* @param answers - Previous answers
|
|
149
|
+
* @returns true to skip, false to show
|
|
150
|
+
*/
|
|
151
|
+
skipIf?: (answers: Record<string, any>) => boolean;
|
|
152
|
+
}
|