@umituz/react-native-onboarding 3.3.8 → 3.3.9

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.
@@ -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, Text, TouchableOpacity, StyleSheet } from "react-native";
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: React.FC<RatingQuestionProps> = ({
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 }, (_, i) => renderStar(i))}
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
- <Text style={[styles.valueText, { color: tokens.colors.textPrimary }]}>
37
+ <AtomicText type="headlineSmall" style={[styles.valueText, { color: tokens.colors.textPrimary }]}>
59
38
  {value} / {max}
60
- </Text>
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: 8,
74
- marginBottom: 16,
52
+ gap: 12,
53
+ marginBottom: 20,
75
54
  },
76
55
  star: {
77
- padding: 4,
56
+ padding: 2,
78
57
  },
79
58
  valueText: {
80
- fontSize: 18,
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, Text, TouchableOpacity, StyleSheet } from "react-native";
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: React.FC<SingleChoiceQuestionProps> = ({
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={[styles.option, isSelected && styles.optionSelected]}
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
- <Text style={styles.emoji}>{option.icon}</Text>
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={isSelected ? tokens.colors.textPrimary : tokens.colors.textSecondary}
47
+ customColor={textColor}
52
48
  />
53
49
  )}
54
50
  </View>
55
51
  )}
56
- <Text style={[
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
- </Text>
54
+ </AtomicText>
63
55
  <View style={[
64
56
  styles.radio,
65
- isSelected && { borderWidth: 3 },
66
- { borderColor: isSelected ? tokens.colors.primary : tokens.colors.border }
67
- ]}>
68
- {isSelected && <View style={[styles.radioInner, { backgroundColor: tokens.colors.textPrimary }]} />}
69
- </View>
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: 12,
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, Text } from "react-native";
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: React.FC<TextInputQuestionProps> = ({
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.backgroundSecondary,
33
- borderColor: tokens.colors.border,
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 ? validation.maxLength > 100 : false}
43
- numberOfLines={validation?.maxLength && validation.maxLength > 100 ? 4 : 1}
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
- <Text style={[styles.charCount, { color: tokens.colors.textSecondary }]}>
42
+ <AtomicText type="labelSmall" style={[styles.charCount, { color: tokens.colors.textSecondary }]}>
49
43
  {value.length} / {validation.maxLength}
50
- </Text>
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: 12,
62
- padding: 16,
55
+ borderRadius: 16,
56
+ padding: 18,
63
57
  fontSize: 16,
64
58
  borderWidth: 2,
65
- minHeight: 56,
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
+
@@ -53,7 +53,7 @@ export interface OnboardingScreenProps extends OnboardingOptions {
53
53
  showPaywallOnComplete?: boolean;
54
54
  }
55
55
 
56
- export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
56
+ export const OnboardingScreen = ({
57
57
  slides,
58
58
  onComplete,
59
59
  onSkip,
@@ -74,7 +74,7 @@ export const OnboardingScreen: React.FC<OnboardingScreenProps> = ({
74
74
  showPaywallOnComplete = false,
75
75
  useGradient: globalUseGradient = false,
76
76
  themeVariant = "default",
77
- }) => {
77
+ }: OnboardingScreenProps) => {
78
78
  if (__DEV__) {
79
79
  console.log("[OnboardingScreen] Rendering with slides:", slides?.length);
80
80
  }
@@ -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
- };
@@ -1,78 +0,0 @@
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
- };