Compare commits
9 Commits
openmct-st
...
version-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a4ff80f96 | ||
|
|
77b720d00d | ||
|
|
ba982671b2 | ||
|
|
5df7d92d64 | ||
|
|
a8228406de | ||
|
|
2401473012 | ||
|
|
e502fb88fa | ||
|
|
37a52cb011 | ||
|
|
04fb4e8a82 |
@@ -76,6 +76,7 @@ define([
|
||||
|
||||
workerRequest[prop] = Number(workerRequest[prop]);
|
||||
});
|
||||
|
||||
workerRequest.name = domainObject.name;
|
||||
|
||||
return workerRequest;
|
||||
|
||||
@@ -108,7 +108,6 @@
|
||||
|
||||
for (; nextStep < end && data.length < 5000; nextStep += step) {
|
||||
data.push({
|
||||
name: request.name,
|
||||
utc: nextStep,
|
||||
yesterday: nextStep - 60 * 60 * 24 * 1000,
|
||||
sin: sin(nextStep, period, amplitude, offset, phase, randomness),
|
||||
|
||||
32
index.html
32
index.html
@@ -30,6 +30,38 @@
|
||||
<link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96" type="image/x-icon">
|
||||
<link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32" type="image/x-icon">
|
||||
<link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16" type="image/x-icon">
|
||||
<style type="text/css">
|
||||
@keyframes splash-spinner {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) rotate(0deg); }
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(360deg); } }
|
||||
|
||||
#splash-screen {
|
||||
background-color: black;
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
#splash-screen:before {
|
||||
animation-name: splash-spinner;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
border-radius: 50%;
|
||||
border-color: rgba(255,255,255,0.25);
|
||||
border-top-color: white;
|
||||
border-style: solid;
|
||||
border-width: 10px;
|
||||
content: '';
|
||||
display: block;
|
||||
opacity: 0.25;
|
||||
position: absolute;
|
||||
left: 50%; top: 50%;
|
||||
height: 100px; width: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "1.3.3-SNAPSHOT",
|
||||
"version": "1.4.0",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="c-clock l-time-display" ng-controller="ClockController as clock">
|
||||
<div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
|
||||
<div class="c-clock__timezone">
|
||||
{{clock.zone()}}
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="c-timer is-{{timer.timerState}}" ng-controller="TimerController as timer">
|
||||
<div class="c-timer u-style-receiver js-style-receiver is-{{timer.timerState}}" ng-controller="TimerController as timer">
|
||||
<div class="c-timer__controls">
|
||||
<button ng-click="timer.clickStopButton()"
|
||||
ng-hide="timer.timerState == 'stopped'"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-lad-table-wrapper">
|
||||
<div class="c-lad-table-wrapper u-style-receiver js-style-receiver">
|
||||
<table class="c-table c-lad-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
.c-cs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
@@ -21,21 +21,22 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-style">
|
||||
<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': !hasProperty(styleItem.style.color) }"
|
||||
<div class="c-style has-local-controls c-toolbar">
|
||||
<div class="c-style__controls">
|
||||
<div :class="[
|
||||
{ 'is-style-invisible': styleItem.style && 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"
|
||||
>
|
||||
ABC
|
||||
</span>
|
||||
</span>
|
||||
<span class="c-toolbar">
|
||||
<span class="c-style-thumb__text"
|
||||
:class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
|
||||
>
|
||||
ABC
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<toolbar-color-picker v-if="hasProperty(styleItem.style.border)"
|
||||
class="c-style__toolbar-button--border-color u-menu-to--center"
|
||||
:options="borderColorOption"
|
||||
@@ -61,7 +62,14 @@
|
||||
:options="isStyleInvisibleOption"
|
||||
@change="updateStyleValue"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Save Styles -->
|
||||
<toolbar-button v-if="canSaveStyle"
|
||||
class="c-style__toolbar-button--save c-local-controls--show-on-hover c-icon-button c-icon-button--major"
|
||||
:options="saveOptions"
|
||||
@click="saveItemStyle()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -80,12 +88,11 @@ export default {
|
||||
ToolbarColorPicker,
|
||||
ToolbarToggleButton
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
isEditing: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
mixedStyles: {
|
||||
type: Array,
|
||||
@@ -93,6 +100,10 @@ export default {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
nonSpecificFontProperties: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
styleItem: {
|
||||
type: Object,
|
||||
required: true
|
||||
@@ -182,7 +193,16 @@ export default {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
},
|
||||
saveOptions() {
|
||||
return {
|
||||
icon: 'icon-save',
|
||||
title: 'Save style',
|
||||
isEditing: this.isEditing
|
||||
};
|
||||
},
|
||||
canSaveStyle() {
|
||||
return this.isEditing && !this.mixedStyles.length && !this.nonSpecificFontProperties.length;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -216,6 +236,9 @@ export default {
|
||||
}
|
||||
|
||||
this.$emit('persist', this.styleItem, item.property);
|
||||
},
|
||||
saveItemStyle() {
|
||||
this.$emit('save-style', this.itemStyle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
<div class="c-inspect-styles__header">
|
||||
Object Style
|
||||
</div>
|
||||
<FontStyleEditor
|
||||
v-if="canStyleFont"
|
||||
:font-style="consolidatedFontStyle"
|
||||
@set-font-property="setFontProperty"
|
||||
/>
|
||||
<div class="c-inspect-styles__content">
|
||||
<div v-if="staticStyle"
|
||||
class="c-inspect-styles__style"
|
||||
@@ -39,7 +44,9 @@
|
||||
:style-item="staticStyle"
|
||||
:is-editing="allowEditing"
|
||||
:mixed-styles="mixedStyles"
|
||||
:non-specific-font-properties="nonSpecificFontProperties"
|
||||
@persist="updateStaticStyle"
|
||||
@save-style="saveStyle"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
@@ -58,10 +65,11 @@
|
||||
</div>
|
||||
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||
<a v-if="conditionSetDomainObject"
|
||||
class="c-object-label icon-conditional"
|
||||
class="c-object-label"
|
||||
:href="navigateToPath"
|
||||
@click="navigateOrPreview"
|
||||
>
|
||||
<span class="c-object-label__type-icon icon-conditional"></span>
|
||||
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
||||
</a>
|
||||
<template v-if="allowEditing">
|
||||
@@ -80,6 +88,12 @@
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<FontStyleEditor
|
||||
v-if="canStyleFont"
|
||||
:font-style="consolidatedFontStyle"
|
||||
@set-font-property="setFontProperty"
|
||||
/>
|
||||
|
||||
<div v-if="conditionsLoaded"
|
||||
class="c-inspect-styles__conditions"
|
||||
>
|
||||
@@ -97,8 +111,10 @@
|
||||
/>
|
||||
<style-editor class="c-inspect-styles__editor"
|
||||
:style-item="conditionStyle"
|
||||
:non-specific-font-properties="nonSpecificFontProperties"
|
||||
:is-editing="allowEditing"
|
||||
@persist="updateConditionalStyle"
|
||||
@save-style="saveStyle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,6 +124,7 @@
|
||||
|
||||
<script>
|
||||
|
||||
import FontStyleEditor from '@/ui/inspector/styles/FontStyleEditor.vue';
|
||||
import StyleEditor from "./StyleEditor.vue";
|
||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionSetIdentifierForItem } from "@/plugins/condition/utils/styleUtils";
|
||||
@@ -116,16 +133,30 @@ import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
||||
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
|
||||
import Vue from 'vue';
|
||||
|
||||
const NON_SPECIFIC = '??';
|
||||
const NON_STYLEABLE_CONTAINER_TYPES = [
|
||||
'layout',
|
||||
'flexible-layout',
|
||||
'tabs'
|
||||
];
|
||||
const NON_STYLEABLE_LAYOUT_ITEM_TYPES = [
|
||||
'line-view',
|
||||
'box-view',
|
||||
'image-view'
|
||||
];
|
||||
|
||||
export default {
|
||||
name: 'StylesView',
|
||||
components: {
|
||||
FontStyleEditor,
|
||||
StyleEditor,
|
||||
ConditionError,
|
||||
ConditionDescription
|
||||
},
|
||||
inject: [
|
||||
'openmct',
|
||||
'selection'
|
||||
'selection',
|
||||
'stylesManager'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@@ -139,19 +170,80 @@ export default {
|
||||
conditionsLoaded: false,
|
||||
navigateToPath: '',
|
||||
selectedConditionId: '',
|
||||
locked: false
|
||||
items: [],
|
||||
domainObject: undefined,
|
||||
consolidatedFontStyle: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
locked() {
|
||||
return this.selection.some(selectionPath => {
|
||||
const self = selectionPath[0].context.item;
|
||||
const parent = selectionPath.length > 1 ? selectionPath[1].context.item : undefined;
|
||||
|
||||
return (self && self.locked) || (parent && parent.locked);
|
||||
});
|
||||
},
|
||||
allowEditing() {
|
||||
return this.isEditing && !this.locked;
|
||||
},
|
||||
styleableFontItems() {
|
||||
return this.selection.filter(selectionPath => {
|
||||
const item = selectionPath[0].context.item;
|
||||
const itemType = item && item.type;
|
||||
const layoutItem = selectionPath[0].context.layoutItem;
|
||||
const layoutItemType = layoutItem && layoutItem.type;
|
||||
|
||||
if (itemType && NON_STYLEABLE_CONTAINER_TYPES.includes(itemType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (layoutItemType && NON_STYLEABLE_LAYOUT_ITEM_TYPES.includes(layoutItemType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
computedconsolidatedFontStyle() {
|
||||
let consolidatedFontStyle;
|
||||
const styles = [];
|
||||
|
||||
this.styleableFontItems.forEach(styleable => {
|
||||
const fontStyle = this.getFontStyle(styleable[0]);
|
||||
|
||||
styles.push(fontStyle);
|
||||
});
|
||||
|
||||
if (styles.length) {
|
||||
const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
|
||||
const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
|
||||
|
||||
consolidatedFontStyle = {
|
||||
fontSize: hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC,
|
||||
font: hasConsolidatedFont ? styles[0].font : NON_SPECIFIC
|
||||
};
|
||||
}
|
||||
|
||||
return consolidatedFontStyle;
|
||||
},
|
||||
nonSpecificFontProperties() {
|
||||
if (!this.consolidatedFontStyle) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.keys(this.consolidatedFontStyle).filter(property => this.consolidatedFontStyle[property] === NON_SPECIFIC);
|
||||
},
|
||||
canStyleFont() {
|
||||
return this.styleableFontItems.length && this.allowEditing;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.removeListeners();
|
||||
this.openmct.editor.off('isEditing', this.setEditState);
|
||||
this.stylesManager.off('styleSelected', this.applyStyleToSelection);
|
||||
},
|
||||
mounted() {
|
||||
this.items = [];
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
this.isMultipleSelection = this.selection.length > 1;
|
||||
this.getObjectsAndItemsFromSelection();
|
||||
@@ -166,7 +258,10 @@ export default {
|
||||
this.initializeStaticStyle();
|
||||
}
|
||||
|
||||
this.setConsolidatedFontStyle();
|
||||
|
||||
this.openmct.editor.on('isEditing', this.setEditState);
|
||||
this.stylesManager.on('styleSelected', this.applyStyleToSelection);
|
||||
},
|
||||
methods: {
|
||||
getObjectStyles() {
|
||||
@@ -178,10 +273,10 @@ export default {
|
||||
}
|
||||
} else if (this.items.length) {
|
||||
const itemId = this.items[0].id;
|
||||
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles && this.domainObject.configuration.objectStyles[itemId]) {
|
||||
if (this.domainObject && this.domainObject.configuration && this.domainObject.configuration.objectStyles && this.domainObject.configuration.objectStyles[itemId]) {
|
||||
objectStyles = this.domainObject.configuration.objectStyles[itemId];
|
||||
}
|
||||
} else if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
||||
} else if (this.domainObject && this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
||||
objectStyles = this.domainObject.configuration.objectStyles;
|
||||
}
|
||||
|
||||
@@ -247,13 +342,8 @@ export default {
|
||||
this.selection.forEach((selectionItem) => {
|
||||
const item = selectionItem[0].context.item;
|
||||
const layoutItem = selectionItem[0].context.layoutItem;
|
||||
const layoutDomainObject = selectionItem[0].context.item;
|
||||
const isChildItem = selectionItem.length > 1;
|
||||
|
||||
if (layoutDomainObject && layoutDomainObject.locked) {
|
||||
this.locked = true;
|
||||
}
|
||||
|
||||
if (!isChildItem) {
|
||||
domainObject = item;
|
||||
itemStyle = getApplicableStylesForItem(item);
|
||||
@@ -287,7 +377,7 @@ export default {
|
||||
const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
|
||||
this.initialStyles = styles;
|
||||
this.mixedStyles = mixedStyles;
|
||||
|
||||
// main layout
|
||||
this.domainObject = domainObject;
|
||||
this.removeListeners();
|
||||
if (this.domainObject) {
|
||||
@@ -310,6 +400,7 @@ export default {
|
||||
isKeyItemId(key) {
|
||||
return (key !== 'styles')
|
||||
&& (key !== 'staticStyle')
|
||||
&& (key !== 'fontStyle')
|
||||
&& (key !== 'defaultConditionId')
|
||||
&& (key !== 'selectedConditionId')
|
||||
&& (key !== 'conditionSetIdentifier');
|
||||
@@ -649,6 +740,124 @@ export default {
|
||||
},
|
||||
persist(domainObject, style) {
|
||||
this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
|
||||
},
|
||||
applyStyleToSelection(style) {
|
||||
if (!this.allowEditing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateSelectionFontStyle(style);
|
||||
this.updateSelectionStyle(style);
|
||||
},
|
||||
updateSelectionFontStyle(style) {
|
||||
const fontSizeProperty = {
|
||||
fontSize: style.fontSize
|
||||
};
|
||||
const fontProperty = {
|
||||
font: style.font
|
||||
};
|
||||
|
||||
this.setFontProperty(fontSizeProperty);
|
||||
this.setFontProperty(fontProperty);
|
||||
},
|
||||
updateSelectionStyle(style) {
|
||||
const foundStyle = this.findStyleByConditionId(this.selectedConditionId);
|
||||
|
||||
if (foundStyle && !this.isStaticAndConditionalStyles) {
|
||||
Object.entries(style).forEach(([property, value]) => {
|
||||
if (foundStyle.style[property] !== undefined && foundStyle.style[property] !== value) {
|
||||
foundStyle.style[property] = value;
|
||||
}
|
||||
});
|
||||
this.getAndPersistStyles();
|
||||
} else {
|
||||
this.removeConditionSet();
|
||||
Object.entries(style).forEach(([property, value]) => {
|
||||
if (this.staticStyle.style[property] !== undefined && this.staticStyle.style[property] !== value) {
|
||||
this.staticStyle.style[property] = value;
|
||||
this.getAndPersistStyles(property);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
saveStyle(style) {
|
||||
const styleToSave = {
|
||||
...style,
|
||||
...this.consolidatedFontStyle
|
||||
};
|
||||
|
||||
this.stylesManager.save(styleToSave);
|
||||
},
|
||||
setConsolidatedFontStyle() {
|
||||
const styles = [];
|
||||
|
||||
this.styleableFontItems.forEach(styleable => {
|
||||
const fontStyle = this.getFontStyle(styleable[0]);
|
||||
|
||||
styles.push(fontStyle);
|
||||
});
|
||||
|
||||
if (styles.length) {
|
||||
const hasConsolidatedFontSize = styles.length && styles.every((fontStyle, i, arr) => fontStyle.fontSize === arr[0].fontSize);
|
||||
const hasConsolidatedFont = styles.length && styles.every((fontStyle, i, arr) => fontStyle.font === arr[0].font);
|
||||
|
||||
const fontSize = hasConsolidatedFontSize ? styles[0].fontSize : NON_SPECIFIC;
|
||||
const font = hasConsolidatedFont ? styles[0].font : NON_SPECIFIC;
|
||||
|
||||
this.$set(this.consolidatedFontStyle, 'fontSize', fontSize);
|
||||
this.$set(this.consolidatedFontStyle, 'font', font);
|
||||
}
|
||||
},
|
||||
getFontStyle(selectionPath) {
|
||||
const item = selectionPath.context.item;
|
||||
const layoutItem = selectionPath.context.layoutItem;
|
||||
let fontStyle = item && item.configuration && item.configuration.fontStyle;
|
||||
|
||||
// support for legacy where font styling in layouts only
|
||||
if (!fontStyle) {
|
||||
fontStyle = {
|
||||
fontSize: layoutItem && layoutItem.fontSize || 'default',
|
||||
font: layoutItem && layoutItem.font || 'default'
|
||||
};
|
||||
}
|
||||
|
||||
return fontStyle;
|
||||
},
|
||||
setFontProperty(fontStyleObject) {
|
||||
let layoutDomainObject;
|
||||
const [property, value] = Object.entries(fontStyleObject)[0];
|
||||
|
||||
this.styleableFontItems.forEach(styleable => {
|
||||
if (!this.isLayoutObject(styleable)) {
|
||||
const fontStyle = this.getFontStyle(styleable[0]);
|
||||
fontStyle[property] = value;
|
||||
|
||||
this.openmct.objects.mutate(styleable[0].context.item, 'configuration.fontStyle', fontStyle);
|
||||
} else {
|
||||
// all layoutItems in this context will share same parent layout
|
||||
if (!layoutDomainObject) {
|
||||
layoutDomainObject = styleable[1].context.item;
|
||||
}
|
||||
|
||||
// save layout item font style to parent layout configuration
|
||||
const layoutItemIndex = styleable[0].context.index;
|
||||
const layoutItemConfiguration = layoutDomainObject.configuration.items[layoutItemIndex];
|
||||
|
||||
layoutItemConfiguration[property] = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (layoutDomainObject) {
|
||||
this.openmct.objects.mutate(layoutDomainObject, 'configuration.items', layoutDomainObject.configuration.items);
|
||||
}
|
||||
|
||||
// sync vue component on font update
|
||||
this.$set(this.consolidatedFontStyle, property, value);
|
||||
},
|
||||
isLayoutObject(selectionPath) {
|
||||
const layoutItemType = selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type;
|
||||
|
||||
return layoutItemType && layoutItemType !== 'subobject-view';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -40,9 +40,11 @@
|
||||
}
|
||||
|
||||
&__condition-set {
|
||||
align-items: baseline;
|
||||
border-bottom: 1px solid $colorInteriorBorder;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-bottom: $interiorMargin;
|
||||
|
||||
.c-object-label {
|
||||
flex: 1 1 auto;
|
||||
@@ -53,7 +55,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__style,
|
||||
&__style {
|
||||
padding-bottom: $interiorMargin;
|
||||
}
|
||||
|
||||
&__condition {
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<template>
|
||||
<component :is="urlDefined ? 'a' : 'span'"
|
||||
class="c-condition-widget"
|
||||
class="c-condition-widget u-style-receiver js-style-receiver"
|
||||
:href="urlDefined ? internalDomainObject.url : null"
|
||||
>
|
||||
<div class="c-condition-widget__label">
|
||||
|
||||
@@ -73,7 +73,6 @@ define(['lodash'], function (_) {
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const VIEW_TYPES = {
|
||||
'telemetry-view': {
|
||||
value: 'telemetry-view',
|
||||
@@ -96,7 +95,6 @@ define(['lodash'], function (_) {
|
||||
class: 'icon-tabular-realtime'
|
||||
}
|
||||
};
|
||||
|
||||
const APPLICABLE_VIEWS = {
|
||||
'telemetry-view': [
|
||||
VIEW_TYPES['telemetry.plot.overlay'],
|
||||
@@ -390,29 +388,6 @@ define(['lodash'], function (_) {
|
||||
}
|
||||
}
|
||||
|
||||
function getTextSizeMenu(selectedParent, selection) {
|
||||
const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128];
|
||||
|
||||
return {
|
||||
control: "select-menu",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
let type = selectionPath[0].context.layoutItem.type;
|
||||
|
||||
return type === 'text-view' || type === 'telemetry-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".size";
|
||||
},
|
||||
title: "Set text size",
|
||||
options: TEXT_SIZE.map(size => {
|
||||
return {
|
||||
value: size + "px"
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
function getTextButton(selectedParent, selection) {
|
||||
return {
|
||||
control: "button",
|
||||
@@ -423,7 +398,7 @@ define(['lodash'], function (_) {
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath);
|
||||
},
|
||||
icon: "icon-font",
|
||||
icon: "icon-pencil",
|
||||
title: "Edit text properties",
|
||||
dialog: DIALOG_FORM.text
|
||||
};
|
||||
@@ -678,7 +653,6 @@ define(['lodash'], function (_) {
|
||||
'display-mode': [],
|
||||
'telemetry-value': [],
|
||||
'style': [],
|
||||
'text-style': [],
|
||||
'position': [],
|
||||
'duplicate': [],
|
||||
'unit-toggle': [],
|
||||
@@ -729,12 +703,6 @@ define(['lodash'], function (_) {
|
||||
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)];
|
||||
}
|
||||
|
||||
if (toolbar['text-style'].length === 0) {
|
||||
toolbar['text-style'] = [
|
||||
getTextSizeMenu(selectedParent, selectedObjects)
|
||||
];
|
||||
}
|
||||
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
@@ -760,12 +728,6 @@ define(['lodash'], function (_) {
|
||||
}
|
||||
}
|
||||
} else if (layoutItem.type === 'text-view') {
|
||||
if (toolbar['text-style'].length === 0) {
|
||||
toolbar['text-style'] = [
|
||||
getTextSizeMenu(selectedParent, selectedObjects)
|
||||
];
|
||||
}
|
||||
|
||||
if (toolbar.position.length === 0) {
|
||||
toolbar.position = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<div
|
||||
class="c-box-view"
|
||||
class="c-box-view u-style-receiver js-style-receiver"
|
||||
:class="[styleClass]"
|
||||
:style="style"
|
||||
></div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="l-layout"
|
||||
class="l-layout u-style-receiver js-style-receiver"
|
||||
:class="{
|
||||
'is-multi-selected': selectedLayoutItems.length > 1,
|
||||
'allow-editing': isEditing
|
||||
|
||||
@@ -81,6 +81,7 @@ export default {
|
||||
style() {
|
||||
let backgroundImage = 'url(' + this.item.url + ')';
|
||||
let border = '1px solid ' + this.item.stroke;
|
||||
|
||||
if (this.itemStyle) {
|
||||
if (this.itemStyle.imageUrl !== undefined) {
|
||||
backgroundImage = 'url(' + this.itemStyle.imageUrl + ')';
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
:object-path="currentObjectPath"
|
||||
:has-frame="item.hasFrame"
|
||||
:show-edit-view="false"
|
||||
:layout-font-size="item.fontSize"
|
||||
:layout-font="item.font"
|
||||
/>
|
||||
</layout-frame>
|
||||
</template>
|
||||
@@ -73,6 +75,8 @@ export default {
|
||||
y: position[1],
|
||||
identifier: domainObject.identifier,
|
||||
hasFrame: hasFrameByDefault(domainObject.type),
|
||||
fontSize: 'default',
|
||||
font: 'default',
|
||||
viewKey
|
||||
};
|
||||
},
|
||||
|
||||
@@ -30,12 +30,14 @@
|
||||
>
|
||||
<div
|
||||
v-if="domainObject"
|
||||
class="c-telemetry-view"
|
||||
class="u-style-receiver c-telemetry-view"
|
||||
:class="{
|
||||
styleClass,
|
||||
'is-missing': domainObject.status === 'missing'
|
||||
}"
|
||||
:style="styleObject"
|
||||
:data-font-size="item.fontSize"
|
||||
:data-font="item.font"
|
||||
@contextmenu.prevent="showContextMenu"
|
||||
>
|
||||
<div class="is-missing__indicator"
|
||||
@@ -95,7 +97,8 @@ export default {
|
||||
stroke: "",
|
||||
fill: "",
|
||||
color: "",
|
||||
size: "13px"
|
||||
fontSize: 'default',
|
||||
font: 'default'
|
||||
};
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
@@ -150,10 +153,15 @@ export default {
|
||||
return unit;
|
||||
},
|
||||
styleObject() {
|
||||
return Object.assign({}, {
|
||||
fontSize: this.item.size
|
||||
}, this.itemStyle);
|
||||
let size;
|
||||
//for legacy size support
|
||||
if (!this.item.fontSize) {
|
||||
size = this.item.size;
|
||||
}
|
||||
|
||||
return Object.assign({}, {
|
||||
size
|
||||
}, this.itemStyle);
|
||||
},
|
||||
fieldName() {
|
||||
return this.valueMetadata && this.valueMetadata.name;
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<div
|
||||
class="c-text-view"
|
||||
class="c-text-view u-style-receiver js-style-receiver"
|
||||
:data-font-size="item.fontSize"
|
||||
:data-font="item.font"
|
||||
:class="[styleClass]"
|
||||
:style="style"
|
||||
>
|
||||
@@ -47,13 +49,14 @@ export default {
|
||||
return {
|
||||
fill: '',
|
||||
stroke: '',
|
||||
size: '13px',
|
||||
color: '',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
text: element.text
|
||||
text: element.text,
|
||||
fontSize: 'default',
|
||||
font: 'default'
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
@@ -84,8 +87,14 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
let size;
|
||||
//legacy size support
|
||||
if (!this.item.fontSize) {
|
||||
size = this.item.size;
|
||||
}
|
||||
|
||||
return Object.assign({
|
||||
fontSize: this.item.size
|
||||
size
|
||||
}, this.itemStyle);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -27,6 +27,7 @@ export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
objectStyle: undefined,
|
||||
itemStyle: undefined,
|
||||
styleClass: ''
|
||||
};
|
||||
|
||||
@@ -341,7 +341,7 @@ describe('the plugin', function () {
|
||||
it('provides controls including separators', () => {
|
||||
const displayLayoutToolbar = openmct.toolbars.get(selection);
|
||||
|
||||
expect(displayLayoutToolbar.length).toBe(11);
|
||||
expect(displayLayoutToolbar.length).toBe(9);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
body.desktop & {
|
||||
flex-flow: row wrap;
|
||||
align-content: flex-start;
|
||||
|
||||
&__item {
|
||||
height: $gridItemDesk;
|
||||
width: $gridItemDesk;
|
||||
|
||||
@@ -7,58 +7,67 @@
|
||||
@mouseover="focusElement"
|
||||
>
|
||||
<div class="c-imagery__main-image-wrapper has-local-controls">
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc">
|
||||
<span class="holder flex-elem grows c-imagery__lc__sliders">
|
||||
<input v-model="filters.brightness"
|
||||
class="icon-brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
<input v-model="filters.contrast"
|
||||
class="icon-contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover c-image-controls__controls">
|
||||
<span class="c-image-controls__sliders"
|
||||
draggable="true"
|
||||
@dragstart="startDrag"
|
||||
>
|
||||
<div class="c-image-controls__slider-wrapper icon-brightness">
|
||||
<input v-model="filters.brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
</div>
|
||||
<div class="c-image-controls__slider-wrapper icon-contrast">
|
||||
<input v-model="filters.contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
|
||||
<span class="t-reset-btn-holder c-imagery__lc__reset-btn c-image-controls__btn-reset">
|
||||
<a class="s-icon-button icon-reset t-btn-reset"
|
||||
@click="filters={brightness: 100, contrast: 100}"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="main-image s-image-main c-imagery__main-image has-local-controls"
|
||||
<div class="c-imagery__main-image__bg"
|
||||
:class="{'paused unnsynced': isPaused,'stale':false }"
|
||||
:style="{'background-image': imageUrl ? `url(${imageUrl})` : 'none',
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`}"
|
||||
:data-openmct-image-timestamp="time"
|
||||
:data-openmct-object-keystring="keyString"
|
||||
>
|
||||
<div class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-buttons">
|
||||
<button class="c-nav c-nav--prev"
|
||||
title="Previous image"
|
||||
:disabled="isPrevDisabled"
|
||||
@click="prevImage()"
|
||||
></button>
|
||||
<button class="c-nav c-nav--next"
|
||||
title="Next image"
|
||||
:disabled="isNextDisabled"
|
||||
@click="nextImage()"
|
||||
></button>
|
||||
</div>
|
||||
<div class="c-imagery__main-image__image"
|
||||
:style="{
|
||||
'background-image': imageUrl ? `url(${imageUrl})` : 'none',
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`
|
||||
}"
|
||||
:data-openmct-image-timestamp="time"
|
||||
:data-openmct-object-keystring="keyString"
|
||||
></div>
|
||||
</div>
|
||||
<div class="c-local-controls c-local-controls--show-on-hover c-imagery__prev-next-buttons">
|
||||
<button class="c-nav c-nav--prev"
|
||||
title="Previous image"
|
||||
:disabled="isPrevDisabled"
|
||||
@click="prevImage()"
|
||||
></button>
|
||||
<button class="c-nav c-nav--next"
|
||||
title="Next image"
|
||||
:disabled="isNextDisabled"
|
||||
@click="nextImage()"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
<div class="c-imagery__control-bar">
|
||||
<div class="c-imagery__time">
|
||||
<div class="c-imagery__timestamp">{{ time }}</div>
|
||||
<div class="c-imagery__timestamp u-style-receiver js-style-receiver">{{ time }}</div>
|
||||
<div
|
||||
v-if="canTrackDuration"
|
||||
:class="{'c-imagery--new': isImageNew && !refreshCSS}"
|
||||
class="c-imagery__age icon-timer"
|
||||
>{{ formattedDuration }}</div>
|
||||
</div>
|
||||
<div class="h-local-controls flex-elem">
|
||||
<div class="h-local-controls">
|
||||
<button
|
||||
class="c-button icon-pause pause-play"
|
||||
:class="{'is-paused': isPaused}"
|
||||
@@ -446,6 +455,10 @@ export default {
|
||||
this.setFocusedImage(--index, THUMBNAIL_CLICKED);
|
||||
}
|
||||
},
|
||||
startDrag(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
arrowDownHandler(event) {
|
||||
let key = event.keyCode;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.c-imagery {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
@@ -19,13 +19,21 @@
|
||||
}
|
||||
|
||||
&__main-image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 100%;
|
||||
&__bg {
|
||||
background-color: $colorPlotBg;
|
||||
border: 1px solid transparent;
|
||||
flex: 1 1 auto;
|
||||
|
||||
&.unnsynced{
|
||||
@include sUnsynced();
|
||||
&.unnsynced{
|
||||
@include sUnsynced();
|
||||
}
|
||||
}
|
||||
|
||||
&__image {
|
||||
@include abs(); // Safari fix
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,11 +146,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.s-image-main {
|
||||
background-color: $colorPlotBg;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
/*************************************** IMAGERY LOCAL CONTROLS*/
|
||||
.c-imagery {
|
||||
.h-local-controls--overlay-content {
|
||||
@@ -152,7 +155,7 @@
|
||||
background: $colorLocalControlOvrBg;
|
||||
border-radius: $basicCr;
|
||||
max-width: 200px;
|
||||
min-width: 100px;
|
||||
min-width: 70px;
|
||||
width: 35%;
|
||||
align-items: center;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
@@ -173,6 +176,7 @@
|
||||
&__lc {
|
||||
&__reset-btn {
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
border-right: 1px solid $bc;
|
||||
@@ -195,6 +199,46 @@
|
||||
}
|
||||
}
|
||||
|
||||
.c-image-controls {
|
||||
// Brightness/contrast
|
||||
|
||||
&__controls {
|
||||
// Sliders and reset element
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: $interiorMargin; // Need some extra space due to proximity to close button
|
||||
}
|
||||
|
||||
&__sliders {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
|
||||
> * + * {
|
||||
margin-top: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
&__slider-wrapper {
|
||||
// A wrapper is needed to add the type icon to left of each range input
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:before {
|
||||
color: rgba($colorMenuFg, 0.5);
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
input[type='range'] {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
&__btn-reset {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** BUTTONS */
|
||||
.c-button.pause-play {
|
||||
// Pause icon set by default in markup
|
||||
@@ -211,14 +255,13 @@
|
||||
}
|
||||
|
||||
.c-imagery__prev-next-buttons {
|
||||
//background: rgba(deeppink, 0.2);
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transform: translateY(-75%);
|
||||
|
||||
.c-nav {
|
||||
pointer-events: all;
|
||||
|
||||
@@ -12,12 +12,11 @@
|
||||
<div class="c-ne__content">
|
||||
<div :id="entry.id"
|
||||
class="c-ne__text"
|
||||
:class="{'c-input-inline' : !readOnly }"
|
||||
:class="{'c-ne__input' : !readOnly }"
|
||||
:contenteditable="!readOnly"
|
||||
:style="!entry.text.length ? defaultEntryStyle : ''"
|
||||
@blur="updateEntryValue($event, entry.id)"
|
||||
@focus="updateCurrentEntryValue($event, entry.id)"
|
||||
>{{ entry.text.length ? entry.text : defaultText }}</div>
|
||||
>{{ entry.text }}</div>
|
||||
<div class="c-snapshots c-ne__embeds">
|
||||
<NotebookEmbed v-for="embed in entry.embeds"
|
||||
:key="embed.id"
|
||||
@@ -106,12 +105,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentEntryValue: '',
|
||||
defaultEntryStyle: {
|
||||
fontStyle: 'italic',
|
||||
color: '#6e6e6e'
|
||||
},
|
||||
defaultText: 'add description'
|
||||
currentEntryValue: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -235,24 +229,13 @@ export default {
|
||||
this.entry.embeds.splice(embedPosition, 1);
|
||||
this.updateEntry(this.entry);
|
||||
},
|
||||
selectTextInsideElement(element) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(element);
|
||||
let selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
},
|
||||
updateCurrentEntryValue($event) {
|
||||
if (this.readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = $event.target;
|
||||
this.currentEntryValue = target ? target.innerText : '';
|
||||
|
||||
if (!this.entry.text.length) {
|
||||
this.selectTextInsideElement(target);
|
||||
}
|
||||
this.currentEntryValue = target ? target.textContent : '';
|
||||
},
|
||||
updateEmbed(newEmbed) {
|
||||
this.entry.embeds.some(e => {
|
||||
@@ -292,6 +275,8 @@ export default {
|
||||
const entryPos = this.entryPosById(entryId);
|
||||
const value = target.textContent.trim();
|
||||
if (this.currentEntryValue !== value) {
|
||||
target.textContent = value;
|
||||
|
||||
const entries = getNotebookEntries(this.domainObject, this.selectedSection, this.selectedPage);
|
||||
entries[entryPos].text = value;
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="l-view-section">
|
||||
<div class="l-view-section u-style-receiver js-style-receiver">
|
||||
<div class="c-loading--overlay loading"
|
||||
ng-show="!!pending"></div>
|
||||
<mct-plot config="controller.config"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
title="Toggle grid lines">
|
||||
</button>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="l-view-section u-style-receiver js-style-receiver">
|
||||
<div class="c-loading--overlay loading"
|
||||
ng-show="!!currentRequest.pending"></div>
|
||||
<div class="gl-plot child-frame u-inspectable"
|
||||
|
||||
@@ -25,6 +25,7 @@ define([
|
||||
'lodash',
|
||||
'./collections/BoundedTableRowCollection',
|
||||
'./collections/FilteredTableRowCollection',
|
||||
'./TelemetryTableNameColumn',
|
||||
'./TelemetryTableRow',
|
||||
'./TelemetryTableColumn',
|
||||
'./TelemetryTableUnitColumn',
|
||||
@@ -34,6 +35,7 @@ define([
|
||||
_,
|
||||
BoundedTableRowCollection,
|
||||
FilteredTableRowCollection,
|
||||
TelemetryTableNameColumn,
|
||||
TelemetryTableRow,
|
||||
TelemetryTableColumn,
|
||||
TelemetryTableUnitColumn,
|
||||
@@ -71,6 +73,24 @@ define([
|
||||
openmct.time.on('timeSystem', this.refreshData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
addNameColumn(telemetryObject, metadataValues) {
|
||||
let metadatum = metadataValues.find(m => m.key === 'name');
|
||||
if (!metadatum) {
|
||||
metadatum = {
|
||||
format: 'string',
|
||||
key: 'name',
|
||||
name: 'Name'
|
||||
};
|
||||
}
|
||||
|
||||
const column = new TelemetryTableNameColumn(this.openmct, telemetryObject, metadatum);
|
||||
|
||||
this.configuration.addSingleColumnForObject(telemetryObject, column);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (this.domainObject.type === 'table') {
|
||||
this.filterObserver = this.openmct.objects.observe(this.domainObject, 'configuration.filters', this.updateFilters);
|
||||
@@ -212,7 +232,13 @@ define([
|
||||
|
||||
addColumnsForObject(telemetryObject) {
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
|
||||
this.addNameColumn(telemetryObject, metadataValues);
|
||||
metadataValues.forEach(metadatum => {
|
||||
if (metadatum.key === 'name') {
|
||||
return;
|
||||
}
|
||||
|
||||
let column = this.createColumn(metadatum);
|
||||
this.configuration.addSingleColumnForObject(telemetryObject, column);
|
||||
// add units column if available
|
||||
|
||||
44
src/plugins/telemetryTable/TelemetryTableNameColumn.js
Normal file
44
src/plugins/telemetryTable/TelemetryTableNameColumn.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/*****************************************************************************
|
||||
* 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([
|
||||
'./TelemetryTableColumn.js'
|
||||
], function (
|
||||
TelemetryTableColumn
|
||||
) {
|
||||
class TelemetryTableNameColumn extends TelemetryTableColumn {
|
||||
constructor(openmct, telemetryObject, metadatum) {
|
||||
super(openmct, metadatum);
|
||||
|
||||
this.telemetryObject = telemetryObject;
|
||||
}
|
||||
|
||||
getRawValue() {
|
||||
return this.telemetryObject.name;
|
||||
}
|
||||
|
||||
getFormattedValue() {
|
||||
return this.telemetryObject.name;
|
||||
}
|
||||
}
|
||||
|
||||
return TelemetryTableNameColumn;
|
||||
});
|
||||
54
src/plugins/telemetryTable/components/sizing-row.vue
Normal file
54
src/plugins/telemetryTable/components/sizing-row.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<tr class="c-telemetry-table__sizing-tr"><td>SIZING ROW</td></tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isEditing: function (isEditing) {
|
||||
if (isEditing) {
|
||||
this.pollForRowHeight();
|
||||
} else {
|
||||
this.clearPoll();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick().then(() => {
|
||||
this.height = this.$el.offsetHeight;
|
||||
this.$emit('change-height', this.height);
|
||||
});
|
||||
if (this.isEditing) {
|
||||
this.pollForRowHeight();
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.clearPoll();
|
||||
},
|
||||
methods: {
|
||||
pollForRowHeight() {
|
||||
this.clearPoll();
|
||||
this.pollID = window.setInterval(this.heightPoll, 300);
|
||||
},
|
||||
clearPoll() {
|
||||
if (this.pollID) {
|
||||
window.clearInterval(this.pollID);
|
||||
this.pollID = undefined;
|
||||
}
|
||||
},
|
||||
heightPoll() {
|
||||
let height = this.$el.offsetHeight;
|
||||
if (height !== this.height) {
|
||||
this.$emit('change-height', height);
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
.c-telemetry-table {
|
||||
// Table that displays telemetry in a scrolling body area
|
||||
|
||||
@include fontAndSize();
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: flex-start;
|
||||
@@ -108,7 +111,7 @@
|
||||
display: flex; // flex-flow defaults to row nowrap (which is what we want) so no need to define
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
height: 18px; // Needed when a row has empty values in its cells
|
||||
min-height: 18px; // Needed when a row has empty values in its cells
|
||||
|
||||
.is-editing .l-layout__frame & {
|
||||
pointer-events: none;
|
||||
@@ -151,6 +154,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__sizing-tr {
|
||||
// A row element used to determine sizing of rows based on font size
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
$pt: 2px;
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
<!-- alternate controlbar end -->
|
||||
|
||||
<div
|
||||
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar u-style-receiver js-style-receiver"
|
||||
:class="{
|
||||
'loading': loading,
|
||||
'is-paused' : paused
|
||||
@@ -234,6 +234,10 @@
|
||||
class="c-telemetry-table__sizing js-telemetry-table__sizing"
|
||||
:style="sizingTableWidth"
|
||||
>
|
||||
<sizing-row
|
||||
:is-editing="isEditing"
|
||||
@change-height="setRowHeight"
|
||||
/>
|
||||
<tr>
|
||||
<template v-for="(title, key) in headers">
|
||||
<th
|
||||
@@ -270,6 +274,7 @@ import TableFooterIndicator from './table-footer-indicator.vue';
|
||||
import CSVExporter from '../../../exporters/CSVExporter.js';
|
||||
import _ from 'lodash';
|
||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||
import SizingRow from './sizing-row.vue';
|
||||
|
||||
const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
@@ -282,7 +287,8 @@ export default {
|
||||
TableColumnHeader,
|
||||
search,
|
||||
TableFooterIndicator,
|
||||
ToggleSwitch
|
||||
ToggleSwitch,
|
||||
SizingRow
|
||||
},
|
||||
inject: ['table', 'openmct', 'objectPath'],
|
||||
props: {
|
||||
@@ -506,7 +512,7 @@ export default {
|
||||
let columnWidths = {};
|
||||
let totalWidth = 0;
|
||||
let headerKeys = Object.keys(this.headers);
|
||||
let sizingTableRow = this.sizingTable.children[0];
|
||||
let sizingTableRow = this.sizingTable.children[1];
|
||||
let sizingCells = sizingTableRow.children;
|
||||
|
||||
headerKeys.forEach((headerKey, headerIndex, array) => {
|
||||
@@ -901,6 +907,12 @@ export default {
|
||||
this.isAutosizeEnabled = true;
|
||||
|
||||
this.$nextTick().then(this.calculateColumnWidths);
|
||||
},
|
||||
setRowHeight(height) {
|
||||
this.rowHeight = height;
|
||||
this.setHeight();
|
||||
this.calculateTableSize();
|
||||
this.clearRowsAndRerender();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,10 +183,11 @@ describe("the plugin", () => {
|
||||
|
||||
it("Renders a column for every item in telemetry metadata", () => {
|
||||
let headers = element.querySelectorAll('span.c-telemetry-table__headers__label');
|
||||
expect(headers.length).toBe(3);
|
||||
expect(headers[0].innerText).toBe('Time');
|
||||
expect(headers[1].innerText).toBe('Some attribute');
|
||||
expect(headers[2].innerText).toBe('Another attribute');
|
||||
expect(headers.length).toBe(4);
|
||||
expect(headers[0].innerText).toBe('Name');
|
||||
expect(headers[1].innerText).toBe('Time');
|
||||
expect(headers[2].innerText).toBe('Some attribute');
|
||||
expect(headers[3].innerText).toBe('Another attribute');
|
||||
});
|
||||
|
||||
it("Supports column reordering via drag and drop", () => {
|
||||
|
||||
@@ -103,6 +103,8 @@ $colorProgressBarHolder: rgba(black, 0.1);
|
||||
$colorProgressBar: #0085ad;
|
||||
$progressAnimW: 500px;
|
||||
$progressBarMinH: 6px;
|
||||
/************************** FONT STYLING */
|
||||
$listFontSizes: 8,9,10,11,12,13,14,16,18,20,24,28,32,36,42,48,72,96,128;
|
||||
|
||||
/************************** GLYPH CHAR UNICODES */
|
||||
$glyph-icon-alert-rect: '\e900';
|
||||
|
||||
@@ -113,6 +113,10 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.c-icon-button--disabled {
|
||||
@include cClickIconButtonLayout();
|
||||
}
|
||||
|
||||
.c-icon-link {
|
||||
&:before {
|
||||
// Icon
|
||||
@@ -121,8 +125,8 @@ button {
|
||||
}
|
||||
|
||||
.c-icon-button {
|
||||
&__label {
|
||||
margin-left: $interiorMargin;
|
||||
[class*='label'] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&--mixed {
|
||||
@@ -300,19 +304,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
&-inline,
|
||||
&--inline {
|
||||
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
|
||||
@include reactive-input($bg: transparent);
|
||||
box-shadow: none;
|
||||
display: block !important;
|
||||
min-width: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
overflow: hidden;
|
||||
transition: all 250ms ease;
|
||||
white-space: nowrap;
|
||||
|
||||
&:not(:focus) {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@include inlineInput;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@@ -651,6 +643,7 @@ select {
|
||||
}
|
||||
|
||||
&__item-none {
|
||||
@include userSelectNone();
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -772,6 +765,12 @@ select {
|
||||
color: $editUIBaseColorFg !important;
|
||||
}
|
||||
|
||||
&--menu {
|
||||
$p: 4px;
|
||||
padding-top: $p;
|
||||
padding-bottom: $p;
|
||||
}
|
||||
|
||||
&--swatched {
|
||||
padding-bottom: floor($pTB / 2);
|
||||
width: 2em; // Standardize the width
|
||||
@@ -853,8 +852,41 @@ select {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
> * + * { margin-left: $interiorMargin; }
|
||||
&__controls {
|
||||
// Holds thumb, icon buttons
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
|
||||
> * + * { margin-left: $interiorMargin; }
|
||||
}
|
||||
|
||||
&__button-save,
|
||||
&__button-delete {
|
||||
// Holds save and delete buttons accordingly
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&--saved {
|
||||
border-radius: $controlCr;
|
||||
padding: $interiorMargin !important;
|
||||
cursor: pointer;
|
||||
|
||||
@include hover {
|
||||
background: rgba($editUIBaseColorHov, 0.3);
|
||||
}
|
||||
|
||||
.c-style__controls {
|
||||
[class*='button'] {
|
||||
pointer-events: none;
|
||||
|
||||
&:before {
|
||||
opacity: $controlDisabledOpacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-style-thumb {
|
||||
@@ -863,7 +895,9 @@ select {
|
||||
border-radius: $basicCr;
|
||||
box-shadow: rgba($colorBodyFg, 0.4) 0 0 3px;
|
||||
flex: 0 0 auto;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
padding: $interiorMargin;
|
||||
text-align: center;
|
||||
width: 50px;
|
||||
|
||||
&--mixed {
|
||||
@include mixedBg();
|
||||
@@ -878,20 +912,33 @@ select {
|
||||
|
||||
|
||||
/******************************************************** SLIDERS AND RANGE */
|
||||
@mixin sliderKnobRound() {
|
||||
$h: 12px;
|
||||
@mixin sliderKnobRound($h: 12px) {
|
||||
@include themedButton();
|
||||
cursor: pointer;
|
||||
width: $h;
|
||||
height: $h;
|
||||
border-radius: 50% !important;
|
||||
transform: translateY(-42%);
|
||||
}
|
||||
|
||||
@mixin sliderTrack($bg: $scrollbarTrackColorBg, $knobH: 12px, $trackH: 3px) {
|
||||
border-radius: 2px;
|
||||
$breakPointPx: floor(($knobH - $trackH) / 2);
|
||||
$bp1: $breakPointPx;
|
||||
$bp2: $breakPointPx + $trackH;
|
||||
box-sizing: border-box;
|
||||
// For cross-browser compatibility, the track needs to be the same height as the knob.
|
||||
height: $knobH;
|
||||
// Gradient visually adds a horizontal line smaller than the knob
|
||||
background: linear-gradient(0deg, rgba($bg,0) $bp1, $bg $bp1, $bg $bp2, rgba($bg,0) $bp2);
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
// HTML5 range inputs
|
||||
$knobH: 11px;
|
||||
$trackH: 3px;
|
||||
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||
background: transparent; /* Otherwise white in Chrome */
|
||||
|
||||
&:focus {
|
||||
outline: none; /* Removes the blue border. */
|
||||
}
|
||||
@@ -899,28 +946,26 @@ input[type="range"] {
|
||||
// Thumb
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
@include sliderKnobRound();
|
||||
@include sliderKnobRound($knobH);
|
||||
}
|
||||
&::-moz-range-thumb {
|
||||
border: none;
|
||||
@include sliderKnobRound();
|
||||
@include sliderKnobRound($knobH);
|
||||
}
|
||||
&::-ms-thumb {
|
||||
border: none;
|
||||
@include sliderKnobRound();
|
||||
@include sliderKnobRound($knobH);
|
||||
}
|
||||
|
||||
// Track
|
||||
&::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
@include sliderTrack();
|
||||
@include sliderTrack($knobH: $knobH, $trackH: $trackH);
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
@include sliderTrack();
|
||||
@include sliderTrack($knobH: $knobH, $trackH: $trackH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,37 @@ body.desktop {
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** FONTS */
|
||||
@mixin fontAndSize() {
|
||||
@each $size in $listFontSizes {
|
||||
&[data-font-size="#{$size}"] {
|
||||
font-size: #{$size}px;
|
||||
|
||||
// Set row heights in telemetry tables
|
||||
tr {
|
||||
min-height: #{$size + ($tabularTdPadTB * 2)};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&[data-font*="bold"] {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&[data-font*="narrow"] {
|
||||
font-family: 'Arial Narrow', sans-serif;
|
||||
}
|
||||
|
||||
&[data-font*="monospace"] {
|
||||
font-family: 'Andale Mono', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.u-style-receiver {
|
||||
@include fontAndSize();
|
||||
}
|
||||
|
||||
/******************************************************** HTML ENTITIES */
|
||||
a {
|
||||
color: $colorA;
|
||||
@@ -227,7 +258,7 @@ body.desktop .has-local-controls {
|
||||
}
|
||||
|
||||
/******************************************************** STATES */
|
||||
@mixin spinner($b: 5px, $c: $colorKey) {
|
||||
@mixin spinner($b: 5, $c: $colorKey) {
|
||||
animation-name: rotation-centered;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
@@ -277,7 +308,7 @@ body.desktop .has-local-controls {
|
||||
}
|
||||
&.c-tree__item {
|
||||
$d: $waitSpinnerTreeD;
|
||||
$spinnerL: 19px + $d/2;
|
||||
$spinnerL: 19 + $d/2;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
$ctrlW: 22px;
|
||||
|
||||
&__controls {
|
||||
font-size: 1rem !important;
|
||||
margin-right: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -62,7 +63,7 @@
|
||||
}
|
||||
|
||||
&__direction {
|
||||
font-size: 0.9em;
|
||||
font-size: 0.9rem !important;
|
||||
margin-right: $interiorMargin;
|
||||
}
|
||||
|
||||
|
||||
@@ -380,11 +380,21 @@
|
||||
&:focus {
|
||||
box-shadow: $shdwInputFoc;
|
||||
}
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
&:not(:focus) {
|
||||
box-shadow: $shdwInputHov;
|
||||
}
|
||||
@mixin inlineInput() {
|
||||
@include reactive-input($bg: transparent);
|
||||
box-shadow: none;
|
||||
display: block !important;
|
||||
min-width: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
overflow: hidden;
|
||||
transition: all 250ms ease;
|
||||
white-space: nowrap;
|
||||
|
||||
&:not(:focus) {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +534,7 @@
|
||||
}
|
||||
|
||||
&[class*="--major"] {
|
||||
color: $colorKey;
|
||||
color: $colorBtnMajorBg !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"metadata": {
|
||||
"name": "Open MCT Symbols 16px",
|
||||
"lastOpened": 0,
|
||||
"created": 1597943624771
|
||||
"created": 1602779919972
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@@ -752,7 +752,7 @@
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 114,
|
||||
"order": 194,
|
||||
"id": 4,
|
||||
"name": "icon-font-size",
|
||||
"prevSize": 24,
|
||||
@@ -2718,16 +2718,25 @@
|
||||
{
|
||||
"id": 4,
|
||||
"paths": [
|
||||
"M842.841 380.048h-120.956l-52.382 139.676 52.918 141.12 59.942-159.84 62.361 166.314h-119.884l34.019 90.717h119.884l39.695 105.836h105.836l-181.434-483.823z",
|
||||
"M263.903 160.129l-263.903 703.742h153.944l57.729-153.944h280.397l57.729 153.944h153.944l-263.903-703.742zM261.154 577.976l90.717-241.911 90.717 241.911z"
|
||||
"M1226.4 320h-176l-76.22 203.24 77 205.34 87.22-232.58 90.74 242h-174.44l49.5 132h174.44l57.76 154h154l-264-704z",
|
||||
"M384 0l-384 1024h224l84-224h408l84 224h224l-384-1024zM380 608l132-352 132 352z"
|
||||
],
|
||||
"attrs": [
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"attrs": [],
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-font-size-alt1"
|
||||
],
|
||||
"width": 1504,
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"colorPermutations": {
|
||||
"12552552551": []
|
||||
"12552552551": [
|
||||
{},
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<glyph unicode="" glyph-name="icon-frame-hide" d="M128 642h420l104 128h-652v-802.4l128 157.4zM896 2h-420l-104-128h652v802.4l-128-157.4zM832 834l-832-1024h192l832 1024zM392 450l104 128h-304v-128z" />
|
||||
<glyph unicode="" glyph-name="icon-import" d="M832 639.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 128v-192l384 384-384 384v-192h-192v-384z" />
|
||||
<glyph unicode="" glyph-name="icon-export" d="M192 0.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 320l-384 384v-192h-192v-384h192v-192l384 384z" />
|
||||
<glyph unicode="" glyph-name="icon-font-size" d="M842.841 451.952h-120.956l-52.382-139.676 52.918-141.12 59.942 159.84 62.361-166.314h-119.884l34.019-90.717h119.884l39.695-105.836h105.836l-181.434 483.823zM263.903 671.871l-263.903-703.742h153.944l57.729 153.944h280.397l57.729-153.944h153.944l-263.903 703.742zM261.154 254.024l90.717 241.911 90.717-241.911z" />
|
||||
<glyph unicode="" glyph-name="icon-font-size" horiz-adv-x="1504" d="M1226.4 512h-176l-76.22-203.24 77-205.34 87.22 232.58 90.74-242h-174.44l49.5-132h174.44l57.76-154h154l-264 704zM384 832l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 224l132 352 132-352z" />
|
||||
<glyph unicode="" glyph-name="icon-clear-data" d="M632 520l-120-120-120 120-80-80 120-120-120-120 80-80 120 120 120-120 80 80-120 120 120 120-80 80zM512 832c-282.76 0-512-86-512-192v-640c0-106 229.24-192 512-192s512 86 512 192v640c0 106-229.24 192-512 192zM512 0c-176.731 0-320 143.269-320 320s143.269 320 320 320c176.731 0 320-143.269 320-320v0c0-176.731-143.269-320-320-320v0z" />
|
||||
<glyph unicode="" glyph-name="icon-history" d="M576 768c-247.4 0-448-200.6-448-448h-128l192-192 192 192h-128c0 85.4 33.2 165.8 93.8 226.2 60.4 60.6 140.8 93.8 226.2 93.8s165.8-33.2 226.2-93.8c60.6-60.4 93.8-140.8 93.8-226.2s-33.2-165.8-93.8-226.2c-60.4-60.6-140.8-93.8-226.2-93.8s-165.8 33.2-226.2 93.8l-90.6-90.6c81-81 193-131.2 316.8-131.2 247.4 0 448 200.6 448 448s-200.6 448-448 448zM576 560c-26.6 0-48-21.4-48-48v-211.8l142-142c9.4-9.4 21.6-14 34-14s24.6 4.6 34 14c18.8 18.8 18.8 49.2 0 67.8l-114 114v172c0 26.6-21.4 48-48 48z" />
|
||||
<glyph unicode="" glyph-name="icon-arrow-up-to-parent" horiz-adv-x="1056" d="M643.427 6.739c-81.955 0.697-148.179 67.065-148.642 149.010v395.872l296.871-247.393v197.914l-395.828 329.857-395.828-328.62v-197.502l296.871 246.156v-396.241c0-190.905 155.239-346.556 346.144-346.968l412.321-0.825 0.412 197.914z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 58 KiB |
Binary file not shown.
Binary file not shown.
@@ -25,6 +25,7 @@
|
||||
$headerFontSize: 1.3em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
@@ -275,21 +276,22 @@
|
||||
&__text {
|
||||
min-height: 22px; // Needed in Firefox when field is blank
|
||||
white-space: pre-wrap;
|
||||
|
||||
&.is-blank-notebook-entry {
|
||||
&:not(:focus):before {
|
||||
content: 'Blank entry';
|
||||
font-style: italic;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__embeds {
|
||||
//flex-wrap: wrap;
|
||||
&__input {
|
||||
// Appended to __text element when Notebook is not in readOnly mode
|
||||
@include inlineInput;
|
||||
padding-left: $inputTextPLeftRight;
|
||||
padding-right: $inputTextPLeftRight;
|
||||
|
||||
> [class*="__embed"] {
|
||||
//margin: 0 $interiorMarginSm $interiorMarginSm 0;
|
||||
@include hover {
|
||||
&:not(:focus) {
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: $colorInputBg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,3 +47,7 @@
|
||||
@import "../ui/toolbar/components/toolbar-checkbox.scss";
|
||||
@import "./notebook.scss";
|
||||
@import "../plugins/notebook/components/sidebar.scss";
|
||||
|
||||
#splash-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
:object="domainObject"
|
||||
:show-edit-view="showEditView"
|
||||
:object-path="objectPath"
|
||||
:layout-font-size="layoutFontSize"
|
||||
:layout-font="layoutFont"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -103,6 +105,14 @@ export default {
|
||||
showEditView: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
layoutFontSize: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
layoutFont: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -20,6 +20,30 @@ export default {
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
layoutFontSize: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
layoutFont: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentObject: this.object
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
objectFontStyle() {
|
||||
return this.currentObject && this.currentObject.configuration && this.currentObject.configuration.fontStyle;
|
||||
},
|
||||
fontSize() {
|
||||
return this.objectFontStyle ? this.objectFontStyle.fontSize : this.layoutFontSize;
|
||||
},
|
||||
font() {
|
||||
return this.objectFontStyle ? this.objectFontStyle.font : this.layoutFont;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -42,6 +66,10 @@ export default {
|
||||
this.stopListeningStyles();
|
||||
}
|
||||
|
||||
if (this.stopListeningFontStyles) {
|
||||
this.stopListeningFontStyles();
|
||||
}
|
||||
|
||||
if (this.styleRuleManager) {
|
||||
this.styleRuleManager.destroy();
|
||||
delete this.styleRuleManager;
|
||||
@@ -51,7 +79,6 @@ export default {
|
||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||
},
|
||||
mounted() {
|
||||
this.currentObject = this.object;
|
||||
this.updateView();
|
||||
this.$el.addEventListener('dragover', this.onDragOver, {
|
||||
capture: true
|
||||
@@ -64,7 +91,6 @@ export default {
|
||||
//This is to apply styles to subobjects in a layout
|
||||
this.initObjectStyles();
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
@@ -92,6 +118,15 @@ export default {
|
||||
|
||||
this.openmct.objectViews.off('clearData', this.clearData);
|
||||
},
|
||||
getStyleReceiver() {
|
||||
let styleReceiver = this.$el.querySelector('.js-style-receiver');
|
||||
|
||||
if (!styleReceiver) {
|
||||
styleReceiver = this.$el.querySelector(':first-child');
|
||||
}
|
||||
|
||||
return styleReceiver;
|
||||
},
|
||||
invokeEditModeHandler(editMode) {
|
||||
let edit;
|
||||
|
||||
@@ -113,21 +148,21 @@ export default {
|
||||
}
|
||||
|
||||
let keys = Object.keys(styleObj);
|
||||
let elemToStyle = this.getStyleReceiver();
|
||||
keys.forEach(key => {
|
||||
let firstChild = this.$el.querySelector(':first-child');
|
||||
if (firstChild) {
|
||||
if (elemToStyle) {
|
||||
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('__no_value') > -1)) {
|
||||
if (firstChild.style[key]) {
|
||||
firstChild.style[key] = '';
|
||||
if (elemToStyle.style[key]) {
|
||||
elemToStyle.style[key] = '';
|
||||
}
|
||||
} else {
|
||||
if (!styleObj.isStyleInvisible && firstChild.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
|
||||
firstChild.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
|
||||
} else if (styleObj.isStyleInvisible && !firstChild.classList.contains(styleObj.isStyleInvisible)) {
|
||||
firstChild.classList.add(styleObj.isStyleInvisible);
|
||||
if (!styleObj.isStyleInvisible && elemToStyle.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
|
||||
elemToStyle.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
|
||||
} else if (styleObj.isStyleInvisible && !elemToStyle.classList.contains(styleObj.isStyleInvisible)) {
|
||||
elemToStyle.classList.add(styleObj.isStyleInvisible);
|
||||
}
|
||||
|
||||
firstChild.style[key] = styleObj[key];
|
||||
elemToStyle.style[key] = styleObj[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -228,6 +263,14 @@ export default {
|
||||
//Updating styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
|
||||
});
|
||||
|
||||
this.setFontSize(this.fontSize);
|
||||
this.setFont(this.font);
|
||||
|
||||
this.stopListeningFontStyles = this.openmct.objects.observe(this.currentObject, 'configuration.fontStyle', (newFontStyle) => {
|
||||
this.setFontSize(newFontStyle.fontSize);
|
||||
this.setFont(newFontStyle.font);
|
||||
});
|
||||
},
|
||||
loadComposition() {
|
||||
return this.composition.load();
|
||||
@@ -311,6 +354,14 @@ export default {
|
||||
let parentObject = objectPath[1];
|
||||
|
||||
return [browseObject, parentObject, this.currentObject].every(object => object && !object.locked);
|
||||
},
|
||||
setFontSize(newSize) {
|
||||
let elemToStyle = this.getStyleReceiver();
|
||||
elemToStyle.dataset.fontSize = newSize;
|
||||
},
|
||||
setFont(newFont) {
|
||||
let elemToStyle = this.getStyleReceiver();
|
||||
elemToStyle.dataset.font = newFont;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
}
|
||||
|
||||
&__object-view {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
height: 0; // Chrome 73 overflow bug fix
|
||||
overflow: auto;
|
||||
|
||||
.u-fills-container {
|
||||
@@ -84,6 +84,6 @@
|
||||
|
||||
.l-angular-ov-wrapper {
|
||||
// This element is the recipient for object styling; cannot be display: contents
|
||||
height: 100%;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
</div>
|
||||
<div class="c-inspector__content">
|
||||
<multipane v-if="currentTabbedView.key === '__properties'"
|
||||
<multipane v-show="currentTabbedView.key === '__properties'"
|
||||
type="vertical"
|
||||
>
|
||||
<pane class="c-inspector__properties">
|
||||
@@ -32,9 +32,22 @@
|
||||
<elements />
|
||||
</pane>
|
||||
</multipane>
|
||||
<template v-else>
|
||||
<styles-inspector-view />
|
||||
</template>
|
||||
<multipane
|
||||
v-show="currentTabbedView.key === '__styles'"
|
||||
type="vertical"
|
||||
>
|
||||
<pane class="c-inspector__styles">
|
||||
<StylesInspectorView />
|
||||
</pane>
|
||||
<pane
|
||||
v-if="isEditing"
|
||||
class="c-inspector__saved-styles"
|
||||
handle="before"
|
||||
label="Saved Styles"
|
||||
>
|
||||
<SavedStylesInspectorView :is-editing="isEditing" />
|
||||
</pane>
|
||||
</multipane>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -48,12 +61,18 @@ import Properties from './Properties.vue';
|
||||
import ObjectName from './ObjectName.vue';
|
||||
import InspectorViews from './InspectorViews.vue';
|
||||
import _ from "lodash";
|
||||
import StylesInspectorView from "./StylesInspectorView.vue";
|
||||
import stylesManager from '@/ui/inspector/styles/StylesManager';
|
||||
import StylesInspectorView from '@/ui/inspector/styles/StylesInspectorView.vue';
|
||||
import SavedStylesInspectorView from '@/ui/inspector/styles/SavedStylesInspectorView.vue';
|
||||
|
||||
export default {
|
||||
provide: {
|
||||
stylesManager: stylesManager
|
||||
},
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
StylesInspectorView,
|
||||
SavedStylesInspectorView,
|
||||
multipane,
|
||||
pane,
|
||||
Elements,
|
||||
@@ -63,7 +82,10 @@ export default {
|
||||
InspectorViews
|
||||
},
|
||||
props: {
|
||||
'isEditing': Boolean
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__saved-styles {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.c-color-swatch {
|
||||
$d: 12px;
|
||||
display: block;
|
||||
@@ -162,6 +166,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************* INSPECTOR PROPERTIES TAB */
|
||||
.c-saved-style {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/********************************************* LEGACY SUPPORT */
|
||||
.c-inspector {
|
||||
// FilterField.vue
|
||||
|
||||
63
src/ui/inspector/styles/FontStyleEditor.vue
Normal file
63
src/ui/inspector/styles/FontStyleEditor.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="c-toolbar">
|
||||
<toolbar-select-menu
|
||||
:options="fontSizeMenuOptions"
|
||||
@change="setFontSize"
|
||||
/>
|
||||
<div class="c-toolbar__separator"></div>
|
||||
<toolbar-select-menu
|
||||
:options="fontMenuOptions"
|
||||
@change="setFont"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ToolbarSelectMenu from '@/ui/toolbar/components/toolbar-select-menu.vue';
|
||||
|
||||
import {
|
||||
FONT_SIZES,
|
||||
FONTS
|
||||
} from '@/ui/inspector/styles/constants';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
ToolbarSelectMenu
|
||||
},
|
||||
props: {
|
||||
fontStyle: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fontMenuOptions() {
|
||||
return {
|
||||
control: 'select-menu',
|
||||
icon: "icon-font",
|
||||
title: "Set font style",
|
||||
value: this.fontStyle.font,
|
||||
options: FONTS
|
||||
};
|
||||
},
|
||||
fontSizeMenuOptions() {
|
||||
return {
|
||||
control: 'select-menu',
|
||||
icon: "icon-font-size",
|
||||
title: "Set font size",
|
||||
value: this.fontStyle.fontSize,
|
||||
options: FONT_SIZES
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setFont(font) {
|
||||
this.$emit('set-font-property', { font: font });
|
||||
},
|
||||
setFontSize(fontSize) {
|
||||
this.$emit('set-font-property', { fontSize: fontSize });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
195
src/ui/inspector/styles/SavedStyleSelector.vue
Normal file
195
src/ui/inspector/styles/SavedStyleSelector.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
/*****************************************************************************
|
||||
* 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>
|
||||
<div class="c-style c-style--saved has-local-controls c-toolbar">
|
||||
<div class="c-style__controls"
|
||||
:title="description"
|
||||
@click="selectStyle()"
|
||||
>
|
||||
<div
|
||||
class="c-style-thumb"
|
||||
:style="thumbStyle"
|
||||
>
|
||||
<span
|
||||
class="c-style-thumb__text u-style-receiver js-style-receiver"
|
||||
:class="{ 'hide-nice': !hasProperty(savedStyle.color) }"
|
||||
:data-font="savedStyle.font"
|
||||
>
|
||||
{{ thumbLabel }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="c-icon-button c-icon-button--disabled c-icon-button--swatched icon-line-horz"
|
||||
title="Border color"
|
||||
>
|
||||
<div
|
||||
class="c-swatch"
|
||||
:style="{
|
||||
background: borderColor
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
class="c-icon-button c-icon-button--disabled c-icon-button--swatched icon-paint-bucket"
|
||||
title="Background color"
|
||||
>
|
||||
<div
|
||||
class="c-swatch"
|
||||
:style="{ background: savedStyle.backgroundColor }"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
class="c-icon-button c-icon-button--disabled c-icon-button--swatched icon-font"
|
||||
title="Text color"
|
||||
>
|
||||
<div
|
||||
class="c-swatch"
|
||||
:style="{ background: savedStyle.color }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="canDeleteStyle"
|
||||
class="c-style__button-delete c-local-controls--show-on-hover"
|
||||
>
|
||||
<div
|
||||
class="c-icon-button icon-trash"
|
||||
title="Delete this saved style"
|
||||
@click.stop="deleteStyle()"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SavedStyleSelector',
|
||||
inject: [
|
||||
'openmct',
|
||||
'stylesManager'
|
||||
],
|
||||
props: {
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
savedStyle: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
borderColor() {
|
||||
return this.savedStyle.border.substring(this.savedStyle.border.indexOf('#'));
|
||||
},
|
||||
thumbStyle() {
|
||||
return {
|
||||
border: this.savedStyle.border,
|
||||
backgroundColor: this.savedStyle.backgroundColor,
|
||||
color: this.savedStyle.color
|
||||
};
|
||||
},
|
||||
thumbLabel() {
|
||||
return this.savedStyle.fontSize !== 'default' ? `${this.savedStyle.fontSize}px` : 'ABC';
|
||||
},
|
||||
description() {
|
||||
const fill = `Fill: ${this.savedStyle.backgroundColor || 'none'}`;
|
||||
const border = `Border: ${this.savedStyle.border || 'none'}`;
|
||||
const color = `Text Color: ${this.savedStyle.color || 'default'}`;
|
||||
const fontSize = this.savedStyle.fontSize ? `Font Size: ${this.savedStyle.fontSize}` : '';
|
||||
const font = this.savedStyle.font ? `Font Style: ${this.savedStyle.font}` : '';
|
||||
|
||||
// Note: lack of indention in the return string is deliberate, it affects how the text is rendered
|
||||
return `Click to apply this style:
|
||||
${fill}
|
||||
${border}
|
||||
${color}
|
||||
${fontSize}
|
||||
${font}`;
|
||||
},
|
||||
canDeleteStyle() {
|
||||
return this.isEditing;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectStyle() {
|
||||
if (this.isEditing) {
|
||||
this.stylesManager.select(this.savedStyle);
|
||||
}
|
||||
},
|
||||
deleteStyle() {
|
||||
this.showDeleteStyleDialog()
|
||||
.then(() => {
|
||||
this.$emit('delete-style');
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
showDeleteStyleDialog(style) {
|
||||
const message = `
|
||||
This will delete this saved style.
|
||||
This action will not effect styling that has already been applied.
|
||||
Do you want to continue?
|
||||
`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
title: 'Delete Saved Style',
|
||||
iconClass: 'alert',
|
||||
message: message,
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
reject();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
},
|
||||
hasProperty(property) {
|
||||
return property !== undefined;
|
||||
},
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
72
src/ui/inspector/styles/SavedStylesInspectorView.vue
Normal file
72
src/ui/inspector/styles/SavedStylesInspectorView.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
/*****************************************************************************
|
||||
* 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="u-contents"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SavedStylesView from '@/ui/inspector/styles/SavedStylesView.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'stylesManager'],
|
||||
data() {
|
||||
return {
|
||||
selection: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.updateSelection(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.updateSelection);
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
if (selection.length > 0 && selection[0].length > 0) {
|
||||
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({
|
||||
el: viewContainer,
|
||||
components: {
|
||||
SavedStylesView
|
||||
},
|
||||
provide: {
|
||||
openmct: this.openmct,
|
||||
selection: selection,
|
||||
stylesManager: this.stylesManager
|
||||
},
|
||||
template: '<saved-styles-view />'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
129
src/ui/inspector/styles/SavedStylesView.vue
Normal file
129
src/ui/inspector/styles/SavedStylesView.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
/*****************************************************************************
|
||||
* 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__saved-styles c-inspect-styles">
|
||||
<div class="c-inspect-styles__content">
|
||||
<div class="c-inspect-styles__saved-styles">
|
||||
<saved-style-selector
|
||||
v-for="(savedStyle, index) in savedStyles"
|
||||
:key="index"
|
||||
class="c-inspect-styles__saved-style"
|
||||
:is-editing="isEditing"
|
||||
:saved-style="savedStyle"
|
||||
@delete-style="deleteStyle(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SavedStyleSelector from '@/ui/inspector/styles/SavedStyleSelector.vue';
|
||||
|
||||
export default {
|
||||
name: 'SavedStylesView',
|
||||
components: {
|
||||
SavedStyleSelector
|
||||
},
|
||||
inject: [
|
||||
'openmct',
|
||||
'selection',
|
||||
'stylesManager'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
savedStyles: undefined
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.setIsEditing);
|
||||
this.stylesManager.on('stylesUpdated', this.setStyles);
|
||||
this.stylesManager.on('limitReached', this.showLimitReachedDialog);
|
||||
this.stylesManager.on('persistError', this.showPersistErrorDialog);
|
||||
|
||||
this.loadStyles();
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.setIsEditing);
|
||||
this.stylesManager.off('stylesUpdated', this.setStyles);
|
||||
this.stylesManager.off('limitReached', this.showLimitReachedDialog);
|
||||
this.stylesManager.off('persistError', this.showPersistErrorDialog);
|
||||
},
|
||||
methods: {
|
||||
setIsEditing(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
loadStyles() {
|
||||
const styles = this.stylesManager.load();
|
||||
|
||||
this.setStyles(styles);
|
||||
},
|
||||
setStyles(styles) {
|
||||
this.savedStyles = styles;
|
||||
},
|
||||
showLimitReachedDialog(limit) {
|
||||
const message = `
|
||||
You have reached the limit on the number of saved styles.
|
||||
Please delete one or more saved styles and try again.
|
||||
`;
|
||||
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
title: 'Saved Styles Limit',
|
||||
iconClass: 'alert',
|
||||
message: message,
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
showPersistErrorDialog() {
|
||||
const message = `
|
||||
Problem encountered saving styles.
|
||||
Try again or delete one or more styles before trying again.
|
||||
`;
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
title: 'Error Saving Style',
|
||||
iconClass: 'error',
|
||||
message: message,
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
deleteStyle(index) {
|
||||
this.stylesManager.delete(index);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -25,11 +25,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StylesView from '../../plugins/condition/components/inspector/StylesView.vue';
|
||||
import StylesView from '@/plugins/condition/components/inspector/StylesView.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
inject: ['openmct', 'stylesManager'],
|
||||
data() {
|
||||
return {
|
||||
selection: []
|
||||
@@ -56,7 +56,8 @@ export default {
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
openmct: this.openmct,
|
||||
selection: selection
|
||||
selection: selection,
|
||||
stylesManager: this.stylesManager
|
||||
},
|
||||
el: viewContainer,
|
||||
components: {
|
||||
126
src/ui/inspector/styles/StylesManager.js
Normal file
126
src/ui/inspector/styles/StylesManager.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'mct-saved-styles';
|
||||
const LIMIT = 20;
|
||||
|
||||
/**
|
||||
* @typedef {Object} Style
|
||||
* @property {*} property
|
||||
*/
|
||||
class StylesManager extends EventEmitter {
|
||||
load() {
|
||||
let styles = window.localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||
styles = styles ? JSON.parse(styles) : [];
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
save(style) {
|
||||
const normalizedStyle = this.normalizeStyle(style);
|
||||
const styles = this.load();
|
||||
|
||||
if (!this.isSaveLimitReached(styles)) {
|
||||
styles.unshift(normalizedStyle);
|
||||
|
||||
if (this.persist(styles)) {
|
||||
this.emit('stylesUpdated', styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(index) {
|
||||
const styles = this.load();
|
||||
styles.splice(index, 1);
|
||||
|
||||
if (this.persist(styles)) {
|
||||
this.emit('stylesUpdated', styles);
|
||||
}
|
||||
}
|
||||
|
||||
select(style) {
|
||||
this.emit('styleSelected', style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
normalizeStyle(style) {
|
||||
const normalizedStyle = this.getBaseStyleObject();
|
||||
|
||||
Object.keys(normalizedStyle).forEach(property => {
|
||||
const value = style[property];
|
||||
if (value !== undefined) {
|
||||
normalizedStyle[property] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return normalizedStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getBaseStyleObject() {
|
||||
return {
|
||||
backgroundColor: '',
|
||||
border: '',
|
||||
color: '',
|
||||
fontSize: 'default',
|
||||
font: 'default'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
isSaveLimitReached(styles) {
|
||||
if (styles.length >= LIMIT) {
|
||||
this.emit('limitReached');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
isExistingStyle(style, styles) {
|
||||
return styles.some(existingStyle => this.isEqual(style, existingStyle));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
persist(styles) {
|
||||
try {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(styles));
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.emit('persistError');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
isEqual(style1, style2) {
|
||||
const keys = Object.keys(Object.assign({}, style1, style2));
|
||||
const different = keys.some(key => (!style1[key] && style2[key])
|
||||
|| (style1[key] && !style2[key])
|
||||
|| (style1[key] !== style2[key])
|
||||
);
|
||||
|
||||
return !different;
|
||||
}
|
||||
}
|
||||
|
||||
const stylesManager = new StylesManager();
|
||||
// breaks on adding listener later
|
||||
// Object.freeze(stylesManager);
|
||||
|
||||
export default stylesManager;
|
||||
109
src/ui/inspector/styles/constants.js
Normal file
109
src/ui/inspector/styles/constants.js
Normal file
@@ -0,0 +1,109 @@
|
||||
export const FONT_SIZES = [
|
||||
{
|
||||
name: 'Default Size',
|
||||
value: 'default'
|
||||
},
|
||||
{
|
||||
name: '8px',
|
||||
value: '8'
|
||||
},
|
||||
{
|
||||
name: '9px',
|
||||
value: '9'
|
||||
},
|
||||
{
|
||||
name: '10px',
|
||||
value: '10'
|
||||
},
|
||||
{
|
||||
name: '11px',
|
||||
value: '11'
|
||||
},
|
||||
{
|
||||
name: '12px',
|
||||
value: '12'
|
||||
},
|
||||
{
|
||||
name: '13px',
|
||||
value: '13'
|
||||
},
|
||||
{
|
||||
name: '14px',
|
||||
value: '14'
|
||||
},
|
||||
{
|
||||
name: '16px',
|
||||
value: '16'
|
||||
},
|
||||
{
|
||||
name: '18px',
|
||||
value: '18'
|
||||
},
|
||||
{
|
||||
name: '20px',
|
||||
value: '20'
|
||||
},
|
||||
{
|
||||
name: '24px',
|
||||
value: '24'
|
||||
},
|
||||
{
|
||||
name: '28px',
|
||||
value: '28'
|
||||
},
|
||||
{
|
||||
name: '32px',
|
||||
value: '32'
|
||||
},
|
||||
{
|
||||
name: '36px',
|
||||
value: '36'
|
||||
},
|
||||
{
|
||||
name: '42px',
|
||||
value: '42'
|
||||
},
|
||||
{
|
||||
name: '48px',
|
||||
value: '48'
|
||||
},
|
||||
{
|
||||
name: '72px',
|
||||
value: '72'
|
||||
},
|
||||
{
|
||||
name: '96px',
|
||||
value: '96'
|
||||
},
|
||||
{
|
||||
name: '128px',
|
||||
value: '128'
|
||||
}
|
||||
];
|
||||
|
||||
export const FONTS = [
|
||||
{
|
||||
name: 'Default',
|
||||
value: 'default'
|
||||
},
|
||||
{
|
||||
name: 'Bold',
|
||||
value: 'default-bold'
|
||||
},
|
||||
{
|
||||
name: 'Narrow',
|
||||
value: 'narrow'
|
||||
},
|
||||
{
|
||||
name: 'Narrow Bold',
|
||||
value: 'narrow-bold'
|
||||
},
|
||||
{
|
||||
name: 'Monospace',
|
||||
value: 'monospace'
|
||||
},
|
||||
{
|
||||
name: 'Monospace Bold',
|
||||
value: 'monospace-bold'
|
||||
}
|
||||
];
|
||||
@@ -5,6 +5,11 @@
|
||||
'is-editing': isEditing
|
||||
}"
|
||||
>
|
||||
|
||||
<div
|
||||
id="splash-screen"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="l-shell__head"
|
||||
:class="{
|
||||
|
||||
@@ -228,8 +228,9 @@
|
||||
/******************************* MAIN AREA */
|
||||
&__main-container {
|
||||
// Wrapper for main views
|
||||
display: flex;
|
||||
flex: 1 1 auto !important;
|
||||
height: 0; // Chrome 73 overflow bug fix
|
||||
height: 100%; // Chrome 73 overflow bug fix
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -338,7 +339,6 @@
|
||||
|
||||
&__start {
|
||||
flex: 1 1 auto;
|
||||
//margin-right: $interiorMargin;
|
||||
min-width: 0; // Forces interior to compress when pushed on
|
||||
|
||||
[class*='button'] {
|
||||
@@ -357,11 +357,6 @@
|
||||
|
||||
&__nav-to-parent-button {
|
||||
// This is an icon-button
|
||||
//$p: $interiorMargin;
|
||||
//margin-right: $interiorMargin;
|
||||
//padding-left: $p;
|
||||
//padding-right: $p;
|
||||
|
||||
.is-editing & {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ export default {
|
||||
return this.mainTreeHeight - this.ancestorsHeight;
|
||||
},
|
||||
showNoItems() {
|
||||
return this.visibleItems.length === 0 && !this.activeSearch;
|
||||
return this.visibleItems.length === 0 && !this.activeSearch && !this.isLoading;
|
||||
},
|
||||
showNoSearchResults() {
|
||||
return this.searchValue && this.searchResultItems.length === 0 && !this.searchLoading;
|
||||
@@ -276,7 +276,9 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.initialize();
|
||||
this.isLoading = true;
|
||||
|
||||
await this.initialize();
|
||||
|
||||
let savedPath = this.getStoredTreePath();
|
||||
let rootComposition = await this.loadRoot();
|
||||
@@ -302,26 +304,15 @@ export default {
|
||||
destroyed() {
|
||||
window.removeEventListener('resize', this.handleWindowResize);
|
||||
this.stopObservingAncestors();
|
||||
document.removeEventListener('readystatechange', this.setTreeTopMargin);
|
||||
},
|
||||
methods: {
|
||||
initialize() {
|
||||
async initialize() {
|
||||
this.searchService = this.openmct.$injector.get('searchService');
|
||||
window.addEventListener('resize', this.handleWindowResize);
|
||||
this.readyStateCheck();
|
||||
this.backwardsCompatibilityCheck();
|
||||
},
|
||||
readyStateCheck() {
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('readystatechange', this.onReadyState);
|
||||
} else {
|
||||
this.onReadyState();
|
||||
}
|
||||
},
|
||||
onReadyState() {
|
||||
if (document.readyState === 'complete') {
|
||||
this.calculateHeights();
|
||||
}
|
||||
await this.calculateHeights();
|
||||
|
||||
return;
|
||||
},
|
||||
backwardsCompatibilityCheck() {
|
||||
let oldTreeExpanded = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED__OLD));
|
||||
@@ -781,17 +772,44 @@ export default {
|
||||
return { height };
|
||||
},
|
||||
getElementStyleValue(el, style) {
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
let styleString = window.getComputedStyle(el)[style];
|
||||
let index = styleString.indexOf('px');
|
||||
|
||||
return Number(styleString.slice(0, index));
|
||||
},
|
||||
calculateHeights() {
|
||||
this.mainTreeTopMargin = this.getElementStyleValue(this.$refs.mainTree, 'marginTop');
|
||||
this.mainTreeHeight = this.$el.offsetHeight
|
||||
- this.$refs.search.offsetHeight
|
||||
- this.mainTreeTopMargin;
|
||||
this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height');
|
||||
const RECHECK = 100;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let checkHeights = () => {
|
||||
let treeTopMargin = this.getElementStyleValue(this.$refs.mainTree, 'marginTop');
|
||||
if (
|
||||
this.$el
|
||||
&& this.$refs.search
|
||||
&& this.$refs.mainTree
|
||||
&& this.$refs.dummyItem
|
||||
&& this.$el.offsetHeight !== 0
|
||||
&& treeTopMargin > 0
|
||||
) {
|
||||
this.mainTreeTopMargin = treeTopMargin;
|
||||
this.mainTreeHeight = this.$el.offsetHeight
|
||||
- this.$refs.search.offsetHeight
|
||||
- this.mainTreeTopMargin;
|
||||
this.itemHeight = this.getElementStyleValue(this.$refs.dummyItem, 'height');
|
||||
|
||||
resolve();
|
||||
} else {
|
||||
setTimeout(checkHeights, RECHECK);
|
||||
}
|
||||
};
|
||||
|
||||
checkHeights();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
}
|
||||
|
||||
// If no selected option, then options are non-specific
|
||||
return '??px';
|
||||
return '??';
|
||||
},
|
||||
nonSpecific() {
|
||||
return this.options.nonSpecific === true;
|
||||
|
||||
Reference in New Issue
Block a user