Skip to content

Commit e51db57

Browse files
authored
Merge pull request #8 from tonyito/mvp
Add/Remove Template Image Functionality Updated
2 parents 5b06f95 + 88e60d4 commit e51db57

File tree

11 files changed

+214
-52
lines changed

11 files changed

+214
-52
lines changed

src/actionTypes/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ export const ADD_PROP = 'ADD_PROP';
2626
export const DELETE_ALL_DATA = 'DELETE_ALL_DATA';
2727
export const CHANGE_IMAGE_PATH = 'CHANGE_IMAGE_PATH';
2828
export const UPDATE_HTML_ATTR = 'UPDATE_HTML_ATTR';
29-
export const UPDATE_CHILDREN_SORT = 'UPDATE_CHILDREN_SORT'
29+
export const UPDATE_CHILDREN_SORT = 'UPDATE_CHILDREN_SORT';
30+
export const CHANGE_IMAGE_SOURCE = 'CHANGE_IMAGE_SOURCE';
31+
export const DELETE_IMAGE = 'DELETE_IMAGE';

src/actions/components.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,19 @@ import {
2525
DELETE_ALL_DATA,
2626
UPDATE_HTML_ATTR,
2727
UPDATE_CHILDREN_SORT,
28+
CHANGE_IMAGE_SOURCE,
29+
DELETE_IMAGE
2830
} from '../actionTypes/index.js';
2931

3032
import { loadState } from '../localStorage';
3133
import createFiles from '../utils/createFiles.util.ts';
3234
import createApplicationUtil from '../utils/createApplication.util.ts';
3335

36+
export const changeImagePath = (imageSource: string) => ({
37+
type: CHANGE_IMAGE_SOURCE,
38+
payload: imageSource,
39+
})
40+
3441
export const loadInitData = () => (dispatch: any) => {
3542
loadState().then((data: any) => dispatch({
3643
type: LOAD_INIT_DATA,
@@ -112,6 +119,12 @@ childId: number;
112119
});
113120
};
114121

122+
export const deleteImage = () => ({
123+
type: DELETE_IMAGE,
124+
payload: ''
125+
})
126+
127+
115128
export const exportFiles = ({
116129
components,
117130
path,

src/components/KonvaStage.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
//This component includes a component for the background image to be uploaded as reference for drawing components
2+
//and also the parent rectangle components.
3+
14
import React, { Component } from "react";
2-
import { Stage, Layer, Line } from "react-konva";
5+
import { Stage, Layer, Line, Image } from "react-konva";
36
import Rectangle from "./Rectangle.tsx";
47
import cloneDeep from "../utils/cloneDeep.ts";
58
import { ComponentInt, ComponentsInt, ChildInt } from "../utils/Interfaces.ts";
9+
import isEmpty from '../utils/isEmpty';
610

711
interface PropsInt {
12+
image: HTMLImageElement;
813
components: ComponentsInt;
914
focusComponent: ComponentInt;
1015
selectableChildren: Array<number>;
@@ -102,6 +107,7 @@ class KonvaStage extends Component<PropsInt, StateInt> {
102107
}
103108
};
104109

110+
//event handler to handle mouse click
105111
handleStageMouseDown = (e: any) => {
106112
// clicked on stage - clear selection
107113
if (e.target === e.target.getStage()) {
@@ -116,7 +122,6 @@ class KonvaStage extends Component<PropsInt, StateInt> {
116122

117123
// find clicked rect by its name
118124
const rectChildId = e.target.attrs.childId;
119-
// console.log("user clicked on child rectangle with childId: ", rectChildId);
120125
this.props.changeFocusChild({ childId: rectChildId });
121126
this.props.changeComponentFocusChild({
122127
componentId: this.props.focusComponent.id,
@@ -167,6 +172,7 @@ class KonvaStage extends Component<PropsInt, StateInt> {
167172

168173
render() {
169174
const {
175+
image,
170176
components,
171177
handleTransform,
172178
focusComponent,
@@ -202,7 +208,10 @@ class KonvaStage extends Component<PropsInt, StateInt> {
202208
}}
203209
>
204210
{this.state.grid}
205-
{this.getDirectChildrenCopy(focusComponent)
211+
<Image image={this.props.focusComponent.id === 1 ? image : null //only display background image if the focused component is <App>
212+
} draggable width={this.state.stageWidth*0.8} height={this.state.stageHeight*0.9 } //for background image uploaded, fix to fit screen
213+
/>
214+
{!isEmpty(focusComponent) && this.getDirectChildrenCopy(focusComponent)
206215
.map((child: ChildInt, i: number) => (
207216
<Rectangle
208217
childType={child.childType}

src/components/Rectangle.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ class Rectangle extends Component<PropsInt, StateInt> {
7373
x: target.x() + focChild.position.x,
7474
y: target.y() + focChild.position.y
7575
};
76-
7776
this.props.handleTransform(componentId, childId, transformation);
7877
}
7978

@@ -166,17 +165,13 @@ class Rectangle extends Component<PropsInt, StateInt> {
166165
strokeWidth={childType === 'COMP' ? 4 : 2}
167166
strokeScaleEnabled={false}
168167
draggable={false}
169-
fill={childId === -1 ? 'white' : null}
168+
fill={null}
170169
shadowBlur={childId === -1 ? 6 : null}
171-
fillPatternImage={
172-
this.state.image ? this.state.image : this.setImage(imageSource)
173-
}
174-
fillPatternScaleX={
175-
this.state.image ? width / this.state.image.width : 1
176-
}
177-
fillPatternScaleY={
178-
this.state.image ? height / this.state.image.height : 1
179-
}
170+
171+
fillPatternImage={this.state.image ? this.state.image : null}
172+
fillPatternScaleX={this.state.image ? width / this.state.image.width : 1}
173+
fillPatternScaleY={this.state.image ? height / this.state.image.height : 1}
174+
180175
_useStrictMode
181176
/>
182177
<Label>

src/containers/AppContainer.tsx

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,94 @@
1-
import React, { Component } from "react";
2-
import { connect } from "react-redux";
3-
import { MuiThemeProvider } from "@material-ui/core/styles";
4-
import LinearProgress from "@material-ui/core/LinearProgress";
5-
import LeftContainer from "./LeftContainer.tsx";
6-
import MainContainer from "./MainContainer.tsx";
7-
import theme from "../components/theme.ts";
8-
import { loadInitData } from "../actions/components.ts";
9-
import { ComponentInt, ComponentsInt } from "../utils/Interfaces.ts";
1+
import React, { Component } from 'react';
2+
import { connect } from 'react-redux';
3+
import { MuiThemeProvider } from '@material-ui/core/styles';
4+
import LinearProgress from '@material-ui/core/LinearProgress';
5+
import LeftContainer from './LeftContainer.tsx';
6+
import MainContainer from './MainContainer.tsx';
7+
import theme from '../components/theme.ts';
8+
import { loadInitData } from '../actions/components.ts';
9+
import { ComponentInt, ComponentsInt } from '../utils/Interfaces.ts';
10+
import * as actions from '../actions/components';
11+
12+
// ** Used with electron to render
13+
const IPC = require('electron').ipcRenderer;
1014

1115
type Props = {
16+
imageSource: string;
1217
components: ComponentsInt;
1318
focusComponent: ComponentInt;
1419
totalComponents: number;
1520
loading: boolean;
1621
selectableChildren: Array<number>;
1722
loadInitData: any;
23+
changeImagePath: any;
24+
changed: boolean;
1825
};
1926

27+
type State = {
28+
image: HTMLImageElement | null;
29+
width: number;
30+
changed: boolean;
31+
}
32+
2033
const mapStateToProps = (store: any) => ({
34+
imageSource: store.workspace.imageSource,
2135
components: store.workspace.components,
2236
totalComponents: store.workspace.totalComponents,
2337
focusComponent: store.workspace.focusComponent,
2438
loading: store.workspace.loading,
2539
selectableChildren: store.workspace.selectableChildren
2640
});
2741

28-
const mapDispatchToProps = { loadInitData };
42+
const mapDispatchToProps = (dispatch: any) => ({
43+
loadInitData,
44+
changeImagePath: (imageSource: string) => dispatch(actions.changeImagePath(imageSource)),
45+
});
46+
47+
class AppContainer extends Component<Props, State> {
48+
49+
constructor(props: Props) {
50+
super(props);
51+
// ** state here to create a collapsable right column where bottom panel currently lives
52+
this.state = {
53+
image: null,
54+
width: 25,
55+
changed: false
56+
};
2957

30-
class AppContainer extends Component<Props> {
31-
state = {
32-
width: 25,
33-
rightColumnOpen: true
58+
IPC.on('new-file', (event, file: string) => {
59+
const image = new window.Image();
60+
image.src = file;
61+
image.onload = () => {
62+
// update state when the image has been uploaded
63+
this.props.changeImagePath(file);
64+
this.setState({ image });
65+
};
66+
});
67+
}
68+
69+
componentDidUpdate(prevProps: Props) {
70+
const { imageSource } = this.props;
71+
const {changed} = this.state;
72+
if (imageSource == '' && changed) {
73+
this.setState({...this.state, image:null, changed:false});
74+
75+
}
76+
else if (imageSource !== prevProps.imageSource) {
77+
this.setImage(imageSource);
78+
}
79+
}
80+
81+
setImage = (imageSource: string) => {
82+
if (imageSource) {
83+
let image: HTMLImageElement;
84+
image = new window.Image();
85+
image.src = imageSource;
86+
image.onload = () => {
87+
// setState will redraw layer
88+
// because "image" property is changed
89+
this.setState({ image, changed: true });
90+
};
91+
}
3492
};
3593

3694
componentDidMount() {
@@ -57,13 +115,14 @@ class AppContainer extends Component<Props> {
57115
focusComponent={focusComponent}
58116
selectableChildren={selectableChildren}
59117
/>
60-
<MainContainer components={components} />
118+
<MainContainer components={components} image={this.state.image}
119+
imageSource={this.props.imageSource}/>
61120
{loading ? (
62121
<div
63122
style={{
64-
alignSelf: "flex-end",
65-
position: "fixed",
66-
width: "100%"
123+
alignSelf: 'flex-end',
124+
position: 'fixed',
125+
width: '100%'
67126
}}
68127
>
69128
<LinearProgress color="secondary" />
@@ -75,7 +134,4 @@ class AppContainer extends Component<Props> {
75134
}
76135
}
77136

78-
export default connect(
79-
mapStateToProps,
80-
mapDispatchToProps
81-
)(AppContainer);
137+
export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);

src/containers/LeftContainer.tsx

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import cloneDeep from '../utils/cloneDeep.ts';
2121
const IPC = require('electron').ipcRenderer;
2222

2323
interface PropsInt {
24+
imageSource: string;
2425
components: ComponentsInt;
2526
focusComponent: ComponentInt;
2627
selectableChildren: Array<number>;
@@ -32,6 +33,7 @@ interface PropsInt {
3233
deleteComponent: any;
3334
createApp: any;
3435
deleteAllData: any;
36+
deleteImage: any;
3537
}
3638

3739
interface StateInt {
@@ -41,6 +43,10 @@ interface StateInt {
4143
genOption: number;
4244
}
4345

46+
const mapStateToProps = (store: any) => ({
47+
imageSource: store.workspace.imageSource
48+
});
49+
4450
const mapDispatchToProps = (dispatch: any) => ({
4551
addComponent: ({ title }: { title: string }) =>
4652
dispatch(actions.addComponent({ title })),
@@ -65,6 +71,7 @@ const mapDispatchToProps = (dispatch: any) => ({
6571
stateComponents: ComponentsInt;
6672
}) => dispatch(actions.deleteComponent({ componentId, stateComponents })),
6773
deleteAllData: () => dispatch(actions.deleteAllData()),
74+
deleteImage: () => dispatch(actions.deleteImage()),
6875
createApp: ({
6976
path,
7077
components,
@@ -98,7 +105,8 @@ class LeftContainer extends Component<PropsInt, StateInt> {
98105
'Export components',
99106
'Export components with application files'
100107
],
101-
genOption: 0
108+
genOption: 0.,
109+
imageSource: this.props.imageSource
102110
};
103111

104112
IPC.on('app_dir_selected', (event: any, path: string) => {
@@ -155,7 +163,10 @@ class LeftContainer extends Component<PropsInt, StateInt> {
155163
this.chooseAppDir();
156164
};
157165

158-
chooseAppDir = () => IPC.send('choose_app_dir');
166+
167+
chooseAppDir = () => IPC.send("choose_app_dir");
168+
addImage = () => IPC.send('update-file');
169+
159170

160171
showGenerateAppModal = () => {
161172
const { closeModal, chooseGenOptions } = this;
@@ -194,14 +205,16 @@ class LeftContainer extends Component<PropsInt, StateInt> {
194205

195206
render(): JSX.Element {
196207
const {
208+
imageSource,
197209
components,
198210
deleteComponent,
199211
focusComponent,
200212
classes,
201213
addChild,
202214
changeFocusComponent,
203215
changeFocusChild,
204-
selectableChildren
216+
selectableChildren,
217+
deleteImage
205218
} = this.props;
206219
const { componentName, modal } = this.state;
207220

@@ -222,6 +235,7 @@ class LeftContainer extends Component<PropsInt, StateInt> {
222235
components={components}
223236
/>
224237
));
238+
const { addImage, clearImage } = this;
225239

226240
return (
227241
<div className='column left'>
@@ -292,6 +306,32 @@ class LeftContainer extends Component<PropsInt, StateInt> {
292306
flexDirection: 'column'
293307
}}
294308
>
309+
{
310+
imageSource ? (
311+
<Button
312+
aria-label="Remove Image"
313+
variant="contained"
314+
fullWidth
315+
onClick={deleteImage
316+
}
317+
className={classes.clearButton}
318+
style={{ borderRadius: 0, top: 0, backgroundColor: '#dc004e', color: '#fff' }}
319+
>
320+
Remove Image
321+
</Button>
322+
) : (
323+
<Button
324+
aria-label="Upload Image"
325+
variant="contained"
326+
fullWidth
327+
onClick={addImage}
328+
className={classes.clearButton}
329+
style={{ borderRadius: 0, top: 0, backgroundColor: '#dc004e', color: '#fff' }}
330+
>
331+
Upload Image
332+
</Button>
333+
)
334+
}
295335
<Button
296336
color='secondary'
297337
aria-label='Delete All'
@@ -376,5 +416,10 @@ function styles(): any {
376416

377417
export default compose(
378418
withStyles(styles),
379-
connect(null, mapDispatchToProps)
419+
420+
connect(
421+
mapStateToProps,
422+
mapDispatchToProps
423+
)
424+
380425
)(LeftContainer);

0 commit comments

Comments
 (0)