@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,198 @@
1
+ /**
2
+ * Layer Operations Service (Facade)
3
+ * Single Responsibility: Coordinate layer operations across specialized services
4
+ */
5
+
6
+ import { textLayerOperationsService } from "./text-layer-operations.service";
7
+ import { imageLayerOperationsService } from "./image-layer-operations.service";
8
+ import { shapeLayerOperationsService } from "./shape-layer-operations.service";
9
+ import { layerManipulationService } from "./layer-manipulation.service";
10
+ import type { Scene, TextLayer, ImageLayer, Animation } from "@domains/video";
11
+ import type {
12
+ LayerOperationResult,
13
+ LayerOrderAction,
14
+ AddTextLayerData,
15
+ AddImageLayerData,
16
+ AddShapeLayerData,
17
+ } from "../../types";
18
+
19
+ class LayerOperationsService {
20
+ /**
21
+ * Add text layer to scene
22
+ */
23
+ addTextLayer(
24
+ scenes: Scene[],
25
+ sceneIndex: number,
26
+ layerData: AddTextLayerData,
27
+ defaultColor: string,
28
+ ): LayerOperationResult {
29
+ return textLayerOperationsService.addTextLayer(
30
+ scenes,
31
+ sceneIndex,
32
+ layerData,
33
+ defaultColor,
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Edit text layer
39
+ */
40
+ editTextLayer(
41
+ scenes: Scene[],
42
+ sceneIndex: number,
43
+ layerId: string,
44
+ layerData: Partial<TextLayer>,
45
+ ): LayerOperationResult {
46
+ return textLayerOperationsService.editTextLayer(
47
+ scenes,
48
+ sceneIndex,
49
+ layerId,
50
+ layerData,
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Add image layer to scene
56
+ */
57
+ addImageLayer(
58
+ scenes: Scene[],
59
+ sceneIndex: number,
60
+ layerData: AddImageLayerData,
61
+ ): LayerOperationResult {
62
+ return imageLayerOperationsService.addImageLayer(
63
+ scenes,
64
+ sceneIndex,
65
+ layerData,
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Edit image layer
71
+ */
72
+ editImageLayer(
73
+ scenes: Scene[],
74
+ sceneIndex: number,
75
+ layerId: string,
76
+ layerData: Partial<ImageLayer>,
77
+ ): LayerOperationResult {
78
+ return imageLayerOperationsService.editImageLayer(
79
+ scenes,
80
+ sceneIndex,
81
+ layerId,
82
+ layerData,
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Add shape layer to scene
88
+ */
89
+ addShapeLayer(
90
+ scenes: Scene[],
91
+ sceneIndex: number,
92
+ layerData: AddShapeLayerData,
93
+ defaultColor: string,
94
+ ): LayerOperationResult {
95
+ return shapeLayerOperationsService.addShapeLayer(
96
+ scenes,
97
+ sceneIndex,
98
+ layerData,
99
+ defaultColor,
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Delete layer from scene
105
+ */
106
+ deleteLayer(
107
+ scenes: Scene[],
108
+ sceneIndex: number,
109
+ layerId: string,
110
+ ): LayerOperationResult {
111
+ return layerManipulationService.deleteLayer(scenes, sceneIndex, layerId);
112
+ }
113
+
114
+ /**
115
+ * Change layer order
116
+ */
117
+ changeLayerOrder(
118
+ scenes: Scene[],
119
+ sceneIndex: number,
120
+ layerId: string,
121
+ action: LayerOrderAction,
122
+ ): LayerOperationResult {
123
+ return layerManipulationService.changeLayerOrder(
124
+ scenes,
125
+ sceneIndex,
126
+ layerId,
127
+ action,
128
+ );
129
+ }
130
+
131
+ /**
132
+ * Duplicate layer
133
+ */
134
+ duplicateLayer(
135
+ scenes: Scene[],
136
+ sceneIndex: number,
137
+ layerId: string,
138
+ ): LayerOperationResult {
139
+ return layerManipulationService.duplicateLayer(scenes, sceneIndex, layerId);
140
+ }
141
+
142
+ /**
143
+ * Update layer position
144
+ */
145
+ updateLayerPosition(
146
+ scenes: Scene[],
147
+ sceneIndex: number,
148
+ layerId: string,
149
+ x: number,
150
+ y: number,
151
+ ): LayerOperationResult {
152
+ return layerManipulationService.updateLayerPosition(
153
+ scenes,
154
+ sceneIndex,
155
+ layerId,
156
+ x,
157
+ y,
158
+ );
159
+ }
160
+
161
+ /**
162
+ * Update layer size
163
+ */
164
+ updateLayerSize(
165
+ scenes: Scene[],
166
+ sceneIndex: number,
167
+ layerId: string,
168
+ width: number,
169
+ height: number,
170
+ ): LayerOperationResult {
171
+ return layerManipulationService.updateLayerSize(
172
+ scenes,
173
+ sceneIndex,
174
+ layerId,
175
+ width,
176
+ height,
177
+ );
178
+ }
179
+
180
+ /**
181
+ * Update layer animation
182
+ */
183
+ updateLayerAnimation(
184
+ scenes: Scene[],
185
+ sceneIndex: number,
186
+ layerId: string,
187
+ animation: Animation | undefined,
188
+ ): LayerOperationResult {
189
+ return layerManipulationService.updateLayerAnimation(
190
+ scenes,
191
+ sceneIndex,
192
+ layerId,
193
+ animation,
194
+ );
195
+ }
196
+ }
197
+
198
+ export const layerOperationsService = new LayerOperationsService();
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Scene Operations Service
3
+ * Single Responsibility: Business logic for scene operations
4
+ */
5
+
6
+ import { generateUUID } from "@umituz/react-native-uuid";
7
+ import type { Scene, Audio } from "@domains/video";
8
+ import type { SceneOperationResult } from "../../types";
9
+
10
+ class SceneOperationsService {
11
+ /**
12
+ * Add new scene
13
+ */
14
+ addScene(scenes: Scene[]): SceneOperationResult {
15
+ try {
16
+ const newScene: Scene = {
17
+ id: generateUUID(),
18
+ duration: 5000,
19
+ background: { type: "color", value: "#000000" },
20
+ layers: [],
21
+ transition: { type: "fade", duration: 500 },
22
+ };
23
+
24
+ const updatedScenes = [...scenes, newScene];
25
+ return {
26
+ success: true,
27
+ updatedScenes,
28
+ newSceneIndex: updatedScenes.length - 1,
29
+ };
30
+ } catch (error) {
31
+ return {
32
+ success: false,
33
+ updatedScenes: scenes,
34
+ error: error instanceof Error ? error.message : "Failed to add scene",
35
+ };
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Duplicate scene
41
+ */
42
+ duplicateScene(scenes: Scene[], sceneIndex: number): SceneOperationResult {
43
+ try {
44
+ if (sceneIndex < 0 || sceneIndex >= scenes.length) {
45
+ return {
46
+ success: false,
47
+ updatedScenes: scenes,
48
+ error: "Invalid scene index",
49
+ };
50
+ }
51
+
52
+ const sceneToDuplicate = scenes[sceneIndex];
53
+ const duplicatedScene: Scene = {
54
+ ...JSON.parse(JSON.stringify(sceneToDuplicate)),
55
+ id: generateUUID(),
56
+ layers: sceneToDuplicate.layers.map((layer) => ({
57
+ ...layer,
58
+ id: generateUUID(),
59
+ })),
60
+ };
61
+
62
+ const updatedScenes = [...scenes];
63
+ updatedScenes.splice(sceneIndex + 1, 0, duplicatedScene);
64
+
65
+ return {
66
+ success: true,
67
+ updatedScenes,
68
+ newSceneIndex: sceneIndex + 1,
69
+ };
70
+ } catch (error) {
71
+ return {
72
+ success: false,
73
+ updatedScenes: scenes,
74
+ error:
75
+ error instanceof Error ? error.message : "Failed to duplicate scene",
76
+ };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Delete scene
82
+ */
83
+ deleteScene(
84
+ scenes: Scene[],
85
+ sceneIndex: number,
86
+ currentSceneIndex: number,
87
+ ): SceneOperationResult {
88
+ try {
89
+ if (scenes.length === 1) {
90
+ return {
91
+ success: false,
92
+ updatedScenes: scenes,
93
+ error: "Cannot delete the last scene",
94
+ };
95
+ }
96
+
97
+ if (sceneIndex < 0 || sceneIndex >= scenes.length) {
98
+ return {
99
+ success: false,
100
+ updatedScenes: scenes,
101
+ error: "Invalid scene index",
102
+ };
103
+ }
104
+
105
+ const updatedScenes = scenes.filter((_, index) => index !== sceneIndex);
106
+
107
+ let newSceneIndex = currentSceneIndex;
108
+ if (currentSceneIndex >= updatedScenes.length) {
109
+ newSceneIndex = updatedScenes.length - 1;
110
+ } else if (currentSceneIndex > sceneIndex) {
111
+ newSceneIndex = currentSceneIndex - 1;
112
+ }
113
+
114
+ return {
115
+ success: true,
116
+ updatedScenes,
117
+ newSceneIndex,
118
+ };
119
+ } catch (error) {
120
+ return {
121
+ success: false,
122
+ updatedScenes: scenes,
123
+ error:
124
+ error instanceof Error ? error.message : "Failed to delete scene",
125
+ };
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Update scene audio
131
+ */
132
+ updateSceneAudio(
133
+ scenes: Scene[],
134
+ sceneIndex: number,
135
+ audio: Audio | undefined,
136
+ ): SceneOperationResult {
137
+ try {
138
+ if (sceneIndex < 0 || sceneIndex >= scenes.length) {
139
+ return {
140
+ success: false,
141
+ updatedScenes: scenes,
142
+ error: "Invalid scene index",
143
+ };
144
+ }
145
+
146
+ const updatedScenes = [...scenes];
147
+ updatedScenes[sceneIndex] = {
148
+ ...updatedScenes[sceneIndex],
149
+ audio,
150
+ };
151
+
152
+ return { success: true, updatedScenes };
153
+ } catch (error) {
154
+ return {
155
+ success: false,
156
+ updatedScenes: scenes,
157
+ error:
158
+ error instanceof Error
159
+ ? error.message
160
+ : "Failed to update scene audio",
161
+ };
162
+ }
163
+ }
164
+ }
165
+
166
+ export const sceneOperationsService = new SceneOperationsService();
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Shape Layer Operations Service
3
+ * Single Responsibility: Shape layer business logic
4
+ */
5
+
6
+ import { generateUUID } from "@umituz/react-native-uuid";
7
+ import type { Scene, ShapeLayer } from "@domains/video";
8
+ import type { LayerOperationResult, AddShapeLayerData } from "../../types";
9
+
10
+ class ShapeLayerOperationsService {
11
+ /**
12
+ * Add shape layer to scene
13
+ */
14
+ addShapeLayer(
15
+ scenes: Scene[],
16
+ sceneIndex: number,
17
+ layerData: AddShapeLayerData,
18
+ defaultColor: string,
19
+ ): LayerOperationResult {
20
+ try {
21
+ if (sceneIndex < 0 || sceneIndex >= scenes.length) {
22
+ return {
23
+ success: false,
24
+ updatedScenes: scenes,
25
+ error: "Invalid scene index",
26
+ };
27
+ }
28
+
29
+ const newLayer: ShapeLayer = {
30
+ id: generateUUID(),
31
+ type: "shape",
32
+ shape: (layerData.shape as ShapeLayer["shape"]) || "rectangle",
33
+ position: { x: 25, y: 25 },
34
+ size: { width: 50, height: 50 },
35
+ rotation: 0,
36
+ opacity: layerData.opacity || 1,
37
+ fillColor: layerData.fillColor || defaultColor,
38
+ borderColor: layerData.borderColor,
39
+ borderWidth: layerData.borderWidth,
40
+ animation: {
41
+ type: "fade",
42
+ duration: 500,
43
+ easing: "ease-in-out",
44
+ },
45
+ };
46
+
47
+ const updatedScenes = [...scenes];
48
+ updatedScenes[sceneIndex] = {
49
+ ...updatedScenes[sceneIndex],
50
+ layers: [...updatedScenes[sceneIndex].layers, newLayer],
51
+ };
52
+
53
+ return { success: true, updatedScenes };
54
+ } catch (error) {
55
+ return {
56
+ success: false,
57
+ updatedScenes: scenes,
58
+ error:
59
+ error instanceof Error ? error.message : "Failed to add shape layer",
60
+ };
61
+ }
62
+ }
63
+ }
64
+
65
+ export const shapeLayerOperationsService = new ShapeLayerOperationsService();
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Text Layer Operations Service
3
+ * Single Responsibility: Text layer business logic
4
+ */
5
+
6
+ import { generateUUID } from "@umituz/react-native-uuid";
7
+ import type { Scene, TextLayer } from "@domains/video";
8
+ import type { LayerOperationResult, AddTextLayerData } from "../../types";
9
+
10
+ class TextLayerOperationsService {
11
+ /**
12
+ * Add text layer to scene
13
+ */
14
+ addTextLayer(
15
+ scenes: Scene[],
16
+ sceneIndex: number,
17
+ layerData: AddTextLayerData,
18
+ defaultColor: string,
19
+ ): LayerOperationResult {
20
+ try {
21
+ if (sceneIndex < 0 || sceneIndex >= scenes.length) {
22
+ return {
23
+ success: false,
24
+ updatedScenes: scenes,
25
+ error: "Invalid scene index",
26
+ };
27
+ }
28
+
29
+ const newLayer: TextLayer = {
30
+ id: generateUUID(),
31
+ type: "text",
32
+ content: layerData.content || "",
33
+ position: { x: 10, y: 40 },
34
+ size: { width: 80, height: 20 },
35
+ rotation: 0,
36
+ opacity: 1,
37
+ fontSize: layerData.fontSize || 48,
38
+ fontFamily: layerData.fontFamily || "System",
39
+ fontWeight: (layerData.fontWeight as TextLayer["fontWeight"]) || "bold",
40
+ color: layerData.color || defaultColor,
41
+ textAlign: layerData.textAlign || "center",
42
+ animation: {
43
+ type: "fade",
44
+ duration: 500,
45
+ easing: "ease-in-out",
46
+ },
47
+ };
48
+
49
+ const updatedScenes = [...scenes];
50
+ updatedScenes[sceneIndex] = {
51
+ ...updatedScenes[sceneIndex],
52
+ layers: [...updatedScenes[sceneIndex].layers, newLayer],
53
+ };
54
+
55
+ return { success: true, updatedScenes };
56
+ } catch (error) {
57
+ return {
58
+ success: false,
59
+ updatedScenes: scenes,
60
+ error:
61
+ error instanceof Error ? error.message : "Failed to add text layer",
62
+ };
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Edit text layer
68
+ */
69
+ editTextLayer(
70
+ scenes: Scene[],
71
+ sceneIndex: number,
72
+ layerId: string,
73
+ layerData: Partial<TextLayer>,
74
+ ): LayerOperationResult {
75
+ try {
76
+ if (sceneIndex < 0 || sceneIndex >= scenes.length) {
77
+ return {
78
+ success: false,
79
+ updatedScenes: scenes,
80
+ error: "Invalid scene index",
81
+ };
82
+ }
83
+
84
+ const updatedScenes = [...scenes];
85
+ const layerIndex = updatedScenes[sceneIndex].layers.findIndex(
86
+ (l) => l.id === layerId,
87
+ );
88
+
89
+ if (layerIndex === -1) {
90
+ return {
91
+ success: false,
92
+ updatedScenes: scenes,
93
+ error: "Layer not found",
94
+ };
95
+ }
96
+
97
+ updatedScenes[sceneIndex].layers[layerIndex] = {
98
+ ...updatedScenes[sceneIndex].layers[layerIndex],
99
+ ...layerData,
100
+ } as TextLayer;
101
+
102
+ return { success: true, updatedScenes };
103
+ } catch (error) {
104
+ return {
105
+ success: false,
106
+ updatedScenes: scenes,
107
+ error:
108
+ error instanceof Error ? error.message : "Failed to edit text layer",
109
+ };
110
+ }
111
+ }
112
+ }
113
+
114
+ export const textLayerOperationsService = new TextLayerOperationsService();
@@ -0,0 +1,103 @@
1
+ /**
2
+ * AnimationEditor Component
3
+ * Main component for editing animation layers
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, ScrollView, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
+ import type { Animation } from "@domains/video";
10
+ import { useAnimationLayerForm } from "../../hooks/useAnimationLayerForm";
11
+ import {
12
+ DURATIONS,
13
+ DELAYS,
14
+ EASINGS,
15
+ type Easing,
16
+ } from "../../constants/animation-layer.constants";
17
+ import { ValueSelector } from "./shape-layer/ValueSelector";
18
+ import { OptionSelector } from "./text-layer/OptionSelector";
19
+ import {
20
+ AnimationTypeSelector,
21
+ AnimationEditorActions,
22
+ } from "./animation-layer";
23
+ import { AnimationInfoBanner } from "./animation-layer";
24
+
25
+ interface AnimationEditorProps {
26
+ animation?: Animation;
27
+ onSave: (animation: Animation) => void;
28
+ onRemove?: () => void;
29
+ onCancel: () => void;
30
+ }
31
+
32
+ export const AnimationEditor: React.FC<AnimationEditorProps> = ({
33
+ animation,
34
+ onSave,
35
+ onRemove,
36
+ onCancel,
37
+ }) => {
38
+ const {
39
+ formState,
40
+ setAnimationType,
41
+ setDuration,
42
+ setDelay,
43
+ setEasing,
44
+ buildAnimationData,
45
+ } = useAnimationLayerForm(animation);
46
+
47
+ const handleSave = () => {
48
+ onSave(buildAnimationData());
49
+ };
50
+
51
+ return (
52
+ <View style={styles.container}>
53
+ <ScrollView showsVerticalScrollIndicator={false}>
54
+ <AnimationTypeSelector
55
+ selectedType={formState.animationType}
56
+ onTypeChange={setAnimationType}
57
+ />
58
+
59
+ {formState.animationType !== "none" && (
60
+ <>
61
+ <ValueSelector
62
+ title="Duration"
63
+ value={formState.duration}
64
+ options={DURATIONS}
65
+ formatValue={(val) => `${val}ms`}
66
+ onValueChange={setDuration}
67
+ />
68
+
69
+ <ValueSelector
70
+ title="Delay"
71
+ value={formState.delay}
72
+ options={DELAYS}
73
+ formatValue={(val) => `${val}ms`}
74
+ onValueChange={setDelay}
75
+ />
76
+
77
+ <OptionSelector
78
+ title="Easing"
79
+ options={EASINGS}
80
+ selectedValue={formState.easing}
81
+ onValueChange={(value) => setEasing(value as Easing)}
82
+ />
83
+
84
+ <AnimationInfoBanner />
85
+ </>
86
+ )}
87
+ </ScrollView>
88
+
89
+ <AnimationEditorActions
90
+ hasAnimation={!!animation}
91
+ onRemove={onRemove}
92
+ onCancel={onCancel}
93
+ onSave={handleSave}
94
+ />
95
+ </View>
96
+ );
97
+ };
98
+
99
+ const styles = StyleSheet.create({
100
+ container: {
101
+ paddingVertical: 16,
102
+ },
103
+ });