mirror of
https://github.com/nasa/openmct.git
synced 2024-12-24 15:26:39 +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
src
api/composition
plugins/LADTable
ui
@ -21,7 +21,11 @@ define([
|
||||
topicService.and.returnValue(mutationTopic);
|
||||
publicAPI = {};
|
||||
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
|
||||
'get'
|
||||
'get',
|
||||
'mutate'
|
||||
]);
|
||||
publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [
|
||||
'on'
|
||||
]);
|
||||
publicAPI.objects.get.and.callFake(function (identifier) {
|
||||
return Promise.resolve({identifier: identifier});
|
||||
@ -52,6 +56,14 @@ define([
|
||||
{
|
||||
namespace: 'test',
|
||||
key: 'a'
|
||||
},
|
||||
{
|
||||
namespace: 'test',
|
||||
key: 'b'
|
||||
},
|
||||
{
|
||||
namespace: 'test',
|
||||
key: 'c'
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -68,12 +80,39 @@ define([
|
||||
composition.on('add', listener);
|
||||
|
||||
return composition.load().then(function () {
|
||||
expect(listener.calls.count()).toBe(1);
|
||||
expect(listener.calls.count()).toBe(3);
|
||||
expect(listener).toHaveBeenCalledWith({
|
||||
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.
|
||||
xit('synchronizes changes between instances', function () {
|
||||
|
@ -56,7 +56,8 @@ define([
|
||||
this.listeners = {
|
||||
add: [],
|
||||
remove: [],
|
||||
load: []
|
||||
load: [],
|
||||
reorder: []
|
||||
};
|
||||
this.onProviderAdd = this.onProviderAdd.bind(this);
|
||||
this.onProviderRemove = this.onProviderRemove.bind(this);
|
||||
@ -91,6 +92,13 @@ define([
|
||||
this.onProviderRemove,
|
||||
this
|
||||
);
|
||||
} if (event === 'reorder') {
|
||||
this.provider.on(
|
||||
this.domainObject,
|
||||
'reorder',
|
||||
this.onProviderReorder,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +149,13 @@ define([
|
||||
this.onProviderRemove,
|
||||
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.
|
||||
* @private
|
||||
@ -232,12 +274,12 @@ define([
|
||||
* Emit events.
|
||||
* @private
|
||||
*/
|
||||
CompositionCollection.prototype.emit = function (event, payload) {
|
||||
CompositionCollection.prototype.emit = function (event, ...payload) {
|
||||
this.listeners[event].forEach(function (l) {
|
||||
if (l.context) {
|
||||
l.callback.call(l.context, payload);
|
||||
l.callback.apply(l.context, payload);
|
||||
} else {
|
||||
l.callback(payload);
|
||||
l.callback(...payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -126,6 +126,7 @@ define([
|
||||
objectListeners = this.listeningTo[keyString] = {
|
||||
add: [],
|
||||
remove: [],
|
||||
reorder: [],
|
||||
composition: [].slice.apply(domainObject.composition)
|
||||
};
|
||||
}
|
||||
@ -160,7 +161,7 @@ define([
|
||||
});
|
||||
|
||||
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];
|
||||
}
|
||||
};
|
||||
@ -203,6 +204,30 @@ define([
|
||||
// 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
|
||||
* circular dependencies.
|
||||
|
@ -35,6 +35,9 @@ define([
|
||||
canView: function (domainObject) {
|
||||
return domainObject.type === 'LadTableSet';
|
||||
},
|
||||
canEdit: function (domainObject) {
|
||||
return domainObject.type === 'LadTableSet';
|
||||
},
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
|
||||
|
@ -35,6 +35,9 @@ define([
|
||||
canView: function (domainObject) {
|
||||
return domainObject.type === 'LadTable';
|
||||
},
|
||||
canEdit: function (domainObject) {
|
||||
return domainObject.type === 'LadTable';
|
||||
},
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
|
||||
|
@ -65,17 +65,24 @@ export default {
|
||||
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
|
||||
|
||||
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() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addItem);
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.reorder);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.reorder);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -93,6 +93,12 @@
|
||||
this.primaryTelemetryObjects.splice(index,1);
|
||||
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) {
|
||||
return (domainObject) => {
|
||||
let secondary = {};
|
||||
@ -120,11 +126,13 @@
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addPrimary);
|
||||
this.composition.on('remove', this.removePrimary);
|
||||
this.composition.on('reorder', this.reorderPrimary);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addPrimary);
|
||||
this.composition.off('remove', this.removePrimary);
|
||||
this.composition.off('reorder', this.reorderPrimary);
|
||||
this.compositions.forEach(c => {
|
||||
c.composition.off('add', c.addCallback);
|
||||
c.composition.off('remove', c.removeCallback);
|
||||
|
@ -83,85 +83,89 @@ export default {
|
||||
this.showSelection(selection);
|
||||
}
|
||||
this.openmct.selection.on('change', this.showSelection);
|
||||
this.openmct.editor.on('isEditing', (isEditing)=>{
|
||||
this.isEditing = isEditing;
|
||||
this.showSelection(this.openmct.selection.get());
|
||||
});
|
||||
this.openmct.editor.on('isEditing', this.setEditState);
|
||||
},
|
||||
methods: {
|
||||
setEditState(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
this.showSelection(this.openmct.selection.get());
|
||||
},
|
||||
showSelection(selection) {
|
||||
this.elements = [];
|
||||
this.elementsCache = [];
|
||||
this.elementsCache = {};
|
||||
this.listeners = [];
|
||||
this.parentObject = selection[0].context.item;
|
||||
if (this.mutationUnobserver) {
|
||||
this.mutationUnobserver();
|
||||
}
|
||||
if (this.compositionUnlistener) {
|
||||
this.compositionUnlistener();
|
||||
}
|
||||
|
||||
if (this.parentObject) {
|
||||
this.mutationUnobserver = this.openmct.objects.observe(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() {
|
||||
let composition = this.openmct.composition.get(this.parentObject);
|
||||
|
||||
if (composition){
|
||||
composition.load().then(this.setElements);
|
||||
}
|
||||
|
||||
addElement(element) {
|
||||
let keyString = this.openmct.objects.makeKeyString(element.identifier);
|
||||
this.elementsCache[keyString] =
|
||||
JSON.parse(JSON.stringify(element));
|
||||
this.applySearch(this.currentSearch);
|
||||
},
|
||||
setElements(elements) {
|
||||
this.elementsCache = elements.map((element)=>JSON.parse(JSON.stringify(element)))
|
||||
reorderElements() {
|
||||
this.applySearch(this.currentSearch);
|
||||
},
|
||||
removeElement(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(element.identifier);
|
||||
delete this.elementsCache[keyString];
|
||||
this.applySearch(this.currentSearch);
|
||||
},
|
||||
applySearch(input) {
|
||||
this.currentSearch = input;
|
||||
this.elements = this.elementsCache.filter((element) => {
|
||||
return element.name.toLowerCase().search(
|
||||
this.currentSearch) !== -1;
|
||||
this.elements = this.parentObject.composition.map((id) =>
|
||||
this.elementsCache[this.openmct.objects.makeKeyString(id)]
|
||||
).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) {
|
||||
event.preventDefault();
|
||||
},
|
||||
moveTo(moveToIndex) {
|
||||
console.log('dropped');
|
||||
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);
|
||||
this.composition.reorder(this.moveFromIndex, moveToIndex);
|
||||
},
|
||||
moveFrom(index){
|
||||
this.moveFromIndex = index;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.setEditState);
|
||||
this.openmct.selection.off('change', this.showSelection);
|
||||
if (this.mutationUnobserver) {
|
||||
this.mutationUnobserver();
|
||||
}
|
||||
if (this.compositionUnlistener) {
|
||||
this.compositionUnlistener();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -7,6 +7,7 @@ define([
|
||||
return function install(openmct) {
|
||||
let navigateCall = 0;
|
||||
let browseObject;
|
||||
let unobserve = undefined;
|
||||
|
||||
function viewObject(object, viewProvider) {
|
||||
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true);
|
||||
@ -18,6 +19,11 @@ define([
|
||||
navigateCall++;
|
||||
let currentNavigation = navigateCall;
|
||||
|
||||
if (unobserve) {
|
||||
unobserve();
|
||||
unobserve = undefined;
|
||||
}
|
||||
|
||||
if (!Array.isArray(path)) {
|
||||
path = path.split('/');
|
||||
}
|
||||
@ -36,6 +42,10 @@ define([
|
||||
// navigation service and router to expose a clear and minimal
|
||||
// API for this.
|
||||
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;
|
||||
browseObject = navigatedObject;
|
||||
|
Loading…
Reference in New Issue
Block a user