@umituz/react-native-onboarding 3.2.0 → 3.2.1
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 +2 -2
- package/src/domain/entities/OnboardingOptions.ts +9 -0
- package/src/domain/entities/OnboardingTheme.ts +6 -0
- package/src/presentation/components/OnboardingScreenContent.tsx +8 -1
- package/src/presentation/components/OnboardingSlide.tsx +45 -108
- package/src/presentation/components/QuestionSlide.tsx +13 -48
- package/src/presentation/screens/OnboardingScreen.tsx +2 -0
- package/src/presentation/styles/OnboardingSlideStyles.ts +144 -0
- package/src/presentation/styles/QuestionSlideStyles.ts +78 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-onboarding",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "Advanced onboarding flow for React Native apps with personalization questions, theme-aware colors, animations, and customizable slides. SOLID, DRY, KISS principles applied.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -68,4 +68,4 @@
|
|
|
68
68
|
"README.md",
|
|
69
69
|
"LICENSE"
|
|
70
70
|
]
|
|
71
|
-
}
|
|
71
|
+
}
|
|
@@ -91,5 +91,14 @@ export interface OnboardingOptions {
|
|
|
91
91
|
* When true, all slides will use gradient backgrounds if available
|
|
92
92
|
*/
|
|
93
93
|
useGradient?: 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";
|
|
94
103
|
}
|
|
95
104
|
|
|
@@ -51,6 +51,7 @@ export interface OnboardingScreenContentProps {
|
|
|
51
51
|
renderSlide?: (slide: OnboardingSlide) => React.ReactNode;
|
|
52
52
|
onUpgrade?: () => void;
|
|
53
53
|
showPaywallOnComplete?: boolean;
|
|
54
|
+
variant?: "default" | "card" | "minimal" | "fullscreen";
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export const OnboardingScreenContent: React.FC<OnboardingScreenContentProps> = ({
|
|
@@ -80,6 +81,7 @@ export const OnboardingScreenContent: React.FC<OnboardingScreenContentProps> = (
|
|
|
80
81
|
renderSlide,
|
|
81
82
|
onUpgrade,
|
|
82
83
|
showPaywallOnComplete,
|
|
84
|
+
variant = "default",
|
|
83
85
|
}) => {
|
|
84
86
|
const { themeMode } = useTheme();
|
|
85
87
|
|
|
@@ -120,9 +122,14 @@ export const OnboardingScreenContent: React.FC<OnboardingScreenContentProps> = (
|
|
|
120
122
|
value={currentAnswer}
|
|
121
123
|
onChange={onAnswerChange}
|
|
122
124
|
useGradient={useGradient}
|
|
125
|
+
variant={variant}
|
|
123
126
|
/>
|
|
124
127
|
) : (
|
|
125
|
-
<OnboardingSlideComponent
|
|
128
|
+
<OnboardingSlideComponent
|
|
129
|
+
slide={currentSlide}
|
|
130
|
+
useGradient={useGradient}
|
|
131
|
+
variant={variant}
|
|
132
|
+
/>
|
|
126
133
|
))}
|
|
127
134
|
{renderFooter ? (
|
|
128
135
|
renderFooter({
|
|
@@ -2,37 +2,56 @@
|
|
|
2
2
|
* Onboarding Slide Component
|
|
3
3
|
*
|
|
4
4
|
* Displays a single onboarding slide with icon, title, and description
|
|
5
|
-
*
|
|
5
|
+
* Supports multiple variants (default, card, minimal) and dark mode
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useMemo } from "react";
|
|
9
|
-
import { View, Text,
|
|
9
|
+
import { View, Text, ScrollView } from "react-native";
|
|
10
10
|
import { AtomicIcon } from "@umituz/react-native-design-system-atoms";
|
|
11
|
-
import { useAppDesignTokens
|
|
11
|
+
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
12
12
|
import type { OnboardingSlide as OnboardingSlideType } from "../../domain/entities/OnboardingSlide";
|
|
13
|
+
import type { OnboardingThemeVariant } from "../../domain/entities/OnboardingTheme";
|
|
14
|
+
import { createOnboardingStyles } from "../styles/OnboardingSlideStyles";
|
|
13
15
|
|
|
14
16
|
export interface OnboardingSlideProps {
|
|
15
17
|
slide: OnboardingSlideType;
|
|
16
18
|
useGradient?: boolean;
|
|
19
|
+
variant?: OnboardingThemeVariant;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
const EMOJI_REGEX = /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u;
|
|
20
23
|
|
|
21
|
-
export const OnboardingSlide: React.FC<OnboardingSlideProps> = ({
|
|
24
|
+
export const OnboardingSlide: React.FC<OnboardingSlideProps> = ({
|
|
25
|
+
slide,
|
|
26
|
+
useGradient = false,
|
|
27
|
+
variant = "default"
|
|
28
|
+
}) => {
|
|
22
29
|
const tokens = useAppDesignTokens();
|
|
23
|
-
const styles = useMemo(
|
|
30
|
+
const styles = useMemo(
|
|
31
|
+
() => createOnboardingStyles(tokens, useGradient, variant),
|
|
32
|
+
[tokens, useGradient, variant]
|
|
33
|
+
);
|
|
24
34
|
|
|
25
35
|
const isEmoji = EMOJI_REGEX.test(slide.icon);
|
|
26
|
-
|
|
36
|
+
// Simple check for valid icon name - assuming string and not emoji
|
|
37
|
+
const isValidIconName = !isEmoji && typeof slide.icon === "string" && slide.icon.length > 0;
|
|
38
|
+
|
|
39
|
+
const iconColor = useGradient ? "#FFFFFF" : tokens.colors.primary;
|
|
40
|
+
const iconSize = variant === "minimal" ? 80 : 64;
|
|
27
41
|
|
|
28
|
-
const renderIcon = (
|
|
42
|
+
const renderIcon = () => {
|
|
29
43
|
if (isEmoji) {
|
|
30
|
-
return <Text style={
|
|
44
|
+
return <Text style={{ fontSize: iconSize }}>{slide.icon}</Text>;
|
|
31
45
|
}
|
|
32
46
|
if (isValidIconName) {
|
|
33
|
-
return <AtomicIcon name={slide.icon} customSize={
|
|
47
|
+
return <AtomicIcon name={slide.icon} customSize={iconSize} customColor={iconColor} />;
|
|
34
48
|
}
|
|
35
|
-
|
|
49
|
+
// Fallback if no icon
|
|
50
|
+
if (slide.image) {
|
|
51
|
+
// TODO: Implement image support if needed, for now just placeholder
|
|
52
|
+
return <AtomicIcon name="image" customSize={iconSize} customColor={iconColor} />;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
36
55
|
};
|
|
37
56
|
|
|
38
57
|
const renderFeatures = () => {
|
|
@@ -49,111 +68,29 @@ export const OnboardingSlide: React.FC<OnboardingSlideProps> = ({ slide, useGrad
|
|
|
49
68
|
);
|
|
50
69
|
};
|
|
51
70
|
|
|
52
|
-
const iconColor = useGradient ? "#FFFFFF" : tokens.colors.textPrimary;
|
|
53
|
-
|
|
54
71
|
return (
|
|
55
72
|
<ScrollView
|
|
56
73
|
contentContainerStyle={styles.content}
|
|
57
74
|
showsVerticalScrollIndicator={false}
|
|
75
|
+
bounces={false}
|
|
58
76
|
>
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
<View style={styles.slideContainer}>
|
|
78
|
+
{/* Icon Section */}
|
|
79
|
+
{(isEmoji || isValidIconName) && (
|
|
80
|
+
<View style={styles.iconBox}>
|
|
81
|
+
{renderIcon()}
|
|
82
|
+
</View>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
{/* Text Section */}
|
|
86
|
+
<Text style={styles.title}>{slide.title}</Text>
|
|
87
|
+
<Text style={styles.description}>{slide.description}</Text>
|
|
88
|
+
|
|
89
|
+
{/* Features Section */}
|
|
90
|
+
{renderFeatures()}
|
|
91
|
+
</View>
|
|
74
92
|
</ScrollView>
|
|
75
93
|
);
|
|
76
94
|
};
|
|
77
95
|
|
|
78
|
-
const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>, useGradient: boolean) =>
|
|
79
|
-
StyleSheet.create({
|
|
80
|
-
content: {
|
|
81
|
-
flexGrow: 1,
|
|
82
|
-
justifyContent: "center",
|
|
83
|
-
alignItems: "center",
|
|
84
|
-
paddingHorizontal: 30,
|
|
85
|
-
paddingVertical: 40,
|
|
86
|
-
},
|
|
87
|
-
slideContent: {
|
|
88
|
-
alignItems: "center",
|
|
89
|
-
maxWidth: 400,
|
|
90
|
-
width: "100%",
|
|
91
|
-
backgroundColor: tokens.colors.surface,
|
|
92
|
-
padding: 30,
|
|
93
|
-
borderRadius: 24,
|
|
94
|
-
borderWidth: 1,
|
|
95
|
-
borderColor: tokens.colors.borderLight,
|
|
96
|
-
shadowColor: tokens.colors.textPrimary,
|
|
97
|
-
shadowOffset: {
|
|
98
|
-
width: 0,
|
|
99
|
-
height: 4,
|
|
100
|
-
},
|
|
101
|
-
shadowOpacity: 0.1,
|
|
102
|
-
shadowRadius: 8,
|
|
103
|
-
elevation: 4,
|
|
104
|
-
},
|
|
105
|
-
iconContainer: {
|
|
106
|
-
width: 120,
|
|
107
|
-
height: 120,
|
|
108
|
-
borderRadius: 60,
|
|
109
|
-
backgroundColor: useGradient
|
|
110
|
-
? "rgba(255, 255, 255, 0.25)"
|
|
111
|
-
: withAlpha(tokens.colors.primary, 0.2),
|
|
112
|
-
alignItems: "center",
|
|
113
|
-
justifyContent: "center",
|
|
114
|
-
marginBottom: 40,
|
|
115
|
-
borderWidth: 2,
|
|
116
|
-
borderColor: useGradient
|
|
117
|
-
? "rgba(255, 255, 255, 0.4)"
|
|
118
|
-
: withAlpha(tokens.colors.primary, 0.4),
|
|
119
|
-
},
|
|
120
|
-
icon: {
|
|
121
|
-
fontSize: 60,
|
|
122
|
-
},
|
|
123
|
-
title: {
|
|
124
|
-
fontSize: 28,
|
|
125
|
-
fontWeight: "bold",
|
|
126
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
127
|
-
textAlign: "center",
|
|
128
|
-
marginBottom: 16,
|
|
129
|
-
},
|
|
130
|
-
description: {
|
|
131
|
-
fontSize: 16,
|
|
132
|
-
color: useGradient ? "rgba(255, 255, 255, 0.9)" : tokens.colors.textSecondary,
|
|
133
|
-
textAlign: "center",
|
|
134
|
-
lineHeight: 24,
|
|
135
|
-
marginBottom: 20,
|
|
136
|
-
},
|
|
137
|
-
featuresContainer: {
|
|
138
|
-
width: "100%",
|
|
139
|
-
marginTop: 10,
|
|
140
|
-
},
|
|
141
|
-
featureItem: {
|
|
142
|
-
flexDirection: "row",
|
|
143
|
-
alignItems: "flex-start",
|
|
144
|
-
marginBottom: 12,
|
|
145
|
-
},
|
|
146
|
-
featureBullet: {
|
|
147
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.primary,
|
|
148
|
-
fontSize: 20,
|
|
149
|
-
marginRight: 12,
|
|
150
|
-
marginTop: 2,
|
|
151
|
-
},
|
|
152
|
-
featureText: {
|
|
153
|
-
flex: 1,
|
|
154
|
-
fontSize: 15,
|
|
155
|
-
color: useGradient ? "rgba(255, 255, 255, 0.9)" : tokens.colors.textSecondary,
|
|
156
|
-
lineHeight: 22,
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
96
|
|
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useMemo } from "react";
|
|
7
|
-
import { View, Text,
|
|
7
|
+
import { View, Text, ScrollView } from "react-native";
|
|
8
8
|
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
9
9
|
import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
|
|
10
|
+
import type { OnboardingThemeVariant } from "../../domain/entities/OnboardingTheme";
|
|
10
11
|
import { QuestionSlideHeader } from "./QuestionSlideHeader";
|
|
11
12
|
import { QuestionRenderer } from "./QuestionRenderer";
|
|
13
|
+
import { createQuestionStyles } from "../styles/QuestionSlideStyles";
|
|
12
14
|
|
|
13
15
|
export interface QuestionSlideProps {
|
|
14
16
|
slide: OnboardingSlide;
|
|
15
17
|
value: any;
|
|
16
18
|
onChange: (value: any) => void;
|
|
17
19
|
useGradient?: boolean;
|
|
20
|
+
variant?: OnboardingThemeVariant;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
@@ -22,9 +25,13 @@ export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
|
22
25
|
value,
|
|
23
26
|
onChange,
|
|
24
27
|
useGradient = false,
|
|
28
|
+
variant = "default",
|
|
25
29
|
}) => {
|
|
26
30
|
const tokens = useAppDesignTokens();
|
|
27
|
-
const styles = useMemo(
|
|
31
|
+
const styles = useMemo(
|
|
32
|
+
() => createQuestionStyles(tokens, useGradient, variant),
|
|
33
|
+
[tokens, useGradient, variant]
|
|
34
|
+
);
|
|
28
35
|
const { question } = slide;
|
|
29
36
|
|
|
30
37
|
if (!question) {
|
|
@@ -53,54 +60,12 @@ export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
|
53
60
|
<ScrollView
|
|
54
61
|
contentContainerStyle={styles.content}
|
|
55
62
|
showsVerticalScrollIndicator={false}
|
|
63
|
+
bounces={false}
|
|
56
64
|
>
|
|
57
|
-
{
|
|
58
|
-
content
|
|
59
|
-
|
|
60
|
-
<View style={styles.slideContent}>{content}</View>
|
|
61
|
-
)}
|
|
65
|
+
<View style={styles.slideContainer}>
|
|
66
|
+
{content}
|
|
67
|
+
</View>
|
|
62
68
|
</ScrollView>
|
|
63
69
|
);
|
|
64
70
|
};
|
|
65
71
|
|
|
66
|
-
const getStyles = (
|
|
67
|
-
tokens: ReturnType<typeof useAppDesignTokens>,
|
|
68
|
-
useGradient: boolean,
|
|
69
|
-
) =>
|
|
70
|
-
StyleSheet.create({
|
|
71
|
-
content: {
|
|
72
|
-
flexGrow: 1,
|
|
73
|
-
justifyContent: "center",
|
|
74
|
-
alignItems: "center",
|
|
75
|
-
paddingHorizontal: 30,
|
|
76
|
-
paddingVertical: 20,
|
|
77
|
-
},
|
|
78
|
-
slideContent: {
|
|
79
|
-
alignItems: "center",
|
|
80
|
-
maxWidth: 500,
|
|
81
|
-
width: "100%",
|
|
82
|
-
backgroundColor: tokens.colors.surface,
|
|
83
|
-
padding: 30,
|
|
84
|
-
borderRadius: 24,
|
|
85
|
-
borderWidth: 1,
|
|
86
|
-
borderColor: tokens.colors.borderLight,
|
|
87
|
-
shadowColor: tokens.colors.textPrimary,
|
|
88
|
-
shadowOffset: {
|
|
89
|
-
width: 0,
|
|
90
|
-
height: 4,
|
|
91
|
-
},
|
|
92
|
-
shadowOpacity: 0.1,
|
|
93
|
-
shadowRadius: 8,
|
|
94
|
-
elevation: 4,
|
|
95
|
-
},
|
|
96
|
-
questionContainer: {
|
|
97
|
-
width: "100%",
|
|
98
|
-
marginTop: 8,
|
|
99
|
-
},
|
|
100
|
-
requiredHint: {
|
|
101
|
-
fontSize: 13,
|
|
102
|
-
color: useGradient ? "rgba(255, 255, 255, 0.8)" : tokens.colors.textSecondary,
|
|
103
|
-
fontStyle: "italic",
|
|
104
|
-
marginTop: 12,
|
|
105
|
-
},
|
|
106
|
-
});
|
|
@@ -73,6 +73,7 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
|
73
73
|
onUpgrade,
|
|
74
74
|
showPaywallOnComplete = false,
|
|
75
75
|
useGradient: globalUseGradient = false,
|
|
76
|
+
themeVariant = "default",
|
|
76
77
|
}) => {
|
|
77
78
|
if (__DEV__) {
|
|
78
79
|
console.log("[OnboardingScreen] Rendering with slides:", slides?.length);
|
|
@@ -140,6 +141,7 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
|
140
141
|
renderSlide={renderSlide}
|
|
141
142
|
onUpgrade={onUpgrade}
|
|
142
143
|
showPaywallOnComplete={showPaywallOnComplete}
|
|
144
|
+
variant={themeVariant}
|
|
143
145
|
/>
|
|
144
146
|
);
|
|
145
147
|
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { withAlpha } from "@umituz/react-native-design-system-theme";
|
|
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
|
+
shadowColor: tokens.colors.shadow,
|
|
83
|
+
shadowOffset: { width: 0, height: 4 },
|
|
84
|
+
shadowOpacity: 0.1,
|
|
85
|
+
shadowRadius: 12,
|
|
86
|
+
elevation: 5,
|
|
87
|
+
borderWidth: 1,
|
|
88
|
+
borderColor: tokens.colors.borderLight,
|
|
89
|
+
}),
|
|
90
|
+
},
|
|
91
|
+
iconBox: {
|
|
92
|
+
width: 100,
|
|
93
|
+
height: 100,
|
|
94
|
+
borderRadius: 50,
|
|
95
|
+
backgroundColor: useGradient
|
|
96
|
+
? "rgba(255, 255, 255, 0.2)"
|
|
97
|
+
: withAlpha(tokens.colors.primary, 0.1),
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
justifyContent: "center",
|
|
100
|
+
marginBottom: 24,
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (variant === "minimal") {
|
|
106
|
+
return StyleSheet.create({
|
|
107
|
+
...baseStyles,
|
|
108
|
+
slideContainer: {
|
|
109
|
+
width: "100%",
|
|
110
|
+
alignItems: "center",
|
|
111
|
+
padding: 0,
|
|
112
|
+
},
|
|
113
|
+
title: {
|
|
114
|
+
...baseStyles.title,
|
|
115
|
+
fontSize: 32,
|
|
116
|
+
marginBottom: 24,
|
|
117
|
+
},
|
|
118
|
+
iconBox: {
|
|
119
|
+
marginBottom: 48,
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Default / Fullscreen
|
|
125
|
+
return StyleSheet.create({
|
|
126
|
+
...baseStyles,
|
|
127
|
+
slideContainer: {
|
|
128
|
+
width: "100%",
|
|
129
|
+
alignItems: "center",
|
|
130
|
+
padding: 16,
|
|
131
|
+
},
|
|
132
|
+
iconBox: {
|
|
133
|
+
width: 120,
|
|
134
|
+
height: 120,
|
|
135
|
+
borderRadius: 60,
|
|
136
|
+
backgroundColor: useGradient
|
|
137
|
+
? "rgba(255, 255, 255, 0.15)"
|
|
138
|
+
: tokens.colors.surfaceSecondary,
|
|
139
|
+
alignItems: "center",
|
|
140
|
+
justifyContent: "center",
|
|
141
|
+
marginBottom: 32,
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
|
|
3
|
+
export const createQuestionStyles = (
|
|
4
|
+
tokens: any,
|
|
5
|
+
useGradient: boolean,
|
|
6
|
+
variant: "default" | "card" | "minimal" | "fullscreen" = "default"
|
|
7
|
+
) => {
|
|
8
|
+
// Base styles
|
|
9
|
+
const baseStyles = StyleSheet.create({
|
|
10
|
+
content: {
|
|
11
|
+
flexGrow: 1,
|
|
12
|
+
justifyContent: "center",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
paddingHorizontal: 24,
|
|
15
|
+
paddingVertical: 20,
|
|
16
|
+
},
|
|
17
|
+
questionContainer: {
|
|
18
|
+
width: "100%",
|
|
19
|
+
marginTop: 24,
|
|
20
|
+
},
|
|
21
|
+
requiredHint: {
|
|
22
|
+
fontSize: 13,
|
|
23
|
+
color: useGradient ? "rgba(255, 255, 255, 0.8)" : tokens.colors.textSecondary,
|
|
24
|
+
fontStyle: "italic",
|
|
25
|
+
marginTop: 16,
|
|
26
|
+
textAlign: "left",
|
|
27
|
+
alignSelf: "flex-start",
|
|
28
|
+
marginLeft: 4,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (variant === "card") {
|
|
33
|
+
return StyleSheet.create({
|
|
34
|
+
...baseStyles,
|
|
35
|
+
slideContainer: {
|
|
36
|
+
width: "100%",
|
|
37
|
+
maxWidth: 500,
|
|
38
|
+
backgroundColor: useGradient ? "transparent" : tokens.colors.surface,
|
|
39
|
+
borderRadius: 24,
|
|
40
|
+
padding: 32,
|
|
41
|
+
alignItems: "center",
|
|
42
|
+
...(!useGradient && {
|
|
43
|
+
shadowColor: tokens.colors.shadow,
|
|
44
|
+
shadowOffset: { width: 0, height: 4 },
|
|
45
|
+
shadowOpacity: 0.1,
|
|
46
|
+
shadowRadius: 12,
|
|
47
|
+
elevation: 5,
|
|
48
|
+
borderWidth: 1,
|
|
49
|
+
borderColor: tokens.colors.borderLight,
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (variant === "minimal") {
|
|
56
|
+
return StyleSheet.create({
|
|
57
|
+
...baseStyles,
|
|
58
|
+
slideContainer: {
|
|
59
|
+
width: "100%",
|
|
60
|
+
alignItems: "stretch",
|
|
61
|
+
padding: 0,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Default
|
|
67
|
+
return StyleSheet.create({
|
|
68
|
+
...baseStyles,
|
|
69
|
+
slideContainer: {
|
|
70
|
+
width: "100%",
|
|
71
|
+
maxWidth: 500,
|
|
72
|
+
alignItems: "center",
|
|
73
|
+
padding: 16,
|
|
74
|
+
backgroundColor: useGradient ? "rgba(255, 255, 255, 0.1)" : tokens.colors.surfaceSecondary,
|
|
75
|
+
borderRadius: 16,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
};
|