@umituz/react-native-ai-generation-content 1.41.2 → 1.41.4
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.41.
|
|
3
|
+
"version": "1.41.4",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@umituz/react-native-design-system": "^2.9.50",
|
|
70
70
|
"@umituz/react-native-firebase": "^1.13.87",
|
|
71
71
|
"@umituz/react-native-localization": "*",
|
|
72
|
-
"@umituz/react-native-subscription": "
|
|
72
|
+
"@umituz/react-native-subscription": "^2.27.23",
|
|
73
73
|
"eslint": "^9.0.0",
|
|
74
74
|
"expo-apple-authentication": "^8.0.8",
|
|
75
75
|
"expo-application": "^7.0.8",
|
package/src/index.ts
CHANGED
|
@@ -90,7 +90,7 @@ export {
|
|
|
90
90
|
GenerateButton, ResultDisplay, AIGenerationResult, ErrorDisplay, FeatureHeader,
|
|
91
91
|
AIGenScreenHeader, CreditBadge, PhotoUploadCard, SettingsSheet, StyleSelector,
|
|
92
92
|
AspectRatioSelector, DurationSelector, GridSelector, StylePresetsGrid, AIGenerationForm,
|
|
93
|
-
AIGenerationConfig,
|
|
93
|
+
AIGenerationConfig, ModelSelector,
|
|
94
94
|
createAspectRatioOptions, createDurationOptions, createStyleOptions, createStyleOptionsFromConfig,
|
|
95
95
|
ASPECT_RATIO_IDS, COMMON_DURATIONS,
|
|
96
96
|
} from "./presentation/components";
|
|
@@ -121,7 +121,7 @@ export type {
|
|
|
121
121
|
AspectRatioSelectorProps, DurationSelectorProps, GridSelectorProps, GridSelectorOption,
|
|
122
122
|
StyleOption, AspectRatioOption, DurationValue, AspectRatioTranslations, DurationOption,
|
|
123
123
|
StyleTranslations, AIGenerationFormProps, AIGenerationFormTranslations,
|
|
124
|
-
AIGenerationConfigProps,
|
|
124
|
+
AIGenerationConfigProps, ModelOption, ModelSelectorProps,
|
|
125
125
|
} from "./presentation/components";
|
|
126
126
|
|
|
127
127
|
export { DEFAULT_SINGLE_PHOTO_FLOW, DEFAULT_DUAL_PHOTO_FLOW } from "./presentation/types/flow-config.types";
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelSelector Component
|
|
3
|
+
* Generic model selection dropdown for AI generation
|
|
4
|
+
* Package: @umituz/react-native-ai-generation-content
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Universal model selector component that works with any AI model type.
|
|
8
|
+
* Designed for 100+ applications with different model requirements.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <ModelSelector
|
|
13
|
+
* models={videoModels}
|
|
14
|
+
* selectedModel={selected}
|
|
15
|
+
* onSelectModel={(id) => setSelected(id)}
|
|
16
|
+
* label="Select Video Model"
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import React, { useState } from "react";
|
|
22
|
+
import {
|
|
23
|
+
View,
|
|
24
|
+
StyleSheet,
|
|
25
|
+
TouchableOpacity,
|
|
26
|
+
Modal,
|
|
27
|
+
Pressable,
|
|
28
|
+
ScrollView,
|
|
29
|
+
} from "react-native";
|
|
30
|
+
import {
|
|
31
|
+
useAppDesignTokens,
|
|
32
|
+
AtomicText,
|
|
33
|
+
AtomicIcon,
|
|
34
|
+
useSafeAreaInsets,
|
|
35
|
+
} from "@umituz/react-native-design-system";
|
|
36
|
+
|
|
37
|
+
export interface ModelOption {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly description?: string;
|
|
41
|
+
readonly cost?: number;
|
|
42
|
+
readonly badge?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ModelSelectorProps {
|
|
46
|
+
readonly models: ModelOption[];
|
|
47
|
+
readonly selectedModel: ModelOption | null;
|
|
48
|
+
readonly onSelectModel: (modelId: string) => void;
|
|
49
|
+
readonly label?: string;
|
|
50
|
+
readonly isLoading?: boolean;
|
|
51
|
+
readonly translations?: {
|
|
52
|
+
readonly selectModel?: string;
|
|
53
|
+
readonly credits?: string;
|
|
54
|
+
readonly close?: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
|
59
|
+
models,
|
|
60
|
+
selectedModel,
|
|
61
|
+
onSelectModel,
|
|
62
|
+
label,
|
|
63
|
+
isLoading = false,
|
|
64
|
+
translations = {},
|
|
65
|
+
}) => {
|
|
66
|
+
const tokens = useAppDesignTokens();
|
|
67
|
+
const insets = useSafeAreaInsets();
|
|
68
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
selectModel = "Select Model",
|
|
72
|
+
credits = "credits",
|
|
73
|
+
close = "Close",
|
|
74
|
+
} = translations;
|
|
75
|
+
|
|
76
|
+
const handleSelect = (modelId: string) => {
|
|
77
|
+
onSelectModel(modelId);
|
|
78
|
+
setIsOpen(false);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const displayName = selectedModel?.name || label || selectModel;
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<>
|
|
85
|
+
<TouchableOpacity
|
|
86
|
+
style={[
|
|
87
|
+
styles.trigger,
|
|
88
|
+
{
|
|
89
|
+
backgroundColor: tokens.colors.surface,
|
|
90
|
+
borderRadius: tokens.borders.radius.md,
|
|
91
|
+
},
|
|
92
|
+
]}
|
|
93
|
+
onPress={() => setIsOpen(true)}
|
|
94
|
+
disabled={isLoading}
|
|
95
|
+
>
|
|
96
|
+
<AtomicText type="labelMedium" color="textPrimary">
|
|
97
|
+
{displayName}
|
|
98
|
+
</AtomicText>
|
|
99
|
+
<AtomicIcon name="chevron-down" size="xs" color="secondary" />
|
|
100
|
+
</TouchableOpacity>
|
|
101
|
+
|
|
102
|
+
<Modal
|
|
103
|
+
visible={isOpen}
|
|
104
|
+
transparent
|
|
105
|
+
animationType="none"
|
|
106
|
+
onRequestClose={() => setIsOpen(false)}
|
|
107
|
+
>
|
|
108
|
+
<Pressable
|
|
109
|
+
style={[
|
|
110
|
+
styles.overlay,
|
|
111
|
+
{ backgroundColor: tokens.colors.modalOverlay },
|
|
112
|
+
]}
|
|
113
|
+
onPress={() => setIsOpen(false)}
|
|
114
|
+
>
|
|
115
|
+
<View
|
|
116
|
+
style={[
|
|
117
|
+
styles.dropdown,
|
|
118
|
+
{
|
|
119
|
+
backgroundColor: tokens.colors.backgroundSecondary,
|
|
120
|
+
marginTop: insets.top + 50,
|
|
121
|
+
borderRadius: tokens.borders.radius.lg,
|
|
122
|
+
},
|
|
123
|
+
]}
|
|
124
|
+
>
|
|
125
|
+
<View
|
|
126
|
+
style={[
|
|
127
|
+
styles.header,
|
|
128
|
+
{ borderBottomColor: tokens.colors.borderLight },
|
|
129
|
+
]}
|
|
130
|
+
>
|
|
131
|
+
<AtomicText type="titleSmall" color="textPrimary">
|
|
132
|
+
{selectModel}
|
|
133
|
+
</AtomicText>
|
|
134
|
+
<TouchableOpacity
|
|
135
|
+
onPress={() => setIsOpen(false)}
|
|
136
|
+
accessibilityLabel={close}
|
|
137
|
+
accessibilityRole="button"
|
|
138
|
+
>
|
|
139
|
+
<AtomicIcon name="close" size="sm" color="secondary" />
|
|
140
|
+
</TouchableOpacity>
|
|
141
|
+
</View>
|
|
142
|
+
|
|
143
|
+
<ScrollView style={styles.list}>
|
|
144
|
+
{models.map((model) => {
|
|
145
|
+
const isSelected = selectedModel?.id === model.id;
|
|
146
|
+
return (
|
|
147
|
+
<TouchableOpacity
|
|
148
|
+
key={model.id}
|
|
149
|
+
style={[
|
|
150
|
+
styles.option,
|
|
151
|
+
{
|
|
152
|
+
backgroundColor: isSelected
|
|
153
|
+
? `${tokens.colors.primary}15`
|
|
154
|
+
: "transparent",
|
|
155
|
+
},
|
|
156
|
+
]}
|
|
157
|
+
onPress={() => handleSelect(model.id)}
|
|
158
|
+
accessibilityRole="button"
|
|
159
|
+
accessibilityState={{ selected: isSelected }}
|
|
160
|
+
>
|
|
161
|
+
<View style={styles.optionContent}>
|
|
162
|
+
<View style={styles.optionInfo}>
|
|
163
|
+
<AtomicText
|
|
164
|
+
type="bodyLarge"
|
|
165
|
+
style={{
|
|
166
|
+
color: isSelected
|
|
167
|
+
? tokens.colors.primary
|
|
168
|
+
: tokens.colors.textPrimary,
|
|
169
|
+
fontWeight: isSelected ? "600" : "400",
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{model.name}
|
|
173
|
+
</AtomicText>
|
|
174
|
+
{model.description && (
|
|
175
|
+
<AtomicText
|
|
176
|
+
type="bodySmall"
|
|
177
|
+
color="textSecondary"
|
|
178
|
+
numberOfLines={2}
|
|
179
|
+
>
|
|
180
|
+
{model.description}
|
|
181
|
+
</AtomicText>
|
|
182
|
+
)}
|
|
183
|
+
{model.badge && (
|
|
184
|
+
<View
|
|
185
|
+
style={[
|
|
186
|
+
styles.badge,
|
|
187
|
+
{
|
|
188
|
+
backgroundColor: `${tokens.colors.success}20`,
|
|
189
|
+
},
|
|
190
|
+
]}
|
|
191
|
+
>
|
|
192
|
+
<AtomicText type="labelSmall" color="success">
|
|
193
|
+
{model.badge}
|
|
194
|
+
</AtomicText>
|
|
195
|
+
</View>
|
|
196
|
+
)}
|
|
197
|
+
</View>
|
|
198
|
+
<View style={styles.optionRight}>
|
|
199
|
+
{model.cost !== undefined && (
|
|
200
|
+
<AtomicText type="labelSmall" color="textSecondary">
|
|
201
|
+
{model.cost} {credits}
|
|
202
|
+
</AtomicText>
|
|
203
|
+
)}
|
|
204
|
+
{isSelected && (
|
|
205
|
+
<AtomicIcon
|
|
206
|
+
name="checkmark"
|
|
207
|
+
size="sm"
|
|
208
|
+
color="primary"
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
</View>
|
|
212
|
+
</View>
|
|
213
|
+
</TouchableOpacity>
|
|
214
|
+
);
|
|
215
|
+
})}
|
|
216
|
+
</ScrollView>
|
|
217
|
+
</View>
|
|
218
|
+
</Pressable>
|
|
219
|
+
</Modal>
|
|
220
|
+
</>
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const styles = StyleSheet.create({
|
|
225
|
+
trigger: {
|
|
226
|
+
flexDirection: "row",
|
|
227
|
+
alignItems: "center",
|
|
228
|
+
paddingHorizontal: 12,
|
|
229
|
+
paddingVertical: 8,
|
|
230
|
+
gap: 4,
|
|
231
|
+
},
|
|
232
|
+
overlay: {
|
|
233
|
+
flex: 1,
|
|
234
|
+
},
|
|
235
|
+
dropdown: {
|
|
236
|
+
marginHorizontal: 16,
|
|
237
|
+
maxHeight: 400,
|
|
238
|
+
overflow: "hidden",
|
|
239
|
+
},
|
|
240
|
+
header: {
|
|
241
|
+
flexDirection: "row",
|
|
242
|
+
justifyContent: "space-between",
|
|
243
|
+
alignItems: "center",
|
|
244
|
+
paddingHorizontal: 16,
|
|
245
|
+
paddingVertical: 12,
|
|
246
|
+
borderBottomWidth: 1,
|
|
247
|
+
},
|
|
248
|
+
list: {
|
|
249
|
+
maxHeight: 350,
|
|
250
|
+
},
|
|
251
|
+
option: {
|
|
252
|
+
paddingHorizontal: 16,
|
|
253
|
+
paddingVertical: 14,
|
|
254
|
+
},
|
|
255
|
+
optionContent: {
|
|
256
|
+
flexDirection: "row",
|
|
257
|
+
justifyContent: "space-between",
|
|
258
|
+
alignItems: "center",
|
|
259
|
+
gap: 12,
|
|
260
|
+
},
|
|
261
|
+
optionInfo: {
|
|
262
|
+
flex: 1,
|
|
263
|
+
gap: 4,
|
|
264
|
+
},
|
|
265
|
+
optionRight: {
|
|
266
|
+
flexDirection: "row",
|
|
267
|
+
alignItems: "center",
|
|
268
|
+
gap: 8,
|
|
269
|
+
},
|
|
270
|
+
badge: {
|
|
271
|
+
alignSelf: "flex-start",
|
|
272
|
+
paddingHorizontal: 8,
|
|
273
|
+
paddingVertical: 2,
|
|
274
|
+
borderRadius: 4,
|
|
275
|
+
marginTop: 4,
|
|
276
|
+
},
|
|
277
|
+
});
|