@umituz/react-native-onboarding 2.7.3 → 2.8.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 +1 -2
- package/src/domain/entities/OnboardingQuestion.ts +2 -3
- package/src/index.ts +0 -2
- package/src/infrastructure/services/OnboardingValidationService.ts +1 -2
- package/src/presentation/components/OnboardingScreenContent.tsx +0 -13
- package/src/presentation/components/QuestionRenderer.tsx +0 -37
- package/src/presentation/components/QuestionSlide.tsx +0 -13
- package/src/presentation/screens/OnboardingScreen.tsx +0 -19
- package/src/presentation/components/questions/SliderQuestion.tsx +0 -138
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-onboarding",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.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",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"url": "https://github.com/umituz/react-native-onboarding"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@react-native-community/slider": "^4.5.0",
|
|
35
34
|
"@umituz/react-native-storage": "latest",
|
|
36
35
|
"@umituz/react-native-localization": "latest",
|
|
37
36
|
"@umituz/react-native-design-system-theme": "latest",
|
|
@@ -11,7 +11,6 @@ export type QuestionType =
|
|
|
11
11
|
| "single_choice" // Radio buttons - single selection
|
|
12
12
|
| "multiple_choice" // Checkboxes - multiple selections
|
|
13
13
|
| "text_input" // Text input field
|
|
14
|
-
| "slider" // Slider for numeric values
|
|
15
14
|
| "rating" // Star rating or numeric rating
|
|
16
15
|
| "date" // Date picker
|
|
17
16
|
| "image_picker"; // Image selection from gallery
|
|
@@ -56,12 +55,12 @@ export interface QuestionValidation {
|
|
|
56
55
|
required?: boolean;
|
|
57
56
|
|
|
58
57
|
/**
|
|
59
|
-
* Minimum value (for
|
|
58
|
+
* Minimum value (for rating)
|
|
60
59
|
*/
|
|
61
60
|
min?: number;
|
|
62
61
|
|
|
63
62
|
/**
|
|
64
|
-
* Maximum value (for
|
|
63
|
+
* Maximum value (for rating)
|
|
65
64
|
*/
|
|
66
65
|
max?: number;
|
|
67
66
|
|
package/src/index.ts
CHANGED
|
@@ -80,8 +80,6 @@ export { MultipleChoiceQuestion } from "./presentation/components/questions/Mult
|
|
|
80
80
|
export type { MultipleChoiceQuestionProps } from "./presentation/components/questions/MultipleChoiceQuestion";
|
|
81
81
|
export { TextInputQuestion } from "./presentation/components/questions/TextInputQuestion";
|
|
82
82
|
export type { TextInputQuestionProps } from "./presentation/components/questions/TextInputQuestion";
|
|
83
|
-
export { SliderQuestion } from "./presentation/components/questions/SliderQuestion";
|
|
84
|
-
export type { SliderQuestionProps } from "./presentation/components/questions/SliderQuestion";
|
|
85
83
|
export { RatingQuestion } from "./presentation/components/questions/RatingQuestion";
|
|
86
84
|
export type { RatingQuestionProps } from "./presentation/components/questions/RatingQuestion";
|
|
87
85
|
|
|
@@ -37,7 +37,6 @@ export class OnboardingValidationService {
|
|
|
37
37
|
return this.validateMultipleChoice(answer, validation);
|
|
38
38
|
case "text_input":
|
|
39
39
|
return this.validateTextInput(answer, validation);
|
|
40
|
-
case "slider":
|
|
41
40
|
case "rating":
|
|
42
41
|
return this.validateNumeric(answer, validation);
|
|
43
42
|
default:
|
|
@@ -102,7 +101,7 @@ export class OnboardingValidationService {
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
/**
|
|
105
|
-
* Validate numeric answer (
|
|
104
|
+
* Validate numeric answer (rating)
|
|
106
105
|
*/
|
|
107
106
|
private static validateNumeric(
|
|
108
107
|
answer: any,
|
|
@@ -51,17 +51,6 @@ export interface OnboardingScreenContentProps {
|
|
|
51
51
|
renderSlide?: (slide: OnboardingSlide) => React.ReactNode;
|
|
52
52
|
onUpgrade?: () => void;
|
|
53
53
|
showPaywallOnComplete?: boolean;
|
|
54
|
-
SliderComponent?: React.ComponentType<{
|
|
55
|
-
style?: any;
|
|
56
|
-
minimumValue: number;
|
|
57
|
-
maximumValue: number;
|
|
58
|
-
value: number;
|
|
59
|
-
onValueChange: (value: number) => void;
|
|
60
|
-
minimumTrackTintColor?: string;
|
|
61
|
-
maximumTrackTintColor?: string;
|
|
62
|
-
thumbTintColor?: string;
|
|
63
|
-
step?: number;
|
|
64
|
-
}>;
|
|
65
54
|
}
|
|
66
55
|
|
|
67
56
|
export const OnboardingScreenContent: React.FC<OnboardingScreenContentProps> = ({
|
|
@@ -91,7 +80,6 @@ export const OnboardingScreenContent: React.FC<OnboardingScreenContentProps> = (
|
|
|
91
80
|
renderSlide,
|
|
92
81
|
onUpgrade,
|
|
93
82
|
showPaywallOnComplete,
|
|
94
|
-
SliderComponent,
|
|
95
83
|
}) => {
|
|
96
84
|
const { themeMode } = useTheme();
|
|
97
85
|
|
|
@@ -132,7 +120,6 @@ export const OnboardingScreenContent: React.FC<OnboardingScreenContentProps> = (
|
|
|
132
120
|
value={currentAnswer}
|
|
133
121
|
onChange={onAnswerChange}
|
|
134
122
|
useGradient={useGradient}
|
|
135
|
-
SliderComponent={SliderComponent}
|
|
136
123
|
/>
|
|
137
124
|
) : (
|
|
138
125
|
<OnboardingSlideComponent slide={currentSlide} useGradient={useGradient} />
|
|
@@ -4,36 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { View, Text } from "react-native";
|
|
8
7
|
import type { OnboardingQuestion } from "../../domain/entities/OnboardingQuestion";
|
|
9
8
|
import { SingleChoiceQuestion } from "./questions/SingleChoiceQuestion";
|
|
10
9
|
import { MultipleChoiceQuestion } from "./questions/MultipleChoiceQuestion";
|
|
11
10
|
import { TextInputQuestion } from "./questions/TextInputQuestion";
|
|
12
|
-
import { SliderQuestion } from "./questions/SliderQuestion";
|
|
13
11
|
import { RatingQuestion } from "./questions/RatingQuestion";
|
|
14
12
|
|
|
15
13
|
export interface QuestionRendererProps {
|
|
16
14
|
question: OnboardingQuestion;
|
|
17
15
|
value: any;
|
|
18
16
|
onChange: (value: any) => void;
|
|
19
|
-
SliderComponent?: React.ComponentType<{
|
|
20
|
-
style?: any;
|
|
21
|
-
minimumValue: number;
|
|
22
|
-
maximumValue: number;
|
|
23
|
-
value: number;
|
|
24
|
-
onValueChange: (value: number) => void;
|
|
25
|
-
minimumTrackTintColor?: string;
|
|
26
|
-
maximumTrackTintColor?: string;
|
|
27
|
-
thumbTintColor?: string;
|
|
28
|
-
step?: number;
|
|
29
|
-
}>;
|
|
30
17
|
}
|
|
31
18
|
|
|
32
19
|
export const QuestionRenderer: React.FC<QuestionRendererProps> = ({
|
|
33
20
|
question,
|
|
34
21
|
value,
|
|
35
22
|
onChange,
|
|
36
|
-
SliderComponent,
|
|
37
23
|
}) => {
|
|
38
24
|
switch (question.type) {
|
|
39
25
|
case "single_choice":
|
|
@@ -60,29 +46,6 @@ export const QuestionRenderer: React.FC<QuestionRendererProps> = ({
|
|
|
60
46
|
onChange={onChange}
|
|
61
47
|
/>
|
|
62
48
|
);
|
|
63
|
-
case "slider":
|
|
64
|
-
if (!SliderComponent) {
|
|
65
|
-
return (
|
|
66
|
-
<View style={{ padding: 20, alignItems: "center" }}>
|
|
67
|
-
<Text style={{ color: "#FFFFFF", textAlign: "center" }}>
|
|
68
|
-
Slider component is not available. Please provide SliderComponent prop
|
|
69
|
-
to OnboardingScreen.
|
|
70
|
-
</Text>
|
|
71
|
-
<Text style={{ color: "#FFFFFF", textAlign: "center", marginTop: 8, fontSize: 12 }}>
|
|
72
|
-
Example: SliderComponent={"Slider"} where {"Slider"} is imported from
|
|
73
|
-
"@react-native-community/slider"
|
|
74
|
-
</Text>
|
|
75
|
-
</View>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
return (
|
|
79
|
-
<SliderQuestion
|
|
80
|
-
question={question}
|
|
81
|
-
value={value}
|
|
82
|
-
onChange={onChange}
|
|
83
|
-
SliderComponent={SliderComponent}
|
|
84
|
-
/>
|
|
85
|
-
);
|
|
86
49
|
case "rating":
|
|
87
50
|
return (
|
|
88
51
|
<RatingQuestion
|
|
@@ -15,17 +15,6 @@ export interface QuestionSlideProps {
|
|
|
15
15
|
value: any;
|
|
16
16
|
onChange: (value: any) => void;
|
|
17
17
|
useGradient?: boolean;
|
|
18
|
-
SliderComponent?: React.ComponentType<{
|
|
19
|
-
style?: any;
|
|
20
|
-
minimumValue: number;
|
|
21
|
-
maximumValue: number;
|
|
22
|
-
value: number;
|
|
23
|
-
onValueChange: (value: number) => void;
|
|
24
|
-
minimumTrackTintColor?: string;
|
|
25
|
-
maximumTrackTintColor?: string;
|
|
26
|
-
thumbTintColor?: string;
|
|
27
|
-
step?: number;
|
|
28
|
-
}>;
|
|
29
18
|
}
|
|
30
19
|
|
|
31
20
|
export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
@@ -33,7 +22,6 @@ export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
|
33
22
|
value,
|
|
34
23
|
onChange,
|
|
35
24
|
useGradient = false,
|
|
36
|
-
SliderComponent,
|
|
37
25
|
}) => {
|
|
38
26
|
const tokens = useAppDesignTokens();
|
|
39
27
|
const styles = useMemo(() => getStyles(tokens, useGradient), [tokens, useGradient]);
|
|
@@ -52,7 +40,6 @@ export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
|
52
40
|
question={question}
|
|
53
41
|
value={value}
|
|
54
42
|
onChange={onChange}
|
|
55
|
-
SliderComponent={SliderComponent}
|
|
56
43
|
/>
|
|
57
44
|
</View>
|
|
58
45
|
|
|
@@ -51,23 +51,6 @@ export interface OnboardingScreenProps extends OnboardingOptions {
|
|
|
51
51
|
* When true, shows premium paywall before completing onboarding
|
|
52
52
|
*/
|
|
53
53
|
showPaywallOnComplete?: boolean;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Slider component for slider questions
|
|
57
|
-
* Required if using slider question type
|
|
58
|
-
* Import from @react-native-community/slider
|
|
59
|
-
*/
|
|
60
|
-
SliderComponent?: React.ComponentType<{
|
|
61
|
-
style?: any;
|
|
62
|
-
minimumValue: number;
|
|
63
|
-
maximumValue: number;
|
|
64
|
-
value: number;
|
|
65
|
-
onValueChange: (value: number) => void;
|
|
66
|
-
minimumTrackTintColor?: string;
|
|
67
|
-
maximumTrackTintColor?: string;
|
|
68
|
-
thumbTintColor?: string;
|
|
69
|
-
step?: number;
|
|
70
|
-
}>;
|
|
71
54
|
}
|
|
72
55
|
|
|
73
56
|
export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
@@ -90,7 +73,6 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
|
90
73
|
onUpgrade,
|
|
91
74
|
showPaywallOnComplete = false,
|
|
92
75
|
useGradient: globalUseGradient = false,
|
|
93
|
-
SliderComponent,
|
|
94
76
|
}) => {
|
|
95
77
|
const {
|
|
96
78
|
filteredSlides,
|
|
@@ -142,7 +124,6 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
|
|
|
142
124
|
renderSlide={renderSlide}
|
|
143
125
|
onUpgrade={onUpgrade}
|
|
144
126
|
showPaywallOnComplete={showPaywallOnComplete}
|
|
145
|
-
SliderComponent={SliderComponent}
|
|
146
127
|
/>
|
|
147
128
|
);
|
|
148
129
|
};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slider Question Component
|
|
3
|
-
* Single Responsibility: Display slider for numeric value selection
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useMemo } from "react";
|
|
7
|
-
import { View, Text, StyleSheet } from "react-native";
|
|
8
|
-
import type { OnboardingQuestion } from "../../../domain/entities/OnboardingQuestion";
|
|
9
|
-
|
|
10
|
-
export interface SliderQuestionProps {
|
|
11
|
-
question: OnboardingQuestion;
|
|
12
|
-
value: number | undefined;
|
|
13
|
-
onChange: (value: number) => void;
|
|
14
|
-
SliderComponent: React.ComponentType<{
|
|
15
|
-
style?: any;
|
|
16
|
-
minimumValue: number;
|
|
17
|
-
maximumValue: number;
|
|
18
|
-
value: number;
|
|
19
|
-
onValueChange: (value: number) => void;
|
|
20
|
-
minimumTrackTintColor?: string;
|
|
21
|
-
maximumTrackTintColor?: string;
|
|
22
|
-
thumbTintColor?: string;
|
|
23
|
-
step?: number;
|
|
24
|
-
}>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const getSliderConfig = (question: OnboardingQuestion) => {
|
|
28
|
-
const { validation } = question;
|
|
29
|
-
const min = validation?.min ?? 0;
|
|
30
|
-
const max = validation?.max ?? 100;
|
|
31
|
-
return { min, max };
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const SliderQuestion: React.FC<SliderQuestionProps> = ({
|
|
35
|
-
question,
|
|
36
|
-
value,
|
|
37
|
-
onChange,
|
|
38
|
-
SliderComponent,
|
|
39
|
-
}) => {
|
|
40
|
-
const { min, max } = useMemo(() => getSliderConfig(question), [question]);
|
|
41
|
-
const currentValue = value ?? question.defaultValue ?? min;
|
|
42
|
-
const [sliderError, setSliderError] = React.useState<string | null>(null);
|
|
43
|
-
|
|
44
|
-
// Render slider with error boundary
|
|
45
|
-
const renderSlider = () => {
|
|
46
|
-
if (!SliderComponent) {
|
|
47
|
-
return (
|
|
48
|
-
<View style={styles.errorContainer}>
|
|
49
|
-
<Text style={styles.errorText}>
|
|
50
|
-
Slider component is not available. Please provide SliderComponent prop.
|
|
51
|
-
</Text>
|
|
52
|
-
</View>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
return (
|
|
58
|
-
<SliderComponent
|
|
59
|
-
style={styles.slider}
|
|
60
|
-
minimumValue={min}
|
|
61
|
-
maximumValue={max}
|
|
62
|
-
value={currentValue}
|
|
63
|
-
onValueChange={onChange}
|
|
64
|
-
minimumTrackTintColor="#FFFFFF"
|
|
65
|
-
maximumTrackTintColor="rgba(255, 255, 255, 0.3)"
|
|
66
|
-
thumbTintColor="#FFFFFF"
|
|
67
|
-
step={1}
|
|
68
|
-
/>
|
|
69
|
-
);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
/* eslint-disable-next-line no-console */
|
|
72
|
-
if (__DEV__) {
|
|
73
|
-
console.error("[SliderQuestion] Error rendering slider:", error);
|
|
74
|
-
}
|
|
75
|
-
setSliderError("Unable to render slider. Native module may not be linked.");
|
|
76
|
-
return (
|
|
77
|
-
<View style={styles.errorContainer}>
|
|
78
|
-
<Text style={styles.errorText}>
|
|
79
|
-
Unable to render slider. Please ensure @react-native-community/slider is properly installed and linked.
|
|
80
|
-
</Text>
|
|
81
|
-
</View>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<View style={styles.container}>
|
|
88
|
-
<View style={styles.valueContainer}>
|
|
89
|
-
<Text style={styles.valueText}>{currentValue}</Text>
|
|
90
|
-
</View>
|
|
91
|
-
{renderSlider()}
|
|
92
|
-
<View style={styles.labels}>
|
|
93
|
-
<Text style={styles.label}>{min}</Text>
|
|
94
|
-
<Text style={styles.label}>{max}</Text>
|
|
95
|
-
</View>
|
|
96
|
-
</View>
|
|
97
|
-
);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const styles = StyleSheet.create({
|
|
101
|
-
container: {
|
|
102
|
-
width: "100%",
|
|
103
|
-
paddingHorizontal: 8,
|
|
104
|
-
},
|
|
105
|
-
valueContainer: {
|
|
106
|
-
alignItems: "center",
|
|
107
|
-
marginBottom: 16,
|
|
108
|
-
},
|
|
109
|
-
valueText: {
|
|
110
|
-
fontSize: 48,
|
|
111
|
-
fontWeight: "bold",
|
|
112
|
-
color: "#FFFFFF",
|
|
113
|
-
},
|
|
114
|
-
slider: {
|
|
115
|
-
width: "100%",
|
|
116
|
-
height: 40,
|
|
117
|
-
},
|
|
118
|
-
labels: {
|
|
119
|
-
flexDirection: "row",
|
|
120
|
-
justifyContent: "space-between",
|
|
121
|
-
marginTop: 8,
|
|
122
|
-
},
|
|
123
|
-
label: {
|
|
124
|
-
fontSize: 14,
|
|
125
|
-
color: "rgba(255, 255, 255, 0.7)",
|
|
126
|
-
fontWeight: "500",
|
|
127
|
-
},
|
|
128
|
-
errorContainer: {
|
|
129
|
-
padding: 20,
|
|
130
|
-
alignItems: "center",
|
|
131
|
-
marginVertical: 16,
|
|
132
|
-
},
|
|
133
|
-
errorText: {
|
|
134
|
-
color: "#FFFFFF",
|
|
135
|
-
textAlign: "center",
|
|
136
|
-
fontSize: 14,
|
|
137
|
-
},
|
|
138
|
-
});
|