Skip to content

Commit 402c4d3

Browse files
feat(components): component selecting
1 parent 234b140 commit 402c4d3

File tree

17 files changed

+271
-11
lines changed

17 files changed

+271
-11
lines changed

src/actions/component.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @file component.js
3+
* Exports actions related to components.
4+
*/
5+
6+
import { put, takeLatest } from 'redux-saga/effects';
7+
8+
import { COMPONENT_SELECT } from '../constants';
9+
10+
/**
11+
* Sets the currently selected component.
12+
*
13+
* @param {object} payload - Payload for this saga action.
14+
* @param {string} payload.id - ID of the component that should be selected.
15+
*/
16+
export function* componentSelect({ id }) {
17+
yield put({
18+
type: `${COMPONENT_SELECT}_SUCCESS`,
19+
payload: {
20+
id
21+
}
22+
});
23+
}
24+
25+
export function* watchComponentActions() {
26+
yield takeLatest(COMPONENT_SELECT, componentSelect);
27+
}

src/actions/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import { all } from 'redux-saga/effects';
88
import { watchUserActions } from './user';
99
import { watchExperiencesActions } from './experiences';
1010
import { watchOpenExperienceActions } from './openExperience';
11+
import { watchComponentActions } from './component';
1112

1213
export default function* rootSaga() {
1314
yield all([
1415
watchUserActions(),
1516
watchExperiencesActions(),
16-
watchOpenExperienceActions()
17+
watchOpenExperienceActions(),
18+
watchComponentActions()
1719
]);
1820
}

src/aframe/components/isEditable.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @file isExitable.js
3+
* AFrame component that allows for components to be selected for editing.
4+
*/
5+
6+
/* globals AFRAME */
7+
8+
import { COMPONENT_SELECT, MODE_COMPONENT_SELECTING } from '../../constants';
9+
import connectRedux from '../utils/connectRedux';
10+
import connectRouter from '../utils/connectRouter';
11+
12+
const isEditable = {
13+
multiple: true,
14+
clickHandler() {
15+
const id = this.el.getAttribute('id');
16+
const { dispatch } = this.props;
17+
const {
18+
match: {
19+
params: { editorMode }
20+
}
21+
} = this.router;
22+
23+
if (id && editorMode === MODE_COMPONENT_SELECTING) {
24+
dispatch({
25+
type: COMPONENT_SELECT,
26+
id
27+
});
28+
}
29+
},
30+
init() {
31+
this.el.addEventListener('click', this.clickHandler.bind(this));
32+
},
33+
remove() {
34+
this.el.removeEventListener('click', this.clickHandler.bind(this));
35+
}
36+
};
37+
38+
AFRAME.registerComponent(
39+
'is-editable',
40+
connectRedux(state => ({
41+
experience: state.openExperience.item
42+
}))(
43+
connectRouter(
44+
isEditable,
45+
'/experience/vreditor/:experienceSlug/:sceneSlug/:editorMode?'
46+
)
47+
)
48+
);

src/aframe/components/spawnDialogs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const spawnDialogs = {
6565
const e = document.createElement('a-entity');
6666
e.setAttribute('id', component.id);
6767
e.setAttribute('look-at', '#camera');
68+
e.setAttribute('is-editable', true);
6869
e.setAttribute('position', { x, y, z });
6970
e.setAttribute('dialog-popup', {
7071
title,

src/aframe/entities/scene.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require('../components/spawnSky');
1414
require('../components/spawnLinks');
1515
require('../components/spawnDialogs');
1616
require('../components/navLink');
17+
require('../components/isEditable');
1718

1819
export default () => (
1920
<a-scene
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @file ComponentForm.container.js
3+
* Exports a redux-connected ComponentForm component.
4+
*/
5+
6+
import { connect } from 'react-redux';
7+
8+
import ComponentForm from './ComponentForm';
9+
10+
const mapDispatchToProps = dispatch => ({
11+
dispatch
12+
});
13+
14+
const mapState = ({ user, openExperience }) => ({
15+
user,
16+
experience: openExperience
17+
});
18+
19+
export default connect(mapState, mapDispatchToProps)(ComponentForm);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @file ComponentForm.js
3+
* Exports a component that allows users to operate on scene components.
4+
*/
5+
6+
import React from 'react';
7+
import PropTypes from 'prop-types';
8+
import { withFormik } from 'formik';
9+
import { withStyles } from '@material-ui/core';
10+
11+
import ComponentFormStyles from './ComponentForm.style';
12+
13+
const ComponentForm = ({ id }) => <form>{id}</form>;
14+
15+
ComponentForm.propTypes = {
16+
id: PropTypes.string.isRequired
17+
};
18+
19+
const FormikComponentForm = withFormik({
20+
displayName: 'ComponentForm',
21+
enableReinitialize: true
22+
})(ComponentForm);
23+
24+
export default withStyles(ComponentFormStyles)(FormikComponentForm);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @file ComponentForm.style.js
3+
* Exports ComponentForm component styles.
4+
*/
5+
6+
export default () => ({});

src/components/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import App from './App/App';
77
import LoginForm from './LoginForm/LoginForm.container';
88
import ExperienceForm from './ExperienceForm/ExperienceForm.container';
99
import SceneForm from './SceneForm/SceneForm.container';
10+
import ComponentForm from './ComponentForm/ComponentForm.container';
1011
import Loading from './Loading/Loading';
1112
import Message from './Message/Message';
1213
import SceneCards from './SceneCards/SceneCards.container';
@@ -20,5 +21,6 @@ export {
2021
Message,
2122
SceneCards,
2223
ToolsMenu,
23-
SceneForm
24+
SceneForm,
25+
ComponentForm
2426
};

src/constants/component.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @file component.js
3+
* Exports component-related constants.
4+
*/
5+
6+
export const COMPONENT_SELECT = 'COMPONENT_SELECT';

src/constants/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export * from './modes';
1010
export * from './form';
1111
export * from './experiences';
1212
export * from './openExperience';
13+
export * from './component';

src/pages/VREditor/VREditor.container.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ const mapDispatchToProps = dispatch => ({
1212
dispatch
1313
});
1414

15-
const mapState = ({ openExperience, user }) => ({
15+
const mapState = ({ openExperience, user, component }) => ({
1616
experience: openExperience,
17+
component,
1718
user
1819
});
1920

src/pages/VREditor/VREditor.js

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,26 @@
33
* Exports a React component that render's EditVR's VREditor interface.
44
*/
55

6-
import React, { Component } from 'react';
6+
import React, { Component, Fragment } from 'react';
77
import PropTypes from 'prop-types';
88
import { Redirect, Link } from 'react-router-dom';
99
import { withStyles, Grid, Typography, Button } from '@material-ui/core';
10-
import { AddBox } from '@material-ui/icons';
10+
import { AddBox, OpenWith, TouchApp } from '@material-ui/icons';
1111

12-
import { SceneCards, SceneForm, ToolsMenu } from '../../components';
12+
import {
13+
SceneCards,
14+
SceneForm,
15+
ToolsMenu,
16+
ComponentForm
17+
} from '../../components';
1318
import {
1419
OPEN_EXPERIENCE_FETCH_FOR_USER,
1520
MODE_SCENE_CREATE,
16-
MODE_SCENE_EDIT
21+
MODE_SCENE_EDIT,
22+
MODE_COMPONENT_SELECTING
1723
} from '../../constants';
1824
import parseSceneFromExperience from '../../lib/parseSceneFromExperience';
25+
import parseComponentFromScene from '../../lib/parseComponentFromScene';
1926
import Scene from '../../aframe/entities/scene';
2027
import { VREditorLayout } from '../../layouts';
2128
import VREditorStyles from './VREditor.style';
@@ -46,14 +53,20 @@ class VREditor extends Component {
4653
experienceSlug: PropTypes.string.isRequired,
4754
editorMode: PropTypes.string
4855
}).isRequired
49-
}).isRequired
56+
}).isRequired,
57+
component: PropTypes.shape({
58+
id: PropTypes.string
59+
})
5060
};
5161

5262
static defaultProps = {
5363
experience: {
5464
item: {
5565
title: false
5666
}
67+
},
68+
component: {
69+
id: null
5770
}
5871
};
5972

@@ -84,6 +97,7 @@ class VREditor extends Component {
8497
params: { sceneSlug, editorMode }
8598
},
8699
experience: { item: experience },
100+
component: { id: component },
87101
classes
88102
} = this.props;
89103

@@ -98,6 +112,17 @@ class VREditor extends Component {
98112
field_scenes: scenes
99113
} = experience;
100114

115+
let scene = null;
116+
if (sceneSlug) {
117+
scene = parseSceneFromExperience(experience, sceneSlug);
118+
}
119+
120+
// TODO: The logic for determining what to render in the main and right
121+
// columns should likely be extracted from this file and placed into another
122+
// component, just to make this component less cluttered. This isn't a
123+
// difficult thing to do, and this component isn't yet too messy, so for now
124+
// holding the logic in here will do.
125+
101126
// The main column defaults to showing instructions to create or select a scene
102127
let mainColumn = <Scene />;
103128

@@ -113,7 +138,6 @@ class VREditor extends Component {
113138

114139
// If the editor mode is scene edit, return the scene form.
115140
if (editorMode === MODE_SCENE_EDIT) {
116-
const scene = parseSceneFromExperience(experience, sceneSlug);
117141
if (scene) {
118142
mainColumn = (
119143
<div className={classes.mainColumn}>
@@ -136,6 +160,55 @@ class VREditor extends Component {
136160
);
137161
}
138162

163+
// Default right column to a preview message.
164+
let rightColumn = (
165+
<Fragment>
166+
<Typography variant="title" className={classes.columnRightTitle}>
167+
Previewing
168+
<OpenWith className={classes.columnRightIcon} />
169+
</Typography>
170+
<Typography>
171+
You are currently previewing your scene. You can use your mouse to
172+
grab the scene area, and drag it around to view different portions of
173+
your scene.
174+
</Typography>
175+
</Fragment>
176+
);
177+
178+
// If the current mode is selecting, but no component has been selected,
179+
// show selection documentation in the right column.
180+
if (editorMode === MODE_COMPONENT_SELECTING && !component) {
181+
rightColumn = (
182+
<Fragment>
183+
<Typography variant="title" className={classes.columnRightTitle}>
184+
Selecting
185+
<TouchApp className={classes.columnRightIcon} />
186+
</Typography>
187+
<Typography>
188+
You are currently in the selecting mode. You can use your mouse to
189+
select components. Upon being selected, a component will be opened
190+
in this pane and you can edit its properties.
191+
</Typography>
192+
</Fragment>
193+
);
194+
}
195+
196+
// If the current mode is selecting, and a component has been selected,
197+
// show the component editorial form.
198+
if (editorMode === MODE_COMPONENT_SELECTING && component) {
199+
const selected = parseComponentFromScene(scene, component);
200+
if (selected) {
201+
rightColumn = (
202+
<Fragment>
203+
<Typography variant="title" className={classes.columnRightTitle}>
204+
Edit {selected.title}
205+
</Typography>
206+
<ComponentForm id={component} />
207+
</Fragment>
208+
);
209+
}
210+
}
211+
139212
return (
140213
<VREditorLayout
141214
title={experience.title ? `Editing ${title} Experience` : 'Loading...'}
@@ -173,7 +246,7 @@ class VREditor extends Component {
173246
</Grid>
174247
</Grid>
175248
}
176-
rightAside="Right Sidebar"
249+
rightAside={<div className={classes.columnRight}>{rightColumn}</div>}
177250
>
178251
{mainColumn}
179252
</VREditorLayout>

src/pages/VREditor/VREditor.style.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ export default theme => ({
1515
padding: theme.spacing.unit * 2,
1616
userSelect: 'none'
1717
},
18+
columnRightTitle: {
19+
marginTop: theme.spacing.unit * 2,
20+
marginBottom: theme.spacing.unit * 2
21+
},
22+
columnRightIcon: {
23+
marginLeft: 6,
24+
marginBottom: -6
25+
},
1826
mainColumn: {
1927
padding: theme.spacing.unit * 4
2028
},
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`<VREditor /> Matches its snapshot 1`] = `"<div class=\\"MuiGrid-container-11 VREditorLayout-wrapper-6\\" id=\\"editor__wrapper\\" align=\\"stretch\\"><div></div><header class=\\"MuiPaper-root-110 MuiPaper-elevation4-116 MuiAppBar-root-102 MuiAppBar-positionFixed-103 MuiAppBar-colorPrimary-108 mui-fixed VREditorLayout-appBar-9\\"><div class=\\"MuiToolbar-root-137 MuiToolbar-gutters-138\\"><a tabindex=\\"0\\" class=\\"MuiButtonBase-root-145 MuiIconButton-root-139 MuiIconButton-colorInherit-140\\" role=\\"button\\" aria-label=\\"Back to Dashboard\\" href=\\"/dashboard\\"><span class=\\"MuiIconButton-label-144\\"><svg class=\\"MuiSvgIcon-root-148\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\"><g><path d=\\"M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z\\"></path></g></svg></span><span class=\\"MuiTouchRipple-root-154\\"></span></a><img src=\\"editvr-logo.svg\\" alt=\\"EditVR logo\\" class=\\"VREditorLayout-logo-10\\"><h2 class=\\"MuiTypography-root-161 MuiTypography-title-167\\">Editing test Experience</h2></div></header><div class=\\"MuiGrid-item-12 MuiGrid-grid-xs-3-40 VREditorLayout-aside-7\\" id=\\"VREditorLayout--left\\"><div class=\\"MuiGrid-container-11\\" align=\\"stretch\\"><div class=\\"MuiGrid-item-12 MuiGrid-grid-xs-3-40 VREditor-columnLeft-2\\" id=\\"column--left_aside\\"><h2 class=\\"MuiTypography-root-161 MuiTypography-title-167\\">Tools</h2><p class=\\"MuiTypography-root-161 MuiTypography-body1-170\\">You must create or select an existing scene before being able to use scene editing tools.</p></div><div class=\\"MuiGrid-item-12 MuiGrid-grid-xs-9-46 VREditor-columnRight-1\\" id=\\"column--left_aside\\"><h2 class=\\"MuiTypography-root-161 MuiTypography-title-167\\">Scenes</h2><div></div></div></div></div><div class=\\"MuiGrid-item-12 MuiGrid-grid-xs-6-43 VREditorLayout-middle-8\\" id=\\"VREditorLayout--middle\\"><div class=\\"VREditor-mainColumn-3\\"><h1 class=\\"MuiTypography-root-161 MuiTypography-headline-166\\">Select or create a scene to continue.</h1></div></div><div class=\\"MuiGrid-item-12 MuiGrid-grid-xs-3-40 VREditorLayout-aside-7\\" id=\\"VREditorLayout--right\\">Right Sidebar</div></div>"`;
3+
exports[`<VREditor /> Matches its snapshot 1`] = `"<div class=\\"MuiGrid-container-13 VREditorLayout-wrapper-8\\" id=\\"editor__wrapper\\" align=\\"stretch\\"><div></div><header class=\\"MuiPaper-root-112 MuiPaper-elevation4-118 MuiAppBar-root-104 MuiAppBar-positionFixed-105 MuiAppBar-colorPrimary-110 mui-fixed VREditorLayout-appBar-11\\"><div class=\\"MuiToolbar-root-139 MuiToolbar-gutters-140\\"><a tabindex=\\"0\\" class=\\"MuiButtonBase-root-147 MuiIconButton-root-141 MuiIconButton-colorInherit-142\\" role=\\"button\\" aria-label=\\"Back to Dashboard\\" href=\\"/dashboard\\"><span class=\\"MuiIconButton-label-146\\"><svg class=\\"MuiSvgIcon-root-150\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\"><g><path d=\\"M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z\\"></path></g></svg></span><span class=\\"MuiTouchRipple-root-156\\"></span></a><img src=\\"editvr-logo.svg\\" alt=\\"EditVR logo\\" class=\\"VREditorLayout-logo-12\\"><h2 class=\\"MuiTypography-root-163 MuiTypography-title-169\\">Editing test Experience</h2></div></header><div class=\\"MuiGrid-item-14 MuiGrid-grid-xs-3-42 VREditorLayout-aside-9\\" id=\\"VREditorLayout--left\\"><div class=\\"MuiGrid-container-13\\" align=\\"stretch\\"><div class=\\"MuiGrid-item-14 MuiGrid-grid-xs-3-42 VREditor-columnLeft-2\\" id=\\"column--left_aside\\"><h2 class=\\"MuiTypography-root-163 MuiTypography-title-169\\">Tools</h2><p class=\\"MuiTypography-root-163 MuiTypography-body1-172\\">You must create or select an existing scene before being able to use scene editing tools.</p></div><div class=\\"MuiGrid-item-14 MuiGrid-grid-xs-9-48 VREditor-columnRight-1\\" id=\\"column--left_aside\\"><h2 class=\\"MuiTypography-root-163 MuiTypography-title-169\\">Scenes</h2><div></div></div></div></div><div class=\\"MuiGrid-item-14 MuiGrid-grid-xs-6-45 VREditorLayout-middle-10\\" id=\\"VREditorLayout--middle\\"><div class=\\"VREditor-mainColumn-5\\"><h1 class=\\"MuiTypography-root-163 MuiTypography-headline-168\\">Select or create a scene to continue.</h1></div></div><div class=\\"MuiGrid-item-14 MuiGrid-grid-xs-3-42 VREditorLayout-aside-9\\" id=\\"VREditorLayout--right\\"><div class=\\"VREditor-columnRight-1\\"><h2 class=\\"MuiTypography-root-163 MuiTypography-title-169 VREditor-columnRightTitle-3\\">Previewing<svg class=\\"MuiSvgIcon-root-150 VREditor-columnRightIcon-4\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\"><g><path d=\\"M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z\\"></path></g></svg></h2><p class=\\"MuiTypography-root-163 MuiTypography-body1-172\\">You are currently previewing your scene. You can use your mouse to grab the scene area, and drag it around to view different portions of your scene.</p></div></div></div>"`;

0 commit comments

Comments
 (0)