senangwebs-aframe-editor 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +3 -0
- package/.editorconfig +12 -0
- package/.eslintignore +2 -0
- package/.eslintrc +40 -0
- package/.github/workflows/ci.yml +39 -0
- package/.husky/pre-commit +4 -0
- package/.prettierignore +1 -0
- package/.prettierrc.json +5 -0
- package/.stylelintrc +12 -0
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/assets/gltf.svg +49 -0
- package/dist/aframe-inspector.js +106250 -0
- package/dist/aframe-inspector.js.map +1 -0
- package/dist/aframe-inspector.min.js +29040 -0
- package/dist/aframe-inspector.min.js.LICENSE.txt +56 -0
- package/dist/aframe-inspector.min.js.map +1 -0
- package/examples/360video.html +48 -0
- package/examples/colors.html +18 -0
- package/examples/controllers.html +60 -0
- package/examples/embedded-zoom.html +78 -0
- package/examples/embedded.html +79 -0
- package/examples/empty.html +13 -0
- package/examples/index-aframe.html +66 -0
- package/examples/index.html +71 -0
- package/examples/supercraft.html +6 -0
- package/index.html +8 -0
- package/package.json +84 -0
- package/senangwebs-webverse-editor.png +0 -0
- package/src/components/AwesomeIcon.js +53 -0
- package/src/components/Collapsible.js +57 -0
- package/src/components/EntityRepresentation.js +83 -0
- package/src/components/Main.js +222 -0
- package/src/components/__tests__/Collapsible.test.js +30 -0
- package/src/components/components/AddComponent.js +104 -0
- package/src/components/components/CommonComponents.js +160 -0
- package/src/components/components/Component.js +151 -0
- package/src/components/components/ComponentsContainer.js +52 -0
- package/src/components/components/DefaultComponents.js +1 -0
- package/src/components/components/Mixins.js +83 -0
- package/src/components/components/PropertyRow.js +145 -0
- package/src/components/components/Sidebar.js +51 -0
- package/src/components/icons/BackViewIcon.js +27 -0
- package/src/components/icons/BottomViewIcon.js +26 -0
- package/src/components/icons/FrontViewIcon.js +23 -0
- package/src/components/icons/LeftViewIcon.js +24 -0
- package/src/components/icons/PerspectiveIcon.js +23 -0
- package/src/components/icons/PrimitiveBoxIcon.js +143 -0
- package/src/components/icons/PrimitiveConeIcon.js +44 -0
- package/src/components/icons/PrimitiveCylinderIcon.js +51 -0
- package/src/components/icons/PrimitiveEmptyEntityIcon.js +78 -0
- package/src/components/icons/PrimitiveImageIcon.js +86 -0
- package/src/components/icons/PrimitiveLightIcon.js +107 -0
- package/src/components/icons/PrimitivePlaneIcon.js +87 -0
- package/src/components/icons/PrimitiveSphereIcon.js +39 -0
- package/src/components/icons/PrimitiveTextIcon.js +89 -0
- package/src/components/icons/PrimitiveTorusIcon.js +31 -0
- package/src/components/icons/RightViewIcon.js +24 -0
- package/src/components/icons/TopViewIcon.js +24 -0
- package/src/components/modals/Modal.js +107 -0
- package/src/components/modals/ModalHelp.js +97 -0
- package/src/components/modals/ModalPrimitive.js +114 -0
- package/src/components/modals/ModalTextures.js +430 -0
- package/src/components/scenegraph/Entity.js +142 -0
- package/src/components/scenegraph/SceneGraph.js +337 -0
- package/src/components/scenegraph/Toolbar.js +147 -0
- package/src/components/viewport/CameraToolbar.js +122 -0
- package/src/components/viewport/TransformToolbar.js +102 -0
- package/src/components/viewport/ViewportHUD.js +33 -0
- package/src/components/widgets/BooleanWidget.js +49 -0
- package/src/components/widgets/ColorWidget.js +89 -0
- package/src/components/widgets/InputWidget.js +42 -0
- package/src/components/widgets/NumberWidget.js +179 -0
- package/src/components/widgets/SelectWidget.js +58 -0
- package/src/components/widgets/TextureWidget.js +252 -0
- package/src/components/widgets/Vec2Widget.js +55 -0
- package/src/components/widgets/Vec3Widget.js +58 -0
- package/src/components/widgets/Vec4Widget.js +61 -0
- package/src/components/widgets/index.js +9 -0
- package/src/index.js +301 -0
- package/src/lib/EditorControls.js +336 -0
- package/src/lib/Events.js +6 -0
- package/src/lib/TransformControls.js +1365 -0
- package/src/lib/assetsLoader.js +43 -0
- package/src/lib/assetsUtils.js +30 -0
- package/src/lib/cameras.js +121 -0
- package/src/lib/entity.js +556 -0
- package/src/lib/history.js +30 -0
- package/src/lib/raycaster.js +129 -0
- package/src/lib/shortcuts.js +211 -0
- package/src/lib/utils.js +118 -0
- package/src/lib/viewport.js +268 -0
- package/src/style/components.styl +275 -0
- package/src/style/entity.styl +22 -0
- package/src/style/help.styl +40 -0
- package/src/style/index.styl +358 -0
- package/src/style/lib.styl +41 -0
- package/src/style/primitiveModal.styl +90 -0
- package/src/style/scenegraph.styl +173 -0
- package/src/style/select.styl +71 -0
- package/src/style/textureModal.styl +220 -0
- package/src/style/viewport.styl +168 -0
- package/src/style/widgets.styl +71 -0
- package/webpack.config.js +65 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
faCamera,
|
|
5
|
+
faCube,
|
|
6
|
+
faFont,
|
|
7
|
+
faLightbulb
|
|
8
|
+
} from '@fortawesome/free-solid-svg-icons';
|
|
9
|
+
import { AwesomeIcon } from './AwesomeIcon';
|
|
10
|
+
|
|
11
|
+
const ICONS = {
|
|
12
|
+
camera: (
|
|
13
|
+
<i title="camera">
|
|
14
|
+
<AwesomeIcon icon={faCamera} />
|
|
15
|
+
</i>
|
|
16
|
+
),
|
|
17
|
+
mesh: (
|
|
18
|
+
<i title="mesh">
|
|
19
|
+
<AwesomeIcon icon={faCube} />
|
|
20
|
+
</i>
|
|
21
|
+
),
|
|
22
|
+
light: (
|
|
23
|
+
<i title="light">
|
|
24
|
+
<AwesomeIcon icon={faLightbulb} />
|
|
25
|
+
</i>
|
|
26
|
+
),
|
|
27
|
+
text: (
|
|
28
|
+
<i title="text">
|
|
29
|
+
<AwesomeIcon icon={faFont} />
|
|
30
|
+
</i>
|
|
31
|
+
)
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default class EntityRepresentation extends React.Component {
|
|
35
|
+
static propTypes = {
|
|
36
|
+
entity: PropTypes.object,
|
|
37
|
+
onDoubleClick: PropTypes.func
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
render() {
|
|
41
|
+
const entity = this.props.entity;
|
|
42
|
+
const onDoubleClick = this.props.onDoubleClick;
|
|
43
|
+
|
|
44
|
+
if (!entity) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Icons.
|
|
49
|
+
const icons = [];
|
|
50
|
+
for (let objType in ICONS) {
|
|
51
|
+
if (!entity.getObject3D(objType)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
icons.push(<span key={objType}> {ICONS[objType]}</span>);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Name.
|
|
58
|
+
let entityName = entity.id;
|
|
59
|
+
let type = 'id';
|
|
60
|
+
if (!entity.isScene && !entityName && entity.getAttribute('class')) {
|
|
61
|
+
entityName = entity.getAttribute('class').split(' ')[0];
|
|
62
|
+
type = 'class';
|
|
63
|
+
} else if (!entity.isScene && !entityName && entity.getAttribute('mixin')) {
|
|
64
|
+
entityName = entity.getAttribute('mixin').split(' ')[0];
|
|
65
|
+
type = 'mixin';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<span className="entityPrint" onDoubleClick={onDoubleClick}>
|
|
70
|
+
<span className="entityTagName">
|
|
71
|
+
{'<' + entity.tagName.toLowerCase()}
|
|
72
|
+
</span>
|
|
73
|
+
{entityName && (
|
|
74
|
+
<span className="entityName" data-entity-name-type={type}>
|
|
75
|
+
{entityName}
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
78
|
+
{icons.length > 0 && <span className="entityIcons">{icons}</span>}
|
|
79
|
+
<span className="entityCloseTag">{'>'}</span>
|
|
80
|
+
</span>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
faPlus,
|
|
4
|
+
faCog,
|
|
5
|
+
faCode,
|
|
6
|
+
faBars,
|
|
7
|
+
faTimes,
|
|
8
|
+
faEye,
|
|
9
|
+
faCube,
|
|
10
|
+
faCubes,
|
|
11
|
+
faFileUpload,
|
|
12
|
+
faFolderOpen,
|
|
13
|
+
faVrCardboard
|
|
14
|
+
} from '@fortawesome/free-solid-svg-icons';
|
|
15
|
+
import { AwesomeIcon } from './AwesomeIcon';
|
|
16
|
+
import Events from '../lib/Events';
|
|
17
|
+
import ComponentsSidebar from './components/Sidebar';
|
|
18
|
+
import ModalTextures from './modals/ModalTextures';
|
|
19
|
+
import ModalHelp from './modals/ModalHelp';
|
|
20
|
+
import SceneGraph from './scenegraph/SceneGraph';
|
|
21
|
+
import CameraToolbar from './viewport/CameraToolbar';
|
|
22
|
+
import TransformToolbar from './viewport/TransformToolbar';
|
|
23
|
+
import ViewportHUD from './viewport/ViewportHUD';
|
|
24
|
+
|
|
25
|
+
THREE.ImageUtils.crossOrigin = '';
|
|
26
|
+
|
|
27
|
+
export default class Main extends React.Component {
|
|
28
|
+
constructor(props) {
|
|
29
|
+
super(props);
|
|
30
|
+
|
|
31
|
+
this.state = {
|
|
32
|
+
entity: null,
|
|
33
|
+
inspectorEnabled: true,
|
|
34
|
+
isModalTexturesOpen: false,
|
|
35
|
+
sceneEl: AFRAME.scenes[0],
|
|
36
|
+
visible: {
|
|
37
|
+
scenegraph: true,
|
|
38
|
+
attributes: false // Set this to false
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
Events.on('togglesidebar', (event) => {
|
|
43
|
+
if (event.which === 'all') {
|
|
44
|
+
if (this.state.visible.scenegraph || this.state.visible.attributes) {
|
|
45
|
+
this.setState({
|
|
46
|
+
visible: {
|
|
47
|
+
scenegraph: false,
|
|
48
|
+
attributes: false
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
this.setState({
|
|
53
|
+
visible: {
|
|
54
|
+
scenegraph: true,
|
|
55
|
+
attributes: true
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
} else if (event.which === 'attributes') {
|
|
60
|
+
this.setState((prevState) => ({
|
|
61
|
+
visible: {
|
|
62
|
+
...prevState.visible,
|
|
63
|
+
attributes: !prevState.visible.attributes
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
} else if (event.which === 'scenegraph') {
|
|
67
|
+
this.setState((prevState) => ({
|
|
68
|
+
visible: {
|
|
69
|
+
...prevState.visible,
|
|
70
|
+
scenegraph: !prevState.visible.scenegraph
|
|
71
|
+
}
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
componentDidMount() {
|
|
78
|
+
Events.on(
|
|
79
|
+
'opentexturesmodal',
|
|
80
|
+
function (selectedTexture, textureOnClose) {
|
|
81
|
+
this.setState({
|
|
82
|
+
selectedTexture: selectedTexture,
|
|
83
|
+
isModalTexturesOpen: true,
|
|
84
|
+
textureOnClose: textureOnClose
|
|
85
|
+
});
|
|
86
|
+
}.bind(this)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
Events.on('entityselect', (entity) => {
|
|
90
|
+
// Set the selected entity and ensure the attributes sidebar is visible
|
|
91
|
+
this.setState({
|
|
92
|
+
entity: entity,
|
|
93
|
+
visible: { ...this.state.visible, attributes: true }
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
Events.on('inspectortoggle', (enabled) => {
|
|
98
|
+
this.setState({ inspectorEnabled: enabled });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
Events.on('openhelpmodal', () => {
|
|
102
|
+
this.setState({ isHelpOpen: true });
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
handleClose = () => {
|
|
107
|
+
Events.emit('togglesidebar', { which: 'attributes' });
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
onCloseHelpModal = (value) => {
|
|
111
|
+
this.setState({ isHelpOpen: false });
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
onModalTextureOnClose = (value) => {
|
|
115
|
+
this.setState({ isModalTexturesOpen: false });
|
|
116
|
+
if (this.state.textureOnClose) {
|
|
117
|
+
this.state.textureOnClose(value);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
toggleEdit = () => {
|
|
122
|
+
if (this.state.inspectorEnabled) {
|
|
123
|
+
AFRAME.INSPECTOR.close();
|
|
124
|
+
} else {
|
|
125
|
+
AFRAME.INSPECTOR.open();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
renderSceneGraphToggle() {
|
|
130
|
+
if (!this.state.inspectorEnabled || this.state.visible.scenegraph) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return (
|
|
134
|
+
<div className="toggle-sidebar left">
|
|
135
|
+
<a
|
|
136
|
+
onClick={() => {
|
|
137
|
+
Events.emit('togglesidebar', { which: 'scenegraph' });
|
|
138
|
+
}}
|
|
139
|
+
title="Show scenegraph"
|
|
140
|
+
>
|
|
141
|
+
<AwesomeIcon icon={faPlus} />
|
|
142
|
+
</a>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
render() {
|
|
148
|
+
const scene = this.state.sceneEl;
|
|
149
|
+
// Simplified toggle button text logic
|
|
150
|
+
const buttonText = this.state.inspectorEnabled ? 'Show' : 'Edit';
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<div>
|
|
154
|
+
<a
|
|
155
|
+
id="previewSceneBtn"
|
|
156
|
+
className="toggle-edit"
|
|
157
|
+
onClick={this.toggleEdit}
|
|
158
|
+
>
|
|
159
|
+
<AwesomeIcon icon={faVrCardboard} />
|
|
160
|
+
</a>
|
|
161
|
+
|
|
162
|
+
{this.renderSceneGraphToggle()}
|
|
163
|
+
|
|
164
|
+
<div
|
|
165
|
+
id="inspectorContainer"
|
|
166
|
+
className={this.state.inspectorEnabled ? '' : 'hidden'}
|
|
167
|
+
>
|
|
168
|
+
<div id="leftPanel">
|
|
169
|
+
<div className="scenegraph-menubar">
|
|
170
|
+
<div id="scenegraph-menu-back"></div>
|
|
171
|
+
<div id="scenegraph-panel-action"></div>
|
|
172
|
+
</div>
|
|
173
|
+
<SceneGraph
|
|
174
|
+
scene={scene}
|
|
175
|
+
selectedEntity={this.state.entity}
|
|
176
|
+
visible={this.state.visible.scenegraph}
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div id="viewportBar">
|
|
181
|
+
<CameraToolbar />
|
|
182
|
+
<TransformToolbar />
|
|
183
|
+
<div className="viewportHud-menubar">
|
|
184
|
+
<ViewportHUD />
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div
|
|
189
|
+
id="rightPanel"
|
|
190
|
+
className={this.state.visible.attributes ? '' : 'hidden'}
|
|
191
|
+
>
|
|
192
|
+
<div className="componentHeader title">
|
|
193
|
+
{/* <a>
|
|
194
|
+
<AwesomeIcon icon={faBars} />
|
|
195
|
+
</a> */}
|
|
196
|
+
<div id="entity-panel-action"></div>
|
|
197
|
+
<a onClick={this.handleClose}>
|
|
198
|
+
<AwesomeIcon icon={faTimes} />
|
|
199
|
+
</a>
|
|
200
|
+
</div>
|
|
201
|
+
<ComponentsSidebar
|
|
202
|
+
entity={this.state.entity}
|
|
203
|
+
visible={this.state.visible.attributes} // This prop might become redundant depending on CSS, but leave for now
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div id="actionBar"></div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<ModalHelp
|
|
211
|
+
isOpen={this.state.isHelpOpen}
|
|
212
|
+
onClose={this.onCloseHelpModal}
|
|
213
|
+
/>
|
|
214
|
+
<ModalTextures
|
|
215
|
+
isOpen={this.state.isModalTexturesOpen}
|
|
216
|
+
selectedTexture={this.state.selectedTexture}
|
|
217
|
+
onClose={this.onModalTextureOnClose}
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
|
|
4
|
+
import Collapsible from '../Collapsible.js';
|
|
5
|
+
|
|
6
|
+
describe('Collapsible', () => {
|
|
7
|
+
it('does not set class when not collapsed', () => {
|
|
8
|
+
const tree = renderer
|
|
9
|
+
.create(
|
|
10
|
+
<Collapsible collapsed={false}>
|
|
11
|
+
<div />
|
|
12
|
+
<div />
|
|
13
|
+
</Collapsible>
|
|
14
|
+
)
|
|
15
|
+
.toJSON();
|
|
16
|
+
expect(tree.children[1].props.className).not.toContain('hide');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('sets class when collapsed', () => {
|
|
20
|
+
const tree = renderer
|
|
21
|
+
.create(
|
|
22
|
+
<Collapsible collapsed={true}>
|
|
23
|
+
<div />
|
|
24
|
+
<div />
|
|
25
|
+
</Collapsible>
|
|
26
|
+
)
|
|
27
|
+
.toJSON();
|
|
28
|
+
expect(tree.children[1].props.className).toContain('hide');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import Events from '../../lib/Events';
|
|
4
|
+
import Select from 'react-select';
|
|
5
|
+
|
|
6
|
+
export default class AddComponent extends React.Component {
|
|
7
|
+
static propTypes = {
|
|
8
|
+
entity: PropTypes.object
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Add blank component.
|
|
13
|
+
* If component is instanced, generate an ID.
|
|
14
|
+
*/
|
|
15
|
+
addComponent = (value) => {
|
|
16
|
+
let componentName = value.value;
|
|
17
|
+
|
|
18
|
+
var entity = this.props.entity;
|
|
19
|
+
|
|
20
|
+
if (AFRAME.components[componentName].multiple) {
|
|
21
|
+
const id = prompt(
|
|
22
|
+
`Provide an ID for this component (e.g., 'foo' for ${componentName}__foo).`
|
|
23
|
+
);
|
|
24
|
+
componentName = id ? `${componentName}__${id}` : componentName;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
entity.setAttribute(componentName, '');
|
|
28
|
+
Events.emit('componentadd', { entity: entity, component: componentName });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Component dropdown options.
|
|
33
|
+
*/
|
|
34
|
+
getComponentsOptions() {
|
|
35
|
+
const usedComponents = Object.keys(this.props.entity.components);
|
|
36
|
+
var commonOptions = Object.keys(AFRAME.components)
|
|
37
|
+
.filter(function (componentName) {
|
|
38
|
+
return (
|
|
39
|
+
AFRAME.components[componentName].multiple ||
|
|
40
|
+
usedComponents.indexOf(componentName) === -1
|
|
41
|
+
);
|
|
42
|
+
})
|
|
43
|
+
.sort()
|
|
44
|
+
.map(function (value) {
|
|
45
|
+
return { value: value, label: value, origin: 'loaded' };
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.options = commonOptions;
|
|
49
|
+
this.options = this.options.sort(function (a, b) {
|
|
50
|
+
return a.label === b.label ? 0 : a.label < b.label ? -1 : 1;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
renderOption(option) {
|
|
55
|
+
var bullet = (
|
|
56
|
+
<span title="Component already loaded in the scene">●</span>
|
|
57
|
+
);
|
|
58
|
+
return (
|
|
59
|
+
<strong className="option">
|
|
60
|
+
{option.label} {option.origin === 'loaded' ? bullet : ''}
|
|
61
|
+
</strong>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
render() {
|
|
66
|
+
const entity = this.props.entity;
|
|
67
|
+
if (!entity) {
|
|
68
|
+
return <div />;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.getComponentsOptions();
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div id="addComponentContainer">
|
|
75
|
+
<p id="addComponentHeader">COMPONENTS</p>
|
|
76
|
+
<Select
|
|
77
|
+
id="addComponent"
|
|
78
|
+
className="addComponent"
|
|
79
|
+
classNamePrefix="select"
|
|
80
|
+
options={this.options}
|
|
81
|
+
simpleValue
|
|
82
|
+
clearable={true}
|
|
83
|
+
placeholder="Add component..."
|
|
84
|
+
noResultsText="No components found"
|
|
85
|
+
onChange={this.addComponent}
|
|
86
|
+
optionRenderer={this.renderOption}
|
|
87
|
+
searchable={true}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* eslint-disable no-unused-vars */
|
|
95
|
+
/**
|
|
96
|
+
* Check if component has multiplicity.
|
|
97
|
+
*/
|
|
98
|
+
function isComponentInstanced(entity, componentName) {
|
|
99
|
+
for (var component in entity.components) {
|
|
100
|
+
if (component.substring(0, component.indexOf('__')) === componentName) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { faClipboard } from '@fortawesome/free-solid-svg-icons';
|
|
4
|
+
import { faTimes, faBars } from '@fortawesome/free-solid-svg-icons';
|
|
5
|
+
import { AwesomeIcon } from '../AwesomeIcon';
|
|
6
|
+
import { InputWidget } from '../widgets';
|
|
7
|
+
import DEFAULT_COMPONENTS from './DefaultComponents';
|
|
8
|
+
import PropertyRow from './PropertyRow';
|
|
9
|
+
import Collapsible from '../Collapsible';
|
|
10
|
+
import Mixins from './Mixins';
|
|
11
|
+
import {
|
|
12
|
+
updateEntity,
|
|
13
|
+
getEntityClipboardRepresentation
|
|
14
|
+
} from '../../lib/entity';
|
|
15
|
+
import EntityRepresentation from '../EntityRepresentation';
|
|
16
|
+
import Events from '../../lib/Events';
|
|
17
|
+
import copy from 'clipboard-copy';
|
|
18
|
+
import { saveBlob } from '../../lib/utils';
|
|
19
|
+
import GLTFIcon from '../../../assets/gltf.svg';
|
|
20
|
+
|
|
21
|
+
// @todo Take this out and use updateEntity?
|
|
22
|
+
function changeId(componentName, value) {
|
|
23
|
+
var entity = AFRAME.INSPECTOR.selectedEntity;
|
|
24
|
+
if (entity.id !== value) {
|
|
25
|
+
entity.id = value;
|
|
26
|
+
Events.emit('entityidchange', entity);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default class CommonComponents extends React.Component {
|
|
31
|
+
static propTypes = {
|
|
32
|
+
entity: PropTypes.object
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
componentDidMount() {
|
|
36
|
+
Events.on('entityupdate', (detail) => {
|
|
37
|
+
if (detail.entity !== this.props.entity) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
DEFAULT_COMPONENTS.indexOf(detail.component) !== -1 ||
|
|
42
|
+
detail.component === 'mixin'
|
|
43
|
+
) {
|
|
44
|
+
this.forceUpdate();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
renderCommonAttributes() {
|
|
50
|
+
const entity = this.props.entity;
|
|
51
|
+
return ['position', 'rotation', 'scale', 'visible'].map((componentName) => {
|
|
52
|
+
const schema = AFRAME.components[componentName].schema;
|
|
53
|
+
var data = entity.object3D[componentName];
|
|
54
|
+
if (componentName === 'rotation') {
|
|
55
|
+
data = {
|
|
56
|
+
x: THREE.MathUtils.radToDeg(entity.object3D.rotation.x),
|
|
57
|
+
y: THREE.MathUtils.radToDeg(entity.object3D.rotation.y),
|
|
58
|
+
z: THREE.MathUtils.radToDeg(entity.object3D.rotation.z)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return (
|
|
62
|
+
<PropertyRow
|
|
63
|
+
onChange={updateEntity}
|
|
64
|
+
key={componentName}
|
|
65
|
+
name={componentName}
|
|
66
|
+
showHelp={true}
|
|
67
|
+
schema={schema}
|
|
68
|
+
data={data}
|
|
69
|
+
isSingle={true}
|
|
70
|
+
componentname={componentName}
|
|
71
|
+
entity={entity}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exportToGLTF() {
|
|
78
|
+
const entity = this.props.entity;
|
|
79
|
+
AFRAME.INSPECTOR.exporters.gltf.parse(
|
|
80
|
+
entity.object3D,
|
|
81
|
+
function (buffer) {
|
|
82
|
+
const blob = new Blob([buffer], { type: 'application/octet-stream' });
|
|
83
|
+
saveBlob(blob, (entity.id || 'entity') + '.glb');
|
|
84
|
+
},
|
|
85
|
+
function (error) {
|
|
86
|
+
console.error(error);
|
|
87
|
+
},
|
|
88
|
+
{ binary: true }
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
render() {
|
|
93
|
+
const entity = this.props.entity;
|
|
94
|
+
if (!entity) {
|
|
95
|
+
return <div />;
|
|
96
|
+
}
|
|
97
|
+
const entityButtons = (
|
|
98
|
+
<div>
|
|
99
|
+
<a
|
|
100
|
+
title="Close components"
|
|
101
|
+
onClick={(event) => {
|
|
102
|
+
this.handleClose();
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<AwesomeIcon icon={faTimes} />
|
|
106
|
+
</a>
|
|
107
|
+
{/* <a
|
|
108
|
+
title="Export entity to GLTF"
|
|
109
|
+
className="gltfIcon"
|
|
110
|
+
onClick={(event) => {
|
|
111
|
+
this.exportToGLTF();
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
event.stopPropagation();
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
<img src={GLTFIcon} />
|
|
117
|
+
</a>
|
|
118
|
+
<a
|
|
119
|
+
title="Copy entity HTML to clipboard"
|
|
120
|
+
className="button"
|
|
121
|
+
onClick={(event) => {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
event.stopPropagation();
|
|
124
|
+
copy(getEntityClipboardRepresentation(this.props.entity));
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<AwesomeIcon icon={faClipboard} />
|
|
128
|
+
</a> */}
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<Collapsible id="componentEntityHeader" className="commonComponents">
|
|
134
|
+
<div className="collapsible-header sidebar-header">
|
|
135
|
+
<EntityRepresentation entity={entity} />
|
|
136
|
+
{/* {entityButtons} */}
|
|
137
|
+
</div>
|
|
138
|
+
<div className="collapsible-content">
|
|
139
|
+
<div className="propertyRow">
|
|
140
|
+
<label htmlFor="id" className="text">
|
|
141
|
+
ID
|
|
142
|
+
</label>
|
|
143
|
+
<InputWidget
|
|
144
|
+
onChange={changeId}
|
|
145
|
+
entity={entity}
|
|
146
|
+
name="id"
|
|
147
|
+
value={entity.id}
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
<div className="propertyRow">
|
|
151
|
+
<label className="text">class</label>
|
|
152
|
+
<span>{entity.getAttribute('class')}</span>
|
|
153
|
+
</div>
|
|
154
|
+
{this.renderCommonAttributes()}
|
|
155
|
+
<Mixins entity={entity} />
|
|
156
|
+
</div>
|
|
157
|
+
</Collapsible>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|