@umituz/react-native-video-editor 1.0.1

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 (97) hide show
  1. package/README.md +92 -0
  2. package/package.json +48 -0
  3. package/src/domain/entities/index.ts +50 -0
  4. package/src/domain/entities/video-project.types.ts +153 -0
  5. package/src/index.ts +100 -0
  6. package/src/infrastructure/constants/animation-layer.constants.ts +32 -0
  7. package/src/infrastructure/constants/audio-layer.constants.ts +14 -0
  8. package/src/infrastructure/constants/export.constants.ts +28 -0
  9. package/src/infrastructure/constants/image-layer.constants.ts +12 -0
  10. package/src/infrastructure/constants/index.ts +11 -0
  11. package/src/infrastructure/constants/shape-layer.constants.ts +29 -0
  12. package/src/infrastructure/constants/text-layer.constants.ts +40 -0
  13. package/src/infrastructure/services/export-orchestrator.service.ts +122 -0
  14. package/src/infrastructure/services/image-layer-operations.service.ts +108 -0
  15. package/src/infrastructure/services/layer-manipulation.service.ts +93 -0
  16. package/src/infrastructure/services/layer-operations/index.ts +9 -0
  17. package/src/infrastructure/services/layer-operations/layer-delete.service.ts +47 -0
  18. package/src/infrastructure/services/layer-operations/layer-duplicate.service.ts +66 -0
  19. package/src/infrastructure/services/layer-operations/layer-order.service.ts +82 -0
  20. package/src/infrastructure/services/layer-operations/layer-transform.service.ts +160 -0
  21. package/src/infrastructure/services/layer-operations.service.ts +198 -0
  22. package/src/infrastructure/services/scene-operations.service.ts +166 -0
  23. package/src/infrastructure/services/shape-layer-operations.service.ts +65 -0
  24. package/src/infrastructure/services/text-layer-operations.service.ts +114 -0
  25. package/src/presentation/components/AnimationEditor.tsx +103 -0
  26. package/src/presentation/components/AudioEditor.tsx +144 -0
  27. package/src/presentation/components/DraggableLayer.tsx +110 -0
  28. package/src/presentation/components/EditorHeader.tsx +107 -0
  29. package/src/presentation/components/EditorPreviewArea.tsx +221 -0
  30. package/src/presentation/components/EditorTimeline.tsx +136 -0
  31. package/src/presentation/components/EditorToolPanel.tsx +180 -0
  32. package/src/presentation/components/ExportDialog.tsx +135 -0
  33. package/src/presentation/components/ImageLayerEditor.tsx +95 -0
  34. package/src/presentation/components/LayerActionsMenu.tsx +197 -0
  35. package/src/presentation/components/SceneActionsMenu.tsx +69 -0
  36. package/src/presentation/components/ShapeLayerEditor.tsx +108 -0
  37. package/src/presentation/components/TextLayerEditor.tsx +104 -0
  38. package/src/presentation/components/animation-layer/AnimationEditorActions.tsx +104 -0
  39. package/src/presentation/components/animation-layer/AnimationInfoBanner.tsx +43 -0
  40. package/src/presentation/components/animation-layer/AnimationTypeSelector.tsx +105 -0
  41. package/src/presentation/components/animation-layer/index.ts +8 -0
  42. package/src/presentation/components/audio-layer/AudioEditorActions.tsx +115 -0
  43. package/src/presentation/components/audio-layer/AudioFileSelector.tsx +126 -0
  44. package/src/presentation/components/audio-layer/FadeEffectsSelector.tsx +151 -0
  45. package/src/presentation/components/audio-layer/InfoBanner.tsx +43 -0
  46. package/src/presentation/components/audio-layer/VolumeSelector.tsx +98 -0
  47. package/src/presentation/components/audio-layer/index.ts +10 -0
  48. package/src/presentation/components/draggable-layer/LayerContent.tsx +106 -0
  49. package/src/presentation/components/draggable-layer/ResizeHandles.tsx +97 -0
  50. package/src/presentation/components/draggable-layer/index.ts +7 -0
  51. package/src/presentation/components/export/ExportActions.tsx +101 -0
  52. package/src/presentation/components/export/ExportInfoBanner.tsx +44 -0
  53. package/src/presentation/components/export/ExportProgress.tsx +114 -0
  54. package/src/presentation/components/export/OptionSelectorRow.tsx +101 -0
  55. package/src/presentation/components/export/ProjectInfoBox.tsx +61 -0
  56. package/src/presentation/components/export/WatermarkToggle.tsx +87 -0
  57. package/src/presentation/components/export/index.ts +11 -0
  58. package/src/presentation/components/image-layer/ImagePreview.tsx +70 -0
  59. package/src/presentation/components/image-layer/ImageSelectionButtons.tsx +82 -0
  60. package/src/presentation/components/image-layer/OpacitySelector.tsx +91 -0
  61. package/src/presentation/components/image-layer/index.ts +8 -0
  62. package/src/presentation/components/index.ts +17 -0
  63. package/src/presentation/components/shape-layer/ColorPickerHorizontal.tsx +92 -0
  64. package/src/presentation/components/shape-layer/ShapePreview.tsx +57 -0
  65. package/src/presentation/components/shape-layer/ShapeTypeSelector.tsx +102 -0
  66. package/src/presentation/components/shape-layer/ValueSelector.tsx +106 -0
  67. package/src/presentation/components/shape-layer/index.ts +9 -0
  68. package/src/presentation/components/text-layer/ColorPicker.tsx +91 -0
  69. package/src/presentation/components/text-layer/EditorActions.tsx +95 -0
  70. package/src/presentation/components/text-layer/FontSizeSelector.tsx +86 -0
  71. package/src/presentation/components/text-layer/OptionSelector.tsx +98 -0
  72. package/src/presentation/components/text-layer/TextAlignSelector.tsx +87 -0
  73. package/src/presentation/components/text-layer/TextInputSection.tsx +70 -0
  74. package/src/presentation/components/text-layer/TextPreview.tsx +71 -0
  75. package/src/presentation/components/text-layer/index.ts +12 -0
  76. package/src/presentation/hooks/useAnimationLayerForm.ts +72 -0
  77. package/src/presentation/hooks/useAudioLayerForm.ts +76 -0
  78. package/src/presentation/hooks/useDraggableLayerGestures.ts +166 -0
  79. package/src/presentation/hooks/useEditorActions.tsx +93 -0
  80. package/src/presentation/hooks/useEditorBottomSheet.ts +43 -0
  81. package/src/presentation/hooks/useEditorHistory.ts +80 -0
  82. package/src/presentation/hooks/useEditorLayers.ts +97 -0
  83. package/src/presentation/hooks/useEditorPlayback.ts +90 -0
  84. package/src/presentation/hooks/useEditorScenes.ts +106 -0
  85. package/src/presentation/hooks/useExport.ts +67 -0
  86. package/src/presentation/hooks/useExportActions.tsx +51 -0
  87. package/src/presentation/hooks/useExportForm.ts +96 -0
  88. package/src/presentation/hooks/useImageLayerForm.ts +57 -0
  89. package/src/presentation/hooks/useImageLayerOperations.ts +71 -0
  90. package/src/presentation/hooks/useLayerActions.tsx +162 -0
  91. package/src/presentation/hooks/useLayerManipulation.ts +178 -0
  92. package/src/presentation/hooks/useMenuActions.tsx +92 -0
  93. package/src/presentation/hooks/useSceneActions.tsx +81 -0
  94. package/src/presentation/hooks/useShapeLayerForm.ts +84 -0
  95. package/src/presentation/hooks/useShapeLayerOperations.ts +52 -0
  96. package/src/presentation/hooks/useTextLayerForm.ts +100 -0
  97. package/src/presentation/hooks/useTextLayerOperations.ts +74 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * ExportProgress Component
3
+ * Export progress display for export dialog
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ useAppDesignTokens,
11
+ } from "@umituz/react-native-design-system";
12
+ import type { ExportProgress as ExportProgressType } from "@domains/video/infrastructure/services/video-export.service";
13
+
14
+ interface ExportProgressProps {
15
+ progress: ExportProgressType;
16
+ }
17
+
18
+ export const ExportProgress: React.FC<ExportProgressProps> = ({ progress }) => {
19
+ const tokens = useAppDesignTokens();
20
+
21
+ const getStatusText = () => {
22
+ switch (progress.status) {
23
+ case "preparing":
24
+ return "Preparing Export...";
25
+ case "encoding":
26
+ return "Encoding Video...";
27
+ case "saving":
28
+ return "Saving Video...";
29
+ case "complete":
30
+ return "Export Complete!";
31
+ case "error":
32
+ return "Export Failed";
33
+ default:
34
+ return "Processing...";
35
+ }
36
+ };
37
+
38
+ return (
39
+ <View
40
+ style={[
41
+ styles.progressContainer,
42
+ { backgroundColor: tokens.colors.surface },
43
+ ]}
44
+ >
45
+ <View style={styles.progressHeader}>
46
+ <AtomicText
47
+ type="bodyMedium"
48
+ style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
49
+ >
50
+ {getStatusText()}
51
+ </AtomicText>
52
+ <AtomicText
53
+ type="labelSmall"
54
+ style={{ color: tokens.colors.textSecondary, marginTop: 4 }}
55
+ >
56
+ {progress.message}
57
+ </AtomicText>
58
+ </View>
59
+
60
+ <View style={styles.progressBarContainer}>
61
+ <View
62
+ style={[
63
+ styles.progressBar,
64
+ {
65
+ backgroundColor: tokens.colors.primary,
66
+ width: `${progress.progress}%`,
67
+ },
68
+ ]}
69
+ />
70
+ </View>
71
+
72
+ <View style={styles.progressStats}>
73
+ <AtomicText
74
+ type="labelSmall"
75
+ style={{ color: tokens.colors.textSecondary }}
76
+ >
77
+ {progress.currentFrame} / {progress.totalFrames} frames
78
+ </AtomicText>
79
+ <AtomicText
80
+ type="labelSmall"
81
+ style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
82
+ >
83
+ {progress.progress}%
84
+ </AtomicText>
85
+ </View>
86
+ </View>
87
+ );
88
+ };
89
+
90
+ const styles = StyleSheet.create({
91
+ progressContainer: {
92
+ padding: 16,
93
+ borderRadius: 12,
94
+ marginBottom: 16,
95
+ },
96
+ progressHeader: {
97
+ marginBottom: 12,
98
+ },
99
+ progressBarContainer: {
100
+ height: 8,
101
+ backgroundColor: "rgba(0, 0, 0, 0.1)",
102
+ borderRadius: 4,
103
+ overflow: "hidden",
104
+ marginBottom: 8,
105
+ },
106
+ progressBar: {
107
+ height: "100%",
108
+ borderRadius: 4,
109
+ },
110
+ progressStats: {
111
+ flexDirection: "row",
112
+ justifyContent: "space-between",
113
+ },
114
+ });
@@ -0,0 +1,101 @@
1
+ /**
2
+ * OptionSelectorRow Component
3
+ * Reusable option selector row for export settings
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ useAppDesignTokens,
11
+ } from "@umituz/react-native-design-system";
12
+
13
+ interface Option<T extends string> {
14
+ value: T;
15
+ label: string;
16
+ textTransform?: "none" | "capitalize" | "uppercase" | "lowercase";
17
+ }
18
+
19
+ interface OptionSelectorRowProps<T extends string> {
20
+ title: string;
21
+ options: Option<T>[];
22
+ selectedValue: T;
23
+ onValueChange: (value: T) => void;
24
+ }
25
+
26
+ export function OptionSelectorRow<T extends string>({
27
+ title,
28
+ options,
29
+ selectedValue,
30
+ onValueChange,
31
+ }: OptionSelectorRowProps<T>) {
32
+ const tokens = useAppDesignTokens();
33
+
34
+ return (
35
+ <View style={styles.section}>
36
+ <AtomicText
37
+ type="bodyMedium"
38
+ style={{
39
+ color: tokens.colors.textPrimary,
40
+ fontWeight: "600",
41
+ marginBottom: 8,
42
+ }}
43
+ >
44
+ {title}
45
+ </AtomicText>
46
+ <View style={styles.optionsRow}>
47
+ {options.map((option) => (
48
+ <TouchableOpacity
49
+ key={option.value}
50
+ style={[
51
+ styles.optionButton,
52
+ {
53
+ backgroundColor:
54
+ selectedValue === option.value
55
+ ? tokens.colors.primary
56
+ : tokens.colors.surface,
57
+ borderColor:
58
+ selectedValue === option.value
59
+ ? tokens.colors.primary
60
+ : tokens.colors.borderLight,
61
+ },
62
+ ]}
63
+ onPress={() => onValueChange(option.value)}
64
+ >
65
+ <AtomicText
66
+ type="bodySmall"
67
+ style={{
68
+ color:
69
+ selectedValue === option.value
70
+ ? "#FFFFFF"
71
+ : tokens.colors.textPrimary,
72
+ fontWeight: selectedValue === option.value ? "600" : "400",
73
+ textTransform: option.textTransform || "none",
74
+ }}
75
+ >
76
+ {option.label}
77
+ </AtomicText>
78
+ </TouchableOpacity>
79
+ ))}
80
+ </View>
81
+ </View>
82
+ );
83
+ }
84
+
85
+ const styles = StyleSheet.create({
86
+ section: {
87
+ marginBottom: 24,
88
+ },
89
+ optionsRow: {
90
+ flexDirection: "row",
91
+ gap: 8,
92
+ },
93
+ optionButton: {
94
+ flex: 1,
95
+ paddingVertical: 12,
96
+ borderRadius: 8,
97
+ borderWidth: 1,
98
+ alignItems: "center",
99
+ justifyContent: "center",
100
+ },
101
+ });
@@ -0,0 +1,61 @@
1
+ /**
2
+ * ProjectInfoBox Component
3
+ * Project information display for export dialog
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicIcon,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import type { VideoProject } from "@domains/video";
14
+
15
+ interface ProjectInfoBoxProps {
16
+ project: VideoProject;
17
+ duration: number;
18
+ estimatedSize: string;
19
+ }
20
+
21
+ export const ProjectInfoBox: React.FC<ProjectInfoBoxProps> = ({
22
+ project,
23
+ duration,
24
+ estimatedSize,
25
+ }) => {
26
+ const tokens = useAppDesignTokens();
27
+
28
+ return (
29
+ <View style={[styles.infoBox, { backgroundColor: tokens.colors.surface }]}>
30
+ <View style={styles.infoRow}>
31
+ <AtomicIcon name="film-outline" size="md" color="primary" />
32
+ <View style={{ marginLeft: 12 }}>
33
+ <AtomicText
34
+ type="bodyMedium"
35
+ style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
36
+ >
37
+ {project.title}
38
+ </AtomicText>
39
+ <AtomicText
40
+ type="labelSmall"
41
+ style={{ color: tokens.colors.textSecondary, marginTop: 2 }}
42
+ >
43
+ Duration: {duration}s • Est. size: {estimatedSize} MB
44
+ </AtomicText>
45
+ </View>
46
+ </View>
47
+ </View>
48
+ );
49
+ };
50
+
51
+ const styles = StyleSheet.create({
52
+ infoBox: {
53
+ padding: 16,
54
+ borderRadius: 12,
55
+ marginBottom: 24,
56
+ },
57
+ infoRow: {
58
+ flexDirection: "row",
59
+ alignItems: "center",
60
+ },
61
+ });
@@ -0,0 +1,87 @@
1
+ /**
2
+ * WatermarkToggle Component
3
+ * Watermark toggle for export dialog
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ useAppDesignTokens,
11
+ } from "@umituz/react-native-design-system";
12
+
13
+ interface WatermarkToggleProps {
14
+ includeWatermark: boolean;
15
+ onToggle: (include: boolean) => void;
16
+ }
17
+
18
+ export const WatermarkToggle: React.FC<WatermarkToggleProps> = ({
19
+ includeWatermark,
20
+ onToggle,
21
+ }) => {
22
+ const tokens = useAppDesignTokens();
23
+
24
+ return (
25
+ <TouchableOpacity
26
+ style={[styles.toggleRow, { backgroundColor: tokens.colors.surface }]}
27
+ onPress={() => onToggle(!includeWatermark)}
28
+ >
29
+ <View style={{ flex: 1 }}>
30
+ <AtomicText
31
+ type="bodyMedium"
32
+ style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
33
+ >
34
+ Include Watermark
35
+ </AtomicText>
36
+ <AtomicText
37
+ type="labelSmall"
38
+ style={{ color: tokens.colors.textSecondary, marginTop: 2 }}
39
+ >
40
+ Add app branding to exported video
41
+ </AtomicText>
42
+ </View>
43
+ <View
44
+ style={[
45
+ styles.toggle,
46
+ {
47
+ backgroundColor: includeWatermark
48
+ ? tokens.colors.primary
49
+ : tokens.colors.borderLight,
50
+ },
51
+ ]}
52
+ >
53
+ <View
54
+ style={[
55
+ styles.toggleThumb,
56
+ {
57
+ backgroundColor: "#FFFFFF",
58
+ transform: [{ translateX: includeWatermark ? 20 : 0 }],
59
+ },
60
+ ]}
61
+ />
62
+ </View>
63
+ </TouchableOpacity>
64
+ );
65
+ };
66
+
67
+ const styles = StyleSheet.create({
68
+ toggleRow: {
69
+ flexDirection: "row",
70
+ alignItems: "center",
71
+ padding: 16,
72
+ borderRadius: 12,
73
+ marginBottom: 16,
74
+ },
75
+ toggle: {
76
+ width: 50,
77
+ height: 30,
78
+ borderRadius: 15,
79
+ padding: 3,
80
+ justifyContent: "center",
81
+ },
82
+ toggleThumb: {
83
+ width: 24,
84
+ height: 24,
85
+ borderRadius: 12,
86
+ },
87
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Export Dialog Components
3
+ * Barrel file for export dialog components
4
+ */
5
+
6
+ export { ProjectInfoBox } from "./ProjectInfoBox";
7
+ export { OptionSelectorRow } from "./OptionSelectorRow";
8
+ export { WatermarkToggle } from "./WatermarkToggle";
9
+ export { ExportProgress } from "./ExportProgress";
10
+ export { ExportInfoBanner } from "./ExportInfoBanner";
11
+ export { ExportActions } from "./ExportActions";
@@ -0,0 +1,70 @@
1
+ /**
2
+ * ImagePreview Component
3
+ * Image preview or placeholder for image layer editor
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Image, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicIcon,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+
14
+ interface ImagePreviewProps {
15
+ imageUri: string;
16
+ opacity: number;
17
+ }
18
+
19
+ export const ImagePreview: React.FC<ImagePreviewProps> = ({
20
+ imageUri,
21
+ opacity,
22
+ }) => {
23
+ const tokens = useAppDesignTokens();
24
+
25
+ if (imageUri) {
26
+ return (
27
+ <Image
28
+ source={{ uri: imageUri }}
29
+ style={[styles.imagePreview, { opacity }]}
30
+ />
31
+ );
32
+ }
33
+
34
+ return (
35
+ <View
36
+ style={[
37
+ styles.imagePlaceholder,
38
+ {
39
+ backgroundColor: tokens.colors.surfaceSecondary,
40
+ borderColor: tokens.colors.borderLight,
41
+ },
42
+ ]}
43
+ >
44
+ <AtomicIcon name="image-outline" size="xl" color="secondary" />
45
+ <AtomicText
46
+ type="bodyMedium"
47
+ style={{ color: tokens.colors.textSecondary, marginTop: 12 }}
48
+ >
49
+ No image selected
50
+ </AtomicText>
51
+ </View>
52
+ );
53
+ };
54
+
55
+ const styles = StyleSheet.create({
56
+ imagePreview: {
57
+ width: "100%",
58
+ height: 200,
59
+ borderRadius: 12,
60
+ },
61
+ imagePlaceholder: {
62
+ width: "100%",
63
+ height: 200,
64
+ borderRadius: 12,
65
+ borderWidth: 2,
66
+ borderStyle: "dashed",
67
+ alignItems: "center",
68
+ justifyContent: "center",
69
+ },
70
+ });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * ImageSelectionButtons Component
3
+ * Image selection buttons (gallery and camera) for image layer editor
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicIcon,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+
14
+ interface ImageSelectionButtonsProps {
15
+ onPickFromGallery: () => void;
16
+ onTakePhoto: () => void;
17
+ }
18
+
19
+ export const ImageSelectionButtons: React.FC<ImageSelectionButtonsProps> = ({
20
+ onPickFromGallery,
21
+ onTakePhoto,
22
+ }) => {
23
+ const tokens = useAppDesignTokens();
24
+
25
+ return (
26
+ <View style={styles.selectionButtons}>
27
+ <TouchableOpacity
28
+ style={[
29
+ styles.selectionButton,
30
+ {
31
+ backgroundColor: tokens.colors.surface,
32
+ borderColor: tokens.colors.borderLight,
33
+ },
34
+ ]}
35
+ onPress={onPickFromGallery}
36
+ >
37
+ <AtomicIcon name="FolderOpen" size="md" color="primary" />
38
+ <AtomicText
39
+ type="bodySmall"
40
+ style={{ color: tokens.colors.textPrimary, marginTop: 8 }}
41
+ >
42
+ From Gallery
43
+ </AtomicText>
44
+ </TouchableOpacity>
45
+
46
+ <TouchableOpacity
47
+ style={[
48
+ styles.selectionButton,
49
+ {
50
+ backgroundColor: tokens.colors.surface,
51
+ borderColor: tokens.colors.borderLight,
52
+ },
53
+ ]}
54
+ onPress={onTakePhoto}
55
+ >
56
+ <AtomicIcon name="Camera" size="md" color="primary" />
57
+ <AtomicText
58
+ type="bodySmall"
59
+ style={{ color: tokens.colors.textPrimary, marginTop: 8 }}
60
+ >
61
+ Take Photo
62
+ </AtomicText>
63
+ </TouchableOpacity>
64
+ </View>
65
+ );
66
+ };
67
+
68
+ const styles = StyleSheet.create({
69
+ selectionButtons: {
70
+ flexDirection: "row",
71
+ gap: 12,
72
+ marginBottom: 24,
73
+ },
74
+ selectionButton: {
75
+ flex: 1,
76
+ paddingVertical: 24,
77
+ borderRadius: 12,
78
+ borderWidth: 1,
79
+ alignItems: "center",
80
+ justifyContent: "center",
81
+ },
82
+ });
@@ -0,0 +1,91 @@
1
+ /**
2
+ * OpacitySelector Component
3
+ * Opacity selector for image layer
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
9
+ import {
10
+ AtomicText,
11
+ useAppDesignTokens,
12
+ } from "@umituz/react-native-design-system";
13
+ import { IMAGE_OPACITY_OPTIONS } from "../../../constants/image-layer.constants";
14
+
15
+ interface OpacitySelectorProps {
16
+ opacity: number;
17
+ onOpacityChange: (opacity: number) => void;
18
+ }
19
+
20
+ export const OpacitySelector: React.FC<OpacitySelectorProps> = ({
21
+ opacity,
22
+ onOpacityChange,
23
+ }) => {
24
+ const { t } = useLocalization();
25
+ const tokens = useAppDesignTokens();
26
+
27
+ return (
28
+ <View style={styles.opacitySection}>
29
+ <AtomicText
30
+ type="bodyMedium"
31
+ style={{
32
+ color: tokens.colors.textPrimary,
33
+ fontWeight: "600",
34
+ marginBottom: 12,
35
+ }}
36
+ >
37
+ {t("editor.properties.opacity")}: {Math.round(opacity * 100)}%
38
+ </AtomicText>
39
+ <View style={styles.opacityButtons}>
40
+ {IMAGE_OPACITY_OPTIONS.map((value) => (
41
+ <TouchableOpacity
42
+ key={value}
43
+ style={[
44
+ styles.opacityButton,
45
+ {
46
+ backgroundColor:
47
+ opacity === value
48
+ ? tokens.colors.primary
49
+ : tokens.colors.surface,
50
+ borderColor:
51
+ opacity === value
52
+ ? tokens.colors.primary
53
+ : tokens.colors.borderLight,
54
+ },
55
+ ]}
56
+ onPress={() => onOpacityChange(value)}
57
+ >
58
+ <AtomicText
59
+ type="bodySmall"
60
+ style={{
61
+ color:
62
+ opacity === value ? "#FFFFFF" : tokens.colors.textPrimary,
63
+ fontWeight: opacity === value ? "600" : "400",
64
+ }}
65
+ >
66
+ {Math.round(value * 100)}%
67
+ </AtomicText>
68
+ </TouchableOpacity>
69
+ ))}
70
+ </View>
71
+ </View>
72
+ );
73
+ };
74
+
75
+ const styles = StyleSheet.create({
76
+ opacitySection: {
77
+ marginBottom: 24,
78
+ },
79
+ opacityButtons: {
80
+ flexDirection: "row",
81
+ gap: 8,
82
+ },
83
+ opacityButton: {
84
+ flex: 1,
85
+ paddingVertical: 12,
86
+ borderRadius: 8,
87
+ borderWidth: 1,
88
+ alignItems: "center",
89
+ justifyContent: "center",
90
+ },
91
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Image Layer Editor Components
3
+ * Barrel file for image layer editor components
4
+ */
5
+
6
+ export { ImagePreview } from "./ImagePreview";
7
+ export { ImageSelectionButtons } from "./ImageSelectionButtons";
8
+ export { OpacitySelector } from "./OpacitySelector";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Editor Presentation Components
3
+ */
4
+
5
+ export * from "./EditorHeader";
6
+ export * from "./EditorPreviewArea";
7
+ export * from "./EditorToolPanel";
8
+ export * from "./EditorTimeline";
9
+ export * from "./LayerActionsMenu";
10
+ export * from "./SceneActionsMenu";
11
+ export * from "./TextLayerEditor";
12
+ export * from "./AudioEditor";
13
+ export * from "./ShapeLayerEditor";
14
+ export * from "./AnimationEditor";
15
+ export * from "./DraggableLayer";
16
+ export * from "./ImageLayerEditor";
17
+ export * from "./ExportDialog";