@umituz/react-native-onboarding 3.3.8 → 3.3.10
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 +1 -1
- package/src/domain/entities/OnboardingSlide.ts +5 -0
- package/src/presentation/components/BackgroundVideo.tsx +8 -17
- package/src/presentation/components/OnboardingFooter.tsx +69 -112
- package/src/presentation/components/OnboardingHeader.tsx +47 -72
- package/src/presentation/components/OnboardingResetSetting.tsx +17 -51
- package/src/presentation/components/OnboardingScreenContent.tsx +2 -2
- package/src/presentation/components/OnboardingSlide.tsx +81 -71
- package/src/presentation/components/QuestionRenderer.tsx +2 -2
- package/src/presentation/components/QuestionSlide.tsx +42 -46
- package/src/presentation/components/QuestionSlideHeader.tsx +52 -67
- package/src/presentation/components/questions/MultipleChoiceQuestion.tsx +26 -55
- package/src/presentation/components/questions/RatingQuestion.tsx +24 -45
- package/src/presentation/components/questions/SingleChoiceQuestion.tsx +23 -49
- package/src/presentation/components/questions/TextInputQuestion.tsx +15 -20
- package/src/presentation/providers/OnboardingThemeProvider.tsx +98 -0
- package/src/presentation/screens/OnboardingScreen.tsx +34 -31
- package/src/presentation/styles/OnboardingSlideStyles.ts +0 -139
- package/src/presentation/styles/QuestionSlideStyles.ts +0 -78
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rating Question Component
|
|
3
|
-
*
|
|
4
|
-
* Star rating or numeric rating selection
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import React from "react";
|
|
8
|
-
import { View,
|
|
9
|
-
import { AtomicIcon } from "@umituz/react-native-design-system";
|
|
10
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
2
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicIcon, AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
11
4
|
import type { OnboardingQuestion } from "../../../domain/entities/OnboardingQuestion";
|
|
12
5
|
|
|
13
6
|
export interface RatingQuestionProps {
|
|
@@ -16,48 +9,34 @@ export interface RatingQuestionProps {
|
|
|
16
9
|
onChange: (value: number) => void;
|
|
17
10
|
}
|
|
18
11
|
|
|
19
|
-
export const RatingQuestion
|
|
12
|
+
export const RatingQuestion = ({
|
|
20
13
|
question,
|
|
21
14
|
value = 0,
|
|
22
15
|
onChange,
|
|
23
|
-
}) => {
|
|
16
|
+
}: RatingQuestionProps) => {
|
|
24
17
|
const tokens = useAppDesignTokens();
|
|
25
|
-
|
|
26
|
-
if (!tokens) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const { validation } = question;
|
|
31
|
-
const max = validation?.max ?? 5;
|
|
32
|
-
|
|
33
|
-
const renderStar = (index: number) => {
|
|
34
|
-
const isFilled = index < value;
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<TouchableOpacity
|
|
38
|
-
key={index}
|
|
39
|
-
onPress={() => onChange(index + 1)}
|
|
40
|
-
activeOpacity={0.7}
|
|
41
|
-
style={styles.star}
|
|
42
|
-
>
|
|
43
|
-
<AtomicIcon
|
|
44
|
-
name={isFilled ? "Star" : "Star"}
|
|
45
|
-
customSize={48}
|
|
46
|
-
customColor={isFilled ? tokens.colors.warning : tokens.colors.textSecondary}
|
|
47
|
-
/>
|
|
48
|
-
</TouchableOpacity>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
18
|
+
const max = question.validation?.max ?? 5;
|
|
51
19
|
|
|
52
20
|
return (
|
|
53
21
|
<View style={styles.container}>
|
|
54
22
|
<View style={styles.stars}>
|
|
55
|
-
{Array.from({ length: max }
|
|
23
|
+
{Array.from({ length: max }).map((_, i) => {
|
|
24
|
+
const isFilled = i < value;
|
|
25
|
+
return (
|
|
26
|
+
<TouchableOpacity key={i} onPress={() => onChange(i + 1)} activeOpacity={0.7} style={styles.star}>
|
|
27
|
+
<AtomicIcon
|
|
28
|
+
name={isFilled ? "star" : "star-outline"}
|
|
29
|
+
customSize={48}
|
|
30
|
+
customColor={isFilled ? tokens.colors.warning : tokens.colors.border}
|
|
31
|
+
/>
|
|
32
|
+
</TouchableOpacity>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
56
35
|
</View>
|
|
57
36
|
{value > 0 && (
|
|
58
|
-
<
|
|
37
|
+
<AtomicText type="headlineSmall" style={[styles.valueText, { color: tokens.colors.textPrimary }]}>
|
|
59
38
|
{value} / {max}
|
|
60
|
-
</
|
|
39
|
+
</AtomicText>
|
|
61
40
|
)}
|
|
62
41
|
</View>
|
|
63
42
|
);
|
|
@@ -70,15 +49,15 @@ const styles = StyleSheet.create({
|
|
|
70
49
|
},
|
|
71
50
|
stars: {
|
|
72
51
|
flexDirection: "row",
|
|
73
|
-
gap:
|
|
74
|
-
marginBottom:
|
|
52
|
+
gap: 12,
|
|
53
|
+
marginBottom: 20,
|
|
75
54
|
},
|
|
76
55
|
star: {
|
|
77
|
-
padding:
|
|
56
|
+
padding: 2,
|
|
78
57
|
},
|
|
79
58
|
valueText: {
|
|
80
|
-
|
|
81
|
-
fontWeight: "600",
|
|
59
|
+
fontWeight: "800",
|
|
82
60
|
},
|
|
83
61
|
});
|
|
84
62
|
|
|
63
|
+
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single Choice Question Component
|
|
3
|
-
*
|
|
4
|
-
* Radio button style question for single selection
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import React from "react";
|
|
8
|
-
import { View,
|
|
9
|
-
import { AtomicIcon } from "@umituz/react-native-design-system";
|
|
10
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
2
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicIcon, AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
11
4
|
import type { OnboardingQuestion, QuestionOption } from "../../../domain/entities/OnboardingQuestion";
|
|
12
5
|
|
|
13
6
|
export interface SingleChoiceQuestionProps {
|
|
@@ -16,57 +9,56 @@ export interface SingleChoiceQuestionProps {
|
|
|
16
9
|
onChange: (value: string) => void;
|
|
17
10
|
}
|
|
18
11
|
|
|
19
|
-
export const SingleChoiceQuestion
|
|
12
|
+
export const SingleChoiceQuestion = ({
|
|
20
13
|
question,
|
|
21
14
|
value,
|
|
22
15
|
onChange,
|
|
23
|
-
}) => {
|
|
16
|
+
}: SingleChoiceQuestionProps) => {
|
|
24
17
|
const tokens = useAppDesignTokens();
|
|
25
18
|
|
|
26
|
-
if (!tokens) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
19
|
const isEmoji = (icon: string) =>
|
|
31
20
|
/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u.test(icon);
|
|
32
21
|
|
|
33
22
|
const renderOption = (option: QuestionOption) => {
|
|
34
23
|
const isSelected = value === option.id;
|
|
24
|
+
const textColor = isSelected ? tokens.colors.textPrimary : tokens.colors.textSecondary;
|
|
35
25
|
|
|
36
26
|
return (
|
|
37
27
|
<TouchableOpacity
|
|
38
28
|
key={option.id}
|
|
39
|
-
style={[
|
|
29
|
+
style={[
|
|
30
|
+
styles.option,
|
|
31
|
+
{
|
|
32
|
+
backgroundColor: isSelected ? tokens.colors.primary + '10' : tokens.colors.surface,
|
|
33
|
+
borderColor: isSelected ? tokens.colors.primary : tokens.colors.border,
|
|
34
|
+
}
|
|
35
|
+
]}
|
|
40
36
|
onPress={() => onChange(option.id)}
|
|
41
37
|
activeOpacity={0.7}
|
|
42
38
|
>
|
|
43
39
|
{option.icon && (
|
|
44
40
|
<View style={styles.optionIcon}>
|
|
45
41
|
{isEmoji(option.icon) ? (
|
|
46
|
-
<
|
|
42
|
+
<AtomicText style={{ fontSize: 24 }}>{option.icon}</AtomicText>
|
|
47
43
|
) : (
|
|
48
44
|
<AtomicIcon
|
|
49
45
|
name={option.icon as any}
|
|
50
46
|
customSize={24}
|
|
51
|
-
customColor={
|
|
47
|
+
customColor={textColor}
|
|
52
48
|
/>
|
|
53
49
|
)}
|
|
54
50
|
</View>
|
|
55
51
|
)}
|
|
56
|
-
<
|
|
57
|
-
styles.optionLabel,
|
|
58
|
-
isSelected && styles.optionLabelSelected,
|
|
59
|
-
{ color: isSelected ? tokens.colors.textPrimary : tokens.colors.textSecondary }
|
|
60
|
-
]}>
|
|
52
|
+
<AtomicText type="bodyLarge" style={[styles.optionLabel, { color: textColor, fontWeight: isSelected ? '700' : '500' }]}>
|
|
61
53
|
{option.label}
|
|
62
|
-
</
|
|
54
|
+
</AtomicText>
|
|
63
55
|
<View style={[
|
|
64
56
|
styles.radio,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
{
|
|
58
|
+
borderColor: isSelected ? tokens.colors.primary : tokens.colors.border,
|
|
59
|
+
borderWidth: isSelected ? 6 : 2,
|
|
60
|
+
}
|
|
61
|
+
]} />
|
|
70
62
|
</TouchableOpacity>
|
|
71
63
|
);
|
|
72
64
|
};
|
|
@@ -86,39 +78,21 @@ const styles = StyleSheet.create({
|
|
|
86
78
|
option: {
|
|
87
79
|
flexDirection: "row",
|
|
88
80
|
alignItems: "center",
|
|
89
|
-
borderRadius:
|
|
81
|
+
borderRadius: 16,
|
|
90
82
|
padding: 16,
|
|
91
83
|
borderWidth: 2,
|
|
92
84
|
},
|
|
93
|
-
optionSelected: {
|
|
94
|
-
borderWidth: 3,
|
|
95
|
-
},
|
|
96
85
|
optionIcon: {
|
|
97
86
|
marginRight: 12,
|
|
98
87
|
},
|
|
99
|
-
emoji: {
|
|
100
|
-
fontSize: 24,
|
|
101
|
-
},
|
|
102
88
|
optionLabel: {
|
|
103
89
|
flex: 1,
|
|
104
|
-
fontSize: 16,
|
|
105
|
-
fontWeight: "500",
|
|
106
|
-
},
|
|
107
|
-
optionLabelSelected: {
|
|
108
|
-
fontWeight: "600",
|
|
109
90
|
},
|
|
110
91
|
radio: {
|
|
111
92
|
width: 24,
|
|
112
93
|
height: 24,
|
|
113
94
|
borderRadius: 12,
|
|
114
|
-
borderWidth: 2,
|
|
115
|
-
alignItems: "center",
|
|
116
|
-
justifyContent: "center",
|
|
117
|
-
},
|
|
118
|
-
radioInner: {
|
|
119
|
-
width: 12,
|
|
120
|
-
height: 12,
|
|
121
|
-
borderRadius: 6,
|
|
122
95
|
},
|
|
123
96
|
});
|
|
124
97
|
|
|
98
|
+
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Text Input Question Component
|
|
3
|
-
*
|
|
4
|
-
* Text input field for free-form text answers
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import React from "react";
|
|
8
|
-
import { View, TextInput, StyleSheet
|
|
9
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
2
|
+
import { View, TextInput, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
10
4
|
import type { OnboardingQuestion } from "../../../domain/entities/OnboardingQuestion";
|
|
11
5
|
|
|
12
6
|
export interface TextInputQuestionProps {
|
|
@@ -15,11 +9,11 @@ export interface TextInputQuestionProps {
|
|
|
15
9
|
onChange: (value: string) => void;
|
|
16
10
|
}
|
|
17
11
|
|
|
18
|
-
export const TextInputQuestion
|
|
12
|
+
export const TextInputQuestion = ({
|
|
19
13
|
question,
|
|
20
14
|
value = "",
|
|
21
15
|
onChange,
|
|
22
|
-
}) => {
|
|
16
|
+
}: TextInputQuestionProps) => {
|
|
23
17
|
const tokens = useAppDesignTokens();
|
|
24
18
|
const { validation } = question;
|
|
25
19
|
|
|
@@ -29,8 +23,8 @@ export const TextInputQuestion: React.FC<TextInputQuestionProps> = ({
|
|
|
29
23
|
style={[
|
|
30
24
|
styles.input,
|
|
31
25
|
{
|
|
32
|
-
backgroundColor: tokens.colors.
|
|
33
|
-
borderColor: tokens.colors.
|
|
26
|
+
backgroundColor: tokens.colors.surface,
|
|
27
|
+
borderColor: tokens.colors.borderLight,
|
|
34
28
|
color: tokens.colors.textPrimary,
|
|
35
29
|
}
|
|
36
30
|
]}
|
|
@@ -39,15 +33,15 @@ export const TextInputQuestion: React.FC<TextInputQuestionProps> = ({
|
|
|
39
33
|
placeholder={question.placeholder || "Type your answer..."}
|
|
40
34
|
placeholderTextColor={tokens.colors.textSecondary}
|
|
41
35
|
maxLength={validation?.maxLength}
|
|
42
|
-
multiline={validation?.maxLength
|
|
43
|
-
numberOfLines={validation?.maxLength
|
|
36
|
+
multiline={(validation?.maxLength ?? 0) > 100}
|
|
37
|
+
numberOfLines={(validation?.maxLength ?? 0) > 100 ? 5 : 1}
|
|
44
38
|
autoCapitalize="sentences"
|
|
45
39
|
autoCorrect={true}
|
|
46
40
|
/>
|
|
47
41
|
{validation?.maxLength && (
|
|
48
|
-
<
|
|
42
|
+
<AtomicText type="labelSmall" style={[styles.charCount, { color: tokens.colors.textSecondary }]}>
|
|
49
43
|
{value.length} / {validation.maxLength}
|
|
50
|
-
</
|
|
44
|
+
</AtomicText>
|
|
51
45
|
)}
|
|
52
46
|
</View>
|
|
53
47
|
);
|
|
@@ -58,15 +52,16 @@ const styles = StyleSheet.create({
|
|
|
58
52
|
width: "100%",
|
|
59
53
|
},
|
|
60
54
|
input: {
|
|
61
|
-
borderRadius:
|
|
62
|
-
padding:
|
|
55
|
+
borderRadius: 16,
|
|
56
|
+
padding: 18,
|
|
63
57
|
fontSize: 16,
|
|
64
58
|
borderWidth: 2,
|
|
65
|
-
minHeight:
|
|
59
|
+
minHeight: 60,
|
|
66
60
|
},
|
|
67
61
|
charCount: {
|
|
68
|
-
fontSize: 13,
|
|
69
62
|
textAlign: "right",
|
|
70
63
|
marginTop: 8,
|
|
64
|
+
fontWeight: "600",
|
|
71
65
|
},
|
|
72
66
|
});
|
|
67
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
3
|
+
|
|
4
|
+
interface OnboardingColors {
|
|
5
|
+
iconColor: string;
|
|
6
|
+
textColor: string;
|
|
7
|
+
subTextColor: string;
|
|
8
|
+
buttonBg: string;
|
|
9
|
+
buttonTextColor: string;
|
|
10
|
+
progressBarBg: string;
|
|
11
|
+
progressFillColor: string;
|
|
12
|
+
dotColor: string;
|
|
13
|
+
activeDotColor: string;
|
|
14
|
+
progressTextColor: string;
|
|
15
|
+
headerButtonBg: string;
|
|
16
|
+
headerButtonBorder: string;
|
|
17
|
+
iconBg: string;
|
|
18
|
+
iconBorder: string;
|
|
19
|
+
errorColor: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface OnboardingThemeValue {
|
|
23
|
+
colors: OnboardingColors;
|
|
24
|
+
useGradient: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const OnboardingTheme = createContext<OnboardingThemeValue | null>(null);
|
|
28
|
+
|
|
29
|
+
export interface OnboardingThemeProviderProps {
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
useGradient?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const OnboardingThemeProvider = ({
|
|
35
|
+
children,
|
|
36
|
+
useGradient = false,
|
|
37
|
+
}: OnboardingThemeProviderProps) => {
|
|
38
|
+
const tokens = useAppDesignTokens();
|
|
39
|
+
|
|
40
|
+
const colors = useMemo<OnboardingColors>(() => {
|
|
41
|
+
if (useGradient) {
|
|
42
|
+
return {
|
|
43
|
+
iconColor: tokens.colors.surface,
|
|
44
|
+
textColor: tokens.colors.surface,
|
|
45
|
+
subTextColor: tokens.colors.surface + 'CC',
|
|
46
|
+
buttonBg: tokens.colors.surface,
|
|
47
|
+
buttonTextColor: tokens.colors.primary,
|
|
48
|
+
progressBarBg: tokens.colors.surface + '30',
|
|
49
|
+
progressFillColor: tokens.colors.surface,
|
|
50
|
+
dotColor: tokens.colors.surface + '60',
|
|
51
|
+
activeDotColor: tokens.colors.surface,
|
|
52
|
+
progressTextColor: tokens.colors.surface + 'CC',
|
|
53
|
+
headerButtonBg: tokens.colors.surface + '30',
|
|
54
|
+
headerButtonBorder: tokens.colors.surface + '50',
|
|
55
|
+
iconBg: tokens.colors.surface + '30',
|
|
56
|
+
iconBorder: tokens.colors.surface + '60',
|
|
57
|
+
errorColor: tokens.colors.surface,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
iconColor: tokens.colors.primary,
|
|
63
|
+
textColor: tokens.colors.textPrimary,
|
|
64
|
+
subTextColor: tokens.colors.textSecondary,
|
|
65
|
+
buttonBg: tokens.colors.primary,
|
|
66
|
+
buttonTextColor: tokens.colors.onPrimary,
|
|
67
|
+
progressBarBg: tokens.colors.borderLight,
|
|
68
|
+
progressFillColor: tokens.colors.primary,
|
|
69
|
+
dotColor: tokens.colors.borderLight,
|
|
70
|
+
activeDotColor: tokens.colors.primary,
|
|
71
|
+
progressTextColor: tokens.colors.textSecondary,
|
|
72
|
+
headerButtonBg: tokens.colors.surface,
|
|
73
|
+
headerButtonBorder: tokens.colors.borderLight,
|
|
74
|
+
iconBg: tokens.colors.primary + '15',
|
|
75
|
+
iconBorder: tokens.colors.primary + '30',
|
|
76
|
+
errorColor: tokens.colors.error,
|
|
77
|
+
};
|
|
78
|
+
}, [tokens, useGradient]);
|
|
79
|
+
|
|
80
|
+
const value = useMemo(
|
|
81
|
+
() => ({ colors, useGradient }),
|
|
82
|
+
[colors, useGradient]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<OnboardingTheme.Provider value={value}>
|
|
87
|
+
{children}
|
|
88
|
+
</OnboardingTheme.Provider>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const useOnboardingTheme = (): OnboardingThemeValue => {
|
|
93
|
+
const theme = useContext(OnboardingTheme);
|
|
94
|
+
if (!theme) {
|
|
95
|
+
throw new Error('useOnboardingTheme must be used within OnboardingThemeProvider');
|
|
96
|
+
}
|
|
97
|
+
return theme;
|
|
98
|
+
};
|
|
@@ -12,6 +12,7 @@ import { StyleSheet } from "react-native";
|
|
|
12
12
|
import type { OnboardingOptions } from "../../domain/entities/OnboardingOptions";
|
|
13
13
|
import { useOnboardingScreenState } from "../hooks/useOnboardingScreenState";
|
|
14
14
|
import { OnboardingScreenContent } from "../components/OnboardingScreenContent";
|
|
15
|
+
import { OnboardingThemeProvider } from "../providers/OnboardingThemeProvider";
|
|
15
16
|
|
|
16
17
|
export interface OnboardingScreenProps extends OnboardingOptions {
|
|
17
18
|
/**
|
|
@@ -53,7 +54,7 @@ export interface OnboardingScreenProps extends OnboardingOptions {
|
|
|
53
54
|
showPaywallOnComplete?: boolean;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
export const OnboardingScreen
|
|
57
|
+
export const OnboardingScreen = ({
|
|
57
58
|
slides,
|
|
58
59
|
onComplete,
|
|
59
60
|
onSkip,
|
|
@@ -74,7 +75,7 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
|
74
75
|
showPaywallOnComplete = false,
|
|
75
76
|
useGradient: globalUseGradient = false,
|
|
76
77
|
themeVariant = "default",
|
|
77
|
-
}) => {
|
|
78
|
+
}: OnboardingScreenProps) => {
|
|
78
79
|
if (__DEV__) {
|
|
79
80
|
console.log("[OnboardingScreen] Rendering with slides:", slides?.length);
|
|
80
81
|
}
|
|
@@ -114,35 +115,37 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
return (
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
118
|
+
<OnboardingThemeProvider useGradient={useGradient}>
|
|
119
|
+
<OnboardingScreenContent
|
|
120
|
+
containerStyle={[styles.container, containerStyle]}
|
|
121
|
+
useGradient={useGradient}
|
|
122
|
+
currentSlide={currentSlide}
|
|
123
|
+
isFirstSlide={isFirstSlide}
|
|
124
|
+
isLastSlide={isLastSlide}
|
|
125
|
+
currentIndex={currentIndex}
|
|
126
|
+
totalSlides={filteredSlides.length}
|
|
127
|
+
currentAnswer={currentAnswer}
|
|
128
|
+
isAnswerValid={isAnswerValid}
|
|
129
|
+
showBackButton={showBackButton}
|
|
130
|
+
showSkipButton={showSkipButton}
|
|
131
|
+
showProgressBar={showProgressBar}
|
|
132
|
+
showDots={showDots}
|
|
133
|
+
showProgressText={showProgressText}
|
|
134
|
+
skipButtonText={skipButtonText}
|
|
135
|
+
nextButtonText={nextButtonText}
|
|
136
|
+
getStartedButtonText={getStartedButtonText}
|
|
137
|
+
onBack={handlePrevious}
|
|
138
|
+
onSkip={handleSkip}
|
|
139
|
+
onNext={handleNext}
|
|
140
|
+
onAnswerChange={setCurrentAnswer}
|
|
141
|
+
renderHeader={renderHeader}
|
|
142
|
+
renderFooter={renderFooter}
|
|
143
|
+
renderSlide={renderSlide}
|
|
144
|
+
onUpgrade={onUpgrade}
|
|
145
|
+
showPaywallOnComplete={showPaywallOnComplete}
|
|
146
|
+
variant={themeVariant}
|
|
147
|
+
/>
|
|
148
|
+
</OnboardingThemeProvider>
|
|
146
149
|
);
|
|
147
150
|
};
|
|
148
151
|
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { StyleSheet } from "react-native";
|
|
2
|
-
import { withAlpha } from "@umituz/react-native-design-system";
|
|
3
|
-
|
|
4
|
-
export const createOnboardingStyles = (
|
|
5
|
-
tokens: any,
|
|
6
|
-
useGradient: boolean,
|
|
7
|
-
variant: "default" | "card" | "minimal" | "fullscreen" = "default"
|
|
8
|
-
) => {
|
|
9
|
-
const isDark = tokens.isDark;
|
|
10
|
-
|
|
11
|
-
// Base styles
|
|
12
|
-
const baseStyles = StyleSheet.create({
|
|
13
|
-
content: {
|
|
14
|
-
flexGrow: 1,
|
|
15
|
-
justifyContent: "center",
|
|
16
|
-
alignItems: "center",
|
|
17
|
-
paddingHorizontal: 24,
|
|
18
|
-
paddingVertical: 40,
|
|
19
|
-
},
|
|
20
|
-
title: {
|
|
21
|
-
...tokens.typography.headingLarge,
|
|
22
|
-
textAlign: "center",
|
|
23
|
-
marginBottom: 16,
|
|
24
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
25
|
-
},
|
|
26
|
-
description: {
|
|
27
|
-
...tokens.typography.bodyLarge,
|
|
28
|
-
textAlign: "center",
|
|
29
|
-
color: useGradient
|
|
30
|
-
? "rgba(255, 255, 255, 0.9)"
|
|
31
|
-
: tokens.colors.textSecondary,
|
|
32
|
-
lineHeight: 24,
|
|
33
|
-
marginBottom: 24,
|
|
34
|
-
maxWidth: 320,
|
|
35
|
-
},
|
|
36
|
-
iconContainer: {
|
|
37
|
-
marginBottom: 40,
|
|
38
|
-
alignItems: "center",
|
|
39
|
-
justifyContent: "center",
|
|
40
|
-
},
|
|
41
|
-
featuresContainer: {
|
|
42
|
-
width: "100%",
|
|
43
|
-
marginTop: 24,
|
|
44
|
-
paddingHorizontal: 16,
|
|
45
|
-
},
|
|
46
|
-
featureItem: {
|
|
47
|
-
flexDirection: "row",
|
|
48
|
-
alignItems: "flex-start",
|
|
49
|
-
marginBottom: 16,
|
|
50
|
-
backgroundColor: useGradient
|
|
51
|
-
? "rgba(255, 255, 255, 0.1)"
|
|
52
|
-
: tokens.colors.surfaceSecondary,
|
|
53
|
-
padding: 12,
|
|
54
|
-
borderRadius: 12,
|
|
55
|
-
},
|
|
56
|
-
featureBullet: {
|
|
57
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.primary,
|
|
58
|
-
fontSize: 18,
|
|
59
|
-
marginRight: 12,
|
|
60
|
-
marginTop: 2,
|
|
61
|
-
},
|
|
62
|
-
featureText: {
|
|
63
|
-
flex: 1,
|
|
64
|
-
...tokens.typography.bodyMedium,
|
|
65
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// Variant specific styles
|
|
70
|
-
if (variant === "card") {
|
|
71
|
-
return StyleSheet.create({
|
|
72
|
-
...baseStyles,
|
|
73
|
-
slideContainer: {
|
|
74
|
-
width: "100%",
|
|
75
|
-
maxWidth: 400,
|
|
76
|
-
backgroundColor: useGradient ? "transparent" : tokens.colors.surface,
|
|
77
|
-
borderRadius: 24,
|
|
78
|
-
padding: 32,
|
|
79
|
-
alignItems: "center",
|
|
80
|
-
// Shadow only for non-gradient or light mode
|
|
81
|
-
...(!useGradient && {
|
|
82
|
-
borderWidth: 1,
|
|
83
|
-
borderColor: tokens.colors.borderLight,
|
|
84
|
-
}),
|
|
85
|
-
},
|
|
86
|
-
iconBox: {
|
|
87
|
-
width: 100,
|
|
88
|
-
height: 100,
|
|
89
|
-
borderRadius: 50,
|
|
90
|
-
backgroundColor: useGradient
|
|
91
|
-
? "rgba(255, 255, 255, 0.2)"
|
|
92
|
-
: withAlpha(tokens.colors.primary, 0.1),
|
|
93
|
-
alignItems: "center",
|
|
94
|
-
justifyContent: "center",
|
|
95
|
-
marginBottom: 24,
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (variant === "minimal") {
|
|
101
|
-
return StyleSheet.create({
|
|
102
|
-
...baseStyles,
|
|
103
|
-
slideContainer: {
|
|
104
|
-
width: "100%",
|
|
105
|
-
alignItems: "center",
|
|
106
|
-
padding: 0,
|
|
107
|
-
},
|
|
108
|
-
title: {
|
|
109
|
-
...baseStyles.title,
|
|
110
|
-
fontSize: 32,
|
|
111
|
-
marginBottom: 24,
|
|
112
|
-
},
|
|
113
|
-
iconBox: {
|
|
114
|
-
marginBottom: 48,
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Default / Fullscreen
|
|
120
|
-
return StyleSheet.create({
|
|
121
|
-
...baseStyles,
|
|
122
|
-
slideContainer: {
|
|
123
|
-
width: "100%",
|
|
124
|
-
alignItems: "center",
|
|
125
|
-
padding: 16,
|
|
126
|
-
},
|
|
127
|
-
iconBox: {
|
|
128
|
-
width: 120,
|
|
129
|
-
height: 120,
|
|
130
|
-
borderRadius: 60,
|
|
131
|
-
backgroundColor: useGradient
|
|
132
|
-
? "rgba(255, 255, 255, 0.15)"
|
|
133
|
-
: tokens.colors.surfaceSecondary,
|
|
134
|
-
alignItems: "center",
|
|
135
|
-
justifyContent: "center",
|
|
136
|
-
marginBottom: 32,
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
};
|