Skip to content

Commit 252c723

Browse files
authored
Merge pull request #83 from AvinashAgarwal14/Image-Editor-Integration
Image editor integration
2 parents 29746d4 + 11e30c5 commit 252c723

21 files changed

+670
-79
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
"bootstrap": "^4.1.1",
99
"chart.js": "^2.7.2",
1010
"copy-webpack-plugin": "^5.0.3",
11+
"cropper": "^4.0.0",
12+
"font-awesome": "^4.7.0",
1113
"interactjs": "^1.4.0-rc.13",
1214
"jquery": "^1.9.1",
1315
"jsplumb": "^2.10.1",
@@ -22,6 +24,7 @@
2224
"react-chartjs-2": "^2.7.2",
2325
"react-dom": "^16.4.0",
2426
"react-intl": "^2.4.0",
27+
"react-modal": "^3.9.1",
2528
"react-redux": "^5.0.7",
2629
"react-router-dom": "^4.3.1",
2730
"react-scripts": "1.1.4",

src/components/ImageEditor.js

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import React, {Component} from 'react';
2+
import Cropper from 'cropperjs';
3+
import 'font-awesome/css/font-awesome.min.css';
4+
import 'cropperjs/dist/cropper.css';
5+
import '../css/ImageEditor.css';
6+
7+
class ImageEditor extends Component {
8+
9+
constructor(props) {
10+
super(props);
11+
12+
this.state = {
13+
cropped: false,
14+
cropping: false,
15+
previousUrl: '',
16+
type: '',
17+
url: '',
18+
enableEditor: false
19+
} ;
20+
21+
this.cropper = null;
22+
}
23+
24+
componentDidMount () {
25+
const {mediaSource} = this.props;
26+
this.setState({
27+
...this.state,
28+
url: mediaSource
29+
})
30+
}
31+
32+
cropperSetup=()=>{
33+
34+
const image = document.getElementsByClassName('image')[0];
35+
this.cropper = new Cropper(image, {
36+
autoCrop: false,
37+
dragMode: 'move',
38+
background: false,
39+
40+
crop: ({ detail }) => {
41+
if (detail.width > 0 && detail.height > 0 && !this.state.cropping) {
42+
this.update({
43+
cropping: true,
44+
});
45+
}
46+
},
47+
});
48+
}
49+
50+
crop = () => {
51+
const { cropper} = this;
52+
const { cropping, url, type } = this.state;
53+
54+
if (cropping) {
55+
this.update({
56+
cropped: true,
57+
cropping: false,
58+
previousUrl: url,
59+
url: cropper.getCroppedCanvas(type === 'image/png' ? {} : {
60+
fillColor: '#fff',
61+
}).toDataURL(type),
62+
});
63+
cropper.clear();
64+
}
65+
}
66+
67+
save = () => {
68+
const { cropper} = this;
69+
const {url, type } = this.state;
70+
71+
if(cropper) {
72+
this.update({
73+
previousUrl: url,
74+
url: cropper.getCroppedCanvas(type === 'image/png' ? {} : {
75+
fillColor: '#fff',
76+
}).toDataURL(type),
77+
});
78+
cropper.clear();
79+
}
80+
}
81+
82+
clear = () => {
83+
const { cropping } = this.state;
84+
if (cropping) {
85+
this.cropper.clear();
86+
this.update({
87+
cropping: false,
88+
});
89+
}
90+
}
91+
92+
restore = () => {
93+
const { previousUrl } = this.state;
94+
if (previousUrl) {
95+
this.setState({
96+
...this.state,
97+
cropper: false,
98+
cropping: false,
99+
previousUrl: '',
100+
url: this.state.previousUrl
101+
}, () => {
102+
this.updateCropper();
103+
}
104+
);
105+
}
106+
}
107+
108+
update = (updatedData) => {
109+
this.setState({
110+
...this.state,
111+
...updatedData
112+
}, () => {
113+
if(this.state.cropped) {
114+
this.updateCropper();
115+
this.props.setMediaSource(this.state.url);
116+
this.props.onClose();
117+
}
118+
});
119+
}
120+
121+
updateCropper = () => {
122+
if(this.cropper)
123+
this.cropper.destroy();
124+
this.cropper = null;
125+
this.cropperSetup();
126+
}
127+
128+
editAction = (e) => {
129+
if(this.state.url!=='') {
130+
const { cropper } = this;
131+
let action = e.currentTarget.dataset.action;
132+
switch (action) {
133+
case 'move':
134+
break;
135+
136+
case 'crop':
137+
cropper.setDragMode(action);
138+
break;
139+
140+
case 'zoom-in':
141+
cropper.zoom(0.1);
142+
this.save();
143+
break;
144+
145+
case 'zoom-out':
146+
cropper.zoom(-0.1);
147+
this.save();
148+
break;
149+
150+
case 'rotate-left':
151+
cropper.rotate(-90);
152+
this.save();
153+
break;
154+
155+
case 'rotate-right':
156+
cropper.rotate(90);
157+
this.save();
158+
break;
159+
160+
case 'flip-horizontal':
161+
cropper.scaleX(-cropper.getData().scaleX || -1);
162+
this.save();
163+
break;
164+
165+
case 'flip-vertical':
166+
cropper.scaleY(-cropper.getData().scaleY || -1);
167+
this.save();
168+
break;
169+
170+
default:
171+
}
172+
}
173+
}
174+
175+
enableEditor = () => {
176+
this.setState({
177+
...this.state,
178+
enableEditor: true
179+
});
180+
this.cropperSetup();
181+
}
182+
183+
render () {
184+
return (
185+
<div>
186+
<div className="canvas-image-editor">
187+
<img src = {this.state.url} controls
188+
alt="editable-img"
189+
className = "image center-element"
190+
>
191+
</img>
192+
</div>
193+
{!this.state.enableEditor &&
194+
<div>
195+
<button onClick = {this.enableEditor}
196+
id='edit-button'
197+
className = "modal-edit-button">
198+
</button>,
199+
<button onClick = {this.props.onClose}
200+
id='save-button'
201+
className = "modal-save-button">
202+
</button>
203+
</div>
204+
}
205+
{this.state.enableEditor &&
206+
<div>
207+
<button onClick={()=>{
208+
if(this.state.cropping) {
209+
this.crop();
210+
} else {
211+
this.props.setMediaSource(this.state.url);
212+
this.props.onClose();
213+
}
214+
}}
215+
id='save-button'
216+
className = "modal-save-button">
217+
</button>
218+
<button onClick = {()=>{
219+
this.clear();
220+
this.props.onClose()
221+
}}
222+
id='close-button'
223+
className = "modal-close-button">
224+
</button>
225+
</div>
226+
}
227+
{this.state.enableEditor &&
228+
<div className="toolbar-image-editor">
229+
<button className="toolbar__button" data-action="move" title="Move (M)" onClick={this.editAction}><span className="fa fa-arrows"></span></button>
230+
<button className="toolbar__button" data-action="crop" title="Crop (C)" onClick={this.editAction}><span className="fa fa-crop"></span></button>
231+
<button className="toolbar__button" data-action="zoom-in" title="Zoom In (I)" onClick={this.editAction}><span className="fa fa-search-plus"></span></button>
232+
<button className="toolbar__button" data-action="zoom-out" title="Zoom Out (O)" onClick={this.editAction}><span className="fa fa-search-minus"></span></button>
233+
<button className="toolbar__button" data-action="rotate-left" title="Rotate Left (L)" onClick={this.editAction}><span className="fa fa-rotate-left"></span></button>
234+
<button className="toolbar__button" data-action="rotate-right" title="Rotate Right (R)" onClick={this.editAction}><span className="fa fa-rotate-right"></span></button>
235+
<button className="toolbar__button" data-action="flip-horizontal" title="Flip Horizontal (H)" onClick={this.editAction}><span className="fa fa-arrows-h"></span></button>
236+
<button className="toolbar__button" data-action="flip-vertical" title="Flip Vertical (V)" onClick={this.editAction}><span className="fa fa-arrows-v"></span></button>
237+
<button className="toolbar__button" data-action="restore" title="Undo (Ctrl + Z)" onClick={this.restore}><span className="fa fa-undo"></span></button>
238+
</div>
239+
}
240+
</div>
241+
)
242+
}
243+
}
244+
245+
export default ImageEditor;

src/components/MultimediaJSX.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function QuestionOptionsJSX(props){
4040
export function QuestionJSX(props){
4141

4242
let question;
43-
let {questionType, questionData, handleChangeQues, showMedia, speak} = props;
43+
let {questionType, questionData, handleChangeQues, showMedia, speak, setImageEditorSource} = props;
4444
if( questionType === MULTIMEDIA.text)
4545
question = (
4646
<input
@@ -56,7 +56,7 @@ export function QuestionJSX(props){
5656
<div className = "media-background">
5757
<img src = {questionData}
5858
style = {{height: '200px'}}
59-
onClick = {()=>{showMedia(questionData)}}
59+
onClick = {()=>{showMedia(questionData, 'img', setImageEditorSource)}}
6060
alt="Question"/>
6161
</div>
6262
);
@@ -93,7 +93,7 @@ export function QuestionJSX(props){
9393

9494
export function AnswerOptionsJSX(props){
9595

96-
const {selectOptionType, resetOption, showMedia, speak, options, changeOrder, handleChangeOption, templateType} = props;
96+
const {selectOptionType, resetOption, showMedia, speak, options, changeOrder, handleChangeOption, templateType, setImageEditorSource} = props;
9797
// Answer-Options
9898
let answerOptions = options.map((option, i) => {
9999
if(!option.type)
@@ -165,7 +165,7 @@ export function AnswerOptionsJSX(props){
165165
<div className = "media-background answers">
166166
<img src = {option.data}
167167
style = {{height: '100px'}}
168-
onClick = {()=>{showMedia(option.data)}}
168+
onClick = {()=>{showMedia(option.data, 'img', setImageEditorSource(i))}}
169169
alt="Option"/>
170170
</div>
171171
<button className="btn button-choices-edit"

0 commit comments

Comments
 (0)