mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 02:29:24 +00:00
Compare commits
46 Commits
code-cover
...
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