@umituz/react-native-ai-generation-content 1.17.4 → 1.17.6
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 +26 -0
- package/src/presentation/components/GenerationProgressModal.tsx +22 -17
- package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.tsx +243 -0
- package/src/presentation/components/PhotoUploadCard/index.ts +5 -0
- package/src/presentation/components/index.ts +1 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -229,6 +229,18 @@ export {
|
|
|
229
229
|
ResultActions,
|
|
230
230
|
DEFAULT_RESULT_CONFIG,
|
|
231
231
|
PhotoStep,
|
|
232
|
+
// Image Picker
|
|
233
|
+
ImagePickerBox,
|
|
234
|
+
DualImagePicker,
|
|
235
|
+
// Buttons
|
|
236
|
+
GenerateButton,
|
|
237
|
+
// Display
|
|
238
|
+
ResultDisplay,
|
|
239
|
+
ErrorDisplay,
|
|
240
|
+
// Headers
|
|
241
|
+
FeatureHeader,
|
|
242
|
+
// Photo Upload
|
|
243
|
+
PhotoUploadCard,
|
|
232
244
|
} from "./presentation/components";
|
|
233
245
|
|
|
234
246
|
export type {
|
|
@@ -254,6 +266,20 @@ export type {
|
|
|
254
266
|
ResultLayoutConfig,
|
|
255
267
|
ResultActionButton,
|
|
256
268
|
PhotoStepProps,
|
|
269
|
+
// Image Picker
|
|
270
|
+
ImagePickerBoxProps,
|
|
271
|
+
DualImagePickerProps,
|
|
272
|
+
// Buttons
|
|
273
|
+
GenerateButtonProps,
|
|
274
|
+
// Display
|
|
275
|
+
ResultDisplayProps,
|
|
276
|
+
ResultDisplayAction,
|
|
277
|
+
ErrorDisplayProps,
|
|
278
|
+
// Headers
|
|
279
|
+
FeatureHeaderProps,
|
|
280
|
+
// Photo Upload
|
|
281
|
+
PhotoUploadCardProps,
|
|
282
|
+
PhotoUploadCardConfig,
|
|
257
283
|
} from "./presentation/components";
|
|
258
284
|
|
|
259
285
|
// =============================================================================
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GenerationProgressModal
|
|
3
|
-
* Generic AI generation progress modal
|
|
3
|
+
* Generic AI generation progress modal using BaseModal
|
|
4
4
|
* Props-driven for 100+ apps compatibility
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from "react";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { StyleSheet } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
BaseModal,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
} from "@umituz/react-native-design-system";
|
|
10
13
|
import {
|
|
11
14
|
GenerationProgressContent,
|
|
12
15
|
type GenerationProgressContentProps,
|
|
@@ -24,7 +27,6 @@ export interface GenerationProgressRenderProps {
|
|
|
24
27
|
export interface GenerationProgressModalProps
|
|
25
28
|
extends Omit<GenerationProgressContentProps, "backgroundColor"> {
|
|
26
29
|
readonly visible: boolean;
|
|
27
|
-
readonly overlayColor?: string;
|
|
28
30
|
readonly modalBackgroundColor?: string;
|
|
29
31
|
readonly renderContent?: (
|
|
30
32
|
props: GenerationProgressRenderProps
|
|
@@ -42,7 +44,6 @@ export const GenerationProgressModal: React.FC<
|
|
|
42
44
|
hint,
|
|
43
45
|
dismissLabel,
|
|
44
46
|
onDismiss,
|
|
45
|
-
overlayColor = "rgba(0, 0, 0, 0.75)",
|
|
46
47
|
modalBackgroundColor,
|
|
47
48
|
textColor,
|
|
48
49
|
hintColor,
|
|
@@ -54,6 +55,10 @@ export const GenerationProgressModal: React.FC<
|
|
|
54
55
|
const tokens = useAppDesignTokens();
|
|
55
56
|
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
56
57
|
|
|
58
|
+
const handleClose = React.useCallback(() => {
|
|
59
|
+
onDismiss?.();
|
|
60
|
+
}, [onDismiss]);
|
|
61
|
+
|
|
57
62
|
const content = renderContent ? (
|
|
58
63
|
renderContent({
|
|
59
64
|
progress: clampedProgress,
|
|
@@ -84,25 +89,25 @@ export const GenerationProgressModal: React.FC<
|
|
|
84
89
|
);
|
|
85
90
|
|
|
86
91
|
return (
|
|
87
|
-
<
|
|
92
|
+
<BaseModal
|
|
88
93
|
visible={visible}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
onRequestClose={onDismiss}
|
|
94
|
+
onClose={handleClose}
|
|
95
|
+
dismissOnBackdrop={false}
|
|
96
|
+
contentStyle={styles.modalContent}
|
|
93
97
|
>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
</View>
|
|
97
|
-
</Modal>
|
|
98
|
+
{content}
|
|
99
|
+
</BaseModal>
|
|
98
100
|
);
|
|
99
101
|
};
|
|
100
102
|
|
|
101
103
|
const styles = StyleSheet.create({
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
modalContent: {
|
|
105
|
+
backgroundColor: "transparent",
|
|
106
|
+
borderWidth: 0,
|
|
107
|
+
padding: 24,
|
|
108
|
+
width: "100%",
|
|
109
|
+
height: "auto",
|
|
104
110
|
justifyContent: "center",
|
|
105
111
|
alignItems: "center",
|
|
106
|
-
padding: 24,
|
|
107
112
|
},
|
|
108
113
|
});
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhotoUploadCard Component
|
|
3
|
+
* Beautiful photo upload card with validation status and responsive design
|
|
4
|
+
*
|
|
5
|
+
* @package @umituz/react-native-ai-generation-content
|
|
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 {
|
|
21
|
+
AtomicText,
|
|
22
|
+
AtomicIcon,
|
|
23
|
+
useAppDesignTokens,
|
|
24
|
+
} from "@umituz/react-native-design-system";
|
|
25
|
+
|
|
26
|
+
export interface PhotoUploadCardConfig {
|
|
27
|
+
aspectRatio?: number;
|
|
28
|
+
borderRadius?: number;
|
|
29
|
+
iconSize?: number;
|
|
30
|
+
showValidationStatus?: boolean;
|
|
31
|
+
allowChange?: boolean;
|
|
32
|
+
borderStyle?: "solid" | "dashed";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PhotoUploadCardProps {
|
|
36
|
+
imageUri: string | null;
|
|
37
|
+
onPress: () => void;
|
|
38
|
+
isValidating?: boolean;
|
|
39
|
+
isValid?: boolean | null;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
config?: PhotoUploadCardConfig;
|
|
42
|
+
translations: {
|
|
43
|
+
tapToUpload: string;
|
|
44
|
+
selectPhoto: string;
|
|
45
|
+
change: string;
|
|
46
|
+
analyzing?: string;
|
|
47
|
+
};
|
|
48
|
+
style?: StyleProp<ViewStyle>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const DEFAULT_CONFIG: PhotoUploadCardConfig = {
|
|
52
|
+
aspectRatio: 1,
|
|
53
|
+
borderRadius: 28,
|
|
54
|
+
iconSize: 40,
|
|
55
|
+
showValidationStatus: true,
|
|
56
|
+
allowChange: true,
|
|
57
|
+
borderStyle: "dashed",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const PhotoUploadCard: React.FC<PhotoUploadCardProps> = ({
|
|
61
|
+
imageUri,
|
|
62
|
+
onPress,
|
|
63
|
+
isValidating = false,
|
|
64
|
+
isValid = null,
|
|
65
|
+
disabled = false,
|
|
66
|
+
config = DEFAULT_CONFIG,
|
|
67
|
+
translations,
|
|
68
|
+
style,
|
|
69
|
+
}) => {
|
|
70
|
+
const tokens = useAppDesignTokens();
|
|
71
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
72
|
+
|
|
73
|
+
const borderColor = useMemo(() => {
|
|
74
|
+
if (!cfg.showValidationStatus) {
|
|
75
|
+
return `${tokens.colors.primary}40`;
|
|
76
|
+
}
|
|
77
|
+
if (isValidating) return tokens.colors.primary;
|
|
78
|
+
if (isValid === true) return tokens.colors.success;
|
|
79
|
+
if (isValid === false) return tokens.colors.error;
|
|
80
|
+
return `${tokens.colors.primary}40`;
|
|
81
|
+
}, [isValidating, isValid, tokens, cfg.showValidationStatus]);
|
|
82
|
+
|
|
83
|
+
const styles = useMemo(
|
|
84
|
+
() =>
|
|
85
|
+
StyleSheet.create({
|
|
86
|
+
container: {
|
|
87
|
+
marginHorizontal: 24,
|
|
88
|
+
marginBottom: 24,
|
|
89
|
+
},
|
|
90
|
+
card: {
|
|
91
|
+
aspectRatio: cfg.aspectRatio,
|
|
92
|
+
backgroundColor: tokens.colors.surfaceSecondary,
|
|
93
|
+
borderRadius: cfg.borderRadius,
|
|
94
|
+
justifyContent: "center",
|
|
95
|
+
alignItems: "center",
|
|
96
|
+
overflow: "hidden",
|
|
97
|
+
borderWidth: 2,
|
|
98
|
+
borderStyle: imageUri ? "solid" : cfg.borderStyle,
|
|
99
|
+
},
|
|
100
|
+
placeholder: {
|
|
101
|
+
alignItems: "center",
|
|
102
|
+
padding: 32,
|
|
103
|
+
},
|
|
104
|
+
iconCircle: {
|
|
105
|
+
width: 88,
|
|
106
|
+
height: 88,
|
|
107
|
+
borderRadius: 44,
|
|
108
|
+
justifyContent: "center",
|
|
109
|
+
alignItems: "center",
|
|
110
|
+
marginBottom: 20,
|
|
111
|
+
borderWidth: 2,
|
|
112
|
+
borderColor: `${tokens.colors.primary}30`,
|
|
113
|
+
},
|
|
114
|
+
iconGradient: {
|
|
115
|
+
width: 88,
|
|
116
|
+
height: 88,
|
|
117
|
+
borderRadius: 44,
|
|
118
|
+
justifyContent: "center",
|
|
119
|
+
alignItems: "center",
|
|
120
|
+
},
|
|
121
|
+
title: {
|
|
122
|
+
fontSize: 20,
|
|
123
|
+
fontWeight: "700",
|
|
124
|
+
color: tokens.colors.textPrimary,
|
|
125
|
+
marginBottom: 8,
|
|
126
|
+
letterSpacing: 0.3,
|
|
127
|
+
},
|
|
128
|
+
subtitle: {
|
|
129
|
+
fontSize: 14,
|
|
130
|
+
color: tokens.colors.textSecondary,
|
|
131
|
+
textAlign: "center",
|
|
132
|
+
lineHeight: 20,
|
|
133
|
+
maxWidth: 240,
|
|
134
|
+
},
|
|
135
|
+
image: {
|
|
136
|
+
width: "100%",
|
|
137
|
+
height: "100%",
|
|
138
|
+
resizeMode: "cover",
|
|
139
|
+
},
|
|
140
|
+
imageOverlay: {
|
|
141
|
+
...StyleSheet.absoluteFillObject,
|
|
142
|
+
backgroundColor: "rgba(0,0,0,0.15)",
|
|
143
|
+
},
|
|
144
|
+
changeButton: {
|
|
145
|
+
position: "absolute",
|
|
146
|
+
bottom: 20,
|
|
147
|
+
right: 20,
|
|
148
|
+
backgroundColor: tokens.colors.surface,
|
|
149
|
+
paddingHorizontal: 18,
|
|
150
|
+
paddingVertical: 12,
|
|
151
|
+
borderRadius: 28,
|
|
152
|
+
flexDirection: "row",
|
|
153
|
+
alignItems: "center",
|
|
154
|
+
gap: 8,
|
|
155
|
+
},
|
|
156
|
+
changeText: {
|
|
157
|
+
fontSize: 14,
|
|
158
|
+
fontWeight: "700",
|
|
159
|
+
color: tokens.colors.primary,
|
|
160
|
+
},
|
|
161
|
+
validatingContainer: {
|
|
162
|
+
alignItems: "center",
|
|
163
|
+
padding: 32,
|
|
164
|
+
},
|
|
165
|
+
validatingText: {
|
|
166
|
+
fontSize: 16,
|
|
167
|
+
fontWeight: "600",
|
|
168
|
+
color: tokens.colors.primary,
|
|
169
|
+
marginTop: 20,
|
|
170
|
+
},
|
|
171
|
+
pulseRing: {
|
|
172
|
+
position: "absolute",
|
|
173
|
+
width: 100,
|
|
174
|
+
height: 100,
|
|
175
|
+
borderRadius: 50,
|
|
176
|
+
borderWidth: 2,
|
|
177
|
+
borderColor: `${tokens.colors.primary}30`,
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
[tokens, imageUri, cfg],
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<View style={[styles.container, style]}>
|
|
185
|
+
<Pressable
|
|
186
|
+
style={[styles.card, { borderColor }]}
|
|
187
|
+
onPress={onPress}
|
|
188
|
+
disabled={disabled || isValidating}
|
|
189
|
+
>
|
|
190
|
+
{isValidating ? (
|
|
191
|
+
<View style={styles.validatingContainer}>
|
|
192
|
+
<View style={styles.pulseRing} />
|
|
193
|
+
<ActivityIndicator size="large" color={tokens.colors.primary} />
|
|
194
|
+
<AtomicText style={styles.validatingText}>
|
|
195
|
+
{translations.analyzing || "Analyzing..."}
|
|
196
|
+
</AtomicText>
|
|
197
|
+
</View>
|
|
198
|
+
) : imageUri ? (
|
|
199
|
+
<>
|
|
200
|
+
<Image source={{ uri: imageUri }} style={styles.image} />
|
|
201
|
+
<View style={styles.imageOverlay} />
|
|
202
|
+
{cfg.allowChange && (
|
|
203
|
+
<TouchableOpacity style={styles.changeButton} onPress={onPress}>
|
|
204
|
+
<AtomicIcon
|
|
205
|
+
name="camera"
|
|
206
|
+
size={18}
|
|
207
|
+
customColor={tokens.colors.primary}
|
|
208
|
+
/>
|
|
209
|
+
<AtomicText style={styles.changeText}>
|
|
210
|
+
{translations.change}
|
|
211
|
+
</AtomicText>
|
|
212
|
+
</TouchableOpacity>
|
|
213
|
+
)}
|
|
214
|
+
</>
|
|
215
|
+
) : (
|
|
216
|
+
<View style={styles.placeholder}>
|
|
217
|
+
<View style={styles.iconCircle}>
|
|
218
|
+
<LinearGradient
|
|
219
|
+
colors={[
|
|
220
|
+
`${tokens.colors.primary}20`,
|
|
221
|
+
`${tokens.colors.primary}10`,
|
|
222
|
+
]}
|
|
223
|
+
style={styles.iconGradient}
|
|
224
|
+
>
|
|
225
|
+
<AtomicIcon
|
|
226
|
+
name="camera"
|
|
227
|
+
size={cfg.iconSize}
|
|
228
|
+
customColor={tokens.colors.primary}
|
|
229
|
+
/>
|
|
230
|
+
</LinearGradient>
|
|
231
|
+
</View>
|
|
232
|
+
<AtomicText style={styles.title}>
|
|
233
|
+
{translations.tapToUpload}
|
|
234
|
+
</AtomicText>
|
|
235
|
+
<AtomicText style={styles.subtitle}>
|
|
236
|
+
{translations.selectPhoto}
|
|
237
|
+
</AtomicText>
|
|
238
|
+
</View>
|
|
239
|
+
)}
|
|
240
|
+
</Pressable>
|
|
241
|
+
</View>
|
|
242
|
+
);
|
|
243
|
+
};
|