@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.
- package/package.json +1 -1
- package/src/domains/creations/presentation/components/CreationActions.tsx +4 -3
- package/src/domains/creations/presentation/components/CreationCard.tsx +19 -145
- package/src/domains/creations/presentation/components/CreationCard.types.ts +58 -0
- package/src/domains/creations/presentation/components/CreationCard.utils.ts +29 -0
- package/src/domains/creations/presentation/components/CreationCardMeta.tsx +50 -0
- package/src/domains/creations/presentation/components/CreationsFilterBar.helpers.ts +96 -0
- package/src/domains/creations/presentation/components/CreationsFilterBar.tsx +6 -121
- package/src/domains/creations/presentation/components/CreationsFilterBar.types.ts +47 -0
- package/src/domains/creations/presentation/components/useCreationCardActions.ts +73 -0
- package/src/features/image-to-video/presentation/components/AddMoreCard.tsx +52 -0
- package/src/features/image-to-video/presentation/components/EmptyGridState.tsx +69 -0
- package/src/features/image-to-video/presentation/components/GridImageItem.tsx +64 -0
- package/src/features/image-to-video/presentation/components/ImageSelectionGrid.styles.ts +84 -0
- package/src/features/image-to-video/presentation/components/ImageSelectionGrid.tsx +32 -189
- package/src/features/image-to-video/presentation/components/ImageSelectionGrid.types.ts +18 -0
- package/src/infrastructure/utils/url-extractor/base-extractor.ts +72 -0
- package/src/infrastructure/utils/url-extractor/index.ts +22 -0
- package/src/infrastructure/utils/url-extractor/media-extractors.ts +78 -0
- package/src/infrastructure/utils/url-extractor/multi-extractor.ts +61 -0
- package/src/infrastructure/utils/url-extractor/thumbnail-extractor.ts +31 -0
- package/src/infrastructure/utils/url-extractor.util.ts +8 -218
- package/src/presentation/components/GenerationProgressContent.styles.ts +68 -0
- package/src/presentation/components/GenerationProgressContent.tsx +28 -160
- package/src/presentation/components/GenerationProgressContent.types.ts +23 -0
- package/src/presentation/components/PhotoUploadCard/ImageContent.tsx +48 -0
- package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.styles.ts +112 -0
- package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.tsx +37 -193
- package/src/presentation/components/PhotoUploadCard/PhotoUploadCard.types.ts +42 -0
- package/src/presentation/components/PhotoUploadCard/PlaceholderContent.tsx +45 -0
- package/src/presentation/components/PhotoUploadCard/ValidatingContent.tsx +32 -0
- package/src/presentation/components/PhotoUploadCard/useBorderColor.ts +31 -0
- package/src/presentation/components/ProgressCloseButton.tsx +27 -0
- package/src/presentation/components/ProgressDismissButton.tsx +48 -0
- package/src/presentation/components/ProgressHeader.tsx +64 -0
- package/src/presentation/components/ProgressHint.tsx +62 -0
- package/src/presentation/components/result/ActionButton.tsx +64 -0
- package/src/presentation/components/result/ResultActions.styles.ts +54 -0
- package/src/presentation/components/result/ResultActions.tsx +44 -179
- package/src/presentation/components/result/ResultActions.types.ts +20 -0
- package/src/presentation/components/result/RetryButton.tsx +39 -0
- 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
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "
|
|
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
|
-
|
|
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
|
-
|
|
43
|
+
generationProgressContentStyles.modal,
|
|
65
44
|
{
|
|
66
45
|
backgroundColor: activeBgColor,
|
|
67
46
|
borderColor: tokens.colors.borderLight,
|
|
68
47
|
},
|
|
69
48
|
]}
|
|
70
49
|
>
|
|
71
|
-
{
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from "
|
|
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
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{translations.analyzing || "Analyzing..."}
|
|
201
|
-
</AtomicText>
|
|
202
|
-
</View>
|
|
58
|
+
<ValidatingContent
|
|
59
|
+
styles={styles}
|
|
60
|
+
analyzingText={translations.analyzing}
|
|
61
|
+
/>
|
|
203
62
|
) : imageUri ? (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
{cfg.allowChange
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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";
|