@umituz/react-native-ai-generation-content 1.17.218 → 1.17.220

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.
Files changed (42) hide show
  1. package/package.json +1 -1
  2. package/src/domains/creations/presentation/components/CreationActions.tsx +4 -3
  3. package/src/domains/creations/presentation/components/CreationCard.tsx +19 -145
  4. package/src/domains/creations/presentation/components/CreationCard.types.ts +58 -0
  5. package/src/domains/creations/presentation/components/CreationCard.utils.ts +29 -0
  6. package/src/domains/creations/presentation/components/CreationCardMeta.tsx +50 -0
  7. package/src/domains/creations/presentation/components/CreationsFilterBar.helpers.ts +96 -0
  8. package/src/domains/creations/presentation/components/CreationsFilterBar.tsx +6 -121
  9. package/src/domains/creations/presentation/components/CreationsFilterBar.types.ts +47 -0
  10. package/src/domains/creations/presentation/components/useCreationCardActions.ts +73 -0
  11. package/src/features/image-to-video/presentation/components/AddMoreCard.tsx +52 -0
  12. package/src/features/image-to-video/presentation/components/EmptyGridState.tsx +69 -0
  13. package/src/features/image-to-video/presentation/components/GridImageItem.tsx +64 -0
  14. package/src/features/image-to-video/presentation/components/ImageSelectionGrid.styles.ts +84 -0
  15. package/src/features/image-to-video/presentation/components/ImageSelectionGrid.tsx +32 -189
  16. package/src/features/image-to-video/presentation/components/ImageSelectionGrid.types.ts +18 -0
  17. package/src/infrastructure/utils/url-extractor/base-extractor.ts +72 -0
  18. package/src/infrastructure/utils/url-extractor/index.ts +22 -0
  19. package/src/infrastructure/utils/url-extractor/media-extractors.ts +78 -0
  20. package/src/infrastructure/utils/url-extractor/multi-extractor.ts +61 -0
  21. package/src/infrastructure/utils/url-extractor/thumbnail-extractor.ts +31 -0
  22. package/src/infrastructure/utils/url-extractor.util.ts +8 -218
  23. package/src/presentation/components/GenerationProgressContent.styles.ts +68 -0
  24. package/src/presentation/components/GenerationProgressContent.tsx +28 -160
  25. package/src/presentation/components/GenerationProgressContent.types.ts +23 -0
  26. package/src/presentation/components/PhotoUploadCard/ImageContent.tsx +48 -0
  27. package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.styles.ts +112 -0
  28. package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.tsx +37 -193
  29. package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.types.ts +42 -0
  30. package/src/presentation/components/PhotoUploadCard/PlaceholderContent.tsx +45 -0
  31. package/src/presentation/components/PhotoUploadCard/ValidatingContent.tsx +32 -0
  32. package/src/presentation/components/PhotoUploadCard/useBorderColor.ts +31 -0
  33. package/src/presentation/components/ProgressCloseButton.tsx +27 -0
  34. package/src/presentation/components/ProgressDismissButton.tsx +48 -0
  35. package/src/presentation/components/ProgressHeader.tsx +64 -0
  36. package/src/presentation/components/ProgressHint.tsx +62 -0
  37. package/src/presentation/components/result/ActionButton.tsx +64 -0
  38. package/src/presentation/components/result/ResultActions.styles.ts +54 -0
  39. package/src/presentation/components/result/ResultActions.tsx +44 -179
  40. package/src/presentation/components/result/ResultActions.types.ts +20 -0
  41. package/src/presentation/components/result/RetryButton.tsx +39 -0
  42. package/src/presentation/components/result/button-style.utils.ts +56 -0
@@ -5,33 +5,15 @@
5
5
  */
6
6
 
7
7
  import React from "react";
8
- import { View, TouchableOpacity, StyleSheet } from "react-native";
9
- import {
10
- AtomicText,
11
- AtomicIcon,
12
- useAppDesignTokens,
13
- } from "@umituz/react-native-design-system";
8
+ import { View } from "react-native";
9
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
10
+ import { ProgressCloseButton } from "./ProgressCloseButton";
11
+ import { ProgressHeader } from "./ProgressHeader";
12
+ import { ProgressHint } from "./ProgressHint";
13
+ import { ProgressDismissButton } from "./ProgressDismissButton";
14
14
  import { GenerationProgressBar } from "./GenerationProgressBar";
15
-
16
- export interface GenerationProgressContentProps {
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
- /** Close button in top-right corner for background generation */
25
- readonly onClose?: () => void;
26
- /** Hint text shown near close button (e.g., "Continue in background") */
27
- readonly backgroundHint?: string;
28
- readonly backgroundColor?: string;
29
- readonly textColor?: string;
30
- readonly hintColor?: string;
31
- readonly progressColor?: string;
32
- readonly progressBackgroundColor?: string;
33
- readonly dismissButtonColor?: string;
34
- }
15
+ import { generationProgressContentStyles } from "./GenerationProgressContent.styles";
16
+ import type { GenerationProgressContentProps } from "./GenerationProgressContent.types";
35
17
 
36
18
  export const GenerationProgressContent: React.FC<
37
19
  GenerationProgressContentProps
@@ -53,55 +35,26 @@ export const GenerationProgressContent: React.FC<
53
35
  dismissButtonColor,
54
36
  }) => {
55
37
  const tokens = useAppDesignTokens();
56
-
57
- const activeTextColor = textColor || tokens.colors.textPrimary;
58
38
  const activeBgColor = backgroundColor || tokens.colors.surface;
59
- const activeHintColor = hintColor || tokens.colors.textTertiary;
60
39
 
61
40
  return (
62
41
  <View
63
42
  style={[
64
- styles.modal,
43
+ generationProgressContentStyles.modal,
65
44
  {
66
45
  backgroundColor: activeBgColor,
67
46
  borderColor: tokens.colors.borderLight,
68
47
  },
69
48
  ]}
70
49
  >
71
- {/* Close button in top-right corner */}
72
- {onClose && (
73
- <TouchableOpacity
74
- style={styles.closeButton}
75
- onPress={onClose}
76
- hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
77
- >
78
- <AtomicIcon name="close" size="md" color="secondary" />
79
- </TouchableOpacity>
80
- )}
81
-
82
- {icon && (
83
- <View style={styles.iconContainer}>
84
- <AtomicIcon name={icon} size="xl" color="primary" />
85
- </View>
86
- )}
87
-
88
- {title && (
89
- <AtomicText
90
- type="headlineSmall"
91
- style={[styles.title, { color: activeTextColor }]}
92
- >
93
- {title}
94
- </AtomicText>
95
- )}
50
+ {onClose && <ProgressCloseButton onPress={onClose} />}
96
51
 
97
- {message && (
98
- <AtomicText
99
- type="bodyMedium"
100
- style={[styles.message, { color: tokens.colors.textSecondary }]}
101
- >
102
- {message}
103
- </AtomicText>
104
- )}
52
+ <ProgressHeader
53
+ icon={icon}
54
+ title={title}
55
+ message={message}
56
+ textColor={textColor}
57
+ />
105
58
 
106
59
  <GenerationProgressBar
107
60
  progress={progress}
@@ -110,107 +63,22 @@ export const GenerationProgressContent: React.FC<
110
63
  backgroundColor={progressBackgroundColor}
111
64
  />
112
65
 
113
- {hint && (
114
- <AtomicText
115
- type="bodySmall"
116
- style={[styles.hint, { color: activeHintColor }]}
117
- >
118
- {hint}
119
- </AtomicText>
120
- )}
121
-
122
- {/* Background hint - clickable to close and continue in background */}
123
- {onClose && backgroundHint && (
124
- <TouchableOpacity
125
- style={styles.backgroundHintButton}
126
- onPress={onClose}
127
- >
128
- <AtomicText
129
- type="bodySmall"
130
- style={[styles.backgroundHintText, { color: tokens.colors.primary }]}
131
- >
132
- {backgroundHint}
133
- </AtomicText>
134
- </TouchableOpacity>
135
- )}
66
+ <ProgressHint
67
+ hint={hint}
68
+ backgroundHint={backgroundHint}
69
+ hintColor={hintColor}
70
+ onBackgroundHintPress={onClose}
71
+ />
136
72
 
137
73
  {onDismiss && (
138
- <TouchableOpacity
139
- style={[
140
- styles.dismissButton,
141
- { backgroundColor: dismissButtonColor || tokens.colors.primary },
142
- ]}
143
- onPress={onDismiss}
144
- >
145
- <AtomicText
146
- type="bodyMedium"
147
- style={[styles.dismissText, { color: tokens.colors.textInverse }]}
148
- >
149
- {dismissLabel || "OK"}
150
- </AtomicText>
151
- </TouchableOpacity>
74
+ <ProgressDismissButton
75
+ dismissLabel={dismissLabel}
76
+ dismissButtonColor={dismissButtonColor}
77
+ onDismiss={onDismiss}
78
+ />
152
79
  )}
153
80
  </View>
154
81
  );
155
82
  };
156
83
 
157
- const styles = StyleSheet.create({
158
- modal: {
159
- width: "100%",
160
- maxWidth: 380,
161
- borderRadius: 24,
162
- padding: 32,
163
- borderWidth: 1,
164
- alignItems: "center",
165
- position: "relative",
166
- },
167
- closeButton: {
168
- position: "absolute",
169
- top: 16,
170
- right: 16,
171
- width: 32,
172
- height: 32,
173
- borderRadius: 16,
174
- justifyContent: "center",
175
- alignItems: "center",
176
- zIndex: 1,
177
- },
178
- iconContainer: {
179
- marginBottom: 20,
180
- },
181
- title: {
182
- fontWeight: "700",
183
- marginBottom: 8,
184
- textAlign: "center",
185
- },
186
- message: {
187
- marginBottom: 28,
188
- textAlign: "center",
189
- lineHeight: 20,
190
- },
191
- hint: {
192
- textAlign: "center",
193
- lineHeight: 18,
194
- paddingHorizontal: 8,
195
- },
196
- backgroundHintButton: {
197
- marginTop: 16,
198
- paddingVertical: 8,
199
- paddingHorizontal: 16,
200
- },
201
- backgroundHintText: {
202
- textAlign: "center",
203
- textDecorationLine: "underline",
204
- },
205
- dismissButton: {
206
- marginTop: 16,
207
- paddingVertical: 14,
208
- paddingHorizontal: 32,
209
- borderRadius: 12,
210
- minWidth: 140,
211
- alignItems: "center",
212
- },
213
- dismissText: {
214
- fontWeight: "600",
215
- },
216
- });
84
+ export type { GenerationProgressContentProps } from "./GenerationProgressContent.types";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * GenerationProgressContent Type Definitions
3
+ */
4
+
5
+ export interface GenerationProgressContentProps {
6
+ readonly progress: number;
7
+ readonly icon?: string;
8
+ readonly title?: string;
9
+ readonly message?: string;
10
+ readonly hint?: string;
11
+ readonly dismissLabel?: string;
12
+ readonly onDismiss?: () => void;
13
+ /** Close button in top-right corner for background generation */
14
+ readonly onClose?: () => void;
15
+ /** Hint text shown near close button (e.g., "Continue in background") */
16
+ readonly backgroundHint?: string;
17
+ readonly backgroundColor?: string;
18
+ readonly textColor?: string;
19
+ readonly hintColor?: string;
20
+ readonly progressColor?: string;
21
+ readonly progressBackgroundColor?: string;
22
+ readonly dismissButtonColor?: string;
23
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ImageContent Component
3
+ * Displays uploaded image with change button
4
+ */
5
+
6
+ import React from "react";
7
+ import { Image, TouchableOpacity, View } from "react-native";
8
+ import {
9
+ AtomicIcon,
10
+ AtomicText,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import type { PhotoUploadCardStyles } from "./PhotoUploadCard.styles";
14
+
15
+ interface ImageContentProps {
16
+ styles: PhotoUploadCardStyles;
17
+ imageUri: string;
18
+ allowChange: boolean;
19
+ changeText: string;
20
+ onPress: () => void;
21
+ }
22
+
23
+ export function ImageContent({
24
+ styles,
25
+ imageUri,
26
+ allowChange,
27
+ changeText,
28
+ onPress,
29
+ }: ImageContentProps) {
30
+ const tokens = useAppDesignTokens();
31
+
32
+ return (
33
+ <>
34
+ <Image source={{ uri: imageUri }} style={styles.image} />
35
+ <View style={styles.imageOverlay} />
36
+ {allowChange && (
37
+ <TouchableOpacity style={styles.changeButton} onPress={onPress}>
38
+ <AtomicIcon
39
+ name="camera"
40
+ size={18}
41
+ customColor={tokens.colors.primary}
42
+ />
43
+ <AtomicText style={styles.changeText}>{changeText}</AtomicText>
44
+ </TouchableOpacity>
45
+ )}
46
+ </>
47
+ );
48
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * PhotoUploadCard Styles
3
+ */
4
+
5
+ import { StyleSheet } from "react-native";
6
+ import type { DesignTokens } from "@umituz/react-native-design-system";
7
+ import type { PhotoUploadCardConfig } from "./PhotoUploadCard.types";
8
+
9
+ export type PhotoUploadCardStyles = ReturnType<typeof createPhotoUploadCardStyles>;
10
+
11
+ interface CreateStylesParams {
12
+ tokens: DesignTokens;
13
+ imageUri: string | null;
14
+ config: PhotoUploadCardConfig;
15
+ }
16
+
17
+ export function createPhotoUploadCardStyles({
18
+ tokens,
19
+ imageUri,
20
+ config,
21
+ }: CreateStylesParams) {
22
+ return StyleSheet.create({
23
+ container: {
24
+ marginHorizontal: 24,
25
+ marginBottom: 24,
26
+ },
27
+ card: {
28
+ aspectRatio: config.aspectRatio,
29
+ backgroundColor: tokens.colors.surfaceSecondary,
30
+ borderRadius: config.borderRadius,
31
+ justifyContent: "center",
32
+ alignItems: "center",
33
+ overflow: "hidden",
34
+ borderWidth: 2,
35
+ borderStyle: imageUri ? "solid" : config.borderStyle,
36
+ },
37
+ placeholder: {
38
+ alignItems: "center",
39
+ padding: 32,
40
+ },
41
+ iconCircle: {
42
+ width: 88,
43
+ height: 88,
44
+ borderRadius: 44,
45
+ justifyContent: "center",
46
+ alignItems: "center",
47
+ marginBottom: 20,
48
+ borderWidth: 2,
49
+ borderColor: tokens.colors.borderLight,
50
+ backgroundColor: tokens.colors.surfaceSecondary,
51
+ },
52
+ title: {
53
+ fontSize: 20,
54
+ fontWeight: "700",
55
+ color: tokens.colors.textPrimary,
56
+ marginBottom: 8,
57
+ letterSpacing: 0.3,
58
+ },
59
+ subtitle: {
60
+ fontSize: 14,
61
+ color: tokens.colors.textSecondary,
62
+ textAlign: "center",
63
+ lineHeight: 20,
64
+ maxWidth: 240,
65
+ },
66
+ image: {
67
+ width: "100%",
68
+ height: "100%",
69
+ resizeMode: "cover",
70
+ },
71
+ imageOverlay: {
72
+ ...StyleSheet.absoluteFillObject,
73
+ backgroundColor: tokens.colors.modalOverlay,
74
+ opacity: 0.3,
75
+ },
76
+ changeButton: {
77
+ position: "absolute",
78
+ bottom: 20,
79
+ right: 20,
80
+ backgroundColor: tokens.colors.surface,
81
+ paddingHorizontal: 18,
82
+ paddingVertical: 12,
83
+ borderRadius: 28,
84
+ flexDirection: "row",
85
+ alignItems: "center",
86
+ gap: 8,
87
+ },
88
+ changeText: {
89
+ fontSize: 14,
90
+ fontWeight: "700",
91
+ color: tokens.colors.primary,
92
+ },
93
+ validatingContainer: {
94
+ alignItems: "center",
95
+ padding: 32,
96
+ },
97
+ validatingText: {
98
+ fontSize: 16,
99
+ fontWeight: "600",
100
+ color: tokens.colors.primary,
101
+ marginTop: 20,
102
+ },
103
+ pulseRing: {
104
+ position: "absolute",
105
+ width: 100,
106
+ height: 100,
107
+ borderRadius: 50,
108
+ borderWidth: 2,
109
+ borderColor: tokens.colors.borderLight,
110
+ },
111
+ });
112
+ }
@@ -6,58 +6,15 @@
6
6
  */
7
7
 
8
8
  import React, { useMemo } from "react";
9
- import {
10
- View,
11
- Image,
12
- StyleSheet,
13
- Pressable,
14
- TouchableOpacity,
15
- type ViewStyle,
16
- type StyleProp,
17
- } from "react-native";
18
- import {
19
- AtomicText,
20
- AtomicIcon,
21
- AtomicSpinner,
22
- useAppDesignTokens,
23
- } from "@umituz/react-native-design-system";
24
-
25
- export interface PhotoUploadCardConfig {
26
- aspectRatio?: number;
27
- borderRadius?: number;
28
- iconSize?: number;
29
- showValidationStatus?: boolean;
30
- allowChange?: boolean;
31
- borderStyle?: "solid" | "dashed";
32
- }
33
-
34
- export interface PhotoUploadCardProps {
35
- imageUri: string | null;
36
- onPress: () => void;
37
- isValidating?: boolean;
38
- isValid?: boolean | null;
39
- disabled?: boolean;
40
- config?: PhotoUploadCardConfig;
41
- translations?: {
42
- tapToUpload: string;
43
- selectPhoto: string;
44
- change: string;
45
- analyzing?: string;
46
- };
47
- title?: string;
48
- subtitle?: string;
49
- icon?: string;
50
- style?: StyleProp<ViewStyle>;
51
- }
52
-
53
- const DEFAULT_CONFIG: PhotoUploadCardConfig = {
54
- aspectRatio: 1,
55
- borderRadius: 28,
56
- iconSize: 40,
57
- showValidationStatus: true,
58
- allowChange: true,
59
- borderStyle: "dashed",
60
- };
9
+ import { View, Pressable } from "react-native";
10
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
11
+ import { ValidatingContent } from "./ValidatingContent";
12
+ import { ImageContent } from "./ImageContent";
13
+ import { PlaceholderContent } from "./PlaceholderContent";
14
+ import { useBorderColor } from "./useBorderColor";
15
+ import { createPhotoUploadCardStyles } from "./PhotoUploadCard.styles";
16
+ import type { PhotoUploadCardProps } from "./PhotoUploadCard.types";
17
+ import { DEFAULT_CONFIG } from "./PhotoUploadCard.types";
61
18
 
62
19
  export const PhotoUploadCard: React.FC<PhotoUploadCardProps> = ({
63
20
  imageUri,
@@ -65,7 +22,7 @@ export const PhotoUploadCard: React.FC<PhotoUploadCardProps> = ({
65
22
  isValidating = false,
66
23
  isValid = null,
67
24
  disabled = false,
68
- config = DEFAULT_CONFIG,
25
+ config,
69
26
  translations = {
70
27
  tapToUpload: "Tap to Upload",
71
28
  selectPhoto: "Select a photo",
@@ -79,110 +36,15 @@ export const PhotoUploadCard: React.FC<PhotoUploadCardProps> = ({
79
36
  }) => {
80
37
  const tokens = useAppDesignTokens();
81
38
  const cfg = { ...DEFAULT_CONFIG, ...config };
82
-
83
- const borderColor = useMemo(() => {
84
- if (!cfg.showValidationStatus) {
85
- return tokens.colors.borderLight;
86
- }
87
- if (isValidating) return tokens.colors.primary;
88
- if (isValid === true) return tokens.colors.success;
89
- if (isValid === false) return tokens.colors.error;
90
- return tokens.colors.borderLight;
91
- }, [isValidating, isValid, tokens, cfg.showValidationStatus]);
39
+ const borderColor = useBorderColor({
40
+ isValidating,
41
+ isValid,
42
+ showValidationStatus: cfg.showValidationStatus ?? true,
43
+ });
92
44
 
93
45
  const styles = useMemo(
94
- () =>
95
- StyleSheet.create({
96
- container: {
97
- marginHorizontal: 24,
98
- marginBottom: 24,
99
- },
100
- card: {
101
- aspectRatio: cfg.aspectRatio,
102
- backgroundColor: tokens.colors.surfaceSecondary,
103
- borderRadius: cfg.borderRadius,
104
- justifyContent: "center",
105
- alignItems: "center",
106
- overflow: "hidden",
107
- borderWidth: 2,
108
- borderStyle: imageUri ? "solid" : cfg.borderStyle,
109
- },
110
- placeholder: {
111
- alignItems: "center",
112
- padding: 32,
113
- },
114
- iconCircle: {
115
- width: 88,
116
- height: 88,
117
- borderRadius: 44,
118
- justifyContent: "center",
119
- alignItems: "center",
120
- marginBottom: 20,
121
- borderWidth: 2,
122
- borderColor: tokens.colors.borderLight,
123
- backgroundColor: tokens.colors.surfaceSecondary,
124
- },
125
- title: {
126
- fontSize: 20,
127
- fontWeight: "700",
128
- color: tokens.colors.textPrimary,
129
- marginBottom: 8,
130
- letterSpacing: 0.3,
131
- },
132
- subtitle: {
133
- fontSize: 14,
134
- color: tokens.colors.textSecondary,
135
- textAlign: "center",
136
- lineHeight: 20,
137
- maxWidth: 240,
138
- },
139
- image: {
140
- width: "100%",
141
- height: "100%",
142
- resizeMode: "cover",
143
- },
144
- imageOverlay: {
145
- ...StyleSheet.absoluteFillObject,
146
- backgroundColor: tokens.colors.modalOverlay,
147
- opacity: 0.3,
148
- },
149
- changeButton: {
150
- position: "absolute",
151
- bottom: 20,
152
- right: 20,
153
- backgroundColor: tokens.colors.surface,
154
- paddingHorizontal: 18,
155
- paddingVertical: 12,
156
- borderRadius: 28,
157
- flexDirection: "row",
158
- alignItems: "center",
159
- gap: 8,
160
- },
161
- changeText: {
162
- fontSize: 14,
163
- fontWeight: "700",
164
- color: tokens.colors.primary,
165
- },
166
- validatingContainer: {
167
- alignItems: "center",
168
- padding: 32,
169
- },
170
- validatingText: {
171
- fontSize: 16,
172
- fontWeight: "600",
173
- color: tokens.colors.primary,
174
- marginTop: 20,
175
- },
176
- pulseRing: {
177
- position: "absolute",
178
- width: 100,
179
- height: 100,
180
- borderRadius: 50,
181
- borderWidth: 2,
182
- borderColor: tokens.colors.borderLight,
183
- },
184
- }),
185
- [tokens, imageUri, cfg],
46
+ () => createPhotoUploadCardStyles({ tokens, imageUri, config: cfg }),
47
+ [tokens, imageUri, cfg]
186
48
  );
187
49
 
188
50
  return (
@@ -193,49 +55,31 @@ export const PhotoUploadCard: React.FC<PhotoUploadCardProps> = ({
193
55
  disabled={disabled || isValidating}
194
56
  >
195
57
  {isValidating ? (
196
- <View style={styles.validatingContainer}>
197
- <View style={styles.pulseRing} />
198
- <AtomicSpinner size="lg" color="primary" />
199
- <AtomicText style={styles.validatingText}>
200
- {translations.analyzing || "Analyzing..."}
201
- </AtomicText>
202
- </View>
58
+ <ValidatingContent
59
+ styles={styles}
60
+ analyzingText={translations.analyzing}
61
+ />
203
62
  ) : imageUri ? (
204
- <>
205
- <Image source={{ uri: imageUri }} style={styles.image} />
206
- <View style={styles.imageOverlay} />
207
- {cfg.allowChange && (
208
- <TouchableOpacity style={styles.changeButton} onPress={onPress}>
209
- <AtomicIcon
210
- name="camera"
211
- size={18}
212
- customColor={tokens.colors.primary}
213
- />
214
- <AtomicText style={styles.changeText}>
215
- {translations.change}
216
- </AtomicText>
217
- </TouchableOpacity>
218
- )}
219
- </>
63
+ <ImageContent
64
+ styles={styles}
65
+ imageUri={imageUri}
66
+ allowChange={cfg.allowChange ?? true}
67
+ changeText={translations.change}
68
+ onPress={onPress}
69
+ />
220
70
  ) : (
221
- <View style={styles.placeholder}>
222
- <View style={styles.iconCircle}>
223
- <AtomicIcon
224
- name={(icon as string) || "camera"}
225
- size={cfg.iconSize}
226
- customColor={tokens.colors.primary}
227
- />
228
- </View>
229
- <AtomicText style={styles.title}>
230
- {title || translations.tapToUpload}
231
- </AtomicText>
232
- <AtomicText style={styles.subtitle}>
233
- {subtitle || translations.selectPhoto}
234
- </AtomicText>
235
- </View>
71
+ <PlaceholderContent
72
+ styles={styles}
73
+ title={title || translations.tapToUpload}
74
+ subtitle={subtitle || translations.selectPhoto}
75
+ icon={icon || "camera"}
76
+ iconSize={cfg.iconSize ?? 40}
77
+ />
236
78
  )}
237
79
  </Pressable>
238
80
  </View>
239
81
  );
240
82
  };
241
83
 
84
+ export type { PhotoUploadCardProps, PhotoUploadCardConfig } from "./PhotoUploadCard.types";
85
+ export { DEFAULT_CONFIG } from "./PhotoUploadCard.types";