Skip to content

Commit eca6895

Browse files
committed
Built container for new left panel and canvas. Implemented new nested rendering logic w/ drag and drop functionality and converted to typescript
1 parent dd24825 commit eca6895

File tree

15 files changed

+471
-34
lines changed

15 files changed

+471
-34
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@
142142
"react": "^16.13.0",
143143
"react-ace": "^8.1.0",
144144
"react-d3-tree": "^1.12.3",
145+
"react-dnd": "^11.1.3",
146+
"react-dnd-html5-backend": "^11.1.3",
145147
"react-dom": "^16.4.1",
146148
"react-draggable": "^3.0.5",
147149
"react-konva": "^16.12.0-0",

src/components/AppNew.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React, { useState } from 'react';
2+
import '../public/styles/style.css';
3+
import '../public/styles/styleNew.css';
4+
import { DndProvider } from 'react-dnd';
5+
import { HTML5Backend } from 'react-dnd-html5-backend';
6+
import AppContainer from '../containers/AppContainer';
7+
import { initialState, stateContext } from '../context/context';
8+
// import { Context } from '../interfaces/InterfacesNew';
9+
10+
// Intermediary component to wrap main App component with higher order provider components
11+
export const App = (): JSX.Element => {
12+
const [context, setContext] = useState(initialState);
13+
return (
14+
<div className="app">
15+
<DndProvider backend={HTML5Backend}>
16+
<header
17+
style={{ height: '40px', width: '100%', backgroundColor: 'white' }}
18+
>
19+
ReacType
20+
</header>
21+
<stateContext.Provider value={[context, setContext]}>
22+
<AppContainer />
23+
</stateContext.Provider>
24+
</DndProvider>
25+
</div>
26+
);
27+
};
28+
29+
export default App;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import Grid from '@material-ui/core/Grid';
3+
4+
import { useDrag } from 'react-dnd';
5+
import { ItemTypes } from '../../constants/ItemTypes';
6+
7+
const ComponentPanelItem = (): JSX.Element => {
8+
// useDrag hook allows components in left panel to be drag source
9+
const [{ isDragging }, drag] = useDrag({
10+
item: {
11+
type: ItemTypes.INSTANCE,
12+
newInstance: true
13+
// category,
14+
},
15+
collect: (monitor: any) => ({
16+
isDragging: !!monitor.isDragging()
17+
})
18+
});
19+
return (
20+
<Grid
21+
item
22+
ref={drag}
23+
xs={12}
24+
style={{
25+
color: 'white',
26+
// this is experimental for version: BLADERUNNER THEME
27+
backgroundColor: 'none',
28+
borderRadius: '10px',
29+
// minWidth: '340px',
30+
minHeight: '100px',
31+
border: '2px solid white'
32+
}}
33+
>
34+
Component Panel
35+
</Grid>
36+
);
37+
};
38+
39+
export default ComponentPanelItem;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
3+
import Grid from '@material-ui/core/Grid';
4+
5+
import ComponentPanelItem from './ComponentPanelItemNew';
6+
7+
const ComponentPanel = (): JSX.Element => {
8+
return (
9+
<Grid
10+
container
11+
direction="row"
12+
justify="center"
13+
alignItems="center"
14+
style={{
15+
minWidth: '470px',
16+
padding: '20px'
17+
}}
18+
>
19+
<ComponentPanelItem />
20+
<ComponentPanelItem />
21+
<ComponentPanelItem />
22+
</Grid>
23+
// </div>
24+
);
25+
}
26+
27+
export default ComponentPanel;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React, { useRef, useMemo, useContext } from 'react';
2+
import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd';
3+
import { ItemTypes } from '../../constants/ItemTypes';
4+
import { stateContext } from '../../context/context';
5+
import { updateInstance } from '../../helperFunctions/instances';
6+
7+
const CanvasComponent = (props):JSX.Element => {
8+
const ref = useRef<HTMLDivElement>(null);
9+
const pageId = 1;
10+
11+
const [context, setContext] = useContext(stateContext);
12+
// both useDrop and useDrag used here to allow canvas components to be both a drop target and drag source
13+
const [{ isOver }, drop] = useDrop({
14+
accept: ItemTypes.INSTANCE,
15+
// triggered on drop
16+
drop: (item: any, monitor: DropTargetMonitor) => {
17+
const didDrop = monitor.didDrop();
18+
if (didDrop) {
19+
return;
20+
}
21+
const hoverId = props.id;
22+
// updates state with new instance
23+
setContext(updateInstance(hoverId, item, context, pageId));
24+
},
25+
collect: (monitor: any) => ({
26+
isOver: !!monitor.isOver({ shallow: true })
27+
})
28+
});
29+
30+
const [{ isDragging }, drag] = useDrag({
31+
// setting item attributes to be referenced when updating state with new instance of dragged item
32+
item: {
33+
type: ItemTypes.INSTANCE,
34+
newInstance: false,
35+
id: props.id
36+
},
37+
// canDrag: !props.children.length,
38+
collect: (monitor: any) => ({
39+
isDragging: !!monitor.isDragging()
40+
})
41+
});
42+
43+
const elementStyle = useMemo(
44+
() => ({
45+
// ...props.style,
46+
borderStyle: isOver ? 'dotted' : 'solid',
47+
opacity: isDragging ? 0.5 : 1
48+
}),
49+
[isDragging, isOver]
50+
);
51+
52+
drag(drop(ref));
53+
return (
54+
<div ref={ref} className="componentDefault" style={elementStyle}>
55+
I am a Canvas Component! :D
56+
{props.children}
57+
</div>
58+
);
59+
}
60+
61+
export default CanvasComponent;

src/components/main/MainCanvasNew.tsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React, { useMemo, useContext } from 'react';
2+
import { useDrop } from 'react-dnd';
3+
import { ItemTypes } from '../../constants/ItemTypes';
4+
import { stateContext } from '../../context/context';
5+
import { updateInstance } from '../../helperFunctions/instances';
6+
import CanvasComponent from './CanvasComponentNew';
7+
8+
//renders canvas components based on state
9+
const renderCanvas = (state, page) => {
10+
// get page ID and assign data to children
11+
let pageData;
12+
for (let i = 0; i <= state.pages.length - 1; i++) {
13+
if (state.pages[i]['pageId'] === page) {
14+
pageData = state.pages[i];
15+
break;
16+
}
17+
}
18+
// if there is no page or the page has no children, end the function
19+
if (!pageData || !pageData.children) return;
20+
// iterate through each child on the page ID and render each component
21+
return pageData.children.map(component => renderComponent(component));
22+
};
23+
24+
// renders an instance in the instance tree. If instance has children, those instances will also be rendered recursively
25+
const renderComponent = component => {
26+
const { id, style } = component;
27+
28+
// const style = { border: '5px solid pink', margin: '40px' };
29+
return (
30+
<CanvasComponent id={id} style={style} key={id}>
31+
{/* render all children of component */}
32+
{component.children.map(comp => renderComponent(comp))}
33+
</CanvasComponent>
34+
);
35+
};
36+
37+
function MainCanvas() {
38+
let pageId = 1;
39+
const [context, setContext] = useContext(stateContext);
40+
// useDrop hook allows main canvas to be a drop target
41+
const [{ isOver }, drop] = useDrop({
42+
accept: ItemTypes.INSTANCE,
43+
drop: (item, monitor) => {
44+
const didDrop = monitor.didDrop(); // returns false for direct drop target
45+
if (didDrop) {
46+
return;
47+
}
48+
setContext(updateInstance(null, item, context, pageId));
49+
},
50+
collect: monitor => ({
51+
isOver: !!monitor.isOver()
52+
})
53+
});
54+
const mainCanvasStyle = useMemo(
55+
() => ({
56+
height: '1000px',
57+
width: '1000px',
58+
backgroundColor: 'white',
59+
border: '2px solid black',
60+
borderStyle: isOver ? 'dotted' : 'solid'
61+
}),
62+
[isOver]
63+
);
64+
return (
65+
<div ref={drop} style={mainCanvasStyle}>
66+
{renderCanvas(context, 1)}
67+
</div>
68+
);
69+
}
70+
71+
export default MainCanvas;

src/constants/ItemTypes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const ItemTypes = {
2+
INSTANCE: 'instance'
3+
};
4+
5+
export { ItemTypes };

src/containers/AppContainer.tsx

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { MuiThemeProvider } from '@material-ui/core/styles';
44
import LinearProgress from '@material-ui/core/LinearProgress';
55
import Tutorial from '../components/main/Tutorial';
66
import LeftContainer from './LeftContainer';
7+
import LeftContainerNew from './LeftContainerNew';
78
import MainContainer from './MainContainer';
89
import theme from '../theme';
910
import {
@@ -141,25 +142,23 @@ class AppContainer extends Component<Props, State> {
141142
image.onload = () => {
142143
// update state when the image has been uploaded
143144
this.setState({ image, changed: true });
145+
};
144146
}
145-
}
146147
// else if (imageSource !== prevProps.imageSource && imageSource !== '') {
147148
// this.setImage(imageSource);
148149
// }
149150
}
150151

151-
152-
153152
//this will load the saved sata from last close
154153
componentDidMount() {
155154
const image = new window.Image();
156155
image.src = 'images/iphone.png';
157156
image.onload = () => {
158-
// update state when the image has been uploaded
159-
this.setState({ nativeImageElement: image });
160-
this.props.loadInitData();
161-
}
162-
};
157+
// update state when the image has been uploaded
158+
this.setState({ nativeImageElement: image });
159+
this.props.loadInitData();
160+
};
161+
}
163162

164163
render(): JSX.Element {
165164
const {
@@ -181,12 +180,7 @@ class AppContainer extends Component<Props, State> {
181180
handleNext={this.handleNext}
182181
/>
183182
<div className="app-container">
184-
<LeftContainer //The left side-bar that contains the component cards and the buttons.
185-
components={components}
186-
totalComponents={totalComponents}
187-
focusComponent={focusComponent} //'focused' just means it's the one currently selected.
188-
selectableChildren={selectableChildren} //this toggles whether a component can be added as a child to the focused component
189-
/>
183+
<LeftContainerNew />
190184
<MainContainer
191185
components={components}
192186
image={this.state.image}

src/containers/LeftContainerNew.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
3+
import Grid from '@material-ui/core/Grid';
4+
import TextField from '@material-ui/core/TextField';
5+
import Button from '@material-ui/core/Button';
6+
import AddIcon from '@material-ui/icons/Add';
7+
import Fab from '@material-ui/core/Fab';
8+
import FormControlLabel from '@material-ui/core/FormControlLabel';
9+
10+
import ComponentPanel from '../components/left/ComponentPanelNew';
11+
12+
// Left-hand portion of the app, where component options are displayed
13+
const LeftContainer = (): JSX.Element => {
14+
return (
15+
<div className="column left" style={{ minWidth: '466px' }}>
16+
<Grid container spacing={8} direction="row" alignItems="center">
17+
<ComponentPanel />
18+
</Grid>
19+
</div>
20+
);
21+
}
22+
23+
export default LeftContainer;

src/containers/MainContainer.tsx

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
updateCode
1616
} from '../actions/actionCreators';
1717
import KonvaStage from '../components/main/KonvaStage';
18+
import MainCanvas from '../components/main/MainCanvasNew';
1819
import { PropsInt, ApplicationStateInt } from '../interfaces/Interfaces';
1920

2021
interface MainContPropsInt extends PropsInt {
@@ -31,7 +32,7 @@ interface MainContPropsInt extends PropsInt {
3132
deleteChild(obj: object): void;
3233
changeFocusComponent(arg: { title: string }): void;
3334
updateCode(arg: { componentId: number; code: string }): void;
34-
native: boolean;
35+
native: boolean;
3536
nativeImageElement: HTMLImageElement | null;
3637
}
3738

@@ -102,7 +103,6 @@ const mapStateToProps = (store: { workspace: ApplicationStateInt }) => ({
102103
});
103104

104105
class MainContainer extends Component<MainContPropsInt, StateInt> {
105-
106106
render() {
107107
//const { draggable, modal } = this.state; //this is being destructured but never read.
108108
const {
@@ -129,22 +129,7 @@ class MainContainer extends Component<MainContPropsInt, StateInt> {
129129
<div
130130
className="main" //ref={main} **no function, commenting out**
131131
>
132-
<KonvaStage
133-
image={image}
134-
scaleX={1}
135-
scaleY={1}
136-
// draggable={draggable} this is also from this local state but never read past this container
137-
components={components}
138-
handleTransform={handleTransformation}
139-
focusComponent={focusComponent}
140-
focusChild={focusChild}
141-
changeFocusChild={changeFocusChild}
142-
changeComponentFocusChild={changeComponentFocusChild}
143-
deleteChild={deleteChild}
144-
native={native}
145-
nativeImageElement={nativeImageElement}
146-
/* classes={classes} commented out because not used anywhere*/
147-
/>
132+
<MainCanvas />
148133
</div>
149134
<BottomPanel
150135
focusComponent={focusComponent}

0 commit comments

Comments
 (0)