@umituz/react-native-design-system 2.4.11 → 2.4.12
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/index.ts +0 -8
- package/src/molecules/index.ts +0 -6
- package/src/molecules/ImagePickerBox/ImagePickerBox.tsx +0 -171
- package/src/molecules/ImagePickerBox/index.ts +0 -2
- package/src/molecules/PhotoUploadCard/PhotoUploadCard.tsx +0 -241
- package/src/molecules/PhotoUploadCard/index.ts +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.12",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
package/src/index.ts
CHANGED
|
@@ -414,14 +414,6 @@ export {
|
|
|
414
414
|
type CountdownTarget,
|
|
415
415
|
type CountdownFormatOptions,
|
|
416
416
|
type CountdownDisplayConfig,
|
|
417
|
-
// Photo Upload
|
|
418
|
-
PhotoUploadCard,
|
|
419
|
-
type PhotoUploadCardProps,
|
|
420
|
-
type PhotoUploadCardConfig,
|
|
421
|
-
// Image Picker
|
|
422
|
-
ImagePickerBox,
|
|
423
|
-
type ImagePickerBoxProps,
|
|
424
|
-
type ImagePickerBoxConfig,
|
|
425
417
|
// Step Header
|
|
426
418
|
StepHeader,
|
|
427
419
|
type StepHeaderProps,
|
package/src/molecules/index.ts
CHANGED
|
@@ -48,12 +48,6 @@ export * from './animation';
|
|
|
48
48
|
// Long Press Menu
|
|
49
49
|
export * from './long-press-menu';
|
|
50
50
|
|
|
51
|
-
// Photo Upload
|
|
52
|
-
export * from './PhotoUploadCard';
|
|
53
|
-
|
|
54
|
-
// Image Picker
|
|
55
|
-
export * from './ImagePickerBox';
|
|
56
|
-
|
|
57
51
|
// Step Header
|
|
58
52
|
export * from './StepHeader';
|
|
59
53
|
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ImagePickerBox Component
|
|
3
|
-
* Generic image picker box with gradient design
|
|
4
|
-
* Props-driven for 100+ apps compatibility
|
|
5
|
-
*
|
|
6
|
-
* @package @umituz/react-native-design-system
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import React, { useMemo } from "react";
|
|
10
|
-
import {
|
|
11
|
-
View,
|
|
12
|
-
StyleSheet,
|
|
13
|
-
TouchableOpacity,
|
|
14
|
-
Image,
|
|
15
|
-
type ViewStyle,
|
|
16
|
-
} from "react-native";
|
|
17
|
-
import { LinearGradient } from "expo-linear-gradient";
|
|
18
|
-
import { AtomicText } from "../../atoms/AtomicText";
|
|
19
|
-
import { AtomicIcon } from "../../atoms/AtomicIcon";
|
|
20
|
-
import { useAppDesignTokens } from "../../theme/hooks/useAppDesignTokens";
|
|
21
|
-
|
|
22
|
-
export interface ImagePickerBoxConfig {
|
|
23
|
-
readonly variant?: "portrait" | "square" | "landscape";
|
|
24
|
-
readonly size?: "sm" | "md" | "lg";
|
|
25
|
-
readonly uploadIcon?: string;
|
|
26
|
-
readonly editIcon?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ImagePickerBoxProps {
|
|
30
|
-
readonly imageUri: string | null;
|
|
31
|
-
readonly isDisabled?: boolean;
|
|
32
|
-
readonly onPress: () => void;
|
|
33
|
-
readonly placeholderText: string;
|
|
34
|
-
readonly gradientColors?: readonly [string, string, ...string[]];
|
|
35
|
-
readonly config?: ImagePickerBoxConfig;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const VARIANT_STYLES: Record<string, ViewStyle> = {
|
|
39
|
-
portrait: { width: 200, height: 280, borderRadius: 20 },
|
|
40
|
-
square: { width: "100%" as unknown as number, aspectRatio: 1, borderRadius: 24 },
|
|
41
|
-
landscape: { width: "100%" as unknown as number, aspectRatio: 16 / 9, borderRadius: 16 },
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const SIZE_MULTIPLIERS = { sm: 0.7, md: 1, lg: 1.3 };
|
|
45
|
-
|
|
46
|
-
const DEFAULT_CONFIG: ImagePickerBoxConfig = {
|
|
47
|
-
variant: "portrait",
|
|
48
|
-
size: "md",
|
|
49
|
-
uploadIcon: "cloud-upload-outline",
|
|
50
|
-
editIcon: "image-outline",
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const ImagePickerBox: React.FC<ImagePickerBoxProps> = ({
|
|
54
|
-
imageUri,
|
|
55
|
-
isDisabled = false,
|
|
56
|
-
onPress,
|
|
57
|
-
placeholderText,
|
|
58
|
-
gradientColors = ["#667eea", "#764ba2"],
|
|
59
|
-
config = DEFAULT_CONFIG,
|
|
60
|
-
}) => {
|
|
61
|
-
const tokens = useAppDesignTokens();
|
|
62
|
-
const cfg = useMemo(() => ({ ...DEFAULT_CONFIG, ...config }), [config]);
|
|
63
|
-
|
|
64
|
-
const multiplier = SIZE_MULTIPLIERS[cfg.size || "md"];
|
|
65
|
-
const baseStyle = VARIANT_STYLES[cfg.variant || "portrait"];
|
|
66
|
-
const iconSize = Math.round(32 * multiplier);
|
|
67
|
-
|
|
68
|
-
const styles = useMemo(
|
|
69
|
-
() =>
|
|
70
|
-
StyleSheet.create({
|
|
71
|
-
container: {
|
|
72
|
-
marginVertical: 16,
|
|
73
|
-
alignItems: "center",
|
|
74
|
-
},
|
|
75
|
-
box: {
|
|
76
|
-
overflow: "hidden",
|
|
77
|
-
...baseStyle,
|
|
78
|
-
backgroundColor: tokens.colors.backgroundSecondary,
|
|
79
|
-
},
|
|
80
|
-
imageContainer: {
|
|
81
|
-
flex: 1,
|
|
82
|
-
position: "relative",
|
|
83
|
-
},
|
|
84
|
-
image: {
|
|
85
|
-
width: "100%",
|
|
86
|
-
height: "100%",
|
|
87
|
-
},
|
|
88
|
-
imageOverlay: {
|
|
89
|
-
position: "absolute",
|
|
90
|
-
bottom: 0,
|
|
91
|
-
left: 0,
|
|
92
|
-
right: 0,
|
|
93
|
-
height: "30%",
|
|
94
|
-
justifyContent: "flex-end",
|
|
95
|
-
alignItems: "flex-end",
|
|
96
|
-
padding: 12,
|
|
97
|
-
},
|
|
98
|
-
editBadge: {
|
|
99
|
-
borderRadius: 20,
|
|
100
|
-
padding: 8,
|
|
101
|
-
backgroundColor: `${gradientColors[1]}E6`,
|
|
102
|
-
},
|
|
103
|
-
placeholder: {
|
|
104
|
-
flex: 1,
|
|
105
|
-
justifyContent: "center",
|
|
106
|
-
alignItems: "center",
|
|
107
|
-
},
|
|
108
|
-
placeholderContent: {
|
|
109
|
-
alignItems: "center",
|
|
110
|
-
justifyContent: "center",
|
|
111
|
-
paddingHorizontal: 16,
|
|
112
|
-
},
|
|
113
|
-
uploadIconContainer: {
|
|
114
|
-
backgroundColor: "rgba(255,255,255,0.2)",
|
|
115
|
-
borderRadius: 40,
|
|
116
|
-
padding: 16,
|
|
117
|
-
marginBottom: 12,
|
|
118
|
-
},
|
|
119
|
-
placeholderText: {
|
|
120
|
-
textAlign: "center",
|
|
121
|
-
fontWeight: "600",
|
|
122
|
-
color: "#FFFFFF",
|
|
123
|
-
},
|
|
124
|
-
}),
|
|
125
|
-
[tokens, baseStyle, gradientColors]
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<View style={styles.container}>
|
|
130
|
-
<TouchableOpacity
|
|
131
|
-
style={styles.box}
|
|
132
|
-
onPress={onPress}
|
|
133
|
-
disabled={isDisabled}
|
|
134
|
-
activeOpacity={0.8}
|
|
135
|
-
>
|
|
136
|
-
{imageUri ? (
|
|
137
|
-
<View style={styles.imageContainer}>
|
|
138
|
-
<Image source={{ uri: imageUri }} style={styles.image} />
|
|
139
|
-
<LinearGradient
|
|
140
|
-
colors={["transparent", "rgba(0,0,0,0.3)"]}
|
|
141
|
-
style={styles.imageOverlay}
|
|
142
|
-
>
|
|
143
|
-
<View style={styles.editBadge}>
|
|
144
|
-
<AtomicIcon
|
|
145
|
-
name={cfg.editIcon || "image-outline"}
|
|
146
|
-
customSize={Math.round(16 * multiplier)}
|
|
147
|
-
customColor="#FFFFFF"
|
|
148
|
-
/>
|
|
149
|
-
</View>
|
|
150
|
-
</LinearGradient>
|
|
151
|
-
</View>
|
|
152
|
-
) : (
|
|
153
|
-
<LinearGradient colors={gradientColors} style={styles.placeholder}>
|
|
154
|
-
<View style={styles.placeholderContent}>
|
|
155
|
-
<View style={styles.uploadIconContainer}>
|
|
156
|
-
<AtomicIcon
|
|
157
|
-
name={cfg.uploadIcon || "cloud-upload-outline"}
|
|
158
|
-
customSize={iconSize}
|
|
159
|
-
customColor="#FFFFFF"
|
|
160
|
-
/>
|
|
161
|
-
</View>
|
|
162
|
-
<AtomicText type="bodyMedium" style={styles.placeholderText}>
|
|
163
|
-
{placeholderText}
|
|
164
|
-
</AtomicText>
|
|
165
|
-
</View>
|
|
166
|
-
</LinearGradient>
|
|
167
|
-
)}
|
|
168
|
-
</TouchableOpacity>
|
|
169
|
-
</View>
|
|
170
|
-
);
|
|
171
|
-
};
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PhotoUploadCard Component
|
|
3
|
-
* Beautiful photo upload card with validation status and responsive design
|
|
4
|
-
*
|
|
5
|
-
* @package @umituz/react-native-design-system
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React, { useMemo } from "react";
|
|
9
|
-
import {
|
|
10
|
-
View,
|
|
11
|
-
Image,
|
|
12
|
-
StyleSheet,
|
|
13
|
-
Pressable,
|
|
14
|
-
TouchableOpacity,
|
|
15
|
-
ActivityIndicator,
|
|
16
|
-
type ViewStyle,
|
|
17
|
-
type StyleProp,
|
|
18
|
-
} from "react-native";
|
|
19
|
-
import { LinearGradient } from "expo-linear-gradient";
|
|
20
|
-
import { AtomicText } from "../../atoms/AtomicText";
|
|
21
|
-
import { AtomicIcon } from "../../atoms/AtomicIcon";
|
|
22
|
-
import { useAppDesignTokens } from "../../theme/hooks/useAppDesignTokens";
|
|
23
|
-
|
|
24
|
-
export interface PhotoUploadCardConfig {
|
|
25
|
-
aspectRatio?: number;
|
|
26
|
-
borderRadius?: number;
|
|
27
|
-
iconSize?: number;
|
|
28
|
-
showValidationStatus?: boolean;
|
|
29
|
-
allowChange?: boolean;
|
|
30
|
-
borderStyle?: "solid" | "dashed";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface PhotoUploadCardProps {
|
|
34
|
-
imageUri: string | null;
|
|
35
|
-
onPress: () => void;
|
|
36
|
-
isValidating?: boolean;
|
|
37
|
-
isValid?: boolean | null;
|
|
38
|
-
disabled?: boolean;
|
|
39
|
-
config?: PhotoUploadCardConfig;
|
|
40
|
-
translations: {
|
|
41
|
-
tapToUpload: string;
|
|
42
|
-
selectPhoto: string;
|
|
43
|
-
change: string;
|
|
44
|
-
analyzing?: string;
|
|
45
|
-
};
|
|
46
|
-
style?: StyleProp<ViewStyle>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const DEFAULT_CONFIG: PhotoUploadCardConfig = {
|
|
50
|
-
aspectRatio: 1,
|
|
51
|
-
borderRadius: 28,
|
|
52
|
-
iconSize: 40,
|
|
53
|
-
showValidationStatus: true,
|
|
54
|
-
allowChange: true,
|
|
55
|
-
borderStyle: "dashed",
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const PhotoUploadCard: React.FC<PhotoUploadCardProps> = ({
|
|
59
|
-
imageUri,
|
|
60
|
-
onPress,
|
|
61
|
-
isValidating = false,
|
|
62
|
-
isValid = null,
|
|
63
|
-
disabled = false,
|
|
64
|
-
config = DEFAULT_CONFIG,
|
|
65
|
-
translations,
|
|
66
|
-
style,
|
|
67
|
-
}) => {
|
|
68
|
-
const tokens = useAppDesignTokens();
|
|
69
|
-
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
70
|
-
|
|
71
|
-
const borderColor = useMemo(() => {
|
|
72
|
-
if (!cfg.showValidationStatus) {
|
|
73
|
-
return `${tokens.colors.primary}40`;
|
|
74
|
-
}
|
|
75
|
-
if (isValidating) return tokens.colors.primary;
|
|
76
|
-
if (isValid === true) return tokens.colors.success;
|
|
77
|
-
if (isValid === false) return tokens.colors.error;
|
|
78
|
-
return `${tokens.colors.primary}40`;
|
|
79
|
-
}, [isValidating, isValid, tokens, cfg.showValidationStatus]);
|
|
80
|
-
|
|
81
|
-
const styles = useMemo(
|
|
82
|
-
() =>
|
|
83
|
-
StyleSheet.create({
|
|
84
|
-
container: {
|
|
85
|
-
marginHorizontal: 24,
|
|
86
|
-
marginBottom: 24,
|
|
87
|
-
},
|
|
88
|
-
card: {
|
|
89
|
-
aspectRatio: cfg.aspectRatio,
|
|
90
|
-
backgroundColor: tokens.colors.surfaceSecondary,
|
|
91
|
-
borderRadius: cfg.borderRadius,
|
|
92
|
-
justifyContent: "center",
|
|
93
|
-
alignItems: "center",
|
|
94
|
-
overflow: "hidden",
|
|
95
|
-
borderWidth: 2,
|
|
96
|
-
borderStyle: imageUri ? "solid" : cfg.borderStyle,
|
|
97
|
-
},
|
|
98
|
-
placeholder: {
|
|
99
|
-
alignItems: "center",
|
|
100
|
-
padding: 32,
|
|
101
|
-
},
|
|
102
|
-
iconCircle: {
|
|
103
|
-
width: 88,
|
|
104
|
-
height: 88,
|
|
105
|
-
borderRadius: 44,
|
|
106
|
-
justifyContent: "center",
|
|
107
|
-
alignItems: "center",
|
|
108
|
-
marginBottom: 20,
|
|
109
|
-
borderWidth: 2,
|
|
110
|
-
borderColor: `${tokens.colors.primary}30`,
|
|
111
|
-
},
|
|
112
|
-
iconGradient: {
|
|
113
|
-
width: 88,
|
|
114
|
-
height: 88,
|
|
115
|
-
borderRadius: 44,
|
|
116
|
-
justifyContent: "center",
|
|
117
|
-
alignItems: "center",
|
|
118
|
-
},
|
|
119
|
-
title: {
|
|
120
|
-
fontSize: 20,
|
|
121
|
-
fontWeight: "700",
|
|
122
|
-
color: tokens.colors.textPrimary,
|
|
123
|
-
marginBottom: 8,
|
|
124
|
-
letterSpacing: 0.3,
|
|
125
|
-
},
|
|
126
|
-
subtitle: {
|
|
127
|
-
fontSize: 14,
|
|
128
|
-
color: tokens.colors.textSecondary,
|
|
129
|
-
textAlign: "center",
|
|
130
|
-
lineHeight: 20,
|
|
131
|
-
maxWidth: 240,
|
|
132
|
-
},
|
|
133
|
-
image: {
|
|
134
|
-
width: "100%",
|
|
135
|
-
height: "100%",
|
|
136
|
-
resizeMode: "cover",
|
|
137
|
-
},
|
|
138
|
-
imageOverlay: {
|
|
139
|
-
...StyleSheet.absoluteFillObject,
|
|
140
|
-
backgroundColor: "rgba(0,0,0,0.15)",
|
|
141
|
-
},
|
|
142
|
-
changeButton: {
|
|
143
|
-
position: "absolute",
|
|
144
|
-
bottom: 20,
|
|
145
|
-
right: 20,
|
|
146
|
-
backgroundColor: tokens.colors.surface,
|
|
147
|
-
paddingHorizontal: 18,
|
|
148
|
-
paddingVertical: 12,
|
|
149
|
-
borderRadius: 28,
|
|
150
|
-
flexDirection: "row",
|
|
151
|
-
alignItems: "center",
|
|
152
|
-
gap: 8,
|
|
153
|
-
},
|
|
154
|
-
changeText: {
|
|
155
|
-
fontSize: 14,
|
|
156
|
-
fontWeight: "700",
|
|
157
|
-
color: tokens.colors.primary,
|
|
158
|
-
},
|
|
159
|
-
validatingContainer: {
|
|
160
|
-
alignItems: "center",
|
|
161
|
-
padding: 32,
|
|
162
|
-
},
|
|
163
|
-
validatingText: {
|
|
164
|
-
fontSize: 16,
|
|
165
|
-
fontWeight: "600",
|
|
166
|
-
color: tokens.colors.primary,
|
|
167
|
-
marginTop: 20,
|
|
168
|
-
},
|
|
169
|
-
pulseRing: {
|
|
170
|
-
position: "absolute",
|
|
171
|
-
width: 100,
|
|
172
|
-
height: 100,
|
|
173
|
-
borderRadius: 50,
|
|
174
|
-
borderWidth: 2,
|
|
175
|
-
borderColor: `${tokens.colors.primary}30`,
|
|
176
|
-
},
|
|
177
|
-
}),
|
|
178
|
-
[tokens, imageUri, cfg],
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
return (
|
|
182
|
-
<View style={[styles.container, style]}>
|
|
183
|
-
<Pressable
|
|
184
|
-
style={[styles.card, { borderColor }]}
|
|
185
|
-
onPress={onPress}
|
|
186
|
-
disabled={disabled || isValidating}
|
|
187
|
-
>
|
|
188
|
-
{isValidating ? (
|
|
189
|
-
<View style={styles.validatingContainer}>
|
|
190
|
-
<View style={styles.pulseRing} />
|
|
191
|
-
<ActivityIndicator size="large" color={tokens.colors.primary} />
|
|
192
|
-
<AtomicText style={styles.validatingText}>
|
|
193
|
-
{translations.analyzing || "Analyzing..."}
|
|
194
|
-
</AtomicText>
|
|
195
|
-
</View>
|
|
196
|
-
) : imageUri ? (
|
|
197
|
-
<>
|
|
198
|
-
<Image source={{ uri: imageUri }} style={styles.image} />
|
|
199
|
-
<View style={styles.imageOverlay} />
|
|
200
|
-
{cfg.allowChange && (
|
|
201
|
-
<TouchableOpacity style={styles.changeButton} onPress={onPress}>
|
|
202
|
-
<AtomicIcon
|
|
203
|
-
name="camera"
|
|
204
|
-
size={18}
|
|
205
|
-
customColor={tokens.colors.primary}
|
|
206
|
-
/>
|
|
207
|
-
<AtomicText style={styles.changeText}>
|
|
208
|
-
{translations.change}
|
|
209
|
-
</AtomicText>
|
|
210
|
-
</TouchableOpacity>
|
|
211
|
-
)}
|
|
212
|
-
</>
|
|
213
|
-
) : (
|
|
214
|
-
<View style={styles.placeholder}>
|
|
215
|
-
<View style={styles.iconCircle}>
|
|
216
|
-
<LinearGradient
|
|
217
|
-
colors={[
|
|
218
|
-
`${tokens.colors.primary}20`,
|
|
219
|
-
`${tokens.colors.primary}10`,
|
|
220
|
-
]}
|
|
221
|
-
style={styles.iconGradient}
|
|
222
|
-
>
|
|
223
|
-
<AtomicIcon
|
|
224
|
-
name="camera"
|
|
225
|
-
size={cfg.iconSize}
|
|
226
|
-
customColor={tokens.colors.primary}
|
|
227
|
-
/>
|
|
228
|
-
</LinearGradient>
|
|
229
|
-
</View>
|
|
230
|
-
<AtomicText style={styles.title}>
|
|
231
|
-
{translations.tapToUpload}
|
|
232
|
-
</AtomicText>
|
|
233
|
-
<AtomicText style={styles.subtitle}>
|
|
234
|
-
{translations.selectPhoto}
|
|
235
|
-
</AtomicText>
|
|
236
|
-
</View>
|
|
237
|
-
)}
|
|
238
|
-
</Pressable>
|
|
239
|
-
</View>
|
|
240
|
-
);
|
|
241
|
-
};
|