mirror of
https://github.com/nasa/openmct.git
synced 2025-07-01 21:01:11 +00:00
Compare commits
13 Commits
select-tab
...
notebook-s
Author | SHA1 | Date | |
---|---|---|---|
0a820aa869 | |||
7c92b6d206 | |||
aafe524454 | |||
e84ade1752 | |||
3b094e43e3 | |||
e6a7b4ed6c | |||
97230bb21f | |||
768d99d928 | |||
c760190a29 | |||
395a1caeec | |||
339b315642 | |||
601d59d6bd | |||
c6e2d5a363 |
@ -78,7 +78,7 @@
|
|||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
openmct.install(openmct.plugins.SummaryWidget());
|
openmct.install(openmct.plugins.SummaryWidget());
|
||||||
openmct.install(openmct.plugins.Notebook());
|
openmct.install(openmct.plugins.NotebookSVC());
|
||||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||||
openmct.install(openmct.plugins.ObjectMigration());
|
openmct.install(openmct.plugins.ObjectMigration());
|
||||||
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "0.14.0-SNAPSHOT",
|
"version": "1.0.0-beta",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -26,6 +26,7 @@ const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "li
|
|||||||
export default class LegacyContextMenuAction {
|
export default class LegacyContextMenuAction {
|
||||||
constructor(openmct, LegacyAction) {
|
constructor(openmct, LegacyAction) {
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
|
this.key = LegacyAction.definition.key;
|
||||||
this.name = LegacyAction.definition.name;
|
this.name = LegacyAction.definition.name;
|
||||||
this.description = LegacyAction.definition.description;
|
this.description = LegacyAction.definition.description;
|
||||||
this.cssClass = LegacyAction.definition.cssClass;
|
this.cssClass = LegacyAction.definition.cssClass;
|
||||||
|
@ -49,6 +49,9 @@ class ContextMenuAPI {
|
|||||||
* a single sentence or short paragraph) of this kind of view
|
* a single sentence or short paragraph) of this kind of view
|
||||||
* @property {string} cssClass the CSS class to apply to labels for this
|
* @property {string} cssClass the CSS class to apply to labels for this
|
||||||
* view (to add icons, for instance)
|
* view (to add icons, for instance)
|
||||||
|
* @property {string} key unique key to identify the context menu action
|
||||||
|
* (used in custom context menu eg table rows, to identify which actions to include)
|
||||||
|
* @property {boolean} hideInDefaultMenu optional flag to hide action from showing in the default context menu (tree item)
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @method appliesTo
|
* @method appliesTo
|
||||||
@ -72,12 +75,21 @@ class ContextMenuAPI {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_showContextMenuForObjectPath(objectPath, x, y) {
|
_showContextMenuForObjectPath(objectPath, x, y, actionsToBeIncluded) {
|
||||||
|
|
||||||
let applicableActions = this._allActions.filter((action) => {
|
let applicableActions = this._allActions.filter((action) => {
|
||||||
if (action.appliesTo === undefined) {
|
|
||||||
return true;
|
if (actionsToBeIncluded) {
|
||||||
|
if (action.appliesTo === undefined && actionsToBeIncluded.includes(action.key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return action.appliesTo(objectPath, actionsToBeIncluded) && actionsToBeIncluded.includes(action.key);
|
||||||
|
} else {
|
||||||
|
if (action.appliesTo === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return action.appliesTo(objectPath) && !action.hideInDefaultMenu;
|
||||||
}
|
}
|
||||||
return action.appliesTo(objectPath);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._activeContextMenu) {
|
if (this._activeContextMenu) {
|
||||||
|
@ -38,7 +38,7 @@ define([
|
|||||||
canEdit: function (domainObject) {
|
canEdit: function (domainObject) {
|
||||||
return domainObject.type === 'LadTableSet';
|
return domainObject.type === 'LadTableSet';
|
||||||
},
|
},
|
||||||
view: function (domainObject) {
|
view: function (domainObject, isEditing, objectPath) {
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -49,7 +49,8 @@ define([
|
|||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
domainObject
|
domainObject,
|
||||||
|
objectPath
|
||||||
},
|
},
|
||||||
el: element,
|
el: element,
|
||||||
template: '<lad-table-set></lad-table-set>'
|
template: '<lad-table-set></lad-table-set>'
|
||||||
|
@ -38,7 +38,7 @@ define([
|
|||||||
canEdit: function (domainObject) {
|
canEdit: function (domainObject) {
|
||||||
return domainObject.type === 'LadTable';
|
return domainObject.type === 'LadTable';
|
||||||
},
|
},
|
||||||
view: function (domainObject) {
|
view: function (domainObject, isEditing, objectPath) {
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -49,7 +49,8 @@ define([
|
|||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
domainObject
|
domainObject,
|
||||||
|
objectPath
|
||||||
},
|
},
|
||||||
el: element,
|
el: element,
|
||||||
template: '<lad-table-component></lad-table-component>'
|
template: '<lad-table-component></lad-table-component>'
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
@ -21,7 +22,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<tr>
|
<tr @contextmenu.prevent="showContextMenu">
|
||||||
<td>{{name}}</td>
|
<td>{{name}}</td>
|
||||||
<td>{{timestamp}}</td>
|
<td>{{timestamp}}</td>
|
||||||
<td :class="valueClass">
|
<td :class="valueClass">
|
||||||
@ -35,15 +36,25 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
const CONTEXT_MENU_ACTIONS = [
|
||||||
|
'viewHistoricalData',
|
||||||
|
'remove'
|
||||||
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct', 'objectPath'],
|
||||||
props: ['domainObject'],
|
props: ['domainObject'],
|
||||||
data() {
|
data() {
|
||||||
|
let currentObjectPath = this.objectPath.slice();
|
||||||
|
currentObjectPath.unshift(this.domainObject);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: this.domainObject.name,
|
name: this.domainObject.name,
|
||||||
timestamp: '---',
|
timestamp: '---',
|
||||||
value: '---',
|
value: '---',
|
||||||
valueClass: ''
|
valueClass: '',
|
||||||
|
currentObjectPath
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -73,11 +84,15 @@ export default {
|
|||||||
.request(this.domainObject, {strategy: 'latest'})
|
.request(this.domainObject, {strategy: 'latest'})
|
||||||
.then((array) => this.updateValues(array[array.length - 1]));
|
.then((array) => this.updateValues(array[array.length - 1]));
|
||||||
|
|
||||||
|
},
|
||||||
|
showContextMenu(event) {
|
||||||
|
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||||
|
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
|
||||||
this.limitEvaluator = openmct
|
this.limitEvaluator = openmct
|
||||||
.telemetry
|
.telemetry
|
||||||
|
@ -44,7 +44,7 @@ import lodash from 'lodash';
|
|||||||
import LadRow from './LADRow.vue';
|
import LadRow from './LADRow.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'domainObject'],
|
inject: ['openmct', 'domainObject', 'objectPath'],
|
||||||
components: {
|
components: {
|
||||||
LadRow
|
LadRow
|
||||||
},
|
},
|
||||||
|
@ -46,13 +46,14 @@ define([
|
|||||||
|
|
||||||
return selection.every(isTelemetryObject);
|
return selection.every(isTelemetryObject);
|
||||||
},
|
},
|
||||||
view: function (selection) {
|
view: function (domainObject, isEditing, objectPath) {
|
||||||
let component;
|
let component;
|
||||||
return {
|
return {
|
||||||
show: function (element) {
|
show: function (element) {
|
||||||
component = new Vue({
|
component = new Vue({
|
||||||
provide: {
|
provide: {
|
||||||
openmct
|
openmct,
|
||||||
|
objectPath
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
AlphanumericFormatView: AlphanumericFormatView.default
|
AlphanumericFormatView: AlphanumericFormatView.default
|
||||||
|
@ -202,7 +202,7 @@
|
|||||||
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
|
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['openmct', 'options'],
|
inject: ['openmct', 'options', 'objectPath'],
|
||||||
props: ['domainObject'],
|
props: ['domainObject'],
|
||||||
components: components,
|
components: components,
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
@endMove="() => $emit('endMove')">
|
@endMove="() => $emit('endMove')">
|
||||||
<object-frame v-if="domainObject"
|
<object-frame v-if="domainObject"
|
||||||
:domain-object="domainObject"
|
:domain-object="domainObject"
|
||||||
:object-path="objectPath"
|
:object-path="currentObjectPath"
|
||||||
:has-frame="item.hasFrame"
|
:has-frame="item.hasFrame"
|
||||||
:show-edit-view="false"
|
:show-edit-view="false"
|
||||||
ref="objectFrame">
|
ref="objectFrame">
|
||||||
@ -71,7 +71,7 @@
|
|||||||
hasFrame: hasFrameByDefault(domainObject.type)
|
hasFrame: hasFrameByDefault(domainObject.type)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct', 'objectPath'],
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object,
|
||||||
gridSize: Array,
|
gridSize: Array,
|
||||||
@ -81,7 +81,7 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
domainObject: undefined,
|
domainObject: undefined,
|
||||||
objectPath: []
|
currentObjectPath: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@ -100,7 +100,7 @@
|
|||||||
methods: {
|
methods: {
|
||||||
setObject(domainObject) {
|
setObject(domainObject) {
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.objectPath = [this.domainObject].concat(this.openmct.router.path);
|
this.currentObjectPath = [this.domainObject].concat(this.objectPath.slice());
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
let childContext = this.$refs.objectFrame.getSelectionContext();
|
let childContext = this.$refs.objectFrame.getSelectionContext();
|
||||||
childContext.item = domainObject;
|
childContext.item = domainObject;
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
@endMove="() => $emit('endMove')">
|
@endMove="() => $emit('endMove')">
|
||||||
<div class="c-telemetry-view"
|
<div class="c-telemetry-view"
|
||||||
:style="styleObject"
|
:style="styleObject"
|
||||||
v-if="domainObject">
|
v-if="domainObject"
|
||||||
|
@contextmenu.prevent="showContextMenu">
|
||||||
<div v-if="showLabel"
|
<div v-if="showLabel"
|
||||||
class="c-telemetry-view__label">
|
class="c-telemetry-view__label">
|
||||||
<div class="c-telemetry-view__label-text">{{ domainObject.name }}</div>
|
<div class="c-telemetry-view__label-text">{{ domainObject.name }}</div>
|
||||||
@ -82,7 +83,8 @@
|
|||||||
import printj from 'printj'
|
import printj from 'printj'
|
||||||
|
|
||||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||||
DEFAULT_POSITION = [1, 1];
|
DEFAULT_POSITION = [1, 1],
|
||||||
|
CONTEXT_MENU_ACTIONS = ['viewHistoricalData'];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||||
@ -103,7 +105,7 @@
|
|||||||
size: "13px"
|
size: "13px"
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct', 'objectPath'],
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object,
|
||||||
gridSize: Array,
|
gridSize: Array,
|
||||||
@ -163,7 +165,8 @@
|
|||||||
return {
|
return {
|
||||||
datum: undefined,
|
datum: undefined,
|
||||||
formats: undefined,
|
formats: undefined,
|
||||||
domainObject: undefined
|
domainObject: undefined,
|
||||||
|
currentObjectPath: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -218,12 +221,16 @@
|
|||||||
},
|
},
|
||||||
setObject(domainObject) {
|
setObject(domainObject) {
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
|
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||||
this.requestHistoricalData();
|
this.requestHistoricalData();
|
||||||
this.subscribeToObject();
|
this.subscribeToObject();
|
||||||
|
|
||||||
|
this.currentObjectPath = this.objectPath.slice();
|
||||||
|
this.currentObjectPath.unshift(this.domainObject);
|
||||||
|
|
||||||
this.context = {
|
this.context = {
|
||||||
item: domainObject,
|
item: domainObject,
|
||||||
layoutItem: this.item,
|
layoutItem: this.item,
|
||||||
@ -235,6 +242,9 @@
|
|||||||
},
|
},
|
||||||
updateTelemetryFormat(format) {
|
updateTelemetryFormat(format) {
|
||||||
this.$emit('formatChanged', this.item, format);
|
this.$emit('formatChanged', this.item, format);
|
||||||
|
},
|
||||||
|
showContextMenu(event) {
|
||||||
|
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -37,7 +37,7 @@ export default function DisplayLayoutPlugin(options) {
|
|||||||
canEdit: function (domainObject) {
|
canEdit: function (domainObject) {
|
||||||
return domainObject.type === 'layout';
|
return domainObject.type === 'layout';
|
||||||
},
|
},
|
||||||
view: function (domainObject) {
|
view: function (domainObject, isEditing, objectPath) {
|
||||||
let component;
|
let component;
|
||||||
return {
|
return {
|
||||||
show(container) {
|
show(container) {
|
||||||
@ -49,13 +49,14 @@ export default function DisplayLayoutPlugin(options) {
|
|||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
objectUtils,
|
objectUtils,
|
||||||
options
|
options,
|
||||||
|
objectPath
|
||||||
},
|
},
|
||||||
el: container,
|
el: container,
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
domainObject: domainObject
|
domainObject: domainObject
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
export default class GoToOriginalAction {
|
export default class GoToOriginalAction {
|
||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
this.name = 'Go To Original';
|
this.name = 'Go To Original';
|
||||||
|
this.key = 'goToOriginal';
|
||||||
this.description = 'Go to the original unlinked instance of this object';
|
this.description = 'Go to the original unlinked instance of this object';
|
||||||
|
|
||||||
this._openmct = openmct;
|
this._openmct = openmct;
|
||||||
|
31
src/plugins/notebook-svc/components/embed.vue
Normal file
31
src/plugins/notebook-svc/components/embed.vue
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div class="c-ne__embed">
|
||||||
|
<div class="c-ne__embed__snap-thumb"
|
||||||
|
v-if="embed.snapshot">
|
||||||
|
<img v-bind:src="embed.snapshot.src">
|
||||||
|
</div>
|
||||||
|
<div class="c-ne__embed__info">
|
||||||
|
<div class="c-ne__embed__name">
|
||||||
|
<a class="c-ne__embed__link"
|
||||||
|
:href="objectLink"
|
||||||
|
:class="embed.cssClass">
|
||||||
|
{{embed.name}}
|
||||||
|
</a>
|
||||||
|
<a class="c-ne__embed__context-available icon-arrow-down"></a>
|
||||||
|
</div>
|
||||||
|
<div class="c-ne__embed__time" v-if="embed.snapshot">
|
||||||
|
{{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import objectLinkMixin from '../../../ui/mixins/object-link';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'formatTime'],
|
||||||
|
props: ['embed'],
|
||||||
|
mixins: [objectLinkMixin]
|
||||||
|
}
|
||||||
|
</script>
|
63
src/plugins/notebook-svc/components/entry.vue
Normal file
63
src/plugins/notebook-svc/components/entry.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<li class="c-notebook__entry c-ne has-local-controls"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop.prevent="onTreeItemDrop">
|
||||||
|
<div class="c-ne__time-and-content">
|
||||||
|
<div class="c-ne__time">
|
||||||
|
<span>{{formatTime(entry.createdOn, 'YYYY-MM-DD')}}</span>
|
||||||
|
<span>{{formatTime(entry.createdOn, 'HH:mm:ss')}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="c-ne__content">
|
||||||
|
<div class="c-ne__text c-input-inline"
|
||||||
|
contenteditable="true"
|
||||||
|
ref="contenteditable"
|
||||||
|
@blur="updateEntry"
|
||||||
|
v-html="entry.text">
|
||||||
|
</div>
|
||||||
|
<div class="c-ne__embeds">
|
||||||
|
<notebook-embed
|
||||||
|
v-for="embed in entry.embeds"
|
||||||
|
:key="embed.id"
|
||||||
|
:embed="embed"
|
||||||
|
:objectPath="embed.objectPath">
|
||||||
|
</notebook-embed>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="c-ne__local-controls--hidden">
|
||||||
|
<button class="c-click-icon c-click-icon--major icon-trash"
|
||||||
|
title="Delete this entry"
|
||||||
|
@click="deleteEntry">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NotebookEmbed from './embed.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['formatTime'],
|
||||||
|
props: ['entry'],
|
||||||
|
components: {
|
||||||
|
NotebookEmbed
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateEntry(event) {
|
||||||
|
this.$emit('update-entry', this.entry.id, event.target.innerText);
|
||||||
|
},
|
||||||
|
deleteEntry() {
|
||||||
|
this.$emit('delete-entry', this.entry.id);
|
||||||
|
},
|
||||||
|
onTreeItemDrop(event) {
|
||||||
|
this.$emit('drop-embed', this.entry.id, event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (!this.entry.text && !this.entry.embeds.length) {
|
||||||
|
this.$refs.contenteditable.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
198
src/plugins/notebook-svc/components/notebook.vue
Normal file
198
src/plugins/notebook-svc/components/notebook.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<template>
|
||||||
|
<div class="c-notebook"
|
||||||
|
@dragover.stop
|
||||||
|
@drop.stop>
|
||||||
|
<div class="c-notebook__head">
|
||||||
|
<search class="c-notebook__search"
|
||||||
|
:value="searchValue"
|
||||||
|
@input="searchEntries"
|
||||||
|
@clear="searchEntries">
|
||||||
|
</search>
|
||||||
|
|
||||||
|
<div class="c-notebook__controls ">
|
||||||
|
<select class="c-notebook__controls__time" v-model="timeFrame">
|
||||||
|
<option value="0" :selected="timeFrame==='0'">Show all</option>
|
||||||
|
<option value="1" :selected="timeFrame==='1'">Last hour</option>
|
||||||
|
<option value="8" :selected="timeFrame==='8'">Last 8 hours</option>
|
||||||
|
<option value="24" :selected="timeFrame==='24'">Last 24 hours</option>
|
||||||
|
</select>
|
||||||
|
<select class="c-notebook__controls__time" v-model="sortOrder">
|
||||||
|
<option value="newest" :selected="sortOrder === 'newest'">Newest first</option>
|
||||||
|
<option value="oldest" :selected="sortOrder === 'oldest'">Oldest first</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="c-notebook__drag-area icon-plus"
|
||||||
|
@click="newEntry">
|
||||||
|
<span class="c-notebook__drag-area__label">To start a new entry, click here or drag and drop any object</span>
|
||||||
|
</div>
|
||||||
|
<div class="c-notebook__entries">
|
||||||
|
<ul>
|
||||||
|
<entry
|
||||||
|
v-for="entry in entries"
|
||||||
|
:key="entry.id"
|
||||||
|
:entry="entry"
|
||||||
|
@update-entry="persistEntry"
|
||||||
|
@delete-entry="deleteEntry"
|
||||||
|
@drop-embed="addEmbed">
|
||||||
|
</entry>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Search from '../../../ui/components/search.vue';
|
||||||
|
import Entry from './entry.vue';
|
||||||
|
import Moment from 'moment';
|
||||||
|
import searchVue from '../../../ui/components/search.vue';
|
||||||
|
|
||||||
|
function formatTime(unixTime, format) {
|
||||||
|
return Moment(unixTime).format(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'providedDomainObject'],
|
||||||
|
components: {
|
||||||
|
Search,
|
||||||
|
Entry
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
formatTime
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
domainObject: this.providedDomainObject,
|
||||||
|
timeFrame: '0',
|
||||||
|
searchValue: '',
|
||||||
|
sortOrder: this.providedDomainObject.configuration.sortOrder
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
searchEntries(value) {
|
||||||
|
this.searchValue = value;
|
||||||
|
},
|
||||||
|
updateDomainObject(domainObject) {
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
},
|
||||||
|
newEntry() {
|
||||||
|
this.searchValue = '';
|
||||||
|
|
||||||
|
let date = Date.now(),
|
||||||
|
entry = {'id': 'entry-' + date, 'createdOn': date, 'embeds':[]},
|
||||||
|
entries = this.domainObject.entries,
|
||||||
|
lastEntry = entries[entries.length - 1];
|
||||||
|
|
||||||
|
if (lastEntry && !lastEntry.text && !lastEntry.embeds.length) {
|
||||||
|
let lastEntryComponent = this.$children.find(entryComponent => {
|
||||||
|
if (entryComponent.entry) {
|
||||||
|
return entryComponent.entry.id === lastEntry.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lastEntryComponent.$refs.contenteditable.focus();
|
||||||
|
} else {
|
||||||
|
entries.push(entry);
|
||||||
|
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
findEntry(entryId) {
|
||||||
|
return this.domainObject.entries.findIndex(entry => {
|
||||||
|
return entry.id === entryId;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
persistEntry(entryId, text) {
|
||||||
|
let entryPos = this.findEntry(entryId);
|
||||||
|
|
||||||
|
this.openmct.objects.mutate(this.domainObject, `entries[${entryPos}].text`, text);
|
||||||
|
},
|
||||||
|
deleteEntry(entryId) {
|
||||||
|
let entryPos = this.findEntry(entryId),
|
||||||
|
entries = this.domainObject.entries;
|
||||||
|
|
||||||
|
if (entryPos !== -1) {
|
||||||
|
let dialog = this.openmct.overlays.dialog({
|
||||||
|
iconClass: 'alert',
|
||||||
|
message: 'This action will permanently delete this entry. Do you wish to continue?',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: "Ok",
|
||||||
|
emphasis: true,
|
||||||
|
callback: () => {
|
||||||
|
entries.splice(entryPos, 1);
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Cancel",
|
||||||
|
callback: () => {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applySearch(entries) {
|
||||||
|
return entries.filter((entry) => {
|
||||||
|
if (entry.text.includes(this.searchValue)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addEmbed(entryId, event) {
|
||||||
|
var data = event.dataTransfer.getData('openmct/domain-object-path');
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
var objectPath = JSON.parse(data),
|
||||||
|
domainObject = objectPath[0],
|
||||||
|
domainObjectKey = domainObject.identifier.key,
|
||||||
|
domainObjectType = this.openmct.types.get(domainObject.type),
|
||||||
|
cssClass = domainObjectType && domainObjectType.definition ?
|
||||||
|
domainObjectType.definition.cssClass : 'icon-object-unknown',
|
||||||
|
entryPos = this.findEntry(entryId),
|
||||||
|
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
|
||||||
|
newEmbed = {
|
||||||
|
id: '' + Date.now(),
|
||||||
|
domainObject: domainObject,
|
||||||
|
objectPath: objectPath,
|
||||||
|
type: domainObjectKey,
|
||||||
|
cssClass: cssClass,
|
||||||
|
name: domainObject.name,
|
||||||
|
snapshot: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
currentEntryEmbeds.push(newEmbed);
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].embeds', currentEntryEmbeds);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDrop() {
|
||||||
|
console.log('droped');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
entries() {
|
||||||
|
let entries = [...this.domainObject.entries];
|
||||||
|
|
||||||
|
if (this.searchValue !== '') {
|
||||||
|
entries = this.applySearch(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sortOrder === 'newest') {
|
||||||
|
return entries.reverse();
|
||||||
|
} else {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
67
src/plugins/notebook-svc/notebook-view-provider.js
Normal file
67
src/plugins/notebook-svc/notebook-view-provider.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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([
|
||||||
|
'vue',
|
||||||
|
'./components/notebook.vue'
|
||||||
|
], function (
|
||||||
|
Vue,
|
||||||
|
Notebook
|
||||||
|
) {
|
||||||
|
function NotebookViewProvider(openmct) {
|
||||||
|
return {
|
||||||
|
key: 'notebook',
|
||||||
|
name: 'Notebook',
|
||||||
|
cssClass: 'icon-notebook',
|
||||||
|
canView: function (domainObject) {
|
||||||
|
return domainObject.type === 'notebook';
|
||||||
|
},
|
||||||
|
view: function (domainObject) {
|
||||||
|
let component;
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: function (element) {
|
||||||
|
component = new Vue({
|
||||||
|
components: {
|
||||||
|
Notebook: Notebook.default
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct,
|
||||||
|
providedDomainObject: domainObject
|
||||||
|
},
|
||||||
|
el: element,
|
||||||
|
template: '<notebook></notebook>'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
destroy: function (element) {
|
||||||
|
component.$destroy();
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
priority: function () {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return NotebookViewProvider;
|
||||||
|
});
|
47
src/plugins/notebook-svc/plugin.js
Normal file
47
src/plugins/notebook-svc/plugin.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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([
|
||||||
|
'./notebook-view-provider'
|
||||||
|
], function (
|
||||||
|
NotebookViewProvider
|
||||||
|
) {
|
||||||
|
return function plugin() {
|
||||||
|
|
||||||
|
return function install(openmct) {
|
||||||
|
openmct.objectViews.addProvider(new NotebookViewProvider(openmct));
|
||||||
|
|
||||||
|
openmct.types.addType('notebook', {
|
||||||
|
name: "Notebook SVC",
|
||||||
|
creatable: true,
|
||||||
|
description: "Create and save timestamped notes with embedded object snapshots.",
|
||||||
|
cssClass: 'icon-notebook',
|
||||||
|
initialize: function (domainObject) {
|
||||||
|
domainObject.configuration = {
|
||||||
|
sortOrder: 'oldest'
|
||||||
|
};
|
||||||
|
domainObject.entries = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
@ -35,7 +35,7 @@ define([
|
|||||||
|
|
||||||
installed = true;
|
installed = true;
|
||||||
|
|
||||||
openmct.legacyRegistry.register('notebook', {
|
openmct.legacyRegistry.register('notebook-old', {
|
||||||
name: 'Notebook Plugin',
|
name: 'Notebook Plugin',
|
||||||
extensions: {
|
extensions: {
|
||||||
types: [
|
types: [
|
||||||
@ -73,10 +73,10 @@ define([
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openmct.legacyRegistry.enable('notebook');
|
openmct.legacyRegistry.enable('notebook-old');
|
||||||
|
|
||||||
openmct.objectViews.addProvider({
|
openmct.objectViews.addProvider({
|
||||||
key: 'notebook-vue',
|
key: 'notebook-old',
|
||||||
name: 'Notebook View',
|
name: 'Notebook View',
|
||||||
cssClass: 'icon-notebook',
|
cssClass: 'icon-notebook',
|
||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
|
@ -115,10 +115,22 @@
|
|||||||
width: (tickWidth + 30) + 'px'
|
width: (tickWidth + 30) + 'px'
|
||||||
}">
|
}">
|
||||||
|
|
||||||
<div class="gl-plot-label gl-plot-y-label">
|
<div class="gl-plot-label gl-plot-y-label" ng-if="!yKeyOptions">
|
||||||
{{ yAxis.get('label') }}
|
{{ yAxis.get('label') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="gl-plot-label gl-plot-y-label" ng-if="yKeyOptions.length > 1 && series.length === 1">
|
||||||
|
<select class="gl-plot-y-label__select"
|
||||||
|
ng-model="yAxisLabel" ng-change="plot.toggleYAxisLabel(yAxisLabel, yKeyOptions, series[0])">
|
||||||
|
<option ng-repeat="option in yKeyOptions"
|
||||||
|
value="{{option.name}}"
|
||||||
|
ng-selected="option.name === yAxisLabel">
|
||||||
|
{{option.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<mct-ticks axis="yAxis">
|
<mct-ticks axis="yAxis">
|
||||||
<div ng-repeat="tick in ticks track by tick.text"
|
<div ng-repeat="tick in ticks track by tick.text"
|
||||||
class="gl-plot-tick gl-plot-y-tick-label"
|
class="gl-plot-tick gl-plot-y-tick-label"
|
||||||
|
@ -93,6 +93,8 @@ define([
|
|||||||
this.$scope.series = this.config.series.models;
|
this.$scope.series = this.config.series.models;
|
||||||
this.$scope.legend = this.config.legend;
|
this.$scope.legend = this.config.legend;
|
||||||
|
|
||||||
|
this.$scope.yAxisLabel = this.config.yAxis.get('label');
|
||||||
|
|
||||||
this.cursorGuideVertical = this.$element[0].querySelector('.js-cursor-guide--v');
|
this.cursorGuideVertical = this.$element[0].querySelector('.js-cursor-guide--v');
|
||||||
this.cursorGuideHorizontal = this.$element[0].querySelector('.js-cursor-guide--h');
|
this.cursorGuideHorizontal = this.$element[0].querySelector('.js-cursor-guide--h');
|
||||||
this.cursorGuide = false;
|
this.cursorGuide = false;
|
||||||
@ -103,9 +105,35 @@ define([
|
|||||||
this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this);
|
this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this);
|
||||||
this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this);
|
this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this);
|
||||||
this.listenTo(this.$scope, 'plot:reinitializeCanvas', this.initCanvas, this);
|
this.listenTo(this.$scope, 'plot:reinitializeCanvas', this.initCanvas, this);
|
||||||
|
|
||||||
this.listenTo(this.config.xAxis, 'change:displayRange', this.onXAxisChange, this);
|
this.listenTo(this.config.xAxis, 'change:displayRange', this.onXAxisChange, this);
|
||||||
this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
|
this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
|
||||||
|
|
||||||
|
this.setUpYAxisOptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
MCTPlotController.prototype.setUpYAxisOptions = function () {
|
||||||
|
if (this.$scope.series.length === 1) {
|
||||||
|
let metadata = this.$scope.series[0].metadata;
|
||||||
|
|
||||||
|
this.$scope.yKeyOptions = metadata
|
||||||
|
.valuesForHints(['range'])
|
||||||
|
.map(function (o) {
|
||||||
|
return {
|
||||||
|
name: o.name,
|
||||||
|
key: o.key
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// set yAxisLabel if none is set yet
|
||||||
|
if (this.$scope.yAxisLabel === 'none') {
|
||||||
|
let yKey = this.$scope.series[0].model.yKey,
|
||||||
|
yKeyModel = this.$scope.yKeyOptions.filter(o => o.key === yKey)[0];
|
||||||
|
|
||||||
|
this.$scope.yAxisLabel = yKeyModel.name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$scope.yKeyOptions = undefined;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MCTPlotController.prototype.onXAxisChange = function (displayBounds) {
|
MCTPlotController.prototype.onXAxisChange = function (displayBounds) {
|
||||||
@ -493,5 +521,13 @@ define([
|
|||||||
this.cursorGuide = !this.cursorGuide;
|
this.cursorGuide = !this.cursorGuide;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MCTPlotController.prototype.toggleYAxisLabel = function (label, options, series) {
|
||||||
|
let yAxisObject = options.filter(o => o.name === label)[0];
|
||||||
|
|
||||||
|
if (yAxisObject) {
|
||||||
|
series.emit('change:yKey', yAxisObject.key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return MCTPlotController;
|
return MCTPlotController;
|
||||||
});
|
});
|
||||||
|
@ -41,6 +41,7 @@ define([
|
|||||||
'./flexibleLayout/plugin',
|
'./flexibleLayout/plugin',
|
||||||
'./tabs/plugin',
|
'./tabs/plugin',
|
||||||
'./LADTable/plugin',
|
'./LADTable/plugin',
|
||||||
|
'./notebook-svc/plugin',
|
||||||
'./filters/plugin',
|
'./filters/plugin',
|
||||||
'./objectMigration/plugin',
|
'./objectMigration/plugin',
|
||||||
'./goToOriginalAction/plugin',
|
'./goToOriginalAction/plugin',
|
||||||
@ -66,6 +67,7 @@ define([
|
|||||||
FlexibleLayout,
|
FlexibleLayout,
|
||||||
Tabs,
|
Tabs,
|
||||||
LADTable,
|
LADTable,
|
||||||
|
NotebookSVC,
|
||||||
Filters,
|
Filters,
|
||||||
ObjectMigration,
|
ObjectMigration,
|
||||||
GoToOriginalAction,
|
GoToOriginalAction,
|
||||||
@ -159,12 +161,13 @@ define([
|
|||||||
plugins.SummaryWidget = SummaryWidget;
|
plugins.SummaryWidget = SummaryWidget;
|
||||||
plugins.TelemetryMean = TelemetryMean;
|
plugins.TelemetryMean = TelemetryMean;
|
||||||
plugins.URLIndicator = URLIndicatorPlugin;
|
plugins.URLIndicator = URLIndicatorPlugin;
|
||||||
plugins.Notebook = Notebook;
|
// plugins.Notebook = Notebook;
|
||||||
plugins.DisplayLayout = DisplayLayoutPlugin.default;
|
plugins.DisplayLayout = DisplayLayoutPlugin.default;
|
||||||
plugins.FolderView = FolderView;
|
plugins.FolderView = FolderView;
|
||||||
plugins.Tabs = Tabs;
|
plugins.Tabs = Tabs;
|
||||||
plugins.FlexibleLayout = FlexibleLayout;
|
plugins.FlexibleLayout = FlexibleLayout;
|
||||||
plugins.LADTable = LADTable;
|
plugins.LADTable = LADTable;
|
||||||
|
plugins.NotebookSVC = NotebookSVC;
|
||||||
plugins.Filters = Filters;
|
plugins.Filters = Filters;
|
||||||
plugins.ObjectMigration = ObjectMigration.default;
|
plugins.ObjectMigration = ObjectMigration.default;
|
||||||
plugins.GoToOriginalAction = GoToOriginalAction.default;
|
plugins.GoToOriginalAction = GoToOriginalAction.default;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
export default class RemoveAction {
|
export default class RemoveAction {
|
||||||
constructor(openmct) {
|
constructor(openmct) {
|
||||||
this.name = 'Remove';
|
this.name = 'Remove';
|
||||||
|
this.key = 'remove';
|
||||||
this.description = 'Remove this object from its containing object.';
|
this.description = 'Remove this object from its containing object.';
|
||||||
this.cssClass = "icon-trash";
|
this.cssClass = "icon-trash";
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ define([
|
|||||||
this.telemetryObjects = [];
|
this.telemetryObjects = [];
|
||||||
this.outstandingRequests = 0;
|
this.outstandingRequests = 0;
|
||||||
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||||
|
this.paused = false;
|
||||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
|
||||||
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
this.addTelemetryObject = this.addTelemetryObject.bind(this);
|
||||||
@ -219,7 +220,10 @@ define([
|
|||||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
if (!this.telemetryObjects.includes(telemetryObject)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.processRealtimeDatum(datum, columnMap, keyString, limitEvaluator);
|
|
||||||
|
if (!this.paused) {
|
||||||
|
this.processRealtimeDatum(datum, columnMap, keyString, limitEvaluator);
|
||||||
|
}
|
||||||
}, subscribeOptions);
|
}, subscribeOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,6 +259,17 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.paused = true;
|
||||||
|
this.boundedRows.unsubscribeFromBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
unpause() {
|
||||||
|
this.paused = false;
|
||||||
|
this.boundedRows.subscribeToBounds();
|
||||||
|
this.refreshData();
|
||||||
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.boundedRows.destroy();
|
this.boundedRows.destroy();
|
||||||
this.filteredRows.destroy();
|
this.filteredRows.destroy();
|
||||||
|
@ -21,10 +21,11 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
define(function () {
|
define(function () {
|
||||||
class TelemetryTableColumn {
|
class TelemetryTableColumn {
|
||||||
constructor (openmct, metadatum) {
|
constructor (openmct, metadatum, options = {selectable: false}) {
|
||||||
this.metadatum = metadatum;
|
this.metadatum = metadatum;
|
||||||
this.formatter = openmct.telemetry.getValueFormatter(metadatum);
|
this.formatter = openmct.telemetry.getValueFormatter(metadatum);
|
||||||
this.titleValue = this.metadatum.name;
|
this.titleValue = this.metadatum.name;
|
||||||
|
this.selectable = options.selectable;
|
||||||
}
|
}
|
||||||
|
|
||||||
getKey() {
|
getKey() {
|
||||||
@ -55,8 +56,7 @@ define(function () {
|
|||||||
return formattedValue;
|
return formattedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return TelemetryTableColumn;
|
return TelemetryTableColumn;
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,7 @@ define([], function () {
|
|||||||
this.limitEvaluator = limitEvaluator;
|
this.limitEvaluator = limitEvaluator;
|
||||||
this.objectKeyString = objectKeyString;
|
this.objectKeyString = objectKeyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormattedDatum(headers) {
|
getFormattedDatum(headers) {
|
||||||
return Object.keys(headers).reduce((formattedDatum, columnKey) => {
|
return Object.keys(headers).reduce((formattedDatum, columnKey) => {
|
||||||
formattedDatum[columnKey] = this.getFormattedValue(columnKey);
|
formattedDatum[columnKey] = this.getFormattedValue(columnKey);
|
||||||
@ -62,12 +62,16 @@ define([], function () {
|
|||||||
this.cellLimitClasses = Object.values(this.columns).reduce((alarmStateMap, column) => {
|
this.cellLimitClasses = Object.values(this.columns).reduce((alarmStateMap, column) => {
|
||||||
let limitEvaluation = this.limitEvaluator.evaluate(this.datum, column.getMetadatum());
|
let limitEvaluation = this.limitEvaluator.evaluate(this.datum, column.getMetadatum());
|
||||||
alarmStateMap[column.getKey()] = limitEvaluation && limitEvaluation.cssClass;
|
alarmStateMap[column.getKey()] = limitEvaluation && limitEvaluation.cssClass;
|
||||||
|
|
||||||
return alarmStateMap;
|
return alarmStateMap;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
return this.cellLimitClasses;
|
return this.cellLimitClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getContextMenuActions() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,4 +89,4 @@ define([], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return TelemetryTableRow;
|
return TelemetryTableRow;
|
||||||
});
|
});
|
||||||
|
@ -48,7 +48,7 @@ define([
|
|||||||
canEdit(domainObject) {
|
canEdit(domainObject) {
|
||||||
return domainObject.type === 'table';
|
return domainObject.type === 'table';
|
||||||
},
|
},
|
||||||
view(domainObject) {
|
view(domainObject, isEditing, objectPath) {
|
||||||
let table = new TelemetryTable(domainObject, openmct);
|
let table = new TelemetryTable(domainObject, openmct);
|
||||||
let component;
|
let component;
|
||||||
return {
|
return {
|
||||||
@ -64,10 +64,11 @@ define([
|
|||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
table
|
table,
|
||||||
|
objectPath
|
||||||
},
|
},
|
||||||
el: element,
|
el: element,
|
||||||
template: '<table-component :isEditing="isEditing"></table-component>'
|
template: '<table-component :isEditing="isEditing" :enableMarking="true"></table-component>'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onEditModeChange(isEditing) {
|
onEditModeChange(isEditing) {
|
||||||
|
@ -43,7 +43,8 @@ define(
|
|||||||
this.sortByTimeSystem(openmct.time.timeSystem());
|
this.sortByTimeSystem(openmct.time.timeSystem());
|
||||||
|
|
||||||
this.lastBounds = openmct.time.bounds();
|
this.lastBounds = openmct.time.bounds();
|
||||||
openmct.time.on('bounds', this.bounds);
|
|
||||||
|
this.subscribeToBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
addOne(item) {
|
addOne(item) {
|
||||||
@ -140,9 +141,17 @@ define(
|
|||||||
return this.parseTime(row.datum[this.sortOptions.key]);
|
return this.parseTime(row.datum[this.sortOptions.key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
unsubscribeFromBounds() {
|
||||||
this.openmct.time.off('bounds', this.bounds);
|
this.openmct.time.off('bounds', this.bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subscribeToBounds() {
|
||||||
|
this.openmct.time.on('bounds', this.bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.unsubscribeFromBounds();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return BoundedTableRowCollection;
|
return BoundedTableRowCollection;
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<td>{{formattedValue}}</td>
|
<td @click="selectCell($event.currentTarget, columnKey)" :title="formattedValue">{{formattedValue}}</td>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
@ -33,11 +33,38 @@ export default {
|
|||||||
columnKey: {
|
columnKey: {
|
||||||
type: String,
|
type: String,
|
||||||
require: true
|
require: true
|
||||||
|
},
|
||||||
|
objectPath: {
|
||||||
|
type: Array,
|
||||||
|
require: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
selectCell(element, columnKey) {
|
||||||
|
if (this.isSelectable) {
|
||||||
|
this.openmct.selection.select([{
|
||||||
|
element: element,
|
||||||
|
context: {
|
||||||
|
type: 'table-cell',
|
||||||
|
row: this.row.objectKeyString,
|
||||||
|
column: columnKey
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
element: this.openmct.layout.$refs.browseObject.$el,
|
||||||
|
context: {
|
||||||
|
item: this.objectPath[0]
|
||||||
|
}
|
||||||
|
}], false);
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
formattedValue() {
|
formattedValue() {
|
||||||
return this.row.getFormattedValue(this.columnKey);
|
return this.row.getFormattedValue(this.columnKey);
|
||||||
|
},
|
||||||
|
isSelectable() {
|
||||||
|
return this.row.columns[this.columnKey].selectable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -20,37 +20,54 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<tr :style="{ top: rowTop }" :class="rowClass">
|
<tr :style="{ top: rowTop }"
|
||||||
<component
|
class="noselect"
|
||||||
v-for="(title, key) in headers"
|
:class="[
|
||||||
|
rowClass,
|
||||||
|
{'is-selected': marked}
|
||||||
|
]"
|
||||||
|
v-on="listeners">
|
||||||
|
<component v-for="(title, key) in headers"
|
||||||
:key="key"
|
:key="key"
|
||||||
:is="componentList[key]"
|
:is="componentList[key]"
|
||||||
:columnKey="key"
|
:columnKey="key"
|
||||||
:style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
:style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
||||||
:title="formattedRow[key]"
|
:class="[cellLimitClasses[key], selectableColumns[key] ? 'is-selectable' : '']"
|
||||||
:class="cellLimitClasses[key]"
|
:objectPath="objectPath"
|
||||||
class="is-selectable"
|
:row="row">
|
||||||
@click="selectCell($event.currentTarget, key)"
|
</component>
|
||||||
:row="row"></component>
|
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.noselect {
|
||||||
|
-webkit-touch-callout: none; /* iOS Safari */
|
||||||
|
-webkit-user-select: none; /* Safari */
|
||||||
|
-khtml-user-select: none; /* Konqueror HTML */
|
||||||
|
-moz-user-select: none; /* Firefox */
|
||||||
|
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||||
|
user-select: none; /* Non-prefixed version, currently
|
||||||
|
supported by Chrome and Opera */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TableCell from './table-cell.vue';
|
import TableCell from './table-cell.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
inject: ['openmct', 'objectPath'],
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
|
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
|
||||||
formattedRow: this.row.getFormattedDatum(this.headers),
|
|
||||||
rowClass: this.row.getRowClass(),
|
rowClass: this.row.getRowClass(),
|
||||||
cellLimitClasses: this.row.getCellLimitClasses(),
|
cellLimitClasses: this.row.getCellLimitClasses(),
|
||||||
componentList: Object.keys(this.headers).reduce((components, header) => {
|
componentList: Object.keys(this.headers).reduce((components, header) => {
|
||||||
components[header] = this.row.getCellComponentName(header) || 'table-cell';
|
components[header] = this.row.getCellComponentName(header) || 'table-cell';
|
||||||
return components
|
return components
|
||||||
|
}, {}),
|
||||||
|
selectableColumns : Object.keys(this.row.columns).reduce((selectable, columnKeys) => {
|
||||||
|
selectable[columnKeys] = this.row.columns[columnKeys].selectable;
|
||||||
|
return selectable;
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -67,6 +84,10 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
objectPath: {
|
||||||
|
type: Array,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
rowIndex: {
|
rowIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
@ -81,6 +102,11 @@ export default {
|
|||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 0
|
default: 0
|
||||||
|
},
|
||||||
|
marked: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -88,26 +114,54 @@ export default {
|
|||||||
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
|
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
|
||||||
},
|
},
|
||||||
formatRow: function (row) {
|
formatRow: function (row) {
|
||||||
this.formattedRow = row.getFormattedDatum(this.headers);
|
|
||||||
this.rowClass = row.getRowClass();
|
this.rowClass = row.getRowClass();
|
||||||
this.cellLimitClasses = row.getCellLimitClasses();
|
this.cellLimitClasses = row.getCellLimitClasses();
|
||||||
},
|
},
|
||||||
|
markRow: function (event) {
|
||||||
|
let keyCtrlModifier = false;
|
||||||
|
|
||||||
|
if (event.ctrlKey || event.metaKey) {
|
||||||
|
keyCtrlModifier = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.shiftKey) {
|
||||||
|
this.$emit('markMultipleConcurrent', this.rowIndex);
|
||||||
|
} else {
|
||||||
|
if (this.marked) {
|
||||||
|
this.$emit('unmark', this.rowIndex, keyCtrlModifier);
|
||||||
|
} else {
|
||||||
|
this.$emit('mark', this.rowIndex, keyCtrlModifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
selectCell(element, columnKey) {
|
selectCell(element, columnKey) {
|
||||||
//TODO: This is a hack. Cannot get parent this way.
|
if (this.selectableColumns[columnKey]) {
|
||||||
this.openmct.selection.select([{
|
//TODO: This is a hack. Cannot get parent this way.
|
||||||
element: element,
|
this.openmct.selection.select([{
|
||||||
context: {
|
element: element,
|
||||||
type: 'table-cell',
|
context: {
|
||||||
row: this.row.objectKeyString,
|
type: 'table-cell',
|
||||||
column: columnKey
|
row: this.row.objectKeyString,
|
||||||
}
|
column: columnKey
|
||||||
},{
|
}
|
||||||
element: this.openmct.layout.$refs.browseObject.$el,
|
},{
|
||||||
context: {
|
element: this.openmct.layout.$refs.browseObject.$el,
|
||||||
item: this.openmct.router.path[0]
|
context: {
|
||||||
}
|
item: this.openmct.router.path[0]
|
||||||
}], false);
|
}
|
||||||
event.stopPropagation();
|
}], false);
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showContextMenu: function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.openmct.objects.get(this.row.objectKeyString).then((domainObject) => {
|
||||||
|
let contextualObjectPath = this.objectPath.slice();
|
||||||
|
contextualObjectPath.unshift(domainObject);
|
||||||
|
|
||||||
|
this.openmct.contextMenu._showContextMenuForObjectPath(contextualObjectPath, event.x, event.y, this.row.getContextMenuActions());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: use computed properties
|
// TODO: use computed properties
|
||||||
@ -120,6 +174,19 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
TableCell
|
TableCell
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
listeners() {
|
||||||
|
let listenersObject = {
|
||||||
|
click: this.markRow
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.row.getContextMenuActions().length) {
|
||||||
|
listenersObject.contextmenu = this.showContextMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
return listenersObject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -20,95 +20,135 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
<div class="c-table-wrapper">
|
||||||
:class="{'loading': loading}">
|
<div class="c-table-control-bar c-control-bar">
|
||||||
<div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}"><slot></slot></div>
|
|
||||||
<div v-if="allowExport" class="c-table__control-bar c-control-bar">
|
|
||||||
<button class="c-button icon-download labeled"
|
<button class="c-button icon-download labeled"
|
||||||
v-on:click="exportAsCSV()"
|
v-if="allowExport"
|
||||||
title="Export This View's Data">
|
v-on:click="exportAllDataAsCSV()"
|
||||||
<span class="c-button__label">Export As CSV</span>
|
title="Export This View's Data">
|
||||||
|
<span class="c-button__label">Export Table Data</span>
|
||||||
|
</button>
|
||||||
|
<button class="c-button icon-download labeled"
|
||||||
|
v-if="allowExport"
|
||||||
|
v-show="markedRows.length"
|
||||||
|
v-on:click="exportMarkedDataAsCSV()"
|
||||||
|
title="Export Marked Rows As CSV">
|
||||||
|
<span class="c-button__label">Export Marked Rows</span>
|
||||||
|
</button>
|
||||||
|
<button class="c-button icon-x labeled"
|
||||||
|
v-show="markedRows.length"
|
||||||
|
v-on:click="unmarkAllRows()"
|
||||||
|
title="Unmark All Rows">
|
||||||
|
<span class="c-button__label">Unmark All Rows</span>
|
||||||
|
</button>
|
||||||
|
<div v-if="enableMarking"
|
||||||
|
class="c-separator">
|
||||||
|
</div>
|
||||||
|
<button v-if="enableMarking"
|
||||||
|
class="c-button icon-pause pause-play labeled"
|
||||||
|
:class=" paused ? 'icon-play is-paused' : 'icon-pause'"
|
||||||
|
v-on:click="togglePauseByButton()"
|
||||||
|
:title="paused ? 'Continue Data Flow' : 'Pause Data Flow'">
|
||||||
|
<span class="c-button__label">
|
||||||
|
{{paused ? 'Play' : 'Pause'}}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<slot name="buttons"></slot>
|
<slot name="buttons"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
|
||||||
<!-- Headers table -->
|
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
:class="{
|
||||||
<table class="c-table__headers c-telemetry-table__headers">
|
'loading': loading,
|
||||||
<thead>
|
'paused' : paused
|
||||||
<tr class="c-telemetry-table__headers__labels">
|
}">
|
||||||
<table-column-header
|
|
||||||
v-for="(title, key, headerIndex) in headers"
|
<div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}"><slot></slot></div>
|
||||||
:key="key"
|
|
||||||
:headerKey="key"
|
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
||||||
:headerIndex="headerIndex"
|
<!-- Headers table -->
|
||||||
@sort="allowSorting && sortBy(key)"
|
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
||||||
@resizeColumn="resizeColumn"
|
<table class="c-table__headers c-telemetry-table__headers">
|
||||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
<thead>
|
||||||
@dropTargetActive="dropTargetActive"
|
<tr class="c-telemetry-table__headers__labels">
|
||||||
@reorderColumn="reorderColumn"
|
<table-column-header
|
||||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
v-for="(title, key, headerIndex) in headers"
|
||||||
:columnWidth="columnWidths[key]"
|
:key="key"
|
||||||
:sortOptions="sortOptions"
|
:headerKey="key"
|
||||||
:isEditing="isEditing"
|
:headerIndex="headerIndex"
|
||||||
><span class="c-telemetry-table__headers__label">{{title}}</span>
|
@sort="allowSorting && sortBy(key)"
|
||||||
</table-column-header>
|
@resizeColumn="resizeColumn"
|
||||||
</tr>
|
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||||
<tr class="c-telemetry-table__headers__filter">
|
@dropTargetActive="dropTargetActive"
|
||||||
<table-column-header
|
@reorderColumn="reorderColumn"
|
||||||
v-for="(title, key, headerIndex) in headers"
|
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||||
:key="key"
|
:columnWidth="columnWidths[key]"
|
||||||
:headerKey="key"
|
:sortOptions="sortOptions"
|
||||||
:headerIndex="headerIndex"
|
:isEditing="isEditing"
|
||||||
@resizeColumn="resizeColumn"
|
><span class="c-telemetry-table__headers__label">{{title}}</span>
|
||||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
</table-column-header>
|
||||||
@dropTargetActive="dropTargetActive"
|
</tr>
|
||||||
@reorderColumn="reorderColumn"
|
<tr class="c-telemetry-table__headers__filter">
|
||||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
<table-column-header
|
||||||
:columnWidth="columnWidths[key]"
|
v-for="(title, key, headerIndex) in headers"
|
||||||
:isEditing="isEditing"
|
:key="key"
|
||||||
>
|
:headerKey="key"
|
||||||
<search class="c-table__search"
|
:headerIndex="headerIndex"
|
||||||
v-model="filters[key]"
|
@resizeColumn="resizeColumn"
|
||||||
v-on:input="filterChanged(key)"
|
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||||
v-on:clear="clearFilter(key)" />
|
@dropTargetActive="dropTargetActive"
|
||||||
</table-column-header>
|
@reorderColumn="reorderColumn"
|
||||||
</tr>
|
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||||
</thead>
|
:columnWidth="columnWidths[key]"
|
||||||
|
:isEditing="isEditing"
|
||||||
|
>
|
||||||
|
<search class="c-table__search"
|
||||||
|
v-model="filters[key]"
|
||||||
|
v-on:input="filterChanged(key)"
|
||||||
|
v-on:clear="clearFilter(key)" />
|
||||||
|
</table-column-header>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- Content table -->
|
||||||
|
<div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll" :style="{ 'max-width': widthWithScroll}">
|
||||||
|
<div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth + 'px' }"></div>
|
||||||
|
<table class="c-table__body c-telemetry-table__body js-telemetry-table__content"
|
||||||
|
:style="{ height: totalHeight + 'px'}">
|
||||||
|
<tbody>
|
||||||
|
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
|
||||||
|
:headers="headers"
|
||||||
|
:columnWidths="columnWidths"
|
||||||
|
:rowIndex="rowIndex"
|
||||||
|
:objectPath="objectPath"
|
||||||
|
:rowOffset="rowOffset"
|
||||||
|
:rowHeight="rowHeight"
|
||||||
|
:row="row"
|
||||||
|
:marked="row.marked"
|
||||||
|
@mark="markRow"
|
||||||
|
@unmark="unmarkRow"
|
||||||
|
@markMultipleConcurrent="markMultipleConcurrentRows">
|
||||||
|
</telemetry-table-row>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- Sizing table -->
|
||||||
|
<table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
|
||||||
|
<tr>
|
||||||
|
<template v-for="(title, key) in headers">
|
||||||
|
<th :key="key" :style="{ width: configuredColumnWidths[key] + 'px', 'max-width': configuredColumnWidths[key] + 'px'}">{{title}}</th>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
|
||||||
|
:key="objectKeyString"
|
||||||
|
:headers="headers"
|
||||||
|
:columnWidths="configuredColumnWidths"
|
||||||
|
:row="sizingRowData">
|
||||||
|
</telemetry-table-row>
|
||||||
</table>
|
</table>
|
||||||
|
<telemetry-filter-indicator></telemetry-filter-indicator>
|
||||||
</div>
|
</div>
|
||||||
<!-- Content table -->
|
</div><!-- closes c-table-wrapper -->
|
||||||
<div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll" :style="{ 'max-width': widthWithScroll}">
|
|
||||||
<div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth + 'px' }"></div>
|
|
||||||
<table class="c-table__body c-telemetry-table__body js-telemetry-table__content"
|
|
||||||
:style="{ height: totalHeight + 'px'}">
|
|
||||||
<tbody>
|
|
||||||
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
|
|
||||||
:headers="headers"
|
|
||||||
:columnWidths="columnWidths"
|
|
||||||
:rowIndex="rowIndex"
|
|
||||||
:rowOffset="rowOffset"
|
|
||||||
:rowHeight="rowHeight"
|
|
||||||
:row="row">
|
|
||||||
</telemetry-table-row>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- Sizing table -->
|
|
||||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
|
|
||||||
<tr>
|
|
||||||
<template v-for="(title, key) in headers">
|
|
||||||
<th :key="key" :style="{ width: configuredColumnWidths[key] + 'px', 'max-width': configuredColumnWidths[key] + 'px'}">{{title}}</th>
|
|
||||||
</template>
|
|
||||||
</tr>
|
|
||||||
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
|
|
||||||
:headers="headers"
|
|
||||||
:columnWidths="configuredColumnWidths"
|
|
||||||
:row="sizingRowData">
|
|
||||||
</telemetry-table-row>
|
|
||||||
</table>
|
|
||||||
<telemetry-filter-indicator></telemetry-filter-indicator>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -134,7 +174,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
vertical-align: middle; // This is crucial to hiding 4px height injected by browser by default
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
@ -219,6 +259,10 @@
|
|||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 18px; // Needed when a row has empty values in its cells
|
height: 18px; // Needed when a row has empty values in its cells
|
||||||
|
|
||||||
|
&.is-selected {
|
||||||
|
background-color: $colorSelectedBg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
@ -269,6 +313,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paused {
|
||||||
|
border: 1px solid #ff9900;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************* LEGACY */
|
/******************************* LEGACY */
|
||||||
.s-status-taking-snapshot,
|
.s-status-taking-snapshot,
|
||||||
.overlay.snapshot {
|
.overlay.snapshot {
|
||||||
@ -301,7 +349,7 @@ export default {
|
|||||||
search,
|
search,
|
||||||
TelemetryFilterIndicator
|
TelemetryFilterIndicator
|
||||||
},
|
},
|
||||||
inject: ['table', 'openmct'],
|
inject: ['table', 'openmct', 'objectPath'],
|
||||||
props: {
|
props: {
|
||||||
isEditing: {
|
isEditing: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -318,6 +366,10 @@ export default {
|
|||||||
allowSorting: {
|
allowSorting: {
|
||||||
'type': Boolean,
|
'type': Boolean,
|
||||||
'default': true
|
'default': true
|
||||||
|
},
|
||||||
|
enableMarking: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -346,7 +398,10 @@ export default {
|
|||||||
dropOffsetLeft: undefined,
|
dropOffsetLeft: undefined,
|
||||||
isDropTargetActive: false,
|
isDropTargetActive: false,
|
||||||
isAutosizeEnabled: configuration.autosize,
|
isAutosizeEnabled: configuration.autosize,
|
||||||
scrollW: 0
|
scrollW: 0,
|
||||||
|
markCounter: 0,
|
||||||
|
paused: false,
|
||||||
|
markedRows: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -532,15 +587,27 @@ export default {
|
|||||||
// which causes subsequent scroll to use an out of date height.
|
// which causes subsequent scroll to use an out of date height.
|
||||||
this.contentTable.style.height = this.totalHeight + 'px';
|
this.contentTable.style.height = this.totalHeight + 'px';
|
||||||
},
|
},
|
||||||
exportAsCSV() {
|
exportAsCSV(data) {
|
||||||
const headerKeys = Object.keys(this.headers);
|
const headerKeys = Object.keys(this.headers);
|
||||||
const justTheData = this.table.filteredRows.getRows()
|
|
||||||
.map(row => row.getFormattedDatum(this.headers));
|
this.csvExporter.export(data, {
|
||||||
this.csvExporter.export(justTheData, {
|
|
||||||
filename: this.table.domainObject.name + '.csv',
|
filename: this.table.domainObject.name + '.csv',
|
||||||
headers: headerKeys
|
headers: headerKeys
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
exportAllDataAsCSV() {
|
||||||
|
const justTheData = this.table.filteredRows.getRows()
|
||||||
|
.map(row => row.getFormattedDatum(this.headers));
|
||||||
|
|
||||||
|
this.exportAsCSV(justTheData);
|
||||||
|
},
|
||||||
|
exportMarkedDataAsCSV() {
|
||||||
|
const data = this.table.filteredRows.getRows()
|
||||||
|
.filter(row => row.marked === true)
|
||||||
|
.map(row => row.getFormattedDatum(this.headers));
|
||||||
|
|
||||||
|
this.exportAsCSV(data);
|
||||||
|
},
|
||||||
outstandingRequests(loading) {
|
outstandingRequests(loading) {
|
||||||
this.loading = loading;
|
this.loading = loading;
|
||||||
},
|
},
|
||||||
@ -632,8 +699,105 @@ export default {
|
|||||||
clearRowsAndRerender() {
|
clearRowsAndRerender() {
|
||||||
this.visibleRows = [];
|
this.visibleRows = [];
|
||||||
this.$nextTick().then(this.updateVisibleRows);
|
this.$nextTick().then(this.updateVisibleRows);
|
||||||
}
|
},
|
||||||
|
pause(pausedByButton) {
|
||||||
|
if (pausedByButton) {
|
||||||
|
this.pausedByButton = true;
|
||||||
|
}
|
||||||
|
this.paused = true;
|
||||||
|
this.table.pause();
|
||||||
|
},
|
||||||
|
unpause(unpausedByButton) {
|
||||||
|
if (unpausedByButton) {
|
||||||
|
this.paused = false;
|
||||||
|
this.table.unpause();
|
||||||
|
this.markedRows = [];
|
||||||
|
this.pausedByButton = false;
|
||||||
|
} else {
|
||||||
|
if (!this.pausedByButton) {
|
||||||
|
this.paused = false;
|
||||||
|
this.table.unpause();
|
||||||
|
this.markedRows = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
togglePauseByButton() {
|
||||||
|
if (this.paused) {
|
||||||
|
this.unpause(true);
|
||||||
|
} else {
|
||||||
|
this.pause(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
undoMarkedRows(unpause) {
|
||||||
|
this.markedRows.forEach(r => r.marked = false);
|
||||||
|
this.markedRows = [];
|
||||||
|
},
|
||||||
|
unmarkRow(rowIndex) {
|
||||||
|
this.undoMarkedRows();
|
||||||
|
this.unpause();
|
||||||
|
},
|
||||||
|
markRow(rowIndex, keyModifier) {
|
||||||
|
if (!this.enableMarking) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let insertMethod = 'unshift';
|
||||||
|
|
||||||
|
if (this.markedRows.length && !keyModifier) {
|
||||||
|
this.undoMarkedRows();
|
||||||
|
insertMethod = 'push';
|
||||||
|
}
|
||||||
|
|
||||||
|
let markedRow = this.visibleRows[rowIndex];
|
||||||
|
|
||||||
|
this.$set(markedRow, 'marked', true);
|
||||||
|
this.pause();
|
||||||
|
|
||||||
|
this.markedRows[insertMethod](markedRow);
|
||||||
|
},
|
||||||
|
unmarkAllRows(skipUnpause) {
|
||||||
|
this.markedRows.forEach(row => row.marked = false);
|
||||||
|
this.markedRows = [];
|
||||||
|
this.unpause();
|
||||||
|
},
|
||||||
|
markMultipleConcurrentRows(rowIndex) {
|
||||||
|
if (!this.enableMarking) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.markedRows.length) {
|
||||||
|
this.markRow(rowIndex);
|
||||||
|
} else {
|
||||||
|
if (this.markedRows.length > 1) {
|
||||||
|
this.markedRows.forEach((r,i) => {
|
||||||
|
if (i !== 0) {
|
||||||
|
r.marked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.markedRows.splice(1);
|
||||||
|
}
|
||||||
|
let lastRowToBeMarked = this.visibleRows[rowIndex];
|
||||||
|
|
||||||
|
let allRows = this.table.filteredRows.getRows(),
|
||||||
|
firstRowIndex = allRows.indexOf(this.markedRows[0]),
|
||||||
|
lastRowIndex = allRows.indexOf(lastRowToBeMarked);
|
||||||
|
|
||||||
|
//supports backward selection
|
||||||
|
if (lastRowIndex < firstRowIndex) {
|
||||||
|
let temp = lastRowIndex;
|
||||||
|
|
||||||
|
lastRowIndex = firstRowIndex;
|
||||||
|
firstRowIndex = temp - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = firstRowIndex + 1; i <= lastRowIndex; i++) {
|
||||||
|
let row = allRows[i];
|
||||||
|
row.marked = true;
|
||||||
|
this.markedRows.push(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||||
|
@ -167,6 +167,7 @@ export default {
|
|||||||
this.xAxis.scale(this.xScale);
|
this.xAxis.scale(this.xScale);
|
||||||
this.xAxis.tickFormat(utcMultiTimeFormat);
|
this.xAxis.tickFormat(utcMultiTimeFormat);
|
||||||
this.axisElement.call(this.xAxis);
|
this.axisElement.call(this.xAxis);
|
||||||
|
this.setScale();
|
||||||
},
|
},
|
||||||
getActiveFormatter() {
|
getActiveFormatter() {
|
||||||
let timeSystem = this.openmct.time.timeSystem();
|
let timeSystem = this.openmct.time.timeSystem();
|
||||||
|
@ -60,7 +60,6 @@ export default {
|
|||||||
.filter(menuOption => menuOption.clock === (clock && clock.key))
|
.filter(menuOption => menuOption.clock === (clock && clock.key))
|
||||||
.map(menuOption => JSON.parse(JSON.stringify(this.openmct.time.timeSystems.get(menuOption.timeSystem))));
|
.map(menuOption => JSON.parse(JSON.stringify(this.openmct.time.timeSystems.get(menuOption.timeSystem))));
|
||||||
},
|
},
|
||||||
|
|
||||||
setTimeSystemFromView(timeSystem) {
|
setTimeSystemFromView(timeSystem) {
|
||||||
if (timeSystem.key !== this.selectedTimeSystem.key) {
|
if (timeSystem.key !== this.selectedTimeSystem.key) {
|
||||||
let activeClock = this.openmct.time.clock();
|
let activeClock = this.openmct.time.clock();
|
||||||
@ -69,7 +68,15 @@ export default {
|
|||||||
timeSystem: timeSystem.key
|
timeSystem: timeSystem.key
|
||||||
});
|
});
|
||||||
if (activeClock === undefined) {
|
if (activeClock === undefined) {
|
||||||
this.openmct.time.timeSystem(timeSystem.key, configuration.bounds);
|
let bounds;
|
||||||
|
|
||||||
|
if (this.selectedTimeSystem.isUTCBased && timeSystem.isUTCBased) {
|
||||||
|
bounds = this.openmct.time.bounds();
|
||||||
|
} else {
|
||||||
|
bounds = configuration.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openmct.time.timeSystem(timeSystem.key, bounds);
|
||||||
} else {
|
} else {
|
||||||
this.openmct.time.timeSystem(timeSystem.key);
|
this.openmct.time.timeSystem(timeSystem.key);
|
||||||
this.openmct.time.clockOffsets(configuration.clockOffsets);
|
this.openmct.time.clockOffsets(configuration.clockOffsets);
|
||||||
|
@ -211,6 +211,10 @@ $btnStdH: 24px;
|
|||||||
$colorCursorGuide: rgba(white, 0.6);
|
$colorCursorGuide: rgba(white, 0.6);
|
||||||
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
|
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
|
||||||
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
|
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
|
||||||
|
$colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects
|
||||||
|
$colorSelectFg: $colorBtnFg;
|
||||||
|
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||||
|
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
||||||
@ -425,7 +429,3 @@ $createBtnTextTransform: uppercase;
|
|||||||
background: linear-gradient(pullForward($c, 5%), $c);
|
background: linear-gradient(pullForward($c, 5%), $c);
|
||||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin themedSelect($bg: $colorBtnBg, $fg: $colorBtnFg) {
|
|
||||||
@include cSelect(linear-gradient(lighten($bg, 5%), $bg), $fg, lighten($bg, 20%), rgba(black, 0.5) 0 0.5px 3px);
|
|
||||||
}
|
|
||||||
|
@ -215,6 +215,10 @@ $btnStdH: 24px;
|
|||||||
$colorCursorGuide: rgba(white, 0.6);
|
$colorCursorGuide: rgba(white, 0.6);
|
||||||
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
|
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
|
||||||
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
|
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
|
||||||
|
$colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects
|
||||||
|
$colorSelectFg: $colorBtnFg;
|
||||||
|
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||||
|
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
||||||
@ -430,10 +434,6 @@ $createBtnTextTransform: uppercase;
|
|||||||
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
box-shadow: rgba(black, 0.5) 0 0.5px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin themedSelect($bg: $colorBtnBg, $fg: $colorBtnFg) {
|
|
||||||
@include cSelect(linear-gradient(lighten($bg, 5%), $bg), $fg, lighten($bg, 20%), rgba(black, 0.5) 0 0.5px 3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************** OVERRIDES */
|
/**************************************************** OVERRIDES */
|
||||||
.c-frame {
|
.c-frame {
|
||||||
&:not(.no-frame) {
|
&:not(.no-frame) {
|
||||||
|
@ -211,6 +211,10 @@ $btnStdH: 24px;
|
|||||||
$colorCursorGuide: rgba(black, 0.6);
|
$colorCursorGuide: rgba(black, 0.6);
|
||||||
$shdwCursorGuide: rgba(white, 0.4) 0 0 2px;
|
$shdwCursorGuide: rgba(white, 0.4) 0 0 2px;
|
||||||
$colorLocalControlOvrBg: rgba($colorBodyFg, 0.8);
|
$colorLocalControlOvrBg: rgba($colorBodyFg, 0.8);
|
||||||
|
$colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects
|
||||||
|
$colorSelectFg: $colorBtnFg;
|
||||||
|
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||||
|
$shdwSelect: none;
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
||||||
@ -424,7 +428,3 @@ $createBtnTextTransform: uppercase;
|
|||||||
@mixin themedButton($c: $colorBtnBg) {
|
@mixin themedButton($c: $colorBtnBg) {
|
||||||
background: $c;
|
background: $c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin themedSelect($bg: $colorBtnBg, $fg: $colorBtnFg) {
|
|
||||||
@include cSelect($bg, $fg, lighten($bg, 20%), none);
|
|
||||||
}
|
|
||||||
|
@ -279,7 +279,10 @@ input[type=number]::-webkit-outer-spin-button {
|
|||||||
// SELECTS
|
// SELECTS
|
||||||
select {
|
select {
|
||||||
@include appearanceNone();
|
@include appearanceNone();
|
||||||
@include themedSelect();
|
background-color: $colorSelectBg;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{svgColorFromHex($colorSelectArw)}' d='M5 5l5-5H0z'/%3e%3c/svg%3e");
|
||||||
|
color: $colorSelectFg;
|
||||||
|
box-shadow: $shdwSelect;
|
||||||
background-repeat: no-repeat, no-repeat;
|
background-repeat: no-repeat, no-repeat;
|
||||||
background-position: right .4em top 80%, 0 0;
|
background-position: right .4em top 80%, 0 0;
|
||||||
border: none;
|
border: none;
|
||||||
@ -600,15 +603,15 @@ select {
|
|||||||
margin-right: $m;
|
margin-right: $m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-separator {
|
||||||
|
@include cToolbarSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
.c-toolbar {
|
.c-toolbar {
|
||||||
> * + * {
|
> * + * {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__button {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&__separator {
|
&__separator {
|
||||||
@include cToolbarSeparator();
|
@include cToolbarSeparator();
|
||||||
}
|
}
|
||||||
|
@ -161,8 +161,7 @@ mct-plot {
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.gl-plot-y-label,
|
&.gl-plot-y-label {
|
||||||
&.l-plot-y-label {
|
|
||||||
$x: -50%;
|
$x: -50%;
|
||||||
$r: -90deg;
|
$r: -90deg;
|
||||||
transform-origin: 50% 0;
|
transform-origin: 50% 0;
|
||||||
@ -172,6 +171,12 @@ mct-plot {
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
select {
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{svgColorFromHex($colorSelectArw)}' d='M0 5l5 5V0L0 5z'/%3e%3c/svg%3e");
|
||||||
|
background-position: left .4em top 50%, 0 0;
|
||||||
|
padding: 1px $interiorMargin 1px 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,6 +585,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@function svgColorFromHex($hexColor) {
|
||||||
|
// Remove initial # in color value
|
||||||
|
@return str-slice(inspect($hexColor), 2, str-length(inspect($hexColor)));
|
||||||
|
}
|
||||||
|
|
||||||
@mixin test($c: deeppink, $a: 0.3) {
|
@mixin test($c: deeppink, $a: 0.3) {
|
||||||
background: rgba($c, $a) !important;
|
background: rgba($c, $a) !important;
|
||||||
background-color: rgba($c, $a) !important;
|
background-color: rgba($c, $a) !important;
|
||||||
|
@ -76,23 +76,43 @@ div.c-table {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-table-wrapper {
|
||||||
|
// Wraps .c-control-bar and .c-table
|
||||||
|
@include abs();
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> .c-table {
|
||||||
|
height: auto;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-table-control-bar {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.c-table {
|
.c-table {
|
||||||
// Can be used by any type of table, scrolling, LAD, etc.
|
// Can be used by any type of table, scrolling, LAD, etc.
|
||||||
$min-w: 50px;
|
$min-w: 50px;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&__control-bar,
|
|
||||||
&__headers-w {
|
&__headers-w {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************* ELEMENTS */
|
/******************************* ELEMENTS */
|
||||||
|
|
||||||
&__control-bar {
|
|
||||||
margin-bottom: $interiorMarginSm;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead tr,
|
thead tr,
|
||||||
&.c-table__headers {
|
&.c-table__headers {
|
||||||
background: $colorTabHeaderBg;
|
background: $colorTabHeaderBg;
|
||||||
|
@ -44,7 +44,8 @@
|
|||||||
class="c-so-view__object-view"
|
class="c-so-view__object-view"
|
||||||
ref="objectView"
|
ref="objectView"
|
||||||
:object="domainObject"
|
:object="domainObject"
|
||||||
:show-edit-view="showEditView">
|
:show-edit-view="showEditView"
|
||||||
|
:object-path="objectPath">
|
||||||
</object-view>
|
</object-view>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -123,7 +124,12 @@
|
|||||||
.c-click-icon,
|
.c-click-icon,
|
||||||
.c-button {
|
.c-button {
|
||||||
// Shrink buttons a bit when they appear in a frame
|
// Shrink buttons a bit when they appear in a frame
|
||||||
font-size: 0.8em;
|
align-items: baseline;
|
||||||
|
font-size: 0.85em;
|
||||||
|
padding: 3px 5px;
|
||||||
|
&:before {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -111,7 +111,7 @@ export default {
|
|||||||
event.dataTransfer.setData("openmct/composable-domain-object", JSON.stringify(this.domainObject));
|
event.dataTransfer.setData("openmct/composable-domain-object", JSON.stringify(this.domainObject));
|
||||||
}
|
}
|
||||||
// serialize domain object anyway, because some views can drag-and-drop objects without composition
|
// serialize domain object anyway, because some views can drag-and-drop objects without composition
|
||||||
// (eg. notabook.)
|
// (eg. notebook.)
|
||||||
event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
|
event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
|
||||||
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.domainObject);
|
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.domainObject);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
view: String,
|
view: String,
|
||||||
object: Object,
|
object: Object,
|
||||||
showEditView: Boolean
|
showEditView: Boolean,
|
||||||
|
objectPath: Array
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.clear();
|
this.clear();
|
||||||
@ -91,17 +92,19 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let objectPath = this.currentObjectPath || this.objectPath;
|
||||||
|
|
||||||
if (provider.edit && this.showEditView) {
|
if (provider.edit && this.showEditView) {
|
||||||
if (this.openmct.editor.isEditing()) {
|
if (this.openmct.editor.isEditing()) {
|
||||||
this.currentView = provider.edit(this.currentObject);
|
this.currentView = provider.edit(this.currentObject, true, objectPath);
|
||||||
} else {
|
} else {
|
||||||
this.currentView = provider.view(this.currentObject, false);
|
this.currentView = provider.view(this.currentObject, false, objectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openmct.editor.on('isEditing', this.toggleEditView);
|
this.openmct.editor.on('isEditing', this.toggleEditView);
|
||||||
this.releaseEditModeHandler = () => this.openmct.editor.off('isEditing', this.toggleEditView);
|
this.releaseEditModeHandler = () => this.openmct.editor.off('isEditing', this.toggleEditView);
|
||||||
} else {
|
} else {
|
||||||
this.currentView = provider.view(this.currentObject, this.openmct.editor.isEditing());
|
this.currentView = provider.view(this.currentObject, this.openmct.editor.isEditing(), objectPath);
|
||||||
|
|
||||||
if (this.currentView.onEditModeChange) {
|
if (this.currentView.onEditModeChange) {
|
||||||
this.openmct.editor.on('isEditing', this.invokeEditModeHandler);
|
this.openmct.editor.on('isEditing', this.invokeEditModeHandler);
|
||||||
@ -117,7 +120,7 @@ export default {
|
|||||||
|
|
||||||
this.openmct.objectViews.on('clearData', this.clearData);
|
this.openmct.objectViews.on('clearData', this.clearData);
|
||||||
},
|
},
|
||||||
show(object, viewKey, immediatelySelect) {
|
show(object, viewKey, immediatelySelect, currentObjectPath) {
|
||||||
if (this.unlisten) {
|
if (this.unlisten) {
|
||||||
this.unlisten();
|
this.unlisten();
|
||||||
}
|
}
|
||||||
@ -132,6 +135,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.currentObject = object;
|
this.currentObject = object;
|
||||||
|
|
||||||
|
if (currentObjectPath) {
|
||||||
|
this.currentObjectPath = currentObjectPath;
|
||||||
|
}
|
||||||
|
|
||||||
this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => {
|
this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => {
|
||||||
this.currentObject = mutatedObject;
|
this.currentObject = mutatedObject;
|
||||||
});
|
});
|
||||||
|
@ -19,28 +19,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="l-browse-bar__end">
|
<div class="l-browse-bar__end">
|
||||||
<div class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
<view-switcher
|
||||||
v-if="views.length > 1">
|
:currentView="currentView"
|
||||||
<button class="c-button--menu"
|
:views="views"
|
||||||
:class="currentView.cssClass"
|
@setView="setView">
|
||||||
title="Switch view type"
|
</view-switcher>
|
||||||
@click.stop="toggleViewMenu">
|
|
||||||
<span class="c-button__label">
|
|
||||||
{{ currentView.name }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<div class="c-menu" v-show="showViewMenu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="(view, index) in views"
|
|
||||||
@click="setView(view)"
|
|
||||||
:key="index"
|
|
||||||
:class="view.cssClass"
|
|
||||||
:title="view.name">
|
|
||||||
{{ view.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Action buttons -->
|
<!-- Action buttons -->
|
||||||
<div class="l-browse-bar__actions">
|
<div class="l-browse-bar__actions">
|
||||||
<button v-if="notebookEnabled"
|
<button v-if="notebookEnabled"
|
||||||
@ -77,14 +60,15 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NotebookSnapshot from '../utils/notebook-snapshot';
|
import NotebookSnapshot from '../utils/notebook-snapshot';
|
||||||
|
import ViewSwitcher from './ViewSwitcher.vue';
|
||||||
const PLACEHOLDER_OBJECT = {};
|
const PLACEHOLDER_OBJECT = {};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
ViewSwitcher
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleViewMenu() {
|
|
||||||
this.showViewMenu = !this.showViewMenu;
|
|
||||||
},
|
|
||||||
toggleSaveMenu() {
|
toggleSaveMenu() {
|
||||||
this.showSaveMenu = !this.showSaveMenu;
|
this.showSaveMenu = !this.showSaveMenu;
|
||||||
},
|
},
|
||||||
|
55
src/ui/layout/ViewSwitcher.vue
Normal file
55
src/ui/layout/ViewSwitcher.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
||||||
|
v-if="views.length > 1">
|
||||||
|
<button class="c-button--menu"
|
||||||
|
:class="currentView.cssClass"
|
||||||
|
title="Switch view type"
|
||||||
|
@click.stop="toggleViewMenu">
|
||||||
|
<span class="c-button__label">
|
||||||
|
{{ currentView.name }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<div class="c-menu" v-show="showViewMenu">
|
||||||
|
<ul>
|
||||||
|
<li v-for="(view, index) in views"
|
||||||
|
@click="setView(view)"
|
||||||
|
:key="index"
|
||||||
|
:class="view.cssClass"
|
||||||
|
:title="view.name">
|
||||||
|
{{ view.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'currentView',
|
||||||
|
'views'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showViewMenu: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setView(view) {
|
||||||
|
this.$emit('setView', view);
|
||||||
|
},
|
||||||
|
toggleViewMenu() {
|
||||||
|
this.showViewMenu = !this.showViewMenu;
|
||||||
|
},
|
||||||
|
hideViewMenu() {
|
||||||
|
this.showViewMenu = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.addEventListener('click', this.hideViewMenu);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
document.removeEventListener('click', this.hideViewMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -33,6 +33,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="l-browse-bar__end">
|
<div class="l-browse-bar__end">
|
||||||
<div class="l-browse-bar__actions">
|
<div class="l-browse-bar__actions">
|
||||||
|
<view-switcher
|
||||||
|
:views="views"
|
||||||
|
:currentView="currentView"
|
||||||
|
@setView="setView">
|
||||||
|
</view-switcher>
|
||||||
<button v-if="notebookEnabled"
|
<button v-if="notebookEnabled"
|
||||||
class="l-browse-bar__actions__edit c-button icon-notebook"
|
class="l-browse-bar__actions__edit c-button icon-notebook"
|
||||||
title="New Notebook entry"
|
title="New Notebook entry"
|
||||||
@ -80,20 +85,52 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
|
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
|
||||||
|
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
|
||||||
import NotebookSnapshot from '../utils/notebook-snapshot';
|
import NotebookSnapshot from '../utils/notebook-snapshot';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ContextMenuDropDown
|
ContextMenuDropDown,
|
||||||
|
ViewSwitcher
|
||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'openmct',
|
'openmct',
|
||||||
'objectPath'
|
'objectPath'
|
||||||
],
|
],
|
||||||
|
computed: {
|
||||||
|
views() {
|
||||||
|
return this
|
||||||
|
.openmct
|
||||||
|
.objectViews
|
||||||
|
.get(this.domainObject);
|
||||||
|
},
|
||||||
|
currentView() {
|
||||||
|
return this.views.filter(v => v.key === this.viewKey)[0] || {};
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
snapshot() {
|
snapshot() {
|
||||||
let element = document.getElementsByClassName("l-preview-window__object-view")[0];
|
let element = document.getElementsByClassName("l-preview-window__object-view")[0];
|
||||||
this.notebookSnapshot.capture(this.domainObject, element);
|
this.notebookSnapshot.capture(this.domainObject, element);
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
if (this.view) {
|
||||||
|
this.view.destroy();
|
||||||
|
this.$refs.objectView.innerHTML = '';
|
||||||
|
}
|
||||||
|
delete this.view;
|
||||||
|
delete this.viewContainer;
|
||||||
|
},
|
||||||
|
setView(view) {
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
this.viewKey = view.key;
|
||||||
|
this.viewContainer = document.createElement('div');
|
||||||
|
this.viewContainer.classList.add('c-object-view','u-contents');
|
||||||
|
this.$refs.objectView.append(this.viewContainer);
|
||||||
|
|
||||||
|
this.view = this.currentView.view(this.domainObject, false, this.objectPath);
|
||||||
|
this.view.show(this.viewContainer, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -103,13 +140,13 @@
|
|||||||
return {
|
return {
|
||||||
domainObject: domainObject,
|
domainObject: domainObject,
|
||||||
type: type,
|
type: type,
|
||||||
notebookEnabled: false
|
notebookEnabled: false,
|
||||||
|
viewKey: undefined
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
let viewProvider = this.openmct.objectViews.get(this.domainObject)[0];
|
let view = this.openmct.objectViews.get(this.domainObject)[0];
|
||||||
this.view = viewProvider.view(this.domainObject);
|
this.setView(view);
|
||||||
this.view.show(this.$refs.objectView, false);
|
|
||||||
|
|
||||||
if (this.openmct.types.get('notebook')) {
|
if (this.openmct.types.get('notebook')) {
|
||||||
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
|
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
|
||||||
|
@ -28,6 +28,7 @@ export default class PreviewAction {
|
|||||||
* Metadata
|
* Metadata
|
||||||
*/
|
*/
|
||||||
this.name = 'Preview';
|
this.name = 'Preview';
|
||||||
|
this.key = 'preview';
|
||||||
this.description = 'Preview in large dialog';
|
this.description = 'Preview in large dialog';
|
||||||
this.cssClass = 'icon-eye-open';
|
this.cssClass = 'icon-eye-open';
|
||||||
|
|
||||||
|
35
src/ui/preview/ViewHistoricalDataAction.js
Normal file
35
src/ui/preview/ViewHistoricalDataAction.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import PreviewAction from './PreviewAction';
|
||||||
|
|
||||||
|
export default class ViewHistoricalDataAction extends PreviewAction {
|
||||||
|
constructor(openmct) {
|
||||||
|
super(openmct);
|
||||||
|
|
||||||
|
this.name = 'View Historical Data';
|
||||||
|
this.key = 'viewHistoricalData';
|
||||||
|
this.description = 'View Historical Data in a Table or Plot';
|
||||||
|
this.cssClass = 'icon-eye-open';
|
||||||
|
this.hideInDefaultMenu = true;
|
||||||
|
}
|
||||||
|
}
|
@ -20,9 +20,11 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import PreviewAction from './PreviewAction.js';
|
import PreviewAction from './PreviewAction.js';
|
||||||
|
import ViewHistoricalDataAction from './ViewHistoricalDataAction';
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return function (openmct) {
|
return function (openmct) {
|
||||||
openmct.contextMenu.registerAction(new PreviewAction(openmct));
|
openmct.contextMenu.registerAction(new PreviewAction(openmct));
|
||||||
}
|
openmct.contextMenu.registerAction(new ViewHistoricalDataAction(openmct));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -223,11 +223,11 @@ define(['EventEmitter'], function (EventEmitter) {
|
|||||||
/**
|
/**
|
||||||
* Provide a view of this object.
|
* Provide a view of this object.
|
||||||
*
|
*
|
||||||
* When called by Open MCT, this may include additional arguments
|
* When called by Open MCT, the following arguments will be passed to it:
|
||||||
* which are on the path to the object to be viewed; for instance,
|
* @param {object} domainObject - the domainObject that the view is provided for
|
||||||
* when viewing "A Folder" within "My Items", this method will be
|
* @param {boolean} isEditing - A boolean value indicating wether openmct is in a global edit mode
|
||||||
* invoked with "A Folder" (as a domain object) as the first argument,
|
* @param {array} objectPath - The current contextual object path of the view object
|
||||||
* and "My Items" as the second argument.
|
* eg current domainObject is located under MyItems which is under Root
|
||||||
*
|
*
|
||||||
* @method view
|
* @method view
|
||||||
* @memberof module:openmct.ViewProvider#
|
* @memberof module:openmct.ViewProvider#
|
||||||
|
@ -8,6 +8,7 @@ define([
|
|||||||
let navigateCall = 0;
|
let navigateCall = 0;
|
||||||
let browseObject;
|
let browseObject;
|
||||||
let unobserve = undefined;
|
let unobserve = undefined;
|
||||||
|
let currentObjectPath;
|
||||||
|
|
||||||
openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot);
|
openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot);
|
||||||
|
|
||||||
@ -26,7 +27,9 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
function viewObject(object, viewProvider) {
|
function viewObject(object, viewProvider) {
|
||||||
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true);
|
currentObjectPath = openmct.router.path;
|
||||||
|
|
||||||
|
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true, currentObjectPath);
|
||||||
openmct.layout.$refs.browseBar.domainObject = object;
|
openmct.layout.$refs.browseBar.domainObject = object;
|
||||||
openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
|
openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ class NotebookSnapshot {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function validateLocation(newParentObj) {
|
function validateLocation(newParentObj) {
|
||||||
return newParentObj.model.type === 'notebook';
|
return newParentObj.model.type === 'notebook' || newParentObj.model.type === 'notebook-svc';
|
||||||
}
|
}
|
||||||
|
|
||||||
return overlayModel;
|
return overlayModel;
|
||||||
|
Reference in New Issue
Block a user