@umituz/react-native-image 1.1.0

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ümit UZ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # @umituz/react-native-image
2
+
3
+ Image manipulation and viewing for React Native apps - resize, crop, rotate, flip, compress, gallery viewer.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Image Manipulation** - Resize, crop, rotate, flip, compress
8
+ - ✅ **Format Conversion** - JPEG, PNG, WEBP
9
+ - ✅ **Thumbnail Generation** - Create small preview images
10
+ - ✅ **Full-Screen Image Viewer** - Gallery viewer with zoom/swipe (react-native-image-viewing)
11
+ - ✅ **Image Gallery Management** - Multiple images with navigation
12
+ - ✅ **Filesystem Integration** - Save images to device using @umituz/react-native-filesystem
13
+ - ✅ **Type-Safe** - Full TypeScript support
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @umituz/react-native-image
19
+ ```
20
+
21
+ ## Peer Dependencies
22
+
23
+ ```bash
24
+ npm install expo-image-manipulator react-native-image-viewing @umituz/react-native-filesystem
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Image Manipulation
30
+
31
+ ```tsx
32
+ import { useImage } from '@umituz/react-native-image';
33
+
34
+ const MyScreen = () => {
35
+ const { resize, crop, rotate, compress, isProcessing } = useImage();
36
+
37
+ const handleResize = async () => {
38
+ const resized = await resize(imageUri, 800, 600);
39
+ if (resized) {
40
+ console.log('Resized:', resized.uri);
41
+ }
42
+ };
43
+
44
+ const handleCrop = async () => {
45
+ const cropped = await crop(imageUri, {
46
+ originX: 0,
47
+ originY: 0,
48
+ width: 500,
49
+ height: 500,
50
+ });
51
+ };
52
+
53
+ const handleCompress = async () => {
54
+ const compressed = await compress(imageUri, 0.7);
55
+ };
56
+
57
+ return (
58
+ <View>
59
+ <Button onPress={handleResize} disabled={isProcessing}>
60
+ Resize Image
61
+ </Button>
62
+ </View>
63
+ );
64
+ };
65
+ ```
66
+
67
+ ### Image Gallery Viewer
68
+
69
+ ```tsx
70
+ import { useImageGallery } from '@umituz/react-native-image';
71
+ import ImageViewing from 'react-native-image-viewing';
72
+
73
+ const MyScreen = () => {
74
+ const { visible, currentIndex, images, open, close, setIndex } = useImageGallery();
75
+
76
+ const handleOpenGallery = () => {
77
+ open(['uri1', 'uri2', 'uri3'], 0);
78
+ };
79
+
80
+ return (
81
+ <View>
82
+ <Button onPress={handleOpenGallery}>Open Gallery</Button>
83
+
84
+ <ImageViewing
85
+ images={images}
86
+ imageIndex={currentIndex}
87
+ visible={visible}
88
+ onRequestClose={close}
89
+ onIndexChange={setIndex}
90
+ />
91
+ </View>
92
+ );
93
+ };
94
+ ```
95
+
96
+ ### Image Service (Direct Usage)
97
+
98
+ ```tsx
99
+ import { ImageService } from '@umituz/react-native-image';
100
+
101
+ // Resize image
102
+ const resized = await ImageService.resize(uri, 800, 600);
103
+
104
+ // Crop image
105
+ const cropped = await ImageService.crop(uri, {
106
+ originX: 0,
107
+ originY: 0,
108
+ width: 500,
109
+ height: 500,
110
+ });
111
+
112
+ // Create thumbnail
113
+ const thumbnail = await ImageService.createThumbnail(uri, 200);
114
+
115
+ // Save image
116
+ const savedUri = await ImageService.saveImage(uri, 'my-image.jpg');
117
+ ```
118
+
119
+ ### Image Utilities
120
+
121
+ ```tsx
122
+ import { ImageUtils, ImageFormat } from '@umituz/react-native-image';
123
+
124
+ // Get orientation
125
+ const orientation = ImageUtils.getOrientation(1920, 1080);
126
+ // Returns: ImageOrientation.LANDSCAPE
127
+
128
+ // Calculate aspect ratio
129
+ const ratio = ImageUtils.getAspectRatio(1920, 1080);
130
+ // Returns: 1.777...
131
+
132
+ // Fit to size
133
+ const dimensions = ImageUtils.fitToSize(1920, 1080, 800, 600);
134
+ // Returns: { width: 800, height: 450 }
135
+
136
+ // Get format from URI
137
+ const format = ImageUtils.getFormatFromUri('image.jpg');
138
+ // Returns: ImageFormat.JPEG
139
+
140
+ // Format file size
141
+ const size = ImageUtils.formatFileSize(1024000);
142
+ // Returns: "1000.0 KB"
143
+ ```
144
+
145
+ ## API Reference
146
+
147
+ ### `useImage()`
148
+
149
+ React hook for image manipulation operations.
150
+
151
+ **Returns:**
152
+ - `resize(uri, width?, height?, options?)` - Resize image
153
+ - `crop(uri, cropArea, options?)` - Crop image
154
+ - `rotate(uri, degrees, options?)` - Rotate image
155
+ - `flip(uri, flipOptions, options?)` - Flip image
156
+ - `manipulate(uri, action, options?)` - Perform multiple manipulations
157
+ - `compress(uri, quality?)` - Compress image
158
+ - `resizeToFit(uri, maxWidth, maxHeight, options?)` - Resize to fit within max dimensions
159
+ - `createThumbnail(uri, size?, options?)` - Create thumbnail
160
+ - `cropToSquare(uri, width, height, options?)` - Crop to square (centered)
161
+ - `convertFormat(uri, format, quality?)` - Convert image format
162
+ - `saveImage(uri, filename?)` - Save image to device
163
+ - `isProcessing` - Processing state
164
+ - `error` - Error message
165
+
166
+ ### `useImageGallery()`
167
+
168
+ React hook for image gallery and viewer.
169
+
170
+ **Returns:**
171
+ - `visible` - Gallery visibility state
172
+ - `currentIndex` - Current image index
173
+ - `images` - Image viewer items
174
+ - `open(images, startIndex?, options?)` - Open gallery
175
+ - `close()` - Close gallery
176
+ - `setIndex(index)` - Set current image index
177
+ - `options` - Gallery options
178
+
179
+ ### `ImageService`
180
+
181
+ Service class for image manipulation.
182
+
183
+ **Methods:**
184
+ - `resize(uri, width?, height?, options?)` - Resize image
185
+ - `crop(uri, cropArea, options?)` - Crop image
186
+ - `rotate(uri, degrees, options?)` - Rotate image
187
+ - `flip(uri, flip, options?)` - Flip image
188
+ - `manipulate(uri, action, options?)` - Perform multiple manipulations
189
+ - `compress(uri, quality?)` - Compress image
190
+ - `resizeToFit(uri, maxWidth, maxHeight, options?)` - Resize to fit
191
+ - `createThumbnail(uri, size?, options?)` - Create thumbnail
192
+ - `cropToSquare(uri, width, height, options?)` - Crop to square
193
+ - `convertFormat(uri, format, quality?)` - Convert format
194
+ - `saveImage(uri, filename?)` - Save image
195
+
196
+ ### `ImageViewerService`
197
+
198
+ Service class for image viewer configuration.
199
+
200
+ **Methods:**
201
+ - `prepareImages(uris)` - Prepare images for viewer
202
+ - `prepareImagesWithMetadata(items)` - Prepare images with metadata
203
+ - `createViewerConfig(images, startIndex?, onDismiss?, options?)` - Create viewer config
204
+ - `getDefaultOptions()` - Get default gallery options
205
+
206
+ ### `ImageUtils`
207
+
208
+ Utility class for image operations.
209
+
210
+ **Methods:**
211
+ - `getOrientation(width, height)` - Get image orientation
212
+ - `getAspectRatio(width, height)` - Calculate aspect ratio
213
+ - `fitToSize(width, height, maxWidth, maxHeight)` - Fit to size
214
+ - `getThumbnailSize(width, height, thumbnailSize?)` - Get thumbnail size
215
+ - `isValidImageUri(uri)` - Validate image URI
216
+ - `getFormatFromUri(uri)` - Get format from URI
217
+ - `getExtensionFromFormat(format)` - Get extension from format
218
+ - `getSquareCrop(width, height)` - Get square crop dimensions
219
+ - `formatFileSize(bytes)` - Format file size
220
+ - `needsCompression(bytes, maxSizeMB?)` - Check if needs compression
221
+
222
+ ## Types
223
+
224
+ - `ImageFormat` - JPEG | PNG | WEBP
225
+ - `ImageOrientation` - PORTRAIT | LANDSCAPE | SQUARE
226
+ - `SaveFormat` - 'jpeg' | 'png' | 'webp'
227
+ - `ImageManipulateAction` - Image manipulation action
228
+ - `ImageSaveOptions` - Image save options
229
+ - `ImageManipulationResult` - Manipulation result
230
+ - `ImageViewerItem` - Image viewer item
231
+ - `ImageGalleryOptions` - Gallery options
232
+
233
+ ## Constants
234
+
235
+ - `IMAGE_CONSTANTS` - Image constants (MAX_WIDTH, MAX_HEIGHT, DEFAULT_QUALITY, THUMBNAIL_SIZE, etc.)
236
+
237
+ ## Important Notes
238
+
239
+ ⚠️ **File Operations**: This package uses `@umituz/react-native-filesystem` for file operations. Make sure to install it as a peer dependency.
240
+
241
+ ⚠️ **Image Viewer**: This package provides hooks and services for `react-native-image-viewing`. You need to render the `ImageViewing` component yourself in your app.
242
+
243
+ ## License
244
+
245
+ MIT
246
+
247
+ ## Author
248
+
249
+ Ümit UZ <umit@umituz.com>
250
+
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@umituz/react-native-image",
3
+ "version": "1.1.0",
4
+ "description": "Image manipulation and viewing for React Native apps - resize, crop, rotate, flip, compress, gallery viewer",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "scripts": {
8
+ "typecheck": "tsc --noEmit",
9
+ "lint": "tsc --noEmit",
10
+ "version:minor": "npm version minor -m 'chore: release v%s'",
11
+ "version:major": "npm version major -m 'chore: release v%s'"
12
+ },
13
+ "keywords": [
14
+ "react-native",
15
+ "image",
16
+ "manipulation",
17
+ "resize",
18
+ "crop",
19
+ "rotate",
20
+ "flip",
21
+ "compress",
22
+ "gallery",
23
+ "viewer"
24
+ ],
25
+ "author": "Ümit UZ <umit@umituz.com>",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/umituz/react-native-image"
30
+ },
31
+ "peerDependencies": {
32
+ "react": ">=18.2.0",
33
+ "react-native": ">=0.74.0",
34
+ "expo-image-manipulator": ">=11.0.0",
35
+ "react-native-image-viewing": ">=0.2.0",
36
+ "@umituz/react-native-filesystem": "latest"
37
+ },
38
+ "devDependencies": {
39
+ "@types/react": "^18.2.45",
40
+ "@types/react-native": "^0.73.0",
41
+ "react": "^18.2.0",
42
+ "react-native": "^0.74.0",
43
+ "typescript": "^5.3.3"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "files": [
49
+ "src",
50
+ "README.md",
51
+ "LICENSE"
52
+ ]
53
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Image Domain - Core Entities
3
+ *
4
+ * This file defines core types and interfaces for image manipulation and viewing.
5
+ * Handles image operations using expo-image-manipulator and react-native-image-viewing.
6
+ */
7
+
8
+ /**
9
+ * Image format types
10
+ */
11
+ export enum ImageFormat {
12
+ JPEG = 'jpeg',
13
+ PNG = 'png',
14
+ WEBP = 'webp',
15
+ }
16
+
17
+ /**
18
+ * Image save format for expo-image-manipulator
19
+ */
20
+ export type SaveFormat = 'jpeg' | 'png' | 'webp';
21
+
22
+ /**
23
+ * Image manipulation action types
24
+ */
25
+ export interface ImageManipulateAction {
26
+ resize?: { width?: number; height?: number };
27
+ crop?: { originX: number; originY: number; width: number; height: number };
28
+ rotate?: number; // degrees
29
+ flip?: { vertical?: boolean; horizontal?: boolean };
30
+ }
31
+
32
+ /**
33
+ * Image save options
34
+ */
35
+ export interface ImageSaveOptions {
36
+ format?: SaveFormat;
37
+ compress?: number; // 0-1
38
+ base64?: boolean;
39
+ }
40
+
41
+ /**
42
+ * Image manipulation result
43
+ */
44
+ export interface ImageManipulationResult {
45
+ uri: string;
46
+ width: number;
47
+ height: number;
48
+ base64?: string;
49
+ }
50
+
51
+ /**
52
+ * Image metadata
53
+ */
54
+ export interface ImageMetadata {
55
+ uri: string;
56
+ width: number;
57
+ height: number;
58
+ format?: ImageFormat;
59
+ size?: number; // bytes
60
+ orientation?: ImageOrientation;
61
+ }
62
+
63
+ /**
64
+ * Image orientation
65
+ */
66
+ export enum ImageOrientation {
67
+ PORTRAIT = 'portrait',
68
+ LANDSCAPE = 'landscape',
69
+ SQUARE = 'square',
70
+ }
71
+
72
+ /**
73
+ * Image viewer item
74
+ */
75
+ export interface ImageViewerItem {
76
+ uri: string;
77
+ title?: string;
78
+ description?: string;
79
+ width?: number;
80
+ height?: number;
81
+ }
82
+
83
+ /**
84
+ * Image gallery options
85
+ */
86
+ export interface ImageGalleryOptions {
87
+ index?: number; // Starting index
88
+ backgroundColor?: string;
89
+ swipeToCloseEnabled?: boolean;
90
+ doubleTapToZoomEnabled?: boolean;
91
+ onDismiss?: () => void;
92
+ onIndexChange?: (index: number) => void;
93
+ }
94
+
95
+ /**
96
+ * Image operation result
97
+ */
98
+ export interface ImageOperationResult {
99
+ success: boolean;
100
+ uri?: string;
101
+ error?: string;
102
+ width?: number;
103
+ height?: number;
104
+ }
105
+
106
+ /**
107
+ * Image constants
108
+ */
109
+ export const IMAGE_CONSTANTS = {
110
+ MAX_WIDTH: 2048,
111
+ MAX_HEIGHT: 2048,
112
+ DEFAULT_QUALITY: 0.8,
113
+ THUMBNAIL_SIZE: 200,
114
+ COMPRESS_QUALITY: {
115
+ LOW: 0.5,
116
+ MEDIUM: 0.7,
117
+ HIGH: 0.9,
118
+ },
119
+ FORMAT: {
120
+ JPEG: 'jpeg' as SaveFormat,
121
+ PNG: 'png' as SaveFormat,
122
+ WEBP: 'webp' as SaveFormat,
123
+ },
124
+ } as const;
125
+
126
+ /**
127
+ * Image utilities
128
+ */
129
+ export class ImageUtils {
130
+ /**
131
+ * Get image orientation from dimensions
132
+ */
133
+ static getOrientation(width: number, height: number): ImageOrientation {
134
+ if (width > height) return ImageOrientation.LANDSCAPE;
135
+ if (height > width) return ImageOrientation.PORTRAIT;
136
+ return ImageOrientation.SQUARE;
137
+ }
138
+
139
+ /**
140
+ * Calculate aspect ratio
141
+ */
142
+ static getAspectRatio(width: number, height: number): number {
143
+ return width / height;
144
+ }
145
+
146
+ /**
147
+ * Calculate dimensions to fit within max size while preserving aspect ratio
148
+ */
149
+ static fitToSize(
150
+ width: number,
151
+ height: number,
152
+ maxWidth: number,
153
+ maxHeight: number
154
+ ): { width: number; height: number } {
155
+ const aspectRatio = ImageUtils.getAspectRatio(width, height);
156
+
157
+ let newWidth = width;
158
+ let newHeight = height;
159
+
160
+ if (width > maxWidth) {
161
+ newWidth = maxWidth;
162
+ newHeight = newWidth / aspectRatio;
163
+ }
164
+
165
+ if (newHeight > maxHeight) {
166
+ newHeight = maxHeight;
167
+ newWidth = newHeight * aspectRatio;
168
+ }
169
+
170
+ return {
171
+ width: Math.round(newWidth),
172
+ height: Math.round(newHeight),
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Calculate thumbnail dimensions (maintains aspect ratio)
178
+ */
179
+ static getThumbnailSize(
180
+ width: number,
181
+ height: number,
182
+ thumbnailSize: number = IMAGE_CONSTANTS.THUMBNAIL_SIZE
183
+ ): { width: number; height: number } {
184
+ return ImageUtils.fitToSize(width, height, thumbnailSize, thumbnailSize);
185
+ }
186
+
187
+ /**
188
+ * Validate image URI
189
+ */
190
+ static isValidImageUri(uri: string): boolean {
191
+ if (!uri) return false;
192
+
193
+ // Check if it's a valid URI format
194
+ return (
195
+ uri.startsWith('file://') ||
196
+ uri.startsWith('content://') ||
197
+ uri.startsWith('http://') ||
198
+ uri.startsWith('https://') ||
199
+ uri.startsWith('data:image/')
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Get image format from URI
205
+ */
206
+ static getFormatFromUri(uri: string): ImageFormat | null {
207
+ const lowerUri = uri.toLowerCase();
208
+
209
+ if (lowerUri.includes('.jpg') || lowerUri.includes('.jpeg')) {
210
+ return ImageFormat.JPEG;
211
+ }
212
+
213
+ if (lowerUri.includes('.png')) {
214
+ return ImageFormat.PNG;
215
+ }
216
+
217
+ if (lowerUri.includes('.webp')) {
218
+ return ImageFormat.WEBP;
219
+ }
220
+
221
+ return null;
222
+ }
223
+
224
+ /**
225
+ * Get file extension from format
226
+ */
227
+ static getExtensionFromFormat(format: ImageFormat): string {
228
+ switch (format) {
229
+ case ImageFormat.JPEG:
230
+ return 'jpg';
231
+ case ImageFormat.PNG:
232
+ return 'png';
233
+ case ImageFormat.WEBP:
234
+ return 'webp';
235
+ default:
236
+ return 'jpg';
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Calculate crop dimensions for square crop (centered)
242
+ */
243
+ static getSquareCrop(width: number, height: number): ImageManipulateAction['crop'] {
244
+ const size = Math.min(width, height);
245
+ const originX = (width - size) / 2;
246
+ const originY = (height - size) / 2;
247
+
248
+ return {
249
+ originX: Math.round(originX),
250
+ originY: Math.round(originY),
251
+ width: Math.round(size),
252
+ height: Math.round(size),
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Format file size to human-readable string
258
+ */
259
+ static formatFileSize(bytes: number): string {
260
+ if (bytes < 1024) return `${bytes} B`;
261
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
262
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
263
+ }
264
+
265
+ /**
266
+ * Check if image needs compression based on size
267
+ */
268
+ static needsCompression(bytes: number, maxSizeMB: number = 2): boolean {
269
+ const maxBytes = maxSizeMB * 1024 * 1024;
270
+ return bytes > maxBytes;
271
+ }
272
+ }
273
+
package/src/index.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @umituz/react-native-image - Public API
3
+ *
4
+ * Image manipulation and viewing for React Native apps
5
+ * Resize, crop, rotate, flip, compress, gallery viewer
6
+ *
7
+ * Usage:
8
+ * import { useImage, useImageGallery, ImageService, ImageUtils } from '@umituz/react-native-image';
9
+ */
10
+
11
+ // =============================================================================
12
+ // DOMAIN LAYER - Entities
13
+ // =============================================================================
14
+
15
+ export type {
16
+ ImageManipulateAction,
17
+ ImageSaveOptions,
18
+ ImageManipulationResult,
19
+ ImageMetadata,
20
+ ImageViewerItem,
21
+ ImageGalleryOptions,
22
+ ImageOperationResult,
23
+ SaveFormat,
24
+ } from './domain/entities/Image';
25
+
26
+ export {
27
+ ImageFormat,
28
+ ImageOrientation,
29
+ IMAGE_CONSTANTS,
30
+ ImageUtils,
31
+ } from './domain/entities/Image';
32
+
33
+ // =============================================================================
34
+ // INFRASTRUCTURE LAYER - Services
35
+ // =============================================================================
36
+
37
+ export { ImageService } from './infrastructure/services/ImageService';
38
+ export {
39
+ ImageViewerService,
40
+ type ImageViewerConfig,
41
+ } from './infrastructure/services/ImageViewerService';
42
+
43
+ // =============================================================================
44
+ // PRESENTATION LAYER - Hooks
45
+ // =============================================================================
46
+
47
+ export {
48
+ useImage,
49
+ } from './presentation/hooks/useImage';
50
+
51
+ export {
52
+ useImageGallery,
53
+ type UseImageGalleryReturn,
54
+ } from './presentation/hooks/useImageGallery';
55
+