@umituz/react-native-design-system 4.25.67 → 4.25.69
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/onboarding/infrastructure/services/ValidationManager.ts +40 -1
- package/src/onboarding/presentation/components/QuestionRenderer.tsx +9 -0
- package/src/onboarding/presentation/components/QuestionSlideHeader.tsx +15 -13
- package/src/onboarding/presentation/components/questions/DateQuestion.tsx +58 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "4.25.
|
|
3
|
+
"version": "4.25.69",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -37,13 +37,15 @@ export class ValidationManager {
|
|
|
37
37
|
return this.validateMultipleChoice(answer, validation);
|
|
38
38
|
case "text_input":
|
|
39
39
|
return this.validateTextInput(answer, validation);
|
|
40
|
+
case "date":
|
|
41
|
+
return this.validateDate(answer, validation);
|
|
40
42
|
case "rating":
|
|
41
43
|
return this.validateNumeric(answer, validation);
|
|
42
44
|
default:
|
|
43
45
|
break;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
// Custom validator
|
|
48
|
+
// Custom validator (for types not covered by switch, e.g. default)
|
|
47
49
|
if (validation.customValidator) {
|
|
48
50
|
const customResult = validation.customValidator(answer);
|
|
49
51
|
return customResult === true;
|
|
@@ -85,10 +87,20 @@ export class ValidationManager {
|
|
|
85
87
|
): boolean {
|
|
86
88
|
if (!validation) return true;
|
|
87
89
|
|
|
90
|
+
// If no answer yet (undefined/null), valid only when not required
|
|
91
|
+
if (answer === undefined || answer === null) {
|
|
92
|
+
return !validation.required;
|
|
93
|
+
}
|
|
94
|
+
|
|
88
95
|
if (typeof answer !== "string") {
|
|
89
96
|
return false;
|
|
90
97
|
}
|
|
91
98
|
|
|
99
|
+
// Empty string: valid only when not required
|
|
100
|
+
if (!answer.trim() && validation.required) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
92
104
|
if (validation.minLength && answer.length < validation.minLength) {
|
|
93
105
|
return false;
|
|
94
106
|
}
|
|
@@ -97,9 +109,36 @@ export class ValidationManager {
|
|
|
97
109
|
return false;
|
|
98
110
|
}
|
|
99
111
|
|
|
112
|
+
// Run custom validator if provided
|
|
113
|
+
if (validation.customValidator) {
|
|
114
|
+
const customResult = validation.customValidator(answer);
|
|
115
|
+
return customResult === true;
|
|
116
|
+
}
|
|
117
|
+
|
|
100
118
|
return true;
|
|
101
119
|
}
|
|
102
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Validate date answer (ISO string from date picker)
|
|
123
|
+
*/
|
|
124
|
+
private static validateDate(
|
|
125
|
+
answer: OnboardingAnswerValue,
|
|
126
|
+
validation: OnboardingQuestion["validation"],
|
|
127
|
+
): boolean {
|
|
128
|
+
if (!validation) return true;
|
|
129
|
+
|
|
130
|
+
if (answer === undefined || answer === null) {
|
|
131
|
+
return !validation.required;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof answer !== "string" || !answer) {
|
|
135
|
+
return !validation.required;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const d = new Date(answer);
|
|
139
|
+
return !isNaN(d.getTime());
|
|
140
|
+
}
|
|
141
|
+
|
|
103
142
|
/**
|
|
104
143
|
* Validate numeric answer (rating)
|
|
105
144
|
*/
|
|
@@ -9,6 +9,7 @@ import { SingleChoiceQuestion } from "./questions/SingleChoiceQuestion";
|
|
|
9
9
|
import { MultipleChoiceQuestion } from "./questions/MultipleChoiceQuestion";
|
|
10
10
|
import { TextInputQuestion } from "./questions/TextInputQuestion";
|
|
11
11
|
import { RatingQuestion } from "./questions/RatingQuestion";
|
|
12
|
+
import { DateQuestion } from "./questions/DateQuestion";
|
|
12
13
|
|
|
13
14
|
export interface QuestionRendererProps {
|
|
14
15
|
question: OnboardingQuestion;
|
|
@@ -54,6 +55,14 @@ export const QuestionRenderer = ({
|
|
|
54
55
|
onChange={onChange as (value: number) => void}
|
|
55
56
|
/>
|
|
56
57
|
);
|
|
58
|
+
case "date":
|
|
59
|
+
return (
|
|
60
|
+
<DateQuestion
|
|
61
|
+
question={question}
|
|
62
|
+
value={value as string | undefined}
|
|
63
|
+
onChange={onChange as (value: string) => void}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
57
66
|
default:
|
|
58
67
|
return null;
|
|
59
68
|
}
|
|
@@ -16,19 +16,21 @@ export const QuestionSlideHeader = ({ slide }: QuestionSlideHeaderProps) => {
|
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<View style={styles.container}>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
{!slide.hideIcon && (
|
|
20
|
+
<View style={[
|
|
21
|
+
styles.iconContainer,
|
|
22
|
+
{
|
|
23
|
+
backgroundColor: colors.iconBg,
|
|
24
|
+
borderColor: colors.iconBorder,
|
|
25
|
+
}
|
|
26
|
+
]}>
|
|
27
|
+
{isEmoji ? (
|
|
28
|
+
<AtomicText style={{ fontSize: 48 }}>{slide.icon}</AtomicText>
|
|
29
|
+
) : (
|
|
30
|
+
<AtomicIcon name={slide.icon} customSize={48} customColor={colors.textColor} />
|
|
31
|
+
)}
|
|
32
|
+
</View>
|
|
33
|
+
)}
|
|
32
34
|
|
|
33
35
|
<AtomicText type="headlineMedium" style={[styles.title, { color: colors.textColor }]}>
|
|
34
36
|
{slide.title}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicDatePicker } from "../../../../atoms/AtomicDatePicker";
|
|
4
|
+
import { useOnboardingProvider } from "../../providers/OnboardingProvider";
|
|
5
|
+
import type { OnboardingQuestion } from "../../../domain/entities/OnboardingQuestion";
|
|
6
|
+
|
|
7
|
+
export interface DateQuestionProps {
|
|
8
|
+
question: OnboardingQuestion;
|
|
9
|
+
value: string | undefined;
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const DateQuestion = ({
|
|
14
|
+
question,
|
|
15
|
+
value,
|
|
16
|
+
onChange,
|
|
17
|
+
}: DateQuestionProps) => {
|
|
18
|
+
const {
|
|
19
|
+
theme: { colors },
|
|
20
|
+
} = useOnboardingProvider();
|
|
21
|
+
|
|
22
|
+
const dateValue = value ? new Date(value) : null;
|
|
23
|
+
|
|
24
|
+
const handleChange = useCallback(
|
|
25
|
+
(date: Date) => {
|
|
26
|
+
onChange(date.toISOString());
|
|
27
|
+
},
|
|
28
|
+
[onChange],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View style={styles.container}>
|
|
33
|
+
<AtomicDatePicker
|
|
34
|
+
value={dateValue}
|
|
35
|
+
onChange={handleChange}
|
|
36
|
+
placeholder={question.placeholder}
|
|
37
|
+
maximumDate={new Date()}
|
|
38
|
+
style={[
|
|
39
|
+
styles.picker,
|
|
40
|
+
{
|
|
41
|
+
borderColor: dateValue ? colors.iconColor : colors.headerButtonBorder,
|
|
42
|
+
},
|
|
43
|
+
]}
|
|
44
|
+
/>
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const styles = StyleSheet.create({
|
|
50
|
+
container: {
|
|
51
|
+
width: "100%",
|
|
52
|
+
},
|
|
53
|
+
picker: {
|
|
54
|
+
borderRadius: 16,
|
|
55
|
+
borderWidth: 2,
|
|
56
|
+
overflow: "hidden",
|
|
57
|
+
},
|
|
58
|
+
});
|