@umituz/react-native-onboarding 2.7.0 → 2.7.2
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/OnboardingQuestion.ts +6 -0
- package/src/domain/entities/OnboardingSlide.ts +6 -0
- package/src/presentation/components/OnboardingHeader.tsx +14 -8
- package/src/presentation/components/QuestionSlideHeader.tsx +4 -0
- package/src/presentation/components/questions/SingleChoiceQuestion.tsx +1 -1
- package/src/presentation/components/questions/SliderQuestion.tsx +63 -21
- package/src/presentation/components/questions/TextInputQuestion.tsx +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-onboarding",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.2",
|
|
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",
|
|
@@ -27,9 +27,15 @@ export interface QuestionOption {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Display label for the option
|
|
30
|
+
* Can be either a string or an i18n key
|
|
30
31
|
*/
|
|
31
32
|
label: string;
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* i18n key for label (optional, if provided, label will be used as fallback)
|
|
36
|
+
*/
|
|
37
|
+
labelKey?: string;
|
|
38
|
+
|
|
33
39
|
/**
|
|
34
40
|
* Optional icon (emoji or Lucide icon name)
|
|
35
41
|
*/
|
|
@@ -62,9 +62,15 @@ export interface OnboardingSlide {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Optional features list to display
|
|
65
|
+
* Can be either strings or i18n keys
|
|
65
66
|
*/
|
|
66
67
|
features?: string[];
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Optional i18n keys for features (optional, if provided, features will be used as fallback)
|
|
71
|
+
*/
|
|
72
|
+
featuresKeys?: string[];
|
|
73
|
+
|
|
68
74
|
/**
|
|
69
75
|
* Optional question for personalization
|
|
70
76
|
* Only used when type is "question"
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import React, { useMemo } from "react";
|
|
8
8
|
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
|
|
9
|
+
import { ArrowLeft } from "lucide-react-native";
|
|
9
10
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
10
11
|
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
11
12
|
|
|
@@ -34,24 +35,34 @@ export const OnboardingHeader: React.FC<OnboardingHeaderProps> = ({
|
|
|
34
35
|
|
|
35
36
|
const skipText = skipButtonText || t("onboarding.skip", "Skip");
|
|
36
37
|
|
|
38
|
+
const handleBackPress = () => {
|
|
39
|
+
if (!isFirstSlide) {
|
|
40
|
+
onBack();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
37
44
|
return (
|
|
38
45
|
<View style={styles.header}>
|
|
39
46
|
{showBackButton ? (
|
|
40
47
|
<TouchableOpacity
|
|
41
|
-
onPress={
|
|
48
|
+
onPress={handleBackPress}
|
|
42
49
|
disabled={isFirstSlide}
|
|
43
50
|
style={[
|
|
44
51
|
styles.headerButton,
|
|
45
52
|
isFirstSlide && styles.headerButtonDisabled,
|
|
46
53
|
]}
|
|
54
|
+
activeOpacity={0.7}
|
|
47
55
|
>
|
|
48
|
-
<
|
|
56
|
+
<ArrowLeft
|
|
57
|
+
size={20}
|
|
58
|
+
{...({ color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary } as any)}
|
|
59
|
+
/>
|
|
49
60
|
</TouchableOpacity>
|
|
50
61
|
) : (
|
|
51
62
|
<View style={styles.headerButton} />
|
|
52
63
|
)}
|
|
53
64
|
{showSkipButton && (
|
|
54
|
-
<TouchableOpacity onPress={onSkip}>
|
|
65
|
+
<TouchableOpacity onPress={onSkip} activeOpacity={0.7}>
|
|
55
66
|
<Text style={styles.skipText}>{skipText}</Text>
|
|
56
67
|
</TouchableOpacity>
|
|
57
68
|
)}
|
|
@@ -89,11 +100,6 @@ const getStyles = (
|
|
|
89
100
|
headerButtonDisabled: {
|
|
90
101
|
opacity: 0.3,
|
|
91
102
|
},
|
|
92
|
-
headerButtonText: {
|
|
93
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
94
|
-
fontSize: 20,
|
|
95
|
-
fontWeight: "bold",
|
|
96
|
-
},
|
|
97
103
|
skipText: {
|
|
98
104
|
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
99
105
|
fontSize: 16,
|
|
@@ -38,30 +38,62 @@ export const SliderQuestion: React.FC<SliderQuestionProps> = ({
|
|
|
38
38
|
SliderComponent,
|
|
39
39
|
}) => {
|
|
40
40
|
const { min, max } = useMemo(() => getSliderConfig(question), [question]);
|
|
41
|
-
const currentValue = value ?? min;
|
|
41
|
+
const currentValue = value ?? question.defaultValue ?? min;
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
// Error boundary for SliderComponent
|
|
44
|
+
try {
|
|
45
|
+
return (
|
|
46
|
+
<View style={styles.container}>
|
|
47
|
+
<View style={styles.valueContainer}>
|
|
48
|
+
<Text style={styles.valueText}>{currentValue}</Text>
|
|
49
|
+
</View>
|
|
50
|
+
{SliderComponent ? (
|
|
51
|
+
<SliderComponent
|
|
52
|
+
style={styles.slider}
|
|
53
|
+
minimumValue={min}
|
|
54
|
+
maximumValue={max}
|
|
55
|
+
value={currentValue}
|
|
56
|
+
onValueChange={onChange}
|
|
57
|
+
minimumTrackTintColor="#FFFFFF"
|
|
58
|
+
maximumTrackTintColor="rgba(255, 255, 255, 0.3)"
|
|
59
|
+
thumbTintColor="#FFFFFF"
|
|
60
|
+
step={1}
|
|
61
|
+
/>
|
|
62
|
+
) : (
|
|
63
|
+
<View style={styles.errorContainer}>
|
|
64
|
+
<Text style={styles.errorText}>
|
|
65
|
+
Slider component is not available. Please provide SliderComponent prop.
|
|
66
|
+
</Text>
|
|
67
|
+
</View>
|
|
68
|
+
)}
|
|
69
|
+
<View style={styles.labels}>
|
|
70
|
+
<Text style={styles.label}>{min}</Text>
|
|
71
|
+
<Text style={styles.label}>{max}</Text>
|
|
72
|
+
</View>
|
|
47
73
|
</View>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
/* eslint-disable-next-line no-console */
|
|
77
|
+
if (__DEV__) {
|
|
78
|
+
console.error("SliderQuestion render error:", error);
|
|
79
|
+
}
|
|
80
|
+
return (
|
|
81
|
+
<View style={styles.container}>
|
|
82
|
+
<View style={styles.valueContainer}>
|
|
83
|
+
<Text style={styles.valueText}>{currentValue}</Text>
|
|
84
|
+
</View>
|
|
85
|
+
<View style={styles.errorContainer}>
|
|
86
|
+
<Text style={styles.errorText}>
|
|
87
|
+
Unable to render slider. Please check SliderComponent installation.
|
|
88
|
+
</Text>
|
|
89
|
+
</View>
|
|
90
|
+
<View style={styles.labels}>
|
|
91
|
+
<Text style={styles.label}>{min}</Text>
|
|
92
|
+
<Text style={styles.label}>{max}</Text>
|
|
93
|
+
</View>
|
|
62
94
|
</View>
|
|
63
|
-
|
|
64
|
-
|
|
95
|
+
);
|
|
96
|
+
}
|
|
65
97
|
};
|
|
66
98
|
|
|
67
99
|
const styles = StyleSheet.create({
|
|
@@ -92,4 +124,14 @@ const styles = StyleSheet.create({
|
|
|
92
124
|
color: "rgba(255, 255, 255, 0.7)",
|
|
93
125
|
fontWeight: "500",
|
|
94
126
|
},
|
|
127
|
+
errorContainer: {
|
|
128
|
+
padding: 20,
|
|
129
|
+
alignItems: "center",
|
|
130
|
+
marginVertical: 16,
|
|
131
|
+
},
|
|
132
|
+
errorText: {
|
|
133
|
+
color: "#FFFFFF",
|
|
134
|
+
textAlign: "center",
|
|
135
|
+
fontSize: 14,
|
|
136
|
+
},
|
|
95
137
|
});
|