Skip to content

Commit

Permalink
Merge pull request #88 from React-Proto/feat/multiple_parents
Browse files Browse the repository at this point in the history
Allow component to have multiple parents.
  • Loading branch information
frankyma committed Jan 10, 2019
2 parents 54d94f2 + 827f576 commit e5e988c
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 127 deletions.
3 changes: 1 addition & 2 deletions src/actionTypes/index.js
Expand Up @@ -2,8 +2,7 @@ export const LOAD_INIT_DATA = 'LOAD_INIT_DATA';
export const ADD_COMPONENT = 'ADD_COMPONENT';
export const UPDATE_COMPONENT = 'UPDATE_COMPONENT';
export const DELETE_COMPONENT = 'DELETE_COMPONENT';
export const ADD_NEW_CHILD = 'ADD_NEW_CHILD';
export const DELETE_CHILD = 'DELETE_CHILD';
export const UPDATE_CHILDREN = 'UPDATE_CHILDREN';
export const REASSIGN_PARENT = 'REASSIGN_PARENT';
export const SET_SELECTABLE_PARENTS = 'SET_SELECTABLE_PARENTS';
export const EXPORT_FILES = 'EXPORT_FILES';
Expand Down
45 changes: 16 additions & 29 deletions src/actions/components.js
Expand Up @@ -3,8 +3,7 @@ import {
ADD_COMPONENT,
UPDATE_COMPONENT,
DELETE_COMPONENT,
ADD_NEW_CHILD,
DELETE_CHILD,
UPDATE_CHILDREN,
REASSIGN_PARENT,
SET_SELECTABLE_PARENTS,
EXPORT_FILES,
Expand Down Expand Up @@ -40,30 +39,21 @@ export const loadInitData = () => (dispatch) => {
}));
};

export const addNewChild = (({
id, childIndex, childId,
export const updateChildren = (({
parentIds, childIndex, childId,
}) => ({
type: ADD_NEW_CHILD,
type: UPDATE_CHILDREN,
payload: {
id, childIndex, childId,
parentIds, childIndex, childId,
},
}));

export const deleteChild = (({
parent, childIndex, childId,
}) => ({
type: DELETE_CHILD,
payload: {
parent, childIndex, childId,
},
}));

export const parentReassignment = (({ index, id, parent }) => ({
export const parentReassignment = (({ index, id, parentIds }) => ({
type: REASSIGN_PARENT,
payload: {
index,
id,
parent,
parentIds,
},
}));

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

export const deleteComponent = ({ index, id, parent }) => (dispatch) => {
// Delete Component from its parent if it has a parent.
if (parent && parent.id) {
dispatch(deleteChild({ parent, childId: id, childIndex: index }));
export const deleteComponent = ({ index, id, parentIds = [] }) => (dispatch) => {
if (parentIds.length) {
// Delete Component from its parent if it has a parent.
dispatch(updateChildren({ parentIds, childId: id, childIndex: index }));
}
// Reassign Component's children to its parent if it has one or make them orphans
dispatch(parentReassignment({ index, id, parent }));
dispatch(parentReassignment({ index, id, parentIds }));

dispatch({ type: DELETE_COMPONENT, payload: { index, id } });
dispatch({ type: SET_SELECTABLE_PARENTS });
};

export const updateComponent = ({
id, index, parent = null, newParentId = null, color = null, stateful = null,
id, index, newParentId = null, color = null, stateful = null,
}) => (dispatch) => {
dispatch({
type: UPDATE_COMPONENT,
Expand All @@ -93,12 +84,8 @@ export const updateComponent = ({
},
});

if (newParentId && newParentId !== 'null') {
dispatch(addNewChild({ id: newParentId, childId: id, childIndex: index }));
}

if (parent && parent.id) {
dispatch(deleteChild({ parent, index, childId: id }));
if (newParentId) {
dispatch(updateChildren({ parentIds: [newParentId], childId: id, childIndex: index }));
}

dispatch({ type: SET_SELECTABLE_PARENTS });
Expand Down
88 changes: 60 additions & 28 deletions src/components/LeftColExpansionPanel.jsx
Expand Up @@ -7,8 +7,12 @@ import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import ExpansionPanelActions from '@material-ui/core/ExpansionPanelActions';
import Typography from '@material-ui/core/Typography';
import Input from '@material-ui/core/Input';
import MenuItem from '@material-ui/core/MenuItem';
import RemoveCircleOutlineIcon from '@material-ui/icons/RemoveCircleOutline';
import ListItemText from '@material-ui/core/ListItemText';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Switch from '@material-ui/core/Switch';
import Chip from '@material-ui/core/Chip';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import FlipToBackIcon from '@material-ui/icons/FlipToBack';
Expand All @@ -28,6 +32,13 @@ const styles = theme => ({
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: theme.spacing.unit / 4,
},
panel: {
backgroundColor: '#333333',
},
Expand Down Expand Up @@ -60,6 +71,15 @@ const styles = theme => ({
group: {
margin: `${theme.spacing.unit}px 0`,
},
icon: {
fontSize: '20px',
color: '#000',
transition: 'all .2s ease',

'&:hover': {
color: 'red',
},
},
});

const LeftColExpansionPanel = (props) => {
Expand All @@ -79,23 +99,24 @@ const LeftColExpansionPanel = (props) => {
id,
stateful,
color,
parent,
parents,
parentIds,
selectableParents,
} = component;

const parentOptions = [
<option value='null' key=''>
None
</option>,
...selectableParents.map(
selectableParent => <option
value={selectableParent.id}
key={selectableParent.id}
>
{selectableParent.title}
</option>,
),
];
const handleParentChange = (event, parentId = null) => {
let newParentId = parentId;
if (event) {
const selectedParents = event.target.value;
newParentId = selectedParents[selectedParents.length - 1].id;
}

return updateComponent({
index,
id,
newParentId,
});
};

return (
<div className={classes.root}>
Expand Down Expand Up @@ -130,24 +151,35 @@ const LeftColExpansionPanel = (props) => {
/>
</div>
<div className={classes.column}>
<InputLabel className={classes.label} htmlFor='parentSelect'>Parent</InputLabel>
<InputLabel className={classes.label} htmlFor='parentSelect'>selectedParents</InputLabel>
<Select
className={classes.light}
native
value={parent.id}
multiple
value={parents}
id='parentSelect'
name='parentName'
onChange={(event) => {
const newParentId = event.target.value;
updateComponent({
newParentId,
index,
id,
parent,
});
}}
disabled={selectableParents.length < 1}
onChange={handleParentChange}
input={<Input id='parentSelect' />}
renderValue={selectedP => (
<div className={classes.chips}>
{selectedP.map(parent => (
<Chip
key={parent.id}
label={parent.title}
className={classes.chip}
onDelete={() => handleParentChange(null, parent.id)}
deleteIcon={<RemoveCircleOutlineIcon className={classes.icon} />}
/>
))}
</div>
)}
>
{parentOptions}
{selectableParents.map(parentObj => (
<MenuItem key={parentObj.id} value={parentObj}>
<ListItemText primary={parentObj.title} />
</MenuItem>
))}
</Select>
</div>
</ExpansionPanelDetails>
Expand All @@ -173,7 +205,7 @@ const LeftColExpansionPanel = (props) => {
className={classes.button}
onClick={() => {
deleteComponent({
index, id, parent,
index, id, parentIds,
});
}}
aria-label='Delete'>
Expand Down
7 changes: 4 additions & 3 deletions src/components/SortableComponent.jsx
Expand Up @@ -5,15 +5,16 @@ import 'react-sortable-tree/style.css';

const SortableComponent = (props) => {
const rootComponents = props.components.filter(
comp => comp.parentId.length === 0,
comp => comp.parentIds.length === 0,
).reverse();

return (
<div className="sortable-tree">
<SortableTree
style={{ backgroundColor: 'rgb(37, 37, 38)' }}
treeData={rootComponents}
canDrag={false}
onChange={treeData => this.setState({ treeData })}
onChange={() => {}}
/>
</div>
);
Expand All @@ -22,5 +23,5 @@ const SortableComponent = (props) => {
export default SortableComponent;

SortableComponent.propTypes = {
components: PropTypes.array,
components: PropTypes.array.isRequired,
};
4 changes: 2 additions & 2 deletions src/containers/AppContainer.jsx
Expand Up @@ -6,7 +6,7 @@ import LinearProgress from '@material-ui/core/LinearProgress';
import LeftContainer from './LeftContainer.jsx';
import MainContainer from './MainContainer.jsx';
import RightContainer from './RightContainer.jsx';
import convertIdToObjs from '../utils/convertIdsToObjs.util';
import convertIdsToObjs from '../utils/convertIdsToObjs.util';
import theme from '../components/theme';
import { loadInitData } from '../actions/components';

Expand Down Expand Up @@ -51,7 +51,7 @@ class AppContainer extends Component {
loading,
} = this.props;
const { width, rightColumnOpen } = this.state;
const updatedComponents = convertIdToObjs(components);
const updatedComponents = convertIdsToObjs(components);

return (
<MuiThemeProvider theme={theme}>
Expand Down
10 changes: 5 additions & 5 deletions src/containers/LeftContainer.jsx
Expand Up @@ -16,20 +16,20 @@ const mapDispatchToProps = dispatch => ({
addComponent: ({ title }) => dispatch(actions.addComponent({ title })),
updateComponent:
({
id, index, parent = null, newParentId = null, color = null, stateful = null,
id, index, newParentId = null, color = null, stateful = null,
}) => dispatch(actions.updateComponent({
id, index, parent, newParentId, color, stateful,
id, index, newParentId, color, stateful,
})),
deleteComponent: ({
index, id, parent,
}) => dispatch(actions.deleteComponent({ index, id, parent })),
index, id, parentIds,
}) => dispatch(actions.deleteComponent({ index, id, parentIds })),
moveToBottom: componentId => dispatch(actions.moveToBottom(componentId)),
moveToTop: componentId => dispatch(actions.moveToTop(componentId)),
openExpansionPanel: component => dispatch(actions.openExpansionPanel(component)),
deleteAllData: () => dispatch(actions.deleteAllData()),
});

const styles = theme => ({
const styles = () => ({
cssLabel: {
color: 'white',

Expand Down
5 changes: 4 additions & 1 deletion src/containers/MainContainer.jsx
Expand Up @@ -221,10 +221,13 @@ class MainContainer extends Component {
showGenerateAppModal,
setImage,
} = this;
const cursor = this.state.draggable ? 'move' : 'default';

return (
<MuiThemeProvider theme={theme}>
<div className="main-container">
<div
className="main-container"
style={{ cursor }}>
<MainContainerHeader
image={image}
increaseHeight={increaseHeight}
Expand Down
1 change: 1 addition & 0 deletions src/containers/RightContainer.jsx
Expand Up @@ -77,6 +77,7 @@ RightContainer.propTypes = {
deleteProp: PropTypes.func.isRequired,
addProp: PropTypes.func.isRequired,
width: PropTypes.number.isRequired,
rightColumnOpen: PropTypes.bool.isRequired,
};


Expand Down
5 changes: 2 additions & 3 deletions src/localStorage.js
@@ -1,5 +1,4 @@
import localforage from 'localforage';

export const saveState = state => localforage.setItem('state', state);

export const loadState = () => localforage.getItem('state');
export const saveState = state => localforage.setItem('state-v1.0.1', state);
export const loadState = () => localforage.getItem('state-v1.0.1');
12 changes: 4 additions & 8 deletions src/reducers/componentReducer.js
Expand Up @@ -3,8 +3,7 @@ import {
ADD_COMPONENT,
UPDATE_COMPONENT,
DELETE_COMPONENT,
ADD_NEW_CHILD,
DELETE_CHILD,
UPDATE_CHILDREN,
REASSIGN_PARENT,
SET_SELECTABLE_PARENTS,
EXPORT_FILES,
Expand All @@ -28,8 +27,7 @@ import {
addComponent,
updateComponent,
deleteComponent,
addChild,
deleteChild,
updateChildren,
reassignParent,
setSelectableP,
exportFilesSuccess,
Expand Down Expand Up @@ -74,10 +72,8 @@ const componentReducer = (state = initialApplicationState, action) => {
return updateComponent(state, action.payload);
case DELETE_COMPONENT:
return deleteComponent(state, action.payload);
case ADD_NEW_CHILD:
return addChild(state, action.payload);
case DELETE_CHILD:
return deleteChild(state, action.payload);
case UPDATE_CHILDREN:
return updateChildren(state, action.payload);
case REASSIGN_PARENT:
return reassignParent(state, action.payload);
case SET_SELECTABLE_PARENTS:
Expand Down

0 comments on commit e5e988c

Please sign in to comment.