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,151 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { faClipboard, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
4
+ import { AwesomeIcon } from '../AwesomeIcon';
5
+ import PropertyRow from './PropertyRow';
6
+ import Collapsible from '../Collapsible';
7
+ import copy from 'clipboard-copy';
8
+ import { getComponentClipboardRepresentation } from '../../lib/entity';
9
+ import Events from '../../lib/Events';
10
+
11
+ const isSingleProperty = AFRAME.schema.isSingleProperty;
12
+
13
+ /**
14
+ * Single component.
15
+ */
16
+ export default class Component extends React.Component {
17
+ static propTypes = {
18
+ component: PropTypes.any,
19
+ entity: PropTypes.object,
20
+ isCollapsed: PropTypes.bool,
21
+ name: PropTypes.string
22
+ };
23
+
24
+ constructor(props) {
25
+ super(props);
26
+ this.state = {
27
+ entity: this.props.entity,
28
+ name: this.props.name
29
+ };
30
+ }
31
+
32
+ componentDidMount() {
33
+ Events.on('entityupdate', (detail) => {
34
+ if (detail.entity !== this.props.entity) {
35
+ return;
36
+ }
37
+ if (detail.component === this.props.name) {
38
+ this.forceUpdate();
39
+ }
40
+ });
41
+ }
42
+
43
+ static getDerivedStateFromProps(props, state) {
44
+ if (state.entity !== props.entity) {
45
+ return { entity: props.entity };
46
+ }
47
+ if (state.name !== props.name) {
48
+ return { name: props.name };
49
+ }
50
+ return null;
51
+ }
52
+
53
+ removeComponent = (event) => {
54
+ var componentName = this.props.name;
55
+ event.stopPropagation();
56
+ if (
57
+ confirm('Do you really want to remove component `' + componentName + '`?')
58
+ ) {
59
+ this.props.entity.removeAttribute(componentName);
60
+ Events.emit('componentremove', {
61
+ entity: this.props.entity,
62
+ component: componentName
63
+ });
64
+ }
65
+ };
66
+
67
+ /**
68
+ * Render propert(ies) of the component.
69
+ */
70
+ renderPropertyRows = () => {
71
+ const componentData = this.props.component;
72
+
73
+ if (isSingleProperty(componentData.schema)) {
74
+ const componentName = this.props.name;
75
+ const schema = AFRAME.components[componentName.split('__')[0]].schema;
76
+ return (
77
+ <PropertyRow
78
+ key={componentName}
79
+ name={componentName}
80
+ schema={schema}
81
+ data={componentData.data}
82
+ componentname={componentName}
83
+ isSingle={true}
84
+ entity={this.props.entity}
85
+ />
86
+ );
87
+ }
88
+
89
+ return Object.keys(componentData.schema)
90
+ .sort()
91
+ .map((propertyName) => (
92
+ <PropertyRow
93
+ key={propertyName}
94
+ name={propertyName}
95
+ schema={componentData.schema[propertyName]}
96
+ data={componentData.data[propertyName]}
97
+ componentname={this.props.name}
98
+ isSingle={false}
99
+ entity={this.props.entity}
100
+ />
101
+ ));
102
+ };
103
+
104
+ render() {
105
+ let componentName = this.props.name;
106
+ let subComponentName = '';
107
+ if (componentName.indexOf('__') !== -1) {
108
+ subComponentName = componentName;
109
+ componentName = componentName.substr(0, componentName.indexOf('__'));
110
+ }
111
+
112
+ return (
113
+ <Collapsible collapsed={this.props.isCollapsed}>
114
+ <div className="componentHeader collapsible-header">
115
+ <span
116
+ className="componentTitle"
117
+ title={subComponentName || componentName}
118
+ >
119
+ <span>{subComponentName || componentName}</span>
120
+ </span>
121
+ <div className="componentHeaderActions">
122
+ {/* <a
123
+ title="Copy to clipboard"
124
+ className="button"
125
+ onClick={(event) => {
126
+ event.preventDefault();
127
+ event.stopPropagation();
128
+ copy(
129
+ getComponentClipboardRepresentation(
130
+ this.state.entity,
131
+ (subComponentName || componentName).toLowerCase()
132
+ )
133
+ );
134
+ }}
135
+ >
136
+ <AwesomeIcon icon={faClipboard} />
137
+ </a> */}
138
+ <a
139
+ title="Remove component"
140
+ className="button"
141
+ onClick={this.removeComponent}
142
+ >
143
+ <AwesomeIcon icon={faTrashAlt} />
144
+ </a>
145
+ </div>
146
+ </div>
147
+ <div className="collapsible-content">{this.renderPropertyRows()}</div>
148
+ </Collapsible>
149
+ );
150
+ }
151
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import AddComponent from './AddComponent';
4
+ import Component from './Component';
5
+ import CommonComponents from './CommonComponents';
6
+ import DEFAULT_COMPONENTS from './DefaultComponents';
7
+ import Events from '../../lib/Events';
8
+
9
+ export default class ComponentsContainer extends React.Component {
10
+ static propTypes = {
11
+ entity: PropTypes.object
12
+ };
13
+
14
+ componentDidMount() {
15
+ Events.on('entityupdate', (detail) => {
16
+ if (detail.entity !== this.props.entity) {
17
+ return;
18
+ }
19
+ if (detail.component === 'mixin') {
20
+ this.forceUpdate();
21
+ }
22
+ });
23
+ }
24
+
25
+ render() {
26
+ const entity = this.props.entity;
27
+ const components = entity ? entity.components : {};
28
+ const definedComponents = Object.keys(components).filter(function (key) {
29
+ return DEFAULT_COMPONENTS.indexOf(key) === -1;
30
+ });
31
+
32
+ const renderedComponents = definedComponents.sort().map(function (key) {
33
+ return (
34
+ <Component
35
+ isCollapsed={definedComponents.length > 2}
36
+ component={components[key]}
37
+ entity={entity}
38
+ key={key}
39
+ name={key}
40
+ />
41
+ );
42
+ });
43
+
44
+ return (
45
+ <div className="components">
46
+ <CommonComponents entity={entity} />
47
+ <AddComponent entity={entity} />
48
+ {renderedComponents}
49
+ </div>
50
+ );
51
+ }
52
+ }
@@ -0,0 +1 @@
1
+ export default ['visible', 'position', 'scale', 'rotation'];
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Select from 'react-select';
4
+ import Events from '../../lib/Events';
5
+
6
+ export default class Mixin extends React.Component {
7
+ static propTypes = {
8
+ entity: PropTypes.object.isRequired
9
+ };
10
+
11
+ constructor(props) {
12
+ super(props);
13
+ this.state = { mixins: this.getMixinValue() };
14
+ }
15
+
16
+ componentDidUpdate(prevProps, prevState) {
17
+ if (this.props.entity === prevProps.entity) {
18
+ return;
19
+ }
20
+ this.setState({ mixins: this.getMixinValue() });
21
+ }
22
+
23
+ getMixinValue() {
24
+ return (this.props.entity.getAttribute('mixin') || '')
25
+ .split(/\s+/g)
26
+ .filter((v) => !!v)
27
+ .map((v) => ({ label: v, value: v }));
28
+ }
29
+
30
+ getMixinOptions = () => {
31
+ const mixinIds = this.props.entity.mixinEls.map(function (mixin) {
32
+ return mixin.id;
33
+ });
34
+
35
+ return Array.prototype.slice
36
+ .call(document.querySelectorAll('a-mixin'))
37
+ .filter(function (mixin) {
38
+ return mixinIds.indexOf(mixin.id) === -1;
39
+ })
40
+ .sort()
41
+ .map(function (mixin) {
42
+ return { value: mixin.id, label: mixin.id };
43
+ });
44
+ };
45
+
46
+ updateMixins = (value) => {
47
+ const entity = this.props.entity;
48
+
49
+ this.setState({ mixins: value });
50
+ const mixinStr = value.map((v) => v.value).join(' ');
51
+ entity.setAttribute('mixin', mixinStr);
52
+
53
+ Events.emit('entityupdate', {
54
+ component: 'mixin',
55
+ entity: entity,
56
+ property: '',
57
+ value: mixinStr
58
+ });
59
+ };
60
+
61
+ render() {
62
+ return (
63
+ <div className="mixinOptions">
64
+ <div className="propertyRow">
65
+ <span className="text">mixins</span>
66
+ <span className="mixinValue">
67
+ <Select
68
+ id="mixinSelect"
69
+ classNamePrefix="select"
70
+ options={this.getMixinOptions()}
71
+ isMulti={true}
72
+ placeholder="Add mixin..."
73
+ noResultsText="No mixins found"
74
+ onChange={this.updateMixins.bind(this)}
75
+ simpleValue
76
+ value={this.state.mixins}
77
+ />
78
+ </span>
79
+ </div>
80
+ </div>
81
+ );
82
+ }
83
+ }
@@ -0,0 +1,145 @@
1
+ /* eslint-disable no-prototype-builtins */
2
+ import React from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import clsx from 'clsx';
5
+
6
+ import BooleanWidget from '../widgets/BooleanWidget';
7
+ import ColorWidget from '../widgets/ColorWidget';
8
+ import InputWidget from '../widgets/InputWidget';
9
+ import NumberWidget from '../widgets/NumberWidget';
10
+ import SelectWidget from '../widgets/SelectWidget';
11
+ import TextureWidget from '../widgets/TextureWidget';
12
+ import Vec4Widget from '../widgets/Vec4Widget';
13
+ import Vec3Widget from '../widgets/Vec3Widget';
14
+ import Vec2Widget from '../widgets/Vec2Widget';
15
+ import { updateEntity } from '../../lib/entity';
16
+
17
+ export default class PropertyRow extends React.Component {
18
+ static propTypes = {
19
+ componentname: PropTypes.string.isRequired,
20
+ data: PropTypes.oneOfType([
21
+ PropTypes.array.isRequired,
22
+ PropTypes.bool.isRequired,
23
+ PropTypes.number.isRequired,
24
+ PropTypes.object.isRequired,
25
+ PropTypes.string.isRequired
26
+ ]),
27
+ entity: PropTypes.object.isRequired,
28
+ isSingle: PropTypes.bool.isRequired,
29
+ name: PropTypes.string.isRequired,
30
+ schema: PropTypes.object.isRequired
31
+ };
32
+
33
+ constructor(props) {
34
+ super(props);
35
+ this.id = props.componentname + ':' + props.name;
36
+ }
37
+
38
+ getWidget() {
39
+ const props = this.props;
40
+ const isMap =
41
+ props.componentname === 'material' &&
42
+ (props.name === 'envMap' || props.name === 'src');
43
+ let type = props.schema.type;
44
+ if (props.componentname === 'animation' && props.name === 'loop') {
45
+ // fix wrong number type for animation loop property
46
+ type = 'boolean';
47
+ }
48
+
49
+ const value =
50
+ props.schema.type === 'selector'
51
+ ? props.entity.getDOMAttribute(props.componentname)?.[props.name]
52
+ : props.data;
53
+
54
+ const widgetProps = {
55
+ componentname: props.componentname,
56
+ entity: props.entity,
57
+ isSingle: props.isSingle,
58
+ name: props.name,
59
+ onChange: function (name, value) {
60
+ updateEntity(
61
+ props.entity,
62
+ props.componentname,
63
+ !props.isSingle ? props.name : '',
64
+ value
65
+ );
66
+ },
67
+ value: value
68
+ };
69
+ const numberWidgetProps = {
70
+ min: props.schema.hasOwnProperty('min') ? props.schema.min : -Infinity,
71
+ max: props.schema.hasOwnProperty('max') ? props.schema.max : Infinity
72
+ };
73
+
74
+ if (props.schema.oneOf && props.schema.oneOf.length > 0) {
75
+ return <SelectWidget {...widgetProps} options={props.schema.oneOf} />;
76
+ }
77
+ if (type === 'map' || isMap) {
78
+ return <TextureWidget {...widgetProps} />;
79
+ }
80
+
81
+ switch (type) {
82
+ case 'number': {
83
+ return <NumberWidget {...widgetProps} {...numberWidgetProps} />;
84
+ }
85
+ case 'int': {
86
+ return (
87
+ <NumberWidget {...widgetProps} {...numberWidgetProps} precision={0} />
88
+ );
89
+ }
90
+ case 'vec2': {
91
+ return <Vec2Widget {...widgetProps} />;
92
+ }
93
+ case 'vec3': {
94
+ return <Vec3Widget {...widgetProps} />;
95
+ }
96
+ case 'vec4': {
97
+ return <Vec4Widget {...widgetProps} />;
98
+ }
99
+ case 'color': {
100
+ return <ColorWidget {...widgetProps} />;
101
+ }
102
+ case 'boolean': {
103
+ return <BooleanWidget {...widgetProps} />;
104
+ }
105
+ default: {
106
+ if (
107
+ props.schema.type === 'string' &&
108
+ widgetProps.value &&
109
+ typeof widgetProps.value !== 'string'
110
+ ) {
111
+ // Allow editing a custom type like event-set component schema
112
+ widgetProps.value = props.schema.stringify(widgetProps.value);
113
+ }
114
+ return <InputWidget {...widgetProps} />;
115
+ }
116
+ }
117
+ }
118
+
119
+ render() {
120
+ const props = this.props;
121
+ const value =
122
+ props.schema.type === 'selector'
123
+ ? props.entity.getDOMAttribute(props.componentname)?.[props.name]
124
+ : JSON.stringify(props.data);
125
+ const title =
126
+ props.name + '\n - type: ' + props.schema.type + '\n - value: ' + value;
127
+
128
+ const className = clsx({
129
+ propertyRow: true,
130
+ propertyRowDefined: props.isSingle
131
+ ? !!props.entity.getDOMAttribute(props.componentname)
132
+ : props.name in
133
+ (props.entity.getDOMAttribute(props.componentname) || {})
134
+ });
135
+
136
+ return (
137
+ <div className={className}>
138
+ <label htmlFor={this.id} className="text" title={title}>
139
+ {props.name}
140
+ </label>
141
+ {this.getWidget(props.schema.type)}
142
+ </div>
143
+ );
144
+ }
145
+ }
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { faTimes } from '@fortawesome/free-solid-svg-icons';
4
+ import { AwesomeIcon } from '../AwesomeIcon'; // Assuming AwesomeIcon is in the parent directory
5
+ import ComponentsContainer from './ComponentsContainer';
6
+ import EntityRepresentation from '../EntityRepresentation';
7
+ import Events from '../../lib/Events';
8
+
9
+ export default class Sidebar extends React.Component {
10
+ static propTypes = {
11
+ entity: PropTypes.object,
12
+ visible: PropTypes.bool
13
+ };
14
+
15
+ constructor(props) {
16
+ super(props);
17
+ this.state = { open: false };
18
+ }
19
+
20
+ componentDidMount() {
21
+ Events.on('componentremove', (event) => {
22
+ this.forceUpdate();
23
+ });
24
+
25
+ Events.on('componentadd', (event) => {
26
+ this.forceUpdate();
27
+ });
28
+ }
29
+
30
+ handleToggle = () => {
31
+ this.setState({ open: !this.state.open });
32
+ };
33
+
34
+ handleClose = () => {
35
+ Events.emit('togglesidebar', { which: 'attributes' });
36
+ };
37
+
38
+ render() {
39
+ const entity = this.props.entity;
40
+ const visible = this.props.visible;
41
+ if (entity && visible) {
42
+ return (
43
+ <div id="sidebar">
44
+ <ComponentsContainer entity={entity} />
45
+ </div>
46
+ );
47
+ } else {
48
+ return <div />;
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ const BackViewIcon = (props) => (
4
+ <svg
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ stroke="currentColor"
8
+ strokeWidth="1.5"
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ {...props}
12
+ >
13
+ {/* Cube Outline (adjust perspective for back view) */}
14
+ {/* Draw the back face first */}
15
+ <path d="M 10 2 L 24 2 L 24 16 L 10 16 Z" fill="#00ff99" />{' '}
16
+ {/* Back Face Highlighted */}
17
+ {/* Draw visible edges from back view */}
18
+ <path d="M 5 5 L 10 2" /> {/* Top-Left Edge */}
19
+ <path d="M 5 5 L 5 19" /> {/* Front-Left Edge */}
20
+ <path d="M 5 19 L 10 16" /> {/* Bottom-Left Edge */}
21
+ <path d="M 19 5 L 24 2" /> {/* Top-Right Edge */}
22
+ <path d="M 19 5 L 19 19" /> {/* Front-Right Edge */}
23
+ <path d="M 19 19 L 24 16" /> {/* Bottom-Right Edge */}
24
+ </svg>
25
+ );
26
+
27
+ export default BackViewIcon;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+
3
+ const BottomViewIcon = (props) => (
4
+ <svg
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ stroke="currentColor"
8
+ strokeWidth="1.5"
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ {...props}
12
+ >
13
+ {/* Cube Outline */}
14
+ <path d="M 5 5 L 19 5 L 19 19 L 5 19 Z" /> {/* Front Face */}
15
+ <path d="M 5 5 L 10 2 L 24 2 L 19 5" /> {/* Top Edge */}
16
+ <path d="M 19 5 L 24 2 L 24 16 L 19 19" /> {/* Right Edge */}
17
+ <path d="M 5 19 L 10 16 L 10 2" /> {/* Left Edge */}
18
+ <path d="M 10 16 L 24 16" /> {/* Bottom Edge */}
19
+ {/* Highlight Bottom Face (approximated) */}
20
+ {/* Since it's a bottom view, we might just show the bottom square or a variation */}
21
+ {/* Let's highlight the bottom part of the front and right faces visible from a low angle */}
22
+ <path d="M 5 19 L 19 19 L 24 16 L 10 16 Z" fill="#00ff99" />
23
+ </svg>
24
+ );
25
+
26
+ export default BottomViewIcon;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+
3
+ const FrontViewIcon = (props) => (
4
+ <svg
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ stroke="currentColor"
8
+ strokeWidth="1.5"
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ {...props}
12
+ >
13
+ {/* Cube Outline */}
14
+ <path d="M 5 5 L 19 5 L 19 19 L 5 19 Z" fill="#00ff99" />{' '}
15
+ {/* Front Face Highlighted */}
16
+ <path d="M 5 5 L 10 2 L 24 2 L 19 5" /> {/* Top Edge */}
17
+ <path d="M 19 5 L 24 2 L 24 16 L 19 19" /> {/* Right Edge */}
18
+ <path d="M 5 19 L 10 16 L 10 2" /> {/* Left Edge */}
19
+ <path d="M 10 16 L 24 16" /> {/* Bottom Edge */}
20
+ </svg>
21
+ );
22
+
23
+ export default FrontViewIcon;
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+
3
+ const LeftViewIcon = (props) => (
4
+ <svg
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ stroke="currentColor"
8
+ strokeWidth="1.5"
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ {...props}
12
+ >
13
+ {/* Cube Outline */}
14
+ <path d="M 5 5 L 19 5 L 19 19 L 5 19 Z" /> {/* Front Face */}
15
+ <path d="M 5 5 L 10 2 L 24 2 L 19 5" /> {/* Top Face */}
16
+ <path d="M 19 5 L 24 2 L 24 16 L 19 19" /> {/* Right Face */}
17
+ <path d="M 5 19 L 10 16 L 10 2" /> {/* Left Edge */}
18
+ <path d="M 10 16 L 24 16" /> {/* Bottom Edge */}
19
+ {/* Highlight Left Face */}
20
+ <path d="M 5 5 L 10 2 L 10 16 L 5 19 Z" fill="#00ff99" />
21
+ </svg>
22
+ );
23
+
24
+ export default LeftViewIcon;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+
3
+ const PerspectiveIcon = (props) => (
4
+ <svg
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ stroke="currentColor"
8
+ strokeWidth="1.5"
9
+ strokeLinecap="round"
10
+ strokeLinejoin="round"
11
+ {...props}
12
+ >
13
+ {/* Simple Cube Outline */}
14
+ <path d="M 5 5 L 19 5 L 19 19 L 5 19 Z" /> {/* Front Face */}
15
+ <path d="M 5 5 L 10 2 L 24 2 L 19 5" /> {/* Top Face */}
16
+ <path d="M 19 5 L 24 2 L 24 16 L 19 19" /> {/* Right Face */}
17
+ {/* Optional: Add inner lines for perspective effect if needed */}
18
+ {/* <path d="M 5 19 L 10 16 L 10 2" /> */}
19
+ {/* <path d="M 10 16 L 24 16" /> */}
20
+ </svg>
21
+ );
22
+
23
+ export default PerspectiveIcon;