Compare commits

...

16 Commits

Author SHA1 Message Date
Joshi
e9779ffbd0 Merge branch 'fix-plot-synchronized-actions' of https://github.com/nasa/openmct into fix-plot-synchronized-actions 2020-11-06 09:15:29 -08:00
Joshi
d1b7d08665 Revert the change as it breaks the inspector 2020-11-06 09:12:34 -08:00
Deep Tailor
606df95866 Merge branch 'master' into fix-plot-synchronized-actions 2020-11-02 13:19:21 -08:00
Joshi
f73518738b Remove console.log 2020-11-02 13:18:07 -08:00
Joshi
a1342c9b4f Append the scope id to the configId of a plot to make the configStore id unique 2020-11-02 12:58:24 -08:00
David Tsay
a8228406de [Inspector] Allow styles (including font and font size) to be saved and reused (#3432)
* working proto for font size

* wip

* Font styling

 - Base classes for font-size and font;
 - WIP!

* working data attribute for fontsize

* Font styling

 - Add `js-style-receiver` to markup, refine style targeting JS for
 better application of styles;
 - Refinements to font and size CSS;
 - WIP!

* Font styling

 - Redo CSS to use `data-*` attributes;
 - New `u-style-receiver` class for use as font-size and font-family CSS
 selector target;
 - New `js-style-receiver` class for use as JS target by ObjectView.vue;
 - New classes added to markup in all Open MCT views;
 - Changed font-size values from 'is-font-size--*' to just the number;
 - Some refinement to individual views to account for font-sizing
 capability;
 - Removed automatic font-size 13px being set by SubobjectView.vue;
 - WIP!

* working mixed styles

* Font styling

 - Added `u-style-receiver` to TelemetryView.vue;
 - Added `icon-font-size` to Font Size dropdown button;
 - TODO: better font-size icon;

* working font-family

* Font styling

 - Art for `icon-font-size` glyph updated;
 - Redefined glyph usage in some Layout toolbar buttons;
 - Updated font-size and font dropdown menus options text;

* Font styling

 - Refined font-size and font dropdown values;
 - Fixed toolbar-select-menu.vue to remove 'px' from non-specific option
  return;

* dont allow font styling on layouts that contain other layouts

* fix lint warning

* add sizing row

* fix bug with column width sizing

* fix bug with header style

* add saved styles inspector view

* WIP

* add vue component for selector

* WIP styles manager to communicate between vue components

* WIP saving and persisting styles

* no duplicate styles prevention

* fix props syntax

* WIP can apply conditional styles

* static styles do not work yet

* display border color in saved styles swatch

* allow deleting styles except default style

* WIP apply static style works but also to layout...

* prevent additional StylesView from being created

* delete style message

* change save order

* move applystyle to selector component

* rename for consistency

* naming refactor

* add style description

* update style properties only if they exist and do not erase properties

* refactor singleton usage

refactor save method

* show save and delete only on hover

* do not show delete icon if not in edit mode

* normalize styles before saving

prevent apply style if conditional and static styles are simultaneously selected

* remove default style

tweak selector display

* allow conditional and static styles to have saved style applied

limit saved styles to 20

* refactor styles manager

remove openmct dependency

use provide/inject

* resolve merge conflicts

* lint fix

* reorganize styles

* add font style editor to styles view

* save and display border correctly in saved styles view

* WIP add font styling controls to inspector styles view

* add font constants

* WIP refactor to provide reactive props

fix locked for edit

* WIP display consolidated font styles for selection in editor

* WIP font styles saved to layout

* WIP persisting font styles from inspector works

* fix styleable check

* move logic up to stylesview because save is two part

* apply font style to thumb

* there can be only one

* show font style for native views

* linting fix

* push stylesManager work to StylesView

* move method to computed

* move constant definition outside of function call

* Styling for saved styles functionality WIP

- Simplified and removed unnecessary markup;
- Standardized style applied to saved style element and toolbar control;
- Removed saved style expand arrow and description, replaced with item
title / tooltip approach;
- Standardized width of `c-style-thumb` element;
- Moved font size and style controls to the designed location;

* Styling for saved styles functionality WIP

- Layout and CSS normalization between style editor control and saved
style preview element;
- Control alignment refined;
- Moved font size and style controls to the designed location;

* Styling for saved styles functionality WIP

- Update font size icon art to normalize size;
- Sanding, tweaking, alignin and layout in style controls area of
Inspector;

* Styling for saved styles functionality WIP

- Hide the font size and style menu buttons unless the user is editing;

* remove font controls from toolbar

* turn styles tab into multipane element

* lint fix

* no font style should not be viewed as non-specific

* delete saved style by index not style

* cleanup

* view and inspector view updates on initial font change

* revert computed back to method

* set initial height

* fix test after removing 2 buttons from toolbar

* fix hidden lint error

* fix lint

Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-11-02 12:35:43 -08:00
Shefali Joshi
2401473012 [#3465] Intercept drag start event for imagery controls (#3485) 2020-11-02 11:26:33 -08:00
Charles Hacskaylo
e502fb88fa Fix Imagery brightness and contrast controls (#3473)
* Fix imagery #3467

- Move location of imagery controls in markup;
- Refine vertical placement;

* Fix imagery #3467

- Fix Firefox-related slider problems: bring over slider fixes and
markup from branch `imagery-view-layers`;

* Fix imagery #3467

- Fix linting problem;

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
2020-11-02 08:38:13 -08:00
Charles Hacskaylo
37a52cb011 Notebook fixes for NT10 'click-to-edit entry' (#3475)
* Notebook fixes for NT10 'click-to-edit entry'

- Hovering over entries now displays a subtle background change, and
only displays the 'inline input' look when clicked into;
- Changed default styling and behavior to not apply default text
content: new entries now start with a blank entry, and do not include
'placeholder' formatting;
- Refactored styles associated with `c-input-inline`, `c-ne__input` and
`reactive-input` mixin;
- New mixin `inlineInput`;
- Removed unused CSS classes, general cleanups;

* fixed defaultText as blank issue and some cleanup

* Update _mixins.scss

- Remove commented code;

Co-authored-by: Nikhil Mandlik <nikhil.k.mandlik@nasa.gov>
2020-10-30 16:47:29 -07:00
Nikhil
04fb4e8a82 [Tables] Object names should appear in tables (#3466)
* [Tables] Object names should appear in tables #3312

* updated tests to include name header.

* fixed lint issue.

* Removed Name from data.

* renamed 'addColunmName'  to 'addNameColumn'.

Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-10-30 15:10:31 -07:00
Jamie V
5646a252f7 [Navigation Tree] Simplify logic (#3474)
* added new navigation method for tracking, lots of optimizations

* updated indicator logic, tweaked objectPath/navigationPath, removed old code

* added temporary ancestors variable to be used while building new tree ui during navigation

* removed observer for ancestors, all handled in composition watch now

* updates from PR comments

* fixing testing errors

* checking for older format of saved path, update if old
2020-10-29 11:58:45 -07:00
Jamie V
0e6ce7f58b [Time Conductor] Realtime presets and history tracking (#3270)
Time conductor realtime preset/history updates

Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-10-28 17:46:28 -07:00
Nikhil
8cd6a4c6a3 [Notebook] Link to snapshot should not be a fully qualified url #3445 (#3460)
Co-authored-by: Andrew Henry <akhenry@gmail.com>
2020-10-28 16:46:54 -07:00
Shefali Joshi
02fc162197 Save subobject styles to container/layout if the object cannot be persisted (#3471)
* styles for Subobjects that can't be persisted should be saved on the container/layout

* Add tests for suboject styles that should be saved on the display layout
2020-10-26 15:58:42 -07:00
David Tsay
84d21a3695 [Display Layout] User should be able to set outer dimensions (#3333)
* Display Layout grid toggle and dimensions

- Added toggle grid button;
- Added Layout 'size' properties;
- Very WIP!

* Display Layout grid toggle and dimensions

- Cleanup toolbar;

* new configuration layoutDimensions

* add outer dimensions

* content dimensions not needed

* show/hide layout dimensions based on selection

* push non-dynamic styles to class definition

* remove grid code for other display layout feature

* reorder to match master

* layoutDimensionsStyle computed prop should return an object

* Styling for Display Layout dimensions box

- Mods to markup and SCSS;
- New ``$editDimensionsColor` theme constant;

* Styling for Display Layout dimensions box

- Refined styling;
- Fixed selector for nested sub-layouts;

* Styling for Display Layout dimensions box

- Added v-if that now only displays the dimensions indicator if both
width and height are greater than 0;

* fix lint issues

* fix merge issues

* fix display layout dimensions logic

* fix display layout dimensions check

Co-authored-by: charlesh88 <charlesh88@gmail.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov>
2020-10-23 12:19:16 -07:00
David Tsay
1a6369c2b9 [Display Layout] Grid lines should show and hide appropriately for nested layouts (#3330)
* change selector from sibling to same element

* hide gridlines for selected layout if is multi selection
2020-10-23 10:02:18 -07:00
65 changed files with 2308 additions and 711 deletions

View File

@@ -76,6 +76,7 @@ define([
workerRequest[prop] = Number(workerRequest[prop]);
});
workerRequest.name = domainObject.name;
return workerRequest;

View File

@@ -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),

View File

@@ -35,7 +35,13 @@
</body>
<script>
const THIRTY_SECONDS = 30 * 1000;
const THIRTY_MINUTES = THIRTY_SECONDS * 60;
const ONE_MINUTE = THIRTY_SECONDS * 2;
const FIVE_MINUTES = ONE_MINUTE * 5;
const FIFTEEN_MINUTES = FIVE_MINUTES * 3;
const THIRTY_MINUTES = FIFTEEN_MINUTES * 2;
const ONE_HOUR = THIRTY_MINUTES * 2;
const TWO_HOURS = ONE_HOUR * 2;
const ONE_DAY = ONE_HOUR * 24;
[
'example/eventGenerator'
@@ -73,21 +79,21 @@
{
label: 'Last Day',
bounds: {
start: () => Date.now() - 1000 * 60 * 60 * 24,
start: () => Date.now() - ONE_DAY,
end: () => Date.now()
}
},
{
label: 'Last 2 hours',
bounds: {
start: () => Date.now() - 1000 * 60 * 60 * 2,
start: () => Date.now() - TWO_HOURS,
end: () => Date.now()
}
},
{
label: 'Last hour',
bounds: {
start: () => Date.now() - 1000 * 60 * 60,
start: () => Date.now() - ONE_HOUR,
end: () => Date.now()
}
}
@@ -96,7 +102,7 @@
records: 10,
// maximum duration between start and end bounds
// for utc-based time systems this is in milliseconds
limit: 1000 * 60 * 60 * 24
limit: ONE_DAY
},
{
name: "Realtime",
@@ -105,7 +111,44 @@
clockOffsets: {
start: - THIRTY_MINUTES,
end: THIRTY_SECONDS
}
},
presets: [
{
label: '1 Hour',
bounds: {
start: - ONE_HOUR,
end: THIRTY_SECONDS
}
},
{
label: '30 Minutes',
bounds: {
start: - THIRTY_MINUTES,
end: THIRTY_SECONDS
}
},
{
label: '15 Minutes',
bounds: {
start: - FIFTEEN_MINUTES,
end: THIRTY_SECONDS
}
},
{
label: '5 Minutes',
bounds: {
start: - FIVE_MINUTES,
end: THIRTY_SECONDS
}
},
{
label: '1 Minute',
bounds: {
start: - ONE_MINUTE,
end: THIRTY_SECONDS
}
}
]
}
]
}));

View File

@@ -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>

View File

@@ -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'"

View File

@@ -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>

View File

@@ -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);
}
}
};

View File

@@ -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;
}
@@ -219,6 +314,18 @@ export default {
isItemType(type, item) {
return item && (item.type === type);
},
canPersistObject(item) {
// for now the only way to tell if an object can be persisted is if it is creatable.
let creatable = false;
if (item) {
const type = this.openmct.types.get(item.type);
if (type && type.definition) {
creatable = (type.definition.creatable === true);
}
}
return creatable;
},
hasConditionalStyle(domainObject, layoutItem) {
const id = layoutItem ? layoutItem.id : undefined;
@@ -235,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);
@@ -251,7 +353,7 @@ export default {
} else {
this.canHide = true;
domainObject = selectionItem[1].context.item;
if (item && !layoutItem || this.isItemType('subobject-view', layoutItem)) {
if (item && !layoutItem || (this.isItemType('subobject-view', layoutItem) && this.canPersistObject(item))) {
subObjects.push(item);
itemStyle = getApplicableStylesForItem(item);
if (this.hasConditionalStyle(item)) {
@@ -275,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) {
@@ -298,6 +400,7 @@ export default {
isKeyItemId(key) {
return (key !== 'styles')
&& (key !== 'staticStyle')
&& (key !== 'fontStyle')
&& (key !== 'defaultConditionId')
&& (key !== 'selectedConditionId')
&& (key !== 'conditionSetIdentifier');
@@ -637,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';
}
}
};

View File

@@ -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;
}

View File

@@ -146,6 +146,8 @@ describe('the plugin', function () {
let displayLayoutItem;
let lineLayoutItem;
let boxLayoutItem;
let notCreatableObjectItem;
let notCreatableObject;
let selection;
let component;
let styleViewComponentObject;
@@ -264,6 +266,19 @@ describe('the plugin', function () {
"stroke": "#717171",
"type": "line-view",
"id": "57d49a28-7863-43bd-9593-6570758916f0"
},
{
"width": 32,
"height": 18,
"x": 36,
"y": 8,
"identifier": {
"key": "~TEST~image",
"namespace": "test-space"
},
"hasFrame": true,
"type": "subobject-view",
"id": "6d9fe81b-a3ce-4e59-b404-a4a0be1a5d85"
}
],
"layoutGrid": [
@@ -297,6 +312,52 @@ describe('the plugin', function () {
"type": "box-view",
"id": "89b88746-d325-487b-aec4-11b79afff9e8"
};
notCreatableObjectItem = {
"width": 32,
"height": 18,
"x": 36,
"y": 8,
"identifier": {
"key": "~TEST~image",
"namespace": "test-space"
},
"hasFrame": true,
"type": "subobject-view",
"id": "6d9fe81b-a3ce-4e59-b404-a4a0be1a5d85"
};
notCreatableObject = {
"identifier": {
"key": "~TEST~image",
"namespace": "test-space"
},
"name": "test~image",
"location": "test-space:~TEST",
"type": "test.image",
"telemetry": {
"values": [
{
"key": "value",
"name": "Value",
"hints": {
"image": 1,
"priority": 0
},
"format": "image",
"source": "value"
},
{
"key": "utc",
"source": "timestamp",
"name": "Timestamp",
"format": "iso",
"hints": {
"domain": 1,
"priority": 1
}
}
]
}
};
selection = [
[{
context: {
@@ -316,6 +377,19 @@ describe('the plugin', function () {
"index": 0
}
},
{
context: {
item: displayLayoutItem,
"supportsMultiSelect": true
}
}],
[{
context: {
"item": notCreatableObject,
"layoutItem": notCreatableObjectItem,
"index": 2
}
},
{
context: {
item: displayLayoutItem,
@@ -344,7 +418,7 @@ describe('the plugin', function () {
});
it('initializes the items in the view', () => {
expect(styleViewComponentObject.items.length).toBe(2);
expect(styleViewComponentObject.items.length).toBe(3);
});
it('initializes conditional styles', () => {
@@ -363,7 +437,7 @@ describe('the plugin', function () {
return Vue.nextTick().then(() => {
expect(styleViewComponentObject.domainObject.configuration.objectStyles).toBeDefined();
[boxLayoutItem, lineLayoutItem].forEach((item) => {
[boxLayoutItem, lineLayoutItem, notCreatableObjectItem].forEach((item) => {
const itemStyles = styleViewComponentObject.domainObject.configuration.objectStyles[item.id].styles;
expect(itemStyles.length).toBe(2);
const foundStyle = itemStyles.find((style) => {
@@ -385,7 +459,7 @@ describe('the plugin', function () {
return Vue.nextTick().then(() => {
expect(styleViewComponentObject.domainObject.configuration.objectStyles).toBeDefined();
[boxLayoutItem, lineLayoutItem].forEach((item) => {
[boxLayoutItem, lineLayoutItem, notCreatableObjectItem].forEach((item) => {
const itemStyle = styleViewComponentObject.domainObject.configuration.objectStyles[item.id].staticStyle;
expect(itemStyle).toBeDefined();
const applicableStyles = getApplicableStylesForItem(styleViewComponentObject.domainObject, item);

View File

@@ -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">

View File

@@ -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),

View File

@@ -56,6 +56,28 @@ define(function () {
1
],
required: true
},
{
name: "Horizontal size (px)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
property: [
"configuration",
"layoutDimensions",
0
],
required: false
},
{
name: "Vertical size (px)",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
property: [
"configuration",
"layoutDimensions",
1
],
required: false
}
]
};

View File

@@ -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>

View File

@@ -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
@@ -36,7 +36,15 @@
:grid-size="gridSize"
:show-grid="showGrid"
/>
<div
v-if="shouldDisplayLayoutDimensions"
class="l-layout__dimensions"
:style="layoutDimensionsStyle"
>
<div class="l-layout__dimensions-vals">
{{ layoutDimensions[0] }},{{ layoutDimensions[1] }}
</div>
</div>
<component
:is="item.type"
v-for="(item, index) in layoutItems"
@@ -165,6 +173,23 @@ export default {
return this.itemIsInCurrentSelection(item);
});
},
layoutDimensions() {
return this.internalDomainObject.configuration.layoutDimensions;
},
shouldDisplayLayoutDimensions() {
return this.layoutDimensions
&& this.layoutDimensions[0] > 0
&& this.layoutDimensions[1] > 0;
},
layoutDimensionsStyle() {
const width = `${this.layoutDimensions[0]}px`;
const height = `${this.layoutDimensions[1]}px`;
return {
width,
height
};
},
showMarquee() {
let selectionPath = this.selection[0];
let singleSelectedLine = this.selection.length === 1

View File

@@ -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 + ')';

View File

@@ -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
};
},

View File

@@ -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;

View File

@@ -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);
}
},

View File

@@ -17,10 +17,29 @@
flex-direction: column;
overflow: auto;
&__grid-holder {
&__grid-holder,
&__dimensions {
display: none;
}
&__dimensions {
$b: 1px dashed $editDimensionsColor;
border-right: $b;
border-bottom: $b;
pointer-events: none;
position: absolute;
&-vals {
$p: 2px;
color: $editDimensionsColor;
display: inline-block;
font-style: italic;
position: absolute;
bottom: $p; right: $p;
opacity: 0.7;
}
}
&__frame {
position: absolute;
}
@@ -34,6 +53,10 @@
> .l-layout {
background: $editUIGridColorBg;
> [class*="__dimensions"] {
display: block;
}
> [class*="__grid-holder"] {
display: block;
}
@@ -42,12 +65,16 @@
}
.l-layout__frame {
&[s-selected],
&[s-selected]:not([multi-select="true"]),
&[s-selected-parent] {
// Display grid and allow edit marquee to display in nested layouts when editing
> * > * > .l-layout + .allow-editing {
> * > * > .l-layout.allow-editing {
box-shadow: inset $editUIGridColorFg 0 0 2px 1px;
> [class*="__dimensions"] {
display: block;
}
> [class*='grid-holder'] {
display: block;
}

View File

@@ -27,6 +27,7 @@ export default {
inject: ['openmct'],
data() {
return {
objectStyle: undefined,
itemStyle: undefined,
styleClass: ''
};

View File

@@ -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);
});
});
});

View File

@@ -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;

View File

@@ -19,13 +19,24 @@
}
&__main-image {
background-position: center;
background-repeat: no-repeat;
background-size: contain;
height: 100%;
&__bg,
&__image {
height: 100%;
}
&.unnsynced{
@include sUnsynced();
&__bg {
background-color: $colorPlotBg;
border: 1px solid transparent;
&.unnsynced{
@include sUnsynced();
}
}
&__image {
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
}
@@ -138,11 +149,6 @@
}
}
.s-image-main {
background-color: $colorPlotBg;
border: 1px solid transparent;
}
/*************************************** IMAGERY LOCAL CONTROLS*/
.c-imagery {
.h-local-controls--overlay-content {
@@ -152,7 +158,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 +179,7 @@
&__lc {
&__reset-btn {
$bc: $scrollbarTrackColorBg;
&:before,
&:after {
border-right: 1px solid $bc;
@@ -195,6 +202,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 +258,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;

View File

@@ -143,7 +143,8 @@ export default {
this.openmct.notifications.alert(message);
}
window.location.href = link;
const url = new URL(link);
window.location.href = url.hash;
},
formatTime(unixTime, timeFormat) {
return Moment.utc(unixTime).format(timeFormat);

View File

@@ -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;

View File

@@ -111,7 +111,7 @@ export default {
const bounds = this.openmct.time.bounds();
const link = !this.ignoreLink
? window.location.href
? window.location.hash
: null;
const objectPath = this.objectPath || this.openmct.router.path;

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View 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;
});

View 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>

View File

@@ -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;

View File

@@ -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();
}
}
};

View File

@@ -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", () => {

View File

@@ -141,10 +141,11 @@
<ConductorMode class="c-conductor__mode-select" />
<ConductorTimeSystem class="c-conductor__time-system-select" />
<ConductorHistory
v-if="isFixed"
class="c-conductor__history-select"
:offsets="openmct.time.clockOffsets()"
:bounds="bounds"
:time-system="timeSystem"
:mode="timeMode"
/>
</div>
<input
@@ -210,6 +211,11 @@ export default {
isZooming: false
};
},
computed: {
timeMode() {
return this.isFixed ? 'fixed' : 'realtime';
}
},
mounted() {
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp);

View File

@@ -66,7 +66,9 @@
<script>
import toggleMixin from '../../ui/mixins/toggle-mixin';
const LOCAL_STORAGE_HISTORY_KEY = 'tcHistory';
const DEFAULT_DURATION_FORMATTER = 'duration';
const LOCAL_STORAGE_HISTORY_KEY_FIXED = 'tcHistory';
const LOCAL_STORAGE_HISTORY_KEY_REALTIME = 'tcHistoryRealtime';
const DEFAULT_RECORDS = 10;
export default {
@@ -77,72 +79,115 @@ export default {
type: Object,
required: true
},
offsets: {
type: Object,
required: false,
default: () => {}
},
timeSystem: {
type: Object,
required: true
},
mode: {
type: String,
required: true
}
},
data() {
return {
/**
* previous bounds entries available for easy re-use
* @history array of timespans
* @realtimeHistory array of timespans
* @timespans {start, end} number representing timestamp
*/
history: this.getHistoryFromLocalStorage(),
realtimeHistory: {},
/**
* previous bounds entries available for easy re-use
* @fixedHistory array of timespans
* @timespans {start, end} number representing timestamp
*/
fixedHistory: {},
presets: []
};
},
computed: {
currentHistory() {
return this.mode + 'History';
},
isFixed() {
return this.openmct.time.clock() === undefined;
},
hasHistoryPresets() {
return this.timeSystem.isUTCBased && this.presets.length;
},
historyForCurrentTimeSystem() {
const history = this.history[this.timeSystem.key];
const history = this[this.currentHistory][this.timeSystem.key];
return history;
},
storageKey() {
let key = LOCAL_STORAGE_HISTORY_KEY_FIXED;
if (this.mode !== 'fixed') {
key = LOCAL_STORAGE_HISTORY_KEY_REALTIME;
}
return key;
}
},
watch: {
bounds: {
handler() {
// only for fixed time since we track offsets for realtime
if (this.isFixed) {
this.addTimespan();
}
},
deep: true
},
offsets: {
handler() {
this.addTimespan();
},
deep: true
},
timeSystem: {
handler() {
handler(ts) {
this.loadConfiguration();
this.addTimespan();
},
deep: true
},
mode: function () {
this.getHistoryFromLocalStorage();
this.initializeHistoryIfNoHistory();
this.loadConfiguration();
}
},
mounted() {
this.getHistoryFromLocalStorage();
this.initializeHistoryIfNoHistory();
},
methods: {
getHistoryFromLocalStorage() {
const localStorageHistory = localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY);
const localStorageHistory = localStorage.getItem(this.storageKey);
const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
return history;
this[this.currentHistory] = history;
},
initializeHistoryIfNoHistory() {
if (!this.history) {
this.history = {};
if (!this[this.currentHistory]) {
this[this.currentHistory] = {};
this.persistHistoryToLocalStorage();
}
},
persistHistoryToLocalStorage() {
localStorage.setItem(LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(this.history));
localStorage.setItem(this.storageKey, JSON.stringify(this[this.currentHistory]));
},
addTimespan() {
const key = this.timeSystem.key;
let [...currentHistory] = this.history[key] || [];
let [...currentHistory] = this[this.currentHistory][key] || [];
const timespan = {
start: this.bounds.start,
end: this.bounds.end
start: this.isFixed ? this.bounds.start : this.offsets.start,
end: this.isFixed ? this.bounds.end : this.offsets.end
};
let self = this;
@@ -160,20 +205,24 @@ export default {
}
currentHistory.unshift(timespan);
this.history[key] = currentHistory;
this.$set(this[this.currentHistory], key, currentHistory);
this.persistHistoryToLocalStorage();
},
selectTimespan(timespan) {
this.openmct.time.bounds(timespan);
if (this.isFixed) {
this.openmct.time.bounds(timespan);
} else {
this.openmct.time.clockOffsets(timespan);
}
},
selectPresetBounds(bounds) {
const start = typeof bounds.start === 'function' ? bounds.start() : bounds.start;
const end = typeof bounds.end === 'function' ? bounds.end() : bounds.end;
this.selectTimespan({
start: start,
end: end
start,
end
});
},
loadConfiguration() {
@@ -184,7 +233,9 @@ export default {
this.records = this.loadRecords(configurations);
},
loadPresets(configurations) {
const configuration = configurations.find(option => option.presets);
const configuration = configurations.find(option => {
return option.presets && option.name.toLowerCase() === this.mode;
});
const presets = configuration ? configuration.presets : [];
return presets;
@@ -196,11 +247,24 @@ export default {
return records;
},
formatTime(time) {
let format = this.timeSystem.timeFormat;
let isNegativeOffset = false;
if (!this.isFixed) {
if (time < 0) {
isNegativeOffset = true;
}
time = Math.abs(time);
format = this.timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER;
}
const formatter = this.openmct.telemetry.getValueFormatter({
format: this.timeSystem.timeFormat
format: format
}).formatter;
return formatter.format(time);
return (isNegativeOffset ? '-' : '') + formatter.format(time);
}
}
};

View File

@@ -173,6 +173,7 @@ $editUIAreaShdw: $editUIAreaBaseColor 0 0 0 2px; // Edit area s-selected-parent
$editUIAreaShdwSelected: $editUIAreaBaseColor 0 0 0 3px; // Edit area s-selected
$editUIGridColorBg: rgba($editUIBaseColor, 0.2); // Background of layout editing area
$editUIGridColorFg: rgba(#000, 0.1); // Grid lines in layout editing area
$editDimensionsColor: #6a5ea6;
$editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-selected frames in a layout; move-bar on frame hover
$editFrameBorder: 1px dotted $editFrameColor;
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects

View File

@@ -177,6 +177,7 @@ $editUIAreaShdw: $editUIAreaBaseColor 0 0 0 2px; // Edit area s-selected-parent
$editUIAreaShdwSelected: $editUIAreaBaseColor 0 0 0 3px; // Edit area s-selected
$editUIGridColorBg: rgba($editUIBaseColor, 0.2); // Background of layout editing area
$editUIGridColorFg: rgba(#000, 0.1); // Grid lines in layout editing area
$editDimensionsColor: #6a5ea6;
$editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-selected frames in a layout; move-bar on frame hover
$editFrameBorder: 1px dotted $editFrameColor;
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects

View File

@@ -173,6 +173,7 @@ $editUIAreaShdw: $editUIAreaBaseColor 0 0 0 2px; // Edit area s-selected-parent
$editUIAreaShdwSelected: $editUIAreaBaseColor 0 0 0 3px; // Edit area s-selected
$editUIGridColorBg: rgba($editUIBaseColor, 0.2); // Background of layout editing area
$editUIGridColorFg: rgba($editUIBaseColor, 0.3); // Grid lines in layout editing area
$editDimensionsColor: #d7aeff;
$editFrameColor: $browseFrameColor; // Solid or dotted border applied to non-selected frames in a layout; move-bar on frame hover
$editFrameBorder: 1px dotted $editFrameColor;
$editFrameColorHov: $editUIColor; // Solid border hover on frames; hover should not be applied to selected objects

View File

@@ -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';

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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": [
{},
{}
]
}
},
{

View File

@@ -100,7 +100,7 @@
<glyph unicode="&#xea2c;" 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="&#xea2d;" 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="&#xea2e;" 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="&#xea2f;" 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="&#xea2f;" 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="&#xea30;" 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="&#xea31;" 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="&#xea32;" 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

View File

@@ -275,21 +275,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;
}
}

View File

@@ -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() {

View File

@@ -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;
}
}
};

View File

@@ -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 {

View File

@@ -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

View 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>

View 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>

View 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>

View 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>

View File

@@ -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: {

View 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;

View 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'
}
];

View File

@@ -26,6 +26,10 @@
overflow: hidden;
transition: all;
.c-tree__item-h {
width: 100%;
}
&__scrollable-children {
overflow: auto;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<template>
<li
<div
ref="me"
:style="{
'top': virtualScroll ? itemTop : 'auto',
@@ -24,7 +24,7 @@
<object-label
:domain-object="node.object"
:object-path="node.objectPath"
:navigate-to-path="navigateToPath"
:navigate-to-path="navigationPath"
:style="{ paddingLeft: leftOffset }"
/>
<view-control
@@ -34,7 +34,7 @@
:enabled="hasComposition && showDown"
/>
</div>
</li>
</div>
</template>
<script>
@@ -83,18 +83,14 @@ export default {
virtualScroll: {
type: Boolean,
default: false
},
shouldEmitHeight: {
type: Boolean,
default: false
}
},
data() {
this.navigateToPath = this.buildPathString(this.node.navigateToParent);
this.navigationPath = this.node.navigationPath;
return {
hasComposition: false,
navigated: this.navigateToPath === this.openmct.router.currentLocation.path,
navigated: this.isNavigated(),
expanded: false
};
},
@@ -122,9 +118,6 @@ export default {
mounted() {
let objectComposition = this.openmct.composition.get(this.node.object);
// only reliable way to get final item height
this.readyStateCheck();
this.domainObject = this.node.object;
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
this.domainObject = newObject;
@@ -139,30 +132,13 @@ export default {
},
destroyed() {
this.openmct.router.off('change:path', this.highlightIfNavigated);
document.removeEventListener('readystatechange', this.emitHeight);
},
methods: {
readyStateCheck() {
if (document.readyState !== 'complete') {
document.addEventListener('readystatechange', this.emitHeight);
} else {
this.emitHeight();
}
isNavigated() {
return this.navigationPath === this.openmct.router.currentLocation.path;
},
emitHeight() {
if (this.shouldEmitHeight && document.readyState === 'complete') {
this.$emit('emittedHeight', this.$el.offsetHeight);
}
},
buildPathString(parentPath) {
return [parentPath, this.openmct.objects.makeKeyString(this.node.object.identifier)].join('/');
},
highlightIfNavigated(newPath, oldPath) {
if (newPath === this.navigateToPath) {
this.navigated = true;
} else if (oldPath === this.navigateToPath) {
this.navigated = false;
}
highlightIfNavigated() {
this.navigated = this.isNavigated();
},
resetTreeHere() {
this.$emit('resetTree', this.node);

View File

@@ -51,7 +51,7 @@ export default {
}
// If no selected option, then options are non-specific
return '??px';
return '??';
},
nonSpecific() {
return this.options.nonSpecific === true;