Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@
export class Command {
constructor(editor) {
this.id = -1;
this.inMemory = false;
this.updatable = false;
this.type = '';
this.name = '';
this.editor = editor;
}

toJSON() {
const output = {};
output.type = this.type;
output.id = this.id;
output.name = this.name;
return output;
}

fromJSON(json) {
this.inMemory = true;
this.type = json.type;
this.id = json.id;
this.name = json.name;
}
}
49 changes: 42 additions & 7 deletions src/lib/commands/ComponentAddCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,41 @@ import Events from '../Events.js';
import { Command } from '../command.js';
import { createUniqueId } from '../entity.js';

/**
* Command to add a component to an entity
* @param editor Editor
* @param payload Object containing entity (element or ID string), component, and value
* @constructor
*/
export class ComponentAddCommand extends Command {
constructor(editor, payload) {
constructor(editor, payload = null) {
super(editor);

this.type = 'componentadd';
this.name = 'Add Component';
this.updatable = false;

const entity = payload.entity;
if (!entity.id) {
entity.id = createUniqueId();
if (payload !== null) {
// Handle case where entity is passed as ID string when used with multi command
let entity;
if (typeof payload.entity === 'string') {
entity = document.querySelector(`#${payload.entity}:not(a-mixin)`);
if (!entity) {
console.error('Entity not found with ID:', payload.entity);
return;
}
this.entityId = payload.entity;
} else {
entity = payload.entity;
if (!entity.id) {
entity.id = createUniqueId();
}
this.entityId = entity.id;
}

this.component = payload.component;
this.value = payload.value;
}
this.entityId = entity.id;
this.component = payload.component;
this.value = payload.value;
}

execute(nextCommandCallback) {
Expand All @@ -43,4 +63,19 @@ export class ComponentAddCommand extends Command {
nextCommandCallback?.(entity);
}
}

toJSON() {
const output = super.toJSON(this);
output.entityId = this.entityId;
output.component = this.component;
output.value = this.value;
return output;
}

fromJSON(json) {
super.fromJSON(json);
this.entityId = json.entityId;
this.component = json.component;
this.value = json.value;
}
}
61 changes: 48 additions & 13 deletions src/lib/commands/ComponentRemoveCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,47 @@ import Events from '../Events.js';
import { Command } from '../command.js';
import { createUniqueId } from '../entity.js';

/**
* Command to remove a component from an entity
* @param editor Editor
* @param payload Object containing entity (element or ID string) and component
* @constructor
*/
export class ComponentRemoveCommand extends Command {
constructor(editor, payload) {
constructor(editor, payload = null) {
super(editor);

this.type = 'componentremove';
this.name = 'Remove Component';
this.updatable = false;

const entity = payload.entity;
if (!entity.id) {
entity.id = createUniqueId();
if (payload !== null) {
// Handle case where entity is passed as ID string when used with multi command
let entity;
if (typeof payload.entity === 'string') {
entity = document.querySelector(`#${payload.entity}:not(a-mixin)`);
if (!entity) {
console.error('Entity not found with ID:', payload.entity);
return;
}
this.entityId = payload.entity;
} else {
entity = payload.entity;
if (!entity.id) {
entity.id = createUniqueId();
}
this.entityId = entity.id;
}

this.component = payload.component;

const component =
entity.components[payload.component] ??
AFRAME.components[payload.component];
this.value = component.isSingleProperty
? component.schema.stringify(entity.getAttribute(payload.component))
: structuredClone(entity.getDOMAttribute(payload.component));
}
this.entityId = entity.id;
this.component = payload.component;

const component =
entity.components[payload.component] ??
AFRAME.components[payload.component];
this.value = component.isSingleProperty
? component.schema.stringify(entity.getAttribute(payload.component))
: structuredClone(entity.getDOMAttribute(payload.component));
}

execute(nextCommandCallback) {
Expand All @@ -49,4 +69,19 @@ export class ComponentRemoveCommand extends Command {
nextCommandCallback?.(entity);
}
}

toJSON() {
const output = super.toJSON(this);
output.entityId = this.entityId;
output.component = this.component;
output.value = this.value;
return output;
}

fromJSON(json) {
super.fromJSON(json);
this.entityId = json.entityId;
this.component = json.component;
this.value = json.value;
}
}
50 changes: 41 additions & 9 deletions src/lib/commands/EntityCloneCommand.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import Events from '../Events.js';
import { Command } from '../command.js';
import { cloneEntityImpl, createUniqueId, insertAfter } from '../entity.js';
import {
cloneEntityImpl,
createUniqueId,
elementToObject,
insertAfter,
objectToElement
} from '../entity.js';

export class EntityCloneCommand extends Command {
constructor(editor, entity) {
constructor(editor, entity = null) {
super(editor);

this.type = 'entityclone';
this.name = 'Clone Entity';
this.updatable = false;
if (!entity.id) {
entity.id = createUniqueId();
}
this.entityIdToClone = entity.id;

this.entityId = null;
this.detachedClone = null;
if (entity !== null) {
if (!entity.id) {
entity.id = createUniqueId();
}
this.entityIdToClone = entity.id;
this.detachedClone = null;
}
}

execute(nextCommandCallback) {
Expand All @@ -29,13 +38,14 @@ export class EntityCloneCommand extends Command {
if (!this.detachedClone) {
this.detachedClone = cloneEntityImpl(entityToClone);
}
if (this.detachedClone === null) return;
const clone = this.detachedClone.cloneNode(true);
clone.addEventListener(
'loaded',
function () {
() => {
clone.pause();
Events.emit('entityclone', clone);
AFRAME.INSPECTOR.selectEntity(clone);
this.editor.selectEntity(clone);
},
{ once: true }
);
Expand All @@ -58,4 +68,26 @@ export class EntityCloneCommand extends Command {
nextCommandCallback?.(entity);
}
}

toJSON() {
const output = super.toJSON(this);
output.entityIdToClone = this.entityIdToClone;
output.definition = this.detachedClone
? elementToObject(this.detachedClone)
: null;
output.entityId = this.entityId;
return output;
}

fromJSON(json) {
super.fromJSON(json);
this.entityIdToClone = json.entityIdToClone;
if (json.definition) {
this.detachedClone = objectToElement(json.definition);
this.detachedClone.flushToDOM();
} else {
this.detachedClone = null;
}
this.entityId = json.entityId;
}
}
47 changes: 32 additions & 15 deletions src/lib/commands/EntityCreateCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@ import { createEntity, createUniqueId } from '../entity.js';
* @constructor
*/
export class EntityCreateCommand extends Command {
constructor(editor, definition, callback = undefined) {
constructor(editor, definition = null, callback = undefined) {
super(editor);

this.type = 'entitycreate';
this.name = 'Create Entity';
this.definition = definition;
this.callback = callback;
this.entityId = null;
// If we have parentEl in the definition, be sure it has an id and store the definition with the id
if (
this.definition.parentEl &&
typeof this.definition.parentEl !== 'string'
) {
if (!this.definition.parentEl.id) {
this.definition.parentEl.id = createUniqueId();
if (definition !== null) {
this.definition = definition;
this.entityId = definition.id ?? null;
// If we have parentEl in the definition, be sure it has an id and store the definition with the id
if (
this.definition.parentEl &&
typeof this.definition.parentEl !== 'string'
) {
if (!this.definition.parentEl.id) {
this.definition.parentEl.id = createUniqueId();
}
this.definition = {
...this.definition,
parentEl: this.definition.parentEl.id
};
}
this.definition = {
...this.definition,
parentEl: this.definition.parentEl.id
};
}
}

Expand All @@ -43,7 +45,9 @@ export class EntityCreateCommand extends Command {
};
let parentEl;
if (this.definition.parentEl) {
parentEl = document.getElementById(this.definition.parentEl);
parentEl = document.querySelector(
`#${this.definition.parentEl}:not(a-mixin)`
);
}
if (!parentEl) {
parentEl = document.querySelector(this.editor.config.defaultParent);
Expand All @@ -67,4 +71,17 @@ export class EntityCreateCommand extends Command {
nextCommandCallback?.(entity);
}
}

toJSON() {
const output = super.toJSON(this);
output.definition = this.definition;
output.entityId = this.entityId;
return output;
}

fromJSON(json) {
super.fromJSON(json);
this.definition = json.definition;
this.entityId = json.entityId;
}
}
46 changes: 41 additions & 5 deletions src/lib/commands/EntityRemoveCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@ import Events from '../Events';
import { Command } from '../command.js';
import { findClosestEntity, prepareForSerialization } from '../entity.js';

/**
* Command to remove an entity from the scene
* @param editor Editor
* @param entity Entity element or ID string
* @constructor
*/
export class EntityRemoveCommand extends Command {
constructor(editor, entity) {
constructor(editor, entity = null) {
super(editor);

this.type = 'entityremove';
this.name = 'Remove Entity';
this.updatable = false;

this.entity = entity;
// Store the parent element and index for precise reinsertion
this.parentEl = entity.parentNode;
this.index = Array.from(this.parentEl.children).indexOf(entity);
if (entity !== null) {
// Handle case where entity is passed as ID string when used with multi command
if (typeof entity === 'string') {
this.entity = document.querySelector(`#${entity}:not(a-mixin)`);
if (!this.entity) {
console.error('Entity not found with ID:', entity);
return;
}
this.entityId = entity;
} else {
this.entity = entity;
this.entityId = entity.id;
}

// Store the parent element and index for precise reinsertion
this.parentEl = this.entity.parentNode;
this.index = Array.from(this.parentEl.children).indexOf(this.entity);
}
}

execute(nextCommandCallback) {
Expand Down Expand Up @@ -51,4 +71,20 @@ export class EntityRemoveCommand extends Command {
{ once: true }
);
}

toJSON() {
const output = super.toJSON(this);
output.entityId = this.entity.id;
output.parentId = this.parentEl.id;
output.index = this.index;
return output;
}

fromJSON(json) {
super.fromJSON(json);
this.entity = document.querySelector(`#${json.entityId}:not(a-mixin)`);
this.entityId = json.entityId;
this.parentEl = document.querySelector(`#${json.parentId}:not(a-mixin)`);
this.index = json.index;
}
}
Loading