Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Header Component #60

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
34 changes: 22 additions & 12 deletions src/Cropper/Cropper.page.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import React, { Component } from 'react';
import { Animated, PanResponder, PanResponderInstance, PanResponderGestureState, ImageCropData } from 'react-native';
import { Animated, PanResponder, PanResponderInstance, PanResponderGestureState, ImageCropData, ImageResizeMode } from 'react-native';
// @ts-ignore; 'react-native-image-rotate' does not have typescript support
import RNImageRotate from '@wili/react-native-image-rotate';
import ImageEditor from '@react-native-community/image-editor';
import { Q } from '../constants';
import Cropper from './Cropper';
import { getCropperLimits } from '../utils';
import { StyleType } from '../Main';

type CropperPageProps = {
footerComponent: JSX.Element;
headerComponent: JSX.Element;
onDone: (croppedImageUri: string) => void;
onError: (err: Error) => void;
onCancel: () => void;
imageUri: string;
imageWidth: number;
imageHeight: number;
imageResizeMode: ImageResizeMode;
TOP_VALUE: number;
LEFT_VALUE: number;
BOTTOM_VALUE: number;
RIGHT_VALUE: number;
initialRotation: number;
NOT_SELECTED_AREA_OPACITY: number;
NOT_SELECTED_AREA_BACKGROUND_COLOR?: string;
BORDER_WIDTH: number;
COMPONENT_WIDTH: number;
COMPONENT_HEIGHT: number;
style: StyleType;
disableBoxPan?: boolean;
};

interface ExtendedAnimatedValue extends Animated.Value {
Expand Down Expand Up @@ -138,7 +144,7 @@ class CropperPage extends Component<CropperPageProps, State> {
const topRightPanResponder = this.initCornerPanResponder('topPosition', 'rightPosition');

const rectanglePosition = new Animated.ValueXY({ x: LEFT_VALUE, y: TOP_VALUE }) as ExtendedAnimatedValueXY;
const rectanglePanResponder = this.initRectanglePanResponder();
const rectanglePanResponder = props.disableBoxPan ? {} : this.initRectanglePanResponder();

this.state = {
topOuterPosition,
Expand Down Expand Up @@ -192,6 +198,8 @@ class CropperPage extends Component<CropperPageProps, State> {
W = this.props.COMPONENT_WIDTH;
H = this.props.COMPONENT_HEIGHT - Q;

outerBackgroundColor = this.props.NOT_SELECTED_AREA_BACKGROUND_COLOR || `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`;

onCancel = () => {
this.props.onCancel();
};
Expand All @@ -203,7 +211,7 @@ class CropperPage extends Component<CropperPageProps, State> {
left: this.state.LEFT_LIMIT,
height: Animated.add(this.props.BORDER_WIDTH - this.state.TOP_LIMIT, this.state.topPosition.y),
width: this.W,
backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`,
backgroundColor: this.outerBackgroundColor,
};
};

Expand All @@ -214,7 +222,7 @@ class CropperPage extends Component<CropperPageProps, State> {
left: this.state.LEFT_LIMIT,
height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))),
width: Animated.add(this.props.BORDER_WIDTH - this.state.LEFT_LIMIT, this.state.leftPosition.x),
backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`,
backgroundColor: this.outerBackgroundColor,
};
};

Expand All @@ -225,7 +233,7 @@ class CropperPage extends Component<CropperPageProps, State> {
left: this.state.LEFT_LIMIT,
height: Animated.add(this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT, Animated.multiply(-1, this.state.bottomPosition.y)),
width: this.W,
backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`,
backgroundColor: this.outerBackgroundColor,
};
};

Expand All @@ -236,7 +244,7 @@ class CropperPage extends Component<CropperPageProps, State> {
left: this.state.rightPosition.x,
height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))),
right: this.state.RIGHT_LIMIT,
backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`,
backgroundColor: this.outerBackgroundColor,
};
};

Expand Down Expand Up @@ -335,7 +343,7 @@ class CropperPage extends Component<CropperPageProps, State> {
left: this.state.LEFT_LIMIT + DIFF,
bottom: this.state.BOTTOM_LIMIT - DIFF,
right: this.state.RIGHT_LIMIT + DIFF,
resizeMode: 'stretch',
resizeMode: this.props.imageResizeMode,
transform: [{ rotate: `${this.state.rotation.toString()}deg` }],
};
};
Expand Down Expand Up @@ -693,7 +701,7 @@ class CropperPage extends Component<CropperPageProps, State> {
(rotatedUri: string) => {
//
ImageEditor.cropImage(rotatedUri, cropData)
.then(croppedUri => {
.then((croppedUri) => {
this.props.onDone(croppedUri);
})
.catch((err: Error) => {
Expand All @@ -710,8 +718,10 @@ class CropperPage extends Component<CropperPageProps, State> {
render() {
return (
<Cropper
style={this.props.style}
imageUri={this.props.imageUri} // 'https://3.imimg.com/data3/SN/NO/MY-10244508/vertical-building-parking-500x500.jpg'
footerComponent={this.props.footerComponent}
headerComponent={this.props.headerComponent}
getTopOuterStyle={this.getTopOuterStyle}
getLeftOuterStyle={this.getLeftOuterStyle}
getBottomOuterStyle={this.getBottomOuterStyle}
Expand Down Expand Up @@ -742,10 +752,10 @@ class CropperPage extends Component<CropperPageProps, State> {
bottomRightPanResponder={this.state.bottomRightPanResponder}
topRightPanResponder={this.state.topRightPanResponder}
rectanglePanResponder={this.state.rectanglePanResponder}
topOuterRef={ref => (this.topOuter = ref)}
leftOuterRef={ref => (this.leftOuter = ref)}
bottomOuterRef={ref => (this.bottomOuter = ref)}
rightOuterRef={ref => (this.rightOuter = ref)}
topOuterRef={(ref) => (this.topOuter = ref)}
leftOuterRef={(ref) => (this.leftOuter = ref)}
bottomOuterRef={(ref) => (this.bottomOuter = ref)}
rightOuterRef={(ref) => (this.rightOuter = ref)}
COMPONENT_WIDTH={this.props.COMPONENT_WIDTH}
COMPONENT_HEIGHT={this.props.COMPONENT_HEIGHT}
W={this.W}
Expand Down
10 changes: 6 additions & 4 deletions src/Cropper/Cropper.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default function getStyles(COMPONENT_WIDTH: number, COMPONENT_HEIGHT: num
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'black',
},
secondContainer: {
position: 'absolute',
Expand All @@ -17,6 +16,12 @@ export default function getStyles(COMPONENT_WIDTH: number, COMPONENT_HEIGHT: num
width: COMPONENT_WIDTH,
height: COMPONENT_HEIGHT,
},
headerContainer: {
position: 'absolute',
top: 20,
left: 10,
right: 10,
},
footerContainer: {
position: 'absolute',
top: COMPONENT_HEIGHT - Q,
Expand All @@ -29,8 +34,6 @@ export default function getStyles(COMPONENT_WIDTH: number, COMPONENT_HEIGHT: num
},
gridColumn: {
flex: 1,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.5)',
},
animation: {
position: 'absolute',
Expand Down Expand Up @@ -92,7 +95,6 @@ export default function getStyles(COMPONENT_WIDTH: number, COMPONENT_HEIGHT: num
borderDesign: {
width: 30,
height: 30,
borderColor: 'white',
},
icon: {
paddingRight: 10,
Expand Down
65 changes: 42 additions & 23 deletions src/Cropper/Cropper.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';
import { View, Animated, Image, PanResponderInstance } from 'react-native';
import { StyleType } from '../Main';
import getStyles from './Cropper.style';

interface CropperProps {
imageUri: string;
headerComponent: JSX.Element;
footerComponent: JSX.Element;
getTopOuterStyle: () => object;
getLeftOuterStyle: () => object;
Expand Down Expand Up @@ -43,12 +45,17 @@ interface CropperProps {
COMPONENT_HEIGHT: number;
W: number;
H: number;
style: StyleType;
}

const Cropper: React.FC<CropperProps> = props => {
const Cropper: React.FC<CropperProps> = (props) => {
const styles = getStyles(props.COMPONENT_WIDTH, props.COMPONENT_HEIGHT, props.W);
const gridColumn = { ...styles.gridColumn, ...props.style.grid.column };
const borderDesign = { ...styles.borderDesign, ...props.style.grid.border };

return (
<View style={styles.container}>
<View style={[styles.container, props.style.container]}>
<View style={styles.headerContainer}>{React.cloneElement(props.headerComponent, { onDone: props.onDone, onCancel: props.onCancel })}</View>
<View style={styles.secondContainer}>
<Image style={props.getImageStyle()} source={{ uri: props.imageUri }} />
</View>
Expand All @@ -67,8 +74,14 @@ const Cropper: React.FC<CropperProps> = props => {
// @ts-ignore */}
<Animated.View ref={props.leftOuterRef} style={[styles.animation, props.getLeftOuterStyle()]} {...props.leftOuterPanResponder.panHandlers} />
{/*
// @ts-ignore */ /* eslint-disable-line */ /* eslint-disable-next-line prettier/prettier */}
<Animated.View ref={props.bottomOuterRef} style={[styles.animation, props.getBottomOuterStyle()]} {...props.bottomOuterPanResponder.panHandlers} />
// @ts-ignore */
/* eslint-disable-line */
/* eslint-disable-next-line prettier/prettier */}
<Animated.View
ref={props.bottomOuterRef}
style={[styles.animation, props.getBottomOuterStyle()]}
{...props.bottomOuterPanResponder.panHandlers}
/>
{/*
// @ts-ignore */}
<Animated.View ref={props.rightOuterRef} style={[styles.animation, props.getRightOuterStyle()]} {...props.rightOuterPanResponder.panHandlers} />
Expand All @@ -80,45 +93,51 @@ const Cropper: React.FC<CropperProps> = props => {

<Animated.View style={[styles.animation, styles.topLeftAnimation, props.getTopLeftStyle()]} {...props.topLeftPanResponder.panHandlers} />
{/* eslint-disable-next-line prettier/prettier */}
<Animated.View style={[styles.animation, styles.bottomLeftAnimation, props.getBottomLeftStyle()]} {...props.bottomLeftPanResponder.panHandlers} />
<Animated.View
style={[styles.animation, styles.bottomLeftAnimation, props.getBottomLeftStyle()]}
{...props.bottomLeftPanResponder.panHandlers}
/>
{/* eslint-disable-next-line prettier/prettier */}
<Animated.View style={[styles.animation, styles.bottomRightAnimation, props.getBottomRightStyle()]} {...props.bottomRightPanResponder.panHandlers} />
<Animated.View
style={[styles.animation, styles.bottomRightAnimation, props.getBottomRightStyle()]}
{...props.bottomRightPanResponder.panHandlers}
/>
<Animated.View style={[styles.animation, styles.topRightAnimation, props.getTopRightStyle()]} {...props.topRightPanResponder.panHandlers} />

<Animated.View style={[styles.animation, props.getRectangleStyle()]} {...props.rectanglePanResponder.panHandlers}>
<View style={styles.gridRow}>
<View style={styles.gridColumn}>
<View style={[styles.borderDesign, { borderLeftWidth: 3, borderTopWidth: 3 }]} />
<View style={gridColumn}>
<View style={[borderDesign, { borderLeftWidth: 3, borderTopWidth: 3 }]} />
</View>
<View style={styles.gridColumn}>
<View style={[styles.borderDesign, { borderTopWidth: 3, alignSelf: 'center' }]} />
<View style={gridColumn}>
<View style={[borderDesign, { borderTopWidth: 3, alignSelf: 'center' }]} />
</View>
<View style={styles.gridColumn}>
<View style={[styles.borderDesign, { borderTopWidth: 3, borderRightWidth: 3, alignSelf: 'flex-end' }]} />
<View style={gridColumn}>
<View style={[borderDesign, { borderTopWidth: 3, borderRightWidth: 3, alignSelf: 'flex-end' }]} />
</View>
</View>

<View style={styles.gridRow}>
<View style={[styles.gridColumn, { flexDirection: 'row' }]}>
<View style={[styles.borderDesign, { borderLeftWidth: 3, alignSelf: 'center' }]} />
<View style={[gridColumn, { flexDirection: 'row' }]}>
<View style={[borderDesign, { borderLeftWidth: 3, alignSelf: 'center' }]} />
</View>
<View style={styles.gridColumn} />
<View style={[styles.gridColumn, { justifyContent: 'center' }]}>
<View style={[styles.borderDesign, { borderRightWidth: 3, alignSelf: 'flex-end' }]} />
<View style={gridColumn} />
<View style={[gridColumn, { justifyContent: 'center' }]}>
<View style={[borderDesign, { borderRightWidth: 3, alignSelf: 'flex-end' }]} />
</View>
</View>

<View style={styles.gridRow}>
<View style={[styles.gridColumn, { justifyContent: 'flex-end' }]}>
<View style={[styles.borderDesign, { borderLeftWidth: 3, borderBottomWidth: 3 }]} />
<View style={[gridColumn, { justifyContent: 'flex-end' }]}>
<View style={[borderDesign, { borderLeftWidth: 3, borderBottomWidth: 3 }]} />
</View>
<View style={[styles.gridColumn, { justifyContent: 'flex-end' }]}>
<View style={[styles.borderDesign, { borderBottomWidth: 3, alignSelf: 'center' }]} />
<View style={[gridColumn, { justifyContent: 'flex-end' }]}>
<View style={[borderDesign, { borderBottomWidth: 3, alignSelf: 'center' }]} />
</View>
<View style={[styles.gridColumn, { justifyContent: 'flex-end' }]}>
<View style={[gridColumn, { justifyContent: 'flex-end' }]}>
<View
style={[
styles.borderDesign,
borderDesign,
{
borderRightWidth: 3,
borderBottomWidth: 3,
Expand Down
24 changes: 24 additions & 0 deletions src/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,48 @@ import React, { Component } from 'react';
import CropperPage from './Cropper/Cropper.page';
import { DefaultFooter } from './common';
import { SCREEN_WIDTH, SCREEN_HEIGHT } from './constants';
import { ImageResizeMode } from 'react-native';

export type StyleType = {
container: { backgroundColor: string };
grid: { border: { borderColor: string }; column: { borderWidth: number; borderColor: string } };
};

export type AmazingCropperProps = {
headerComponent?: JSX.Element;
footerComponent?: JSX.Element;
onDone: (croppedImageUri: string) => void;
onError: (err: Error) => void;
onCancel: () => void;
imageUri: string;
imageWidth: number;
imageHeight: number;
imageResizeMode: ImageResizeMode;
TOP_VALUE?: number;
LEFT_VALUE?: number;
BOTTOM_VALUE?: number;
RIGHT_VALUE?: number;
initialRotation?: number;
NOT_SELECTED_AREA_OPACITY?: number;
NOT_SELECTED_AREA_BACKGROUND_COLOR?: string;
BORDER_WIDTH?: number;
COMPONENT_WIDTH?: number;
COMPONENT_HEIGHT?: number;
style?: StyleType;
disableBoxPan?: boolean;
} & typeof defaultProps;
/// new key change

const defaultProps = {
footerComponent: <DefaultFooter doneText='DONE' rotateText='ROTATE' cancelText='CANCEL' />,
headerComponent: <></>,
onDone: (_croppedImageUri: string) => {},
onError: (_err: Error) => {},
onCancel: () => {},
imageUri: '',
imageWidth: 1280,
imageHeight: 747,
imageResizeMode: 'stretch',
TOP_VALUE: 0,
LEFT_VALUE: 0,
BOTTOM_VALUE: 0,
Expand All @@ -39,6 +53,11 @@ const defaultProps = {
BORDER_WIDTH: 50,
COMPONENT_WIDTH: SCREEN_WIDTH,
COMPONENT_HEIGHT: SCREEN_HEIGHT,
style: {
container: { backgroundColor: '#000' },
grid: { border: { borderColor: '#FFF' }, column: { borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.5)' } },
},
disableBoxPan: false,
};

class Main extends Component<AmazingCropperProps> {
Expand All @@ -47,22 +66,27 @@ class Main extends Component<AmazingCropperProps> {
render() {
return (
<CropperPage
headerComponent={this.props.headerComponent}
footerComponent={this.props.footerComponent}
onDone={this.props.onDone}
onError={this.props.onError}
onCancel={this.props.onCancel}
imageUri={this.props.imageUri}
imageWidth={this.props.imageWidth}
imageHeight={this.props.imageHeight}
imageResizeMode={this.props.imageResizeMode}
TOP_VALUE={this.props.TOP_VALUE}
LEFT_VALUE={this.props.LEFT_VALUE}
BOTTOM_VALUE={this.props.BOTTOM_VALUE}
RIGHT_VALUE={this.props.RIGHT_VALUE}
initialRotation={this.props.initialRotation}
NOT_SELECTED_AREA_OPACITY={this.props.NOT_SELECTED_AREA_OPACITY}
NOT_SELECTED_AREA_BACKGROUND_COLOR={this.props.NOT_SELECTED_AREA_BACKGROUND_COLOR}
BORDER_WIDTH={this.props.BORDER_WIDTH}
COMPONENT_WIDTH={this.props.COMPONENT_WIDTH}
COMPONENT_HEIGHT={this.props.COMPONENT_HEIGHT}
style={this.props.style}
disableBoxPan={this.props.disableBoxPan}
/>
);
}
Expand Down