@umituz/react-native-ai-generation-content 1.17.28 → 1.17.29
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 +11 -0
- package/src/presentation/components/index.ts +1 -0
- package/src/presentation/components/selectors/AspectRatioSelector.tsx +105 -0
- package/src/presentation/components/selectors/DurationSelector.tsx +98 -0
- package/src/presentation/components/selectors/StyleSelector.tsx +131 -0
- package/src/presentation/components/selectors/index.ts +18 -0
- package/src/presentation/components/selectors/types.ts +32 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -275,6 +275,10 @@ export {
|
|
|
275
275
|
FeatureHeader,
|
|
276
276
|
// Photo Upload
|
|
277
277
|
PhotoUploadCard,
|
|
278
|
+
// Selectors
|
|
279
|
+
StyleSelector,
|
|
280
|
+
AspectRatioSelector,
|
|
281
|
+
DurationSelector,
|
|
278
282
|
} from "./presentation/components";
|
|
279
283
|
|
|
280
284
|
export type {
|
|
@@ -314,6 +318,13 @@ export type {
|
|
|
314
318
|
// Photo Upload
|
|
315
319
|
PhotoUploadCardProps,
|
|
316
320
|
PhotoUploadCardConfig,
|
|
321
|
+
// Selectors
|
|
322
|
+
StyleSelectorProps,
|
|
323
|
+
AspectRatioSelectorProps,
|
|
324
|
+
DurationSelectorProps,
|
|
325
|
+
StyleOption,
|
|
326
|
+
AspectRatioOption,
|
|
327
|
+
DurationValue,
|
|
317
328
|
} from "./presentation/components";
|
|
318
329
|
|
|
319
330
|
// =============================================================================
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aspect Ratio Selector Component
|
|
3
|
+
* Generic, props-driven aspect ratio selection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
AtomicIcon,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
} from "@umituz/react-native-design-system";
|
|
13
|
+
import type { AspectRatioOption } from "./types";
|
|
14
|
+
|
|
15
|
+
export interface AspectRatioSelectorProps {
|
|
16
|
+
ratios: AspectRatioOption[];
|
|
17
|
+
selectedRatio: "16:9" | "9:16" | "1:1";
|
|
18
|
+
onRatioSelect: (ratio: "16:9" | "9:16" | "1:1") => void;
|
|
19
|
+
title: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
|
|
23
|
+
ratios,
|
|
24
|
+
selectedRatio,
|
|
25
|
+
onRatioSelect,
|
|
26
|
+
title,
|
|
27
|
+
}) => {
|
|
28
|
+
const tokens = useAppDesignTokens();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<View style={componentStyles.section}>
|
|
32
|
+
<AtomicText
|
|
33
|
+
type="bodyMedium"
|
|
34
|
+
style={{
|
|
35
|
+
color: tokens.colors.textPrimary,
|
|
36
|
+
fontWeight: "600",
|
|
37
|
+
marginBottom: 12,
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{title}
|
|
41
|
+
</AtomicText>
|
|
42
|
+
<View style={componentStyles.aspectRatioGrid}>
|
|
43
|
+
{ratios.map((ratio) => (
|
|
44
|
+
<TouchableOpacity
|
|
45
|
+
key={ratio.id}
|
|
46
|
+
style={[
|
|
47
|
+
componentStyles.aspectRatioCard,
|
|
48
|
+
{
|
|
49
|
+
backgroundColor:
|
|
50
|
+
selectedRatio === ratio.id
|
|
51
|
+
? tokens.colors.primary + "20"
|
|
52
|
+
: tokens.colors.surface,
|
|
53
|
+
borderColor:
|
|
54
|
+
selectedRatio === ratio.id
|
|
55
|
+
? tokens.colors.primary
|
|
56
|
+
: tokens.colors.borderLight,
|
|
57
|
+
},
|
|
58
|
+
]}
|
|
59
|
+
onPress={() => onRatioSelect(ratio.id)}
|
|
60
|
+
>
|
|
61
|
+
<AtomicIcon
|
|
62
|
+
name={ratio.icon as never}
|
|
63
|
+
size="lg"
|
|
64
|
+
color={selectedRatio === ratio.id ? "primary" : "secondary"}
|
|
65
|
+
/>
|
|
66
|
+
<AtomicText
|
|
67
|
+
type="bodySmall"
|
|
68
|
+
style={{
|
|
69
|
+
color: tokens.colors.textPrimary,
|
|
70
|
+
fontWeight: selectedRatio === ratio.id ? "600" : "400",
|
|
71
|
+
marginTop: 8,
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
{ratio.name}
|
|
75
|
+
</AtomicText>
|
|
76
|
+
<AtomicText
|
|
77
|
+
type="labelSmall"
|
|
78
|
+
style={{ color: tokens.colors.textSecondary, marginTop: 2 }}
|
|
79
|
+
>
|
|
80
|
+
{ratio.description}
|
|
81
|
+
</AtomicText>
|
|
82
|
+
</TouchableOpacity>
|
|
83
|
+
))}
|
|
84
|
+
</View>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const componentStyles = StyleSheet.create({
|
|
90
|
+
section: {
|
|
91
|
+
padding: 16,
|
|
92
|
+
marginBottom: 8,
|
|
93
|
+
},
|
|
94
|
+
aspectRatioGrid: {
|
|
95
|
+
flexDirection: "row",
|
|
96
|
+
gap: 12,
|
|
97
|
+
},
|
|
98
|
+
aspectRatioCard: {
|
|
99
|
+
flex: 1,
|
|
100
|
+
padding: 16,
|
|
101
|
+
borderRadius: 12,
|
|
102
|
+
borderWidth: 2,
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duration Selector Component
|
|
3
|
+
* Generic, props-driven duration selection for video/audio generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
useAppDesignTokens,
|
|
11
|
+
} from "@umituz/react-native-design-system";
|
|
12
|
+
import type { DurationValue } from "./types";
|
|
13
|
+
|
|
14
|
+
export interface DurationSelectorProps<T extends DurationValue> {
|
|
15
|
+
duration: T;
|
|
16
|
+
durationOptions: readonly T[];
|
|
17
|
+
onDurationSelect: (duration: T) => void;
|
|
18
|
+
title: string;
|
|
19
|
+
formatLabel?: (duration: T) => string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function DurationSelector<T extends DurationValue>({
|
|
23
|
+
duration,
|
|
24
|
+
durationOptions,
|
|
25
|
+
onDurationSelect,
|
|
26
|
+
title,
|
|
27
|
+
formatLabel = (d) => `${d}s`,
|
|
28
|
+
}: DurationSelectorProps<T>): React.ReactElement {
|
|
29
|
+
const tokens = useAppDesignTokens();
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View style={componentStyles.section}>
|
|
33
|
+
<AtomicText
|
|
34
|
+
type="bodyMedium"
|
|
35
|
+
style={{
|
|
36
|
+
color: tokens.colors.textPrimary,
|
|
37
|
+
fontWeight: "600",
|
|
38
|
+
marginBottom: 12,
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
{title}
|
|
42
|
+
</AtomicText>
|
|
43
|
+
<View style={componentStyles.durationGrid}>
|
|
44
|
+
{durationOptions.map((sec) => (
|
|
45
|
+
<TouchableOpacity
|
|
46
|
+
key={sec}
|
|
47
|
+
style={[
|
|
48
|
+
componentStyles.durationButton,
|
|
49
|
+
{
|
|
50
|
+
backgroundColor:
|
|
51
|
+
duration === sec
|
|
52
|
+
? tokens.colors.primary
|
|
53
|
+
: tokens.colors.surface,
|
|
54
|
+
borderColor:
|
|
55
|
+
duration === sec
|
|
56
|
+
? tokens.colors.primary
|
|
57
|
+
: tokens.colors.borderLight,
|
|
58
|
+
},
|
|
59
|
+
]}
|
|
60
|
+
onPress={() => onDurationSelect(sec)}
|
|
61
|
+
>
|
|
62
|
+
<AtomicText
|
|
63
|
+
type="bodyLarge"
|
|
64
|
+
style={{
|
|
65
|
+
color:
|
|
66
|
+
duration === sec
|
|
67
|
+
? tokens.colors.textInverse
|
|
68
|
+
: tokens.colors.textPrimary,
|
|
69
|
+
fontWeight: duration === sec ? "700" : "400",
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{formatLabel(sec)}
|
|
73
|
+
</AtomicText>
|
|
74
|
+
</TouchableOpacity>
|
|
75
|
+
))}
|
|
76
|
+
</View>
|
|
77
|
+
</View>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const componentStyles = StyleSheet.create({
|
|
82
|
+
section: {
|
|
83
|
+
padding: 16,
|
|
84
|
+
marginBottom: 8,
|
|
85
|
+
},
|
|
86
|
+
durationGrid: {
|
|
87
|
+
flexDirection: "row",
|
|
88
|
+
gap: 12,
|
|
89
|
+
},
|
|
90
|
+
durationButton: {
|
|
91
|
+
flex: 1,
|
|
92
|
+
paddingVertical: 20,
|
|
93
|
+
borderRadius: 12,
|
|
94
|
+
borderWidth: 2,
|
|
95
|
+
alignItems: "center",
|
|
96
|
+
justifyContent: "center",
|
|
97
|
+
},
|
|
98
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style Selector Component
|
|
3
|
+
* Generic, props-driven style selection for AI generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, ScrollView, TouchableOpacity, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
AtomicIcon,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
} from "@umituz/react-native-design-system";
|
|
13
|
+
import type { StyleOption } from "./types";
|
|
14
|
+
|
|
15
|
+
export interface StyleSelectorProps {
|
|
16
|
+
styles: StyleOption[];
|
|
17
|
+
selectedStyle: string;
|
|
18
|
+
onStyleSelect: (styleId: string) => void;
|
|
19
|
+
title: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const StyleSelector: React.FC<StyleSelectorProps> = ({
|
|
23
|
+
styles,
|
|
24
|
+
selectedStyle,
|
|
25
|
+
onStyleSelect,
|
|
26
|
+
title,
|
|
27
|
+
}) => {
|
|
28
|
+
const tokens = useAppDesignTokens();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<View style={componentStyles.section}>
|
|
32
|
+
<AtomicText
|
|
33
|
+
type="bodyMedium"
|
|
34
|
+
style={{
|
|
35
|
+
color: tokens.colors.textPrimary,
|
|
36
|
+
fontWeight: "600",
|
|
37
|
+
marginBottom: 12,
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{title}
|
|
41
|
+
</AtomicText>
|
|
42
|
+
<ScrollView
|
|
43
|
+
horizontal
|
|
44
|
+
showsHorizontalScrollIndicator={false}
|
|
45
|
+
style={componentStyles.stylesScroll}
|
|
46
|
+
>
|
|
47
|
+
{styles.map((style) => {
|
|
48
|
+
const isSelected = selectedStyle === style.id;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<TouchableOpacity
|
|
52
|
+
key={style.id}
|
|
53
|
+
style={[
|
|
54
|
+
componentStyles.styleCard,
|
|
55
|
+
{
|
|
56
|
+
backgroundColor: isSelected
|
|
57
|
+
? tokens.colors.primary
|
|
58
|
+
: tokens.colors.surface,
|
|
59
|
+
borderColor: isSelected
|
|
60
|
+
? tokens.colors.primary
|
|
61
|
+
: tokens.colors.borderLight,
|
|
62
|
+
},
|
|
63
|
+
]}
|
|
64
|
+
onPress={() => onStyleSelect(style.id)}
|
|
65
|
+
>
|
|
66
|
+
{style.thumbnail ? (
|
|
67
|
+
<AtomicText type="headlineLarge" style={{ marginBottom: 8 }}>
|
|
68
|
+
{style.thumbnail}
|
|
69
|
+
</AtomicText>
|
|
70
|
+
) : style.icon ? (
|
|
71
|
+
<AtomicIcon
|
|
72
|
+
name={style.icon as never}
|
|
73
|
+
size="lg"
|
|
74
|
+
color={isSelected ? "primary" : "secondary"}
|
|
75
|
+
/>
|
|
76
|
+
) : null}
|
|
77
|
+
<AtomicText
|
|
78
|
+
type="bodySmall"
|
|
79
|
+
style={{
|
|
80
|
+
color: isSelected
|
|
81
|
+
? tokens.colors.textInverse
|
|
82
|
+
: tokens.colors.textPrimary,
|
|
83
|
+
fontWeight: isSelected ? "600" : "400",
|
|
84
|
+
textAlign: "center",
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{style.name}
|
|
88
|
+
</AtomicText>
|
|
89
|
+
{style.description && (
|
|
90
|
+
<AtomicText
|
|
91
|
+
type="labelSmall"
|
|
92
|
+
style={{
|
|
93
|
+
color: isSelected
|
|
94
|
+
? tokens.colors.textInverse
|
|
95
|
+
: tokens.colors.textSecondary,
|
|
96
|
+
opacity: isSelected ? 0.9 : 0.7,
|
|
97
|
+
textAlign: "center",
|
|
98
|
+
marginTop: 4,
|
|
99
|
+
}}
|
|
100
|
+
numberOfLines={2}
|
|
101
|
+
>
|
|
102
|
+
{style.description}
|
|
103
|
+
</AtomicText>
|
|
104
|
+
)}
|
|
105
|
+
</TouchableOpacity>
|
|
106
|
+
);
|
|
107
|
+
})}
|
|
108
|
+
</ScrollView>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const componentStyles = StyleSheet.create({
|
|
114
|
+
section: {
|
|
115
|
+
padding: 16,
|
|
116
|
+
marginBottom: 8,
|
|
117
|
+
},
|
|
118
|
+
stylesScroll: {
|
|
119
|
+
marginHorizontal: -16,
|
|
120
|
+
paddingHorizontal: 16,
|
|
121
|
+
},
|
|
122
|
+
styleCard: {
|
|
123
|
+
width: 120,
|
|
124
|
+
padding: 16,
|
|
125
|
+
borderRadius: 16,
|
|
126
|
+
borderWidth: 2,
|
|
127
|
+
marginRight: 12,
|
|
128
|
+
alignItems: "center",
|
|
129
|
+
minHeight: 140,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector Components
|
|
3
|
+
* Generic, props-driven selection UI components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { StyleSelector } from "./StyleSelector";
|
|
7
|
+
export { AspectRatioSelector } from "./AspectRatioSelector";
|
|
8
|
+
export { DurationSelector } from "./DurationSelector";
|
|
9
|
+
|
|
10
|
+
export type { StyleSelectorProps } from "./StyleSelector";
|
|
11
|
+
export type { AspectRatioSelectorProps } from "./AspectRatioSelector";
|
|
12
|
+
export type { DurationSelectorProps } from "./DurationSelector";
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
StyleOption,
|
|
16
|
+
AspectRatioOption,
|
|
17
|
+
DurationValue,
|
|
18
|
+
} from "./types";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector Component Types
|
|
3
|
+
* Generic types for selector UI components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Style option for StyleSelector
|
|
8
|
+
* All fields are required - app provides translated values
|
|
9
|
+
*/
|
|
10
|
+
export interface StyleOption {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
thumbnail?: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Aspect ratio option for AspectRatioSelector
|
|
20
|
+
* App provides translated name and description
|
|
21
|
+
*/
|
|
22
|
+
export interface AspectRatioOption {
|
|
23
|
+
id: "16:9" | "9:16" | "1:1";
|
|
24
|
+
name: string;
|
|
25
|
+
icon: string;
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Duration value type (seconds)
|
|
31
|
+
*/
|
|
32
|
+
export type DurationValue = number;
|