mirror of
https://github.com/nasa/openmct.git
synced 2025-01-12 07:52:42 +00:00
Reorder api (#2316)
* Added 'reorder' function to composition API * Re-implemented reordering in Elements * Make LAD table editable * Remove test spec focus * Fixing bugs with event listeners * Clean up listeners properly in Elements pool * Fixed race condition on drag-and-drop to initiate edit * Implement reordering in LAD tables
This commit is contained in:
parent
23efef4469
commit
6116351dad
@ -21,7 +21,11 @@ define([
|
|||||||
topicService.and.returnValue(mutationTopic);
|
topicService.and.returnValue(mutationTopic);
|
||||||
publicAPI = {};
|
publicAPI = {};
|
||||||
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
|
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
|
||||||
'get'
|
'get',
|
||||||
|
'mutate'
|
||||||
|
]);
|
||||||
|
publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [
|
||||||
|
'on'
|
||||||
]);
|
]);
|
||||||
publicAPI.objects.get.and.callFake(function (identifier) {
|
publicAPI.objects.get.and.callFake(function (identifier) {
|
||||||
return Promise.resolve({identifier: identifier});
|
return Promise.resolve({identifier: identifier});
|
||||||
@ -52,6 +56,14 @@ define([
|
|||||||
{
|
{
|
||||||
namespace: 'test',
|
namespace: 'test',
|
||||||
key: 'a'
|
key: 'a'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: 'test',
|
||||||
|
key: 'b'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: 'test',
|
||||||
|
key: 'c'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -68,12 +80,39 @@ define([
|
|||||||
composition.on('add', listener);
|
composition.on('add', listener);
|
||||||
|
|
||||||
return composition.load().then(function () {
|
return composition.load().then(function () {
|
||||||
expect(listener.calls.count()).toBe(1);
|
expect(listener.calls.count()).toBe(3);
|
||||||
expect(listener).toHaveBeenCalledWith({
|
expect(listener).toHaveBeenCalledWith({
|
||||||
identifier: {namespace: 'test', key: 'a'}
|
identifier: {namespace: 'test', key: 'a'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('supports reordering of composition', function () {
|
||||||
|
var listener;
|
||||||
|
beforeEach(function () {
|
||||||
|
listener = jasmine.createSpy('reorderListener');
|
||||||
|
composition.on('reorder', listener);
|
||||||
|
|
||||||
|
return composition.load();
|
||||||
|
});
|
||||||
|
it('', function () {
|
||||||
|
composition.reorder(1, 0);
|
||||||
|
let newComposition =
|
||||||
|
publicAPI.objects.mutate.calls.mostRecent().args[2];
|
||||||
|
|
||||||
|
expect(listener).toHaveBeenCalledWith(1, 0);
|
||||||
|
expect(newComposition[0].key).toEqual('b');
|
||||||
|
expect(newComposition[1].key).toEqual('a');
|
||||||
|
});
|
||||||
|
it('', function () {
|
||||||
|
composition.reorder(0, 2);
|
||||||
|
let newComposition =
|
||||||
|
publicAPI.objects.mutate.calls.mostRecent().args[2];
|
||||||
|
|
||||||
|
expect(listener).toHaveBeenCalledWith(0, 2);
|
||||||
|
expect(newComposition[0].key).toEqual('c');
|
||||||
|
expect(newComposition[2].key).toEqual('a');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Implement add/removal in new default provider.
|
// TODO: Implement add/removal in new default provider.
|
||||||
xit('synchronizes changes between instances', function () {
|
xit('synchronizes changes between instances', function () {
|
||||||
|
@ -56,7 +56,8 @@ define([
|
|||||||
this.listeners = {
|
this.listeners = {
|
||||||
add: [],
|
add: [],
|
||||||
remove: [],
|
remove: [],
|
||||||
load: []
|
load: [],
|
||||||
|
reorder: []
|
||||||
};
|
};
|
||||||
this.onProviderAdd = this.onProviderAdd.bind(this);
|
this.onProviderAdd = this.onProviderAdd.bind(this);
|
||||||
this.onProviderRemove = this.onProviderRemove.bind(this);
|
this.onProviderRemove = this.onProviderRemove.bind(this);
|
||||||
@ -91,6 +92,13 @@ define([
|
|||||||
this.onProviderRemove,
|
this.onProviderRemove,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
|
} if (event === 'reorder') {
|
||||||
|
this.provider.on(
|
||||||
|
this.domainObject,
|
||||||
|
'reorder',
|
||||||
|
this.onProviderReorder,
|
||||||
|
this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +149,13 @@ define([
|
|||||||
this.onProviderRemove,
|
this.onProviderRemove,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
|
} else if (event === 'reorder') {
|
||||||
|
this.provider.off(
|
||||||
|
this.domainObject,
|
||||||
|
'reorder',
|
||||||
|
this.onProviderReorder,
|
||||||
|
this
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,6 +224,33 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder the domain objects in this composition.
|
||||||
|
*
|
||||||
|
* A call to [load]{@link module:openmct.CompositionCollection#load}
|
||||||
|
* must have resolved before using this method.
|
||||||
|
*
|
||||||
|
* @param {number} oldIndex
|
||||||
|
* @param {number} newIndex
|
||||||
|
* @memberof module:openmct.CompositionCollection#
|
||||||
|
* @name remove
|
||||||
|
*/
|
||||||
|
CompositionCollection.prototype.reorder = function (oldIndex, newIndex, skipMutate) {
|
||||||
|
if (!skipMutate) {
|
||||||
|
this.provider.reorder(this.domainObject, oldIndex, newIndex);
|
||||||
|
} else {
|
||||||
|
this.emit('reorder', oldIndex, newIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle reorder from provider.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
CompositionCollection.prototype.onProviderReorder = function (oldIndex, newIndex) {
|
||||||
|
this.reorder(oldIndex, newIndex, true);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle adds from provider.
|
* Handle adds from provider.
|
||||||
* @private
|
* @private
|
||||||
@ -232,12 +274,12 @@ define([
|
|||||||
* Emit events.
|
* Emit events.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
CompositionCollection.prototype.emit = function (event, payload) {
|
CompositionCollection.prototype.emit = function (event, ...payload) {
|
||||||
this.listeners[event].forEach(function (l) {
|
this.listeners[event].forEach(function (l) {
|
||||||
if (l.context) {
|
if (l.context) {
|
||||||
l.callback.call(l.context, payload);
|
l.callback.apply(l.context, payload);
|
||||||
} else {
|
} else {
|
||||||
l.callback(payload);
|
l.callback(...payload);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -126,6 +126,7 @@ define([
|
|||||||
objectListeners = this.listeningTo[keyString] = {
|
objectListeners = this.listeningTo[keyString] = {
|
||||||
add: [],
|
add: [],
|
||||||
remove: [],
|
remove: [],
|
||||||
|
reorder: [],
|
||||||
composition: [].slice.apply(domainObject.composition)
|
composition: [].slice.apply(domainObject.composition)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -160,7 +161,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
objectListeners[event].splice(index, 1);
|
objectListeners[event].splice(index, 1);
|
||||||
if (!objectListeners.add.length && !objectListeners.remove.length) {
|
if (!objectListeners.add.length && !objectListeners.remove.length && !objectListeners.reorder.length) {
|
||||||
delete this.listeningTo[keyString];
|
delete this.listeningTo[keyString];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -203,6 +204,30 @@ define([
|
|||||||
// TODO: this needs to be synchronized via mutation
|
// TODO: this needs to be synchronized via mutation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
|
||||||
|
let newComposition = domainObject.composition.slice();
|
||||||
|
newComposition[newIndex] = domainObject.composition[oldIndex];
|
||||||
|
newComposition[oldIndex] = domainObject.composition[newIndex];
|
||||||
|
this.publicAPI.objects.mutate(domainObject, 'composition', newComposition);
|
||||||
|
|
||||||
|
let id = objectUtils.makeKeyString(domainObject.identifier);
|
||||||
|
var listeners = this.listeningTo[id];
|
||||||
|
|
||||||
|
if (!listeners) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.reorder.forEach(notify);
|
||||||
|
|
||||||
|
function notify(listener) {
|
||||||
|
if (listener.context) {
|
||||||
|
listener.callback.call(listener.context, oldIndex, newIndex);
|
||||||
|
} else {
|
||||||
|
listener.callback(oldIndex, newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens on general mutation topic, using injector to fetch to avoid
|
* Listens on general mutation topic, using injector to fetch to avoid
|
||||||
* circular dependencies.
|
* circular dependencies.
|
||||||
|
@ -35,6 +35,9 @@ define([
|
|||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
return domainObject.type === 'LadTableSet';
|
return domainObject.type === 'LadTableSet';
|
||||||
},
|
},
|
||||||
|
canEdit: function (domainObject) {
|
||||||
|
return domainObject.type === 'LadTableSet';
|
||||||
|
},
|
||||||
view: function (domainObject) {
|
view: function (domainObject) {
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ define([
|
|||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
return domainObject.type === 'LadTable';
|
return domainObject.type === 'LadTable';
|
||||||
},
|
},
|
||||||
|
canEdit: function (domainObject) {
|
||||||
|
return domainObject.type === 'LadTable';
|
||||||
|
},
|
||||||
view: function (domainObject) {
|
view: function (domainObject) {
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
|
@ -65,17 +65,24 @@ export default {
|
|||||||
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
|
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
|
||||||
|
|
||||||
this.items.splice(index, 1);
|
this.items.splice(index, 1);
|
||||||
|
},
|
||||||
|
reorder(oldIndex, newIndex) {
|
||||||
|
let objectAtOldIndex = this.items[oldIndex];
|
||||||
|
this.$set(this.items, oldIndex, this.items[newIndex]);
|
||||||
|
this.$set(this.items, newIndex, objectAtOldIndex);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.composition = this.openmct.composition.get(this.domainObject);
|
this.composition = this.openmct.composition.get(this.domainObject);
|
||||||
this.composition.on('add', this.addItem);
|
this.composition.on('add', this.addItem);
|
||||||
this.composition.on('remove', this.removeItem);
|
this.composition.on('remove', this.removeItem);
|
||||||
|
this.composition.on('reorder', this.reorder);
|
||||||
this.composition.load();
|
this.composition.load();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.composition.off('add', this.addItem);
|
this.composition.off('add', this.addItem);
|
||||||
this.composition.off('remove', this.removeItem);
|
this.composition.off('remove', this.removeItem);
|
||||||
|
this.composition.off('reorder', this.reorder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -93,6 +93,12 @@
|
|||||||
this.primaryTelemetryObjects.splice(index,1);
|
this.primaryTelemetryObjects.splice(index,1);
|
||||||
primary = undefined;
|
primary = undefined;
|
||||||
},
|
},
|
||||||
|
reorderPrimary(oldIndex, newIndex) {
|
||||||
|
let objectAtOldIndex = this.primaryTelemetryObjects[oldIndex];
|
||||||
|
this.$set(this.primaryTelemetryObjects, oldIndex, this.primaryTelemetryObjects[newIndex]);
|
||||||
|
this.$set(this.primaryTelemetryObjects, newIndex, objectAtOldIndex);
|
||||||
|
|
||||||
|
},
|
||||||
addSecondary(primary) {
|
addSecondary(primary) {
|
||||||
return (domainObject) => {
|
return (domainObject) => {
|
||||||
let secondary = {};
|
let secondary = {};
|
||||||
@ -120,11 +126,13 @@
|
|||||||
this.composition = this.openmct.composition.get(this.domainObject);
|
this.composition = this.openmct.composition.get(this.domainObject);
|
||||||
this.composition.on('add', this.addPrimary);
|
this.composition.on('add', this.addPrimary);
|
||||||
this.composition.on('remove', this.removePrimary);
|
this.composition.on('remove', this.removePrimary);
|
||||||
|
this.composition.on('reorder', this.reorderPrimary);
|
||||||
this.composition.load();
|
this.composition.load();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.composition.off('add', this.addPrimary);
|
this.composition.off('add', this.addPrimary);
|
||||||
this.composition.off('remove', this.removePrimary);
|
this.composition.off('remove', this.removePrimary);
|
||||||
|
this.composition.off('reorder', this.reorderPrimary);
|
||||||
this.compositions.forEach(c => {
|
this.compositions.forEach(c => {
|
||||||
c.composition.off('add', c.addCallback);
|
c.composition.off('add', c.addCallback);
|
||||||
c.composition.off('remove', c.removeCallback);
|
c.composition.off('remove', c.removeCallback);
|
||||||
|
@ -83,85 +83,89 @@ export default {
|
|||||||
this.showSelection(selection);
|
this.showSelection(selection);
|
||||||
}
|
}
|
||||||
this.openmct.selection.on('change', this.showSelection);
|
this.openmct.selection.on('change', this.showSelection);
|
||||||
this.openmct.editor.on('isEditing', (isEditing)=>{
|
this.openmct.editor.on('isEditing', this.setEditState);
|
||||||
this.isEditing = isEditing;
|
|
||||||
this.showSelection(this.openmct.selection.get());
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setEditState(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
this.showSelection(this.openmct.selection.get());
|
||||||
|
},
|
||||||
showSelection(selection) {
|
showSelection(selection) {
|
||||||
this.elements = [];
|
this.elements = [];
|
||||||
this.elementsCache = [];
|
this.elementsCache = {};
|
||||||
|
this.listeners = [];
|
||||||
this.parentObject = selection[0].context.item;
|
this.parentObject = selection[0].context.item;
|
||||||
if (this.mutationUnobserver) {
|
if (this.mutationUnobserver) {
|
||||||
this.mutationUnobserver();
|
this.mutationUnobserver();
|
||||||
}
|
}
|
||||||
|
if (this.compositionUnlistener) {
|
||||||
|
this.compositionUnlistener();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.parentObject) {
|
if (this.parentObject) {
|
||||||
this.mutationUnobserver = this.openmct.objects.observe(this.parentObject, '*', (updatedModel) => {
|
this.mutationUnobserver = this.openmct.objects.observe(this.parentObject, '*', (updatedModel) => {
|
||||||
this.parentObject = updatedModel;
|
this.parentObject = updatedModel;
|
||||||
this.refreshComposition();
|
|
||||||
});
|
});
|
||||||
this.refreshComposition();
|
this.composition = this.openmct.composition.get(this.parentObject);
|
||||||
|
|
||||||
|
if (this.composition) {
|
||||||
|
this.composition.load();
|
||||||
|
|
||||||
|
this.composition.on('add', this.addElement);
|
||||||
|
this.composition.on('remove', this.removeElement);
|
||||||
|
this.composition.on('reorder', this.reorderElements);
|
||||||
|
|
||||||
|
this.compositionUnlistener = () => {
|
||||||
|
this.composition.off('add', this.addElement);
|
||||||
|
this.composition.off('remove', this.removeElement);
|
||||||
|
this.composition.off('reorder', this.reorderElements);
|
||||||
|
delete this.compositionUnlistener;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
refreshComposition() {
|
addElement(element) {
|
||||||
let composition = this.openmct.composition.get(this.parentObject);
|
let keyString = this.openmct.objects.makeKeyString(element.identifier);
|
||||||
|
this.elementsCache[keyString] =
|
||||||
if (composition){
|
JSON.parse(JSON.stringify(element));
|
||||||
composition.load().then(this.setElements);
|
this.applySearch(this.currentSearch);
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
setElements(elements) {
|
reorderElements() {
|
||||||
this.elementsCache = elements.map((element)=>JSON.parse(JSON.stringify(element)))
|
this.applySearch(this.currentSearch);
|
||||||
|
},
|
||||||
|
removeElement(identifier) {
|
||||||
|
let keyString = this.openmct.objects.makeKeyString(element.identifier);
|
||||||
|
delete this.elementsCache[keyString];
|
||||||
this.applySearch(this.currentSearch);
|
this.applySearch(this.currentSearch);
|
||||||
},
|
},
|
||||||
applySearch(input) {
|
applySearch(input) {
|
||||||
this.currentSearch = input;
|
this.currentSearch = input;
|
||||||
this.elements = this.elementsCache.filter((element) => {
|
this.elements = this.parentObject.composition.map((id) =>
|
||||||
return element.name.toLowerCase().search(
|
this.elementsCache[this.openmct.objects.makeKeyString(id)]
|
||||||
this.currentSearch) !== -1;
|
).filter((element) => {
|
||||||
|
return element !== undefined &&
|
||||||
|
element.name.toLowerCase().search(this.currentSearch) !== -1;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addObject(child){
|
|
||||||
this.elementsCache.push(child);
|
|
||||||
this.applySearch(this.currentSearch);
|
|
||||||
},
|
|
||||||
removeObject(childId){
|
|
||||||
this.elementsCache = this.elementsCache.filter((element) => !matches(element, childId));
|
|
||||||
this.applySearch(this.currentSearch);
|
|
||||||
|
|
||||||
function matches(elementA, elementBId) {
|
|
||||||
return elementA.identifier.namespace === elementBId.namespace &&
|
|
||||||
elementA.identifier.key === elementBId.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
allowDrop(event) {
|
allowDrop(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
moveTo(moveToIndex) {
|
moveTo(moveToIndex) {
|
||||||
console.log('dropped');
|
this.composition.reorder(this.moveFromIndex, moveToIndex);
|
||||||
let composition = this.parentObject.composition;
|
|
||||||
let moveFromId = composition[this.moveFromIndex];
|
|
||||||
let deleteIndex = this.moveFromIndex;
|
|
||||||
if (moveToIndex < this.moveFromIndex) {
|
|
||||||
composition.splice(deleteIndex, 1);
|
|
||||||
composition.splice(moveToIndex, 0, moveFromId);
|
|
||||||
} else {
|
|
||||||
composition.splice(deleteIndex, 1);
|
|
||||||
composition.splice(moveToIndex, 0, moveFromId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.openmct.objects.mutate(this.parentObject, 'composition', composition);
|
|
||||||
},
|
},
|
||||||
moveFrom(index){
|
moveFrom(index){
|
||||||
this.moveFromIndex = index;
|
this.moveFromIndex = index;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
|
this.openmct.editor.off('isEditing', this.setEditState);
|
||||||
this.openmct.selection.off('change', this.showSelection);
|
this.openmct.selection.off('change', this.showSelection);
|
||||||
|
if (this.mutationUnobserver) {
|
||||||
|
this.mutationUnobserver();
|
||||||
|
}
|
||||||
|
if (this.compositionUnlistener) {
|
||||||
|
this.compositionUnlistener();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -7,6 +7,7 @@ define([
|
|||||||
return function install(openmct) {
|
return function install(openmct) {
|
||||||
let navigateCall = 0;
|
let navigateCall = 0;
|
||||||
let browseObject;
|
let browseObject;
|
||||||
|
let unobserve = undefined;
|
||||||
|
|
||||||
function viewObject(object, viewProvider) {
|
function viewObject(object, viewProvider) {
|
||||||
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true);
|
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true);
|
||||||
@ -18,6 +19,11 @@ define([
|
|||||||
navigateCall++;
|
navigateCall++;
|
||||||
let currentNavigation = navigateCall;
|
let currentNavigation = navigateCall;
|
||||||
|
|
||||||
|
if (unobserve) {
|
||||||
|
unobserve();
|
||||||
|
unobserve = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Array.isArray(path)) {
|
if (!Array.isArray(path)) {
|
||||||
path = path.split('/');
|
path = path.split('/');
|
||||||
}
|
}
|
||||||
@ -37,6 +43,10 @@ define([
|
|||||||
// API for this.
|
// API for this.
|
||||||
openmct.router.path = objects.reverse();
|
openmct.router.path = objects.reverse();
|
||||||
|
|
||||||
|
unobserve = this.openmct.objects.observe(openmct.router.path[0], '*', (newObject) => {
|
||||||
|
openmct.router.path[0] = newObject;
|
||||||
|
});
|
||||||
|
|
||||||
openmct.layout.$refs.browseBar.domainObject = navigatedObject;
|
openmct.layout.$refs.browseBar.domainObject = navigatedObject;
|
||||||
browseObject = navigatedObject;
|
browseObject = navigatedObject;
|
||||||
if (!navigatedObject) {
|
if (!navigatedObject) {
|
||||||
|
Loading…
Reference in New Issue
Block a user