@umituz/react-native-onboarding 2.6.6 → 2.6.7
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-onboarding",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.7",
|
|
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",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Question Renderer Component
|
|
3
|
+
* Single Responsibility: Render appropriate question component based on type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import type { OnboardingQuestion } from "../../domain/entities/OnboardingQuestion";
|
|
8
|
+
import { SingleChoiceQuestion } from "./questions/SingleChoiceQuestion";
|
|
9
|
+
import { MultipleChoiceQuestion } from "./questions/MultipleChoiceQuestion";
|
|
10
|
+
import { TextInputQuestion } from "./questions/TextInputQuestion";
|
|
11
|
+
import { SliderQuestion } from "./questions/SliderQuestion";
|
|
12
|
+
import { RatingQuestion } from "./questions/RatingQuestion";
|
|
13
|
+
|
|
14
|
+
export interface QuestionRendererProps {
|
|
15
|
+
question: OnboardingQuestion;
|
|
16
|
+
value: any;
|
|
17
|
+
onChange: (value: any) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const QuestionRenderer: React.FC<QuestionRendererProps> = ({
|
|
21
|
+
question,
|
|
22
|
+
value,
|
|
23
|
+
onChange,
|
|
24
|
+
}) => {
|
|
25
|
+
switch (question.type) {
|
|
26
|
+
case "single_choice":
|
|
27
|
+
return (
|
|
28
|
+
<SingleChoiceQuestion
|
|
29
|
+
question={question}
|
|
30
|
+
value={value}
|
|
31
|
+
onChange={onChange}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
case "multiple_choice":
|
|
35
|
+
return (
|
|
36
|
+
<MultipleChoiceQuestion
|
|
37
|
+
question={question}
|
|
38
|
+
value={value}
|
|
39
|
+
onChange={onChange}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
case "text_input":
|
|
43
|
+
return (
|
|
44
|
+
<TextInputQuestion
|
|
45
|
+
question={question}
|
|
46
|
+
value={value}
|
|
47
|
+
onChange={onChange}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
case "slider":
|
|
51
|
+
return (
|
|
52
|
+
<SliderQuestion
|
|
53
|
+
question={question}
|
|
54
|
+
value={value}
|
|
55
|
+
onChange={onChange}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
case "rating":
|
|
59
|
+
return (
|
|
60
|
+
<RatingQuestion
|
|
61
|
+
question={question}
|
|
62
|
+
value={value}
|
|
63
|
+
onChange={onChange}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
default:
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Question Slide Component
|
|
3
|
-
*
|
|
4
|
-
* Displays a personalization question slide
|
|
3
|
+
* Single Responsibility: Display question slide with header and question
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import React, { useMemo } from "react";
|
|
8
7
|
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
|
9
|
-
import {
|
|
10
|
-
import { useAppDesignTokens, withAlpha } from "@umituz/react-native-design-system-theme";
|
|
8
|
+
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
11
9
|
import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { TextInputQuestion } from "./questions/TextInputQuestion";
|
|
15
|
-
import { SliderQuestion } from "./questions/SliderQuestion";
|
|
16
|
-
import { RatingQuestion } from "./questions/RatingQuestion";
|
|
10
|
+
import { QuestionSlideHeader } from "./QuestionSlideHeader";
|
|
11
|
+
import { QuestionRenderer } from "./QuestionRenderer";
|
|
17
12
|
|
|
18
13
|
export interface QuestionSlideProps {
|
|
19
14
|
slide: OnboardingSlide;
|
|
@@ -36,55 +31,19 @@ export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
|
36
31
|
return null;
|
|
37
32
|
}
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const content = (
|
|
35
|
+
<>
|
|
36
|
+
<QuestionSlideHeader slide={slide} useGradient={useGradient} />
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
52
|
-
case "multiple_choice":
|
|
53
|
-
return (
|
|
54
|
-
<MultipleChoiceQuestion
|
|
55
|
-
question={question}
|
|
56
|
-
value={value}
|
|
57
|
-
onChange={onChange}
|
|
58
|
-
/>
|
|
59
|
-
);
|
|
60
|
-
case "text_input":
|
|
61
|
-
return (
|
|
62
|
-
<TextInputQuestion
|
|
63
|
-
question={question}
|
|
64
|
-
value={value}
|
|
65
|
-
onChange={onChange}
|
|
66
|
-
/>
|
|
67
|
-
);
|
|
68
|
-
case "slider":
|
|
69
|
-
return (
|
|
70
|
-
<SliderQuestion
|
|
71
|
-
question={question}
|
|
72
|
-
value={value}
|
|
73
|
-
onChange={onChange}
|
|
74
|
-
/>
|
|
75
|
-
);
|
|
76
|
-
case "rating":
|
|
77
|
-
return (
|
|
78
|
-
<RatingQuestion
|
|
79
|
-
question={question}
|
|
80
|
-
value={value}
|
|
81
|
-
onChange={onChange}
|
|
82
|
-
/>
|
|
83
|
-
);
|
|
84
|
-
default:
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
38
|
+
<View style={styles.questionContainer}>
|
|
39
|
+
<QuestionRenderer question={question} value={value} onChange={onChange} />
|
|
40
|
+
</View>
|
|
41
|
+
|
|
42
|
+
{question.validation?.required && !value && (
|
|
43
|
+
<Text style={styles.requiredHint}>* This field is required</Text>
|
|
44
|
+
)}
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
88
47
|
|
|
89
48
|
return (
|
|
90
49
|
<ScrollView
|
|
@@ -92,75 +51,18 @@ export const QuestionSlide: React.FC<QuestionSlideProps> = ({
|
|
|
92
51
|
showsVerticalScrollIndicator={false}
|
|
93
52
|
>
|
|
94
53
|
{useGradient ? (
|
|
95
|
-
|
|
96
|
-
<>
|
|
97
|
-
{/* Icon */}
|
|
98
|
-
<View style={styles.iconContainer}>
|
|
99
|
-
{isEmoji ? (
|
|
100
|
-
<Text style={styles.icon}>{slide.icon}</Text>
|
|
101
|
-
) : (
|
|
102
|
-
<AtomicIcon
|
|
103
|
-
name={slide.icon as any}
|
|
104
|
-
customSize={48}
|
|
105
|
-
customColor="#FFFFFF"
|
|
106
|
-
/>
|
|
107
|
-
)}
|
|
108
|
-
</View>
|
|
109
|
-
|
|
110
|
-
{/* Title */}
|
|
111
|
-
<Text style={styles.title}>{slide.title}</Text>
|
|
112
|
-
|
|
113
|
-
{/* Description */}
|
|
114
|
-
{slide.description && (
|
|
115
|
-
<Text style={styles.description}>{slide.description}</Text>
|
|
116
|
-
)}
|
|
117
|
-
|
|
118
|
-
{/* Question */}
|
|
119
|
-
<View style={styles.questionContainer}>{renderQuestion()}</View>
|
|
120
|
-
|
|
121
|
-
{/* Validation hint */}
|
|
122
|
-
{question.validation?.required && !value && (
|
|
123
|
-
<Text style={styles.requiredHint}>* This field is required</Text>
|
|
124
|
-
)}
|
|
125
|
-
</>
|
|
54
|
+
content
|
|
126
55
|
) : (
|
|
127
|
-
|
|
128
|
-
<View style={styles.slideContent}>
|
|
129
|
-
{/* Icon */}
|
|
130
|
-
<View style={styles.iconContainer}>
|
|
131
|
-
{isEmoji ? (
|
|
132
|
-
<Text style={styles.icon}>{slide.icon}</Text>
|
|
133
|
-
) : (
|
|
134
|
-
<AtomicIcon
|
|
135
|
-
name={slide.icon as any}
|
|
136
|
-
customSize={48}
|
|
137
|
-
customColor={tokens.colors.textPrimary}
|
|
138
|
-
/>
|
|
139
|
-
)}
|
|
140
|
-
</View>
|
|
141
|
-
|
|
142
|
-
{/* Title */}
|
|
143
|
-
<Text style={styles.title}>{slide.title}</Text>
|
|
144
|
-
|
|
145
|
-
{/* Description */}
|
|
146
|
-
{slide.description && (
|
|
147
|
-
<Text style={styles.description}>{slide.description}</Text>
|
|
148
|
-
)}
|
|
149
|
-
|
|
150
|
-
{/* Question */}
|
|
151
|
-
<View style={styles.questionContainer}>{renderQuestion()}</View>
|
|
152
|
-
|
|
153
|
-
{/* Validation hint */}
|
|
154
|
-
{question.validation?.required && !value && (
|
|
155
|
-
<Text style={styles.requiredHint}>* This field is required</Text>
|
|
156
|
-
)}
|
|
157
|
-
</View>
|
|
56
|
+
<View style={styles.slideContent}>{content}</View>
|
|
158
57
|
)}
|
|
159
58
|
</ScrollView>
|
|
160
59
|
);
|
|
161
60
|
};
|
|
162
61
|
|
|
163
|
-
const getStyles = (
|
|
62
|
+
const getStyles = (
|
|
63
|
+
tokens: ReturnType<typeof useAppDesignTokens>,
|
|
64
|
+
useGradient: boolean,
|
|
65
|
+
) =>
|
|
164
66
|
StyleSheet.create({
|
|
165
67
|
content: {
|
|
166
68
|
flexGrow: 1,
|
|
@@ -187,38 +89,6 @@ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>, useGradient: b
|
|
|
187
89
|
shadowRadius: 8,
|
|
188
90
|
elevation: 4,
|
|
189
91
|
},
|
|
190
|
-
iconContainer: {
|
|
191
|
-
width: 96,
|
|
192
|
-
height: 96,
|
|
193
|
-
borderRadius: 48,
|
|
194
|
-
backgroundColor: useGradient
|
|
195
|
-
? "rgba(255, 255, 255, 0.25)"
|
|
196
|
-
: withAlpha(tokens.colors.primary, 0.2),
|
|
197
|
-
alignItems: "center",
|
|
198
|
-
justifyContent: "center",
|
|
199
|
-
marginBottom: 24,
|
|
200
|
-
borderWidth: 2,
|
|
201
|
-
borderColor: useGradient
|
|
202
|
-
? "rgba(255, 255, 255, 0.4)"
|
|
203
|
-
: withAlpha(tokens.colors.primary, 0.4),
|
|
204
|
-
},
|
|
205
|
-
icon: {
|
|
206
|
-
fontSize: 48,
|
|
207
|
-
},
|
|
208
|
-
title: {
|
|
209
|
-
fontSize: 24,
|
|
210
|
-
fontWeight: "bold",
|
|
211
|
-
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
212
|
-
textAlign: "center",
|
|
213
|
-
marginBottom: 12,
|
|
214
|
-
},
|
|
215
|
-
description: {
|
|
216
|
-
fontSize: 15,
|
|
217
|
-
color: useGradient ? "rgba(255, 255, 255, 0.9)" : tokens.colors.textSecondary,
|
|
218
|
-
textAlign: "center",
|
|
219
|
-
lineHeight: 22,
|
|
220
|
-
marginBottom: 24,
|
|
221
|
-
},
|
|
222
92
|
questionContainer: {
|
|
223
93
|
width: "100%",
|
|
224
94
|
marginTop: 8,
|
|
@@ -230,4 +100,3 @@ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>, useGradient: b
|
|
|
230
100
|
marginTop: 12,
|
|
231
101
|
},
|
|
232
102
|
});
|
|
233
|
-
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Question Slide Header Component
|
|
3
|
+
* Single Responsibility: Display slide header (icon, title, description)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
8
|
+
import { AtomicIcon } from "@umituz/react-native-design-system-atoms";
|
|
9
|
+
import { useAppDesignTokens, withAlpha } from "@umituz/react-native-design-system-theme";
|
|
10
|
+
import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
|
|
11
|
+
|
|
12
|
+
export interface QuestionSlideHeaderProps {
|
|
13
|
+
slide: OnboardingSlide;
|
|
14
|
+
useGradient: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const isEmoji = (icon: string): boolean =>
|
|
18
|
+
/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u.test(icon);
|
|
19
|
+
|
|
20
|
+
export const QuestionSlideHeader: React.FC<QuestionSlideHeaderProps> = ({
|
|
21
|
+
slide,
|
|
22
|
+
useGradient,
|
|
23
|
+
}) => {
|
|
24
|
+
const tokens = useAppDesignTokens();
|
|
25
|
+
const styles = getStyles(tokens, useGradient);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<View style={styles.iconContainer}>
|
|
30
|
+
{isEmoji(slide.icon) ? (
|
|
31
|
+
<Text style={styles.icon}>{slide.icon}</Text>
|
|
32
|
+
) : (
|
|
33
|
+
<AtomicIcon
|
|
34
|
+
name={slide.icon as any}
|
|
35
|
+
customSize={48}
|
|
36
|
+
customColor={useGradient ? "#FFFFFF" : tokens.colors.textPrimary}
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
</View>
|
|
40
|
+
|
|
41
|
+
<Text style={styles.title}>{slide.title}</Text>
|
|
42
|
+
|
|
43
|
+
{slide.description && <Text style={styles.description}>{slide.description}</Text>}
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const getStyles = (
|
|
49
|
+
tokens: ReturnType<typeof useAppDesignTokens>,
|
|
50
|
+
useGradient: boolean,
|
|
51
|
+
) =>
|
|
52
|
+
StyleSheet.create({
|
|
53
|
+
iconContainer: {
|
|
54
|
+
width: 96,
|
|
55
|
+
height: 96,
|
|
56
|
+
borderRadius: 48,
|
|
57
|
+
backgroundColor: useGradient
|
|
58
|
+
? "rgba(255, 255, 255, 0.25)"
|
|
59
|
+
: withAlpha(tokens.colors.primary, 0.2),
|
|
60
|
+
alignItems: "center",
|
|
61
|
+
justifyContent: "center",
|
|
62
|
+
marginBottom: 24,
|
|
63
|
+
borderWidth: 2,
|
|
64
|
+
borderColor: useGradient
|
|
65
|
+
? "rgba(255, 255, 255, 0.4)"
|
|
66
|
+
: withAlpha(tokens.colors.primary, 0.4),
|
|
67
|
+
},
|
|
68
|
+
icon: {
|
|
69
|
+
fontSize: 48,
|
|
70
|
+
},
|
|
71
|
+
title: {
|
|
72
|
+
fontSize: 24,
|
|
73
|
+
fontWeight: "bold",
|
|
74
|
+
color: useGradient ? "#FFFFFF" : tokens.colors.textPrimary,
|
|
75
|
+
textAlign: "center",
|
|
76
|
+
marginBottom: 12,
|
|
77
|
+
},
|
|
78
|
+
description: {
|
|
79
|
+
fontSize: 15,
|
|
80
|
+
color: useGradient ? "rgba(255, 255, 255, 0.9)" : tokens.colors.textSecondary,
|
|
81
|
+
textAlign: "center",
|
|
82
|
+
lineHeight: 22,
|
|
83
|
+
marginBottom: 24,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
@@ -1,50 +1,115 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slider Question Component
|
|
3
|
-
*
|
|
4
|
-
* Slider for numeric value selection
|
|
3
|
+
* Single Responsibility: Display slider for numeric value selection
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
|
-
import React from "react";
|
|
8
|
-
import { View, Text, StyleSheet } from "react-native";
|
|
9
|
-
import Slider from "@react-native-community/slider";
|
|
6
|
+
import React, { useMemo } from "react";
|
|
7
|
+
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
|
|
10
8
|
import type { OnboardingQuestion } from "../../../domain/entities/OnboardingQuestion";
|
|
11
9
|
|
|
10
|
+
// Lazy import slider to handle peer dependency gracefully
|
|
11
|
+
let SliderComponent: any = null;
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
14
|
+
SliderComponent = require("@react-native-community/slider").default;
|
|
15
|
+
} catch {
|
|
16
|
+
// Slider not available - will show fallback
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
export interface SliderQuestionProps {
|
|
13
20
|
question: OnboardingQuestion;
|
|
14
21
|
value: number | undefined;
|
|
15
22
|
onChange: (value: number) => void;
|
|
16
23
|
}
|
|
17
24
|
|
|
25
|
+
const getSliderConfig = (question: OnboardingQuestion) => {
|
|
26
|
+
const { validation } = question;
|
|
27
|
+
const min = validation?.min ?? 0;
|
|
28
|
+
const max = validation?.max ?? 100;
|
|
29
|
+
return { min, max };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const SliderFallback: React.FC<{
|
|
33
|
+
min: number;
|
|
34
|
+
max: number;
|
|
35
|
+
value: number;
|
|
36
|
+
onChange: (value: number) => void;
|
|
37
|
+
}> = ({ min, max, value, onChange }) => {
|
|
38
|
+
const handleIncrement = () => {
|
|
39
|
+
if (value < max) {
|
|
40
|
+
onChange(value + 1);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleDecrement = () => {
|
|
45
|
+
if (value > min) {
|
|
46
|
+
onChange(value - 1);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<View style={styles.fallbackContainer}>
|
|
52
|
+
<View style={styles.fallbackControls}>
|
|
53
|
+
<TouchableOpacity
|
|
54
|
+
style={styles.fallbackButton}
|
|
55
|
+
onPress={handleDecrement}
|
|
56
|
+
activeOpacity={0.7}
|
|
57
|
+
>
|
|
58
|
+
<Text style={styles.fallbackButtonText}>−</Text>
|
|
59
|
+
</TouchableOpacity>
|
|
60
|
+
<Text style={styles.fallbackValue}>{value}</Text>
|
|
61
|
+
<TouchableOpacity
|
|
62
|
+
style={styles.fallbackButton}
|
|
63
|
+
onPress={handleIncrement}
|
|
64
|
+
activeOpacity={0.7}
|
|
65
|
+
>
|
|
66
|
+
<Text style={styles.fallbackButtonText}>+</Text>
|
|
67
|
+
</TouchableOpacity>
|
|
68
|
+
</View>
|
|
69
|
+
<View style={styles.fallbackLabels}>
|
|
70
|
+
<Text style={styles.label}>{min}</Text>
|
|
71
|
+
<Text style={styles.label}>{max}</Text>
|
|
72
|
+
</View>
|
|
73
|
+
</View>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
18
77
|
export const SliderQuestion: React.FC<SliderQuestionProps> = ({
|
|
19
78
|
question,
|
|
20
79
|
value,
|
|
21
80
|
onChange,
|
|
22
81
|
}) => {
|
|
23
|
-
const {
|
|
24
|
-
const min = validation?.min ?? 0;
|
|
25
|
-
const max = validation?.max ?? 100;
|
|
82
|
+
const { min, max } = useMemo(() => getSliderConfig(question), [question]);
|
|
26
83
|
const currentValue = value ?? min;
|
|
27
84
|
|
|
85
|
+
const sliderProps = {
|
|
86
|
+
style: styles.slider,
|
|
87
|
+
minimumValue: min,
|
|
88
|
+
maximumValue: max,
|
|
89
|
+
value: currentValue,
|
|
90
|
+
onValueChange: onChange,
|
|
91
|
+
minimumTrackTintColor: "#FFFFFF",
|
|
92
|
+
maximumTrackTintColor: "rgba(255, 255, 255, 0.3)",
|
|
93
|
+
thumbTintColor: "#FFFFFF",
|
|
94
|
+
step: 1,
|
|
95
|
+
};
|
|
96
|
+
|
|
28
97
|
return (
|
|
29
98
|
<View style={styles.container}>
|
|
30
99
|
<View style={styles.valueContainer}>
|
|
31
100
|
<Text style={styles.valueText}>{currentValue}</Text>
|
|
32
101
|
</View>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<View style={styles.labels}>
|
|
45
|
-
<Text style={styles.label}>{min}</Text>
|
|
46
|
-
<Text style={styles.label}>{max}</Text>
|
|
47
|
-
</View>
|
|
102
|
+
{SliderComponent ? (
|
|
103
|
+
<>
|
|
104
|
+
<SliderComponent {...sliderProps} />
|
|
105
|
+
<View style={styles.labels}>
|
|
106
|
+
<Text style={styles.label}>{min}</Text>
|
|
107
|
+
<Text style={styles.label}>{max}</Text>
|
|
108
|
+
</View>
|
|
109
|
+
</>
|
|
110
|
+
) : (
|
|
111
|
+
<SliderFallback min={min} max={max} value={currentValue} onChange={onChange} />
|
|
112
|
+
)}
|
|
48
113
|
</View>
|
|
49
114
|
);
|
|
50
115
|
};
|
|
@@ -77,5 +142,39 @@ const styles = StyleSheet.create({
|
|
|
77
142
|
color: "rgba(255, 255, 255, 0.7)",
|
|
78
143
|
fontWeight: "500",
|
|
79
144
|
},
|
|
145
|
+
fallbackContainer: {
|
|
146
|
+
width: "100%",
|
|
147
|
+
},
|
|
148
|
+
fallbackControls: {
|
|
149
|
+
flexDirection: "row",
|
|
150
|
+
alignItems: "center",
|
|
151
|
+
justifyContent: "center",
|
|
152
|
+
gap: 24,
|
|
153
|
+
},
|
|
154
|
+
fallbackButton: {
|
|
155
|
+
width: 60,
|
|
156
|
+
height: 60,
|
|
157
|
+
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
158
|
+
borderRadius: 30,
|
|
159
|
+
alignItems: "center",
|
|
160
|
+
justifyContent: "center",
|
|
161
|
+
},
|
|
162
|
+
fallbackButtonText: {
|
|
163
|
+
fontSize: 48,
|
|
164
|
+
fontWeight: "bold",
|
|
165
|
+
color: "#FFFFFF",
|
|
166
|
+
lineHeight: 60,
|
|
167
|
+
},
|
|
168
|
+
fallbackValue: {
|
|
169
|
+
fontSize: 36,
|
|
170
|
+
fontWeight: "bold",
|
|
171
|
+
color: "#FFFFFF",
|
|
172
|
+
minWidth: 80,
|
|
173
|
+
textAlign: "center",
|
|
174
|
+
},
|
|
175
|
+
fallbackLabels: {
|
|
176
|
+
flexDirection: "row",
|
|
177
|
+
justifyContent: "space-between",
|
|
178
|
+
marginTop: 16,
|
|
179
|
+
},
|
|
80
180
|
});
|
|
81
|
-
|