-
Notifications
You must be signed in to change notification settings - Fork 212
Added clone and remove entity to the Edit menu #197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| /* global AFRAME */ | ||
| /* global AFRAME editor */ | ||
| var Events = require('../lib/Events.js'); | ||
|
|
||
| var components = AFRAME.components; | ||
|
|
@@ -38,3 +38,81 @@ export function updateEntity (entity, componentName, propertyName, value) { | |
| } | ||
| Events.emit('objectChanged', entity.object3D); | ||
| } | ||
|
|
||
| /** | ||
| * Remove an entity | ||
| * @param {Element} entity Entity to remove. | ||
| * @param {boolean} force (Optional) If true it won't ask for confirmation | ||
| */ | ||
| export function removeEntity (entity, force) { | ||
| if (entity) { | ||
| if (force === true || confirm('Do you really want to remove the entity: `' + (entity.id || entity.tagName) + '`')) { | ||
| entity.parentNode.removeChild(entity); | ||
| // @todo Select the next entity in the scenegraph | ||
| editor.selectEntity(null); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Remove the selected entity | ||
| * @param {boolean} force (Optional) If true it won't ask for confirmation | ||
| */ | ||
| export function removeSelectedEntity (force) { | ||
| removeEntity(editor.selectedEntity); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we pass in the entity rather than rely on global
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, in fact we have exposed both functions, but on the var menuOptions = {
'Clone': {group: 'base', callback: cloneSelectedEntity, needsEntity: true},
'Remove': {group: 'base', callback: removeSelectedEntity, needsEntity: true}
};Maybe
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, that's fine for now. have the callback pass it |
||
| } | ||
|
|
||
| /** | ||
| * Clone an entity, inserting it after the cloned one. | ||
| * @param {Element} entity Entity to clone | ||
| */ | ||
| export function cloneEntity (entity) { | ||
| var copy = entity.cloneNode(true); | ||
| copy.addEventListener('loaded', function (e) { | ||
| editor.selectEntity(copy); | ||
| }); | ||
|
|
||
| // Get a valid unique ID for the entity | ||
| copy.id = getUniqueId(entity.id); | ||
| //insertAfter(copy, entity); | ||
| entity.insertAdjacentHTML('afterend', copy.outerHTML); | ||
| } | ||
|
|
||
| /** | ||
| * Clone the selected entity | ||
| */ | ||
| export function cloneSelectedEntity () { | ||
| cloneEntity(editor.selectedEntity); | ||
| } | ||
|
|
||
| /** | ||
| * Insert an node after a referenced node. | ||
| * @param {Element} newNode Node to insert. | ||
| * @param {Element} referenceNode Node used as reference to insert after it. | ||
| */ | ||
| function insertAfter (newNode, referenceNode) { | ||
| referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); | ||
|
||
| } | ||
|
|
||
| /** | ||
| * Detect element's Id collision and returns a valid one | ||
| * @param {string} baseId Proposed Id | ||
| * @return {string} Valid Id based on the proposed Id | ||
| */ | ||
| function getUniqueId (baseId) { | ||
| if (!document.getElementById(baseId)) { | ||
| return baseId; | ||
| } | ||
|
|
||
| var i = 2; | ||
| // If the baseId ends with _#, it extracts the baseId removing the suffix | ||
| var groups = baseId.match(/(\w+)-(\d+)/); | ||
| if (groups) { | ||
| baseId = groups[1]; | ||
| i = groups[2]; | ||
| } | ||
|
|
||
| while (document.getElementById(baseId + '-' + i)) { i++; } | ||
|
|
||
| return baseId + '-' + i; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import React from 'react'; | ||
| var Events = require('../../lib/Events.js'); | ||
|
|
||
| var primitivesDefinitions = { | ||
| 'Entity': {group: 'entities', element: 'a-entity', components: {}}, | ||
|
|
||
| 'Box': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:box', material: 'color:#f00'}}, | ||
| 'Sphere': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:sphere', material: 'color:#ff0'}}, | ||
| 'Cylinder': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:cylinder', material: 'color:#00f'}}, | ||
| 'Plane': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:plane', material: 'color:#fff'}}, | ||
| 'Torus': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:torus', material: 'color:#0f0'}}, | ||
| 'TorusKnot': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:torusKnot', material: 'color:#f0f'}}, | ||
| 'Circle': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:circle', material: 'color:#f0f'}}, | ||
| 'Ring': {group: 'primitives', element: 'a-entity', components: {geometry: 'primitive:ring', material: 'color:#0ff'}}, | ||
|
|
||
| 'Ambient': {group: 'lights', element: 'a-entity', components: {light: 'type:ambient'}}, | ||
| 'Directional': {group: 'lights', element: 'a-entity', components: {light: 'type:directional'}}, | ||
| 'Hemisphere': {group: 'lights', element: 'a-entity', components: {light: 'type:hemisphere'}}, | ||
| 'Point': {group: 'lights', element: 'a-entity', components: {light: 'type:point'}}, | ||
| 'Spot': {group: 'lights', element: 'a-entity', components: {light: 'type:spot'}}, | ||
|
|
||
| 'Camera': {group: 'cameras', element: 'a-entity', components: {camera: ''}} | ||
| }; | ||
|
|
||
| function createEntity (definition) { | ||
| Events.emit('createNewEntity', primitivesDefinitions[definition]); | ||
| } | ||
|
|
||
| export const CreateMenu = props => { | ||
| var prevGroup = null; | ||
| var definitions = primitivesDefinitions; | ||
| return ( | ||
| <div className='menu'> | ||
| <div className='title'>Create</div> | ||
| <div className='options'> | ||
| { | ||
| Object.keys(definitions).map(definition => { | ||
| var output = []; | ||
| if (prevGroup === null) { | ||
| prevGroup = definitions[definition].group; | ||
| } else if (prevGroup !== definitions[definition].group) { | ||
| prevGroup = definitions[definition].group; | ||
| output.push(<hr/>); | ||
| } | ||
| output.push(<div className='option' key={definition} value={definition} | ||
| onClick={() => createEntity(definition)}>{definition}</div>); | ||
| return output; | ||
| }) | ||
| } | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import React from 'react'; | ||
| import {removeSelectedEntity, cloneSelectedEntity} from '../../actions/entity'; | ||
|
|
||
| var menuOptions = { | ||
| 'Clone': {group: 'base', callback: cloneSelectedEntity, needsEntity: true}, | ||
| 'Remove': {group: 'base', callback: removeSelectedEntity, needsEntity: true} | ||
| }; | ||
|
|
||
| export const EditMenu = () => { | ||
| var prevGroup = null; | ||
| return ( | ||
| <div className='menu'> | ||
| <div className='title'>Edit</div> | ||
| <div className='options'> | ||
| { | ||
| Object.keys(menuOptions).map(key => { | ||
| var option = menuOptions[key]; | ||
| var output = []; | ||
| if (prevGroup === null) { | ||
| prevGroup = option.group; | ||
| } else if (prevGroup !== option.group) { | ||
| prevGroup = option.group; | ||
| output.push(<hr/>); | ||
| } | ||
| output.push(<div className='option' key={key} value={key} | ||
| onClick={() => option.callback()}>{key}</div>); | ||
| return output; | ||
| }) | ||
| } | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import React from 'react'; | ||
| import Clipboard from 'clipboard'; | ||
| import {getSceneName, generateHtml} from '../../lib/exporter'; | ||
|
|
||
| export class ExportMenu extends React.Component { | ||
| componentDidMount () { | ||
| var clipboard = new Clipboard('[data-action="copy-to-clipboard"]', { | ||
| text: trigger => { | ||
| return generateHtml(); | ||
| } | ||
| }); | ||
| clipboard.on('error', e => { | ||
| // @todo Show Error on the UI | ||
| }); | ||
| } | ||
|
|
||
| saveToHTML () { | ||
| var link = document.createElement('a'); | ||
| link.style.display = 'none'; | ||
| document.body.appendChild(link); | ||
| function save (blob, filename) { | ||
| link.href = URL.createObjectURL(blob); | ||
| link.download = filename || 'ascene.html'; | ||
| link.click(); | ||
| // URL.revokeObjectURL(url); breaks Firefox... | ||
| } | ||
| function saveString (text, filename) { | ||
| save(new Blob([ text ], { type: 'text/plain' }), filename); | ||
| } | ||
| var sceneName = getSceneName(document.querySelector('a-scene')); | ||
| saveString(generateHtml(), sceneName); | ||
| } | ||
|
|
||
| render () { | ||
| return ( | ||
| <div className='menu'> | ||
| <div className='title'>Export</div> | ||
| <div className='options'> | ||
| <div className='option' onClick={this.saveToHTML}>Save HTML</div> | ||
| <div className='option' data-action='copy-to-clipboard'>Copy to clipboard</div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can run linter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👼 sorry for that :) done