Handles static and mixed styles for multiple items in a layout (#2907)
* Show non specific styles when updating multiple item styles * Save sub object styles to it's domain object * Layout UI tweak * Fixes flexible layout bug. * Fixes font size bug in telemetry view * Fixes issues with newly places TVOs including transparent properties. * Fixes #2908 * Say NO to 'transparent' === '__no_value' - Fixes #2895; * Ensure styles are correctly applied to domain objects and drawing objects when selected individually * Ensure none treatment is correctly applied to objects when multple selecting * Fix intial box border * Tweaks to c-text-view layout - Vertically center text; - Normalize padding; - Overflow: hidden; * Tweaks to Clock and Timer layout - Fixes #2893; - Vertically center text; - Normalize padding; - Overflow: hidden; - `position: absolute` when in Layout; Co-authored-by: charlesh88 <charlesh88@gmail.com> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
@@ -105,7 +105,7 @@ import ConditionDescription from "@/plugins/condition/components/ConditionDescri
|
||||
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
||||
import Vue from 'vue';
|
||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||
import {getInitialStyleForItem} from "@/plugins/condition/utils/styleUtils";
|
||||
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
|
||||
|
||||
export default {
|
||||
name: 'ConditionalStylesView',
|
||||
@@ -130,9 +130,7 @@ export default {
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.stopObserving) {
|
||||
this.stopObserving();
|
||||
}
|
||||
this.removeListeners();
|
||||
},
|
||||
mounted() {
|
||||
this.itemId = '';
|
||||
@@ -164,7 +162,8 @@ export default {
|
||||
layoutItem = this.selection[0][0].context.layoutItem;
|
||||
const item = this.selection[0][0].context.item;
|
||||
this.canHide = true;
|
||||
if (item && this.isItemType('subobject-view', layoutItem)) {
|
||||
if (item &&
|
||||
(!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
|
||||
domainObject = item;
|
||||
} else {
|
||||
domainObject = this.selection[0][1].context.item;
|
||||
@@ -176,12 +175,21 @@ export default {
|
||||
domainObject = this.selection[0][0].context.item;
|
||||
}
|
||||
this.domainObject = domainObject;
|
||||
this.initialStyles = getInitialStyleForItem(domainObject, layoutItem);
|
||||
this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
|
||||
this.$nextTick(() => {
|
||||
this.removeListeners();
|
||||
if (this.domainObject) {
|
||||
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
||||
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
||||
}
|
||||
});
|
||||
},
|
||||
removeListeners() {
|
||||
if (this.stopObserving) {
|
||||
this.stopObserving();
|
||||
}
|
||||
if (this.domainObject) {
|
||||
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
||||
if (this.stopObservingItems) {
|
||||
this.stopObservingItems();
|
||||
}
|
||||
},
|
||||
initialize(conditionSetDomainObject) {
|
||||
@@ -280,6 +288,36 @@ export default {
|
||||
|
||||
this.persist(domainObjectStyles);
|
||||
},
|
||||
updateDomainObjectItemStyles(newItems) {
|
||||
//check that all items that have been styles still exist. Otherwise delete those styles
|
||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||
let itemsToRemove = [];
|
||||
let keys = Object.keys(domainObjectStyles);
|
||||
keys.forEach((key) => {
|
||||
if ((key !== 'styles') &&
|
||||
(key !== 'staticStyle') &&
|
||||
(key !== 'conditionSetIdentifier')) {
|
||||
if (!(newItems.find(item => item.id === key))) {
|
||||
itemsToRemove.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (itemsToRemove.length) {
|
||||
this.removeItemStyles(itemsToRemove, domainObjectStyles);
|
||||
}
|
||||
},
|
||||
removeItemStyles(itemIds, domainObjectStyles) {
|
||||
itemIds.forEach(itemId => {
|
||||
if (domainObjectStyles[itemId]) {
|
||||
domainObjectStyles[itemId] = undefined;
|
||||
delete domainObjectStyles[this.itemId];
|
||||
}
|
||||
});
|
||||
if (_.isEmpty(domainObjectStyles)) {
|
||||
domainObjectStyles = undefined;
|
||||
}
|
||||
this.persist(domainObjectStyles);
|
||||
},
|
||||
initializeConditionalStyles() {
|
||||
if (!this.conditions) {
|
||||
this.conditions = {};
|
||||
@@ -305,9 +343,15 @@ export default {
|
||||
},
|
||||
initializeStaticStyle(objectStyles) {
|
||||
let staticStyle = objectStyles && objectStyles.staticStyle;
|
||||
this.staticStyle = staticStyle || {
|
||||
style: Object.assign({}, this.initialStyles)
|
||||
};
|
||||
if (staticStyle) {
|
||||
this.staticStyle = {
|
||||
style: Object.assign({}, this.initialStyles, staticStyle.style)
|
||||
};
|
||||
} else {
|
||||
this.staticStyle = {
|
||||
style: Object.assign({}, this.initialStyles)
|
||||
};
|
||||
}
|
||||
},
|
||||
findStyleByConditionId(id) {
|
||||
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-inspector__styles c-inspect-styles">
|
||||
<div class="c-inspect-styles__header">
|
||||
Object Style
|
||||
</div>
|
||||
<div class="c-inspect-styles__content">
|
||||
<div v-if="isStaticAndConditionalStyles"
|
||||
class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
|
||||
>
|
||||
Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
|
||||
</div>
|
||||
<div v-if="staticStyle"
|
||||
class="c-inspect-styles__style"
|
||||
>
|
||||
<style-editor class="c-inspect-styles__editor"
|
||||
:style-item="staticStyle"
|
||||
:is-editing="isEditing"
|
||||
:mixed-styles="mixedStyles"
|
||||
@persist="updateStaticStyle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import StyleEditor from "./StyleEditor.vue";
|
||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
|
||||
|
||||
export default {
|
||||
name: 'MultiSelectStylesView',
|
||||
components: {
|
||||
StyleEditor
|
||||
},
|
||||
inject: [
|
||||
'openmct',
|
||||
'selection'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
staticStyle: undefined,
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
mixedStyles: [],
|
||||
isStaticAndConditionalStyles: false
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.removeListeners();
|
||||
},
|
||||
mounted() {
|
||||
this.items = [];
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.getObjectsAndItemsFromSelection();
|
||||
this.initializeStaticStyle();
|
||||
this.openmct.editor.on('isEditing', this.setEditState);
|
||||
},
|
||||
methods: {
|
||||
isItemType(type, item) {
|
||||
return item && (item.type === type);
|
||||
},
|
||||
hasConditionalStyles(domainObject, id) {
|
||||
return getConditionalStyleForItem(domainObject, id) !== undefined;
|
||||
},
|
||||
getObjectsAndItemsFromSelection() {
|
||||
let domainObject;
|
||||
let subObjects = [];
|
||||
|
||||
//multiple selection
|
||||
let itemInitialStyles = [];
|
||||
let itemStyle;
|
||||
this.selection.forEach((selectionItem) => {
|
||||
const item = selectionItem[0].context.item;
|
||||
const layoutItem = selectionItem[0].context.layoutItem;
|
||||
if (item && this.isItemType('subobject-view', layoutItem)) {
|
||||
subObjects.push(item);
|
||||
itemStyle = getApplicableStylesForItem(item);
|
||||
if (!this.isStaticAndConditionalStyles) {
|
||||
this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
|
||||
}
|
||||
} else {
|
||||
domainObject = selectionItem[1].context.item;
|
||||
itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
|
||||
this.items.push({
|
||||
id: layoutItem.id,
|
||||
applicableStyles: itemStyle
|
||||
});
|
||||
if (!this.isStaticAndConditionalStyles) {
|
||||
this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
|
||||
}
|
||||
}
|
||||
itemInitialStyles.push(itemStyle);
|
||||
});
|
||||
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
|
||||
this.initialStyles = styles;
|
||||
this.mixedStyles = mixedStyles;
|
||||
|
||||
this.domainObject = domainObject;
|
||||
this.removeListeners();
|
||||
if (this.domainObject) {
|
||||
this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
|
||||
this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
|
||||
}
|
||||
|
||||
subObjects.forEach(this.registerListener);
|
||||
},
|
||||
updateDomainObjectItemStyles(newItems) {
|
||||
//check that all items that have been styles still exist. Otherwise delete those styles
|
||||
let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
|
||||
keys.forEach((key) => {
|
||||
if ((key !== 'styles') &&
|
||||
(key !== 'staticStyle') &&
|
||||
(key !== 'conditionSetIdentifier')) {
|
||||
if (!(newItems.find(item => item.id === key))) {
|
||||
this.removeItemStyles(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
registerListener(domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!this.domainObjectsById) {
|
||||
this.domainObjectsById = {};
|
||||
}
|
||||
|
||||
if (!this.domainObjectsById[id]) {
|
||||
this.domainObjectsById[id] = domainObject;
|
||||
this.observeObject(domainObject, id);
|
||||
}
|
||||
},
|
||||
observeObject(domainObject, id) {
|
||||
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
|
||||
this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
|
||||
}.bind(this));
|
||||
this.unObserveObjects.push(unobserveObject);
|
||||
},
|
||||
removeListeners() {
|
||||
if (this.stopObserving) {
|
||||
this.stopObserving();
|
||||
}
|
||||
if (this.stopObservingItems) {
|
||||
this.stopObservingItems();
|
||||
}
|
||||
if (this.unObserveObjects) {
|
||||
this.unObserveObjects.forEach((unObserveObject) => {
|
||||
unObserveObject();
|
||||
});
|
||||
}
|
||||
this.unObserveObjects = [];
|
||||
},
|
||||
removeItemStyles(itemId) {
|
||||
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||
if (itemId && domainObjectStyles[itemId]) {
|
||||
domainObjectStyles[itemId] = undefined;
|
||||
delete domainObjectStyles[this.itemId];
|
||||
|
||||
if (_.isEmpty(domainObjectStyles)) {
|
||||
domainObjectStyles = undefined;
|
||||
}
|
||||
this.persist(this.domainObject, domainObjectStyles);
|
||||
}
|
||||
},
|
||||
removeConditionalStyles(domainObjectStyles, itemId) {
|
||||
if (itemId) {
|
||||
domainObjectStyles[itemId].conditionSetIdentifier = undefined;
|
||||
delete domainObjectStyles[itemId].conditionSetIdentifier;
|
||||
domainObjectStyles[itemId].styles = undefined;
|
||||
delete domainObjectStyles[itemId].styles;
|
||||
} else {
|
||||
domainObjectStyles.conditionSetIdentifier = undefined;
|
||||
delete domainObjectStyles.conditionSetIdentifier;
|
||||
domainObjectStyles.styles = undefined;
|
||||
delete domainObjectStyles.styles;
|
||||
}
|
||||
},
|
||||
setEditState(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
initializeStaticStyle() {
|
||||
this.staticStyle = {
|
||||
style: Object.assign({}, this.initialStyles)
|
||||
};
|
||||
},
|
||||
updateStaticStyle(staticStyle, property) {
|
||||
//update the static style for each of the layoutItems as well as each sub object item
|
||||
this.staticStyle = staticStyle;
|
||||
this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
|
||||
if (this.domainObjectsById) {
|
||||
const keys = Object.keys(this.domainObjectsById);
|
||||
keys.forEach(key => {
|
||||
let domainObject = this.domainObjectsById[key];
|
||||
this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
|
||||
});
|
||||
}
|
||||
this.isStaticAndConditionalStyles = false;
|
||||
let foundIndex = this.mixedStyles.indexOf(property);
|
||||
if (foundIndex > -1) {
|
||||
this.mixedStyles.splice(foundIndex, 1);
|
||||
}
|
||||
},
|
||||
getDomainObjectStyle(domainObject, property, items) {
|
||||
let domainObjectStyles = (domainObject.configuration && domainObject.configuration.objectStyles) || {};
|
||||
|
||||
if (items) {
|
||||
items.forEach(item => {
|
||||
let itemStaticStyle = {};
|
||||
if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
|
||||
itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
|
||||
}
|
||||
Object.keys(item.applicableStyles).forEach(key => {
|
||||
if (property === key) {
|
||||
itemStaticStyle[key] = this.staticStyle.style[key];
|
||||
}
|
||||
});
|
||||
if (this.isStaticAndConditionalStyles) {
|
||||
this.removeConditionalStyles(domainObjectStyles, item.id);
|
||||
}
|
||||
if (_.isEmpty(itemStaticStyle)) {
|
||||
itemStaticStyle = undefined;
|
||||
domainObjectStyles[item.id] = undefined;
|
||||
} else {
|
||||
domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!domainObjectStyles.staticStyle) {
|
||||
domainObjectStyles.staticStyle = {
|
||||
style: {}
|
||||
}
|
||||
}
|
||||
if (this.isStaticAndConditionalStyles) {
|
||||
this.removeConditionalStyles(domainObjectStyles);
|
||||
}
|
||||
domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
|
||||
}
|
||||
|
||||
return domainObjectStyles;
|
||||
},
|
||||
|
||||
persist(domainObject, style) {
|
||||
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -22,12 +22,15 @@
|
||||
|
||||
<template>
|
||||
<div class="c-style">
|
||||
<span class="c-style-thumb"
|
||||
:class="{ 'is-style-invisible': styleItem.style.isStyleInvisible }"
|
||||
<span :class="[
|
||||
{ 'is-style-invisible': styleItem.style.isStyleInvisible },
|
||||
{ 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
|
||||
]"
|
||||
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
|
||||
class="c-style-thumb"
|
||||
>
|
||||
<span class="c-style-thumb__text"
|
||||
:class="{ 'hide-nice': !styleItem.style.color }"
|
||||
:class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
|
||||
>
|
||||
ABC
|
||||
</span>
|
||||
@@ -68,6 +71,7 @@ import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue
|
||||
import ToolbarButton from "@/ui/toolbar/components/toolbar-button.vue";
|
||||
import ToolbarToggleButton from "@/ui/toolbar/components/toolbar-toggle-button.vue";
|
||||
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
||||
import {getStylesWithoutNoneValue} from "@/plugins/condition/utils/styleUtils";
|
||||
|
||||
export default {
|
||||
name: 'StyleEditor',
|
||||
@@ -83,6 +87,12 @@ export default {
|
||||
isEditing: {
|
||||
type: Boolean
|
||||
},
|
||||
mixedStyles: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
styleItem: {
|
||||
type: Object,
|
||||
required: true
|
||||
@@ -90,21 +100,17 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
itemStyle() {
|
||||
let style = {};
|
||||
const keys = Object.keys(this.styleItem.style);
|
||||
keys.forEach(key => {
|
||||
style[key] = this.normalizeValue(this.styleItem.style[key]);
|
||||
});
|
||||
return style;
|
||||
return getStylesWithoutNoneValue(this.styleItem.style);
|
||||
},
|
||||
borderColorOption() {
|
||||
let value = this.styleItem.style.border.replace('1px solid ', '');
|
||||
return {
|
||||
icon: 'icon-line-horz',
|
||||
title: STYLE_CONSTANTS.borderColorTitle,
|
||||
value: this.normalizeValue(value),
|
||||
value: this.normalizeValueForSwatch(value),
|
||||
property: 'border',
|
||||
isEditing: this.isEditing
|
||||
isEditing: this.isEditing,
|
||||
nonSpecific: this.mixedStyles.indexOf('border') > -1
|
||||
}
|
||||
},
|
||||
backgroundColorOption() {
|
||||
@@ -112,9 +118,10 @@ export default {
|
||||
return {
|
||||
icon: 'icon-paint-bucket',
|
||||
title: STYLE_CONSTANTS.backgroundColorTitle,
|
||||
value: this.normalizeValue(value),
|
||||
value: this.normalizeValueForSwatch(value),
|
||||
property: 'backgroundColor',
|
||||
isEditing: this.isEditing
|
||||
isEditing: this.isEditing,
|
||||
nonSpecific: this.mixedStyles.indexOf('backgroundColor') > -1
|
||||
}
|
||||
},
|
||||
colorOption() {
|
||||
@@ -122,9 +129,10 @@ export default {
|
||||
return {
|
||||
icon: 'icon-font',
|
||||
title: STYLE_CONSTANTS.textColorTitle,
|
||||
value: this.normalizeValue(value),
|
||||
value: this.normalizeValueForSwatch(value),
|
||||
property: 'color',
|
||||
isEditing: this.isEditing
|
||||
isEditing: this.isEditing,
|
||||
nonSpecific: this.mixedStyles.indexOf('color') > -1
|
||||
}
|
||||
},
|
||||
imageUrlOption() {
|
||||
@@ -149,7 +157,8 @@ export default {
|
||||
property: 'imageUrl',
|
||||
formKeys: ['url'],
|
||||
value: {url: this.styleItem.style.imageUrl},
|
||||
isEditing: this.isEditing
|
||||
isEditing: this.isEditing,
|
||||
nonSpecific: this.mixedStyles.indexOf('imageUrl') > -1
|
||||
}
|
||||
},
|
||||
isStyleInvisibleOption() {
|
||||
@@ -177,13 +186,20 @@ export default {
|
||||
hasProperty(property) {
|
||||
return property !== undefined;
|
||||
},
|
||||
normalizeValue(value) {
|
||||
if (!value) {
|
||||
return 'transparent';
|
||||
normalizeValueForSwatch(value) {
|
||||
if (value && value.indexOf('__no_value') > -1) {
|
||||
return value.replace('__no_value', 'transparent');
|
||||
}
|
||||
return value;
|
||||
},
|
||||
normalizeValueForStyle(value) {
|
||||
if (value && value === 'transparent') {
|
||||
return '__no_value';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
updateStyleValue(value, item) {
|
||||
value = this.normalizeValueForStyle(value);
|
||||
if (item.property === 'border') {
|
||||
value = '1px solid ' + value;
|
||||
}
|
||||
@@ -192,7 +208,7 @@ export default {
|
||||
} else {
|
||||
this.styleItem.style[item.property] = value;
|
||||
}
|
||||
this.$emit('persist', this.styleItem);
|
||||
this.$emit('persist', this.styleItem, item.property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user