@umituz/react-native-ai-generation-content 1.17.3 → 1.17.5

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-ai-generation-content",
3
- "version": "1.17.3",
3
+ "version": "1.17.5",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -1,123 +1,161 @@
1
1
  /**
2
2
  * GenerationProgressContent
3
3
  * Content for the AI generation progress modal
4
+ * Props-driven for 100+ apps compatibility
4
5
  */
5
6
 
6
7
  import React from "react";
7
8
  import { View, TouchableOpacity, StyleSheet } from "react-native";
8
- import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
9
+ import {
10
+ AtomicText,
11
+ AtomicIcon,
12
+ useAppDesignTokens,
13
+ } from "@umituz/react-native-design-system";
9
14
  import { GenerationProgressBar } from "./GenerationProgressBar";
10
15
 
11
16
  export interface GenerationProgressContentProps {
12
- progress: number;
13
- title?: string;
14
- message?: string;
15
- hint?: string;
16
- dismissLabel?: string;
17
- onDismiss?: () => void;
18
- backgroundColor?: string;
19
- textColor?: string;
20
- progressColor?: string;
21
- progressBackgroundColor?: string;
22
- dismissButtonColor?: string;
17
+ readonly progress: number;
18
+ readonly icon?: string;
19
+ readonly title?: string;
20
+ readonly message?: string;
21
+ readonly hint?: string;
22
+ readonly dismissLabel?: string;
23
+ readonly onDismiss?: () => void;
24
+ readonly backgroundColor?: string;
25
+ readonly textColor?: string;
26
+ readonly hintColor?: string;
27
+ readonly progressColor?: string;
28
+ readonly progressBackgroundColor?: string;
29
+ readonly dismissButtonColor?: string;
23
30
  }
24
31
 
25
32
  export const GenerationProgressContent: React.FC<
26
- GenerationProgressContentProps
33
+ GenerationProgressContentProps
27
34
  > = ({
28
- progress,
29
- title,
30
- message,
31
- hint,
32
- dismissLabel,
33
- onDismiss,
34
- backgroundColor,
35
- textColor,
36
- progressColor,
37
- progressBackgroundColor,
38
- dismissButtonColor,
35
+ progress,
36
+ icon,
37
+ title,
38
+ message,
39
+ hint,
40
+ dismissLabel,
41
+ onDismiss,
42
+ backgroundColor,
43
+ textColor,
44
+ hintColor,
45
+ progressColor,
46
+ progressBackgroundColor,
47
+ dismissButtonColor,
39
48
  }) => {
40
- const tokens = useAppDesignTokens();
49
+ const tokens = useAppDesignTokens();
41
50
 
42
- const activeTextColor = textColor || tokens.colors.textPrimary;
43
- const activeBgColor = backgroundColor || tokens.colors.surface;
51
+ const activeTextColor = textColor || tokens.colors.textPrimary;
52
+ const activeBgColor = backgroundColor || tokens.colors.surface;
53
+ const activeHintColor = hintColor || tokens.colors.textTertiary;
44
54
 
45
- return (
46
- <View style={[styles.modal, { backgroundColor: activeBgColor }]}>
47
- {title && (
48
- <AtomicText style={[styles.title, { color: activeTextColor }]}>{title}</AtomicText>
49
- )}
55
+ return (
56
+ <View
57
+ style={[
58
+ styles.modal,
59
+ {
60
+ backgroundColor: activeBgColor,
61
+ borderColor: tokens.colors.borderLight,
62
+ },
63
+ ]}
64
+ >
65
+ {icon && (
66
+ <View style={styles.iconContainer}>
67
+ <AtomicIcon name={icon} size="xl" color="primary" />
68
+ </View>
69
+ )}
50
70
 
51
- {message && (
52
- <AtomicText style={[styles.message, { color: activeTextColor }]}>
53
- {message}
54
- </AtomicText>
55
- )}
71
+ {title && (
72
+ <AtomicText
73
+ type="headlineSmall"
74
+ style={[styles.title, { color: activeTextColor }]}
75
+ >
76
+ {title}
77
+ </AtomicText>
78
+ )}
56
79
 
57
- <GenerationProgressBar
58
- progress={progress}
59
- textColor={activeTextColor}
60
- progressColor={progressColor}
61
- backgroundColor={progressBackgroundColor}
62
- />
80
+ {message && (
81
+ <AtomicText
82
+ type="bodyMedium"
83
+ style={[styles.message, { color: tokens.colors.textSecondary }]}
84
+ >
85
+ {message}
86
+ </AtomicText>
87
+ )}
63
88
 
64
- {hint && (
65
- <AtomicText style={[styles.hint, { color: activeTextColor }]}>{hint}</AtomicText>
66
- )}
89
+ <GenerationProgressBar
90
+ progress={progress}
91
+ textColor={tokens.colors.primary}
92
+ progressColor={progressColor}
93
+ backgroundColor={progressBackgroundColor}
94
+ />
67
95
 
68
- {onDismiss && (
69
- <TouchableOpacity
70
- style={[
71
- styles.dismissButton,
72
- { backgroundColor: dismissButtonColor || tokens.colors.primary },
73
- ]}
74
- onPress={onDismiss}
75
- >
76
- <AtomicText style={styles.dismissText}>{dismissLabel || "OK"}</AtomicText>
77
- </TouchableOpacity>
78
- )}
79
- </View>
80
- );
81
- };
96
+ {hint && (
97
+ <AtomicText
98
+ type="bodySmall"
99
+ style={[styles.hint, { color: activeHintColor }]}
100
+ >
101
+ {hint}
102
+ </AtomicText>
103
+ )}
104
+
105
+ {onDismiss && (
106
+ <TouchableOpacity
107
+ style={[
108
+ styles.dismissButton,
109
+ { backgroundColor: dismissButtonColor || tokens.colors.primary },
110
+ ]}
111
+ onPress={onDismiss}
112
+ >
113
+ <AtomicText type="bodyMedium" style={styles.dismissText}>
114
+ {dismissLabel || "OK"}
115
+ </AtomicText>
116
+ </TouchableOpacity>
117
+ )}
118
+ </View>
119
+ );
120
+ };
82
121
 
83
122
  const styles = StyleSheet.create({
84
- modal: {
85
- width: "100%",
86
- maxWidth: 400,
87
- borderRadius: 24,
88
- padding: 32,
89
- alignItems: "center",
90
- },
91
- title: {
92
- fontSize: 20,
93
- fontWeight: "700",
94
- marginBottom: 12,
95
- textAlign: "center",
96
- },
97
- message: {
98
- fontSize: 16,
99
- marginBottom: 24,
100
- textAlign: "center",
101
- opacity: 0.8,
102
- },
103
- hint: {
104
- fontSize: 14,
105
- textAlign: "center",
106
- fontStyle: "italic",
107
- opacity: 0.6,
108
- marginBottom: 16,
109
- },
110
- dismissButton: {
111
- marginTop: 8,
112
- paddingVertical: 14,
113
- paddingHorizontal: 32,
114
- borderRadius: 12,
115
- minWidth: 140,
116
- alignItems: "center",
117
- },
118
- dismissText: {
119
- color: "#FFFFFF",
120
- fontSize: 16,
121
- fontWeight: "600",
122
- },
123
+ modal: {
124
+ width: "100%",
125
+ maxWidth: 380,
126
+ borderRadius: 24,
127
+ padding: 32,
128
+ borderWidth: 1,
129
+ alignItems: "center",
130
+ },
131
+ iconContainer: {
132
+ marginBottom: 20,
133
+ },
134
+ title: {
135
+ fontWeight: "700",
136
+ marginBottom: 8,
137
+ textAlign: "center",
138
+ },
139
+ message: {
140
+ marginBottom: 28,
141
+ textAlign: "center",
142
+ lineHeight: 20,
143
+ },
144
+ hint: {
145
+ textAlign: "center",
146
+ lineHeight: 18,
147
+ paddingHorizontal: 8,
148
+ },
149
+ dismissButton: {
150
+ marginTop: 16,
151
+ paddingVertical: 14,
152
+ paddingHorizontal: 32,
153
+ borderRadius: 12,
154
+ minWidth: 140,
155
+ alignItems: "center",
156
+ },
157
+ dismissText: {
158
+ color: "#FFFFFF",
159
+ fontWeight: "600",
160
+ },
123
161
  });
@@ -1,30 +1,36 @@
1
1
  /**
2
2
  * GenerationProgressModal
3
- * Generic AI generation progress modal with customizable rendering
3
+ * Generic AI generation progress modal using BaseModal
4
+ * Props-driven for 100+ apps compatibility
4
5
  */
5
6
 
6
7
  import React from "react";
7
- import { Modal, View, StyleSheet } from "react-native";
8
- import { useAppDesignTokens } from "@umituz/react-native-design-system";
8
+ import { StyleSheet } from "react-native";
9
+ import {
10
+ BaseModal,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
9
13
  import {
10
14
  GenerationProgressContent,
11
- GenerationProgressContentProps,
15
+ type GenerationProgressContentProps,
12
16
  } from "./GenerationProgressContent";
13
17
 
14
18
  export interface GenerationProgressRenderProps {
15
- progress: number;
16
- title?: string;
17
- message?: string;
18
- hint?: string;
19
- onDismiss?: () => void;
19
+ readonly progress: number;
20
+ readonly icon?: string;
21
+ readonly title?: string;
22
+ readonly message?: string;
23
+ readonly hint?: string;
24
+ readonly onDismiss?: () => void;
20
25
  }
21
26
 
22
27
  export interface GenerationProgressModalProps
23
28
  extends Omit<GenerationProgressContentProps, "backgroundColor"> {
24
- visible: boolean;
25
- overlayColor?: string;
26
- modalBackgroundColor?: string;
27
- renderContent?: (props: GenerationProgressRenderProps) => React.ReactNode;
29
+ readonly visible: boolean;
30
+ readonly modalBackgroundColor?: string;
31
+ readonly renderContent?: (
32
+ props: GenerationProgressRenderProps
33
+ ) => React.ReactNode;
28
34
  }
29
35
 
30
36
  export const GenerationProgressModal: React.FC<
@@ -32,67 +38,76 @@ export const GenerationProgressModal: React.FC<
32
38
  > = ({
33
39
  visible,
34
40
  progress,
41
+ icon,
35
42
  title,
36
43
  message,
37
44
  hint,
38
45
  dismissLabel,
39
46
  onDismiss,
40
- overlayColor = "rgba(0, 0, 0, 0.7)",
41
47
  modalBackgroundColor,
42
48
  textColor,
49
+ hintColor,
43
50
  progressColor,
44
51
  progressBackgroundColor,
45
52
  dismissButtonColor,
46
53
  renderContent,
47
54
  }) => {
48
- const tokens = useAppDesignTokens();
49
- const clampedProgress = Math.max(0, Math.min(100, progress));
55
+ const tokens = useAppDesignTokens();
56
+ const clampedProgress = Math.max(0, Math.min(100, progress));
57
+
58
+ const handleClose = React.useCallback(() => {
59
+ onDismiss?.();
60
+ }, [onDismiss]);
50
61
 
51
- const content = renderContent ? (
52
- renderContent({
53
- progress: clampedProgress,
54
- title,
55
- message,
56
- hint,
57
- onDismiss,
58
- })
59
- ) : (
60
- <GenerationProgressContent
61
- progress={clampedProgress}
62
- title={title}
63
- message={message}
64
- hint={hint}
65
- dismissLabel={dismissLabel}
66
- onDismiss={onDismiss}
67
- backgroundColor={modalBackgroundColor || tokens.colors.surface}
68
- textColor={textColor || tokens.colors.textPrimary}
69
- progressColor={progressColor || tokens.colors.primary}
70
- progressBackgroundColor={
71
- progressBackgroundColor || tokens.colors.borderLight
72
- }
73
- dismissButtonColor={dismissButtonColor || tokens.colors.primary}
74
- />
75
- );
62
+ const content = renderContent ? (
63
+ renderContent({
64
+ progress: clampedProgress,
65
+ icon,
66
+ title,
67
+ message,
68
+ hint,
69
+ onDismiss,
70
+ })
71
+ ) : (
72
+ <GenerationProgressContent
73
+ progress={clampedProgress}
74
+ icon={icon}
75
+ title={title}
76
+ message={message}
77
+ hint={hint}
78
+ dismissLabel={dismissLabel}
79
+ onDismiss={onDismiss}
80
+ backgroundColor={modalBackgroundColor || tokens.colors.surface}
81
+ textColor={textColor || tokens.colors.textPrimary}
82
+ hintColor={hintColor || tokens.colors.textTertiary}
83
+ progressColor={progressColor || tokens.colors.primary}
84
+ progressBackgroundColor={
85
+ progressBackgroundColor || tokens.colors.surfaceVariant
86
+ }
87
+ dismissButtonColor={dismissButtonColor || tokens.colors.primary}
88
+ />
89
+ );
76
90
 
77
- return (
78
- <Modal
79
- visible={visible}
80
- transparent
81
- animationType="fade"
82
- statusBarTranslucent
83
- >
84
- <View style={[styles.overlay, { backgroundColor: overlayColor }]}>
85
- {content}
86
- </View>
87
- </Modal>
88
- );
89
- };
91
+ return (
92
+ <BaseModal
93
+ visible={visible}
94
+ onClose={handleClose}
95
+ dismissOnBackdrop={false}
96
+ contentStyle={styles.modalContent}
97
+ >
98
+ {content}
99
+ </BaseModal>
100
+ );
101
+ };
90
102
 
91
103
  const styles = StyleSheet.create({
92
- overlay: {
93
- flex: 1,
104
+ modalContent: {
105
+ backgroundColor: "transparent",
106
+ borderWidth: 0,
107
+ padding: 24,
108
+ width: "100%",
109
+ height: "auto",
94
110
  justifyContent: "center",
95
111
  alignItems: "center",
96
- padding: 20,
97
112
  },
98
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
+ };
@@ -0,0 +1,5 @@
1
+ export { PhotoUploadCard } from "./PhotoUploadCard";
2
+ export type {
3
+ PhotoUploadCardProps,
4
+ PhotoUploadCardConfig,
5
+ } from "./PhotoUploadCard";
@@ -27,3 +27,4 @@ export * from "./image-picker";
27
27
  export * from "./buttons";
28
28
  export * from "./display";
29
29
  export * from "./headers";
30
+ export * from "./PhotoUploadCard";