Repository: hhunaid/react-native-image-crop-tools
Branch: master
Commit: f1fafefaafb1
Files: 24
Total size: 32.0 KB
Directory structure:
gitextract_oass29u0/
├── .gitattributes
├── .gitignore
├── .npmignore
├── .prettierrc
├── .watchmanconfig
├── README.md
├── android/
│ ├── README.md
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── parsempo/
│ └── ImageCropTools/
│ ├── ImageCropToolsModule.kt
│ ├── ImageCropToolsPackage.kt
│ └── ImageCropViewManager.kt
├── ios/
│ ├── CropViewManager.h
│ ├── CropViewManager.m
│ ├── RCTCropView.h
│ ├── RCTCropView.m
│ └── react-native-image-crop-tools.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
├── package.json
├── react-native-image-crop-tools.podspec
├── src/
│ ├── crop-view.component.tsx
│ └── index.ts
├── tsconfig.json
└── tslint.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.pbxproj -text
================================================
FILE: .gitignore
================================================
node_modules
.idea
.DS_Store
dist/
build/
/android/react-native-image-crop-tools.iml
/android/.gradle
contents.xcworkspacedata
xcuserdata
.vscode
================================================
FILE: .npmignore
================================================
src/
Example/
previews/
README.md
tsconfig.json
tslint.json
node_modules
.prettierrc
.watchmanconfig
.idea
/android/build/
/android/.gradle
.vscode
README.md
================================================
FILE: .prettierrc
================================================
{
"printWidth": 120,
"trailingComma": "es5",
"singleQuote": true
}
================================================
FILE: .watchmanconfig
================================================
{
"ignore_dirs": [
"node_modules"
]
}
================================================
FILE: README.md
================================================
# react-native-image-crop-tools
## Previews
<p float="left">
<img src="https://github.com/hhunaid/react-native-image-crop-tools/blob/master/previews/android-preview.gif?raw=true" width="150" />
<img src="https://github.com/hhunaid/react-native-image-crop-tools/blob/master/previews/ios-preview.gif?raw=true" width="150" />
</p>
## Getting started
`$ yarn add react-native-image-crop-tools`
### Automatic installation
Only RN > 0.61.x is supported.
- Android: Installation is automatic.
- iOS: Run `pod install`in `ios` folder.
### Why another cropping library?
Most cropping tools available for RN are usually wrappers over popular native tools which itself isn't a bad thing. But this means you are stuck with their UI and feature set. The ones made in RN are not the most optimized and correct tools.
## Features
1. Native views. Which means performance even on low end devices.
2. You can embed the view into you own UI. It's not very customizable (yet)
3. Change and lock/unlock aspect ratio on the fly (This is the main reason I am making this library)
# NOTE
This library is not supposed to work directly with remote images. There are very few usecases for that. You need to provide a sourceUrl string for a local file which you can obtain from image pickers or by downloading a remote file with rn-fetch-blob
## Usage
```javascript
import { CropView } from 'react-native-image-crop-tools';
<CropView
sourceUrl={uri}
style={styles.cropView}
ref={cropViewRef}
onImageCrop={(res) => console.warn(res)}
keepAspectRatio
aspectRatio={{width: 16, height: 9}}
/>
```
Two methods are exposed on the ref you can use them as follows
```javascript
this.cropViewRef.saveImage(true, 90 // image quality percentage)
this.cropViewRef.rotateImage(true // true for clockwise, false for counterclockwise)
```
### Props
| Name | Description | Default |
| ---- | ----------- | ------- |
| sourceUrl | URL of the source image | `null` |
| aspectRatio | Aspect ratio of the cropped image | `null` |
| keepAspectRatio | Locks the aspect ratio to given aspect ratio | `false` |
| iosDimensionSwapEnabled | (iOS Only) Swaps the width and height of the crop rectangle upon rotation | `false` |
#### TODO:
- [x] Add screenshots
- [x] Support transparency
- [ ] Add access to prebuilt UI for those who want to use it.
================================================
FILE: android/README.md
================================================
README
======
If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm:
1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed
2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK
```
ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle
sdk.dir=/Users/{username}/Library/Android/sdk
```
3. Delete the `maven` folder
4. Run `./gradlew installArchives`
5. Verify that latest set of generated files is in the maven folder with the correct version number
================================================
FILE: android/build.gradle
================================================
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
buildscript {
ext.kotlin_version = '1.7.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
flatDir {
dirs 'libs'
}
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion safeExtGet('compileSdkVersion', 31)
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 17)
targetSdkVersion safeExtGet('targetSdkVersion', 31)
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
api "com.facebook.react:react-native:+"
implementation 'com.github.CanHub:Android-Image-Cropper:4.1.0'
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
================================================
FILE: android/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.parsempo.ImageCropTools">
</manifest>
================================================
FILE: android/src/main/java/com/parsempo/ImageCropTools/ImageCropToolsModule.kt
================================================
package com.parsempo.ImageCropTools
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
class ImageCropToolsModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "ImageCropTools"
}
}
================================================
FILE: android/src/main/java/com/parsempo/ImageCropTools/ImageCropToolsPackage.kt
================================================
package com.parsempo.ImageCropTools
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class ImageCropToolsPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return mutableListOf(ImageCropToolsModule(reactContext))
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return mutableListOf(ImageCropViewManager())
}
}
================================================
FILE: android/src/main/java/com/parsempo/ImageCropTools/ImageCropViewManager.kt
================================================
package com.parsempo.ImageCropTools
import android.graphics.Bitmap
import android.net.Uri
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.common.MapBuilder
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.canhub.cropper.CropImageView
import com.facebook.react.uimanager.events.RCTEventEmitter
import java.io.File
import java.util.*
class ImageCropViewManager: SimpleViewManager<CropImageView>() {
companion object {
const val REACT_CLASS = "CropView"
const val ON_IMAGE_SAVED = "onImageSaved"
const val SOURCE_URL_PROP = "sourceUrl"
const val KEEP_ASPECT_RATIO_PROP = "keepAspectRatio"
const val ASPECT_RATIO_PROP = "cropAspectRatio"
const val SAVE_IMAGE_COMMAND = 1
const val ROTATE_IMAGE_COMMAND = 2
const val SAVE_IMAGE_COMMAND_NAME = "saveImage"
const val ROTATE_IMAGE_COMMAND_NAME = "rotateImage"
}
override fun createViewInstance(reactContext: ThemedReactContext): CropImageView {
val view = CropImageView(reactContext)
view.setOnCropImageCompleteListener { _, result ->
if (result.isSuccessful) {
val map = Arguments.createMap()
map.putString("uri", result.getUriFilePath(reactContext, true).toString())
map.putInt("x", result.cropRect!!.left)
map.putInt("y", result.cropRect!!.top)
map.putInt("width", result.cropRect!!.width())
map.putInt("height", result.cropRect!!.height())
reactContext.getJSModule(RCTEventEmitter::class.java)?.receiveEvent(
view.id,
ON_IMAGE_SAVED,
map
)
}
}
return view
}
override fun getName(): String {
return REACT_CLASS
}
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
return MapBuilder.of(
ON_IMAGE_SAVED,
MapBuilder.of("registrationName", ON_IMAGE_SAVED)
)
}
override fun getCommandsMap(): MutableMap<String, Int> {
return MapBuilder.of(
SAVE_IMAGE_COMMAND_NAME, SAVE_IMAGE_COMMAND,
ROTATE_IMAGE_COMMAND_NAME, ROTATE_IMAGE_COMMAND
)
}
override fun receiveCommand(root: CropImageView, commandId: Int, args: ReadableArray?) {
when (commandId) {
SAVE_IMAGE_COMMAND -> {
val preserveTransparency = args?.getBoolean(0) ?: false
var extension = "jpg"
var format = Bitmap.CompressFormat.JPEG
if (preserveTransparency && root.croppedImage!!.hasAlpha()) {
extension = "png"
format = Bitmap.CompressFormat.PNG
}
val path = File(root.context.cacheDir, "${UUID.randomUUID()}.$extension").toURI().toString()
val quality = args?.getInt(1) ?: 100
root.croppedImageAsync(format, quality, customOutputUri = Uri.parse(path))
}
ROTATE_IMAGE_COMMAND -> {
val clockwise = args?.getBoolean(0) ?: true
root.rotateImage(if (clockwise) 90 else -90)
}
}
}
@ReactProp(name = SOURCE_URL_PROP)
fun setSourceUrl(view: CropImageView, url: String?) {
url?.let {
view.setImageUriAsync(Uri.parse(it))
}
}
@ReactProp(name = KEEP_ASPECT_RATIO_PROP)
fun setFixedAspectRatio(view: CropImageView, fixed: Boolean) {
view.setFixedAspectRatio(fixed)
}
@ReactProp(name = ASPECT_RATIO_PROP)
fun setAspectRatio(view: CropImageView, aspectRatio: ReadableMap?) {
if (aspectRatio != null) {
view.setAspectRatio(aspectRatio.getInt("width"), aspectRatio.getInt("height"))
}else {
view.clearAspectRatio()
}
}
}
================================================
FILE: ios/CropViewManager.h
================================================
//
// Header.h
// react-native-image-crop-tools
//
// Created by Hunaid Hassan on 06/01/2020.
//
#import <React/RCTViewManager.h>
NS_ASSUME_NONNULL_BEGIN
@interface CropViewManager : RCTViewManager
@end
NS_ASSUME_NONNULL_END
================================================
FILE: ios/CropViewManager.m
================================================
//
// CropViewManager.m
// react-native-image-crop-tools
//
// Created by Hunaid Hassan on 31/12/2019.
//
#import "CropViewManager.h"
#import "RCTCropView.h"
#import <React/RCTUIManager.h>
@implementation CropViewManager
RCT_EXPORT_MODULE()
-(UIView *)view {
return [[RCTCropView alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(sourceUrl, NSString)
RCT_EXPORT_VIEW_PROPERTY(onImageSaved, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(keepAspectRatio, BOOL)
RCT_EXPORT_VIEW_PROPERTY(cropAspectRatio, CGSize)
RCT_EXPORT_VIEW_PROPERTY(iosDimensionSwapEnabled, BOOL)
RCT_EXPORT_METHOD(saveImage:(nonnull NSNumber*) reactTag
preserveTransparency:(BOOL) preserveTransparency
quality:(nonnull NSNumber *) quality) {
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
RCTCropView *cropView = (RCTCropView *)viewRegistry[reactTag];
CGRect cropFrame = [cropView getCropFrame];
UIImage *image = [cropView getCroppedImage];
NSString *extension = @"jpg";
if ([[image valueForKey:@"hasAlpha"] boolValue] && preserveTransparency) {
extension = @"png";
}
NSArray *paths = [[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask];
NSURL *url = [[paths firstObject] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [[NSUUID UUID] UUIDString], extension]];
if ([[image valueForKey:@"hasAlpha"] boolValue] && preserveTransparency) {
[UIImagePNGRepresentation(image) writeToURL:url atomically:YES];
}else {
[UIImageJPEGRepresentation(image, [quality floatValue] / 100.0f) writeToURL:url atomically:YES];
}
cropView.onImageSaved(@{
@"uri": url.absoluteString,
@"width": [NSNumber numberWithDouble:cropFrame.size.width],
@"height": [NSNumber numberWithDouble:cropFrame.size.height],
@"x": [NSNumber numberWithDouble:cropFrame.origin.x],
@"y": [NSNumber numberWithDouble:cropFrame.origin.y]
});
}];
}
RCT_EXPORT_METHOD(rotateImage:(nonnull NSNumber*) reactTag clockwise:(BOOL) clockwise) {
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
RCTCropView *cropView = (RCTCropView *)viewRegistry[reactTag];
[cropView rotateImage:clockwise];
}];
}
@end
================================================
FILE: ios/RCTCropView.h
================================================
//
// RCTCropView.h
// react-native-image-crop-tools
//
// Created by Hunaid Hassan on 06/01/2020.
//
#import <React/RCTView.h>
#import <Photos/Photos.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTCropView : RCTView
@property (nonatomic, strong) NSString * sourceUrl;
@property (atomic, assign) BOOL keepAspectRatio;
@property (atomic, assign) BOOL iosDimensionSwapEnabled;
@property (nonatomic, assign) CGSize cropAspectRatio;
@property (nonatomic, strong) RCTDirectEventBlock onImageSaved;
- (UIImage *)getCroppedImage;
- (CGRect)getCropFrame;
- (void)rotateImage:(BOOL)clockwise;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: ios/RCTCropView.m
================================================
//
// RCTCropView.m
// react-native-image-crop-tools
//
// Created by Hunaid Hassan on 06/01/2020.
//
#import "RCTCropView.h"
#import <TOCropViewController/TOCropView.h>
#import <TOCropViewController/UIImage+CropRotate.h>
#import <Photos/Photos.h>
@implementation RCTCropView {
TOCropView * _inlineCropView;
}
@synthesize sourceUrl, keepAspectRatio, cropAspectRatio, iosDimensionSwapEnabled;
- (void)layoutSubviews {
if (_inlineCropView == nil) {
if([sourceUrl rangeOfString:@"ph://"].location == 0) {
NSString *url = [sourceUrl stringByReplacingOccurrencesOfString:@"ph://" withString:@""];
PHImageRequestOptions * requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
requestOptions.synchronous = YES;
PHAsset *photosAsset = [PHAsset fetchAssetsWithLocalIdentifiers:@[url] options: nil].lastObject;
PHImageManager *manager = [PHImageManager defaultManager];
__block UIImage *blockImage;
[manager requestImageForAsset:photosAsset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
blockImage = image;
}];
_inlineCropView = [[TOCropView alloc] initWithImage:blockImage];
} else {
UIImage * image =[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:sourceUrl]]];
_inlineCropView = [[TOCropView alloc] initWithImage:image];
}
_inlineCropView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_inlineCropView.frame = self.bounds;
if (self->keepAspectRatio) {
_inlineCropView.aspectRatioLockEnabled = keepAspectRatio;
}
if (self->iosDimensionSwapEnabled) {
_inlineCropView.aspectRatioLockDimensionSwapEnabled = iosDimensionSwapEnabled;
}
if (!CGSizeEqualToSize(self -> cropAspectRatio, CGSizeZero)) {
_inlineCropView.aspectRatio = self->cropAspectRatio;
}
[_inlineCropView moveCroppedContentToCenterAnimated:NO];
[_inlineCropView performInitialSetup];
[self addSubview:_inlineCropView];
}
}
- (UIImage *)getCroppedImage {
return [_inlineCropView.image croppedImageWithFrame:_inlineCropView.imageCropFrame angle:_inlineCropView.angle circularClip:NO];
}
- (CGRect)getCropFrame {
return _inlineCropView.imageCropFrame;
}
- (void)setCropAspectRatio:(CGSize)aspectRatio {
if (_inlineCropView) {
_inlineCropView.aspectRatio = aspectRatio;
}
self->cropAspectRatio = aspectRatio;
}
-(CGSize)cropAspectRatio {
return _inlineCropView.aspectRatio;
}
- (void)setKeepAspectRatio:(BOOL)keepAspectRatio {
if (_inlineCropView) {
_inlineCropView.aspectRatioLockEnabled = keepAspectRatio;
}
self->keepAspectRatio = keepAspectRatio;
}
- (BOOL)keepAspectRatio {
return _inlineCropView.aspectRatioLockEnabled;
}
- (void)setIosDimensionSwapEnabled:(BOOL)iosDimensionSwapEnabled {
if (_inlineCropView) {
_inlineCropView.aspectRatioLockDimensionSwapEnabled = iosDimensionSwapEnabled;
}
self->iosDimensionSwapEnabled = iosDimensionSwapEnabled;
}
- (BOOL)iosDimensionSwapEnabled {
return _inlineCropView.aspectRatioLockDimensionSwapEnabled;
}
- (void)rotateImage:(BOOL)clockwise {
[_inlineCropView rotateImageNinetyDegreesAnimated:YES clockwise:clockwise];
}
@end
================================================
FILE: ios/react-native-image-crop-tools.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
20E26B5827222734001DE3D2 /* CropViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E26B5527222734001DE3D2 /* CropViewManager.m */; };
20E26B5927222734001DE3D2 /* RCTCropView.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E26B5727222734001DE3D2 /* RCTCropView.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
20E26B46272226EF001DE3D2 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
20E26B48272226EF001DE3D2 /* libreact-native-image-crop-tools.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libreact-native-image-crop-tools.a"; sourceTree = BUILT_PRODUCTS_DIR; };
20E26B5427222734001DE3D2 /* CropViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CropViewManager.h; sourceTree = "<group>"; };
20E26B5527222734001DE3D2 /* CropViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CropViewManager.m; sourceTree = "<group>"; };
20E26B5627222734001DE3D2 /* RCTCropView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCropView.h; sourceTree = "<group>"; };
20E26B5727222734001DE3D2 /* RCTCropView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCropView.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
20E26B45272226EF001DE3D2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
20E26B3F272226EF001DE3D2 = {
isa = PBXGroup;
children = (
20E26B5427222734001DE3D2 /* CropViewManager.h */,
20E26B5527222734001DE3D2 /* CropViewManager.m */,
20E26B5627222734001DE3D2 /* RCTCropView.h */,
20E26B5727222734001DE3D2 /* RCTCropView.m */,
20E26B49272226EF001DE3D2 /* Products */,
);
sourceTree = "<group>";
};
20E26B49272226EF001DE3D2 /* Products */ = {
isa = PBXGroup;
children = (
20E26B48272226EF001DE3D2 /* libreact-native-image-crop-tools.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
20E26B47272226EF001DE3D2 /* react-native-image-crop-tools */ = {
isa = PBXNativeTarget;
buildConfigurationList = 20E26B51272226EF001DE3D2 /* Build configuration list for PBXNativeTarget "react-native-image-crop-tools" */;
buildPhases = (
20E26B44272226EF001DE3D2 /* Sources */,
20E26B45272226EF001DE3D2 /* Frameworks */,
20E26B46272226EF001DE3D2 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = "react-native-image-crop-tools";
productName = "react-native-image-crop-tools";
productReference = 20E26B48272226EF001DE3D2 /* libreact-native-image-crop-tools.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
20E26B40272226EF001DE3D2 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 1300;
TargetAttributes = {
20E26B47272226EF001DE3D2 = {
CreatedOnToolsVersion = 13.0;
};
};
};
buildConfigurationList = 20E26B43272226EF001DE3D2 /* Build configuration list for PBXProject "react-native-image-crop-tools" */;
compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 20E26B3F272226EF001DE3D2;
productRefGroup = 20E26B49272226EF001DE3D2 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
20E26B47272226EF001DE3D2 /* react-native-image-crop-tools */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
20E26B44272226EF001DE3D2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
20E26B5927222734001DE3D2 /* RCTCropView.m in Sources */,
20E26B5827222734001DE3D2 /* CropViewManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
20E26B4F272226EF001DE3D2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
20E26B50272226EF001DE3D2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
20E26B52272226EF001DE3D2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5NJK8TB2FJ;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
20E26B53272226EF001DE3D2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 5NJK8TB2FJ;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
20E26B43272226EF001DE3D2 /* Build configuration list for PBXProject "react-native-image-crop-tools" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20E26B4F272226EF001DE3D2 /* Debug */,
20E26B50272226EF001DE3D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
20E26B51272226EF001DE3D2 /* Build configuration list for PBXNativeTarget "react-native-image-crop-tools" */ = {
isa = XCConfigurationList;
buildConfigurations = (
20E26B52272226EF001DE3D2 /* Debug */,
20E26B53272226EF001DE3D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 20E26B40272226EF001DE3D2 /* Project object */;
}
================================================
FILE: ios/react-native-image-crop-tools.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: package.json
================================================
{
"name": "react-native-image-crop-tools",
"title": "React Native Image Crop Tools",
"version": "1.8.0",
"description": "Native image crop tools with embeddable UI and on the fly aspect ratio switchin",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.tsx\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json --fix",
"prepublishOnly": "npm run lint",
"preversion": "npm run lint",
"version": "npm run format && git add -A src",
"postversion": "git push && git push --tags"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/hhunaid/react-native-image-crop-tools.git",
"baseUrl": "https://github.com/hhunaid/react-native-image-crop-tools"
},
"keywords": [
"react-native",
"crop",
"cropping",
"image",
"rotate",
"native",
"react",
"typescript"
],
"author": {
"name": "Hunaid Hassan",
"email": "hhunaid@gmail.com"
},
"license": "MIT",
"readmeFilename": "README.md",
"peerDependencies": {
"react": ">=16.0.0",
"react-native": ">=0.59.0-rc.0 <1.0.x"
},
"devDependencies": {
"react": "^16.9.0",
"react-native": "^0.61.0",
"tslint": "^5.17.0",
"tslint-config-prettier": "^1.18.0",
"tslint-react": "^4.0.0",
"typescript": "^3.7.2",
"prettier": "^1.19.1",
"@types/react-native": "^0.60.23"
}
}
================================================
FILE: react-native-image-crop-tools.podspec
================================================
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "react-native-image-crop-tools"
s.version = package["version"]
s.summary = package["description"]
s.description = <<-DESC
react-native-image-crop-tools
DESC
s.homepage = "https://github.com/hhunaid/react-native-image-crop-tools"
s.license = "MIT"
s.authors = { "Hunaid Hassan" => "hhunaid@gmail.com" }
s.platforms = { :ios => "9.0" }
s.source = { :git => "https://github.com/hhunaid/react-native-image-crop-tools.git", :tag => "#{s.version}" }
s.source_files = "ios/**/*.{h,m}"
s.requires_arc = true
s.dependency "React-Core"
s.dependency 'TOCropViewController', '2.5.3'
end
================================================
FILE: src/crop-view.component.tsx
================================================
import React, { createRef } from 'react';
import {
findNodeHandle,
NativeSyntheticEvent,
requireNativeComponent,
StyleProp,
UIManager,
ViewStyle,
} from 'react-native';
const RCTCropView = requireNativeComponent('CropView');
type Response = {
uri: string;
width: number;
height: number;
x: number;
y: number;
};
type Props = {
sourceUrl: string;
style?: StyleProp<ViewStyle>;
onImageCrop?: (res: Response) => void;
keepAspectRatio?: boolean;
aspectRatio?: { width: number; height: number };
iosDimensionSwapEnabled?: boolean;
};
class CropView extends React.PureComponent<Props> {
public static defaultProps = {
keepAspectRatio: false,
iosDimensionSwapEnabled: false,
};
private viewRef = createRef<any>();
public saveImage = (preserveTransparency: boolean = true, quality: number = 90) => {
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.viewRef.current!),
UIManager.getViewManagerConfig('CropView').Commands.saveImage,
[preserveTransparency, quality]
);
};
public rotateImage = (clockwise: boolean = true) => {
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.viewRef.current!),
UIManager.getViewManagerConfig('CropView').Commands.rotateImage,
[clockwise]
);
};
public render() {
const { onImageCrop, aspectRatio, ...rest } = this.props;
return (
<RCTCropView
ref={this.viewRef}
cropAspectRatio={aspectRatio}
onImageSaved={(event: NativeSyntheticEvent<Response>) => {
onImageCrop!(event.nativeEvent);
}}
{...rest}
/>
);
}
}
export default CropView;
================================================
FILE: src/index.ts
================================================
import CropView from './crop-view.component';
export { CropView };
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"experimentalDecorators": true,
"declaration": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "react",
"typeRoots": [
"./node_modules/@types"
],
"lib": ["es6"],
"moduleResolution": "node",
"target": "esnext"
},
"include": [
"src/**/*"
],
"files": ["src/index.ts"],
"exclude": [
"node_modules",
"**/__tests__/*",
"dist"
]
}
================================================
FILE: tslint.json
================================================
{
"extends": [
"tslint:recommended",
"tslint-config-prettier"
],
"rules": {
"interface-over-type-literal": false,
"object-literal-sort-keys": false,
"variable-name": {
"options": [
"check-format",
"allow-leading-underscore",
"allow-pascal-case"
]
},
"no-unused-variable": true,
"no-shadowed-variable": false
}
}
gitextract_oass29u0/ ├── .gitattributes ├── .gitignore ├── .npmignore ├── .prettierrc ├── .watchmanconfig ├── README.md ├── android/ │ ├── README.md │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── parsempo/ │ └── ImageCropTools/ │ ├── ImageCropToolsModule.kt │ ├── ImageCropToolsPackage.kt │ └── ImageCropViewManager.kt ├── ios/ │ ├── CropViewManager.h │ ├── CropViewManager.m │ ├── RCTCropView.h │ ├── RCTCropView.m │ └── react-native-image-crop-tools.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── package.json ├── react-native-image-crop-tools.podspec ├── src/ │ ├── crop-view.component.tsx │ └── index.ts ├── tsconfig.json └── tslint.json
SYMBOL INDEX (4 symbols across 1 files)
FILE: src/crop-view.component.tsx
type Response (line 13) | type Response = {
type Props (line 21) | type Props = {
class CropView (line 30) | class CropView extends React.PureComponent<Props> {
method render (line 54) | public render() {
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (37K chars).
[
{
"path": ".gitattributes",
"chars": 16,
"preview": "*.pbxproj -text\n"
},
{
"path": ".gitignore",
"chars": 146,
"preview": "node_modules\n.idea\n.DS_Store\ndist/\nbuild/\n/android/react-native-image-crop-tools.iml\n/android/.gradle\ncontents.xcworkspa"
},
{
"path": ".npmignore",
"chars": 157,
"preview": "src/\nExample/\npreviews/\nREADME.md\ntsconfig.json\ntslint.json\nnode_modules\n.prettierrc\n.watchmanconfig\n.idea\n/android/buil"
},
{
"path": ".prettierrc",
"chars": 72,
"preview": "{\n \"printWidth\": 120,\n \"trailingComma\": \"es5\",\n \"singleQuote\": true\n}"
},
{
"path": ".watchmanconfig",
"chars": 46,
"preview": "{\n \"ignore_dirs\": [\n \"node_modules\"\n ]\n}\n"
},
{
"path": "README.md",
"chars": 2403,
"preview": "# react-native-image-crop-tools\n\n## Previews\n<p float=\"left\">\n <img src=\"https://github.com/hhunaid/react-native-image-"
},
{
"path": "android/README.md",
"chars": 662,
"preview": "README\n======\n\nIf you want to publish the lib as a maven dependency, follow these steps before publishing a new version "
},
{
"path": "android/build.gradle",
"chars": 1358,
"preview": "def safeExtGet(prop, fallback) {\n rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback\n}\n\nbuildscript {\n "
},
{
"path": "android/src/main/AndroidManifest.xml",
"chars": 131,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.parsempo.ImageCropTools\">\n\n<"
},
{
"path": "android/src/main/java/com/parsempo/ImageCropTools/ImageCropToolsModule.kt",
"chars": 354,
"preview": "package com.parsempo.ImageCropTools\n\nimport com.facebook.react.bridge.ReactApplicationContext\nimport com.facebook.react."
},
{
"path": "android/src/main/java/com/parsempo/ImageCropTools/ImageCropToolsPackage.kt",
"chars": 605,
"preview": "package com.parsempo.ImageCropTools\n\nimport com.facebook.react.ReactPackage\nimport com.facebook.react.bridge.NativeModul"
},
{
"path": "android/src/main/java/com/parsempo/ImageCropTools/ImageCropViewManager.kt",
"chars": 4153,
"preview": "package com.parsempo.ImageCropTools\n\nimport android.graphics.Bitmap\nimport android.net.Uri\nimport com.facebook.react.bri"
},
{
"path": "ios/CropViewManager.h",
"chars": 233,
"preview": "//\n// Header.h\n// react-native-image-crop-tools\n//\n// Created by Hunaid Hassan on 06/01/2020.\n//\n\n#import <React/RCTV"
},
{
"path": "ios/CropViewManager.m",
"chars": 2482,
"preview": "//\n// CropViewManager.m\n// react-native-image-crop-tools\n//\n// Created by Hunaid Hassan on 31/12/2019.\n//\n\n#import \"C"
},
{
"path": "ios/RCTCropView.h",
"chars": 615,
"preview": "//\n// RCTCropView.h\n// react-native-image-crop-tools\n//\n// Created by Hunaid Hassan on 06/01/2020.\n//\n\n#import <React"
},
{
"path": "ios/RCTCropView.m",
"chars": 3810,
"preview": "//\n// RCTCropView.m\n// react-native-image-crop-tools\n//\n// Created by Hunaid Hassan on 06/01/2020.\n//\n\n#import \"RCTCr"
},
{
"path": "ios/react-native-image-crop-tools.xcodeproj/project.pbxproj",
"chars": 10401,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 55;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "ios/react-native-image-crop-tools.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "package.json",
"chars": 1506,
"preview": "{\n \"name\": \"react-native-image-crop-tools\",\n \"title\": \"React Native Image Crop Tools\",\n \"version\": \"1.8.0\",\n \"descri"
},
{
"path": "react-native-image-crop-tools.podspec",
"chars": 798,
"preview": "require \"json\"\n\npackage = JSON.parse(File.read(File.join(__dir__, \"package.json\")))\n\nPod::Spec.new do |s|\n s.name "
},
{
"path": "src/crop-view.component.tsx",
"chars": 1667,
"preview": "import React, { createRef } from 'react';\nimport {\n findNodeHandle,\n NativeSyntheticEvent,\n requireNativeComponent,\n "
},
{
"path": "src/index.ts",
"chars": 68,
"preview": "import CropView from './crop-view.component';\n\nexport { CropView };\n"
},
{
"path": "tsconfig.json",
"chars": 494,
"preview": "{\n \"compilerOptions\": {\n \"experimentalDecorators\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\",\n \"stric"
},
{
"path": "tslint.json",
"chars": 388,
"preview": "{\n \"extends\": [\n \"tslint:recommended\",\n \"tslint-config-prettier\"\n ],\n \"rules\": {\n \"interface-over-type-liter"
}
]
About this extraction
This page contains the full source code of the hhunaid/react-native-image-crop-tools GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (32.0 KB), approximately 9.5k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.