Compare commits
	
		
			46 Commits
		
	
	
		
			v1.4.0-rc3
			...
			filters-in
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9601427e9e | ||
|   | acc08247a3 | ||
|   | 5237630cc5 | ||
|   | 010a1c2784 | ||
|   | e7611e91a9 | ||
|   | 731ad5c4a8 | ||
|   | a6986d7fda | ||
|   | c3c872537a | ||
|   | 5def99dc48 | ||
|   | c4c33f82c6 | ||
|   | c9f802229f | ||
|   | 3808a29676 | ||
|   | cd1a98c38f | ||
|   | 813546f2d8 | ||
|   | 29976d42bd | ||
|   | 52ce2c547f | ||
|   | ead131b51c | ||
|   | cf61883ae9 | ||
|   | fd568cac96 | ||
|   | f8dfc22613 | ||
|   | 35cc2e7329 | ||
|   | bd79f097ca | ||
|   | c2b5eaeaae | ||
|   | 3230da0654 | ||
|   | 948450eba8 | ||
|   | 78dde3ce6b | ||
|   | b379534cf7 | ||
|   | d7a4247ac5 | ||
|   | 323d992255 | ||
|   | 39c56f12cd | ||
|   | 72ebadbafb | ||
|   | b2b481dcf4 | ||
|   | 2dcef27a06 | ||
|   | 60c53b87de | ||
|   | 6293b32574 | ||
|   | 8a26ac809a | ||
|   | 4635b8f840 | ||
|   | afe653af49 | ||
|   | d891319585 | ||
|   | fab641f9be | ||
|   | 098d114a17 | ||
|   | 51078efe9e | ||
|   | b0b160d71e | ||
|   | 9c4553dd90 | ||
|   | 86e6885fb9 | ||
|   | c1daac1696 | 
| @@ -33,12 +33,20 @@ define([ | ||||
|                     formatString: '%0.2f', | ||||
|                     hints: { | ||||
|                         range: 1 | ||||
|                     } | ||||
|                     }, | ||||
|                     filters: [ | ||||
|                         { | ||||
|                             comparator: 'equals', | ||||
|                             possibleValues: [1,2,3,4] | ||||
|                         } | ||||
|                     ] | ||||
|  | ||||
|                 }, | ||||
|                 { | ||||
|                     key: "cos", | ||||
|                     name: "Cosine", | ||||
|                     formatString: '%0.2f', | ||||
|                     filters: ['equals'], | ||||
|                     hints: { | ||||
|                         range: 2 | ||||
|                     } | ||||
|   | ||||
| @@ -81,6 +81,7 @@ | ||||
|         openmct.install(openmct.plugins.Tabs()); | ||||
|         openmct.install(openmct.plugins.FlexibleLayout()); | ||||
|         openmct.install(openmct.plugins.LADTable()); | ||||
|         openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.stacked', 'telemetry.plot.overlay'])); | ||||
|         openmct.start(); | ||||
|     </script> | ||||
| </html> | ||||
|   | ||||
| @@ -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]; | ||||
|         } | ||||
|     }; | ||||
| @@ -199,6 +200,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. | ||||
|   | ||||
| @@ -297,7 +297,7 @@ define([ | ||||
|      * @returns {Function} a function which may be called to terminate | ||||
|      *          the subscription | ||||
|      */ | ||||
|     TelemetryAPI.prototype.subscribe = function (domainObject, callback) { | ||||
|     TelemetryAPI.prototype.subscribe = function (domainObject, callback, options) { | ||||
|         var provider = this.findSubscriptionProvider(domainObject); | ||||
|  | ||||
|         if (!this.subscribeCache) { | ||||
| @@ -316,7 +316,7 @@ define([ | ||||
|                         subscriber.callbacks.forEach(function (cb) { | ||||
|                             cb(value); | ||||
|                         }); | ||||
|                     }); | ||||
|                     }, options); | ||||
|             } else { | ||||
|                 subscriber.unsubscribe = function () {}; | ||||
|             } | ||||
|   | ||||
| @@ -28,14 +28,22 @@ define([ | ||||
|     describe('Telemetry API', function () { | ||||
|         var openmct; | ||||
|         var telemetryAPI; | ||||
|         var mockTypeService; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             openmct = { | ||||
|                 time: jasmine.createSpyObj('timeAPI', [ | ||||
|                     'timeSystem', | ||||
|                     'bounds' | ||||
|                 ]), | ||||
|                 $injector: jasmine.createSpyObj('injector', [ | ||||
|                     'get' | ||||
|                 ]) | ||||
|             }; | ||||
|             mockTypeService = jasmine.createSpyObj('typeService', [ | ||||
|                 'getType' | ||||
|             ]); | ||||
|             openmct.$injector.get.and.returnValue(mockTypeService); | ||||
|             openmct.time.timeSystem.and.returnValue({key: 'system'}); | ||||
|             openmct.time.bounds.and.returnValue({start: 0, end: 1}); | ||||
|             telemetryAPI = new TelemetryAPI(openmct); | ||||
| @@ -296,5 +304,233 @@ define([ | ||||
|                 ); | ||||
|             }); | ||||
|         }); | ||||
|         describe('metadata', function () { | ||||
|             let mockMetadata = {}; | ||||
|             let mockObjectType = { | ||||
|                 typeDef: {} | ||||
|             }; | ||||
|             beforeEach(function () { | ||||
|                 telemetryAPI.addProvider({ | ||||
|                     key: 'mockMetadataProvider', | ||||
|                     supportsMetadata() { | ||||
|                         return true; | ||||
|                     }, | ||||
|                     getMetadata() { | ||||
|                         return mockMetadata; | ||||
|                     } | ||||
|                 }); | ||||
|                 mockTypeService.getType.and.returnValue(mockObjectType); | ||||
|             }) | ||||
|             it('respects explicit priority', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name", | ||||
|                         hints: { | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp", | ||||
|                         name: "Timestamp", | ||||
|                         hints: { | ||||
|                             priority: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine", | ||||
|                         hints: { | ||||
|                             priority: 4 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine", | ||||
|                         hints: { | ||||
|                             priority: 3 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.values(); | ||||
|  | ||||
|                 values.forEach((value, index) => { | ||||
|                     expect(value.hints.priority).toBe(index + 1); | ||||
|                 }); | ||||
|             }); | ||||
|             it('if no explicit priority, defaults to order defined', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name" | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp", | ||||
|                         name: "Timestamp" | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine" | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine" | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.values(); | ||||
|  | ||||
|                 values.forEach((value, index) => { | ||||
|                     expect(value.key).toBe(mockMetadata.values[index].key); | ||||
|                 }); | ||||
|             }); | ||||
|             it('respects domain priority', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name" | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-utc", | ||||
|                         name: "Timestamp UTC", | ||||
|                         hints: { | ||||
|                             domain: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-local", | ||||
|                         name: "Timestamp Local", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine", | ||||
|                         hints: { | ||||
|                             range: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine", | ||||
|                         hints: { | ||||
|                             range: 1 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.valuesForHints(['domain']); | ||||
|  | ||||
|                 expect(values[0].key).toBe('timestamp-local'); | ||||
|                 expect(values[1].key).toBe('timestamp-utc'); | ||||
|             }); | ||||
|             it('respects range priority', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name" | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-utc", | ||||
|                         name: "Timestamp UTC", | ||||
|                         hints: { | ||||
|                             domain: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-local", | ||||
|                         name: "Timestamp Local", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine", | ||||
|                         hints: { | ||||
|                             range: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine", | ||||
|                         hints: { | ||||
|                             range: 1 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.valuesForHints(['range']); | ||||
|  | ||||
|                 expect(values[0].key).toBe('cos'); | ||||
|                 expect(values[1].key).toBe('sin'); | ||||
|             }); | ||||
|             it('respects priority and domain ordering', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "id", | ||||
|                         name: "ID", | ||||
|                         hints: { | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name", | ||||
|                         hints: { | ||||
|                             priority: 1 | ||||
|                         } | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-utc", | ||||
|                         name: "Timestamp UTC", | ||||
|                         hints: { | ||||
|                             domain: 2, | ||||
|                             priority: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-local", | ||||
|                         name: "Timestamp Local", | ||||
|                         hints: { | ||||
|                             domain: 1, | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-pst", | ||||
|                         name: "Timestamp PST", | ||||
|                         hints: { | ||||
|                             domain: 3, | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine" | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine" | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.valuesForHints(['priority', 'domain']); | ||||
|                 [ | ||||
|                     'timestamp-utc', | ||||
|                     'timestamp-local', | ||||
|                     'timestamp-pst' | ||||
|                 ].forEach((key, index) => { | ||||
|                     expect(values[index].key).toBe(key); | ||||
|                 }); | ||||
|             }); | ||||
|         }) | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -116,14 +116,18 @@ define([ | ||||
|             return hints.every(hasHint, metadata); | ||||
|         } | ||||
|         var matchingMetadata = this.valueMetadatas.filter(hasHints); | ||||
|         var sortedMetadata = _.sortBy(matchingMetadata, function (metadata) { | ||||
|             return hints.map(function (hint) { | ||||
|         let iteratees = hints.map(hint => { | ||||
|             return (metadata) => { | ||||
|                 return metadata.hints[hint]; | ||||
|             }); | ||||
|             } | ||||
|         }); | ||||
|         return sortedMetadata; | ||||
|         return _.sortByAll(matchingMetadata, ...iteratees); | ||||
|     }; | ||||
|  | ||||
|     TelemetryMetadataManager.prototype.getFilterableValues = function () { | ||||
|         return this.valueMetadatas.filter(metadatum => metadatum.filters && metadatum.filters.length > 0); | ||||
|     } | ||||
|  | ||||
|     TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () { | ||||
|         let valueMetadata = this.valuesForHints(['range'])[0]; | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,9 @@ define([ | ||||
|             canView: function (domainObject) { | ||||
|                 return domainObject.type === 'LadTable'; | ||||
|             }, | ||||
|             canEdit: function (domainObject) { | ||||
|                 return domainObject.type === 'LadTable'; | ||||
|             }, | ||||
|             view: function (domainObject) { | ||||
|                 let component; | ||||
|  | ||||
|   | ||||
							
								
								
									
										113
									
								
								src/plugins/filters/components/FilterField.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/plugins/filters/components/FilterField.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| <template> | ||||
|     <div class="u-contents c-filter-settings"> | ||||
|         <li class="grid-row c-filter-settings__setting" | ||||
|             v-for="(filter, index) in filterField.filters" | ||||
|             :key="index"> | ||||
|             <div class="grid-cell label"> | ||||
|                 {{ filterField.name }} = | ||||
|             </div> | ||||
|             <div class="grid-cell value"> | ||||
|                 <!-- EDITING --> | ||||
|                 <!-- String input, editing --> | ||||
|                 <template v-if="!filter.possibleValues && isEditing"> | ||||
|                     <input class="c-input--flex" | ||||
|                            type="text" | ||||
|                            :id="`${filter}filterControl`" | ||||
|                            placeholder="Enter Value" | ||||
|                            :value="persistedValue(filter)" | ||||
|                            @blur="updateFilterValue($event, filter)"> | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- Checkbox list, editing --> | ||||
|                 <template v-if="filter.possibleValues && isEditing"> | ||||
|                     <div class="c-checkbox-list__row" | ||||
|                          v-for="value in filter.possibleValues" | ||||
|                          :key="value"> | ||||
|                         <input class="c-checkbox-list__input" | ||||
|                                type="checkbox" | ||||
|                                :id="`${value}filterControl`" | ||||
|                                @change="onUserSelect($event, filter.comparator, value)" | ||||
|                                :checked="isChecked(filter.comparator, value)"> | ||||
|                         <span class="c-checkbox-list__value"> | ||||
|                             {{ value }} | ||||
|                         </span> | ||||
|                     </div> | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- BROWSING --> | ||||
|                 <!-- String input, NOT editing --> | ||||
|                 <template v-if="!filter.possibleValues && !isEditing"> | ||||
|                     {{ persistedValue(filter) }} | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- Checkbox list, NOT editing --> | ||||
|                 <template v-if="filter.possibleValues && !isEditing"> | ||||
|                     <span>{{persistedFilters[filter.comparator].join(', ')}}</span> | ||||
|                 </template> | ||||
|             </div> | ||||
|         </li> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     .c-filter-settings { | ||||
|         &__setting { | ||||
|             .grid-cell.label { | ||||
|                 white-space: nowrap; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
|  | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|     inject: [ | ||||
|         'openmct' | ||||
|     ], | ||||
|     props: { | ||||
|         filterField: Object,  | ||||
|         persistedFilters: { | ||||
|             type: Object, | ||||
|             default: () => { | ||||
|                 return {} | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             expanded: false, | ||||
|             isEditing: this.openmct.editor.isEditing() | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         toggleIsEditing(isEditing) { | ||||
|             this.isEditing = isEditing; | ||||
|         }, | ||||
|         onUserSelect(event, comparator, value){ | ||||
|             this.$emit('onUserSelect', this.filterField.key, comparator, value, event.target.checked); | ||||
|         }, | ||||
|         isChecked(comparator, value) { | ||||
|             if (this.persistedFilters[comparator] && this.persistedFilters[comparator].includes(value)) { | ||||
|                 return true; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         }, | ||||
|         persistedValue(comparator) { | ||||
|             return this.persistedFilters && this.persistedFilters[comparator]; | ||||
|         }, | ||||
|         updateFilterValue(event, comparator) { | ||||
|             this.$emit('onTextEnter', this.filterField.key, comparator, event.target.value); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.openmct.editor.on('isEditing', this.toggleIsEditing); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.openmct.editor.off('isEditing', this.toggleIsEditing); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										93
									
								
								src/plugins/filters/components/FilterObject.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/plugins/filters/components/FilterObject.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| <template> | ||||
|     <li> | ||||
|         <div class="c-tree__item menus-to-left" | ||||
|              @click="toggleExpanded"> | ||||
|             <span class="c-disclosure-triangle is-enabled flex-elem" | ||||
|               :class="{'c-disclosure-triangle--expanded': expanded}"></span> | ||||
|             <div class="c-tree__item__label"> | ||||
|                 <div class="t-object-label l-flex-row flex-elem grows"> | ||||
|                     <div class="t-item-icon flex-elem" | ||||
|                          :class="objectCssClass"> | ||||
|                     </div> | ||||
|                     <div class="t-title-label flex-elem grows">{{ filterObject.name }}</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <ul class="grid-properties" v-if="expanded"> | ||||
|             <filter-field | ||||
|                     v-for="field in filterObject.valuesWithFilters" | ||||
|                     :key="field.key" | ||||
|                     :filterField="field" | ||||
|                     :persistedFilters="persistedFilters[field.key]" | ||||
|                     @onUserSelect="collectUserSelects" | ||||
|                     @onTextEnter="updateTextFilter"> | ||||
|             </filter-field> | ||||
|         </ul> | ||||
|     </li> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|  | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| import FilterField from './FilterField.vue'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     components: { | ||||
|         FilterField | ||||
|     }, | ||||
|     props: { | ||||
|         filterObject: Object,  | ||||
|         persistedFilters: { | ||||
|             type: Object, | ||||
|             default: () => { | ||||
|                 return {}; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             expanded: false, | ||||
|             objectCssClass: undefined, | ||||
|             updatedFilters: this.persistedFilters | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         toggleExpanded() { | ||||
|             this.expanded = !this.expanded; | ||||
|         }, | ||||
|         collectUserSelects(key, comparator, valueName, value) { | ||||
|             let filterValue = this.updatedFilters[key]; | ||||
|  | ||||
|             if (filterValue && filterValue[comparator]) { | ||||
|                 if (value === false) { | ||||
|                     filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName); | ||||
|                 } else { | ||||
|                     filterValue[comparator].push(valueName); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (!this.updatedFilters[key]) { | ||||
|                     this.updatedFilters[key] = {}; | ||||
|                 } | ||||
|                 this.updatedFilters[key][comparator] = [value ? valueName : undefined]; | ||||
|             } | ||||
|  | ||||
|             this.$emit('updateFilters', this.keyString, this.updatedFilters); | ||||
|         }, | ||||
|         updateTextFilter(key, comparator, value) { | ||||
|             if (!this.updatedFilters[key]) { | ||||
|                 this.updatedFilters[key] = {}; | ||||
|             } | ||||
|             this.updatedFilters[key][comparator] = value; | ||||
|             this.$emit('updateFilters', this.keyString, this.updatedFilters); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         let type = this.openmct.types.get(this.filterObject.domainObject.type) || {}; | ||||
|         this.keyString = this.openmct.objects.makeKeyString(this.filterObject.domainObject.identifier); | ||||
|         this.objectCssClass = type.definition.cssClass; | ||||
|     } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										85
									
								
								src/plugins/filters/components/FiltersView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/plugins/filters/components/FiltersView.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| <template> | ||||
|     <ul class="tree c-tree c-properties__section" v-if="Object.keys(children).length"> | ||||
|         <h2 class="c-properties__header">Filters</h2> | ||||
|         <filter-object  | ||||
|             v-for="(child, key) in children" | ||||
|             :key="key" | ||||
|             :filterObject="child" | ||||
|             :persistedFilters="persistedFilters[key]" | ||||
|             @updateFilters="persistFilters"> | ||||
|         </filter-object> | ||||
|     </ul> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|  | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| import FilterObject from './FilterObject.vue'; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         FilterObject | ||||
|     }, | ||||
|     inject: [ | ||||
|         'openmct', | ||||
|         'providedObject' | ||||
|     ], | ||||
|     data() { | ||||
|         let persistedFilters = {}; | ||||
|  | ||||
|         if (this.providedObject.configuration && this.providedObject.configuration.filters) { | ||||
|             persistedFilters = this.providedObject.configuration.filters; | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             persistedFilters, | ||||
|             children: {} | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         addChildren(child) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(child.identifier), | ||||
|                 metadata = this.openmct.telemetry.getMetadata(child), | ||||
|                 valuesWithFilters = metadata.valueMetadatas.filter((value) => value.filters), | ||||
|                 childObject = { | ||||
|                     name: child.name, | ||||
|                     domainObject: child, | ||||
|                     valuesWithFilters | ||||
|                 }; | ||||
|  | ||||
|             if (childObject.valuesWithFilters.length) { | ||||
|                 this.$set(this.children, keyString, childObject); | ||||
|             } else { | ||||
|                 return; | ||||
|             } | ||||
|         }, | ||||
|         removeChildren(identifier) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(identifier); | ||||
|             this.$delete(this.children, keyString); | ||||
|             this.persistFilters(keyString); | ||||
|         }, | ||||
|         persistFilters(keyString, userSelects) { | ||||
|             this.persistedFilters[keyString] = userSelects; | ||||
|             this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters); | ||||
|         }, | ||||
|         updatePersistedFilters(filters) { | ||||
|             this.persistedFilters = filters; | ||||
|         } | ||||
|     }, | ||||
|     mounted(){ | ||||
|         this.composition = this.openmct.composition.get(this.providedObject); | ||||
|         this.composition.on('add', this.addChildren); | ||||
|         this.composition.on('remove', this.removeChildren); | ||||
|         this.composition.load(); | ||||
|  | ||||
|         this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.composition.off('add', this.addChildren); | ||||
|         this.composition.off('remove', this.removeChildren); | ||||
|         this.unobserve(); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										73
									
								
								src/plugins/filters/filtersInspectorViewProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/plugins/filters/filtersInspectorViewProvider.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './components/FiltersView.vue', | ||||
|     'vue' | ||||
| ], function ( | ||||
|     FiltersView, | ||||
|     Vue | ||||
| ) { | ||||
|  | ||||
|     function FiltersInspectorViewProvider(openmct, supportedObjectTypesArray) { | ||||
|         return { | ||||
|             key: 'filters-inspector', | ||||
|             name: 'Filters Inspector View', | ||||
|             canView: function (selection) { | ||||
|                 if (selection.length === 0) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 let object = selection[0].context.item; | ||||
|  | ||||
|                 return object && supportedObjectTypesArray.some(type => object.type === type); | ||||
|             }, | ||||
|             view: function (selection) { | ||||
|                 let component; | ||||
|                 let providedObject = selection[0].context.item; | ||||
|  | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = new Vue({ | ||||
|                             provide: { | ||||
|                                 openmct, | ||||
|                                 providedObject | ||||
|                             }, | ||||
|                             components: { | ||||
|                                 FiltersView: FiltersView.default | ||||
|                             }, | ||||
|                             template: '<filters-view></filters-view>', | ||||
|                             el: element | ||||
|                         }); | ||||
|                     }, | ||||
|                     destroy: function () { | ||||
|                         component.$destroy(); | ||||
|                         component = undefined; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             priority: function () { | ||||
|                 return 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return FiltersInspectorViewProvider; | ||||
| }); | ||||
							
								
								
									
										33
									
								
								src/plugins/filters/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/plugins/filters/plugin.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './filtersInspectorViewProvider' | ||||
| ], function ( | ||||
|     FiltersInspectorViewProvider | ||||
| ) { | ||||
|     return function plugin(supportedObjectTypesArray) { | ||||
|         return function install(openmct) { | ||||
|             openmct.inspectorViews.addProvider(new FiltersInspectorViewProvider(openmct, supportedObjectTypesArray)); | ||||
|         }; | ||||
|     }; | ||||
| }); | ||||
| @@ -250,7 +250,8 @@ define([ | ||||
|                                 {"has": "telemetry"} | ||||
|                             ], | ||||
|                             "model": { | ||||
|                                 "composition": [] | ||||
|                                 "composition": [], | ||||
|                                 "configuration": {} | ||||
|                             }, | ||||
|                             "properties": [], | ||||
|                             "priority": 890 | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
| --> | ||||
| <div ng-controller="PlotOptionsController"> | ||||
|     <ul class="tree c-tree"> | ||||
|         <h2 title="Plot series display properties in this object">Plot Series</h2> | ||||
|         <h2 title="Plot series display properties in this object">Plot Series Options</h2> | ||||
|         <li ng-repeat="series in config.series.models"> | ||||
|             <div class="c-tree__item menus-to-left"> | ||||
|                 <span class='c-disclosure-triangle is-enabled flex-elem' | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
| --> | ||||
| <div ng-controller="PlotOptionsController"> | ||||
|     <ul class="tree c-tree"> | ||||
|         <h2 title="Display properties for this object">Plot Series</h2> | ||||
|         <h2 title="Display properties for this object">Plot Series Options</h2> | ||||
|         <li ng-repeat="series in plotSeries" | ||||
|             ng-controller="PlotSeriesFormController" | ||||
|             form-model="series"> | ||||
|   | ||||
| @@ -101,6 +101,19 @@ define([ | ||||
|                     seriesConfig.identifier.namespace === identifier.namespace; | ||||
|             })[0]; | ||||
|         }, | ||||
|         /** | ||||
|          * Retrieve the persisted filters for a given identifier. | ||||
|          */ | ||||
|         getPersistedFilters: function (identifier) { | ||||
|             var domainObject = this.get('domainObject'), | ||||
|                 keystring = this.openmct.objects.makeKeyString(identifier); | ||||
|  | ||||
|             if (!domainObject.configuration || !domainObject.configuration.filters) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             return domainObject.configuration.filters[keystring]; | ||||
|         }, | ||||
|         /** | ||||
|          * Update the domain object with the given value. | ||||
|          */ | ||||
|   | ||||
| @@ -84,6 +84,7 @@ define([ | ||||
|             this.listenTo(this, 'change:xKey', this.onXKeyChange, this); | ||||
|             this.listenTo(this, 'change:yKey', this.onYKeyChange, this); | ||||
|             this.persistedConfig = options.persistedConfig; | ||||
|             this.filters = options.filters; | ||||
|  | ||||
|             Model.apply(this, arguments); | ||||
|             this.onXKeyChange(this.get('xKey')); | ||||
| @@ -139,13 +140,16 @@ define([ | ||||
|          * @returns {Promise} | ||||
|          */ | ||||
|         fetch: function (options) { | ||||
|             options = _.extend({}, {size: 1000, strategy: 'minmax'}, options || {}); | ||||
|             options = _.extend({}, {size: 1000, strategy: 'minmax', filters: this.filters}, options || {}); | ||||
|             if (!this.unsubscribe) { | ||||
|                 this.unsubscribe = this.openmct | ||||
|                     .telemetry | ||||
|                     .subscribe( | ||||
|                         this.domainObject, | ||||
|                         this.add.bind(this) | ||||
|                         this.add.bind(this), | ||||
|                         { | ||||
|                             filters: this.filters | ||||
|                         } | ||||
|                     ); | ||||
|             } | ||||
|  | ||||
| @@ -360,6 +364,19 @@ define([ | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         }, | ||||
|         /** | ||||
|          * Updates filters, clears the plot series, unsubscribes and resubscribes | ||||
|          * @public | ||||
|          */ | ||||
|         updateFiltersAndRefresh: function (updatedFilters) { | ||||
|             this.filters = updatedFilters; | ||||
|             this.reset(); | ||||
|             if (this.unsubscribe) { | ||||
|                 this.unsubscribe(); | ||||
|                 delete this.unsubscribe; | ||||
|             } | ||||
|             this.fetch(); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -66,6 +66,7 @@ define([ | ||||
|         }, | ||||
|         addTelemetryObject: function (domainObject, index) { | ||||
|             var seriesConfig = this.plot.getPersistedSeriesConfig(domainObject.identifier); | ||||
|             var filters = this.plot.getPersistedFilters(domainObject.identifier); | ||||
|             var plotObject = this.plot.get('domainObject'); | ||||
|  | ||||
|             if (!seriesConfig) { | ||||
| @@ -92,7 +93,8 @@ define([ | ||||
|                 collection: this, | ||||
|                 openmct: this.openmct, | ||||
|                 persistedConfig: this.plot | ||||
|                     .getPersistedSeriesConfig(domainObject.identifier) | ||||
|                     .getPersistedSeriesConfig(domainObject.identifier), | ||||
|                 filters: filters | ||||
|             })); | ||||
|         }, | ||||
|         removeTelemetryObject: function (identifier) { | ||||
|   | ||||
| @@ -71,6 +71,14 @@ define([ | ||||
|         this.config.series.forEach(this.addSeries, this); | ||||
|  | ||||
|         this.followTimeConductor(); | ||||
|  | ||||
|         this.newStyleDomainObject = $scope.domainObject.useCapability('adapter'); | ||||
|  | ||||
|         this.filterObserver = this.openmct.objects.observe( | ||||
|             this.newStyleDomainObject, | ||||
|             'configuration.filters', | ||||
|             this.updateFiltersAndResubscribe.bind(this) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     eventHelpers.extend(PlotController.prototype); | ||||
| @@ -154,6 +162,9 @@ define([ | ||||
|             clearInterval(this.checkForSize); | ||||
|             delete this.checkForSize; | ||||
|         } | ||||
|         if (this.filterObserver) { | ||||
|             this.filterObserver(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     PlotController.prototype.loadMoreData = function (range, purge) { | ||||
| @@ -244,6 +255,12 @@ define([ | ||||
|                           xRange.max === xDisplayRange.max); | ||||
|     }; | ||||
|  | ||||
|     PlotController.prototype.updateFiltersAndResubscribe = function (updatedFilters) { | ||||
|         this.config.series.forEach(function (series) { | ||||
|             series.updateFiltersAndRefresh(updatedFilters[series.keyString]); | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Export view as JPG. | ||||
|      */ | ||||
|   | ||||
| @@ -39,7 +39,8 @@ define([ | ||||
|     './folderView/plugin', | ||||
|     './flexibleLayout/plugin', | ||||
|     './tabs/plugin', | ||||
|     './LADTable/plugin' | ||||
|     './LADTable/plugin', | ||||
|     './filters/plugin' | ||||
| ], function ( | ||||
|     _, | ||||
|     UTCTimeSystem, | ||||
| @@ -59,7 +60,8 @@ define([ | ||||
|     FolderView, | ||||
|     FlexibleLayout, | ||||
|     Tabs, | ||||
|     LADTable | ||||
|     LADTable, | ||||
|     Filters | ||||
| ) { | ||||
|     var bundleMap = { | ||||
|         LocalStorage: 'platform/persistence/local', | ||||
| @@ -174,6 +176,7 @@ define([ | ||||
|     plugins.Tabs = Tabs; | ||||
|     plugins.FlexibleLayout = FlexibleLayout; | ||||
|     plugins.LADTable = LADTable; | ||||
|     plugins.Filters = Filters; | ||||
|  | ||||
|     return plugins; | ||||
| }); | ||||
|   | ||||
| @@ -46,7 +46,7 @@ define([ | ||||
|             view: function (selection) { | ||||
|                 let component; | ||||
|                 let domainObject = selection[0].context.item; | ||||
|                 const tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct); | ||||
|                 let tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct); | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = new Vue({ | ||||
| @@ -64,6 +64,7 @@ define([ | ||||
|                     destroy: function () { | ||||
|                         component.$destroy(); | ||||
|                         component = undefined; | ||||
|                         tableConfiguration = undefined; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|   | ||||
| @@ -53,6 +53,10 @@ define([ | ||||
|             this.isTelemetryObject = this.isTelemetryObject.bind(this); | ||||
|             this.refreshData = this.refreshData.bind(this); | ||||
|             this.requestDataFor = this.requestDataFor.bind(this); | ||||
|             this.updateFilters = this.updateFilters.bind(this); | ||||
|             this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this); | ||||
|  | ||||
|             this.filterObserver = undefined; | ||||
|  | ||||
|             this.createTableRowCollections(); | ||||
|             openmct.time.on('bounds', this.refreshData); | ||||
| @@ -60,6 +64,7 @@ define([ | ||||
|  | ||||
|         initialize() { | ||||
|             if (this.domainObject.type === 'table') { | ||||
|                 this.filterObserver = this.openmct.objects.observe(this.domainObject, 'configuration.filters', this.updateFilters); | ||||
|                 this.loadComposition(); | ||||
|             } else { | ||||
|                 this.addTelemetryObject(this.domainObject); | ||||
| @@ -81,6 +86,7 @@ define([ | ||||
|             this.tableComposition = this.openmct.composition.get(this.domainObject); | ||||
|             if (this.tableComposition !== undefined) { | ||||
|                 this.tableComposition.load().then((composition) => { | ||||
|  | ||||
|                     composition = composition.filter(this.isTelemetryObject); | ||||
|  | ||||
|                     this.configuration.addColumnsForAllObjects(composition); | ||||
| @@ -101,6 +107,15 @@ define([ | ||||
|             this.emit('object-added', telemetryObject); | ||||
|         } | ||||
|  | ||||
|         updateFilters() { | ||||
|             this.filteredRows.clear(); | ||||
|             this.boundedRows.clear(); | ||||
|             Object.keys(this.subscriptions).forEach(this.unsubscribe, this); | ||||
|  | ||||
|             this.telemetryObjects.forEach(this.requestDataFor.bind(this)); | ||||
|             this.telemetryObjects.forEach(this.subscribeTo.bind(this)); | ||||
|         } | ||||
|  | ||||
|         removeTelemetryObject(objectIdentifier) { | ||||
|             this.configuration.removeColumnsForObject(objectIdentifier, true); | ||||
|             let keyString = this.openmct.objects.makeKeyString(objectIdentifier); | ||||
| @@ -113,8 +128,8 @@ define([ | ||||
|  | ||||
|         requestDataFor(telemetryObject) { | ||||
|             this.incrementOutstandingRequests(); | ||||
|  | ||||
|             return this.openmct.telemetry.request(telemetryObject) | ||||
|             let requestOptions = this.buildOptionsFromConfiguration(telemetryObject); | ||||
|             return this.openmct.telemetry.request(telemetryObject, requestOptions) | ||||
|                 .then(telemetryData => { | ||||
|                     let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|                     let columnMap = this.getColumnMapForObject(keyString); | ||||
| @@ -165,19 +180,29 @@ define([ | ||||
|         } | ||||
|  | ||||
|         subscribeTo(telemetryObject) { | ||||
|             let subscribeOptions = this.buildOptionsFromConfiguration(telemetryObject); | ||||
|             let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier); | ||||
|             let columnMap = this.getColumnMapForObject(keyString); | ||||
|             let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject); | ||||
|  | ||||
|             this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => { | ||||
|                 this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator)); | ||||
|             }); | ||||
|             }, subscribeOptions); | ||||
|         } | ||||
|  | ||||
|         isTelemetryObject(domainObject) { | ||||
|             return domainObject.hasOwnProperty('telemetry'); | ||||
|         } | ||||
|  | ||||
|         buildOptionsFromConfiguration(telemetryObject) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier), | ||||
|                 filters = this.domainObject.configuration && | ||||
|                     this.domainObject.configuration.filters && | ||||
|                     this.domainObject.configuration.filters[keyString]; | ||||
|  | ||||
|             return {filters} || {}; | ||||
|         } | ||||
|  | ||||
|         unsubscribe(keyString) { | ||||
|             this.subscriptions[keyString](); | ||||
|             delete this.subscriptions[keyString]; | ||||
| @@ -188,6 +213,9 @@ define([ | ||||
|             this.filteredRows.destroy(); | ||||
|             Object.keys(this.subscriptions).forEach(this.unsubscribe, this); | ||||
|             this.openmct.time.off('bounds', this.refreshData); | ||||
|             if (this.filterObserver) { | ||||
|                 this.filterObserver(); | ||||
|             } | ||||
|  | ||||
|             if (this.tableComposition !== undefined) { | ||||
|                 this.tableComposition.off('add', this.addTelemetryObject); | ||||
|   | ||||
| @@ -49,7 +49,7 @@ define(function () { | ||||
|  | ||||
|         getFormattedValue(telemetryDatum) { | ||||
|             let formattedValue = this.formatter.format(telemetryDatum); | ||||
|             if (typeof formattedValue !== 'string') { | ||||
|             if (formattedValue !== undefined && typeof formattedValue !== 'string') { | ||||
|                 return formattedValue.toString(); | ||||
|             } else { | ||||
|                 return formattedValue; | ||||
|   | ||||
| @@ -1,19 +1,21 @@ | ||||
| <template> | ||||
| <div class="c-properties" v-if="isEditing"> | ||||
|     <div class="c-properties__header">Table Column Size</div> | ||||
|     <ul class="c-properties__section"> | ||||
|         <li class="c-properties__row"> | ||||
|             <div class="c-properties__label" title="Show or Hide Column"><label for="AutoSizeControl">Auto-size</label></div> | ||||
|             <div class="c-properties__value"><input type="checkbox" id="AutoSizeControl" :checked="configuration.autosize !== false" @change="toggleAutosize()"></div>             | ||||
|         </li> | ||||
|     </ul> | ||||
|     <div class="c-properties__header">Table Column Visibility</div> | ||||
|     <ul class="c-properties__section"> | ||||
|         <li class="c-properties__row" v-for="(title, key) in headers"> | ||||
|             <div class="c-properties__label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div> | ||||
|             <div class="c-properties__value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div> | ||||
|         </li> | ||||
|     </ul> | ||||
| <div class="c-properties"> | ||||
|     <template v-if="isEditing"> | ||||
|         <div class="c-properties__header">Table Column Size</div> | ||||
|         <ul class="c-properties__section"> | ||||
|             <li class="c-properties__row"> | ||||
|                 <div class="c-properties__label" title="Show or Hide Column"><label for="AutoSizeControl">Auto-size</label></div> | ||||
|                 <div class="c-properties__value"><input type="checkbox" id="AutoSizeControl" :checked="configuration.autosize !== false" @change="toggleAutosize()"></div>             | ||||
|             </li> | ||||
|         </ul> | ||||
|         <div class="c-properties__header">Table Column Visibility</div> | ||||
|         <ul class="c-properties__section"> | ||||
|             <li class="c-properties__row" v-for="(title, key) in headers"> | ||||
|                 <div class="c-properties__label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div> | ||||
|                 <div class="c-properties__value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </template> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
|   | ||||
| @@ -239,6 +239,18 @@ select { | ||||
|     padding: 1px 20px 1px $interiorMargin; | ||||
| } | ||||
|  | ||||
| // CHECKBOX LISTS | ||||
| // __input followed by __label | ||||
| .c-checkbox-list { | ||||
|     // Rows | ||||
|     &__row + &__row { margin-top: $interiorMarginSm; } | ||||
|  | ||||
|     // input and label in each __row | ||||
|     &__row { | ||||
|         > * + * { margin-left: $interiorMargin; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /******************************************************** HYPERLINKS AND HYPERLINK BUTTONS */ | ||||
| .c-hyperlink { | ||||
|     &--link { | ||||
|   | ||||
| @@ -87,29 +87,53 @@ export default { | ||||
|         showSelection(selection) { | ||||
|             this.elements = []; | ||||
|             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) { | ||||
|             this.elementsCache.push(JSON.parse(JSON.stringify(element))); | ||||
|             this.applySearch(this.currentSearch); | ||||
|         }, | ||||
|         setElements(elements) { | ||||
|             this.elementsCache = elements.map((element)=>JSON.parse(JSON.stringify(element))) | ||||
|         reorderElements(oldIndex, newIndex) { | ||||
|             let tempElement = this.elementsCache[oldIndex]; | ||||
|             this.elementsCache[oldIndex] = this.elementsCache[newIndex]; | ||||
|             this.elementsCache[newIndex] = tempElement; | ||||
|  | ||||
|             this.applySearch(this.currentSearch); | ||||
|         }, | ||||
|         removeElement(identifier) { | ||||
|             let index = this.elementsCache.findIndex(cachedElement =>  | ||||
|                 !this.openmct.objects.areIdsEqual(identifier, | ||||
|                     cachedElement.identifier)); | ||||
|             this.elementsCache.splice(index, 1); | ||||
|  | ||||
|             this.applySearch(this.currentSearch); | ||||
|         }, | ||||
|         applySearch(input) { | ||||
| @@ -137,19 +161,7 @@ export default { | ||||
|             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; | ||||
| @@ -157,6 +169,9 @@ export default { | ||||
|     }, | ||||
|     destroyed() { | ||||
|         this.openmct.selection.off('change', this.showSelection); | ||||
|         if (this.compositionUnlistener) { | ||||
|             this.compositionUnlistener(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|         <pane class="c-inspector__properties"> | ||||
|             <properties></properties> | ||||
|             <location></location> | ||||
|             <inspector-view></inspector-view> | ||||
|             <inspector-views></inspector-views> | ||||
|         </pane> | ||||
|         <pane class="c-inspector__elements" | ||||
|               handle="before" | ||||
| @@ -183,6 +183,13 @@ | ||||
|     } | ||||
|     /********************************************* LEGACY SUPPORT */ | ||||
|     .c-inspector { | ||||
|         // FilterField.vue | ||||
|         .u-contents + .u-contents { | ||||
|             li.grid-row > * { | ||||
|                 border-top: 1px solid $colorInspectorSectionHeaderBg; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         li.grid-row + li.grid-row { | ||||
|             > * { | ||||
|                 border-top: 1px solid $colorInspectorSectionHeaderBg; | ||||
| @@ -210,7 +217,7 @@ | ||||
|     import Elements from './Elements.vue'; | ||||
|     import Location from './Location.vue'; | ||||
|     import Properties from './Properties.vue'; | ||||
|     import InspectorView from './InspectorView.vue'; | ||||
|     import InspectorViews from './InspectorViews.vue'; | ||||
|  | ||||
|     export default { | ||||
|         inject: ['openmct'], | ||||
| @@ -223,7 +230,7 @@ | ||||
|             Elements, | ||||
|             Properties, | ||||
|             Location, | ||||
|             InspectorView | ||||
|             InspectorViews | ||||
|         }, | ||||
|         data() { | ||||
|             return { | ||||
|   | ||||
| @@ -1,37 +0,0 @@ | ||||
| <template> | ||||
|     <div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <style> | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         inject: ['openmct'], | ||||
|         mounted() { | ||||
|             this.openmct.selection.on('change', this.updateSelection); | ||||
|             this.updateSelection(); | ||||
|         }, | ||||
|         destroyed() { | ||||
|             this.openmct.selection.off('change', this.updateSelection); | ||||
|         }, | ||||
|         methods: { | ||||
|             updateSelection() { | ||||
|                 let selection = this.openmct.selection.get(); | ||||
|                 if (this.selectedView && this.selectedView.destroy) { | ||||
|                     this.selectedView.destroy(); | ||||
|                     delete this.viewContainer; | ||||
|                     this.$el.innerHTML = ''; | ||||
|                 } | ||||
|                 this.selectedView = this.openmct.inspectorViews.get(selection); | ||||
|                 if (!this.selectedView) { | ||||
|                     return; | ||||
|                 } | ||||
|                 this.viewContainer = document.createElement('div'); | ||||
|                 this.$el.append(this.viewContainer) | ||||
|                 this.selectedView.show(this.viewContainer); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
							
								
								
									
										40
									
								
								src/ui/inspector/InspectorViews.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/ui/inspector/InspectorViews.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <template> | ||||
|     <div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <style> | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         inject: ['openmct'], | ||||
|         mounted() { | ||||
|             this.openmct.selection.on('change', this.updateSelection); | ||||
|             this.updateSelection(); | ||||
|         }, | ||||
|         destroyed() { | ||||
|             this.openmct.selection.off('change', this.updateSelection); | ||||
|         }, | ||||
|         methods: { | ||||
|             updateSelection() { | ||||
|                 let selection = this.openmct.selection.get(); | ||||
|                 if (this.selectedViews) { | ||||
|                     this.selectedViews.forEach(selectedView => { | ||||
|                         selectedView.destroy(); | ||||
|                     }); | ||||
|                     this.$el.innerHTML = ''; | ||||
|                 } | ||||
|                 this.viewContainers = []; | ||||
|                 this.selectedViews = this.openmct.inspectorViews.get(selection); | ||||
|                 this.selectedViews.forEach(selectedView => { | ||||
|                     let viewContainer = document.createElement('div'); | ||||
|                     this.viewContainers.push(viewContainer); | ||||
|  | ||||
|                     this.$el.append(viewContainer) | ||||
|                     selectedView.show(viewContainer); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| @@ -42,13 +42,9 @@ define([], function () { | ||||
|      * @private for platform-internal use | ||||
|      */ | ||||
|     InspectorViewRegistry.prototype.get = function (selection) { | ||||
|         var providers = this.getAllProviders().filter(function (provider) { | ||||
|         return this.getAllProviders().filter(function (provider) { | ||||
|             return provider.canView(selection); | ||||
|         }); | ||||
|  | ||||
|         if (providers && providers.length > 0) { | ||||
|             return providers[0].view(selection); | ||||
|         } | ||||
|         }).map(provider => provider.view(selection)); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
		Reference in New Issue
	
	Block a user