@umituz/react-native-image 1.1.5 → 1.2.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 (37) hide show
  1. package/package.json +25 -10
  2. package/src/domain/entities/EditorTypes.ts +180 -0
  3. package/src/domain/entities/ImageFilterTypes.ts +70 -0
  4. package/src/index.ts +54 -0
  5. package/src/infrastructure/services/ImageAIEnhancementService.ts +136 -0
  6. package/src/infrastructure/services/ImageAnnotationService.ts +189 -0
  7. package/src/infrastructure/services/ImageBatchService.ts +199 -0
  8. package/src/infrastructure/services/ImageEditorService.ts +274 -0
  9. package/src/infrastructure/services/ImageFilterService.ts +168 -0
  10. package/src/infrastructure/services/ImageMetadataService.ts +187 -0
  11. package/src/infrastructure/services/ImageSpecializedEnhancementService.ts +57 -0
  12. package/src/infrastructure/utils/AIImageAnalysisUtils.ts +122 -0
  13. package/src/infrastructure/utils/CanvasRenderingService.ts +134 -0
  14. package/src/infrastructure/utils/CropTool.ts +260 -0
  15. package/src/infrastructure/utils/DrawingEngine.ts +210 -0
  16. package/src/infrastructure/utils/FilterEffects.ts +51 -0
  17. package/src/infrastructure/utils/FilterProcessor.ts +361 -0
  18. package/src/infrastructure/utils/ImageQualityPresets.ts +110 -0
  19. package/src/infrastructure/utils/LayerManager.ts +158 -0
  20. package/src/infrastructure/utils/ShapeRenderer.ts +168 -0
  21. package/src/infrastructure/utils/TextEditor.ts +273 -0
  22. package/src/presentation/components/CropComponent.tsx +183 -0
  23. package/src/presentation/components/Editor.tsx +261 -0
  24. package/src/presentation/components/EditorCanvas.tsx +135 -0
  25. package/src/presentation/components/EditorPanel.tsx +321 -0
  26. package/src/presentation/components/EditorToolbar.tsx +180 -0
  27. package/src/presentation/components/FilterSlider.tsx +123 -0
  28. package/src/presentation/components/GalleryHeader.tsx +87 -25
  29. package/src/presentation/components/ImageGallery.tsx +97 -48
  30. package/src/presentation/hooks/useEditorTools.ts +188 -0
  31. package/src/presentation/hooks/useImage.ts +33 -2
  32. package/src/presentation/hooks/useImageAIEnhancement.ts +33 -0
  33. package/src/presentation/hooks/useImageAnnotation.ts +32 -0
  34. package/src/presentation/hooks/useImageBatch.ts +33 -0
  35. package/src/presentation/hooks/useImageEditor.ts +165 -38
  36. package/src/presentation/hooks/useImageFilter.ts +38 -0
  37. package/src/presentation/hooks/useImageMetadata.ts +28 -0
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Presentation - Editor Component
3
+ *
4
+ * Main image editor interface
5
+ */
6
+
7
+ import React, { useState, useCallback } from 'react';
8
+ import { View, StyleSheet, Alert, Text, TouchableOpacity } from 'react-native';
9
+ import { EditorCanvas } from './EditorCanvas';
10
+ import { EditorToolbar } from './EditorToolbar';
11
+ import { EditorPanel } from './EditorPanel';
12
+ import { useImageEditor } from '../hooks/useImageEditor';
13
+ import { EditorTool, EditorExportOptions } from '../../domain/entities/EditorTypes';
14
+ import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
15
+
16
+ interface EditorProps {
17
+ imageUri: string;
18
+ width?: number;
19
+ height?: number;
20
+ onSave?: (result: ImageManipulationResult) => void | Promise<void>;
21
+ onCancel?: () => void;
22
+ backgroundColor?: string;
23
+ toolbarBackgroundColor?: string;
24
+ panelBackgroundColor?: string;
25
+ exportOptions?: EditorExportOptions;
26
+ }
27
+
28
+ export function Editor({
29
+ imageUri,
30
+ width = 800,
31
+ height = 600,
32
+ onSave,
33
+ onCancel,
34
+ backgroundColor = '#ffffff',
35
+ toolbarBackgroundColor = '#f8f9fa',
36
+ panelBackgroundColor = '#f8f9fa',
37
+ exportOptions,
38
+ }: EditorProps) {
39
+ const [selectedTool, setSelectedTool] = useState<EditorTool>(EditorTool.MOVE);
40
+ const [toolConfig, setToolConfig] = useState<any>({});
41
+ const [canvas, setCanvas] = useState<any>(null);
42
+
43
+ const {
44
+ editorState,
45
+ isProcessing,
46
+ error,
47
+ canUndo,
48
+ canRedo,
49
+ initializeEditor,
50
+ setTool,
51
+ addLayer,
52
+ removeLayer,
53
+ undo,
54
+ redo,
55
+ exportImage,
56
+ cancel,
57
+ } = useImageEditor({
58
+ onSave,
59
+ onCancel,
60
+ });
61
+
62
+ const handleCanvasReady = useCallback((canvasElement: any) => {
63
+ setCanvas(canvasElement);
64
+ if (imageUri && !editorState) {
65
+ initializeEditor(imageUri);
66
+ }
67
+ }, [imageUri, editorState, initializeEditor]);
68
+
69
+ const handleToolSelect = useCallback((tool: EditorTool) => {
70
+ setSelectedTool(tool);
71
+ setTool(tool);
72
+ setToolConfig({});
73
+ }, [setTool]);
74
+
75
+ const handleToolConfigChange = useCallback((config: any) => {
76
+ setToolConfig(config);
77
+ }, []);
78
+
79
+ const handleSave = useCallback(async () => {
80
+ if (isProcessing) return;
81
+
82
+ try {
83
+ await exportImage(exportOptions);
84
+ } catch (err) {
85
+ Alert.alert('Error', 'Failed to save image');
86
+ }
87
+ }, [isProcessing, exportImage, exportImage]);
88
+
89
+ const handleCancel = useCallback(() => {
90
+ cancel();
91
+ }, [cancel]);
92
+
93
+ const handleUndo = useCallback(() => {
94
+ undo();
95
+ }, [undo]);
96
+
97
+ const handleRedo = useCallback(() => {
98
+ redo();
99
+ }, [redo]);
100
+
101
+ return (
102
+ <View style={[styles.container, { backgroundColor }]}>
103
+ {/* Toolbar */}
104
+ <EditorToolbar
105
+ selectedTool={selectedTool}
106
+ onToolSelect={handleToolSelect}
107
+ backgroundColor={toolbarBackgroundColor}
108
+ />
109
+
110
+ {/* Main Editor Area */}
111
+ <View style={styles.editorArea}>
112
+ <EditorCanvas
113
+ width={width}
114
+ height={height}
115
+ onCanvasReady={handleCanvasReady}
116
+ onToolChange={handleToolSelect}
117
+ onStateChange={handleToolConfigChange}
118
+ backgroundColor={backgroundColor}
119
+ />
120
+
121
+ {/* Error Display */}
122
+ {error && (
123
+ <View style={styles.errorContainer}>
124
+ <Text style={styles.errorText}>{error}</Text>
125
+ </View>
126
+ )}
127
+ </View>
128
+
129
+ {/* Configuration Panel */}
130
+ {(selectedTool === EditorTool.BRUSH ||
131
+ selectedTool === EditorTool.ERASER ||
132
+ selectedTool === EditorTool.TEXT ||
133
+ selectedTool === EditorTool.CROP) && (
134
+ <EditorPanel
135
+ selectedTool={selectedTool}
136
+ onToolConfigChange={handleToolConfigChange}
137
+ backgroundColor={panelBackgroundColor}
138
+ />
139
+ )}
140
+
141
+ {/* Action Buttons */}
142
+ <View style={styles.actionBar}>
143
+ <View style={styles.undoRedoContainer}>
144
+ <TouchableOpacity
145
+ style={[styles.actionButton, !canUndo && styles.disabledButton]}
146
+ onPress={handleUndo}
147
+ disabled={!canUndo || isProcessing}
148
+ >
149
+ <Text style={styles.actionButtonText}>↶</Text>
150
+ </TouchableOpacity>
151
+
152
+ <TouchableOpacity
153
+ style={[styles.actionButton, !canRedo && styles.disabledButton]}
154
+ onPress={handleRedo}
155
+ disabled={!canRedo || isProcessing}
156
+ >
157
+ <Text style={styles.actionButtonText}>↷</Text>
158
+ </TouchableOpacity>
159
+ </View>
160
+
161
+ <View style={styles.saveCancelContainer}>
162
+ <TouchableOpacity
163
+ style={[styles.actionButton, styles.cancelButton]}
164
+ onPress={handleCancel}
165
+ disabled={isProcessing}
166
+ >
167
+ <Text style={styles.cancelButtonText}>Cancel</Text>
168
+ </TouchableOpacity>
169
+
170
+ <TouchableOpacity
171
+ style={[styles.actionButton, styles.saveButton, isProcessing && styles.processingButton]}
172
+ onPress={handleSave}
173
+ disabled={isProcessing}
174
+ >
175
+ <Text style={styles.saveButtonText}>
176
+ {isProcessing ? 'Saving...' : 'Save'}
177
+ </Text>
178
+ </TouchableOpacity>
179
+ </View>
180
+ </View>
181
+ </View>
182
+ );
183
+ }
184
+
185
+ const styles = StyleSheet.create({
186
+ container: {
187
+ flex: 1,
188
+ backgroundColor: '#ffffff',
189
+ },
190
+ editorArea: {
191
+ flex: 1,
192
+ position: 'relative',
193
+ },
194
+ errorContainer: {
195
+ position: 'absolute',
196
+ top: 16,
197
+ left: 16,
198
+ right: 16,
199
+ backgroundColor: '#ff4444',
200
+ padding: 12,
201
+ borderRadius: 8,
202
+ },
203
+ errorText: {
204
+ color: '#ffffff',
205
+ fontSize: 14,
206
+ fontWeight: '500',
207
+ },
208
+ actionBar: {
209
+ flexDirection: 'row',
210
+ justifyContent: 'space-between',
211
+ alignItems: 'center',
212
+ padding: 16,
213
+ borderTopWidth: 1,
214
+ borderTopColor: '#e0e0e0',
215
+ backgroundColor: '#f8f9fa',
216
+ },
217
+ undoRedoContainer: {
218
+ flexDirection: 'row',
219
+ gap: 8,
220
+ },
221
+ saveCancelContainer: {
222
+ flexDirection: 'row',
223
+ gap: 8,
224
+ },
225
+ actionButton: {
226
+ paddingHorizontal: 16,
227
+ paddingVertical: 8,
228
+ borderRadius: 6,
229
+ borderWidth: 1,
230
+ borderColor: '#d0d0d0',
231
+ backgroundColor: '#ffffff',
232
+ },
233
+ disabledButton: {
234
+ opacity: 0.5,
235
+ },
236
+ cancelButton: {
237
+ borderColor: '#6c757d',
238
+ },
239
+ saveButton: {
240
+ backgroundColor: '#28a745',
241
+ borderColor: '#28a745',
242
+ },
243
+ processingButton: {
244
+ opacity: 0.7,
245
+ },
246
+ actionButtonText: {
247
+ fontSize: 16,
248
+ fontWeight: '600',
249
+ color: '#333333',
250
+ },
251
+ cancelButtonText: {
252
+ fontSize: 16,
253
+ fontWeight: '600',
254
+ color: '#6c757d',
255
+ },
256
+ saveButtonText: {
257
+ fontSize: 16,
258
+ fontWeight: '600',
259
+ color: '#ffffff',
260
+ },
261
+ });
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Presentation - Editor Canvas Component
3
+ *
4
+ * Main canvas component for image editing
5
+ */
6
+
7
+ import React, { useRef, useEffect, useCallback } from 'react';
8
+ import { View, StyleSheet } from 'react-native';
9
+ import { useEditorTools } from '../hooks/useEditorTools';
10
+
11
+ import { EditorTool } from '../../domain/entities/EditorTypes';
12
+
13
+ interface EditorCanvasProps {
14
+ width: number;
15
+ height: number;
16
+ onCanvasReady?: (canvas: HTMLCanvasElement) => void;
17
+ onToolChange?: (tool: EditorTool) => void;
18
+ onStateChange?: (state: any) => void;
19
+ backgroundColor?: string;
20
+ }
21
+
22
+ export function EditorCanvas({
23
+ width,
24
+ height,
25
+ onCanvasReady,
26
+ onToolChange,
27
+ onStateChange,
28
+ backgroundColor = '#ffffff',
29
+ }: EditorCanvasProps) {
30
+ const canvasRef = useRef<any>(null);
31
+ const {
32
+ setTool,
33
+ startDrawing,
34
+ draw,
35
+ stopDrawing,
36
+ setCropArea,
37
+ } = useEditorTools({
38
+ onStateChange,
39
+ canvas: canvasRef.current,
40
+ });
41
+
42
+ useEffect(() => {
43
+ if (canvasRef.current && onCanvasReady) {
44
+ onCanvasReady(canvasRef.current);
45
+ }
46
+ }, [canvasRef.current, onCanvasReady]);
47
+
48
+ const handleTouchStart = useCallback((event: any) => {
49
+ const touch = event.nativeEvent.touches[0];
50
+ const point = { x: touch.pageX, y: touch.pageY };
51
+ setTool(EditorTool.BRUSH);
52
+ startDrawing(point);
53
+ onToolChange?.(EditorTool.BRUSH);
54
+ }, [setTool, startDrawing, onToolChange]);
55
+
56
+ const handleTouchMove = useCallback((event: any) => {
57
+ const touch = event.nativeEvent.touches[0];
58
+ const point = { x: touch.pageX, y: touch.pageY };
59
+ draw(point);
60
+ }, [draw]);
61
+
62
+ const handleTouchEnd = useCallback(() => {
63
+ stopDrawing();
64
+ }, [stopDrawing]);
65
+
66
+ // For React Native Web, we need to handle this differently
67
+ useEffect(() => {
68
+ const canvas = canvasRef.current;
69
+ if (!canvas) return;
70
+
71
+ // Touch events for mobile
72
+ canvas.addEventListener('touchstart', handleTouchStart);
73
+ canvas.addEventListener('touchmove', handleTouchMove);
74
+ canvas.addEventListener('touchend', handleTouchEnd);
75
+
76
+ // Mouse events for desktop
77
+ const handleMouseDown = (e: MouseEvent) => {
78
+ const point = { x: e.offsetX, y: e.offsetY };
79
+ setTool(EditorTool.BRUSH);
80
+ startDrawing(point);
81
+ onToolChange?.(EditorTool.BRUSH);
82
+ };
83
+
84
+ const handleMouseMove = (e: MouseEvent) => {
85
+ const point = { x: e.offsetX, y: e.offsetY };
86
+ draw(point);
87
+ };
88
+
89
+ const handleMouseUp = () => {
90
+ stopDrawing();
91
+ };
92
+
93
+ canvas.addEventListener('mousedown', handleMouseDown);
94
+ canvas.addEventListener('mousemove', handleMouseMove);
95
+ canvas.addEventListener('mouseup', handleMouseUp);
96
+
97
+ return () => {
98
+ canvas.removeEventListener('touchstart', handleTouchStart);
99
+ canvas.removeEventListener('touchmove', handleTouchMove);
100
+ canvas.removeEventListener('touchend', handleTouchEnd);
101
+ canvas.removeEventListener('mousedown', handleMouseDown);
102
+ canvas.removeEventListener('mousemove', handleMouseMove);
103
+ canvas.removeEventListener('mouseup', handleMouseUp);
104
+ };
105
+ }, [setTool, startDrawing, draw, stopDrawing, onToolChange]);
106
+
107
+ // For React Native, we'll need to use a different approach
108
+ if (typeof window === 'undefined') {
109
+ return (
110
+ <View style={[styles.container, { width, height, backgroundColor }]} />
111
+ );
112
+ }
113
+
114
+ return (
115
+ <View style={[styles.container, { width, height }]}>
116
+ <canvas
117
+ ref={canvasRef}
118
+ width={width}
119
+ height={height}
120
+ style={{
121
+ width,
122
+ height,
123
+ backgroundColor,
124
+ }}
125
+ />
126
+ </View>
127
+ );
128
+ }
129
+
130
+ const styles = StyleSheet.create({
131
+ container: {
132
+ position: 'relative',
133
+ overflow: 'hidden',
134
+ },
135
+ });