Conditional styles for drawing objects (#2740)

* Hardcoded prototype - conditional styles for display layout or generator domain objects only.
* 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
* Add default styles to conditionalStyles when a condition set is added to the domain object.
* Use EventEmitter alias instead of eventEmitter3 in imports
* 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
* Removes unnecessary object get call.
* Adds conditional styles for drawing objects
* Removes hard coded conditionSetIdentifier
* Fixes small conditionManager bug
This commit is contained in:
Shefali Joshi 2020-03-17 14:42:15 -07:00 committed by GitHub
parent fe2e29d69b
commit 43a82ec05f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 266 additions and 65 deletions

View File

@ -135,18 +135,7 @@ export default class ConditionManager extends EventEmitter {
} }
findConditionById(id) { findConditionById(id) {
let found; return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
for (let i=0, ii=this.conditionClassCollection.length; i < ii; i++) {
if (this.conditionClassCollection[i].id === id) {
found = {
item: this.conditionClassCollection[i],
index: i
};
break;
}
}
return found;
} }
//this.$set(this.conditionClassCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]); //this.$set(this.conditionClassCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);

View File

@ -34,7 +34,6 @@ export default class StyleRuleManager extends EventEmitter {
initialize(conditionalStyleConfiguration) { initialize(conditionalStyleConfiguration) {
this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier; this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier;
this.defaultStyle = conditionalStyleConfiguration.defaultStyle;
this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []); this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []);
} }
@ -89,7 +88,11 @@ export default class StyleRuleManager extends EventEmitter {
} }
destroy() { destroy() {
this.currentStyle = this.defaultStyle; if (this.currentStyle) {
Object.keys(this.currentStyle).forEach(key => {
this.currentStyle[key] = 'inherit';
});
}
this.updateDomainObjectStyle(); this.updateDomainObjectStyle();
if (this.stopProvidingTelemetry) { if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry(); this.stopProvidingTelemetry();

View File

@ -1,28 +1,69 @@
<template> <template>
<div> <div>
<div v-if="conditionName" <div v-if="conditionStyle"
class="holder c-c-button-wrapper align-left" class="holder c-c-button-wrapper align-left"
> >
<div>{{ conditionName }}</div> <div>{{ conditionStyle.conditionName }}</div>
<span :style="conditionStyle.style">ABC</span>
<toolbar-color-picker v-if="conditionStyle.style.border"
:options="borderColorOption"
@change="updateStyleValue"
/>
<toolbar-color-picker v-if="conditionStyle.style.backgroundColor"
:options="backgroundColorOption"
@change="updateStyleValue"
/>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue";
export default { export default {
components: { components: {
ToolbarColorPicker
}, },
inject: [ inject: [
'openmct' 'openmct'
], ],
props: { props: {
conditionName: { conditionStyle: {
type: String, type: Object,
required: true required: true
} }
}, },
destroyed() { data() {
return {
condition: null
}
},
computed: {
borderColorOption() {
return {
icon: 'icon-line-horz',
title: 'Set border color',
value: this.conditionStyle.style.border.replace('1px solid ', ''),
property: 'border'
}
},
backgroundColorOption() {
return {
icon: 'icon-paint-bucket',
title: 'Set background color',
value: this.conditionStyle.style.backgroundColor,
property: 'backgroundColor'
}
}
},
methods: {
updateStyleValue(value, item) {
if (item.property === 'border') {
value = '1px solid ' + value;
}
this.conditionStyle.style[item.property] = value;
this.$emit('persist', this.conditionStyle)
}
} }
} }
</script> </script>

View File

@ -23,8 +23,8 @@
<li v-for="conditionStyle in conditionalStyles" <li v-for="conditionStyle in conditionalStyles"
:key="conditionStyle.conditionId" :key="conditionStyle.conditionId"
> >
<conditional-style :condition-name="conditionStyle.conditionName" <conditional-style :condition-style="conditionStyle"
:condition-style="conditionStyle.style" @persist="updateConditionalStyle"
/> />
</li> </li>
</ul> </ul>
@ -41,21 +41,33 @@ export default {
}, },
inject: [ inject: [
'openmct', 'openmct',
'domainObject', 'domainObject'
'layoutItem'
], ],
props: {
itemId: {
type: String,
default: ''
},
initialStyles: {
type: Object,
default() {
return undefined;
}
}
},
data() { data() {
return { return {
conditionalStyles: [] conditionalStyles: []
} }
}, },
mounted() { mounted() {
if (this.layoutItem) { if (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) {
//TODO: Handle layout items if (this.itemId) {
} let conditionalStyle = this.domainObject.configuration.conditionalStyle[this.itemId];
if (this.domainObject.configuration) { if (conditionalStyle) {
this.defautStyle = this.domainObject.configuration.defaultStyle; this.conditionalStyles = conditionalStyle.styles || [];
if (this.domainObject.configuration.conditionalStyle) { }
} else {
this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || []; this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || [];
} }
} }
@ -65,49 +77,86 @@ export default {
//TODO: this.conditionSetIdentifier will be set by the UI before calling this //TODO: this.conditionSetIdentifier will be set by the UI before calling this
this.conditionSetIdentifier = { this.conditionSetIdentifier = {
namespace: '', namespace: '',
key: 'bb0f61ad-268d-4d3e-bb30-90ca4a2053c4' key: "81088c8a-4b80-41fe-9d07-fda8b22d6f5f"
}; };
this.initializeConditionalStyles(); this.initializeConditionalStyles();
}, },
removeConditionSet() { removeConditionSet() {
//TODO: Handle the case where domainObject has items with styles but we're trying to remove the styles on the domainObject itself
this.conditionSetIdentifier = ''; this.conditionSetIdentifier = '';
this.conditionalStyles = []; this.conditionalStyles = [];
this.persist(undefined); let domainObjectConditionalStyle = (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) || {};
if (domainObjectConditionalStyle) {
if (this.itemId) {
domainObjectConditionalStyle[this.itemId] = undefined;
delete domainObjectConditionalStyle[this.itemId];
} else {
domainObjectConditionalStyle.conditionSetIdentifier = undefined;
delete domainObjectConditionalStyle.conditionSetIdentifier;
domainObjectConditionalStyle.styles = undefined;
delete domainObjectConditionalStyle.styles;
}
if (_.isEmpty(domainObjectConditionalStyle)) {
domainObjectConditionalStyle = undefined;
}
}
this.persist(domainObjectConditionalStyle);
}, },
initializeConditionalStyles() { initializeConditionalStyles() {
const backgroundColors = [{backgroundColor: 'red'},{backgroundColor: 'orange'}, {backgroundColor: 'blue'}];
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => { this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => { conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
this.conditionalStyles.push({ this.conditionalStyles.push({
conditionId: conditionConfiguration.id, conditionId: conditionConfiguration.id,
conditionName: conditionConfiguration.name, conditionName: conditionConfiguration.name,
style: backgroundColors[index] style: Object.assign({}, this.initialStyles)
}); });
}); });
this.persist({ let domainObjectConditionalStyle = (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) || {};
defaultStyle: this.defaultStyle || {backgroundColor: 'inherit'}, let conditionalStyle = {
conditionSetIdentifier: this.conditionSetIdentifier, conditionSetIdentifier: this.conditionSetIdentifier,
styles: this.conditionalStyles styles: this.conditionalStyles
}); };
if (this.itemId) {
this.persist({
...domainObjectConditionalStyle,
[this.itemId]: conditionalStyle
});
} else {
this.persist({
...domainObjectConditionalStyle,
...conditionalStyle
});
}
}); });
}, },
findStyleByConditionId(id) { findStyleByConditionId(id) {
for(let i=0, ii=this.conditionalStyles.length; i < ii; i++) { return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
if (this.conditionalStyles[i].conditionId === id) { },
return { updateConditionalStyle(conditionStyle) {
index: i, let found = this.findStyleByConditionId(conditionStyle.conditionId);
item: this.conditionalStyles[i] if (found) {
}; found.style = conditionStyle.style;
let domainObjectConditionalStyle = this.domainObject.configuration.conditionalStyle || {};
if (this.itemId) {
let itemConditionalStyle = domainObjectConditionalStyle[this.itemId];
if (itemConditionalStyle) {
this.persist({
...domainObjectConditionalStyle,
[this.itemId]: {
...itemConditionalStyle,
styles: this.conditionalStyles
}
});
}
} else {
domainObjectConditionalStyle.styles = this.conditionalStyles;
this.persist(domainObjectConditionalStyle);
} }
} }
}, },
updateConditionalStyle(conditionId, style) {
let found = this.findStyleByConditionId(conditionId);
if (found) {
this.conditionalStyles[found.index].style = style;
}
this.persist(undefined);
},
persist(conditionalStyle) { persist(conditionalStyle) {
this.openmct.objects.mutate(this.domainObject, 'configuration.conditionalStyle', conditionalStyle); this.openmct.objects.mutate(this.domainObject, 'configuration.conditionalStyle', conditionalStyle);
} }

View File

@ -0,0 +1,19 @@
export const getStyleProp = (key, defaultValue) => {
let styleProp = undefined;
switch(key) {
case 'fill': styleProp = {
backgroundColor: defaultValue || 'none'
};
break;
case 'stroke': styleProp = {
border: '1px solid ' + defaultValue || 'none'
};
break;
case 'color': styleProp = {
color: defaultValue || 'inherit'
};
break;
}
return styleProp;
};

View File

@ -36,6 +36,7 @@
<script> <script>
import LayoutFrame from './LayoutFrame.vue' import LayoutFrame from './LayoutFrame.vue'
import conditionalStylesMixin from '../mixins/conditionalStyles-mixin';
export default { export default {
makeDefinition() { makeDefinition() {
@ -52,6 +53,7 @@ export default {
components: { components: {
LayoutFrame LayoutFrame
}, },
mixins: [conditionalStylesMixin],
props: { props: {
item: { item: {
type: Object, type: Object,
@ -71,10 +73,14 @@ export default {
}, },
computed: { computed: {
style() { style() {
return { if (this.itemStyle) {
backgroundColor: this.item.fill, return this.itemStyle;
border: '1px solid ' + this.item.stroke } else {
}; return {
backgroundColor: this.item.fill,
border: '1px solid ' + this.item.stroke
};
}
} }
}, },
watch: { watch: {

View File

@ -36,6 +36,7 @@
<script> <script>
import LayoutFrame from './LayoutFrame.vue' import LayoutFrame from './LayoutFrame.vue'
import conditionalStylesMixin from "../mixins/conditionalStyles-mixin";
export default { export default {
makeDefinition(openmct, gridSize, element) { makeDefinition(openmct, gridSize, element) {
@ -52,6 +53,7 @@ export default {
components: { components: {
LayoutFrame LayoutFrame
}, },
mixins: [conditionalStylesMixin],
props: { props: {
item: { item: {
type: Object, type: Object,
@ -73,7 +75,7 @@ export default {
style() { style() {
return { return {
backgroundImage: 'url(' + this.item.url + ')', backgroundImage: 'url(' + this.item.url + ')',
border: '1px solid ' + this.item.stroke border: this.itemStyle ? this.itemStyle.border : '1px solid ' + this.item.stroke
} }
} }
}, },

View File

@ -31,7 +31,7 @@
> >
<line <line
v-bind="linePosition" v-bind="linePosition"
:stroke="item.stroke" :stroke="stroke"
stroke-width="2" stroke-width="2"
/> />
</svg> </svg>
@ -60,6 +60,8 @@
<script> <script>
import conditionalStylesMixin from "../mixins/conditionalStyles-mixin";
const START_HANDLE_QUADRANTS = { const START_HANDLE_QUADRANTS = {
1: 'c-frame-edit__handle--sw', 1: 'c-frame-edit__handle--sw',
2: 'c-frame-edit__handle--se', 2: 'c-frame-edit__handle--se',
@ -85,6 +87,7 @@ export default {
}; };
}, },
inject: ['openmct'], inject: ['openmct'],
mixins: [conditionalStylesMixin],
props: { props: {
item: { item: {
type: Object, type: Object,
@ -122,6 +125,13 @@ export default {
} }
return {x, y, x2, y2}; return {x, y, x2, y2};
}, },
stroke() {
if (this.itemStyle && this.itemStyle.border) {
return this.itemStyle.border.replace('1px solid ', '');
} else {
return this.item.stroke;
}
},
style() { style() {
let {x, y, x2, y2} = this.position; let {x, y, x2, y2} = this.position;
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1); let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);

View File

@ -38,6 +38,7 @@
<script> <script>
import LayoutFrame from './LayoutFrame.vue' import LayoutFrame from './LayoutFrame.vue'
import conditionalStylesMixin from "../mixins/conditionalStyles-mixin";
export default { export default {
makeDefinition(openmct, gridSize, element) { makeDefinition(openmct, gridSize, element) {
@ -57,6 +58,7 @@ export default {
components: { components: {
LayoutFrame LayoutFrame
}, },
mixins: [conditionalStylesMixin],
props: { props: {
item: { item: {
type: Object, type: Object,
@ -76,12 +78,16 @@ export default {
}, },
computed: { computed: {
style() { style() {
return { if (this.itemStyle) {
backgroundColor: this.item.fill, return this.itemStyle;
borderColor: this.item.stroke, } else {
color: this.item.color, return {
fontSize: this.item.size backgroundColor: this.item.fill,
}; borderColor: this.item.stroke,
color: this.item.color,
fontSize: this.item.size
};
}
} }
}, },
watch: { watch: {

View File

@ -0,0 +1,54 @@
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
export default {
inject: ['openmct'],
data() {
return {
itemStyle: this.itemStyle
}
},
mounted() {
this.domainObject = this.$parent.domainObject;
this.itemId = this.item.id;
this.conditionalStyle = this.getConditionalStyleForItem(this.domainObject.configuration.conditionalStyle);
this.initConditionalStyles();
},
destroyed() {
if (this.stopListeningConditionalStyles) {
this.stopListeningConditionalStyles();
}
},
methods: {
getConditionalStyleForItem(conditionalStyle) {
if (conditionalStyle) {
return conditionalStyle[this.itemId];
} else {
return undefined;
}
},
initConditionalStyles() {
if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager(this.conditionalStyle, this.openmct);
this.styleRuleManager.on('conditionalStyleUpdated', this.updateStyle.bind(this));
} else {
this.styleRuleManager.updateConditionalStyleConfig(this.conditionalStyle);
}
if (this.stopListeningConditionalStyles) {
this.stopListeningConditionalStyles();
}
this.stopListeningConditionalStyles = 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
let newItemConditionalStyle = this.getConditionalStyleForItem(newConditionalStyle);
if (this.conditionalStyle !== newItemConditionalStyle) {
this.conditionalStyle = newItemConditionalStyle;
this.styleRuleManager.updateConditionalStyleConfig(this.conditionalStyle);
}
});
},
updateStyle(style) {
this.itemStyle = style;
}
}
};

View File

@ -154,6 +154,8 @@ export default {
this.openmct.objectViews.on('clearData', this.clearData); this.openmct.objectViews.on('clearData', this.clearData);
}, },
show(object, viewKey, immediatelySelect, currentObjectPath) { show(object, viewKey, immediatelySelect, currentObjectPath) {
this.updateStyle();
if (this.unlisten) { if (this.unlisten) {
this.unlisten(); this.unlisten();
} }

View File

@ -8,6 +8,7 @@
<script> <script>
import ConditionalStylesView from '../../plugins/condition/components/inspector/ConditionalStylesView.vue'; import ConditionalStylesView from '../../plugins/condition/components/inspector/ConditionalStylesView.vue';
import Vue from 'vue'; import Vue from 'vue';
import { getStyleProp } from "../../plugins/condition/utils/styleUtils";
export default { export default {
inject: ['openmct'], inject: ['openmct'],
@ -24,16 +25,30 @@ export default {
this.openmct.selection.off('change', this.updateSelection); this.openmct.selection.off('change', this.updateSelection);
}, },
methods: { methods: {
getStyleProperties(item) {
let styleProps = {};
Object.keys(item).forEach((key) => {
Object.assign(styleProps, getStyleProp(key, item[key]));
});
return styleProps;
},
updateSelection(selection) { updateSelection(selection) {
if (selection.length > 0 && selection[0].length > 0) { if (selection.length > 0 && selection[0].length > 0) {
let domainObject = selection[0][0].context.item; let domainObject = selection[0][0].context.item;
let layoutItem; let layoutItem = {};
let styleProps = this.getStyleProperties({
fill: 'inherit',
stroke: 'inherit',
color: 'inherit'
});
if (selection[0].length > 1) { 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. //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) //The second item in the selection[0] list is the container object (usually a layout)
domainObject = selection[0][0].context.item; domainObject = selection[0][0].context.item;
if (!domainObject) { if (!domainObject) {
styleProps = {};
layoutItem = selection[0][0].context.layoutItem; layoutItem = selection[0][0].context.layoutItem;
styleProps = this.getStyleProperties(layoutItem);
domainObject = selection[0][1].context.item; domainObject = selection[0][1].context.item;
} }
} }
@ -49,14 +64,19 @@ export default {
this.component = new Vue({ this.component = new Vue({
provide: { provide: {
openmct: this.openmct, openmct: this.openmct,
domainObject: domainObject, domainObject: domainObject
layoutItem: layoutItem
}, },
el: viewContainer, el: viewContainer,
components: { components: {
ConditionalStylesView ConditionalStylesView
}, },
template: '<conditional-styles-view></conditional-styles-view>' data() {
return {
layoutItem,
styleProps
}
},
template: '<conditional-styles-view :item-id="layoutItem.id" :initial-styles="styleProps"></conditional-styles-view>'
}); });
} }
} }