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.
Files changed (104) hide show
  1. package/.babelrc +3 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintignore +2 -0
  4. package/.eslintrc +40 -0
  5. package/.github/workflows/ci.yml +39 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.prettierignore +1 -0
  8. package/.prettierrc.json +5 -0
  9. package/.stylelintrc +12 -0
  10. package/LICENSE +21 -0
  11. package/README.md +75 -0
  12. package/assets/gltf.svg +49 -0
  13. package/dist/aframe-inspector.js +106250 -0
  14. package/dist/aframe-inspector.js.map +1 -0
  15. package/dist/aframe-inspector.min.js +29040 -0
  16. package/dist/aframe-inspector.min.js.LICENSE.txt +56 -0
  17. package/dist/aframe-inspector.min.js.map +1 -0
  18. package/examples/360video.html +48 -0
  19. package/examples/colors.html +18 -0
  20. package/examples/controllers.html +60 -0
  21. package/examples/embedded-zoom.html +78 -0
  22. package/examples/embedded.html +79 -0
  23. package/examples/empty.html +13 -0
  24. package/examples/index-aframe.html +66 -0
  25. package/examples/index.html +71 -0
  26. package/examples/supercraft.html +6 -0
  27. package/index.html +8 -0
  28. package/package.json +84 -0
  29. package/senangwebs-webverse-editor.png +0 -0
  30. package/src/components/AwesomeIcon.js +53 -0
  31. package/src/components/Collapsible.js +57 -0
  32. package/src/components/EntityRepresentation.js +83 -0
  33. package/src/components/Main.js +222 -0
  34. package/src/components/__tests__/Collapsible.test.js +30 -0
  35. package/src/components/components/AddComponent.js +104 -0
  36. package/src/components/components/CommonComponents.js +160 -0
  37. package/src/components/components/Component.js +151 -0
  38. package/src/components/components/ComponentsContainer.js +52 -0
  39. package/src/components/components/DefaultComponents.js +1 -0
  40. package/src/components/components/Mixins.js +83 -0
  41. package/src/components/components/PropertyRow.js +145 -0
  42. package/src/components/components/Sidebar.js +51 -0
  43. package/src/components/icons/BackViewIcon.js +27 -0
  44. package/src/components/icons/BottomViewIcon.js +26 -0
  45. package/src/components/icons/FrontViewIcon.js +23 -0
  46. package/src/components/icons/LeftViewIcon.js +24 -0
  47. package/src/components/icons/PerspectiveIcon.js +23 -0
  48. package/src/components/icons/PrimitiveBoxIcon.js +143 -0
  49. package/src/components/icons/PrimitiveConeIcon.js +44 -0
  50. package/src/components/icons/PrimitiveCylinderIcon.js +51 -0
  51. package/src/components/icons/PrimitiveEmptyEntityIcon.js +78 -0
  52. package/src/components/icons/PrimitiveImageIcon.js +86 -0
  53. package/src/components/icons/PrimitiveLightIcon.js +107 -0
  54. package/src/components/icons/PrimitivePlaneIcon.js +87 -0
  55. package/src/components/icons/PrimitiveSphereIcon.js +39 -0
  56. package/src/components/icons/PrimitiveTextIcon.js +89 -0
  57. package/src/components/icons/PrimitiveTorusIcon.js +31 -0
  58. package/src/components/icons/RightViewIcon.js +24 -0
  59. package/src/components/icons/TopViewIcon.js +24 -0
  60. package/src/components/modals/Modal.js +107 -0
  61. package/src/components/modals/ModalHelp.js +97 -0
  62. package/src/components/modals/ModalPrimitive.js +114 -0
  63. package/src/components/modals/ModalTextures.js +430 -0
  64. package/src/components/scenegraph/Entity.js +142 -0
  65. package/src/components/scenegraph/SceneGraph.js +337 -0
  66. package/src/components/scenegraph/Toolbar.js +147 -0
  67. package/src/components/viewport/CameraToolbar.js +122 -0
  68. package/src/components/viewport/TransformToolbar.js +102 -0
  69. package/src/components/viewport/ViewportHUD.js +33 -0
  70. package/src/components/widgets/BooleanWidget.js +49 -0
  71. package/src/components/widgets/ColorWidget.js +89 -0
  72. package/src/components/widgets/InputWidget.js +42 -0
  73. package/src/components/widgets/NumberWidget.js +179 -0
  74. package/src/components/widgets/SelectWidget.js +58 -0
  75. package/src/components/widgets/TextureWidget.js +252 -0
  76. package/src/components/widgets/Vec2Widget.js +55 -0
  77. package/src/components/widgets/Vec3Widget.js +58 -0
  78. package/src/components/widgets/Vec4Widget.js +61 -0
  79. package/src/components/widgets/index.js +9 -0
  80. package/src/index.js +301 -0
  81. package/src/lib/EditorControls.js +336 -0
  82. package/src/lib/Events.js +6 -0
  83. package/src/lib/TransformControls.js +1365 -0
  84. package/src/lib/assetsLoader.js +43 -0
  85. package/src/lib/assetsUtils.js +30 -0
  86. package/src/lib/cameras.js +121 -0
  87. package/src/lib/entity.js +556 -0
  88. package/src/lib/history.js +30 -0
  89. package/src/lib/raycaster.js +129 -0
  90. package/src/lib/shortcuts.js +211 -0
  91. package/src/lib/utils.js +118 -0
  92. package/src/lib/viewport.js +268 -0
  93. package/src/style/components.styl +275 -0
  94. package/src/style/entity.styl +22 -0
  95. package/src/style/help.styl +40 -0
  96. package/src/style/index.styl +358 -0
  97. package/src/style/lib.styl +41 -0
  98. package/src/style/primitiveModal.styl +90 -0
  99. package/src/style/scenegraph.styl +173 -0
  100. package/src/style/select.styl +71 -0
  101. package/src/style/textureModal.styl +220 -0
  102. package/src/style/viewport.styl +168 -0
  103. package/src/style/widgets.styl +71 -0
  104. package/webpack.config.js +65 -0
@@ -0,0 +1,430 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { faSearch } from '@fortawesome/free-solid-svg-icons';
4
+ import { AwesomeIcon } from '../AwesomeIcon';
5
+ import Events from '../../lib/Events';
6
+ import Modal from './Modal';
7
+ import { insertNewAsset } from '../../lib/assetsUtils';
8
+
9
+ function getFilename(url, converted = false) {
10
+ var filename = url.split('/').pop();
11
+ if (converted) {
12
+ filename = getValidId(filename);
13
+ }
14
+ return filename;
15
+ }
16
+
17
+ function isValidId(id) {
18
+ // The correct re should include : and . but A-frame seems to fail while accessing them
19
+ var re = /^[A-Za-z]+[\w-]*$/;
20
+ return re.test(id);
21
+ }
22
+
23
+ function getValidId(name) {
24
+ // info.name.replace(/\.[^/.]+$/, '').replace(/\s+/g, '')
25
+ return name
26
+ .split('.')
27
+ .shift()
28
+ .replace(/\s/, '-')
29
+ .replace(/^\d+\s*/, '')
30
+ .replace(/[\W]/, '')
31
+ .toLowerCase();
32
+ }
33
+
34
+ export default class ModalTextures extends React.Component {
35
+ static propTypes = {
36
+ isOpen: PropTypes.bool,
37
+ onClose: PropTypes.func,
38
+ selectedTexture: PropTypes.string
39
+ };
40
+
41
+ constructor(props) {
42
+ super(props);
43
+ this.state = {
44
+ filterText: '',
45
+ isOpen: this.props.isOpen,
46
+ loadedTextures: [],
47
+ assetsImages: [],
48
+ registryImages: [],
49
+ addNewDialogOpened: false,
50
+ newUrl: '',
51
+ preview: {
52
+ width: 0,
53
+ height: 0,
54
+ src: '',
55
+ id: '',
56
+ name: '',
57
+ filename: '',
58
+ type: '',
59
+ value: '',
60
+ loaded: false
61
+ }
62
+ };
63
+ this.imageName = React.createRef();
64
+ this.preview = React.createRef();
65
+ this.registryGallery = React.createRef();
66
+ }
67
+
68
+ componentDidMount() {
69
+ Events.on('assetsimagesload', (images) => {
70
+ this.generateFromRegistry();
71
+ });
72
+
73
+ this.generateFromAssets();
74
+ }
75
+
76
+ componentDidUpdate(prevProps) {
77
+ if (this.state.isOpen && !AFRAME.INSPECTOR.assetsLoader.hasLoaded) {
78
+ AFRAME.INSPECTOR.assetsLoader.load();
79
+ }
80
+ if (this.state.isOpen && this.state.isOpen !== prevProps.isOpen) {
81
+ this.generateFromAssets();
82
+ }
83
+ }
84
+
85
+ static getDerivedStateFromProps(props, state) {
86
+ if (state.isOpen !== props.isOpen) {
87
+ return { isOpen: props.isOpen };
88
+ }
89
+ return null;
90
+ }
91
+
92
+ onClose = (value) => {
93
+ if (this.props.onClose) {
94
+ this.props.onClose();
95
+ }
96
+ };
97
+
98
+ selectTexture = (value) => {
99
+ if (this.props.onClose) {
100
+ this.props.onClose(value);
101
+ }
102
+ };
103
+
104
+ generateFromRegistry = () => {
105
+ var self = this;
106
+ AFRAME.INSPECTOR.assetsLoader.images.forEach((imageData) => {
107
+ var image = new Image();
108
+ image.addEventListener('load', () => {
109
+ self.state.registryImages.push({
110
+ id: imageData.id,
111
+ src: imageData.fullPath,
112
+ width: imageData.width,
113
+ height: imageData.height,
114
+ name: imageData.id,
115
+ type: 'registry',
116
+ tags: imageData.tags,
117
+ value: 'url(' + imageData.fullPath + ')'
118
+ });
119
+ self.setState({ registryImages: self.state.registryImages.slice() });
120
+ });
121
+ image.src = imageData.fullThumbPath;
122
+ });
123
+ };
124
+
125
+ generateFromAssets = () => {
126
+ this.setState({ assetsImages: [] });
127
+
128
+ var self = this;
129
+ Array.prototype.slice
130
+ .call(document.querySelectorAll('a-assets img'))
131
+ .forEach((asset) => {
132
+ var image = new Image();
133
+ image.addEventListener('load', () => {
134
+ self.state.assetsImages.push({
135
+ id: asset.id,
136
+ src: image.src,
137
+ width: image.width,
138
+ height: image.height,
139
+ name: asset.id,
140
+ type: 'asset',
141
+ value: '#' + asset.id
142
+ });
143
+ self.setState({ assetsImages: self.state.assetsImages });
144
+ });
145
+ image.src = asset.src;
146
+ });
147
+ };
148
+
149
+ onNewUrl = (event) => {
150
+ if (event.keyCode !== 13) {
151
+ return;
152
+ }
153
+
154
+ var self = this;
155
+ function onImageLoaded(img) {
156
+ var src = self.preview.current.src;
157
+ self.setState({
158
+ preview: {
159
+ width: self.preview.current.naturalWidth,
160
+ height: self.preview.current.naturalHeight,
161
+ src: src,
162
+ id: '',
163
+ name: getFilename(src, true),
164
+ filename: getFilename(src),
165
+ type: 'new',
166
+ loaded: true,
167
+ value: 'url(' + src + ')'
168
+ }
169
+ });
170
+ self.preview.current.removeEventListener('load', onImageLoaded);
171
+ }
172
+ this.preview.current.addEventListener('load', onImageLoaded);
173
+ this.preview.current.src = event.target.value;
174
+
175
+ this.imageName.current.focus();
176
+ };
177
+
178
+ onNameKeyUp = (event) => {
179
+ if (event.keyCode === 13 && this.isValidAsset()) {
180
+ this.addNewAsset();
181
+ }
182
+ };
183
+
184
+ onNameChanged = (event) => {
185
+ var state = this.state.preview;
186
+ state.name = event.target.value;
187
+ this.setState({ preview: state });
188
+ };
189
+
190
+ toggleNewDialog = () => {
191
+ this.setState({ addNewDialogOpened: !this.state.addNewDialogOpened });
192
+ };
193
+
194
+ clear() {
195
+ this.setState({
196
+ preview: {
197
+ width: 0,
198
+ height: 0,
199
+ src: '',
200
+ id: '',
201
+ filename: '',
202
+ name: '',
203
+ type: '',
204
+ loaded: false,
205
+ value: ''
206
+ },
207
+ newUrl: ''
208
+ });
209
+ }
210
+
211
+ onUrlChange = (e) => {
212
+ this.setState({ newUrl: e.target.value });
213
+ };
214
+
215
+ isValidAsset() {
216
+ let validUrl = isValidId(this.state.preview.name);
217
+ let validAsset = this.state.preview.loaded && validUrl;
218
+ return validAsset;
219
+ }
220
+
221
+ addNewAsset = () => {
222
+ var self = this;
223
+ insertNewAsset(
224
+ 'img',
225
+ this.state.preview.name,
226
+ this.state.preview.src,
227
+ true,
228
+ function () {
229
+ self.generateFromAssets();
230
+ self.setState({ addNewDialogOpened: false });
231
+ self.clear();
232
+ }
233
+ );
234
+ };
235
+
236
+ onChangeFilter = (e) => {
237
+ this.setState({ filterText: e.target.value });
238
+ };
239
+
240
+ renderRegistryImages() {
241
+ var self = this;
242
+ let selectSample = function (image) {
243
+ self.setState({
244
+ preview: {
245
+ width: image.width,
246
+ height: image.height,
247
+ src: image.src,
248
+ id: '',
249
+ name: getFilename(image.name, true),
250
+ filename: getFilename(image.src),
251
+ type: 'registry',
252
+ loaded: true,
253
+ value: 'url(' + image.src + ')'
254
+ }
255
+ });
256
+ self.imageName.current.focus();
257
+ };
258
+
259
+ var filterText = this.state.filterText.toUpperCase();
260
+
261
+ return this.state.registryImages
262
+ .filter((image) => {
263
+ return (
264
+ image.id.toUpperCase().indexOf(filterText) > -1 ||
265
+ image.name.toUpperCase().indexOf(filterText) > -1 ||
266
+ image.tags.indexOf(filterText) > -1
267
+ );
268
+ })
269
+ .map(function (image) {
270
+ let imageClick = selectSample.bind(this, image);
271
+ return (
272
+ <li key={image.src} onClick={imageClick}>
273
+ <img width="155px" height="155px" src={image.src} />
274
+ <div className="detail">
275
+ <span className="title">{image.name}</span>
276
+ <span>{getFilename(image.src)}</span>
277
+ <span>
278
+ {image.width} x {image.height}
279
+ </span>
280
+ </div>
281
+ </li>
282
+ );
283
+ });
284
+ }
285
+
286
+ render() {
287
+ let isOpen = this.state.isOpen;
288
+ let loadedTextures = this.state.loadedTextures;
289
+ let preview = this.state.preview;
290
+
291
+ let validUrl = isValidId(this.state.preview.name);
292
+ let validAsset = this.isValidAsset();
293
+
294
+ let addNewAssetButton = this.state.addNewDialogOpened
295
+ ? 'BACK'
296
+ : 'LOAD TEXTURE';
297
+
298
+ return (
299
+ <Modal
300
+ id="textureModal"
301
+ title="Textures"
302
+ isOpen={isOpen}
303
+ onClose={this.onClose}
304
+ closeOnClickOutside={false}
305
+ >
306
+ <button onClick={this.toggleNewDialog}>{addNewAssetButton}</button>
307
+ <div className={this.state.addNewDialogOpened ? '' : 'hide'}>
308
+ <div className="newimage">
309
+ <div className="new_asset_options">
310
+ <span>Load a new texture from one of these sources:</span>
311
+ <ul>
312
+ <li>
313
+ <span>From URL (and press Enter):</span>{' '}
314
+ <input
315
+ type="text"
316
+ className="imageUrl"
317
+ value={this.state.newUrl}
318
+ onChange={this.onUrlChange}
319
+ onKeyUp={this.onNewUrl}
320
+ />
321
+ </li>
322
+ <li>
323
+ <span>From assets registry: </span>
324
+ <div className="assets search">
325
+ <input
326
+ placeholder="Filter..."
327
+ value={this.state.filterText}
328
+ onChange={this.onChangeFilter}
329
+ />
330
+ <AwesomeIcon icon={faSearch} />
331
+ </div>
332
+ <ul ref={this.registryGallery} className="gallery">
333
+ {this.renderRegistryImages()}
334
+ </ul>
335
+ </li>
336
+ </ul>
337
+ </div>
338
+ <div className="preview">
339
+ Name:{' '}
340
+ <input
341
+ ref={this.imageName}
342
+ className={
343
+ this.state.preview.name.length > 0 && !validUrl ? 'error' : ''
344
+ }
345
+ type="text"
346
+ value={this.state.preview.name}
347
+ onChange={this.onNameChanged}
348
+ onKeyUp={this.onNameKeyUp}
349
+ />
350
+ <img
351
+ ref={this.preview}
352
+ width="155px"
353
+ height="155px"
354
+ src={preview.src}
355
+ />
356
+ {this.state.preview.loaded ? (
357
+ <div className="detail">
358
+ <span className="title" title={preview.filename}>
359
+ {preview.filename}
360
+ </span>
361
+ <br />
362
+ <span>
363
+ {preview.width} x {preview.height}
364
+ </span>
365
+ </div>
366
+ ) : (
367
+ <span />
368
+ )}
369
+ <br />
370
+ <button disabled={!validAsset} onClick={this.addNewAsset}>
371
+ LOAD THIS TEXTURE
372
+ </button>
373
+ </div>
374
+ </div>
375
+ </div>
376
+ <div className={this.state.addNewDialogOpened ? 'hide' : ''}>
377
+ <ul className="gallery">
378
+ {this.state.assetsImages
379
+ .sort(function (a, b) {
380
+ return a.id > b.id;
381
+ })
382
+ .map(
383
+ function (image) {
384
+ let textureClick = this.selectTexture.bind(this, image);
385
+ var selectedClass =
386
+ this.props.selectedTexture === '#' + image.id
387
+ ? 'selected'
388
+ : '';
389
+ return (
390
+ <li
391
+ key={image.id}
392
+ onClick={textureClick}
393
+ className={selectedClass}
394
+ >
395
+ <img width="155px" height="155px" src={image.src} />
396
+ <div className="detail">
397
+ <span className="title">{image.name}</span>
398
+ <span>{getFilename(image.src)}</span>
399
+ <span>
400
+ {image.width} x {image.height}
401
+ </span>
402
+ </div>
403
+ </li>
404
+ );
405
+ }.bind(this)
406
+ )}
407
+ {loadedTextures.map(function (texture) {
408
+ var image = texture.image;
409
+ let textureClick = this.selectTexture.bind(this, texture);
410
+ return (
411
+ <li key={texture.uuid} onClick={textureClick}>
412
+ <img width="155px" height="155px" src={image.src} />
413
+ <div className="detail">
414
+ <span className="title">Name:</span>{' '}
415
+ <span>{image.name}</span>
416
+ <span className="title">Filename:</span>{' '}
417
+ <span>{getFilename(image.src)}</span>
418
+ <span>
419
+ {image.width} x {image.height}
420
+ </span>
421
+ </div>
422
+ </li>
423
+ );
424
+ })}
425
+ </ul>
426
+ </div>
427
+ </Modal>
428
+ );
429
+ }
430
+ }
@@ -0,0 +1,142 @@
1
+ /* eslint-disable react/no-danger */
2
+ import React from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import {
5
+ faCaretDown,
6
+ faCaretRight,
7
+ faClone,
8
+ faEye,
9
+ faEyeSlash,
10
+ faTrashAlt
11
+ } from '@fortawesome/free-solid-svg-icons';
12
+ import { AwesomeIcon } from '../AwesomeIcon';
13
+ import clsx from 'clsx';
14
+ import { removeEntity, cloneEntity } from '../../lib/entity';
15
+ import EntityRepresentation from '../EntityRepresentation';
16
+ import Events from '../../lib/Events';
17
+
18
+ export default class Entity extends React.Component {
19
+ static propTypes = {
20
+ depth: PropTypes.number,
21
+ entity: PropTypes.object,
22
+ isExpanded: PropTypes.bool,
23
+ isFiltering: PropTypes.bool,
24
+ isSelected: PropTypes.bool,
25
+ selectEntity: PropTypes.func,
26
+ toggleExpandedCollapsed: PropTypes.func
27
+ };
28
+
29
+ constructor(props) {
30
+ super(props);
31
+ this.state = {};
32
+ }
33
+
34
+ onClick = () => this.props.selectEntity(this.props.entity);
35
+
36
+ onDoubleClick = () => Events.emit('objectfocus', this.props.entity.object3D);
37
+
38
+ toggleVisibility = () => {
39
+ const entity = this.props.entity;
40
+ const visible =
41
+ entity.tagName.toLowerCase() === 'a-scene'
42
+ ? entity.object3D.visible
43
+ : entity.getAttribute('visible');
44
+ entity.setAttribute('visible', !visible);
45
+ };
46
+
47
+ render() {
48
+ const isFiltering = this.props.isFiltering;
49
+ const isExpanded = this.props.isExpanded;
50
+ const entity = this.props.entity;
51
+ const tagName = entity.tagName.toLowerCase();
52
+
53
+ // Clone and remove buttons if not a-scene.
54
+ const cloneButton =
55
+ tagName === 'a-scene' ? null : (
56
+ <a
57
+ onClick={() => cloneEntity(entity)}
58
+ title="Clone entity"
59
+ className="button"
60
+ >
61
+ <AwesomeIcon icon={faClone} />
62
+ </a>
63
+ );
64
+ const removeButton =
65
+ tagName === 'a-scene' ? null : (
66
+ <a
67
+ onClick={(event) => {
68
+ event.stopPropagation();
69
+ removeEntity(entity);
70
+ }}
71
+ title="Remove entity"
72
+ className="button"
73
+ >
74
+ <AwesomeIcon icon={faTrashAlt} />
75
+ </a>
76
+ );
77
+
78
+ // Add spaces depending on depth.
79
+ const pad = '&nbsp;&nbsp;&nbsp;&nbsp;'.repeat(this.props.depth);
80
+ let collapse;
81
+ if (entity.children.length > 0 && !isFiltering) {
82
+ collapse = (
83
+ <span
84
+ onClick={() => this.props.toggleExpandedCollapsed(entity)}
85
+ className="collapsespace"
86
+ >
87
+ {isExpanded ? (
88
+ <AwesomeIcon icon={faCaretDown} />
89
+ ) : (
90
+ <AwesomeIcon icon={faCaretRight} />
91
+ )}
92
+ </span>
93
+ );
94
+ } else {
95
+ collapse = <span className="collapsespace" />;
96
+ }
97
+
98
+ // Visibility button.
99
+ const visible =
100
+ tagName === 'a-scene'
101
+ ? entity.object3D.visible
102
+ : entity.getAttribute('visible');
103
+ const visibilityButton = (
104
+ <i title="Toggle entity visibility" onClick={this.toggleVisibility}>
105
+ {visible ? (
106
+ <AwesomeIcon icon={faEye} />
107
+ ) : (
108
+ <AwesomeIcon icon={faEyeSlash} />
109
+ )}
110
+ </i>
111
+ );
112
+
113
+ // Class name.
114
+ const className = clsx({
115
+ active: this.props.isSelected,
116
+ entity: true,
117
+ novisible: !visible,
118
+ option: true
119
+ });
120
+
121
+ return (
122
+ <div className={className} onClick={this.onClick}>
123
+ <span>
124
+ {visibilityButton}
125
+ <span
126
+ className="entityChildPadding"
127
+ dangerouslySetInnerHTML={{ __html: pad }}
128
+ />
129
+ {collapse}
130
+ <EntityRepresentation
131
+ entity={entity}
132
+ onDoubleClick={this.onDoubleClick}
133
+ />
134
+ </span>
135
+ <span className="entityActions">
136
+ {cloneButton}
137
+ {removeButton}
138
+ </span>
139
+ </div>
140
+ );
141
+ }
142
+ }