Skip to content

Commit 827f576

Browse files
Allow component to have multiple parents.
1 parent e49cebb commit 827f576

13 files changed

+170
-127
lines changed

src/actionTypes/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ export const LOAD_INIT_DATA = 'LOAD_INIT_DATA';
22
export const ADD_COMPONENT = 'ADD_COMPONENT';
33
export const UPDATE_COMPONENT = 'UPDATE_COMPONENT';
44
export const DELETE_COMPONENT = 'DELETE_COMPONENT';
5-
export const ADD_NEW_CHILD = 'ADD_NEW_CHILD';
6-
export const DELETE_CHILD = 'DELETE_CHILD';
5+
export const UPDATE_CHILDREN = 'UPDATE_CHILDREN';
76
export const REASSIGN_PARENT = 'REASSIGN_PARENT';
87
export const SET_SELECTABLE_PARENTS = 'SET_SELECTABLE_PARENTS';
98
export const EXPORT_FILES = 'EXPORT_FILES';

src/actions/components.js

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import {
33
ADD_COMPONENT,
44
UPDATE_COMPONENT,
55
DELETE_COMPONENT,
6-
ADD_NEW_CHILD,
7-
DELETE_CHILD,
6+
UPDATE_CHILDREN,
87
REASSIGN_PARENT,
98
SET_SELECTABLE_PARENTS,
109
EXPORT_FILES,
@@ -40,30 +39,21 @@ export const loadInitData = () => (dispatch) => {
4039
}));
4140
};
4241

43-
export const addNewChild = (({
44-
id, childIndex, childId,
42+
export const updateChildren = (({
43+
parentIds, childIndex, childId,
4544
}) => ({
46-
type: ADD_NEW_CHILD,
45+
type: UPDATE_CHILDREN,
4746
payload: {
48-
id, childIndex, childId,
47+
parentIds, childIndex, childId,
4948
},
5049
}));
5150

52-
export const deleteChild = (({
53-
parent, childIndex, childId,
54-
}) => ({
55-
type: DELETE_CHILD,
56-
payload: {
57-
parent, childIndex, childId,
58-
},
59-
}));
60-
61-
export const parentReassignment = (({ index, id, parent }) => ({
51+
export const parentReassignment = (({ index, id, parentIds }) => ({
6252
type: REASSIGN_PARENT,
6353
payload: {
6454
index,
6555
id,
66-
parent,
56+
parentIds,
6757
},
6858
}));
6959

@@ -72,19 +62,20 @@ export const addComponent = ({ title }) => (dispatch) => {
7262
dispatch({ type: SET_SELECTABLE_PARENTS });
7363
};
7464

75-
export const deleteComponent = ({ index, id, parent }) => (dispatch) => {
76-
// Delete Component from its parent if it has a parent.
77-
if (parent && parent.id) {
78-
dispatch(deleteChild({ parent, childId: id, childIndex: index }));
65+
export const deleteComponent = ({ index, id, parentIds = [] }) => (dispatch) => {
66+
if (parentIds.length) {
67+
// Delete Component from its parent if it has a parent.
68+
dispatch(updateChildren({ parentIds, childId: id, childIndex: index }));
7969
}
8070
// Reassign Component's children to its parent if it has one or make them orphans
81-
dispatch(parentReassignment({ index, id, parent }));
71+
dispatch(parentReassignment({ index, id, parentIds }));
72+
8273
dispatch({ type: DELETE_COMPONENT, payload: { index, id } });
8374
dispatch({ type: SET_SELECTABLE_PARENTS });
8475
};
8576

8677
export const updateComponent = ({
87-
id, index, parent = null, newParentId = null, color = null, stateful = null,
78+
id, index, newParentId = null, color = null, stateful = null,
8879
}) => (dispatch) => {
8980
dispatch({
9081
type: UPDATE_COMPONENT,
@@ -93,12 +84,8 @@ export const updateComponent = ({
9384
},
9485
});
9586

96-
if (newParentId && newParentId !== 'null') {
97-
dispatch(addNewChild({ id: newParentId, childId: id, childIndex: index }));
98-
}
99-
100-
if (parent && parent.id) {
101-
dispatch(deleteChild({ parent, index, childId: id }));
87+
if (newParentId) {
88+
dispatch(updateChildren({ parentIds: [newParentId], childId: id, childIndex: index }));
10289
}
10390

10491
dispatch({ type: SET_SELECTABLE_PARENTS });

src/components/LeftColExpansionPanel.jsx

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
77
import ExpansionPanelActions from '@material-ui/core/ExpansionPanelActions';
88
import Typography from '@material-ui/core/Typography';
99
import Input from '@material-ui/core/Input';
10+
import MenuItem from '@material-ui/core/MenuItem';
11+
import RemoveCircleOutlineIcon from '@material-ui/icons/RemoveCircleOutline';
12+
import ListItemText from '@material-ui/core/ListItemText';
1013
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
1114
import Switch from '@material-ui/core/Switch';
15+
import Chip from '@material-ui/core/Chip';
1216
import IconButton from '@material-ui/core/IconButton';
1317
import DeleteIcon from '@material-ui/icons/Delete';
1418
import FlipToBackIcon from '@material-ui/icons/FlipToBack';
@@ -28,6 +32,13 @@ const styles = theme => ({
2832
fontSize: theme.typography.pxToRem(15),
2933
fontWeight: theme.typography.fontWeightRegular,
3034
},
35+
chips: {
36+
display: 'flex',
37+
flexWrap: 'wrap',
38+
},
39+
chip: {
40+
margin: theme.spacing.unit / 4,
41+
},
3142
panel: {
3243
backgroundColor: '#333333',
3344
},
@@ -60,6 +71,15 @@ const styles = theme => ({
6071
group: {
6172
margin: `${theme.spacing.unit}px 0`,
6273
},
74+
icon: {
75+
fontSize: '20px',
76+
color: '#000',
77+
transition: 'all .2s ease',
78+
79+
'&:hover': {
80+
color: 'red',
81+
},
82+
},
6383
});
6484

6585
const LeftColExpansionPanel = (props) => {
@@ -79,23 +99,24 @@ const LeftColExpansionPanel = (props) => {
7999
id,
80100
stateful,
81101
color,
82-
parent,
102+
parents,
103+
parentIds,
83104
selectableParents,
84105
} = component;
85106

86-
const parentOptions = [
87-
<option value='null' key=''>
88-
None
89-
</option>,
90-
...selectableParents.map(
91-
selectableParent => <option
92-
value={selectableParent.id}
93-
key={selectableParent.id}
94-
>
95-
{selectableParent.title}
96-
</option>,
97-
),
98-
];
107+
const handleParentChange = (event, parentId = null) => {
108+
let newParentId = parentId;
109+
if (event) {
110+
const selectedParents = event.target.value;
111+
newParentId = selectedParents[selectedParents.length - 1].id;
112+
}
113+
114+
return updateComponent({
115+
index,
116+
id,
117+
newParentId,
118+
});
119+
};
99120

100121
return (
101122
<div className={classes.root}>
@@ -130,24 +151,35 @@ const LeftColExpansionPanel = (props) => {
130151
/>
131152
</div>
132153
<div className={classes.column}>
133-
<InputLabel className={classes.label} htmlFor='parentSelect'>Parent</InputLabel>
154+
<InputLabel className={classes.label} htmlFor='parentSelect'>selectedParents</InputLabel>
134155
<Select
135156
className={classes.light}
136-
native
137-
value={parent.id}
157+
multiple
158+
value={parents}
138159
id='parentSelect'
139160
name='parentName'
140-
onChange={(event) => {
141-
const newParentId = event.target.value;
142-
updateComponent({
143-
newParentId,
144-
index,
145-
id,
146-
parent,
147-
});
148-
}}
161+
disabled={selectableParents.length < 1}
162+
onChange={handleParentChange}
163+
input={<Input id='parentSelect' />}
164+
renderValue={selectedP => (
165+
<div className={classes.chips}>
166+
{selectedP.map(parent => (
167+
<Chip
168+
key={parent.id}
169+
label={parent.title}
170+
className={classes.chip}
171+
onDelete={() => handleParentChange(null, parent.id)}
172+
deleteIcon={<RemoveCircleOutlineIcon className={classes.icon} />}
173+
/>
174+
))}
175+
</div>
176+
)}
149177
>
150-
{parentOptions}
178+
{selectableParents.map(parentObj => (
179+
<MenuItem key={parentObj.id} value={parentObj}>
180+
<ListItemText primary={parentObj.title} />
181+
</MenuItem>
182+
))}
151183
</Select>
152184
</div>
153185
</ExpansionPanelDetails>
@@ -173,7 +205,7 @@ const LeftColExpansionPanel = (props) => {
173205
className={classes.button}
174206
onClick={() => {
175207
deleteComponent({
176-
index, id, parent,
208+
index, id, parentIds,
177209
});
178210
}}
179211
aria-label='Delete'>

src/components/SortableComponent.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import 'react-sortable-tree/style.css';
55

66
const SortableComponent = (props) => {
77
const rootComponents = props.components.filter(
8-
comp => comp.parentId.length === 0,
8+
comp => comp.parentIds.length === 0,
99
).reverse();
10+
1011
return (
1112
<div className="sortable-tree">
1213
<SortableTree
1314
style={{ backgroundColor: 'rgb(37, 37, 38)' }}
1415
treeData={rootComponents}
1516
canDrag={false}
16-
onChange={treeData => this.setState({ treeData })}
17+
onChange={() => {}}
1718
/>
1819
</div>
1920
);
@@ -22,5 +23,5 @@ const SortableComponent = (props) => {
2223
export default SortableComponent;
2324

2425
SortableComponent.propTypes = {
25-
components: PropTypes.array,
26+
components: PropTypes.array.isRequired,
2627
};

src/containers/AppContainer.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import LinearProgress from '@material-ui/core/LinearProgress';
66
import LeftContainer from './LeftContainer.jsx';
77
import MainContainer from './MainContainer.jsx';
88
import RightContainer from './RightContainer.jsx';
9-
import convertIdToObjs from '../utils/convertIdsToObjs.util';
9+
import convertIdsToObjs from '../utils/convertIdsToObjs.util';
1010
import theme from '../components/theme';
1111
import { loadInitData } from '../actions/components';
1212

@@ -51,7 +51,7 @@ class AppContainer extends Component {
5151
loading,
5252
} = this.props;
5353
const { width, rightColumnOpen } = this.state;
54-
const updatedComponents = convertIdToObjs(components);
54+
const updatedComponents = convertIdsToObjs(components);
5555

5656
return (
5757
<MuiThemeProvider theme={theme}>

src/containers/LeftContainer.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ const mapDispatchToProps = dispatch => ({
1616
addComponent: ({ title }) => dispatch(actions.addComponent({ title })),
1717
updateComponent:
1818
({
19-
id, index, parent = null, newParentId = null, color = null, stateful = null,
19+
id, index, newParentId = null, color = null, stateful = null,
2020
}) => dispatch(actions.updateComponent({
21-
id, index, parent, newParentId, color, stateful,
21+
id, index, newParentId, color, stateful,
2222
})),
2323
deleteComponent: ({
24-
index, id, parent,
25-
}) => dispatch(actions.deleteComponent({ index, id, parent })),
24+
index, id, parentIds,
25+
}) => dispatch(actions.deleteComponent({ index, id, parentIds })),
2626
moveToBottom: componentId => dispatch(actions.moveToBottom(componentId)),
2727
moveToTop: componentId => dispatch(actions.moveToTop(componentId)),
2828
openExpansionPanel: component => dispatch(actions.openExpansionPanel(component)),
2929
deleteAllData: () => dispatch(actions.deleteAllData()),
3030
});
3131

32-
const styles = theme => ({
32+
const styles = () => ({
3333
cssLabel: {
3434
color: 'white',
3535

src/containers/MainContainer.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,13 @@ class MainContainer extends Component {
221221
showGenerateAppModal,
222222
setImage,
223223
} = this;
224+
const cursor = this.state.draggable ? 'move' : 'default';
224225

225226
return (
226227
<MuiThemeProvider theme={theme}>
227-
<div className="main-container">
228+
<div
229+
className="main-container"
230+
style={{ cursor }}>
228231
<MainContainerHeader
229232
image={image}
230233
increaseHeight={increaseHeight}

src/containers/RightContainer.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ RightContainer.propTypes = {
7777
deleteProp: PropTypes.func.isRequired,
7878
addProp: PropTypes.func.isRequired,
7979
width: PropTypes.number.isRequired,
80+
rightColumnOpen: PropTypes.bool.isRequired,
8081
};
8182

8283

src/localStorage.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import localforage from 'localforage';
22

3-
export const saveState = state => localforage.setItem('state', state);
4-
5-
export const loadState = () => localforage.getItem('state');
3+
export const saveState = state => localforage.setItem('state-v1.0.1', state);
4+
export const loadState = () => localforage.getItem('state-v1.0.1');

src/reducers/componentReducer.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import {
33
ADD_COMPONENT,
44
UPDATE_COMPONENT,
55
DELETE_COMPONENT,
6-
ADD_NEW_CHILD,
7-
DELETE_CHILD,
6+
UPDATE_CHILDREN,
87
REASSIGN_PARENT,
98
SET_SELECTABLE_PARENTS,
109
EXPORT_FILES,
@@ -28,8 +27,7 @@ import {
2827
addComponent,
2928
updateComponent,
3029
deleteComponent,
31-
addChild,
32-
deleteChild,
30+
updateChildren,
3331
reassignParent,
3432
setSelectableP,
3533
exportFilesSuccess,
@@ -74,10 +72,8 @@ const componentReducer = (state = initialApplicationState, action) => {
7472
return updateComponent(state, action.payload);
7573
case DELETE_COMPONENT:
7674
return deleteComponent(state, action.payload);
77-
case ADD_NEW_CHILD:
78-
return addChild(state, action.payload);
79-
case DELETE_CHILD:
80-
return deleteChild(state, action.payload);
75+
case UPDATE_CHILDREN:
76+
return updateChildren(state, action.payload);
8177
case REASSIGN_PARENT:
8278
return reassignParent(state, action.payload);
8379
case SET_SELECTABLE_PARENTS:

0 commit comments

Comments
 (0)