@umituz/react-native-design-system 2.4.9 → 2.4.11
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 +7 -7
- package/src/index.ts +4 -0
- package/src/molecules/ImagePickerBox/ImagePickerBox.tsx +171 -0
- package/src/molecules/ImagePickerBox/index.ts +2 -0
- package/src/molecules/index.ts +3 -0
- package/src/molecules/navigation/StackNavigator.tsx +1 -0
- package/src/molecules/navigation/TabsNavigator.tsx +1 -0
- package/src/molecules/navigation/types.ts +2 -0
- package/src/types/jsx.d.ts +19 -0
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.11",
|
|
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",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
54
54
|
"@react-native-async-storage/async-storage": ">=2.0.0",
|
|
55
55
|
"@react-native-community/datetimepicker": ">=8.0.0",
|
|
56
|
-
"@react-navigation/bottom-tabs": ">=
|
|
57
|
-
"@react-navigation/native": ">=
|
|
58
|
-
"@react-navigation/stack": ">=
|
|
56
|
+
"@react-navigation/bottom-tabs": ">=7.0.0",
|
|
57
|
+
"@react-navigation/native": ">=7.0.0",
|
|
58
|
+
"@react-navigation/stack": ">=7.0.0",
|
|
59
59
|
"@umituz/react-native-filesystem": "*",
|
|
60
60
|
"@umituz/react-native-haptics": "*",
|
|
61
61
|
"@umituz/react-native-localization": "*",
|
|
@@ -99,9 +99,9 @@
|
|
|
99
99
|
"@gorhom/bottom-sheet": "^5.0.0",
|
|
100
100
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
101
101
|
"@react-native-community/datetimepicker": "^8.5.1",
|
|
102
|
-
"@react-navigation/bottom-tabs": "^
|
|
103
|
-
"@react-navigation/native": "^
|
|
104
|
-
"@react-navigation/stack": "^6.
|
|
102
|
+
"@react-navigation/bottom-tabs": "^7.9.0",
|
|
103
|
+
"@react-navigation/native": "^7.1.26",
|
|
104
|
+
"@react-navigation/stack": "^7.6.13",
|
|
105
105
|
"@testing-library/react": "^16.3.1",
|
|
106
106
|
"@testing-library/react-native": "^13.3.3",
|
|
107
107
|
"@types/jest": "^30.0.0",
|
package/src/index.ts
CHANGED
|
@@ -418,6 +418,10 @@ export {
|
|
|
418
418
|
PhotoUploadCard,
|
|
419
419
|
type PhotoUploadCardProps,
|
|
420
420
|
type PhotoUploadCardConfig,
|
|
421
|
+
// Image Picker
|
|
422
|
+
ImagePickerBox,
|
|
423
|
+
type ImagePickerBoxProps,
|
|
424
|
+
type ImagePickerBoxConfig,
|
|
421
425
|
// Step Header
|
|
422
426
|
StepHeader,
|
|
423
427
|
type StepHeaderProps,
|
|
@@ -0,0 +1,171 @@
|
|
|
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
|
+
};
|
package/src/molecules/index.ts
CHANGED
|
@@ -66,6 +66,8 @@ export interface StackScreen<T extends ParamListBase = ParamListBase>
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export interface BaseNavigatorConfig<T extends ParamListBase = ParamListBase> {
|
|
69
|
+
/** Unique identifier for the navigator (required for React Navigation v7+) */
|
|
70
|
+
id?: string;
|
|
69
71
|
screens: TabScreen[] | StackScreen[];
|
|
70
72
|
initialRouteName?: Extract<keyof T, string>;
|
|
71
73
|
getLabel?: (label: string) => string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React 19 JSX Compatibility for React Native
|
|
3
|
+
* Fixes JSX namespace issues between React 19 and react-native
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { JSX as ReactJSX } from 'react';
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
namespace JSX {
|
|
10
|
+
interface Element extends ReactJSX.Element {}
|
|
11
|
+
interface ElementClass extends ReactJSX.ElementClass {}
|
|
12
|
+
interface ElementAttributesProperty extends ReactJSX.ElementAttributesProperty {}
|
|
13
|
+
interface ElementChildrenAttribute extends ReactJSX.ElementChildrenAttribute {}
|
|
14
|
+
interface IntrinsicAttributes extends ReactJSX.IntrinsicAttributes {}
|
|
15
|
+
interface IntrinsicClassAttributes<T> extends ReactJSX.IntrinsicClassAttributes<T> {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {};
|