mirror of
https://github.com/nasa/openmct.git
synced 2025-02-01 00:45:41 +00:00
Conditional styles (#2718)
* Hardcoded prototype - conditional styles for display layout or generator domain objects only. Needs Architectural review * Updates to ConditionalStylesView * Adds background colors list * Show conditional styles for subObjectViews and telemetryViews * Uses telemetry provider to determine which style is active Removes hardcoded conditionSet work used for prototype * Fixes failing test * Add default styles to conditionalStyles when a condition set is added to the domain object. * Use EventEmitter alias instead of eventEmitter3 in imports Change variable name for better readability and clarity Remove unused code * Change StyleRuleManager to accept conditionStyle objects instead of domainObjects * Uses a map for conditional styles instead of a list in order to improve performance * Use in-built api to check for identifier equality Pass missing arguments * Removes unnecessary object get call.
This commit is contained in:
parent
576b843bd5
commit
4675fc8ae6
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import uuid from 'uuid';
|
||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||
import { TRIGGER } from "./utils/constants";
|
||||
|
@ -22,13 +22,13 @@
|
||||
|
||||
import Condition from "./Condition";
|
||||
import uuid from "uuid";
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class ConditionManager extends EventEmitter {
|
||||
constructor(domainObject, openmct) {
|
||||
super();
|
||||
this.domainObject = domainObject;
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
this.timeAPI = this.openmct.time;
|
||||
this.latestTimestamp = {};
|
||||
this.instantiate = this.openmct.$injector.get('instantiate');
|
||||
@ -37,19 +37,17 @@ export default class ConditionManager extends EventEmitter {
|
||||
|
||||
initialize() {
|
||||
this.conditionResults = {};
|
||||
this.openmct.objects.get(this.domainObject.identifier).then((obj) => {
|
||||
this.observeForChanges(obj);
|
||||
this.conditionCollection = [];
|
||||
if (this.domainObject.configuration.conditionCollection.length) {
|
||||
this.domainObject.configuration.conditionCollection.forEach((conditionConfigurationId, index) => {
|
||||
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
|
||||
this.initCondition(conditionConfiguration, index)
|
||||
});
|
||||
this.observeForChanges(this.domainObject);
|
||||
this.conditionCollection = [];
|
||||
if (this.domainObject.configuration.conditionCollection.length) {
|
||||
this.domainObject.configuration.conditionCollection.forEach((conditionConfigurationId, index) => {
|
||||
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
|
||||
this.initCondition(conditionConfiguration, index)
|
||||
});
|
||||
} else {
|
||||
this.addCondition(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.addCondition(true);
|
||||
}
|
||||
}
|
||||
|
||||
observeForChanges(domainObject) {
|
||||
|
@ -0,0 +1,102 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2020, 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class StyleRuleManager extends EventEmitter {
|
||||
constructor(conditionalStyleConfiguration, openmct) {
|
||||
super();
|
||||
this.openmct = openmct;
|
||||
if (conditionalStyleConfiguration && conditionalStyleConfiguration.conditionSetIdentifier) {
|
||||
this.initialize(conditionalStyleConfiguration);
|
||||
this.subscribeToConditionSet();
|
||||
}
|
||||
}
|
||||
|
||||
initialize(conditionalStyleConfiguration) {
|
||||
this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier;
|
||||
this.defaultStyle = conditionalStyleConfiguration.defaultStyle;
|
||||
this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []);
|
||||
}
|
||||
|
||||
subscribeToConditionSet() {
|
||||
if (this.stopProvidingTelemetry) {
|
||||
this.stopProvidingTelemetry();
|
||||
}
|
||||
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
|
||||
});
|
||||
}
|
||||
|
||||
updateConditionalStyleConfig(conditionalStyleConfiguration) {
|
||||
if (!conditionalStyleConfiguration || !conditionalStyleConfiguration.conditionSetIdentifier) {
|
||||
this.destroy();
|
||||
} else {
|
||||
let isNewConditionSet = !this.conditionSetIdentifier ||
|
||||
this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, conditionalStyleConfiguration.conditionSetIdentifier);
|
||||
this.initialize(conditionalStyleConfiguration);
|
||||
//Only resubscribe if the conditionSet has changed.
|
||||
if (isNewConditionSet) {
|
||||
this.subscribeToConditionSet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateConditionStylesMap(conditionStyles) {
|
||||
let conditionStyleMap = {};
|
||||
conditionStyles.forEach((conditionStyle) => {
|
||||
const identifier = this.openmct.objects.makeKeyString(conditionStyle.conditionIdentifier);
|
||||
conditionStyleMap[identifier] = conditionStyle.style;
|
||||
});
|
||||
this.conditionalStyleMap = conditionStyleMap;
|
||||
}
|
||||
|
||||
handleConditionSetResultUpdated(resultData) {
|
||||
let identifier = this.openmct.objects.makeKeyString(resultData.conditionId);
|
||||
let foundStyle = this.conditionalStyleMap[identifier];
|
||||
if (foundStyle) {
|
||||
if (foundStyle !== this.currentStyle) {
|
||||
this.currentStyle = foundStyle;
|
||||
}
|
||||
} else {
|
||||
if (this.currentStyle !== this.defaultStyle) {
|
||||
this.currentStyle = this.defaultStyle;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateDomainObjectStyle();
|
||||
}
|
||||
|
||||
updateDomainObjectStyle() {
|
||||
this.emit('conditionalStyleUpdated', this.currentStyle)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.currentStyle = this.defaultStyle;
|
||||
this.updateDomainObjectStyle();
|
||||
if (this.stopProvidingTelemetry) {
|
||||
this.stopProvidingTelemetry();
|
||||
}
|
||||
this.conditionSetIdentifier = undefined;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="condition"
|
||||
class="holder c-c-button-wrapper align-left"
|
||||
>
|
||||
<div>{{ condition.configuration.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
props: {
|
||||
conditionIdentifier: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
condition: null
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.get(this.conditionIdentifier).then((conditionDomainObject) => {
|
||||
this.condition = conditionDomainObject;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,14 +1,110 @@
|
||||
<template>
|
||||
<div>Conditional Styles inspector view</div>
|
||||
<div>
|
||||
<div v-if="!conditionalStyles.length"
|
||||
class="holder c-c-button-wrapper align-left">
|
||||
<button
|
||||
class="c-c-button c-c-button--minor add-criteria-button"
|
||||
@click="addConditionSet">
|
||||
<span class="c-c-button__label">Use conditional styling</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="holder c-c-button-wrapper align-left">
|
||||
<button
|
||||
class="c-c-button c-c-button--minor add-criteria-button"
|
||||
@click="removeConditionSet">
|
||||
<span class="c-c-button__label">Remove conditional styling</span>
|
||||
</button>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="conditionStyle in conditionalStyles"
|
||||
:key="conditionStyle.conditionIdentifier.key">
|
||||
<conditional-style :condition-identifier="conditionStyle.conditionIdentifier"
|
||||
:condition-style="conditionStyle.style"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import ConditionalStyle from "./ConditionalStyle.vue";
|
||||
export default {
|
||||
components: {
|
||||
ConditionalStyle
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
]
|
||||
'openmct',
|
||||
'domainObject',
|
||||
'layoutItem'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
conditionalStyles: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.layoutItem) {
|
||||
//TODO: Handle layout items
|
||||
}
|
||||
if (this.domainObject.configuration) {
|
||||
this.defautStyle = this.domainObject.configuration.defaultStyle;
|
||||
if (this.domainObject.configuration.conditionalStyle) {
|
||||
this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || [];
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addConditionSet() {
|
||||
//TODO: this.conditionSetIdentifier will be set by the UI before calling this
|
||||
this.conditionSetIdentifier = {
|
||||
namespace: '',
|
||||
key: 'bb0f61ad-268d-4d3e-bb30-90ca4a2053c4'
|
||||
};
|
||||
this.initializeConditionalStyles();
|
||||
},
|
||||
removeConditionSet() {
|
||||
this.conditionSetIdentifier = '';
|
||||
this.conditionalStyles = [];
|
||||
this.persist(undefined);
|
||||
},
|
||||
initializeConditionalStyles() {
|
||||
const backgroundColors = [{backgroundColor: 'red'},{backgroundColor: 'orange'}, {backgroundColor: 'blue'}];
|
||||
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||
conditionSetDomainObject.configuration.conditionCollection.forEach((identifier, index) => {
|
||||
this.conditionalStyles.push({
|
||||
conditionIdentifier: identifier,
|
||||
style: backgroundColors[index]
|
||||
});
|
||||
});
|
||||
this.persist({
|
||||
defaultStyle: this.defaultStyle || {backgroundColor: 'inherit'},
|
||||
conditionSetIdentifier: this.conditionSetIdentifier,
|
||||
styles: this.conditionalStyles
|
||||
});
|
||||
});
|
||||
},
|
||||
findStyleByConditionId(id) {
|
||||
for(let i=0, ii=this.conditionalStyles.length; i < ii; i++) {
|
||||
if (this.openmct.objects.makeKeyString(this.conditionalStyles[i].conditionIdentifier) === this.openmct.objects.makeKeyString(id)) {
|
||||
return {
|
||||
index: i,
|
||||
item: this.conditionalStyles[i]
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
updateConditionalStyle(conditionIdentifier, style) {
|
||||
let found = this.findStyleByConditionId(conditionIdentifier);
|
||||
if (found) {
|
||||
this.conditionalStyles[found.index].style = style;
|
||||
}
|
||||
this.persist(undefined);
|
||||
},
|
||||
persist(conditionalStyle) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'configuration.conditionalStyle', conditionalStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import {OPERATIONS} from '../utils/operations';
|
||||
|
||||
export default class TelemetryCriterion extends EventEmitter {
|
||||
|
@ -36,6 +36,7 @@
|
||||
<div
|
||||
v-if="showLabel"
|
||||
class="c-telemetry-view__label"
|
||||
:style="conditionalStyle"
|
||||
>
|
||||
<div class="c-telemetry-view__label-text">
|
||||
{{ domainObject.name }}
|
||||
@ -47,6 +48,7 @@
|
||||
:title="fieldName"
|
||||
class="c-telemetry-view__value"
|
||||
:class="[telemetryClass]"
|
||||
:style="!telemetryClass && conditionalStyle"
|
||||
>
|
||||
<div class="c-telemetry-view__value-text">
|
||||
{{ telemetryValue }}
|
||||
@ -59,6 +61,7 @@
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import printj from 'printj'
|
||||
import StyleRuleManager from "../../condition/StyleRuleManager";
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
@ -109,7 +112,8 @@ export default {
|
||||
datum: undefined,
|
||||
formats: undefined,
|
||||
domainObject: undefined,
|
||||
currentObjectPath: undefined
|
||||
currentObjectPath: undefined,
|
||||
conditionalStyle: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -182,6 +186,16 @@ export default {
|
||||
this.removeSelectable();
|
||||
}
|
||||
|
||||
if (this.unlistenStyles) {
|
||||
this.unlistenStyles();
|
||||
}
|
||||
|
||||
if (this.styleRuleManager) {
|
||||
this.styleRuleManager.destroy();
|
||||
this.styleRuleManager.off('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
delete this.styleRuleManager;
|
||||
}
|
||||
|
||||
this.openmct.time.off("bounds", this.refreshData);
|
||||
},
|
||||
methods: {
|
||||
@ -224,6 +238,7 @@ export default {
|
||||
},
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.initConditionalStyles();
|
||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
@ -248,6 +263,21 @@ export default {
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
},
|
||||
initConditionalStyles() {
|
||||
this.styleRuleManager = new StyleRuleManager(this.domainObject.configuration.conditionalStyle, this.openmct);
|
||||
this.styleRuleManager.on('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
|
||||
if (this.unlistenStyles) {
|
||||
this.unlistenStyles();
|
||||
}
|
||||
this.unlistenStyles = this.openmct.objects.observe(this.domainObject, 'configuration.conditionalStyle', (newConditionalStyle) => {
|
||||
//Updating conditional styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||
this.styleRuleManager.updateConditionalStyleConfig(newConditionalStyle);
|
||||
});
|
||||
},
|
||||
updateStyle(styleObj) {
|
||||
this.conditionalStyle = styleObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import _ from "lodash"
|
||||
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
||||
|
||||
export default {
|
||||
inject: ["openmct"],
|
||||
@ -31,6 +32,20 @@ export default {
|
||||
if (this.releaseEditModeHandler) {
|
||||
this.releaseEditModeHandler();
|
||||
}
|
||||
|
||||
if (this.unlisten) {
|
||||
this.unlisten();
|
||||
}
|
||||
|
||||
if (this.stopListeningConditionalStyles) {
|
||||
this.stopListeningConditionalStyles();
|
||||
}
|
||||
|
||||
if (this.styleRuleManager) {
|
||||
this.styleRuleManager.destroy();
|
||||
this.styleRuleManager.off('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
delete this.styleRuleManager;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||
@ -43,6 +58,11 @@ export default {
|
||||
capture: true
|
||||
});
|
||||
this.$el.addEventListener('drop', this.addObjectToParent);
|
||||
if (this.currentObject) {
|
||||
//This is to apply styles to subobjects in a layout
|
||||
this.initConditionalStyles();
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
@ -76,6 +96,15 @@ export default {
|
||||
this.clear();
|
||||
this.updateView(true);
|
||||
},
|
||||
updateStyle(styleObj) {
|
||||
if (!styleObj) {
|
||||
return;
|
||||
}
|
||||
let keys = Object.keys(styleObj);
|
||||
keys.forEach(key => {
|
||||
this.$el.style[key] = styleObj[key];
|
||||
})
|
||||
},
|
||||
updateView(immediatelySelect) {
|
||||
this.clear();
|
||||
if (!this.currentObject) {
|
||||
@ -149,8 +178,28 @@ export default {
|
||||
});
|
||||
|
||||
this.viewKey = viewKey;
|
||||
|
||||
this.initConditionalStyles();
|
||||
|
||||
this.updateView(immediatelySelect);
|
||||
},
|
||||
initConditionalStyles() {
|
||||
if (!this.styleRuleManager) {
|
||||
this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.conditionalStyle), this.openmct);
|
||||
this.styleRuleManager.on('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
} else {
|
||||
this.styleRuleManager.updateConditionalStyleConfig(this.currentObject.configuration && this.currentObject.configuration.conditionalStyle);
|
||||
}
|
||||
|
||||
if (this.stopListeningConditionalStyles) {
|
||||
this.stopListeningConditionalStyles();
|
||||
}
|
||||
|
||||
this.stopListeningConditionalStyles = this.openmct.objects.observe(this.currentObject, 'configuration.conditionalStyle', (newConditionalStyle) => {
|
||||
//Updating conditional styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||
this.styleRuleManager.updateConditionalStyleConfig(newConditionalStyle);
|
||||
});
|
||||
},
|
||||
loadComposition() {
|
||||
return this.composition.load();
|
||||
},
|
||||
|
@ -25,27 +25,40 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
this.selection = selection;
|
||||
if (selection.length > 0 && selection[0].length > 0) {
|
||||
let domainObject = selection[0][0].context.item;
|
||||
let layoutItem;
|
||||
if (selection[0].length > 1) {
|
||||
//If there are more than 1 items in the selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
|
||||
//The second item in the selection[0] list is the container object (usually a layout)
|
||||
domainObject = selection[0][0].context.item;
|
||||
if (!domainObject) {
|
||||
layoutItem = selection[0][0].context.layoutItem;
|
||||
domainObject = selection[0][1].context.item;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.component) {
|
||||
this.component.$destroy();
|
||||
this.component = undefined;
|
||||
this.$el.innerHTML = '';
|
||||
if (this.component) {
|
||||
this.component.$destroy();
|
||||
this.component = undefined;
|
||||
this.$el.innerHTML = '';
|
||||
}
|
||||
|
||||
let viewContainer = document.createElement('div');
|
||||
this.$el.append(viewContainer);
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
openmct: this.openmct,
|
||||
domainObject: domainObject,
|
||||
layoutItem: layoutItem
|
||||
},
|
||||
el: viewContainer,
|
||||
components: {
|
||||
ConditionalStylesView
|
||||
},
|
||||
template: '<conditional-styles-view></conditional-styles-view>'
|
||||
});
|
||||
}
|
||||
|
||||
let viewContainer = document.createElement('div');
|
||||
this.$el.append(viewContainer);
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
openmct: this.openmct
|
||||
},
|
||||
el: viewContainer,
|
||||
components: {
|
||||
ConditionalStylesView
|
||||
},
|
||||
template: '<conditional-styles-view></conditional-styles-view>'
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user