mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
Feature/eslint plugin vue (#2548)
* Use eslint-plugin-vue to lint vue files
This commit is contained in:
parent
14ce5e159b
commit
14a0f84c1b
44
.eslintrc.js
44
.eslintrc.js
@ -5,9 +5,16 @@ module.exports = {
|
||||
"jasmine": true,
|
||||
"amd": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parser": "babel-eslint",
|
||||
"globals": {
|
||||
"_": "readonly"
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:vue/recommended"
|
||||
],
|
||||
"parser": "vue-eslint-parser",
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint",
|
||||
"allowImportExportEverywhere": true,
|
||||
"ecmaVersion": 2015,
|
||||
"ecmaFeatures": {
|
||||
@ -58,7 +65,38 @@ module.exports = {
|
||||
}
|
||||
],
|
||||
"dot-notation": "error",
|
||||
"indent": ["error", 4]
|
||||
"indent": ["error", 4],
|
||||
"vue/html-indent": [
|
||||
"error",
|
||||
4,
|
||||
{
|
||||
"attribute": 1,
|
||||
"baseIndent": 0,
|
||||
"closeBracket": 0,
|
||||
"alignAttributesVertically": true,
|
||||
"ignores": []
|
||||
}
|
||||
],
|
||||
"vue/html-self-closing": ["error",
|
||||
{
|
||||
"html": {
|
||||
"void": "never",
|
||||
"normal": "never",
|
||||
"component": "always"
|
||||
},
|
||||
"svg": "always",
|
||||
"math": "always"
|
||||
}
|
||||
],
|
||||
"vue/max-attributes-per-line": ["error", {
|
||||
"singleline": 1,
|
||||
"multiline": {
|
||||
"max": 1,
|
||||
"allowFirstLine": true
|
||||
}
|
||||
}],
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/singleline-html-element-content-newline": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
@ -23,6 +23,7 @@
|
||||
"d3-time": "1.0.x",
|
||||
"d3-time-format": "2.1.x",
|
||||
"eslint": "5.2.0",
|
||||
"eslint-plugin-vue": "^6.0.0",
|
||||
"eventemitter3": "^1.2.0",
|
||||
"exports-loader": "^0.7.0",
|
||||
"express": "^4.13.1",
|
||||
@ -74,8 +75,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"lint": "eslint platform example src openmct.js",
|
||||
"lint:fix": "eslint platform example src openmct.js --fix",
|
||||
"lint": "eslint platform example src/**/*.{js,vue} openmct.js",
|
||||
"lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix",
|
||||
"build:prod": "NODE_ENV=production webpack",
|
||||
"build:dev": "webpack",
|
||||
"build:watch": "webpack --watch",
|
||||
|
@ -1,20 +1,24 @@
|
||||
<template>
|
||||
<div class="c-menu">
|
||||
<ul>
|
||||
<li v-for="action in actions"
|
||||
:key="action.name"
|
||||
:class="action.cssClass"
|
||||
:title="action.description"
|
||||
@click="action.invoke(objectPath)">
|
||||
{{ action.name }}
|
||||
</li>
|
||||
<li v-if="actions.length === 0">No actions defined.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="c-menu">
|
||||
<ul>
|
||||
<li
|
||||
v-for="action in actions"
|
||||
:key="action.name"
|
||||
:class="action.cssClass"
|
||||
:title="action.description"
|
||||
@click="action.invoke(objectPath)"
|
||||
>
|
||||
{{ action.name }}
|
||||
</li>
|
||||
<li v-if="actions.length === 0">
|
||||
No actions defined.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['actions', 'objectPath']
|
||||
}
|
||||
</script>
|
||||
export default {
|
||||
inject: ['actions', 'objectPath']
|
||||
}
|
||||
</script>
|
||||
|
@ -1,28 +1,36 @@
|
||||
<template>
|
||||
<div class="c-message">
|
||||
<!--Uses flex-row -->
|
||||
<div class="c-message__icon"
|
||||
:class="['u-icon-bg-color-' + iconClass]"></div>
|
||||
<div class="c-message__text">
|
||||
<!-- Uses flex-column -->
|
||||
<div class="c-message__title"
|
||||
v-if="title">
|
||||
{{title}}
|
||||
</div>
|
||||
|
||||
<div class="c-message__hint"
|
||||
v-if="hint">
|
||||
{{hint}}
|
||||
<span v-if="timestamp">[{{timestamp}}]</span>
|
||||
</div>
|
||||
|
||||
<div class="c-message__action-text"
|
||||
v-if="message">
|
||||
{{message}}
|
||||
</div>
|
||||
<slot></slot>
|
||||
<div class="c-message">
|
||||
<!--Uses flex-row -->
|
||||
<div
|
||||
class="c-message__icon"
|
||||
:class="['u-icon-bg-color-' + iconClass]"
|
||||
></div>
|
||||
<div class="c-message__text">
|
||||
<!-- Uses flex-column -->
|
||||
<div
|
||||
v-if="title"
|
||||
class="c-message__title"
|
||||
>
|
||||
{{ title }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="hint"
|
||||
class="c-message__hint"
|
||||
>
|
||||
{{ hint }}
|
||||
<span v-if="timestamp">[{{ timestamp }}]</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="message"
|
||||
class="c-message__action-text"
|
||||
>
|
||||
{{ message }}
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -1,28 +1,57 @@
|
||||
<template>
|
||||
<div class="c-overlay">
|
||||
<div class="c-overlay__blocker"
|
||||
@click="destroy">
|
||||
</div>
|
||||
<div class="c-overlay__outer">
|
||||
<button class="c-click-icon c-overlay__close-button icon-x-in-circle"
|
||||
v-if="dismissable"
|
||||
@click="destroy">
|
||||
<div class="c-overlay">
|
||||
<div
|
||||
class="c-overlay__blocker"
|
||||
@click="destroy"
|
||||
></div>
|
||||
<div class="c-overlay__outer">
|
||||
<button
|
||||
v-if="dismissable"
|
||||
class="c-click-icon c-overlay__close-button icon-x-in-circle"
|
||||
@click="destroy"
|
||||
></button>
|
||||
<div
|
||||
ref="element"
|
||||
class="c-overlay__contents"
|
||||
></div>
|
||||
<div
|
||||
v-if="buttons"
|
||||
class="c-overlay__button-bar"
|
||||
>
|
||||
<button
|
||||
v-for="(button, index) in buttons"
|
||||
:key="index"
|
||||
class="c-button"
|
||||
:class="{'c-button--major': button.emphasis}"
|
||||
@click="buttonClickHandler(button.callback)"
|
||||
>
|
||||
{{ button.label }}
|
||||
</button>
|
||||
<div class="c-overlay__contents" ref="element" tabindex="0"></div>
|
||||
<div class="c-overlay__button-bar" v-if="buttons">
|
||||
<button class="c-button"
|
||||
tabindex="0"
|
||||
ref="buttons"
|
||||
v-for="(button, index) in buttons"
|
||||
:key="index"
|
||||
@focus="focusIndex=index"
|
||||
:class="{'c-button--major': focusIndex===index}"
|
||||
@click="buttonClickHandler(button.callback)">
|
||||
{{button.label}}
|
||||
<div
|
||||
ref="element"
|
||||
class="c-overlay__contents"
|
||||
tabindex="0"
|
||||
></div>
|
||||
<div
|
||||
v-if="buttons"
|
||||
class="c-overlay__button-bar"
|
||||
>
|
||||
<button
|
||||
v-for="(button, index) in buttons"
|
||||
ref="buttons"
|
||||
:key="index"
|
||||
class="c-button"
|
||||
tabindex="0"
|
||||
:class="{'c-button--major': focusIndex===index}"
|
||||
@focus="focusIndex=index"
|
||||
@click="buttonClickHandler(button.callback)"
|
||||
>
|
||||
{{ button.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -138,7 +167,7 @@
|
||||
box-shadow: rgba(black, 0.5) 0 2px 25px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.l-overlay-fullscreen {
|
||||
// Used by About > Licenses display
|
||||
.c-overlay__outer {
|
||||
@ -174,50 +203,50 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
focusIndex: -1
|
||||
};
|
||||
},
|
||||
inject: ['dismiss', 'element', 'buttons', 'dismissable'],
|
||||
mounted() {
|
||||
const element = this.$refs.element;
|
||||
element.appendChild(this.element);
|
||||
const elementForFocus = this.getElementForFocus() || element;
|
||||
this.$nextTick(() => {
|
||||
elementForFocus.focus();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
destroy: function () {
|
||||
if (this.dismissable) {
|
||||
this.dismiss();
|
||||
}
|
||||
},
|
||||
buttonClickHandler: function (method) {
|
||||
method();
|
||||
this.$emit('destroy');
|
||||
},
|
||||
getElementForFocus: function () {
|
||||
const defaultElement = this.$refs.element;;
|
||||
if (!this.$refs.buttons) {
|
||||
return defaultElement;
|
||||
}
|
||||
|
||||
const focusButton = this.$refs.buttons.filter((button, index) => {
|
||||
if (this.buttons[index].emphasis) {
|
||||
this.focusIndex = index;
|
||||
}
|
||||
return this.buttons[index].emphasis;
|
||||
});
|
||||
|
||||
if (!focusButton.length) {
|
||||
return defaultElement;
|
||||
}
|
||||
|
||||
return focusButton[0];
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
focusIndex: -1
|
||||
};
|
||||
},
|
||||
inject: ['dismiss', 'element', 'buttons', 'dismissable'],
|
||||
mounted() {
|
||||
const element = this.$refs.element;
|
||||
element.appendChild(this.element);
|
||||
const elementForFocus = this.getElementForFocus() || element;
|
||||
this.$nextTick(() => {
|
||||
elementForFocus.focus();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
destroy: function () {
|
||||
if (this.dismissable) {
|
||||
this.dismiss();
|
||||
}
|
||||
},
|
||||
buttonClickHandler: function (method) {
|
||||
method();
|
||||
this.$emit('destroy');
|
||||
},
|
||||
getElementForFocus: function () {
|
||||
const defaultElement = this.$refs.element;
|
||||
if (!this.$refs.buttons) {
|
||||
return defaultElement;
|
||||
}
|
||||
|
||||
const focusButton = this.$refs.buttons.filter((button, index) => {
|
||||
if (this.buttons[index].emphasis) {
|
||||
this.focusIndex = index;
|
||||
}
|
||||
return this.buttons[index].emphasis;
|
||||
});
|
||||
|
||||
if (!focusButton.length) {
|
||||
return defaultElement;
|
||||
}
|
||||
|
||||
return focusButton[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<dialog-component>
|
||||
<progress-component :model="model"></progress-component>
|
||||
</dialog-component>
|
||||
<dialog-component>
|
||||
<progress-component :model="model" />
|
||||
</dialog-component>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -14,9 +14,14 @@ import DialogComponent from './DialogComponent.vue';
|
||||
export default {
|
||||
components: {
|
||||
DialogComponent: DialogComponent,
|
||||
ProgressComponent: ProgressComponent,
|
||||
ProgressComponent: ProgressComponent
|
||||
},
|
||||
inject:['iconClass', 'title', 'hint', 'timestamp', 'message'],
|
||||
props:['model']
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -44,6 +44,7 @@ define([
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
LadTableSet: LadTableSet.default
|
||||
},
|
||||
@ -52,7 +53,6 @@ define([
|
||||
domainObject,
|
||||
objectPath
|
||||
},
|
||||
el: element,
|
||||
template: '<lad-table-set></lad-table-set>'
|
||||
});
|
||||
},
|
||||
|
@ -44,6 +44,7 @@ define([
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
LadTableComponent: LadTableComponent.default
|
||||
},
|
||||
@ -52,7 +53,6 @@ define([
|
||||
domainObject,
|
||||
objectPath
|
||||
},
|
||||
el: element,
|
||||
template: '<lad-table-component></lad-table-component>'
|
||||
});
|
||||
},
|
||||
|
@ -22,13 +22,13 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<tr @contextmenu.prevent="showContextMenu">
|
||||
<td>{{name}}</td>
|
||||
<td>{{timestamp}}</td>
|
||||
<td :class="valueClass">
|
||||
{{value}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr @contextmenu.prevent="showContextMenu">
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ timestamp }}</td>
|
||||
<td :class="valueClass">
|
||||
{{ value }}
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -44,7 +44,12 @@ const CONTEXT_MENU_ACTIONS = [
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'objectPath'],
|
||||
props: ['domainObject'],
|
||||
props: {
|
||||
domainObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let currentObjectPath = this.objectPath.slice();
|
||||
currentObjectPath.unshift(this.domainObject);
|
||||
@ -57,48 +62,16 @@ export default {
|
||||
currentObjectPath
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValues(datum) {
|
||||
this.timestamp = this.formats[this.timestampKey].format(datum);
|
||||
this.value = this.formats[this.valueKey].format(datum);
|
||||
|
||||
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
||||
|
||||
if (limit) {
|
||||
this.valueClass = limit.cssClass;
|
||||
} else {
|
||||
this.valueClass = '';
|
||||
}
|
||||
},
|
||||
updateName(name){
|
||||
this.name = name;
|
||||
},
|
||||
updateTimeSystem(timeSystem) {
|
||||
this.value = '---';
|
||||
this.timestamp = '---';
|
||||
this.valueClass = '';
|
||||
this.timestampKey = timeSystem.key;
|
||||
|
||||
this.openmct
|
||||
.telemetry
|
||||
.request(this.domainObject, {strategy: 'latest'})
|
||||
.then((array) => this.updateValues(array[array.length - 1]));
|
||||
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
|
||||
this.limitEvaluator = openmct
|
||||
this.limitEvaluator = this.openmct
|
||||
.telemetry
|
||||
.limitEvaluator(this.domainObject);
|
||||
|
||||
this.stopWatchingMutation = openmct
|
||||
this.stopWatchingMutation = this.openmct
|
||||
.objects
|
||||
.observe(
|
||||
this.domainObject,
|
||||
@ -129,6 +102,38 @@ export default {
|
||||
this.stopWatchingMutation();
|
||||
this.unsubscribe();
|
||||
this.openmct.off('timeSystem', this.updateTimeSystem);
|
||||
},
|
||||
methods: {
|
||||
updateValues(datum) {
|
||||
this.timestamp = this.formats[this.timestampKey].format(datum);
|
||||
this.value = this.formats[this.valueKey].format(datum);
|
||||
|
||||
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
||||
|
||||
if (limit) {
|
||||
this.valueClass = limit.cssClass;
|
||||
} else {
|
||||
this.valueClass = '';
|
||||
}
|
||||
},
|
||||
updateName(name) {
|
||||
this.name = name;
|
||||
},
|
||||
updateTimeSystem(timeSystem) {
|
||||
this.value = '---';
|
||||
this.timestamp = '---';
|
||||
this.valueClass = '';
|
||||
this.timestampKey = timeSystem.key;
|
||||
|
||||
this.openmct
|
||||
.telemetry
|
||||
.request(this.domainObject, {strategy: 'latest'})
|
||||
.then((array) => this.updateValues(array[array.length - 1]));
|
||||
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -30,17 +30,16 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<lad-row
|
||||
<lad-row
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
:domainObject="item.domainObject">
|
||||
</lad-row>
|
||||
:domain-object="item.domainObject"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import lodash from 'lodash';
|
||||
import LadRow from './LADRow.vue';
|
||||
|
||||
export default {
|
||||
@ -53,6 +52,18 @@ export default {
|
||||
items: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addItem);
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.reorder);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.reorder);
|
||||
},
|
||||
methods: {
|
||||
addItem(domainObject) {
|
||||
let item = {};
|
||||
@ -72,18 +83,6 @@ export default {
|
||||
this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addItem);
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.reorder);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.reorder);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,29 +21,34 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<table class="c-table c-lad-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Value</th>
|
||||
<table class="c-table c-lad-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template
|
||||
v-for="primary in primaryTelemetryObjects"
|
||||
>
|
||||
<tr
|
||||
:key="primary.key"
|
||||
class="c-table__group-header"
|
||||
>
|
||||
<td colspan="10">
|
||||
{{ primary.domainObject.name }}
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template
|
||||
v-for="primary in primaryTelemetryObjects">
|
||||
<tr class="c-table__group-header"
|
||||
:key="primary.key">
|
||||
<td colspan="10">{{primary.domainObject.name}}</td>
|
||||
</tr>
|
||||
<lad-row
|
||||
v-for="secondary in secondaryTelemetryObjects[primary.key]"
|
||||
:key="secondary.key"
|
||||
:domainObject="secondary.domainObject">
|
||||
</lad-row>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<lad-row
|
||||
v-for="secondary in secondaryTelemetryObjects[primary.key]"
|
||||
:key="secondary.key"
|
||||
:domain-object="secondary.domainObject"
|
||||
/>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -51,10 +56,9 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import lodash from 'lodash';
|
||||
import LadRow from './LADRow.vue';
|
||||
import LadRow from './LADRow.vue';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
components: {
|
||||
LadRow
|
||||
@ -66,6 +70,22 @@
|
||||
compositions: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addPrimary);
|
||||
this.composition.on('remove', this.removePrimary);
|
||||
this.composition.on('reorder', this.reorderPrimary);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addPrimary);
|
||||
this.composition.off('remove', this.removePrimary);
|
||||
this.composition.off('reorder', this.reorderPrimary);
|
||||
this.compositions.forEach(c => {
|
||||
c.composition.off('add', c.addCallback);
|
||||
c.composition.off('remove', c.removeCallback);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
addPrimary(domainObject) {
|
||||
let primary = {};
|
||||
@ -75,7 +95,7 @@
|
||||
this.$set(this.secondaryTelemetryObjects, primary.key, []);
|
||||
this.primaryTelemetryObjects.push(primary);
|
||||
|
||||
let composition = openmct.composition.get(primary.domainObject),
|
||||
let composition = this.openmct.composition.get(primary.domainObject),
|
||||
addCallback = this.addSecondary(primary),
|
||||
removeCallback = this.removeSecondary(primary);
|
||||
|
||||
@ -88,7 +108,7 @@
|
||||
removePrimary(identifier) {
|
||||
let index = _.findIndex(this.primaryTelemetryObjects, (primary) => this.openmct.objects.makeKeyString(identifier) === primary.key),
|
||||
primary = this.primaryTelemetryObjects[index];
|
||||
|
||||
|
||||
this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
|
||||
this.primaryTelemetryObjects.splice(index,1);
|
||||
primary = undefined;
|
||||
@ -107,7 +127,7 @@
|
||||
|
||||
let array = this.secondaryTelemetryObjects[primary.key];
|
||||
array.push(secondary);
|
||||
|
||||
|
||||
this.$set(this.secondaryTelemetryObjects, primary.key, array);
|
||||
}
|
||||
},
|
||||
@ -121,23 +141,6 @@
|
||||
this.$set(this.secondaryTelemetryObjects, primary.key, array);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addPrimary);
|
||||
this.composition.on('remove', this.removePrimary);
|
||||
this.composition.on('reorder', this.reorderPrimary);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addPrimary);
|
||||
this.composition.off('remove', this.removePrimary);
|
||||
this.composition.off('reorder', this.reorderPrimary);
|
||||
this.compositions.forEach(c => {
|
||||
c.composition.off('add', c.addCallback);
|
||||
c.composition.off('remove', c.removeCallback);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="c-indicator c-indicator--clickable icon-clear-data s-status-caution">
|
||||
<span class="label c-indicator__label">
|
||||
<button @click="globalClearEmit">Clear Data</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="c-indicator c-indicator--clickable icon-clear-data s-status-caution">
|
||||
<span class="label c-indicator__label">
|
||||
<button @click="globalClearEmit">Clear Data</button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -55,11 +55,11 @@ define([
|
||||
openmct,
|
||||
objectPath
|
||||
},
|
||||
el: element,
|
||||
components: {
|
||||
AlphanumericFormatView: AlphanumericFormatView.default
|
||||
},
|
||||
template: '<alphanumeric-format-view></alphanumeric-format-view>',
|
||||
el: element
|
||||
template: '<alphanumeric-format-view></alphanumeric-format-view>'
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
|
@ -21,70 +21,78 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-properties" v-if="isEditing">
|
||||
<div class="c-properties__header">Alphanumeric Format</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label" title="Printf formatting for the selected telemetry">
|
||||
<label for="telemetryPrintfFormat">Format</label>
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
<input id="telemetryPrintfFormat"
|
||||
type="text"
|
||||
@change="formatTelemetry"
|
||||
:value="telemetryFormat"
|
||||
:placeholder="nonMixedFormat ? '' : 'Mixed'"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
v-if="isEditing"
|
||||
class="c-properties"
|
||||
>
|
||||
<div class="c-properties__header">
|
||||
Alphanumeric Format
|
||||
</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row">
|
||||
<div
|
||||
class="c-properties__label"
|
||||
title="Printf formatting for the selected telemetry"
|
||||
>
|
||||
<label for="telemetryPrintfFormat">Format</label>
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
<input
|
||||
id="telemetryPrintfFormat"
|
||||
type="text"
|
||||
:value="telemetryFormat"
|
||||
:placeholder="nonMixedFormat ? '' : 'Mixed'"
|
||||
@change="formatTelemetry"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
let selectionPath = this.openmct.selection.get()[0];
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
telemetryFormat: undefined,
|
||||
nonMixedFormat: false
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
telemetryFormat: undefined,
|
||||
nonMixedFormat: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
this.openmct.selection.on('change', this.handleSelection);
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.openmct.selection.off('change', this.handleSelection);
|
||||
},
|
||||
methods: {
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
formatTelemetry(event) {
|
||||
let newFormat = event.currentTarget.value;
|
||||
this.openmct.selection.get().forEach(selectionPath => {
|
||||
selectionPath[0].context.updateTelemetryFormat(newFormat);
|
||||
});
|
||||
this.telemetryFormat = newFormat;
|
||||
},
|
||||
handleSelection(selection) {
|
||||
if (selection.length === 0 || selection[0].length < 2) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
formatTelemetry(event) {
|
||||
let newFormat = event.currentTarget.value;
|
||||
this.openmct.selection.get().forEach(selectionPath => {
|
||||
selectionPath[0].context.updateTelemetryFormat(newFormat);
|
||||
});
|
||||
this.telemetryFormat = newFormat;
|
||||
},
|
||||
handleSelection(selection) {
|
||||
if (selection.length === 0 || selection[0].length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let format = selection[0][0].context.layoutItem.format;
|
||||
this.nonMixedFormat = selection.every(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.format === format;
|
||||
});
|
||||
let format = selection[0][0].context.layoutItem.format;
|
||||
this.nonMixedFormat = selection.every(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.format === format;
|
||||
});
|
||||
|
||||
this.telemetryFormat = this.nonMixedFormat ? format : '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
this.openmct.selection.on('change', this.handleSelection);
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.openmct.selection.off('change', this.handleSelection);
|
||||
this.telemetryFormat = this.nonMixedFormat ? format : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
@ -21,15 +21,18 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
<div class="c-box-view"
|
||||
:style="style">
|
||||
</div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
<layout-frame
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<div
|
||||
class="c-box-view"
|
||||
:style="style"
|
||||
></div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
@ -44,60 +47,70 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
|
||||
export default {
|
||||
makeDefinition() {
|
||||
export default {
|
||||
makeDefinition() {
|
||||
return {
|
||||
fill: '#717171',
|
||||
stroke: 'transparent',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
initSelect: Boolean
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
fill: '#717171',
|
||||
stroke: 'transparent',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5
|
||||
backgroundColor: this.item.fill,
|
||||
border: '1px solid ' + this.item.stroke
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
index: Number,
|
||||
initSelect: Boolean
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
backgroundColor: this.item.fill,
|
||||
border: '1px solid ' + this.item.stroke
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -21,42 +21,49 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="l-layout"
|
||||
@dragover="handleDragOver"
|
||||
@click.capture="bypassSelection"
|
||||
@drop="handleDrop"
|
||||
:class="{
|
||||
'is-multi-selected': selectedLayoutItems.length > 1
|
||||
}">
|
||||
<!-- Background grid -->
|
||||
<div class="l-layout__grid-holder c-grid">
|
||||
<div class="c-grid__x l-grid l-grid-x"
|
||||
v-if="gridSize[0] >= 3"
|
||||
:style="[{ backgroundSize: gridSize[0] + 'px 100%' }]">
|
||||
</div>
|
||||
<div class="c-grid__y l-grid l-grid-y"
|
||||
v-if="gridSize[1] >= 3"
|
||||
:style="[{ backgroundSize: '100%' + gridSize[1] + 'px' }]"></div>
|
||||
</div>
|
||||
<component v-for="(item, index) in layoutItems"
|
||||
:is="item.type"
|
||||
:item="item"
|
||||
:key="item.id"
|
||||
:gridSize="gridSize"
|
||||
:initSelect="initSelectIndex === index"
|
||||
:index="index"
|
||||
:multiSelect="selectedLayoutItems.length > 1"
|
||||
@move="move"
|
||||
@endMove="endMove"
|
||||
@endLineResize='endLineResize'
|
||||
@formatChanged='updateTelemetryFormat'>
|
||||
</component>
|
||||
<edit-marquee v-if='showMarquee'
|
||||
:gridSize="gridSize"
|
||||
:selectedLayoutItems="selectedLayoutItems"
|
||||
@endResize="endResize">
|
||||
</edit-marquee>
|
||||
<div
|
||||
class="l-layout"
|
||||
:class="{
|
||||
'is-multi-selected': selectedLayoutItems.length > 1
|
||||
}"
|
||||
@dragover="handleDragOver"
|
||||
@click.capture="bypassSelection"
|
||||
@drop="handleDrop"
|
||||
>
|
||||
<!-- Background grid -->
|
||||
<div class="l-layout__grid-holder c-grid">
|
||||
<div
|
||||
v-if="gridSize[0] >= 3"
|
||||
class="c-grid__x l-grid l-grid-x"
|
||||
:style="[{ backgroundSize: gridSize[0] + 'px 100%' }]"
|
||||
></div>
|
||||
<div
|
||||
v-if="gridSize[1] >= 3"
|
||||
class="c-grid__y l-grid l-grid-y"
|
||||
:style="[{ backgroundSize: '100%' + gridSize[1] + 'px' }]"
|
||||
></div>
|
||||
</div>
|
||||
<component
|
||||
:is="item.type"
|
||||
v-for="(item, index) in layoutItems"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
:init-select="initSelectIndex === index"
|
||||
:index="index"
|
||||
:multi-select="selectedLayoutItems.length > 1"
|
||||
@move="move"
|
||||
@endMove="endMove"
|
||||
@endLineResize="endLineResize"
|
||||
@formatChanged="updateTelemetryFormat"
|
||||
/>
|
||||
<edit-marquee
|
||||
v-if="showMarquee"
|
||||
:grid-size="gridSize"
|
||||
:selected-layout-items="selectedLayoutItems"
|
||||
@endResize="endResize"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -135,452 +142,455 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import uuid from 'uuid';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import SubobjectView from './SubobjectView.vue'
|
||||
import TelemetryView from './TelemetryView.vue'
|
||||
import BoxView from './BoxView.vue'
|
||||
import TextView from './TextView.vue'
|
||||
import LineView from './LineView.vue'
|
||||
import ImageView from './ImageView.vue'
|
||||
import EditMarquee from './EditMarquee.vue'
|
||||
import SubobjectView from './SubobjectView.vue'
|
||||
import TelemetryView from './TelemetryView.vue'
|
||||
import BoxView from './BoxView.vue'
|
||||
import TextView from './TextView.vue'
|
||||
import LineView from './LineView.vue'
|
||||
import ImageView from './ImageView.vue'
|
||||
import EditMarquee from './EditMarquee.vue'
|
||||
|
||||
const ITEM_TYPE_VIEW_MAP = {
|
||||
'subobject-view': SubobjectView,
|
||||
'telemetry-view': TelemetryView,
|
||||
'box-view': BoxView,
|
||||
'line-view': LineView,
|
||||
'text-view': TextView,
|
||||
'image-view': ImageView
|
||||
};
|
||||
const ORDERS = {
|
||||
top: Number.POSITIVE_INFINITY,
|
||||
up: 1,
|
||||
down: -1,
|
||||
bottom: Number.NEGATIVE_INFINITY
|
||||
};
|
||||
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
|
||||
const ITEM_TYPE_VIEW_MAP = {
|
||||
'subobject-view': SubobjectView,
|
||||
'telemetry-view': TelemetryView,
|
||||
'box-view': BoxView,
|
||||
'line-view': LineView,
|
||||
'text-view': TextView,
|
||||
'image-view': ImageView
|
||||
};
|
||||
const ORDERS = {
|
||||
top: Number.POSITIVE_INFINITY,
|
||||
up: 1,
|
||||
down: -1,
|
||||
bottom: Number.NEGATIVE_INFINITY
|
||||
};
|
||||
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
|
||||
|
||||
let components = ITEM_TYPE_VIEW_MAP;
|
||||
components['edit-marquee'] = EditMarquee;
|
||||
let components = ITEM_TYPE_VIEW_MAP;
|
||||
components['edit-marquee'] = EditMarquee;
|
||||
|
||||
function getItemDefinition(itemType, ...options) {
|
||||
let itemView = ITEM_TYPE_VIEW_MAP[itemType];
|
||||
function getItemDefinition(itemType, ...options) {
|
||||
let itemView = ITEM_TYPE_VIEW_MAP[itemType];
|
||||
|
||||
if (!itemView) {
|
||||
throw `Invalid itemType: ${itemType}`;
|
||||
}
|
||||
|
||||
return itemView.makeDefinition(...options);
|
||||
if (!itemView) {
|
||||
throw `Invalid itemType: ${itemType}`;
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let domainObject = JSON.parse(JSON.stringify(this.domainObject));
|
||||
return {
|
||||
internalDomainObject: domainObject,
|
||||
initSelectIndex: undefined,
|
||||
selection: []
|
||||
};
|
||||
return itemView.makeDefinition(...options);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: components,
|
||||
props: {
|
||||
domainObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let domainObject = JSON.parse(JSON.stringify(this.domainObject));
|
||||
return {
|
||||
internalDomainObject: domainObject,
|
||||
initSelectIndex: undefined,
|
||||
selection: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
gridSize() {
|
||||
return this.internalDomainObject.configuration.layoutGrid;
|
||||
},
|
||||
computed: {
|
||||
gridSize() {
|
||||
return this.internalDomainObject.configuration.layoutGrid;
|
||||
},
|
||||
layoutItems() {
|
||||
return this.internalDomainObject.configuration.items;
|
||||
},
|
||||
selectedLayoutItems() {
|
||||
return this.layoutItems.filter(item => {
|
||||
return this.itemIsInCurrentSelection(item);
|
||||
});
|
||||
},
|
||||
showMarquee() {
|
||||
let selectionPath = this.selection[0];
|
||||
let singleSelectedLine = this.selection.length === 1 &&
|
||||
layoutItems() {
|
||||
return this.internalDomainObject.configuration.items;
|
||||
},
|
||||
selectedLayoutItems() {
|
||||
return this.layoutItems.filter(item => {
|
||||
return this.itemIsInCurrentSelection(item);
|
||||
});
|
||||
},
|
||||
showMarquee() {
|
||||
let selectionPath = this.selection[0];
|
||||
let singleSelectedLine = this.selection.length === 1 &&
|
||||
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type === 'line-view';
|
||||
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
|
||||
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
|
||||
}
|
||||
},
|
||||
inject: ['openmct', 'options', 'objectPath'],
|
||||
mounted() {
|
||||
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', function (obj) {
|
||||
this.internalDomainObject = JSON.parse(JSON.stringify(obj));
|
||||
}.bind(this));
|
||||
this.openmct.selection.on('change', this.setSelection);
|
||||
this.initializeItems();
|
||||
this.composition = this.openmct.composition.get(this.internalDomainObject);
|
||||
this.composition.on('add', this.addChild);
|
||||
this.composition.on('remove', this.removeChild);
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.selection.off('change', this.setSelection);
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
this.unlisten();
|
||||
},
|
||||
methods: {
|
||||
addElement(itemType, element) {
|
||||
this.addItem(itemType + '-view', element);
|
||||
},
|
||||
setSelection(selection) {
|
||||
this.selection = selection;
|
||||
},
|
||||
itemIsInCurrentSelection(item) {
|
||||
return this.selection.some(selectionPath =>
|
||||
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.id === item.id);
|
||||
},
|
||||
bypassSelection($event) {
|
||||
if (this.dragInProgress) {
|
||||
if ($event) {
|
||||
$event.stopImmediatePropagation();
|
||||
}
|
||||
this.dragInProgress = false;
|
||||
return;
|
||||
}
|
||||
},
|
||||
inject: ['openmct', 'options', 'objectPath'],
|
||||
props: ['domainObject'],
|
||||
components: components,
|
||||
methods: {
|
||||
addElement(itemType, element) {
|
||||
this.addItem(itemType + '-view', element);
|
||||
},
|
||||
setSelection(selection) {
|
||||
this.selection = selection;
|
||||
},
|
||||
itemIsInCurrentSelection(item) {
|
||||
return this.selection.some(selectionPath =>
|
||||
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.id === item.id);
|
||||
},
|
||||
bypassSelection($event) {
|
||||
if (this.dragInProgress) {
|
||||
if ($event) {
|
||||
$event.stopImmediatePropagation();
|
||||
}
|
||||
this.dragInProgress = false;
|
||||
return;
|
||||
}
|
||||
},
|
||||
endLineResize(item, updates) {
|
||||
this.dragInProgress = true;
|
||||
let index = this.layoutItems.indexOf(item);
|
||||
Object.assign(item, updates);
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
},
|
||||
endResize(scaleWidth, scaleHeight, marqueeStart, marqueeOffset) {
|
||||
this.dragInProgress = true;
|
||||
this.layoutItems.forEach(item => {
|
||||
if (this.itemIsInCurrentSelection(item)) {
|
||||
let itemXInMarqueeSpace = item.x - marqueeStart.x;
|
||||
let itemXInMarqueeSpaceAfterScale = Math.round(itemXInMarqueeSpace * scaleWidth);
|
||||
item.x = itemXInMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
|
||||
endLineResize(item, updates) {
|
||||
this.dragInProgress = true;
|
||||
let index = this.layoutItems.indexOf(item);
|
||||
Object.assign(item, updates);
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
},
|
||||
endResize(scaleWidth, scaleHeight, marqueeStart, marqueeOffset) {
|
||||
this.dragInProgress = true;
|
||||
this.layoutItems.forEach(item => {
|
||||
if (this.itemIsInCurrentSelection(item)) {
|
||||
let itemXInMarqueeSpace = item.x - marqueeStart.x;
|
||||
let itemXInMarqueeSpaceAfterScale = Math.round(itemXInMarqueeSpace * scaleWidth);
|
||||
item.x = itemXInMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
|
||||
|
||||
let itemYInMarqueeSpace = item.y - marqueeStart.y;
|
||||
let itemYInMarqueeSpaceAfterScale = Math.round(itemYInMarqueeSpace * scaleHeight);
|
||||
item.y = itemYInMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
|
||||
let itemYInMarqueeSpace = item.y - marqueeStart.y;
|
||||
let itemYInMarqueeSpaceAfterScale = Math.round(itemYInMarqueeSpace * scaleHeight);
|
||||
item.y = itemYInMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
|
||||
|
||||
if (item.x2) {
|
||||
let itemX2InMarqueeSpace = item.x2 - marqueeStart.x;
|
||||
let itemX2InMarqueeSpaceAfterScale = Math.round(itemX2InMarqueeSpace * scaleWidth);
|
||||
item.x2 = itemX2InMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
|
||||
} else {
|
||||
item.width = Math.round(item.width * scaleWidth);
|
||||
}
|
||||
|
||||
if (item.y2) {
|
||||
let itemY2InMarqueeSpace = item.y2 - marqueeStart.y;
|
||||
let itemY2InMarqueeSpaceAfterScale = Math.round(itemY2InMarqueeSpace * scaleHeight);
|
||||
item.y2 = itemY2InMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
|
||||
} else {
|
||||
item.height = Math.round(item.height * scaleHeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.mutate("configuration.items", this.layoutItems);
|
||||
},
|
||||
move(gridDelta) {
|
||||
this.dragInProgress = true;
|
||||
|
||||
if (!this.initialPositions) {
|
||||
this.initialPositions = {};
|
||||
_.cloneDeep(this.selectedLayoutItems).forEach(selectedItem => {
|
||||
if (selectedItem.type === 'line-view') {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y, selectedItem.x2, selectedItem.y2];
|
||||
this.startingMinX2 = this.startingMinX2 !== undefined ? Math.min(this.startingMinX2, selectedItem.x2) : selectedItem.x2;
|
||||
this.startingMinY2 = this.startingMinY2 !== undefined ? Math.min(this.startingMinY2, selectedItem.y2) : selectedItem.y2;
|
||||
} else {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y];
|
||||
}
|
||||
|
||||
this.startingMinX = this.startingMinX !== undefined ? Math.min(this.startingMinX, selectedItem.x) : selectedItem.x;
|
||||
this.startingMinY = this.startingMinY !== undefined ? Math.min(this.startingMinY, selectedItem.y) : selectedItem.y;
|
||||
});
|
||||
}
|
||||
|
||||
let layoutItems = this.layoutItems.map(item => {
|
||||
if (this.initialPositions[item.id]) {
|
||||
this.updateItemPosition(item, gridDelta);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
},
|
||||
updateItemPosition(item, gridDelta) {
|
||||
let startingPosition = this.initialPositions[item.id];
|
||||
let [startingX, startingY, startingX2, startingY2] = startingPosition;
|
||||
|
||||
if (this.startingMinX + gridDelta[0] >= 0) {
|
||||
if (item.x2 !== undefined) {
|
||||
if (this.startingMinX2 + gridDelta[0] >= 0) {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
if (item.x2) {
|
||||
let itemX2InMarqueeSpace = item.x2 - marqueeStart.x;
|
||||
let itemX2InMarqueeSpaceAfterScale = Math.round(itemX2InMarqueeSpace * scaleWidth);
|
||||
item.x2 = itemX2InMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
|
||||
} else {
|
||||
item.width = Math.round(item.width * scaleWidth);
|
||||
}
|
||||
|
||||
if (item.y2) {
|
||||
let itemY2InMarqueeSpace = item.y2 - marqueeStart.y;
|
||||
let itemY2InMarqueeSpaceAfterScale = Math.round(itemY2InMarqueeSpace * scaleHeight);
|
||||
item.y2 = itemY2InMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
|
||||
} else {
|
||||
item.height = Math.round(item.height * scaleHeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.mutate("configuration.items", this.layoutItems);
|
||||
},
|
||||
move(gridDelta) {
|
||||
this.dragInProgress = true;
|
||||
|
||||
if (!this.initialPositions) {
|
||||
this.initialPositions = {};
|
||||
_.cloneDeep(this.selectedLayoutItems).forEach(selectedItem => {
|
||||
if (selectedItem.type === 'line-view') {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y, selectedItem.x2, selectedItem.y2];
|
||||
this.startingMinX2 = this.startingMinX2 !== undefined ? Math.min(this.startingMinX2, selectedItem.x2) : selectedItem.x2;
|
||||
this.startingMinY2 = this.startingMinY2 !== undefined ? Math.min(this.startingMinY2, selectedItem.y2) : selectedItem.y2;
|
||||
} else {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y];
|
||||
}
|
||||
|
||||
this.startingMinX = this.startingMinX !== undefined ? Math.min(this.startingMinX, selectedItem.x) : selectedItem.x;
|
||||
this.startingMinY = this.startingMinY !== undefined ? Math.min(this.startingMinY, selectedItem.y) : selectedItem.y;
|
||||
});
|
||||
}
|
||||
|
||||
this.layoutItems.forEach(item => {
|
||||
if (this.initialPositions[item.id]) {
|
||||
this.updateItemPosition(item, gridDelta);
|
||||
}
|
||||
});
|
||||
},
|
||||
updateItemPosition(item, gridDelta) {
|
||||
let startingPosition = this.initialPositions[item.id];
|
||||
let [startingX, startingY, startingX2, startingY2] = startingPosition;
|
||||
|
||||
if (this.startingMinX + gridDelta[0] >= 0) {
|
||||
if (item.x2 !== undefined) {
|
||||
if (this.startingMinX2 + gridDelta[0] >= 0) {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
} else {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.startingMinY + gridDelta[1] >= 0) {
|
||||
if (item.y2 !== undefined) {
|
||||
if (this.startingMinY2 + gridDelta[1] >= 0) {
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
} else {
|
||||
if (this.startingMinY + gridDelta[1] >= 0) {
|
||||
if (item.y2 !== undefined) {
|
||||
if (this.startingMinY2 + gridDelta[1] >= 0) {
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (item.x2 !== undefined && this.startingMinX2 + gridDelta[0] >= 0 && this.startingMinX + gridDelta[0] >= 0) {
|
||||
item.x2 = startingX2 + gridDelta[0];
|
||||
}
|
||||
|
||||
if (item.y2 !== undefined && this.startingMinY2 + gridDelta[1] >= 0 && this.startingMinY + gridDelta[1] >= 0) {
|
||||
item.y2 = startingY2 + gridDelta[1];
|
||||
}
|
||||
},
|
||||
endMove() {
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
this.initialPositions = undefined;
|
||||
this.startingMinX = undefined;
|
||||
this.startingMinY = undefined;
|
||||
this.startingMinX2 = undefined;
|
||||
this.startingMinY2 = undefined;
|
||||
},
|
||||
mutate(path, value) {
|
||||
this.openmct.objects.mutate(this.internalDomainObject, path, value);
|
||||
},
|
||||
handleDrop($event) {
|
||||
if (!$event.dataTransfer.types.includes('openmct/domain-object-path')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event.preventDefault();
|
||||
|
||||
let domainObject = JSON.parse($event.dataTransfer.getData('openmct/domain-object-path'))[0];
|
||||
let elementRect = this.$el.getBoundingClientRect();
|
||||
let droppedObjectPosition = [
|
||||
Math.floor(($event.pageX - elementRect.left) / this.gridSize[0]),
|
||||
Math.floor(($event.pageY - elementRect.top) / this.gridSize[1])
|
||||
];
|
||||
|
||||
if (this.isTelemetry(domainObject)) {
|
||||
this.addItem('telemetry-view', domainObject, droppedObjectPosition);
|
||||
} else {
|
||||
let identifier = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!this.objectViewMap[identifier]) {
|
||||
this.addItem('subobject-view', domainObject, droppedObjectPosition);
|
||||
} else {
|
||||
let prompt = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: "This item is already in layout and will not be added again.",
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
containsObject(identifier) {
|
||||
return _.get(this.internalDomainObject, 'composition')
|
||||
.some(childId => this.openmct.objects.areIdsEqual(childId, identifier));
|
||||
},
|
||||
handleDragOver($event) {
|
||||
// Get the ID of the dragged object
|
||||
let draggedKeyString = $event.dataTransfer.types
|
||||
.filter(type => type.startsWith(DRAG_OBJECT_TRANSFER_PREFIX))
|
||||
.map(type => type.substring(DRAG_OBJECT_TRANSFER_PREFIX.length))[0];
|
||||
|
||||
// If the layout already contains the given object, then shortcut the default dragover behavior and
|
||||
// potentially allow drop. Display layouts allow drag drop of duplicate telemetry objects.
|
||||
if (this.containsObject(draggedKeyString)){
|
||||
$event.preventDefault();
|
||||
}
|
||||
},
|
||||
isTelemetry(domainObject) {
|
||||
if (this.openmct.telemetry.isTelemetryObject(domainObject) &&
|
||||
!this.options.showAsView.includes(domainObject.type)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
addItem(itemType, ...options) {
|
||||
let item = getItemDefinition(itemType, this.openmct, this.gridSize, ...options);
|
||||
item.type = itemType;
|
||||
item.id = uuid();
|
||||
this.trackItem(item);
|
||||
this.layoutItems.push(item);
|
||||
this.openmct.objects.mutate(this.internalDomainObject, "configuration.items", this.layoutItems);
|
||||
this.initSelectIndex = this.layoutItems.length - 1;
|
||||
},
|
||||
trackItem(item) {
|
||||
if (!item.identifier) {
|
||||
return;
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
}
|
||||
|
||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
||||
if (item.x2 !== undefined && this.startingMinX2 + gridDelta[0] >= 0 && this.startingMinX + gridDelta[0] >= 0) {
|
||||
item.x2 = startingX2 + gridDelta[0];
|
||||
}
|
||||
|
||||
if (item.type === "telemetry-view") {
|
||||
let count = this.telemetryViewMap[keyString] || 0;
|
||||
this.telemetryViewMap[keyString] = ++count;
|
||||
} else if (item.type === "subobject-view") {
|
||||
this.objectViewMap[keyString] = true;
|
||||
}
|
||||
},
|
||||
removeItem(selectedItems) {
|
||||
let indices = [];
|
||||
this.initSelectIndex = -1;
|
||||
selectedItems.forEach(selectedItem => {
|
||||
indices.push(selectedItem[0].context.index);
|
||||
this.untrackItem(selectedItem[0].context.layoutItem);
|
||||
});
|
||||
_.pullAt(this.layoutItems, indices);
|
||||
this.mutate("configuration.items", this.layoutItems);
|
||||
this.$el.click();
|
||||
},
|
||||
untrackItem(item) {
|
||||
if (!item.identifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
||||
|
||||
if (item.type === 'telemetry-view') {
|
||||
let count = --this.telemetryViewMap[keyString];
|
||||
|
||||
if (count === 0) {
|
||||
delete this.telemetryViewMap[keyString];
|
||||
this.removeFromComposition(keyString);
|
||||
}
|
||||
} else if (item.type === 'subobject-view') {
|
||||
delete this.objectViewMap[keyString];
|
||||
this.removeFromComposition(keyString);
|
||||
}
|
||||
},
|
||||
removeFromComposition(keyString) {
|
||||
let composition = _.get(this.internalDomainObject, 'composition');
|
||||
composition = composition.filter(identifier => {
|
||||
return this.openmct.objects.makeKeyString(identifier) !== keyString;
|
||||
});
|
||||
this.mutate("composition", composition);
|
||||
},
|
||||
initializeItems() {
|
||||
this.telemetryViewMap = {};
|
||||
this.objectViewMap = {};
|
||||
this.layoutItems.forEach(this.trackItem);
|
||||
},
|
||||
addChild(child) {
|
||||
let identifier = this.openmct.objects.makeKeyString(child.identifier);
|
||||
if (this.isTelemetry(child)) {
|
||||
if (!this.telemetryViewMap[identifier]) {
|
||||
this.addItem('telemetry-view', child);
|
||||
}
|
||||
} else if (!this.objectViewMap[identifier]) {
|
||||
this.addItem('subobject-view', child);
|
||||
}
|
||||
},
|
||||
removeChild(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
|
||||
if (this.objectViewMap[keyString]) {
|
||||
delete this.objectViewMap[keyString];
|
||||
this.removeFromConfiguration(keyString);
|
||||
} else if (this.telemetryViewMap[keyString]) {
|
||||
delete this.telemetryViewMap[keyString];
|
||||
this.removeFromConfiguration(keyString);
|
||||
}
|
||||
},
|
||||
removeFromConfiguration(keyString) {
|
||||
let layoutItems = this.layoutItems.filter(item => {
|
||||
if (!item.identifier) {
|
||||
return true;
|
||||
} else {
|
||||
return this.openmct.objects.makeKeyString(item.identifier) !== keyString;
|
||||
}
|
||||
});
|
||||
this.mutate("configuration.items", layoutItems);
|
||||
this.$el.click();
|
||||
},
|
||||
orderItem(position, selectedItems) {
|
||||
let delta = ORDERS[position];
|
||||
let indices = [];
|
||||
let newIndex = -1;
|
||||
let items = [];
|
||||
|
||||
Object.assign(items, this.layoutItems);
|
||||
this.selectedLayoutItems.forEach(selectedItem => {
|
||||
indices.push(this.layoutItems.indexOf(selectedItem));
|
||||
});
|
||||
indices.sort((a, b) => a - b);
|
||||
|
||||
if (position === 'top' || position === 'up') {
|
||||
indices.reverse();
|
||||
}
|
||||
|
||||
if (position === 'top' || position === 'bottom') {
|
||||
this.moveToTopOrBottom(position, indices, items, delta);
|
||||
} else {
|
||||
this.moveUpOrDown(position, indices, items, delta);
|
||||
}
|
||||
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
},
|
||||
moveUpOrDown(position, indices, items, delta) {
|
||||
let previousItemIndex = -1;
|
||||
let newIndex = -1;
|
||||
|
||||
indices.forEach((itemIndex, index) => {
|
||||
let isAdjacentItemSelected = position === 'up' ?
|
||||
itemIndex + 1 === previousItemIndex :
|
||||
itemIndex - 1 === previousItemIndex;
|
||||
|
||||
if (index > 0 && isAdjacentItemSelected) {
|
||||
if (position === 'up') {
|
||||
newIndex -= 1;
|
||||
} else {
|
||||
newIndex += 1;
|
||||
}
|
||||
} else {
|
||||
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
|
||||
}
|
||||
|
||||
previousItemIndex = itemIndex;
|
||||
this.updateItemOrder(newIndex, itemIndex, items);
|
||||
});
|
||||
},
|
||||
moveToTopOrBottom(position, indices, items, delta) {
|
||||
let newIndex = -1;
|
||||
|
||||
indices.forEach((itemIndex, index) => {
|
||||
if (index === 0) {
|
||||
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
|
||||
} else {
|
||||
if (position === 'top') {
|
||||
newIndex -= 1;
|
||||
} else {
|
||||
newIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateItemOrder(newIndex, itemIndex, items);
|
||||
});
|
||||
},
|
||||
updateItemOrder(newIndex, itemIndex, items) {
|
||||
if (newIndex !== itemIndex) {
|
||||
this.layoutItems.splice(itemIndex, 1);
|
||||
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
|
||||
}
|
||||
},
|
||||
updateTelemetryFormat(item, format) {
|
||||
let index = _.findIndex(this.layoutItems, item);
|
||||
item.format = format;
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
if (item.y2 !== undefined && this.startingMinY2 + gridDelta[1] >= 0 && this.startingMinY + gridDelta[1] >= 0) {
|
||||
item.y2 = startingY2 + gridDelta[1];
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', function (obj) {
|
||||
this.internalDomainObject = JSON.parse(JSON.stringify(obj));
|
||||
}.bind(this));
|
||||
this.openmct.selection.on('change', this.setSelection);
|
||||
this.initializeItems();
|
||||
this.composition = this.openmct.composition.get(this.internalDomainObject);
|
||||
this.composition.on('add', this.addChild);
|
||||
this.composition.on('remove', this.removeChild);
|
||||
this.composition.load();
|
||||
endMove() {
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
this.initialPositions = undefined;
|
||||
this.startingMinX = undefined;
|
||||
this.startingMinY = undefined;
|
||||
this.startingMinX2 = undefined;
|
||||
this.startingMinY2 = undefined;
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.selection.off('change', this.setSelection);
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
this.unlisten();
|
||||
mutate(path, value) {
|
||||
this.openmct.objects.mutate(this.internalDomainObject, path, value);
|
||||
},
|
||||
handleDrop($event) {
|
||||
if (!$event.dataTransfer.types.includes('openmct/domain-object-path')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event.preventDefault();
|
||||
|
||||
let domainObject = JSON.parse($event.dataTransfer.getData('openmct/domain-object-path'))[0];
|
||||
let elementRect = this.$el.getBoundingClientRect();
|
||||
let droppedObjectPosition = [
|
||||
Math.floor(($event.pageX - elementRect.left) / this.gridSize[0]),
|
||||
Math.floor(($event.pageY - elementRect.top) / this.gridSize[1])
|
||||
];
|
||||
|
||||
if (this.isTelemetry(domainObject)) {
|
||||
this.addItem('telemetry-view', domainObject, droppedObjectPosition);
|
||||
} else {
|
||||
let identifier = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!this.objectViewMap[identifier]) {
|
||||
this.addItem('subobject-view', domainObject, droppedObjectPosition);
|
||||
} else {
|
||||
let prompt = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: "This item is already in layout and will not be added again.",
|
||||
buttons: [
|
||||
{
|
||||
label: 'OK',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
containsObject(identifier) {
|
||||
return _.get(this.internalDomainObject, 'composition')
|
||||
.some(childId => this.openmct.objects.areIdsEqual(childId, identifier));
|
||||
},
|
||||
handleDragOver($event) {
|
||||
// Get the ID of the dragged object
|
||||
let draggedKeyString = $event.dataTransfer.types
|
||||
.filter(type => type.startsWith(DRAG_OBJECT_TRANSFER_PREFIX))
|
||||
.map(type => type.substring(DRAG_OBJECT_TRANSFER_PREFIX.length))[0];
|
||||
|
||||
// If the layout already contains the given object, then shortcut the default dragover behavior and
|
||||
// potentially allow drop. Display layouts allow drag drop of duplicate telemetry objects.
|
||||
if (this.containsObject(draggedKeyString)) {
|
||||
$event.preventDefault();
|
||||
}
|
||||
},
|
||||
isTelemetry(domainObject) {
|
||||
if (this.openmct.telemetry.isTelemetryObject(domainObject) &&
|
||||
!this.options.showAsView.includes(domainObject.type)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
addItem(itemType, ...options) {
|
||||
let item = getItemDefinition(itemType, this.openmct, this.gridSize, ...options);
|
||||
item.type = itemType;
|
||||
item.id = uuid();
|
||||
this.trackItem(item);
|
||||
this.layoutItems.push(item);
|
||||
this.openmct.objects.mutate(this.internalDomainObject, "configuration.items", this.layoutItems);
|
||||
this.initSelectIndex = this.layoutItems.length - 1;
|
||||
},
|
||||
trackItem(item) {
|
||||
if (!item.identifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
||||
|
||||
if (item.type === "telemetry-view") {
|
||||
let count = this.telemetryViewMap[keyString] || 0;
|
||||
this.telemetryViewMap[keyString] = ++count;
|
||||
} else if (item.type === "subobject-view") {
|
||||
this.objectViewMap[keyString] = true;
|
||||
}
|
||||
},
|
||||
removeItem(selectedItems) {
|
||||
let indices = [];
|
||||
this.initSelectIndex = -1;
|
||||
selectedItems.forEach(selectedItem => {
|
||||
indices.push(selectedItem[0].context.index);
|
||||
this.untrackItem(selectedItem[0].context.layoutItem);
|
||||
});
|
||||
_.pullAt(this.layoutItems, indices);
|
||||
this.mutate("configuration.items", this.layoutItems);
|
||||
this.$el.click();
|
||||
},
|
||||
untrackItem(item) {
|
||||
if (!item.identifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
||||
|
||||
if (item.type === 'telemetry-view') {
|
||||
let count = --this.telemetryViewMap[keyString];
|
||||
|
||||
if (count === 0) {
|
||||
delete this.telemetryViewMap[keyString];
|
||||
this.removeFromComposition(keyString);
|
||||
}
|
||||
} else if (item.type === 'subobject-view') {
|
||||
delete this.objectViewMap[keyString];
|
||||
this.removeFromComposition(keyString);
|
||||
}
|
||||
},
|
||||
removeFromComposition(keyString) {
|
||||
let composition = _.get(this.internalDomainObject, 'composition');
|
||||
composition = composition.filter(identifier => {
|
||||
return this.openmct.objects.makeKeyString(identifier) !== keyString;
|
||||
});
|
||||
this.mutate("composition", composition);
|
||||
},
|
||||
initializeItems() {
|
||||
this.telemetryViewMap = {};
|
||||
this.objectViewMap = {};
|
||||
this.layoutItems.forEach(this.trackItem);
|
||||
},
|
||||
addChild(child) {
|
||||
let identifier = this.openmct.objects.makeKeyString(child.identifier);
|
||||
if (this.isTelemetry(child)) {
|
||||
if (!this.telemetryViewMap[identifier]) {
|
||||
this.addItem('telemetry-view', child);
|
||||
}
|
||||
} else if (!this.objectViewMap[identifier]) {
|
||||
this.addItem('subobject-view', child);
|
||||
}
|
||||
},
|
||||
removeChild(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
|
||||
if (this.objectViewMap[keyString]) {
|
||||
delete this.objectViewMap[keyString];
|
||||
this.removeFromConfiguration(keyString);
|
||||
} else if (this.telemetryViewMap[keyString]) {
|
||||
delete this.telemetryViewMap[keyString];
|
||||
this.removeFromConfiguration(keyString);
|
||||
}
|
||||
},
|
||||
removeFromConfiguration(keyString) {
|
||||
let layoutItems = this.layoutItems.filter(item => {
|
||||
if (!item.identifier) {
|
||||
return true;
|
||||
} else {
|
||||
return this.openmct.objects.makeKeyString(item.identifier) !== keyString;
|
||||
}
|
||||
});
|
||||
this.mutate("configuration.items", layoutItems);
|
||||
this.$el.click();
|
||||
},
|
||||
orderItem(position, selectedItems) {
|
||||
let delta = ORDERS[position];
|
||||
let indices = [];
|
||||
let items = [];
|
||||
|
||||
Object.assign(items, this.layoutItems);
|
||||
this.selectedLayoutItems.forEach(selectedItem => {
|
||||
indices.push(this.layoutItems.indexOf(selectedItem));
|
||||
});
|
||||
indices.sort((a, b) => a - b);
|
||||
|
||||
if (position === 'top' || position === 'up') {
|
||||
indices.reverse();
|
||||
}
|
||||
|
||||
if (position === 'top' || position === 'bottom') {
|
||||
this.moveToTopOrBottom(position, indices, items, delta);
|
||||
} else {
|
||||
this.moveUpOrDown(position, indices, items, delta);
|
||||
}
|
||||
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
},
|
||||
moveUpOrDown(position, indices, items, delta) {
|
||||
let previousItemIndex = -1;
|
||||
let newIndex = -1;
|
||||
|
||||
indices.forEach((itemIndex, index) => {
|
||||
let isAdjacentItemSelected = position === 'up' ?
|
||||
itemIndex + 1 === previousItemIndex :
|
||||
itemIndex - 1 === previousItemIndex;
|
||||
|
||||
if (index > 0 && isAdjacentItemSelected) {
|
||||
if (position === 'up') {
|
||||
newIndex -= 1;
|
||||
} else {
|
||||
newIndex += 1;
|
||||
}
|
||||
} else {
|
||||
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
|
||||
}
|
||||
|
||||
previousItemIndex = itemIndex;
|
||||
this.updateItemOrder(newIndex, itemIndex, items);
|
||||
});
|
||||
},
|
||||
moveToTopOrBottom(position, indices, items, delta) {
|
||||
let newIndex = -1;
|
||||
|
||||
indices.forEach((itemIndex, index) => {
|
||||
if (index === 0) {
|
||||
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
|
||||
} else {
|
||||
if (position === 'top') {
|
||||
newIndex -= 1;
|
||||
} else {
|
||||
newIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateItemOrder(newIndex, itemIndex, items);
|
||||
});
|
||||
},
|
||||
updateItemOrder(newIndex, itemIndex, items) {
|
||||
if (newIndex !== itemIndex) {
|
||||
this.layoutItems.splice(itemIndex, 1);
|
||||
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
|
||||
}
|
||||
},
|
||||
updateTelemetryFormat(item, format) {
|
||||
let index = _.findIndex(this.layoutItems, item);
|
||||
item.format = format;
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,17 +21,28 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<!-- Resize handles -->
|
||||
<div class="c-frame-edit" :style="style">
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--nw"
|
||||
@mousedown="startResize([1,1], [-1,-1], $event)"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--ne"
|
||||
@mousedown="startResize([0,1], [1,-1], $event)"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--sw"
|
||||
@mousedown="startResize([1,0], [-1,1], $event)"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--se"
|
||||
@mousedown="startResize([0,0], [1,1], $event)"></div>
|
||||
</div>
|
||||
<!-- Resize handles -->
|
||||
<div
|
||||
class="c-frame-edit"
|
||||
:style="marqueeStyle"
|
||||
>
|
||||
<div
|
||||
class="c-frame-edit__handle c-frame-edit__handle--nw"
|
||||
@mousedown="startResize([1,1], [-1,-1], $event)"
|
||||
></div>
|
||||
<div
|
||||
class="c-frame-edit__handle c-frame-edit__handle--ne"
|
||||
@mousedown="startResize([0,1], [1,-1], $event)"
|
||||
></div>
|
||||
<div
|
||||
class="c-frame-edit__handle c-frame-edit__handle--sw"
|
||||
@mousedown="startResize([1,0], [-1,1], $event)"
|
||||
></div>
|
||||
<div
|
||||
class="c-frame-edit__handle c-frame-edit__handle--se"
|
||||
@mousedown="startResize([0,0], [1,1], $event)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -95,139 +106,146 @@
|
||||
|
||||
|
||||
<script>
|
||||
import LayoutDrag from './../LayoutDrag'
|
||||
import LayoutDrag from './../LayoutDrag'
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
selectedLayoutItems: Array,
|
||||
gridSize: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined
|
||||
}
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
selectedLayoutItems: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
let x = Number.POSITIVE_INFINITY;
|
||||
let y = Number.POSITIVE_INFINITY;
|
||||
let width = Number.NEGATIVE_INFINITY;
|
||||
let height = Number.NEGATIVE_INFINITY;
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
marqueePosition() {
|
||||
let x = Number.POSITIVE_INFINITY;
|
||||
let y = Number.POSITIVE_INFINITY;
|
||||
let width = Number.NEGATIVE_INFINITY;
|
||||
let height = Number.NEGATIVE_INFINITY;
|
||||
|
||||
this.selectedLayoutItems.forEach(item => {
|
||||
if (item.x2 !== undefined) {
|
||||
let lineWidth = Math.abs(item.x - item.x2);
|
||||
let lineMinX = Math.min(item.x, item.x2);
|
||||
x = Math.min(lineMinX, x);
|
||||
width = Math.max(lineWidth + lineMinX, width);
|
||||
} else {
|
||||
x = Math.min(item.x, x);
|
||||
width = Math.max(item.width + item.x, width);
|
||||
}
|
||||
|
||||
if (item.y2 !== undefined) {
|
||||
let lineHeight = Math.abs(item.y - item.y2);
|
||||
let lineMinY = Math.min(item.y, item.y2);
|
||||
y = Math.min(lineMinY, y);
|
||||
height = Math.max(lineHeight + lineMinY, height);
|
||||
} else {
|
||||
y = Math.min(item.y, y);
|
||||
height = Math.max(item.height + item.y, height);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.dragPosition) {
|
||||
[x, y] = this.dragPosition.position;
|
||||
[width, height] = this.dragPosition.dimensions;
|
||||
this.selectedLayoutItems.forEach(item => {
|
||||
if (item.x2 !== undefined) {
|
||||
let lineWidth = Math.abs(item.x - item.x2);
|
||||
let lineMinX = Math.min(item.x, item.x2);
|
||||
x = Math.min(lineMinX, x);
|
||||
width = Math.max(lineWidth + lineMinX, width);
|
||||
} else {
|
||||
width = width - x;
|
||||
height = height - y;
|
||||
x = Math.min(item.x, x);
|
||||
width = Math.max(item.width + item.x, width);
|
||||
}
|
||||
|
||||
this.marqueePosition = {
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
if (item.y2 !== undefined) {
|
||||
let lineHeight = Math.abs(item.y - item.y2);
|
||||
let lineMinY = Math.min(item.y, item.y2);
|
||||
y = Math.min(lineMinY, y);
|
||||
height = Math.max(lineHeight + lineMinY, height);
|
||||
} else {
|
||||
y = Math.min(item.y, y);
|
||||
height = Math.max(item.height + item.y, height);
|
||||
}
|
||||
return this.getMarqueeStyle(x, y, width, height);
|
||||
});
|
||||
|
||||
if (this.dragPosition) {
|
||||
[x, y] = this.dragPosition.position;
|
||||
[width, height] = this.dragPosition.dimensions;
|
||||
} else {
|
||||
width = width - x;
|
||||
height = height - y;
|
||||
}
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMarqueeStyle(x, y, width, height) {
|
||||
return {
|
||||
left: (this.gridSize[0] * x) + 'px',
|
||||
top: (this.gridSize[1] * y) + 'px',
|
||||
width: (this.gridSize[0] * width) + 'px',
|
||||
height: (this.gridSize[1] * height) + 'px'
|
||||
};
|
||||
},
|
||||
updatePosition(event) {
|
||||
let currentPosition = [event.pageX, event.pageY];
|
||||
this.initialPosition = this.initialPosition || currentPosition;
|
||||
this.delta = currentPosition.map(function (value, index) {
|
||||
return value - this.initialPosition[index];
|
||||
}.bind(this));
|
||||
},
|
||||
startResize(posFactor, dimFactor, event) {
|
||||
document.body.addEventListener('mousemove', this.continueResize);
|
||||
document.body.addEventListener('mouseup', this.endResize);
|
||||
this.marqueeStartPosition = {
|
||||
position: [this.marqueePosition.x, this.marqueePosition.y],
|
||||
dimensions: [this.marqueePosition.width, this.marqueePosition.height]
|
||||
};
|
||||
this.updatePosition(event);
|
||||
this.activeDrag = new LayoutDrag(this.marqueeStartPosition, posFactor, dimFactor, this.gridSize);
|
||||
event.preventDefault();
|
||||
},
|
||||
continueResize(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
this.dragPosition = this.activeDrag.getAdjustedPositionAndDimensions(this.delta);
|
||||
},
|
||||
endResize(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueResize);
|
||||
document.body.removeEventListener('mouseup', this.endResize);
|
||||
this.continueResize(event);
|
||||
marqueeStyle() {
|
||||
return {
|
||||
left: (this.gridSize[0] * this.marqueePosition.x) + 'px',
|
||||
top: (this.gridSize[1] * this.marqueePosition.y) + 'px',
|
||||
width: (this.gridSize[0] * this.marqueePosition.width) + 'px',
|
||||
height: (this.gridSize[1] * this.marqueePosition.height) + 'px'
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updatePosition(event) {
|
||||
let currentPosition = [event.pageX, event.pageY];
|
||||
this.initialPosition = this.initialPosition || currentPosition;
|
||||
this.delta = currentPosition.map(function (value, index) {
|
||||
return value - this.initialPosition[index];
|
||||
}.bind(this));
|
||||
},
|
||||
startResize(posFactor, dimFactor, event) {
|
||||
document.body.addEventListener('mousemove', this.continueResize);
|
||||
document.body.addEventListener('mouseup', this.endResize);
|
||||
this.marqueeStartPosition = {
|
||||
position: [this.marqueePosition.x, this.marqueePosition.y],
|
||||
dimensions: [this.marqueePosition.width, this.marqueePosition.height]
|
||||
};
|
||||
this.updatePosition(event);
|
||||
this.activeDrag = new LayoutDrag(this.marqueeStartPosition, posFactor, dimFactor, this.gridSize);
|
||||
event.preventDefault();
|
||||
},
|
||||
continueResize(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
this.dragPosition = this.activeDrag.getAdjustedPositionAndDimensions(this.delta);
|
||||
},
|
||||
endResize(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueResize);
|
||||
document.body.removeEventListener('mouseup', this.endResize);
|
||||
this.continueResize(event);
|
||||
|
||||
let marqueeStartWidth = this.marqueeStartPosition.dimensions[0];
|
||||
let marqueeStartHeight = this.marqueeStartPosition.dimensions[1];
|
||||
let marqueeStartX = this.marqueeStartPosition.position[0];
|
||||
let marqueeStartY = this.marqueeStartPosition.position[1];
|
||||
let marqueeStartWidth = this.marqueeStartPosition.dimensions[0];
|
||||
let marqueeStartHeight = this.marqueeStartPosition.dimensions[1];
|
||||
let marqueeStartX = this.marqueeStartPosition.position[0];
|
||||
let marqueeStartY = this.marqueeStartPosition.position[1];
|
||||
|
||||
let marqueeEndX = this.dragPosition.position[0];
|
||||
let marqueeEndY = this.dragPosition.position[1];
|
||||
let marqueeEndWidth = this.dragPosition.dimensions[0];
|
||||
let marqueeEndHeight = this.dragPosition.dimensions[1];
|
||||
let marqueeEndX = this.dragPosition.position[0];
|
||||
let marqueeEndY = this.dragPosition.position[1];
|
||||
let marqueeEndWidth = this.dragPosition.dimensions[0];
|
||||
let marqueeEndHeight = this.dragPosition.dimensions[1];
|
||||
|
||||
let scaleWidth = marqueeEndWidth / marqueeStartWidth;
|
||||
let scaleHeight = marqueeEndHeight / marqueeStartHeight;
|
||||
let scaleWidth = marqueeEndWidth / marqueeStartWidth;
|
||||
let scaleHeight = marqueeEndHeight / marqueeStartHeight;
|
||||
|
||||
let marqueeStart = {
|
||||
x: marqueeStartX,
|
||||
y: marqueeStartY,
|
||||
height: marqueeStartWidth,
|
||||
width: marqueeStartHeight
|
||||
};
|
||||
let marqueeEnd = {
|
||||
x: marqueeEndX,
|
||||
y: marqueeEndY,
|
||||
width: marqueeEndWidth,
|
||||
height: marqueeEndHeight
|
||||
};
|
||||
let marqueeOffset = {
|
||||
x: marqueeEnd.x - marqueeStart.x,
|
||||
y: marqueeEnd.y - marqueeStart.y
|
||||
};
|
||||
let marqueeStart = {
|
||||
x: marqueeStartX,
|
||||
y: marqueeStartY,
|
||||
height: marqueeStartWidth,
|
||||
width: marqueeStartHeight
|
||||
};
|
||||
let marqueeEnd = {
|
||||
x: marqueeEndX,
|
||||
y: marqueeEndY,
|
||||
width: marqueeEndWidth,
|
||||
height: marqueeEndHeight
|
||||
};
|
||||
let marqueeOffset = {
|
||||
x: marqueeEnd.x - marqueeStart.x,
|
||||
y: marqueeEnd.y - marqueeStart.y
|
||||
};
|
||||
|
||||
this.$emit('endResize', scaleWidth, scaleHeight, marqueeStart, marqueeOffset);
|
||||
this.dragPosition = undefined;
|
||||
this.initialPosition = undefined;
|
||||
this.marqueeStartPosition = undefined;
|
||||
this.delta = undefined;
|
||||
event.preventDefault();
|
||||
}
|
||||
this.$emit('endResize', scaleWidth, scaleHeight, marqueeStart, marqueeOffset);
|
||||
this.dragPosition = undefined;
|
||||
this.initialPosition = undefined;
|
||||
this.marqueeStartPosition = undefined;
|
||||
this.delta = undefined;
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,15 +21,18 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
<div class="c-image-view"
|
||||
:style="style">
|
||||
</div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
<layout-frame
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<div
|
||||
class="c-image-view"
|
||||
:style="style"
|
||||
></div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
@ -46,59 +49,70 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, element) {
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, element) {
|
||||
return {
|
||||
stroke: 'transparent',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
url: element.url
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
initSelect: Boolean
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
stroke: 'transparent',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
url: element.url
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
index: Number,
|
||||
initSelect: Boolean
|
||||
},
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
backgroundImage: 'url(' + this.item.url + ')',
|
||||
border: '1px solid ' + this.item.stroke
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
backgroundImage: 'url(' + this.item.url + ')',
|
||||
border: '1px solid ' + this.item.stroke
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
@ -21,19 +21,21 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="l-layout__frame c-frame"
|
||||
:class="{
|
||||
'no-frame': !item.hasFrame,
|
||||
'u-inspectable': inspectable
|
||||
}"
|
||||
:style="style">
|
||||
<div
|
||||
class="l-layout__frame c-frame"
|
||||
:class="{
|
||||
'no-frame': !item.hasFrame,
|
||||
'u-inspectable': inspectable
|
||||
}"
|
||||
:style="style"
|
||||
>
|
||||
<slot></slot>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<div class="c-frame-edit__move"
|
||||
@mousedown="startMove([1,1], [0,0], $event)">
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c-frame-edit__move"
|
||||
@mousedown="startMove([1,1], [0,0], $event)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -173,73 +175,81 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import LayoutDrag from './../LayoutDrag'
|
||||
import LayoutDrag from './../LayoutDrag'
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
let {x, y, width, height} = this.item;
|
||||
return {
|
||||
left: (this.gridSize[0] * x) + 'px',
|
||||
top: (this.gridSize[1] * y) + 'px',
|
||||
width: (this.gridSize[0] * width) + 'px',
|
||||
height: (this.gridSize[1] * height) + 'px',
|
||||
minWidth: (this.gridSize[0] * width) + 'px',
|
||||
minHeight: (this.gridSize[1] * height) + 'px'
|
||||
};
|
||||
},
|
||||
inspectable() {
|
||||
return this.item.type === 'subobject-view' || this.item.type === 'telemetry-view';
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
let {x, y, width, height} = this.item;
|
||||
return {
|
||||
left: (this.gridSize[0] * x) + 'px',
|
||||
top: (this.gridSize[1] * y) + 'px',
|
||||
width: (this.gridSize[0] * width) + 'px',
|
||||
height: (this.gridSize[1] * height) + 'px',
|
||||
minWidth: (this.gridSize[0] * width) + 'px',
|
||||
minHeight: (this.gridSize[1] * height) + 'px'
|
||||
};
|
||||
},
|
||||
inspectable() {
|
||||
return this.item.type === 'subobject-view' || this.item.type === 'telemetry-view';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updatePosition(event) {
|
||||
let currentPosition = [event.pageX, event.pageY];
|
||||
this.initialPosition = this.initialPosition || currentPosition;
|
||||
this.delta = currentPosition.map(function (value, index) {
|
||||
return value - this.initialPosition[index];
|
||||
}.bind(this));
|
||||
},
|
||||
startMove(posFactor, dimFactor, event) {
|
||||
document.body.addEventListener('mousemove', this.continueMove);
|
||||
document.body.addEventListener('mouseup', this.endMove);
|
||||
this.dragPosition = {
|
||||
position: [this.item.x, this.item.y]
|
||||
};
|
||||
this.updatePosition(event);
|
||||
this.activeDrag = new LayoutDrag(this.dragPosition, posFactor, dimFactor, this.gridSize);
|
||||
event.preventDefault();
|
||||
},
|
||||
continueMove(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
let newPosition = this.activeDrag.getAdjustedPosition(this.delta);
|
||||
|
||||
if (!_.isEqual(newPosition, this.dragPosition)) {
|
||||
this.dragPosition = newPosition;
|
||||
this.$emit('move', this.toGridDelta(this.delta));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updatePosition(event) {
|
||||
let currentPosition = [event.pageX, event.pageY];
|
||||
this.initialPosition = this.initialPosition || currentPosition;
|
||||
this.delta = currentPosition.map(function (value, index) {
|
||||
return value - this.initialPosition[index];
|
||||
}.bind(this));
|
||||
},
|
||||
startMove(posFactor, dimFactor, event) {
|
||||
document.body.addEventListener('mousemove', this.continueMove);
|
||||
document.body.addEventListener('mouseup', this.endMove);
|
||||
this.dragPosition = {
|
||||
position: [this.item.x, this.item.y]
|
||||
};
|
||||
this.updatePosition(event);
|
||||
this.activeDrag = new LayoutDrag(this.dragPosition, posFactor, dimFactor, this.gridSize);
|
||||
event.preventDefault();
|
||||
},
|
||||
continueMove(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
let newPosition = this.activeDrag.getAdjustedPosition(this.delta);
|
||||
|
||||
if (!_.isEqual(newPosition, this.dragPosition)) {
|
||||
this.dragPosition = newPosition;
|
||||
this.$emit('move', this.toGridDelta(this.delta));
|
||||
}
|
||||
},
|
||||
endMove(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueMove);
|
||||
document.body.removeEventListener('mouseup', this.endMove);
|
||||
this.continueMove(event);
|
||||
this.$emit('endMove');
|
||||
this.dragPosition = undefined;
|
||||
this.initialPosition = undefined;
|
||||
this.delta = undefined;
|
||||
event.preventDefault();
|
||||
},
|
||||
toGridDelta(pixelDelta) {
|
||||
return pixelDelta.map((v, i) => {
|
||||
return Math.round(v / this.gridSize[i]);
|
||||
});
|
||||
}
|
||||
endMove(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueMove);
|
||||
document.body.removeEventListener('mouseup', this.endMove);
|
||||
this.continueMove(event);
|
||||
this.$emit('endMove');
|
||||
this.dragPosition = undefined;
|
||||
this.initialPosition = undefined;
|
||||
this.delta = undefined;
|
||||
event.preventDefault();
|
||||
},
|
||||
toGridDelta(pixelDelta) {
|
||||
return pixelDelta.map((v, i) => {
|
||||
return Math.round(v / this.gridSize[i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,244 +21,253 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="l-layout__frame c-frame no-frame"
|
||||
:style="style">
|
||||
<svg width="100%" height="100%">
|
||||
<line v-bind="linePosition"
|
||||
:stroke="item.stroke"
|
||||
stroke-width="2">
|
||||
</line>
|
||||
</svg>
|
||||
<div
|
||||
class="l-layout__frame c-frame no-frame"
|
||||
:style="style"
|
||||
>
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<line
|
||||
v-bind="linePosition"
|
||||
:stroke="item.stroke"
|
||||
stroke-width="2"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<div class="c-frame-edit__move"
|
||||
@mousedown="startDrag($event)"></div>
|
||||
<div class="c-frame-edit" v-if="showFrameEdit">
|
||||
<div class="c-frame-edit__handle"
|
||||
:class="startHandleClass"
|
||||
@mousedown="startDrag($event, 'start')"></div>
|
||||
<div class="c-frame-edit__handle"
|
||||
:class="endHandleClass"
|
||||
@mousedown="startDrag($event, 'end')"></div>
|
||||
</div>
|
||||
<div
|
||||
class="c-frame-edit__move"
|
||||
@mousedown="startDrag($event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="showFrameEdit"
|
||||
class="c-frame-edit"
|
||||
>
|
||||
<div
|
||||
class="c-frame-edit__handle"
|
||||
:class="startHandleClass"
|
||||
@mousedown="startDrag($event, 'start')"
|
||||
></div>
|
||||
<div
|
||||
class="c-frame-edit__handle"
|
||||
:class="endHandleClass"
|
||||
@mousedown="startDrag($event, 'end')"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
|
||||
const START_HANDLE_QUADRANTS = {
|
||||
1: 'c-frame-edit__handle--sw',
|
||||
2: 'c-frame-edit__handle--se',
|
||||
3: 'c-frame-edit__handle--ne',
|
||||
4: 'c-frame-edit__handle--nw'
|
||||
};
|
||||
const START_HANDLE_QUADRANTS = {
|
||||
1: 'c-frame-edit__handle--sw',
|
||||
2: 'c-frame-edit__handle--se',
|
||||
3: 'c-frame-edit__handle--ne',
|
||||
4: 'c-frame-edit__handle--nw'
|
||||
};
|
||||
|
||||
const END_HANDLE_QUADRANTS = {
|
||||
1: 'c-frame-edit__handle--ne',
|
||||
2: 'c-frame-edit__handle--nw',
|
||||
3: 'c-frame-edit__handle--sw',
|
||||
4: 'c-frame-edit__handle--se'
|
||||
};
|
||||
const END_HANDLE_QUADRANTS = {
|
||||
1: 'c-frame-edit__handle--ne',
|
||||
2: 'c-frame-edit__handle--nw',
|
||||
3: 'c-frame-edit__handle--sw',
|
||||
4: 'c-frame-edit__handle--se'
|
||||
};
|
||||
|
||||
export default {
|
||||
makeDefinition() {
|
||||
export default {
|
||||
makeDefinition() {
|
||||
return {
|
||||
x: 5,
|
||||
y: 10,
|
||||
x2: 10,
|
||||
y2: 5,
|
||||
stroke: '#717171'
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
},
|
||||
initSelect: Boolean,
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
multiSelect: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined,
|
||||
dragging: undefined,
|
||||
selection: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showFrameEdit() {
|
||||
let layoutItem = this.selection.length > 0 && this.selection[0][0].context.layoutItem;
|
||||
return !this.multiSelect && layoutItem && layoutItem.id === this.item.id;
|
||||
},
|
||||
position() {
|
||||
let {x, y, x2, y2} = this.item;
|
||||
if (this.dragging && this.dragPosition) {
|
||||
({x, y, x2, y2} = this.dragPosition);
|
||||
}
|
||||
return {x, y, x2, y2};
|
||||
},
|
||||
style() {
|
||||
let {x, y, x2, y2} = this.position;
|
||||
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);
|
||||
let height = Math.max(this.gridSize[1] * Math.abs(y - y2), 1);
|
||||
let left = this.gridSize[0] * Math.min(x, x2);
|
||||
let top = this.gridSize[1] * Math.min(y, y2);
|
||||
return {
|
||||
x: 5,
|
||||
y: 10,
|
||||
x2: 10,
|
||||
y2: 5,
|
||||
stroke: '#717171'
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
width: `${width}px`,
|
||||
height: `${height}px`
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
initSelect: Boolean,
|
||||
index: Number,
|
||||
multiSelect: Boolean
|
||||
startHandleClass() {
|
||||
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined,
|
||||
dragging: undefined,
|
||||
selection: []
|
||||
};
|
||||
endHandleClass() {
|
||||
return END_HANDLE_QUADRANTS[this.vectorQuadrant];
|
||||
},
|
||||
computed: {
|
||||
showFrameEdit() {
|
||||
let layoutItem = this.selection.length > 0 && this.selection[0][0].context.layoutItem;
|
||||
return !this.multiSelect && layoutItem && layoutItem.id === this.item.id;
|
||||
},
|
||||
position() {
|
||||
let {x, y, x2, y2} = this.item;
|
||||
if (this.dragging && this.dragPosition) {
|
||||
({x, y, x2, y2} = this.dragPosition);
|
||||
}
|
||||
return {x, y, x2, y2};
|
||||
},
|
||||
style() {
|
||||
let {x, y, x2, y2} = this.position;
|
||||
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);
|
||||
let height = Math.max(this.gridSize[1] * Math.abs(y - y2), 1);
|
||||
let left = this.gridSize[0] * Math.min(x, x2);
|
||||
let top = this.gridSize[1] * Math.min(y, y2);
|
||||
return {
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
};
|
||||
},
|
||||
startHandleClass() {
|
||||
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
||||
},
|
||||
endHandleClass() {
|
||||
return END_HANDLE_QUADRANTS[this.vectorQuadrant];
|
||||
},
|
||||
vectorQuadrant() {
|
||||
let {x, y, x2, y2} = this.position;
|
||||
if (x2 > x) {
|
||||
if (y2 < y) {
|
||||
return 1;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
vectorQuadrant() {
|
||||
let {x, y, x2, y2} = this.position;
|
||||
if (x2 > x) {
|
||||
if (y2 < y) {
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
},
|
||||
linePosition() {
|
||||
if (this.vectorQuadrant === 1) {
|
||||
return {
|
||||
x1: '0%',
|
||||
y1: '100%',
|
||||
x2: '100%',
|
||||
y2: '0%'
|
||||
};
|
||||
}
|
||||
if (this.vectorQuadrant === 4) {
|
||||
return {
|
||||
x1: '0%',
|
||||
y1: '0%',
|
||||
x2: '100%',
|
||||
y2: '100%'
|
||||
};
|
||||
}
|
||||
if (this.vectorQuadrant === 2) {
|
||||
return {
|
||||
x1: '0%',
|
||||
y1: '0%',
|
||||
x2: '100%',
|
||||
y2: '100%'
|
||||
};
|
||||
}
|
||||
if (this.vectorQuadrant === 3) {
|
||||
return {
|
||||
x1: '100%',
|
||||
y1: '0%',
|
||||
x2: '0%',
|
||||
y2: '100%'
|
||||
};
|
||||
return 1;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
if (y2 < y) {
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
},
|
||||
methods: {
|
||||
startDrag(event, position) {
|
||||
this.dragging = position;
|
||||
document.body.addEventListener('mousemove', this.continueDrag);
|
||||
document.body.addEventListener('mouseup', this.endDrag);
|
||||
this.startPosition = [event.pageX, event.pageY];
|
||||
this.dragPosition = {
|
||||
x: this.item.x,
|
||||
y: this.item.y,
|
||||
x2: this.item.x2,
|
||||
y2: this.item.y2
|
||||
linePosition() {
|
||||
return this.vectorQuadrant % 2 !== 0
|
||||
// odd vectorQuadrant slopes up
|
||||
? {
|
||||
x1: '0%',
|
||||
y1: '100%',
|
||||
x2: '100%',
|
||||
y2: '0%'
|
||||
}
|
||||
// even vectorQuadrant slopes down
|
||||
: {
|
||||
x1: '0%',
|
||||
y1: '0%',
|
||||
x2: '100%',
|
||||
y2: '100%'
|
||||
};
|
||||
event.preventDefault();
|
||||
},
|
||||
continueDrag(event) {
|
||||
event.preventDefault();
|
||||
let pxDeltaX = this.startPosition[0] - event.pageX;
|
||||
let pxDeltaY = this.startPosition[1] - event.pageY;
|
||||
let newPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY);
|
||||
|
||||
if (!this.dragging) {
|
||||
if (!_.isEqual(newPosition, this.dragPosition)) {
|
||||
let gridDelta = [event.pageX - this.startPosition[0], event.pageY - this.startPosition[1]];
|
||||
this.dragPosition = newPosition;
|
||||
this.$emit('move', this.toGridDelta(gridDelta));
|
||||
}
|
||||
} else {
|
||||
this.dragPosition = newPosition;
|
||||
}
|
||||
},
|
||||
endDrag(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueDrag);
|
||||
document.body.removeEventListener('mouseup', this.endDrag);
|
||||
let {x, y, x2, y2} = this.dragPosition;
|
||||
if (!this.dragging) {
|
||||
this.$emit('endMove');
|
||||
} else {
|
||||
this.$emit('endLineResize', this.item, {x, y, x2, y2});
|
||||
}
|
||||
this.dragPosition = undefined;
|
||||
this.dragging = undefined;
|
||||
event.preventDefault();
|
||||
},
|
||||
calculateDragPosition(pxDeltaX, pxDeltaY) {
|
||||
let gridDeltaX = Math.round(pxDeltaX / this.gridSize[0]);
|
||||
let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]?
|
||||
let {x, y, x2, y2} = this.item;
|
||||
let dragPosition = {x, y, x2, y2};
|
||||
|
||||
if (this.dragging === 'start') {
|
||||
dragPosition.x -= gridDeltaX;
|
||||
dragPosition.y -= gridDeltaY;
|
||||
} else if (this.dragging === 'end') {
|
||||
dragPosition.x2 -= gridDeltaX;
|
||||
dragPosition.y2 -= gridDeltaY;
|
||||
} else {
|
||||
// dragging entire line.
|
||||
dragPosition.x -= gridDeltaX;
|
||||
dragPosition.y -= gridDeltaY;
|
||||
dragPosition.x2 -= gridDeltaX;
|
||||
dragPosition.y2 -= gridDeltaY;
|
||||
}
|
||||
return dragPosition;
|
||||
},
|
||||
setSelection(selection) {
|
||||
this.selection = selection;
|
||||
},
|
||||
toGridDelta(pixelDelta) {
|
||||
return pixelDelta.map((v, i) => {
|
||||
return Math.round(v / this.gridSize[i]);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.setSelection);
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.setSelection);
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
this.openmct.selection.off('change', this.setSelection);
|
||||
},
|
||||
methods: {
|
||||
startDrag(event, position) {
|
||||
this.dragging = position;
|
||||
document.body.addEventListener('mousemove', this.continueDrag);
|
||||
document.body.addEventListener('mouseup', this.endDrag);
|
||||
this.startPosition = [event.pageX, event.pageY];
|
||||
this.dragPosition = {
|
||||
x: this.item.x,
|
||||
y: this.item.y,
|
||||
x2: this.item.x2,
|
||||
y2: this.item.y2
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
event.preventDefault();
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
continueDrag(event) {
|
||||
event.preventDefault();
|
||||
let pxDeltaX = this.startPosition[0] - event.pageX;
|
||||
let pxDeltaY = this.startPosition[1] - event.pageY;
|
||||
let newPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY);
|
||||
|
||||
if (!this.dragging) {
|
||||
if (!_.isEqual(newPosition, this.dragPosition)) {
|
||||
let gridDelta = [event.pageX - this.startPosition[0], event.pageY - this.startPosition[1]];
|
||||
this.dragPosition = newPosition;
|
||||
this.$emit('move', this.toGridDelta(gridDelta));
|
||||
}
|
||||
} else {
|
||||
this.dragPosition = newPosition;
|
||||
}
|
||||
this.openmct.selection.off('change', this.setSelection);
|
||||
},
|
||||
endDrag(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueDrag);
|
||||
document.body.removeEventListener('mouseup', this.endDrag);
|
||||
let {x, y, x2, y2} = this.dragPosition;
|
||||
if (!this.dragging) {
|
||||
this.$emit('endMove');
|
||||
} else {
|
||||
this.$emit('endLineResize', this.item, {x, y, x2, y2});
|
||||
}
|
||||
this.dragPosition = undefined;
|
||||
this.dragging = undefined;
|
||||
event.preventDefault();
|
||||
},
|
||||
calculateDragPosition(pxDeltaX, pxDeltaY) {
|
||||
let gridDeltaX = Math.round(pxDeltaX / this.gridSize[0]);
|
||||
let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]?
|
||||
let {x, y, x2, y2} = this.item;
|
||||
let dragPosition = {x, y, x2, y2};
|
||||
|
||||
if (this.dragging === 'start') {
|
||||
dragPosition.x -= gridDeltaX;
|
||||
dragPosition.y -= gridDeltaY;
|
||||
} else if (this.dragging === 'end') {
|
||||
dragPosition.x2 -= gridDeltaX;
|
||||
dragPosition.y2 -= gridDeltaY;
|
||||
} else {
|
||||
// dragging entire line.
|
||||
dragPosition.x -= gridDeltaX;
|
||||
dragPosition.y -= gridDeltaY;
|
||||
dragPosition.x2 -= gridDeltaX;
|
||||
dragPosition.y2 -= gridDeltaY;
|
||||
}
|
||||
return dragPosition;
|
||||
},
|
||||
setSelection(selection) {
|
||||
this.selection = selection;
|
||||
},
|
||||
toGridDelta(pixelDelta) {
|
||||
return pixelDelta.map((v, i) => {
|
||||
return Math.round(v / this.gridSize[i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
@ -20,106 +20,120 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
:title="domainObject && domainObject.name"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
<object-frame v-if="domainObject"
|
||||
:domain-object="domainObject"
|
||||
:object-path="currentObjectPath"
|
||||
:has-frame="item.hasFrame"
|
||||
:show-edit-view="false"
|
||||
ref="objectFrame">
|
||||
</object-frame>
|
||||
</layout-frame>
|
||||
<layout-frame
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
:title="domainObject && domainObject.name"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<object-frame
|
||||
v-if="domainObject"
|
||||
ref="objectFrame"
|
||||
:domain-object="domainObject"
|
||||
:object-path="currentObjectPath"
|
||||
:has-frame="item.hasFrame"
|
||||
:show-edit-view="false"
|
||||
/>
|
||||
</layout-frame>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ObjectFrame from '../../../ui/components/ObjectFrame.vue'
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import ObjectFrame from '../../../ui/components/ObjectFrame.vue'
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
|
||||
const MINIMUM_FRAME_SIZE = [320, 180],
|
||||
DEFAULT_DIMENSIONS = [10, 10],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget'];
|
||||
const MINIMUM_FRAME_SIZE = [320, 180],
|
||||
DEFAULT_DIMENSIONS = [10, 10],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget'];
|
||||
|
||||
function getDefaultDimensions(gridSize) {
|
||||
return MINIMUM_FRAME_SIZE.map((min, index) => {
|
||||
return Math.max(
|
||||
Math.ceil(min / gridSize[index]),
|
||||
DEFAULT_DIMENSIONS[index]
|
||||
);
|
||||
});
|
||||
}
|
||||
function getDefaultDimensions(gridSize) {
|
||||
return MINIMUM_FRAME_SIZE.map((min, index) => {
|
||||
return Math.max(
|
||||
Math.ceil(min / gridSize[index]),
|
||||
DEFAULT_DIMENSIONS[index]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function hasFrameByDefault(type) {
|
||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||
}
|
||||
function hasFrameByDefault(type) {
|
||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||
}
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
let defaultDimensions = getDefaultDimensions(gridSize);
|
||||
position = position || DEFAULT_POSITION;
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
let defaultDimensions = getDefaultDimensions(gridSize);
|
||||
position = position || DEFAULT_POSITION;
|
||||
|
||||
return {
|
||||
width: defaultDimensions[0],
|
||||
height: defaultDimensions[1],
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
identifier: domainObject.identifier,
|
||||
hasFrame: hasFrameByDefault(domainObject.type)
|
||||
};
|
||||
return {
|
||||
width: defaultDimensions[0],
|
||||
height: defaultDimensions[1],
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
identifier: domainObject.identifier,
|
||||
hasFrame: hasFrameByDefault(domainObject.type)
|
||||
};
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
components: {
|
||||
ObjectFrame,
|
||||
LayoutFrame
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
initSelect: Boolean,
|
||||
index: Number
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject: undefined,
|
||||
currentObjectPath: []
|
||||
initSelect: Boolean,
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject: undefined,
|
||||
currentObjectPath: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ObjectFrame,
|
||||
LayoutFrame
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.currentObjectPath = [this.domainObject].concat(this.objectPath.slice());
|
||||
this.$nextTick(function () {
|
||||
let childContext = this.$refs.objectFrame.getSelectionContext();
|
||||
childContext.item = domainObject;
|
||||
childContext.layoutItem = this.item;
|
||||
childContext.index = this.index;
|
||||
this.context = childContext;
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.get(this.item.identifier)
|
||||
.then(this.setObject);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.get(this.item.identifier)
|
||||
.then(this.setObject);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.currentObjectPath = [this.domainObject].concat(this.objectPath.slice());
|
||||
this.$nextTick(function () {
|
||||
let childContext = this.$refs.objectFrame.getSelectionContext();
|
||||
childContext.item = domainObject;
|
||||
childContext.layoutItem = this.item;
|
||||
childContext.index = this.index;
|
||||
this.context = childContext;
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,29 +20,41 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
<div class="c-telemetry-view"
|
||||
:style="styleObject"
|
||||
v-if="domainObject"
|
||||
@contextmenu.prevent="showContextMenu">
|
||||
<div v-if="showLabel"
|
||||
class="c-telemetry-view__label">
|
||||
<div class="c-telemetry-view__label-text">{{ domainObject.name }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showValue"
|
||||
:title="fieldName"
|
||||
class="c-telemetry-view__value"
|
||||
:class="[telemetryClass]">
|
||||
<div class="c-telemetry-view__value-text">{{ telemetryValue }}</div>
|
||||
<template>
|
||||
<layout-frame
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<div
|
||||
v-if="domainObject"
|
||||
class="c-telemetry-view"
|
||||
:style="styleObject"
|
||||
@contextmenu.prevent="showContextMenu"
|
||||
>
|
||||
<div
|
||||
v-if="showLabel"
|
||||
class="c-telemetry-view__label"
|
||||
>
|
||||
<div class="c-telemetry-view__label-text">
|
||||
{{ domainObject.name }}
|
||||
</div>
|
||||
</div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-if="showValue"
|
||||
:title="fieldName"
|
||||
class="c-telemetry-view__value"
|
||||
:class="[telemetryClass]"
|
||||
>
|
||||
<div class="c-telemetry-view__value-text">
|
||||
{{ telemetryValue }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
@ -78,189 +90,200 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import printj from 'printj'
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import printj from 'printj'
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
CONTEXT_MENU_ACTIONS = ['viewHistoricalData'];
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
CONTEXT_MENU_ACTIONS = ['viewHistoricalData'];
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
let metadata = openmct.telemetry.getMetadata(domainObject);
|
||||
position = position || DEFAULT_POSITION;
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
||||
let metadata = openmct.telemetry.getMetadata(domainObject);
|
||||
position = position || DEFAULT_POSITION;
|
||||
|
||||
return {
|
||||
identifier: domainObject.identifier,
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
width: DEFAULT_TELEMETRY_DIMENSIONS[0],
|
||||
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
|
||||
displayMode: 'all',
|
||||
value: metadata.getDefaultDisplayValue(),
|
||||
stroke: "transparent",
|
||||
fill: "transparent",
|
||||
color: "",
|
||||
size: "13px"
|
||||
};
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
},
|
||||
initSelect: Boolean,
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
datum: undefined,
|
||||
formats: undefined,
|
||||
domainObject: undefined,
|
||||
currentObjectPath: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showLabel() {
|
||||
let displayMode = this.item.displayMode;
|
||||
return displayMode === 'all' || displayMode === 'label';
|
||||
},
|
||||
showValue() {
|
||||
let displayMode = this.item.displayMode;
|
||||
return displayMode === 'all' || displayMode === 'value';
|
||||
},
|
||||
styleObject() {
|
||||
return {
|
||||
identifier: domainObject.identifier,
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
width: DEFAULT_TELEMETRY_DIMENSIONS[0],
|
||||
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
|
||||
displayMode: 'all',
|
||||
value: metadata.getDefaultDisplayValue(),
|
||||
stroke: "transparent",
|
||||
fill: "transparent",
|
||||
color: "",
|
||||
size: "13px"
|
||||
backgroundColor: this.item.fill,
|
||||
borderColor: this.item.stroke,
|
||||
color: this.item.color,
|
||||
fontSize: this.item.size
|
||||
}
|
||||
},
|
||||
fieldName() {
|
||||
return this.valueMetadata && this.valueMetadata.name;
|
||||
},
|
||||
valueMetadata() {
|
||||
return this.datum && this.metadata.value(this.item.value);
|
||||
},
|
||||
valueFormatter() {
|
||||
return this.formats[this.item.value];
|
||||
},
|
||||
telemetryValue() {
|
||||
if (!this.datum) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.item.format) {
|
||||
return printj.sprintf(this.item.format, this.datum[this.valueMetadata.key]);
|
||||
}
|
||||
|
||||
return this.valueFormatter && this.valueFormatter.format(this.datum);
|
||||
},
|
||||
telemetryClass() {
|
||||
if (!this.datum) {
|
||||
return;
|
||||
}
|
||||
|
||||
let alarm = this.limitEvaluator && this.limitEvaluator.evaluate(this.datum, this.valueMetadata);
|
||||
return alarm && alarm.cssClass;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
},
|
||||
item(newItem) {
|
||||
this.context.layoutItem = newItem;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.get(this.item.identifier)
|
||||
.then(this.setObject);
|
||||
this.openmct.time.on("bounds", this.refreshData);
|
||||
},
|
||||
destroyed() {
|
||||
this.removeSubscription();
|
||||
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
|
||||
this.openmct.time.off("bounds", this.refreshData);
|
||||
},
|
||||
methods: {
|
||||
requestHistoricalData() {
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let options = {
|
||||
start: bounds.start,
|
||||
end: bounds.end,
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
};
|
||||
},
|
||||
inject: ['openmct', 'objectPath'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
initSelect: Boolean,
|
||||
index: Number
|
||||
},
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
computed: {
|
||||
showLabel() {
|
||||
let displayMode = this.item.displayMode;
|
||||
return displayMode === 'all' || displayMode === 'label';
|
||||
},
|
||||
showValue() {
|
||||
let displayMode = this.item.displayMode;
|
||||
return displayMode === 'all' || displayMode === 'value';
|
||||
},
|
||||
styleObject() {
|
||||
return {
|
||||
backgroundColor: this.item.fill,
|
||||
borderColor: this.item.stroke,
|
||||
color: this.item.color,
|
||||
fontSize: this.item.size
|
||||
}
|
||||
},
|
||||
fieldName() {
|
||||
return this.valueMetadata && this.valueMetadata.name;
|
||||
},
|
||||
valueMetadata() {
|
||||
return this.datum && this.metadata.value(this.item.value);
|
||||
},
|
||||
valueFormatter() {
|
||||
return this.formats[this.item.value];
|
||||
},
|
||||
telemetryValue() {
|
||||
if (!this.datum) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.item.format) {
|
||||
return printj.sprintf(this.item.format, this.datum[this.valueMetadata.key]);
|
||||
}
|
||||
|
||||
return this.valueFormatter && this.valueFormatter.format(this.datum);
|
||||
},
|
||||
telemetryClass() {
|
||||
if (!this.datum) {
|
||||
return;
|
||||
}
|
||||
|
||||
let alarm = this.limitEvaluator && this.limitEvaluator.evaluate(this.datum, this.valueMetadata);
|
||||
return alarm && alarm.cssClass;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
datum: undefined,
|
||||
formats: undefined,
|
||||
domainObject: undefined,
|
||||
currentObjectPath: undefined
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
},
|
||||
item(newItem) {
|
||||
this.context.layoutItem = newItem;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
requestHistoricalData() {
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let options = {
|
||||
start: bounds.start,
|
||||
end: bounds.end,
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
};
|
||||
this.openmct.telemetry.request(this.domainObject, options)
|
||||
.then(data => {
|
||||
if (data.length > 0) {
|
||||
this.updateView(data[data.length - 1]);
|
||||
}
|
||||
});
|
||||
},
|
||||
subscribeToObject() {
|
||||
this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
|
||||
if (this.openmct.time.clock() !== undefined) {
|
||||
this.updateView(datum);
|
||||
this.openmct.telemetry.request(this.domainObject, options)
|
||||
.then(data => {
|
||||
if (data.length > 0) {
|
||||
this.updateView(data[data.length - 1]);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
updateView(datum) {
|
||||
this.datum = datum;
|
||||
},
|
||||
removeSubscription() {
|
||||
if (this.subscription) {
|
||||
this.subscription();
|
||||
this.subscription = undefined;
|
||||
});
|
||||
},
|
||||
subscribeToObject() {
|
||||
this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
|
||||
if (this.openmct.time.clock() !== undefined) {
|
||||
this.updateView(datum);
|
||||
}
|
||||
},
|
||||
refreshData(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.datum = undefined;
|
||||
this.requestHistoricalData(this.domainObject);
|
||||
}
|
||||
},
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.requestHistoricalData();
|
||||
this.subscribeToObject();
|
||||
|
||||
this.currentObjectPath = this.objectPath.slice();
|
||||
this.currentObjectPath.unshift(this.domainObject);
|
||||
|
||||
this.context = {
|
||||
item: domainObject,
|
||||
layoutItem: this.item,
|
||||
index: this.index,
|
||||
updateTelemetryFormat: this.updateTelemetryFormat
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
updateTelemetryFormat(format) {
|
||||
this.$emit('formatChanged', this.item, format);
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}.bind(this));
|
||||
},
|
||||
updateView(datum) {
|
||||
this.datum = datum;
|
||||
},
|
||||
removeSubscription() {
|
||||
if (this.subscription) {
|
||||
this.subscription();
|
||||
this.subscription = undefined;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.get(this.item.identifier)
|
||||
.then(this.setObject);
|
||||
this.openmct.time.on("bounds", this.refreshData);
|
||||
},
|
||||
destroyed() {
|
||||
this.removeSubscription();
|
||||
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
refreshData(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.datum = undefined;
|
||||
this.requestHistoricalData(this.domainObject);
|
||||
}
|
||||
},
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.requestHistoricalData();
|
||||
this.subscribeToObject();
|
||||
|
||||
this.openmct.time.off("bounds", this.refreshData);
|
||||
this.currentObjectPath = this.objectPath.slice();
|
||||
this.currentObjectPath.unshift(this.domainObject);
|
||||
|
||||
this.context = {
|
||||
item: domainObject,
|
||||
layoutItem: this.item,
|
||||
index: this.index,
|
||||
updateTelemetryFormat: this.updateTelemetryFormat
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
updateTelemetryFormat(format) {
|
||||
this.$emit('formatChanged', this.item, format);
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
@ -20,17 +20,21 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
<div class="c-text-view"
|
||||
:style="style">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
<template>
|
||||
<layout-frame
|
||||
:item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')"
|
||||
>
|
||||
<div
|
||||
class="c-text-view"
|
||||
:style="style"
|
||||
>
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</layout-frame>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
@ -46,65 +50,75 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, element) {
|
||||
export default {
|
||||
makeDefinition(openmct, gridSize, element) {
|
||||
return {
|
||||
fill: 'transparent',
|
||||
stroke: 'transparent',
|
||||
size: '13px',
|
||||
color: '',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
text: element.text
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
gridSize: {
|
||||
type: Array,
|
||||
required: true,
|
||||
validator: (arr) => arr && arr.length === 2
|
||||
&& arr.every(el => typeof el === 'number')
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
initSelect: Boolean
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
fill: 'transparent',
|
||||
stroke: 'transparent',
|
||||
size: '13px',
|
||||
color: '',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
text: element.text
|
||||
backgroundColor: this.item.fill,
|
||||
borderColor: this.item.stroke,
|
||||
color: this.item.color,
|
||||
fontSize: this.item.size
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array,
|
||||
index: Number,
|
||||
initSelect: Boolean
|
||||
},
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
backgroundColor: this.item.fill,
|
||||
borderColor: this.item.stroke,
|
||||
color: this.item.color,
|
||||
fontSize: this.item.size
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
index(newIndex) {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
this.context.index = newIndex;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
};
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.context, this.initSelect);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -42,22 +42,22 @@ export default function DisplayLayoutPlugin(options) {
|
||||
return {
|
||||
show(container) {
|
||||
component = new Vue({
|
||||
el: container,
|
||||
components: {
|
||||
Layout
|
||||
},
|
||||
template: '<layout ref="displayLayout" :domain-object="domainObject"></layout>',
|
||||
provide: {
|
||||
openmct,
|
||||
objectUtils,
|
||||
options,
|
||||
objectPath
|
||||
},
|
||||
el: container,
|
||||
data() {
|
||||
return {
|
||||
domainObject: domainObject
|
||||
};
|
||||
}
|
||||
},
|
||||
template: '<layout ref="displayLayout" :domain-object="domainObject"></layout>'
|
||||
});
|
||||
},
|
||||
getSelectionContext() {
|
||||
|
@ -48,11 +48,11 @@ define([
|
||||
provide: {
|
||||
openmct
|
||||
},
|
||||
el: element,
|
||||
components: {
|
||||
FiltersView: FiltersView.default
|
||||
},
|
||||
template: '<filters-view></filters-view>',
|
||||
el: element
|
||||
template: '<filters-view></filters-view>'
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
|
@ -1,56 +1,66 @@
|
||||
<template>
|
||||
<div class="c-properties__section c-filter-settings">
|
||||
<li class="c-properties__row c-filter-settings__setting"
|
||||
v-for="(filter, index) in filterField.filters"
|
||||
:key="index">
|
||||
<div class="c-properties__label label"
|
||||
:disabled="useGlobal">
|
||||
{{ filterField.name }} =
|
||||
</div>
|
||||
<div class="c-properties__value value">
|
||||
<!-- EDITING -->
|
||||
<!-- String input, editing -->
|
||||
<template v-if="!filter.possibleValues && isEditing">
|
||||
<input class="c-input--flex"
|
||||
type="text"
|
||||
:id="`${filter}filterControl`"
|
||||
:disabled="useGlobal"
|
||||
:value="persistedValue(filter)"
|
||||
@change="updateFilterValue($event, filter)">
|
||||
</template>
|
||||
<div class="c-properties__section c-filter-settings">
|
||||
<li
|
||||
v-for="(filter, index) in filterField.filters"
|
||||
:key="index"
|
||||
class="c-properties__row c-filter-settings__setting"
|
||||
>
|
||||
<div
|
||||
class="c-properties__label label"
|
||||
:disabled="useGlobal"
|
||||
>
|
||||
{{ filterField.name }} =
|
||||
</div>
|
||||
<div class="c-properties__value value">
|
||||
<!-- EDITING -->
|
||||
<!-- String input, editing -->
|
||||
<template v-if="!filter.possibleValues && isEditing">
|
||||
<input
|
||||
:id="`${filter}filterControl`"
|
||||
class="c-input--flex"
|
||||
type="text"
|
||||
:disabled="useGlobal"
|
||||
:value="persistedValue(filter)"
|
||||
@change="updateFilterValue($event, filter)"
|
||||
>
|
||||
</template>
|
||||
|
||||
<!-- Checkbox list, editing -->
|
||||
<template v-if="filter.possibleValues && isEditing">
|
||||
<div class="c-checkbox-list__row"
|
||||
v-for="option in filter.possibleValues"
|
||||
:key="option.value">
|
||||
<input class="c-checkbox-list__input"
|
||||
type="checkbox"
|
||||
:id="`${option.value}filterControl`"
|
||||
:disabled="useGlobal"
|
||||
@change="updateFilterValue($event, filter.comparator, option.value)"
|
||||
:checked="isChecked(filter.comparator, option.value)">
|
||||
<span class="c-checkbox-list__value">
|
||||
{{ option.label }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- BROWSING -->
|
||||
<!-- String input, NOT editing -->
|
||||
<template v-if="!filter.possibleValues && !isEditing">
|
||||
{{ persistedValue(filter) }}
|
||||
</template>
|
||||
|
||||
<!-- Checkbox list, NOT editing -->
|
||||
<template v-if="filter.possibleValues && !isEditing">
|
||||
<span v-if="persistedFilters[filter.comparator]">
|
||||
{{ getFilterLabels(filter) }}
|
||||
<!-- Checkbox list, editing -->
|
||||
<template v-if="filter.possibleValues && isEditing">
|
||||
<div
|
||||
v-for="option in filter.possibleValues"
|
||||
:key="option.value"
|
||||
class="c-checkbox-list__row"
|
||||
>
|
||||
<input
|
||||
:id="`${option.value}filterControl`"
|
||||
class="c-checkbox-list__input"
|
||||
type="checkbox"
|
||||
:disabled="useGlobal"
|
||||
:checked="isChecked(filter.comparator, option.value)"
|
||||
@change="updateFilterValue($event, filter.comparator, option.value)"
|
||||
>
|
||||
<span class="c-checkbox-list__value">
|
||||
{{ option.label }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- BROWSING -->
|
||||
<!-- String input, NOT editing -->
|
||||
<template v-if="!filter.possibleValues && !isEditing">
|
||||
{{ persistedValue(filter) }}
|
||||
</template>
|
||||
|
||||
<!-- Checkbox list, NOT editing -->
|
||||
<template v-if="filter.possibleValues && !isEditing">
|
||||
<span v-if="persistedFilters[filter.comparator]">
|
||||
{{ getFilterLabels(filter) }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -59,7 +69,10 @@ export default {
|
||||
'openmct'
|
||||
],
|
||||
props: {
|
||||
filterField: Object,
|
||||
filterField: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
useGlobal: Boolean,
|
||||
persistedFilters: {
|
||||
type: Object,
|
||||
@ -73,6 +86,12 @@ export default {
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.openmct.editor.off('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
methods: {
|
||||
toggleIsEditing(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
@ -107,12 +126,6 @@ export default {
|
||||
return accum;
|
||||
}, []).join(', ');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.openmct.editor.off('isEditing', this.toggleIsEditing);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,50 +1,62 @@
|
||||
<template>
|
||||
<li class="c-tree__item-h">
|
||||
<div class="c-tree__item menus-to-left"
|
||||
@click="toggleExpanded">
|
||||
<div class="c-filter-tree-item__filter-indicator"
|
||||
:class="{'icon-filter': hasActiveFilters }"></div>
|
||||
<span class="c-disclosure-triangle is-enabled flex-elem"
|
||||
:class="{'c-disclosure-triangle--expanded': expanded}"></span>
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div class="c-object-label">
|
||||
<div class="c-object-label__type-icon"
|
||||
:class="objectCssClass">
|
||||
</div>
|
||||
<div class="c-object-label__name flex-elem grows">{{ filterObject.name }}</div>
|
||||
<li class="c-tree__item-h">
|
||||
<div
|
||||
class="c-tree__item menus-to-left"
|
||||
@click="toggleExpanded"
|
||||
>
|
||||
<div
|
||||
class="c-filter-tree-item__filter-indicator"
|
||||
:class="{'icon-filter': hasActiveFilters }"
|
||||
></div>
|
||||
<span
|
||||
class="c-disclosure-triangle is-enabled flex-elem"
|
||||
:class="{'c-disclosure-triangle--expanded': expanded}"
|
||||
></span>
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div class="c-object-label">
|
||||
<div
|
||||
class="c-object-label__type-icon"
|
||||
:class="objectCssClass"
|
||||
></div>
|
||||
<div class="c-object-label__name flex-elem grows">
|
||||
{{ filterObject.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="expanded">
|
||||
<ul class="c-properties">
|
||||
<div class="c-properties__label span-all"
|
||||
v-if="!isEditing && persistedFilters.useGlobal">
|
||||
Uses global filter
|
||||
</div>
|
||||
<div v-if="expanded">
|
||||
<ul class="c-properties">
|
||||
<div
|
||||
v-if="!isEditing && persistedFilters.useGlobal"
|
||||
class="c-properties__label span-all"
|
||||
>
|
||||
Uses global filter
|
||||
</div>
|
||||
|
||||
<div class="c-properties__label span-all"
|
||||
v-if="isEditing">
|
||||
<toggle-switch
|
||||
:id="keyString"
|
||||
@change="useGlobalFilter"
|
||||
:checked="persistedFilters.useGlobal">
|
||||
</toggle-switch>
|
||||
Use global filter
|
||||
</div>
|
||||
<filter-field
|
||||
v-if="(!persistedFilters.useGlobal && !isEditing) || isEditing"
|
||||
v-for="metadatum in filterObject.metadataWithFilters"
|
||||
:key="metadatum.key"
|
||||
:filterField="metadatum"
|
||||
:useGlobal="persistedFilters.useGlobal"
|
||||
:persistedFilters="updatedFilters[metadatum.key]"
|
||||
@filterSelected="updateFiltersWithSelectedValue"
|
||||
@filterTextValueChanged="updateFiltersWithTextValue">
|
||||
</filter-field>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<div
|
||||
v-if="isEditing"
|
||||
class="c-properties__label span-all"
|
||||
>
|
||||
<toggle-switch
|
||||
:id="keyString"
|
||||
:checked="persistedFilters.useGlobal"
|
||||
@change="useGlobalFilter"
|
||||
/>
|
||||
Use global filter
|
||||
</div>
|
||||
<filter-field
|
||||
v-for="metadatum in activeFilters"
|
||||
:key="metadatum.key"
|
||||
:filter-field="metadatum"
|
||||
:use-global="persistedFilters.useGlobal"
|
||||
:persisted-filters="updatedFilters[metadatum.key]"
|
||||
@filterSelected="updateFiltersWithSelectedValue"
|
||||
@filterTextValueChanged="updateFiltersWithTextValue"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -58,7 +70,10 @@ export default {
|
||||
ToggleSwitch
|
||||
},
|
||||
props: {
|
||||
filterObject: Object,
|
||||
filterObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
persistedFilters: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
@ -74,6 +89,23 @@ export default {
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// do not show filter fields if using global filter
|
||||
// if editing however, show all filter fields
|
||||
activeFilters() {
|
||||
if (!this.isEditing && this.persistedFilters.useGlobal) {
|
||||
return []
|
||||
}
|
||||
|
||||
return this.filterObject.metadataWithFilters
|
||||
},
|
||||
hasActiveFilters() {
|
||||
// Should be true when the user has entered any filter values.
|
||||
return Object.values(this.persistedFilters).some(comparator => {
|
||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
persistedFilters: {
|
||||
handler: function checkFilters(newpersistedFilters) {
|
||||
@ -82,13 +114,14 @@ export default {
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveFilters() {
|
||||
// Should be true when the user has entered any filter values.
|
||||
return Object.values(this.persistedFilters).some(comparator => {
|
||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
||||
});
|
||||
}
|
||||
mounted() {
|
||||
let type = this.openmct.types.get(this.filterObject.domainObject.type) || {};
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.filterObject.domainObject.identifier);
|
||||
this.objectCssClass = type.definition.cssClass;
|
||||
this.openmct.editor.on('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.openmct.editor.off('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
methods: {
|
||||
toggleExpanded() {
|
||||
@ -128,16 +161,7 @@ export default {
|
||||
},
|
||||
toggleIsEditing(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
let type = this.openmct.types.get(this.filterObject.domainObject.type) || {};
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.filterObject.domainObject.identifier);
|
||||
this.objectCssClass = type.definition.cssClass;
|
||||
this.openmct.editor.on('isEditing', this.toggleIsEditing);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.openmct.editor.off('isEditing', this.toggleIsEditing);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,22 +1,28 @@
|
||||
<template>
|
||||
<ul class="c-tree c-filter-tree" v-if="Object.keys(children).length">
|
||||
<h2>Data Filters</h2>
|
||||
<div class="c-filter-indication"
|
||||
v-if="hasActiveFilters">{{ label }}
|
||||
</div>
|
||||
<global-filters
|
||||
:globalFilters="globalFilters"
|
||||
:globalMetadata="globalMetadata"
|
||||
@persistGlobalFilters="persistGlobalFilters">
|
||||
</global-filters>
|
||||
<filter-object
|
||||
v-for="(child, key) in children"
|
||||
:key="key"
|
||||
:filterObject="child"
|
||||
:persistedFilters="persistedFilters[key]"
|
||||
@updateFilters="persistFilters">
|
||||
</filter-object>
|
||||
</ul>
|
||||
<ul
|
||||
v-if="Object.keys(children).length"
|
||||
class="c-tree c-filter-tree"
|
||||
>
|
||||
<h2>Data Filters</h2>
|
||||
<div
|
||||
v-if="hasActiveFilters"
|
||||
class="c-filter-indication"
|
||||
>
|
||||
{{ label }}
|
||||
</div>
|
||||
<global-filters
|
||||
:global-filters="globalFilters"
|
||||
:global-metadata="globalMetadata"
|
||||
@persistGlobalFilters="persistGlobalFilters"
|
||||
/>
|
||||
<filter-object
|
||||
v-for="(child, key) in children"
|
||||
:key="key"
|
||||
:filter-object="child"
|
||||
:persisted-filters="persistedFilters[key]"
|
||||
@updateFilters="persistFilters"
|
||||
/>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -40,210 +46,211 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import FilterObject from './FilterObject.vue';
|
||||
import GlobalFilters from './GlobalFilters.vue'
|
||||
import FilterObject from './FilterObject.vue';
|
||||
import GlobalFilters from './GlobalFilters.vue'
|
||||
|
||||
const FILTER_VIEW_TITLE = 'Filters applied';
|
||||
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';
|
||||
const USE_GLOBAL = 'useGlobal';
|
||||
const FILTER_VIEW_TITLE = 'Filters applied';
|
||||
const FILTER_VIEW_TITLE_MIXED = 'Mixed filters applied';
|
||||
const USE_GLOBAL = 'useGlobal';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FilterObject,
|
||||
GlobalFilters
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
data() {
|
||||
let providedObject = this.openmct.selection.get()[0][0].context.item;
|
||||
let configuration = providedObject.configuration;
|
||||
export default {
|
||||
components: {
|
||||
FilterObject,
|
||||
GlobalFilters
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
data() {
|
||||
let providedObject = this.openmct.selection.get()[0][0].context.item;
|
||||
let configuration = providedObject.configuration;
|
||||
|
||||
return {
|
||||
persistedFilters: (configuration && configuration.filters) || {},
|
||||
globalFilters: (configuration && configuration.globalFilters) || {},
|
||||
globalMetadata: {},
|
||||
providedObject,
|
||||
children: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveFilters() {
|
||||
// Should be true when the user has entered any filter values.
|
||||
return Object.values(this.persistedFilters).some(filters => {
|
||||
return Object.values(filters).some(comparator => {
|
||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
||||
});
|
||||
return {
|
||||
persistedFilters: (configuration && configuration.filters) || {},
|
||||
globalFilters: (configuration && configuration.globalFilters) || {},
|
||||
globalMetadata: {},
|
||||
providedObject,
|
||||
children: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveFilters() {
|
||||
// Should be true when the user has entered any filter values.
|
||||
return Object.values(this.persistedFilters).some(filters => {
|
||||
return Object.values(filters).some(comparator => {
|
||||
return (typeof(comparator) === 'object' && !_.isEmpty(comparator));
|
||||
});
|
||||
},
|
||||
hasMixedFilters() {
|
||||
// Should be true when filter values are mixed.
|
||||
let filtersToCompare = _.omit(this.persistedFilters[Object.keys(this.persistedFilters)[0]], [USE_GLOBAL]);
|
||||
return Object.values(this.persistedFilters).some(filters => {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
label() {
|
||||
if (this.hasActiveFilters) {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_VIEW_TITLE_MIXED;
|
||||
} else {
|
||||
return FILTER_VIEW_TITLE;
|
||||
}
|
||||
});
|
||||
},
|
||||
hasMixedFilters() {
|
||||
// Should be true when filter values are mixed.
|
||||
let filtersToCompare = _.omit(this.persistedFilters[Object.keys(this.persistedFilters)[0]], [USE_GLOBAL]);
|
||||
return Object.values(this.persistedFilters).some(filters => {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
label() {
|
||||
if (this.hasActiveFilters) {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_VIEW_TITLE_MIXED;
|
||||
} else {
|
||||
return FILTER_VIEW_TITLE;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addChildren(domainObject) {
|
||||
let keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let metadata = this.openmct.telemetry.getMetadata(domainObject);
|
||||
let metadataWithFilters = metadata.valueMetadatas.filter(value => value.filters);
|
||||
let hasFiltersWithKeyString = this.persistedFilters[keyString] !== undefined;
|
||||
let mutateFilters = false;
|
||||
let childObject = {
|
||||
name: domainObject.name,
|
||||
domainObject: domainObject,
|
||||
metadataWithFilters
|
||||
};
|
||||
return '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.providedObject);
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
this.composition.load();
|
||||
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
||||
this.unobserveGlobalFilters = this.openmct.objects.observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters);
|
||||
this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
this.unobserve();
|
||||
this.unobserveGlobalFilters();
|
||||
this.unobserveAllMutation();
|
||||
},
|
||||
methods: {
|
||||
addChildren(domainObject) {
|
||||
let keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let metadata = this.openmct.telemetry.getMetadata(domainObject);
|
||||
let metadataWithFilters = metadata.valueMetadatas.filter(value => value.filters);
|
||||
let hasFiltersWithKeyString = this.persistedFilters[keyString] !== undefined;
|
||||
let mutateFilters = false;
|
||||
let childObject = {
|
||||
name: domainObject.name,
|
||||
domainObject: domainObject,
|
||||
metadataWithFilters
|
||||
};
|
||||
|
||||
if (metadataWithFilters.length) {
|
||||
this.$set(this.children, keyString, childObject);
|
||||
if (metadataWithFilters.length) {
|
||||
this.$set(this.children, keyString, childObject);
|
||||
|
||||
metadataWithFilters.forEach(metadatum => {
|
||||
if (!this.globalFilters[metadatum.key]) {
|
||||
this.$set(this.globalFilters, metadatum.key, {});
|
||||
metadataWithFilters.forEach(metadatum => {
|
||||
if (!this.globalFilters[metadatum.key]) {
|
||||
this.$set(this.globalFilters, metadatum.key, {});
|
||||
}
|
||||
|
||||
if (!this.globalMetadata[metadatum.key]) {
|
||||
this.$set(this.globalMetadata, metadatum.key, metadatum);
|
||||
}
|
||||
|
||||
if (!hasFiltersWithKeyString) {
|
||||
if (!this.persistedFilters[keyString]) {
|
||||
this.$set(this.persistedFilters, keyString, {});
|
||||
this.$set(this.persistedFilters[keyString], 'useGlobal', true);
|
||||
mutateFilters = true;
|
||||
}
|
||||
|
||||
if (!this.globalMetadata[metadatum.key]) {
|
||||
this.$set(this.globalMetadata, metadatum.key, metadatum);
|
||||
}
|
||||
|
||||
if (!hasFiltersWithKeyString) {
|
||||
if (!this.persistedFilters[keyString]) {
|
||||
this.$set(this.persistedFilters, keyString, {});
|
||||
this.$set(this.persistedFilters[keyString], 'useGlobal', true);
|
||||
mutateFilters = true;
|
||||
}
|
||||
|
||||
this.$set(this.persistedFilters[keyString], metadatum.key, this.globalFilters[metadatum.key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mutateFilters) {
|
||||
this.mutateConfigurationFilters();
|
||||
}
|
||||
},
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
let globalFiltersToRemove = this.getGlobalFiltersToRemove(keyString);
|
||||
|
||||
if (globalFiltersToRemove.length > 0) {
|
||||
globalFiltersToRemove.forEach(key => {
|
||||
this.$delete(this.globalFilters, key);
|
||||
this.$delete(this.globalMetadata, key);
|
||||
});
|
||||
this.mutateConfigurationGlobalFilters();
|
||||
}
|
||||
|
||||
this.$delete(this.children, keyString);
|
||||
this.$delete(this.persistedFilters, keyString);
|
||||
this.mutateConfigurationFilters();
|
||||
},
|
||||
getGlobalFiltersToRemove(keyString) {
|
||||
let filtersToRemove = new Set();
|
||||
|
||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
||||
let keepFilter = false
|
||||
Object.keys(this.children).forEach(childKeyString => {
|
||||
if (childKeyString !== keyString) {
|
||||
let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key);
|
||||
|
||||
if (filterMatched) {
|
||||
keepFilter = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!keepFilter) {
|
||||
filtersToRemove.add(metadatum.key);
|
||||
this.$set(this.persistedFilters[keyString], metadatum.key, this.globalFilters[metadatum.key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(filtersToRemove);
|
||||
},
|
||||
persistFilters(keyString, updatedFilters, useGlobalValues) {
|
||||
this.persistedFilters[keyString] = updatedFilters;
|
||||
|
||||
if (useGlobalValues) {
|
||||
Object.keys(this.persistedFilters[keyString]).forEach(key => {
|
||||
if (typeof(this.persistedFilters[keyString][key]) === 'object') {
|
||||
this.persistedFilters[keyString][key] = this.globalFilters[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mutateFilters) {
|
||||
this.mutateConfigurationFilters();
|
||||
},
|
||||
updatePersistedFilters(filters) {
|
||||
this.persistedFilters = filters;
|
||||
},
|
||||
persistGlobalFilters(key, filters) {
|
||||
this.globalFilters[key] = filters[key];
|
||||
}
|
||||
},
|
||||
removeChildren(identifier) {
|
||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
||||
let globalFiltersToRemove = this.getGlobalFiltersToRemove(keyString);
|
||||
|
||||
if (globalFiltersToRemove.length > 0) {
|
||||
globalFiltersToRemove.forEach(key => {
|
||||
this.$delete(this.globalFilters, key);
|
||||
this.$delete(this.globalMetadata, key);
|
||||
});
|
||||
this.mutateConfigurationGlobalFilters();
|
||||
let mutateFilters = false;
|
||||
}
|
||||
|
||||
Object.keys(this.children).forEach(keyString => {
|
||||
if (this.persistedFilters[keyString].useGlobal !== false && this.containsField(keyString, key)) {
|
||||
if (!this.persistedFilters[keyString][key]) {
|
||||
this.$set(this.persistedFilters[keyString], key, {});
|
||||
this.$delete(this.children, keyString);
|
||||
this.$delete(this.persistedFilters, keyString);
|
||||
this.mutateConfigurationFilters();
|
||||
},
|
||||
getGlobalFiltersToRemove(keyString) {
|
||||
let filtersToRemove = new Set();
|
||||
|
||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
||||
let keepFilter = false
|
||||
Object.keys(this.children).forEach(childKeyString => {
|
||||
if (childKeyString !== keyString) {
|
||||
let filterMatched = this.children[childKeyString].metadataWithFilters.some(childMetadatum => childMetadatum.key === metadatum.key);
|
||||
|
||||
if (filterMatched) {
|
||||
keepFilter = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.$set(this.persistedFilters[keyString], key, filters[key]);
|
||||
mutateFilters = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (mutateFilters) {
|
||||
this.mutateConfigurationFilters();
|
||||
if (!keepFilter) {
|
||||
filtersToRemove.add(metadatum.key);
|
||||
}
|
||||
},
|
||||
updateGlobalFilters(filters) {
|
||||
this.globalFilters = filters;
|
||||
},
|
||||
containsField(keyString, field) {
|
||||
let hasField = false;
|
||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
||||
if (metadatum.key === field) {
|
||||
hasField = true;
|
||||
return;
|
||||
});
|
||||
|
||||
return Array.from(filtersToRemove);
|
||||
},
|
||||
persistFilters(keyString, updatedFilters, useGlobalValues) {
|
||||
this.persistedFilters[keyString] = updatedFilters;
|
||||
|
||||
if (useGlobalValues) {
|
||||
Object.keys(this.persistedFilters[keyString]).forEach(key => {
|
||||
if (typeof(this.persistedFilters[keyString][key]) === 'object') {
|
||||
this.persistedFilters[keyString][key] = this.globalFilters[key];
|
||||
}
|
||||
});
|
||||
return hasField;
|
||||
},
|
||||
mutateConfigurationFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
|
||||
},
|
||||
mutateConfigurationGlobalFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.globalFilters', this.globalFilters);
|
||||
}
|
||||
|
||||
this.mutateConfigurationFilters();
|
||||
},
|
||||
updatePersistedFilters(filters) {
|
||||
this.persistedFilters = filters;
|
||||
},
|
||||
persistGlobalFilters(key, filters) {
|
||||
this.globalFilters[key] = filters[key];
|
||||
this.mutateConfigurationGlobalFilters();
|
||||
let mutateFilters = false;
|
||||
|
||||
Object.keys(this.children).forEach(keyString => {
|
||||
if (this.persistedFilters[keyString].useGlobal !== false && this.containsField(keyString, key)) {
|
||||
if (!this.persistedFilters[keyString][key]) {
|
||||
this.$set(this.persistedFilters[keyString], key, {});
|
||||
}
|
||||
|
||||
this.$set(this.persistedFilters[keyString], key, filters[key]);
|
||||
mutateFilters = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (mutateFilters) {
|
||||
this.mutateConfigurationFilters();
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.composition = this.openmct.composition.get(this.providedObject);
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
this.composition.load();
|
||||
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
||||
this.unobserveGlobalFilters = this.openmct.objects.observe(this.providedObject, 'configuration.globalFilters', this.updateGlobalFilters);
|
||||
this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject);
|
||||
updateGlobalFilters(filters) {
|
||||
this.globalFilters = filters;
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
this.unobserve();
|
||||
this.unobserveGlobalFilters();
|
||||
this.unobserveAllMutation();
|
||||
containsField(keyString, field) {
|
||||
let hasField = false;
|
||||
this.children[keyString].metadataWithFilters.forEach(metadatum => {
|
||||
if (metadatum.key === field) {
|
||||
hasField = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return hasField;
|
||||
},
|
||||
mutateConfigurationFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
|
||||
},
|
||||
mutateConfigurationGlobalFilters() {
|
||||
this.openmct.objects.mutate(this.providedObject, 'configuration.globalFilters', this.globalFilters);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,29 +1,40 @@
|
||||
<template>
|
||||
<li class="c-tree__item-h">
|
||||
<div class="c-tree__item menus-to-left"
|
||||
@click="toggleExpanded">
|
||||
<div class="c-filter-tree-item__filter-indicator"
|
||||
:class="{'icon-filter': hasActiveGlobalFilters }"></div>
|
||||
<span class="c-disclosure-triangle is-enabled flex-elem"
|
||||
:class="{'c-disclosure-triangle--expanded': expanded}"></span>
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div class="c-object-label">
|
||||
<div class="c-object-label__type-icon icon-gear"></div>
|
||||
<div class="c-object-label__name flex-elem grows">Global Filtering</div>
|
||||
<li class="c-tree__item-h">
|
||||
<div
|
||||
class="c-tree__item menus-to-left"
|
||||
@click="toggleExpanded"
|
||||
>
|
||||
<div
|
||||
class="c-filter-tree-item__filter-indicator"
|
||||
:class="{'icon-filter': hasActiveGlobalFilters }"
|
||||
></div>
|
||||
<span
|
||||
class="c-disclosure-triangle is-enabled flex-elem"
|
||||
:class="{'c-disclosure-triangle--expanded': expanded}"
|
||||
></span>
|
||||
<div class="c-tree__item__label c-object-label">
|
||||
<div class="c-object-label">
|
||||
<div class="c-object-label__type-icon icon-gear"></div>
|
||||
<div class="c-object-label__name flex-elem grows">
|
||||
Global Filtering
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="c-properties" v-if="expanded">
|
||||
<filter-field
|
||||
v-for="metadatum in globalMetadata"
|
||||
:key="metadatum.key"
|
||||
:filterField="metadatum"
|
||||
:persistedFilters="updatedFilters[metadatum.key]"
|
||||
@filterSelected="updateFiltersWithSelectedValue"
|
||||
@filterTextValueChanged="updateFiltersWithTextValue">
|
||||
</filter-field>
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
<ul
|
||||
v-if="expanded"
|
||||
class="c-properties"
|
||||
>
|
||||
<filter-field
|
||||
v-for="metadatum in globalMetadata"
|
||||
:key="metadatum.key"
|
||||
:filter-field="metadatum"
|
||||
:persisted-filters="updatedFilters[metadatum.key]"
|
||||
@filterSelected="updateFiltersWithSelectedValue"
|
||||
@filterTextValueChanged="updateFiltersWithTextValue"
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -59,77 +70,80 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import FilterField from './FilterField.vue';
|
||||
import FilterField from './FilterField.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
FilterField
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
FilterField
|
||||
},
|
||||
props: {
|
||||
globalMetadata: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
props: {
|
||||
globalMetadata: Object,
|
||||
globalFilters: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
updatedFilters: JSON.parse(JSON.stringify(this.globalFilters))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveGlobalFilters() {
|
||||
return Object.values(this.globalFilters).some(field => {
|
||||
return Object.values(field).some(comparator => {
|
||||
return (comparator && (comparator !== '' || comparator.length > 0));
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
globalFilters: {
|
||||
handler: function checkFilters(newGlobalFilters) {
|
||||
this.updatedFilters = JSON.parse(JSON.stringify(newGlobalFilters));
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
updateFiltersWithSelectedValue(key, comparator, valueName, value) {
|
||||
let filterValue = this.updatedFilters[key];
|
||||
|
||||
if (filterValue[comparator]) {
|
||||
if (value === true) {
|
||||
filterValue[comparator].push(valueName);
|
||||
} else {
|
||||
if (filterValue[comparator].length === 1) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, [valueName]);
|
||||
}
|
||||
|
||||
this.$emit('persistGlobalFilters', key, this.updatedFilters);
|
||||
},
|
||||
updateFiltersWithTextValue(key, comparator, value) {
|
||||
if (value.trim() === '') {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, value);
|
||||
}
|
||||
|
||||
this.$emit('persistGlobalFilters', key, this.updatedFilters);
|
||||
globalFilters: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
updatedFilters: JSON.parse(JSON.stringify(this.globalFilters))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasActiveGlobalFilters() {
|
||||
return Object.values(this.globalFilters).some(field => {
|
||||
return Object.values(field).some(comparator => {
|
||||
return (comparator && (comparator !== '' || comparator.length > 0));
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
globalFilters: {
|
||||
handler: function checkFilters(newGlobalFilters) {
|
||||
this.updatedFilters = JSON.parse(JSON.stringify(newGlobalFilters));
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
updateFiltersWithSelectedValue(key, comparator, valueName, value) {
|
||||
let filterValue = this.updatedFilters[key];
|
||||
|
||||
if (filterValue[comparator]) {
|
||||
if (value === true) {
|
||||
filterValue[comparator].push(valueName);
|
||||
} else {
|
||||
if (filterValue[comparator].length === 1) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, [valueName]);
|
||||
}
|
||||
|
||||
this.$emit('persistGlobalFilters', key, this.updatedFilters);
|
||||
},
|
||||
updateFiltersWithTextValue(key, comparator, value) {
|
||||
if (value.trim() === '') {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
} else {
|
||||
this.$set(this.updatedFilters[key], comparator, value);
|
||||
}
|
||||
|
||||
this.$emit('persistGlobalFilters', key, this.updatedFilters);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,62 +21,65 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fl-container"
|
||||
:style="[{'flex-basis': sizeString}]"
|
||||
:class="{'is-empty': !frames.length}">
|
||||
<div class="c-fl-container__header"
|
||||
v-show="isEditing"
|
||||
draggable="true"
|
||||
@dragstart="startContainerDrag">
|
||||
<span class="c-fl-container__size-indicator">{{ sizeString }}</span>
|
||||
</div>
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="-1"
|
||||
:allow-drop="allowDrop"
|
||||
@object-drop-to="moveOrCreateNewFrame">
|
||||
</drop-hint>
|
||||
|
||||
<div class="c-fl-container__frames-holder">
|
||||
<template
|
||||
v-for="(frame, i) in frames">
|
||||
|
||||
<frame-component
|
||||
class="c-fl-container__frame"
|
||||
:key="frame.id"
|
||||
:frame="frame"
|
||||
:index="i"
|
||||
:containerIndex="index"
|
||||
:isEditing="isEditing">
|
||||
</frame-component>
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
:key="i"
|
||||
:index="i"
|
||||
:allowDrop="allowDrop"
|
||||
@object-drop-to="moveOrCreateNewFrame">
|
||||
</drop-hint>
|
||||
|
||||
<resize-handle
|
||||
v-if="(i !== frames.length - 1)"
|
||||
:key="i"
|
||||
:index="i"
|
||||
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
||||
@init-move="startFrameResizing"
|
||||
@move="frameResizing"
|
||||
@end-move="endFrameResizing"
|
||||
:isEditing="isEditing">
|
||||
</resize-handle>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
class="c-fl-container"
|
||||
:style="[{'flex-basis': sizeString}]"
|
||||
:class="{'is-empty': !frames.length}"
|
||||
>
|
||||
<div
|
||||
v-show="isEditing"
|
||||
class="c-fl-container__header"
|
||||
draggable="true"
|
||||
@dragstart="startContainerDrag"
|
||||
>
|
||||
<span class="c-fl-container__size-indicator">{{ sizeString }}</span>
|
||||
</div>
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="-1"
|
||||
:allow-drop="allowDrop"
|
||||
@object-drop-to="moveOrCreateNewFrame"
|
||||
/>
|
||||
|
||||
<div class="c-fl-container__frames-holder">
|
||||
<template
|
||||
v-for="(frame, i) in frames"
|
||||
>
|
||||
<frame-component
|
||||
:key="frame.id"
|
||||
class="c-fl-container__frame"
|
||||
:frame="frame"
|
||||
:index="i"
|
||||
:container-index="index"
|
||||
:is-editing="isEditing"
|
||||
/>
|
||||
|
||||
<drop-hint
|
||||
:key="i"
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="i"
|
||||
:allow-drop="allowDrop"
|
||||
@object-drop-to="moveOrCreateNewFrame"
|
||||
/>
|
||||
|
||||
<resize-handle
|
||||
v-if="(i !== frames.length - 1)"
|
||||
:key="i"
|
||||
:index="i"
|
||||
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
||||
:is-editing="isEditing"
|
||||
@init-move="startFrameResizing"
|
||||
@move="frameResizing"
|
||||
@end-move="endFrameResizing"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FrameComponent from './frame.vue';
|
||||
import Frame from '../utils/frame';
|
||||
import ResizeHandle from './resizeHandle.vue';
|
||||
import DropHint from './dropHint.vue';
|
||||
|
||||
@ -84,12 +87,26 @@ const MIN_FRAME_SIZE = 5;
|
||||
|
||||
export default {
|
||||
inject:['openmct'],
|
||||
props: ['container', 'index', 'rowsLayout', 'isEditing'],
|
||||
components: {
|
||||
FrameComponent,
|
||||
ResizeHandle,
|
||||
DropHint
|
||||
},
|
||||
props: {
|
||||
container: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
rowsLayout: Boolean,
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
frames() {
|
||||
return this.container.frames;
|
||||
@ -98,6 +115,19 @@ export default {
|
||||
return `${Math.round(this.container.size)}%`
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let context = {
|
||||
item: this.$parent.domainObject,
|
||||
addContainer: this.addContainer,
|
||||
type: 'container',
|
||||
containerId: this.container.id
|
||||
}
|
||||
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unsubscribeSelection();
|
||||
},
|
||||
methods: {
|
||||
allowDrop(event, index) {
|
||||
if (event.dataTransfer.types.includes('openmct/domain-object-path')) {
|
||||
@ -131,7 +161,7 @@ export default {
|
||||
insertIndex
|
||||
);
|
||||
return;
|
||||
};
|
||||
}
|
||||
// move frame.
|
||||
let frameId = event.dataTransfer.getData('frameid');
|
||||
let containerIndex = Number(event.dataTransfer.getData('containerIndex'));
|
||||
@ -182,19 +212,6 @@ export default {
|
||||
startContainerDrag(event) {
|
||||
event.dataTransfer.setData('containerid', this.container.id);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let context = {
|
||||
item: this.$parent.domainObject,
|
||||
addContainer: this.addContainer,
|
||||
type: 'container',
|
||||
containerId: this.container.id
|
||||
}
|
||||
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unsubscribeSelection();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,15 +21,16 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div v-show="isValidTarget">
|
||||
<div class="c-drop-hint c-drop-hint--always-show"
|
||||
:class="{'is-mouse-over': isMouseOver}"
|
||||
@dragover.prevent
|
||||
@dragenter="dragenter"
|
||||
@dragleave="dragleave"
|
||||
@drop="dropHandler">
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isValidTarget">
|
||||
<div
|
||||
class="c-drop-hint c-drop-hint--always-show"
|
||||
:class="{'is-mouse-over': isMouseOver}"
|
||||
@dragover.prevent
|
||||
@dragenter="dragenter"
|
||||
@dragleave="dragleave"
|
||||
@drop="dropHandler"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -39,7 +40,10 @@
|
||||
<script>
|
||||
export default {
|
||||
props:{
|
||||
index: Number,
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
allowDrop: {
|
||||
type: Function,
|
||||
required: true
|
||||
@ -51,6 +55,16 @@ export default {
|
||||
isValidTarget: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
document.addEventListener('drop', this.dragend);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
document.removeEventListener('drop', this.dragend);
|
||||
},
|
||||
methods: {
|
||||
dragenter() {
|
||||
this.isMouseOver = true;
|
||||
@ -68,16 +82,6 @@ export default {
|
||||
dragend() {
|
||||
this.isValidTarget = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
document.addEventListener('drop', this.dragend);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
document.removeEventListener('drop', this.dragend);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,72 +21,74 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fl">
|
||||
<div
|
||||
id="js-fl-drag-ghost"
|
||||
class="c-fl__drag-ghost">
|
||||
</div>
|
||||
<div class="c-fl">
|
||||
<div
|
||||
id="js-fl-drag-ghost"
|
||||
class="c-fl__drag-ghost"
|
||||
></div>
|
||||
|
||||
<div class="c-fl__empty"
|
||||
v-if="areAllContainersEmpty()">
|
||||
<span class="c-fl__empty-message">This Flexible Layout is currently empty</span>
|
||||
</div>
|
||||
|
||||
<div class="c-fl__container-holder"
|
||||
:class="{
|
||||
'c-fl--rows': rowsLayout === true
|
||||
}">
|
||||
|
||||
<template v-for="(container, index) in containers">
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
v-if="index === 0 && containers.length > 1"
|
||||
:key="index"
|
||||
:index="-1"
|
||||
:allow-drop="allowContainerDrop"
|
||||
@object-drop-to="moveContainer">
|
||||
</drop-hint>
|
||||
|
||||
<container-component
|
||||
class="c-fl__container"
|
||||
:key="container.id"
|
||||
:index="index"
|
||||
:container="container"
|
||||
:rowsLayout="rowsLayout"
|
||||
:isEditing="isEditing"
|
||||
@move-frame="moveFrame"
|
||||
@new-frame="setFrameLocation"
|
||||
@persist="persist">
|
||||
</container-component>
|
||||
|
||||
<resize-handle
|
||||
v-if="index !== (containers.length - 1)"
|
||||
:key="index"
|
||||
:index="index"
|
||||
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
||||
:isEditing="isEditing"
|
||||
@init-move="startContainerResizing"
|
||||
@move="containerResizing"
|
||||
@end-move="endContainerResizing">
|
||||
</resize-handle>
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
v-if="containers.length > 1"
|
||||
:key="index"
|
||||
:index="index"
|
||||
:allowDrop="allowContainerDrop"
|
||||
@object-drop-to="moveContainer">
|
||||
</drop-hint>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-if="areAllContainersEmpty()"
|
||||
class="c-fl__empty"
|
||||
>
|
||||
<span class="c-fl__empty-message">This Flexible Layout is currently empty</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="c-fl__container-holder"
|
||||
:class="{
|
||||
'c-fl--rows': rowsLayout === true
|
||||
}"
|
||||
>
|
||||
<template v-for="(container, index) in containers">
|
||||
<drop-hint
|
||||
v-if="index === 0 && containers.length > 1"
|
||||
:key="index"
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="-1"
|
||||
:allow-drop="allowContainerDrop"
|
||||
@object-drop-to="moveContainer"
|
||||
/>
|
||||
|
||||
<container-component
|
||||
:key="container.id"
|
||||
class="c-fl__container"
|
||||
:index="index"
|
||||
:container="container"
|
||||
:rows-layout="rowsLayout"
|
||||
:is-editing="isEditing"
|
||||
@move-frame="moveFrame"
|
||||
@new-frame="setFrameLocation"
|
||||
@persist="persist"
|
||||
/>
|
||||
|
||||
<resize-handle
|
||||
v-if="index !== (containers.length - 1)"
|
||||
:key="index"
|
||||
:index="index"
|
||||
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
||||
:is-editing="isEditing"
|
||||
@init-move="startContainerResizing"
|
||||
@move="containerResizing"
|
||||
@end-move="endContainerResizing"
|
||||
/>
|
||||
|
||||
<drop-hint
|
||||
v-if="containers.length > 1"
|
||||
:key="index"
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="index"
|
||||
:allow-drop="allowContainerDrop"
|
||||
@object-drop-to="moveContainer"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
|
||||
|
||||
@mixin containerGrippy($headerSize, $dir) {
|
||||
position: absolute;
|
||||
$h: 6px;
|
||||
@ -465,15 +467,15 @@ export default {
|
||||
ResizeHandle,
|
||||
DropHint
|
||||
},
|
||||
props: {
|
||||
isEditing: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject: this.layoutObject,
|
||||
newFrameLocation: []
|
||||
}
|
||||
},
|
||||
props: {
|
||||
isEditing: Boolean
|
||||
},
|
||||
computed: {
|
||||
layoutDirectionStr() {
|
||||
if (this.rowsLayout) {
|
||||
@ -489,9 +491,24 @@ export default {
|
||||
return this.domainObject.configuration.rowsLayout;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('remove', this.removeChildObject);
|
||||
this.composition.on('add', this.addFrame);
|
||||
|
||||
this.RemoveAction = new RemoveAction(this.openmct);
|
||||
|
||||
this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.composition.off('remove', this.removeChildObject);
|
||||
this.composition.off('add', this.addFrame);
|
||||
|
||||
this.unobserve();
|
||||
},
|
||||
methods: {
|
||||
areAllContainersEmpty() {
|
||||
return !!!this.containers.filter(container => container.frames.length).length;
|
||||
return !this.containers.filter(container => container.frames.length).length;
|
||||
},
|
||||
addContainer() {
|
||||
let container = new Container();
|
||||
@ -515,7 +532,7 @@ export default {
|
||||
/*
|
||||
add a container when there are no containers in the FL,
|
||||
to prevent user from not being able to add a frame via
|
||||
drag and drop.
|
||||
drag and drop.
|
||||
*/
|
||||
if (this.containers.length === 0) {
|
||||
this.containers.push(new Container(100));
|
||||
@ -589,7 +606,7 @@ export default {
|
||||
return containerPos !== index && (containerPos - 1) !== index
|
||||
}
|
||||
},
|
||||
persist(index){
|
||||
persist(index) {
|
||||
if (index) {
|
||||
this.openmct.objects.mutate(this.domainObject, `configuration.containers[${index}]`, this.containers[index]);
|
||||
} else {
|
||||
@ -650,28 +667,13 @@ export default {
|
||||
this.containers.forEach(container => {
|
||||
container.frames = container.frames.filter(frame => {
|
||||
let frameIdentifier = this.openmct.objects.makeKeyString(frame.domainObjectIdentifier);
|
||||
|
||||
|
||||
return removeIdentifier !== frameIdentifier;
|
||||
});
|
||||
});
|
||||
|
||||
this.persist();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('remove', this.removeChildObject);
|
||||
this.composition.on('add', this.addFrame);
|
||||
|
||||
this.RemoveAction = new RemoveAction(this.openmct);
|
||||
|
||||
this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.composition.off('remove', this.removeChildObject);
|
||||
this.composition.off('add', this.addFrame);
|
||||
|
||||
this.unobserve();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,56 +21,89 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fl-frame"
|
||||
:style="{
|
||||
'flex-basis': `${frame.size}%`
|
||||
}">
|
||||
<div
|
||||
class="c-fl-frame"
|
||||
:style="{
|
||||
'flex-basis': `${frame.size}%`
|
||||
}"
|
||||
>
|
||||
<div
|
||||
ref="frame"
|
||||
class="c-frame c-fl-frame__drag-wrapper is-selectable u-inspectable is-moveable"
|
||||
draggable="true"
|
||||
@dragstart="initDrag"
|
||||
>
|
||||
<object-frame
|
||||
v-if="domainObject"
|
||||
ref="objectFrame"
|
||||
:domain-object="domainObject"
|
||||
:object-path="objectPath"
|
||||
:has-frame="hasFrame"
|
||||
:show-edit-view="false"
|
||||
/>
|
||||
|
||||
<div class="c-frame c-fl-frame__drag-wrapper is-selectable u-inspectable is-moveable"
|
||||
draggable="true"
|
||||
@dragstart="initDrag"
|
||||
ref="frame">
|
||||
|
||||
<object-frame
|
||||
v-if="domainObject"
|
||||
:domain-object="domainObject"
|
||||
:object-path="objectPath"
|
||||
:has-frame="hasFrame"
|
||||
:show-edit-view="false"
|
||||
ref="objectFrame">
|
||||
</object-frame>
|
||||
|
||||
<div class="c-fl-frame__size-indicator"
|
||||
v-if="isEditing"
|
||||
v-show="frame.size && frame.size < 100">
|
||||
{{frame.size}}%
|
||||
</div>
|
||||
<div
|
||||
v-if="isEditing"
|
||||
v-show="frame.size && frame.size < 100"
|
||||
class="c-fl-frame__size-indicator"
|
||||
>
|
||||
{{ frame.size }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ResizeHandle from './resizeHandle.vue';
|
||||
import ObjectFrame from '../../../ui/components/ObjectFrame.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: ['frame', 'index', 'containerIndex', 'isEditing'],
|
||||
components: {
|
||||
ObjectFrame
|
||||
},
|
||||
props: {
|
||||
frame: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
containerIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject: undefined,
|
||||
objectPath: undefined
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ResizeHandle,
|
||||
ObjectFrame
|
||||
},
|
||||
computed: {
|
||||
hasFrame() {
|
||||
return !this.frame.noFrame;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.frame.domainObjectIdentifier) {
|
||||
this.openmct.objects.get(this.frame.domainObjectIdentifier).then((object)=>{
|
||||
this.setDomainObject(object);
|
||||
});
|
||||
}
|
||||
|
||||
this.dragGhost = document.getElementById('js-fl-drag-ghost');
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.unsubscribeSelection) {
|
||||
this.unsubscribeSelection();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDomainObject(object) {
|
||||
this.domainObject = object;
|
||||
@ -92,7 +125,7 @@ export default {
|
||||
initDrag(event) {
|
||||
let type = this.openmct.types.get(this.domainObject.type),
|
||||
iconClass = type.definition ? type.definition.cssClass : 'icon-object-unknown';
|
||||
|
||||
|
||||
if (this.dragGhost) {
|
||||
let originalClassName = this.dragGhost.classList[0];
|
||||
this.dragGhost.className = '';
|
||||
@ -105,20 +138,6 @@ export default {
|
||||
event.dataTransfer.setData('frameid', this.frame.id);
|
||||
event.dataTransfer.setData('containerIndex', this.containerIndex);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.frame.domainObjectIdentifier) {
|
||||
this.openmct.objects.get(this.frame.domainObjectIdentifier).then((object)=>{
|
||||
this.setDomainObject(object);
|
||||
});
|
||||
}
|
||||
|
||||
this.dragGhost = document.getElementById('js-fl-drag-ghost');
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.unsubscribeSelection) {
|
||||
this.unsubscribeSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -21,22 +21,46 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fl-frame__resize-handle"
|
||||
:class="[orientation]"
|
||||
v-show="isEditing && !isDragging"
|
||||
@mousedown="mousedown">
|
||||
</div>
|
||||
<div
|
||||
v-show="isEditing && !isDragging"
|
||||
class="c-fl-frame__resize-handle"
|
||||
:class="[orientation]"
|
||||
@mousedown="mousedown"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['orientation', 'index', 'isEditing'],
|
||||
props: {
|
||||
orientation: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
isEditing: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialPos: 0,
|
||||
isDragging: false,
|
||||
isDragging: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.setDragging);
|
||||
document.addEventListener('dragend', this.unsetDragging);
|
||||
document.addEventListener('drop', this.unsetDragging);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.setDragging);
|
||||
document.removeEventListener('dragend', this.unsetDragging);
|
||||
document.removeEventListener('drop', this.unsetDragging);
|
||||
},
|
||||
methods: {
|
||||
mousedown(event) {
|
||||
event.preventDefault();
|
||||
@ -75,16 +99,6 @@ export default {
|
||||
unsetDragging(event) {
|
||||
this.isDragging = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.setDragging);
|
||||
document.addEventListener('dragend', this.unsetDragging);
|
||||
document.addEventListener('drop', this.unsetDragging);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.setDragging);
|
||||
document.removeEventListener('dragend', this.unsetDragging);
|
||||
document.removeEventListener('drop', this.unsetDragging);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -44,19 +44,19 @@ define([
|
||||
return {
|
||||
show: function (element, isEditing) {
|
||||
component = new Vue({
|
||||
data() {
|
||||
return {
|
||||
isEditing: isEditing
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FlexibleLayoutComponent: FlexibleLayoutComponent.default
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
layoutObject: domainObject
|
||||
},
|
||||
el: element,
|
||||
components: {
|
||||
FlexibleLayoutComponent: FlexibleLayoutComponent.default
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEditing: isEditing
|
||||
}
|
||||
},
|
||||
template: '<flexible-layout-component ref="flexibleLayout" :isEditing="isEditing"></flexible-layout-component>'
|
||||
});
|
||||
},
|
||||
|
@ -41,6 +41,7 @@ define([
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
gridViewComponent: GridViewComponent.default
|
||||
},
|
||||
@ -48,7 +49,6 @@ define([
|
||||
openmct,
|
||||
domainObject
|
||||
},
|
||||
el: element,
|
||||
template: '<grid-view-component></grid-view-component>'
|
||||
});
|
||||
},
|
||||
|
@ -43,6 +43,7 @@ define([
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
listViewComponent: ListViewComponent.default
|
||||
},
|
||||
@ -51,7 +52,6 @@ define([
|
||||
domainObject,
|
||||
Moment
|
||||
},
|
||||
el: element,
|
||||
template: '<list-view-component></list-view-component>'
|
||||
});
|
||||
},
|
||||
|
@ -1,25 +1,38 @@
|
||||
<template>
|
||||
<a class="l-grid-view__item c-grid-item"
|
||||
:class="{ 'is-alias': item.isAlias === true }"
|
||||
:href="objectLink">
|
||||
<div class="c-grid-item__type-icon"
|
||||
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'">
|
||||
<a
|
||||
class="l-grid-view__item c-grid-item"
|
||||
:class="{ 'is-alias': item.isAlias === true }"
|
||||
:href="objectLink"
|
||||
>
|
||||
<div
|
||||
class="c-grid-item__type-icon"
|
||||
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"
|
||||
></div>
|
||||
<div class="c-grid-item__details">
|
||||
<!-- Name and metadata -->
|
||||
<div
|
||||
class="c-grid-item__name"
|
||||
:title="item.model.name"
|
||||
>{{ item.model.name }}</div>
|
||||
<div
|
||||
class="c-grid-item__metadata"
|
||||
:title="item.type.name"
|
||||
>
|
||||
<span class="c-grid-item__metadata__type">{{ item.type.name }}</span>
|
||||
</div>
|
||||
<div class="c-grid-item__details">
|
||||
<!-- Name and metadata -->
|
||||
<div class="c-grid-item__name"
|
||||
:title="item.model.name">{{item.model.name}}</div>
|
||||
<div class="c-grid-item__metadata"
|
||||
:title="item.type.name">
|
||||
<span class="c-grid-item__metadata__type">{{item.type.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-grid-item__controls">
|
||||
<div class="icon-people" title='Shared'></div>
|
||||
<button class="c-icon-button icon-info c-info-button" title='More Info'></button>
|
||||
<div class="icon-pointer-right c-pointer-icon"></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="c-grid-item__controls">
|
||||
<div
|
||||
class="icon-people"
|
||||
title="Shared"
|
||||
></div>
|
||||
<button
|
||||
class="c-icon-button icon-info c-info-button"
|
||||
title="More Info"
|
||||
></button>
|
||||
<div class="icon-pointer-right c-pointer-icon"></div>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -154,6 +167,11 @@ import objectLink from '../../../ui/mixins/object-link';
|
||||
|
||||
export default {
|
||||
mixins: [contextMenuGesture, objectLink],
|
||||
props: ['item']
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<div class="l-grid-view">
|
||||
<grid-item v-for="(item, index) in items"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:object-path="item.objectPath">
|
||||
</grid-item>
|
||||
</div>
|
||||
<div class="l-grid-view">
|
||||
<grid-item
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:object-path="item.objectPath"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -1,17 +1,31 @@
|
||||
<template>
|
||||
<tr class="c-list-item"
|
||||
:class="{ 'is-alias': item.isAlias === true }"
|
||||
@click="navigate">
|
||||
<td class="c-list-item__name">
|
||||
<a :href="objectLink" ref="objectLink">
|
||||
<div class="c-list-item__type-icon" :class="item.type.cssClass"></div>
|
||||
<div class="c-list-item__name-value">{{item.model.name}}</div>
|
||||
</a>
|
||||
</td>
|
||||
<td class="c-list-item__type">{{ item.type.name }}</td>
|
||||
<td class="c-list-item__date-created">{{ formatTime(item.model.persisted, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z</td>
|
||||
<td class="c-list-item__date-updated">{{ formatTime(item.model.modified, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="c-list-item"
|
||||
:class="{ 'is-alias': item.isAlias === true }"
|
||||
@click="navigate"
|
||||
>
|
||||
<td class="c-list-item__name">
|
||||
<a
|
||||
ref="objectLink"
|
||||
:href="objectLink"
|
||||
>
|
||||
<div
|
||||
class="c-list-item__type-icon"
|
||||
:class="item.type.cssClass"
|
||||
></div>
|
||||
<div class="c-list-item__name-value">{{ item.model.name }}</div>
|
||||
</a>
|
||||
</td>
|
||||
<td class="c-list-item__type">
|
||||
{{ item.type.name }}
|
||||
</td>
|
||||
<td class="c-list-item__date-created">
|
||||
{{ formatTime(item.model.persisted, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z
|
||||
</td>
|
||||
<td class="c-list-item__date-updated">
|
||||
{{ formatTime(item.model.modified, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -64,7 +78,12 @@ import objectLink from '../../../ui/mixins/object-link';
|
||||
|
||||
export default {
|
||||
mixins: [contextMenuGesture, objectLink],
|
||||
props: ['item'],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatTime(timestamp, format) {
|
||||
return moment(timestamp).format(format);
|
||||
|
@ -1,55 +1,64 @@
|
||||
<template>
|
||||
<div class="c-table c-table--sortable c-list-view">
|
||||
<table class="c-table__body">
|
||||
<thead class="c-table__header">
|
||||
<tr>
|
||||
<th class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'model.name',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('model.name', true)">
|
||||
Name
|
||||
</th>
|
||||
<th class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'type.name',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('type.name', true)">
|
||||
Type
|
||||
</th>
|
||||
<th class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'model.persisted',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('model.persisted', false)">
|
||||
Created Date
|
||||
</th>
|
||||
<th class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'model.modified',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('model.modified', false)">
|
||||
Updated Date
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<list-item v-for="item in sortedItems"
|
||||
:key="item.objectKeyString"
|
||||
:item="item"
|
||||
:object-path="item.objectPath">
|
||||
</list-item>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="c-table c-table--sortable c-list-view">
|
||||
<table class="c-table__body">
|
||||
<thead class="c-table__header">
|
||||
<tr>
|
||||
<th
|
||||
class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'model.name',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('model.name', true)"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'type.name',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('type.name', true)"
|
||||
>
|
||||
Type
|
||||
</th>
|
||||
<th
|
||||
class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'model.persisted',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('model.persisted', false)"
|
||||
>
|
||||
Created Date
|
||||
</th>
|
||||
<th
|
||||
class="is-sortable"
|
||||
:class="{
|
||||
'is-sorting': sortBy === 'model.modified',
|
||||
'asc': ascending,
|
||||
'desc': !ascending
|
||||
}"
|
||||
@click="sort('model.modified', false)"
|
||||
>
|
||||
Updated Date
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<list-item
|
||||
v-for="item in sortedItems"
|
||||
:key="item.objectKeyString"
|
||||
:item="item"
|
||||
:object-path="item.objectPath"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -93,8 +102,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import lodash from 'lodash';
|
||||
import compositionLoader from './composition-loader';
|
||||
import ListItem from './ListItem.vue';
|
||||
|
||||
@ -120,7 +127,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
sortedItems () {
|
||||
sortedItems() {
|
||||
let sortedItems = _.sortBy(this.items, this.sortBy);
|
||||
if (!this.ascending) {
|
||||
sortedItems = sortedItems.reverse();
|
||||
|
@ -20,21 +20,30 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-about c-about--licenses">
|
||||
<h1>Open MCT Third Party Licenses</h1>
|
||||
<p>This software includes components released under the following licenses:</p>
|
||||
<div v-for="(pkg, key) in packages" :key="key" class="c-license">
|
||||
<h2 class="c-license__name">{{key}}</h2>
|
||||
<div class="c-license__details">
|
||||
<span class="c-license__author"><em>Author</em> {{pkg.publisher}}</span> |
|
||||
<span class="c-license__license"><em>License(s)</em> {{pkg.licenses}}</span> |
|
||||
<span class="c-license__repo"><em>Repository</em> <a :href="pkg.repository" target="_blank">{{pkg.repository}}</a></span>
|
||||
</div>
|
||||
<div class="c-license__text">
|
||||
<p>{{pkg.licenseText}}</p>
|
||||
</div>
|
||||
<div class="c-about c-about--licenses">
|
||||
<h1>Open MCT Third Party Licenses</h1>
|
||||
<p>This software includes components released under the following licenses:</p>
|
||||
<div
|
||||
v-for="(pkg, key) in packages"
|
||||
:key="key"
|
||||
class="c-license"
|
||||
>
|
||||
<h2 class="c-license__name">
|
||||
{{ key }}
|
||||
</h2>
|
||||
<div class="c-license__details">
|
||||
<span class="c-license__author"><em>Author</em> {{ pkg.publisher }}</span> |
|
||||
<span class="c-license__license"><em>License(s)</em> {{ pkg.licenses }}</span> |
|
||||
<span class="c-license__repo"><em>Repository</em> <a
|
||||
:href="pkg.repository"
|
||||
target="_blank"
|
||||
>{{ pkg.repository }}</a></span>
|
||||
</div>
|
||||
<div class="c-license__text">
|
||||
<p>{{ pkg.licenseText }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="sass">
|
||||
</style>
|
||||
@ -49,4 +58,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -141,7 +141,6 @@ function (
|
||||
|
||||
var self = this,
|
||||
snapshot = new Vue({
|
||||
template: SnapshotTemplate,
|
||||
data: function () {
|
||||
return {
|
||||
embed: self.embed
|
||||
@ -151,7 +150,8 @@ function (
|
||||
formatTime: self.formatTime,
|
||||
annotateSnapshot: annotateSnapshot(self.openmct),
|
||||
findInArray: self.findInArray
|
||||
}
|
||||
},
|
||||
template: SnapshotTemplate
|
||||
});
|
||||
|
||||
var snapshotOverlay = this.openmct.overlays.overlay({
|
||||
|
@ -84,7 +84,6 @@ function (
|
||||
};
|
||||
|
||||
var NotebookVue = Vue.extend({
|
||||
template: NotebookTemplate,
|
||||
provide: {openmct: self.openmct, domainObject: self.domainObject},
|
||||
components: {
|
||||
'notebook-entry': entryComponent,
|
||||
@ -111,7 +110,8 @@ function (
|
||||
newEntry: self.newEntry,
|
||||
filterBySearch: self.filterBySearch,
|
||||
sort: self.sort
|
||||
}
|
||||
},
|
||||
template: NotebookTemplate
|
||||
});
|
||||
|
||||
this.NotebookVue = new NotebookVue();
|
||||
|
@ -1,44 +1,58 @@
|
||||
<template>
|
||||
<div class="c-tabs-view">
|
||||
<div class="c-tabs-view__tabs-holder c-tabs"
|
||||
:class="{
|
||||
'is-dragging': isDragging,
|
||||
'is-mouse-over': allowDrop
|
||||
}">
|
||||
<div class="c-drop-hint"
|
||||
@drop="onDrop"
|
||||
@dragenter="dragenter"
|
||||
@dragleave="dragleave">
|
||||
</div>
|
||||
<div class="c-tabs-view__empty-message"
|
||||
v-if="!tabsList.length > 0">Drag objects here to add them to this view.</div>
|
||||
<button class="c-tabs-view__tab c-tab"
|
||||
v-for="(tab,index) in tabsList"
|
||||
:key="index"
|
||||
:class="[
|
||||
{'is-current': isCurrent(tab)},
|
||||
tab.type.definition.cssClass
|
||||
]"
|
||||
@click="showTab(tab)">
|
||||
<span class="c-button__label">{{tab.domainObject.name}}</span>
|
||||
</button>
|
||||
<div class="c-tabs-view">
|
||||
<div
|
||||
class="c-tabs-view__tabs-holder c-tabs"
|
||||
:class="{
|
||||
'is-dragging': isDragging,
|
||||
'is-mouse-over': allowDrop
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="c-drop-hint"
|
||||
@drop="onDrop"
|
||||
@dragenter="dragenter"
|
||||
@dragleave="dragleave"
|
||||
></div>
|
||||
<div
|
||||
v-if="!tabsList.length > 0"
|
||||
class="c-tabs-view__empty-message"
|
||||
>
|
||||
Drag objects here to add them to this view.
|
||||
</div>
|
||||
<div class="c-tabs-view__object-holder"
|
||||
v-for="(tab, index) in tabsList"
|
||||
<button
|
||||
v-for="(tab,index) in tabsList"
|
||||
:key="index"
|
||||
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}">
|
||||
<div v-if="currentTab"
|
||||
class="c-tabs-view__object-name l-browse-bar__object-name--w"
|
||||
:class="currentTab.type.definition.cssClass">
|
||||
<div class="l-browse-bar__object-name">
|
||||
{{currentTab.domainObject.name}}
|
||||
</div>
|
||||
</div>
|
||||
<object-view class="c-tabs-view__object"
|
||||
:object="tab.domainObject">
|
||||
</object-view>
|
||||
</div>
|
||||
class="c-tabs-view__tab c-tab"
|
||||
:class="[
|
||||
{'is-current': isCurrent(tab)},
|
||||
tab.type.definition.cssClass
|
||||
]"
|
||||
@click="showTab(tab)"
|
||||
>
|
||||
<span class="c-button__label">{{ tab.domainObject.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-for="(tab, index) in tabsList"
|
||||
:key="index"
|
||||
class="c-tabs-view__object-holder"
|
||||
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
|
||||
>
|
||||
<div
|
||||
v-if="currentTab"
|
||||
class="c-tabs-view__object-name l-browse-bar__object-name--w"
|
||||
:class="currentTab.type.definition.cssClass"
|
||||
>
|
||||
<div class="l-browse-bar__object-name">
|
||||
{{ currentTab.domainObject.name }}
|
||||
</div>
|
||||
</div>
|
||||
<object-view
|
||||
class="c-tabs-view__object"
|
||||
:object="tab.domainObject"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -129,6 +143,25 @@ export default {
|
||||
allowDrop: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.composition) {
|
||||
this.composition.on('add', this.addItem);
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.onReorder);
|
||||
this.composition.load();
|
||||
}
|
||||
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.onReorder);
|
||||
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
},
|
||||
methods:{
|
||||
showTab(tab) {
|
||||
this.currentTab = tab;
|
||||
@ -148,7 +181,7 @@ export default {
|
||||
}
|
||||
},
|
||||
removeItem(identifier) {
|
||||
let pos = this.tabsList.findIndex(tab =>
|
||||
let pos = this.tabsList.findIndex(tab =>
|
||||
tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key
|
||||
),
|
||||
tabToBeRemoved = this.tabsList[pos];
|
||||
@ -169,7 +202,7 @@ export default {
|
||||
onDrop(e) {
|
||||
this.setCurrentTab = true;
|
||||
},
|
||||
dragstart (e) {
|
||||
dragstart(e) {
|
||||
if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
|
||||
this.isDragging = true;
|
||||
}
|
||||
@ -187,25 +220,6 @@ export default {
|
||||
isCurrent(tab) {
|
||||
return _.isEqual(this.currentTab, tab)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.composition) {
|
||||
this.composition.on('add', this.addItem);
|
||||
this.composition.on('remove', this.removeItem);
|
||||
this.composition.on('reorder', this.onReorder);
|
||||
this.composition.load();
|
||||
}
|
||||
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
},
|
||||
destroyed() {
|
||||
this.composition.off('add', this.addItem);
|
||||
this.composition.off('remove', this.removeItem);
|
||||
this.composition.off('reorder', this.onReorder);
|
||||
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -44,6 +44,7 @@ define([
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
TabsComponent: TabsComponent.default
|
||||
},
|
||||
@ -52,7 +53,6 @@ define([
|
||||
domainObject,
|
||||
composition: openmct.composition.get(domainObject)
|
||||
},
|
||||
el: element,
|
||||
template: '<tabs-component></tabs-component>'
|
||||
});
|
||||
},
|
||||
|
@ -54,11 +54,11 @@ define([
|
||||
openmct,
|
||||
tableConfiguration
|
||||
},
|
||||
el: element,
|
||||
components: {
|
||||
TableConfiguration: TableConfigurationComponent.default
|
||||
},
|
||||
template: '<table-configuration></table-configuration>',
|
||||
el: element
|
||||
template: '<table-configuration></table-configuration>'
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
|
@ -54,20 +54,20 @@ define([
|
||||
return {
|
||||
show: function (element, editMode) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
TableComponent: TableComponent.default
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEditing: editMode
|
||||
}
|
||||
},
|
||||
components: {
|
||||
TableComponent: TableComponent.default
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
table,
|
||||
objectPath
|
||||
},
|
||||
el: element,
|
||||
template: '<table-component :isEditing="isEditing" :enableMarking="true"></table-component>'
|
||||
});
|
||||
},
|
||||
|
@ -1,14 +1,19 @@
|
||||
<template>
|
||||
<div v-if="filterNames.length > 0"
|
||||
:title=title
|
||||
class="c-filter-indication"
|
||||
:class="{ 'c-filter-indication--mixed': hasMixedFilters }">
|
||||
<span class="c-filter-indication__mixed">{{ label }}</span>
|
||||
<span v-for="(name, index) in filterNames"
|
||||
class="c-filter-indication__label">
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="filterNames.length > 0"
|
||||
:title="title"
|
||||
class="c-filter-indication"
|
||||
:class="{ 'c-filter-indication--mixed': hasMixedFilters }"
|
||||
>
|
||||
<span class="c-filter-indication__mixed">{{ label }}</span>
|
||||
<span
|
||||
v-for="(name, index) in filterNames"
|
||||
:key="index"
|
||||
class="c-filter-indication__label"
|
||||
>
|
||||
{{ name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -53,113 +58,113 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const FILTER_INDICATOR_LABEL = 'Filters:';
|
||||
const FILTER_INDICATOR_LABEL_MIXED = 'Mixed Filters:';
|
||||
const FILTER_INDICATOR_TITLE = 'Data filters are being applied to this view.';
|
||||
const FILTER_INDICATOR_TITLE_MIXED = 'A mix of data filter values are being applied to this view.';
|
||||
const USE_GLOBAL = 'useGlobal';
|
||||
const FILTER_INDICATOR_LABEL = 'Filters:';
|
||||
const FILTER_INDICATOR_LABEL_MIXED = 'Mixed Filters:';
|
||||
const FILTER_INDICATOR_TITLE = 'Data filters are being applied to this view.';
|
||||
const FILTER_INDICATOR_TITLE_MIXED = 'A mix of data filter values are being applied to this view.';
|
||||
const USE_GLOBAL = 'useGlobal';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'table'],
|
||||
data() {
|
||||
return {
|
||||
filterNames: [],
|
||||
filteredTelemetry: {}
|
||||
}
|
||||
export default {
|
||||
inject: ['openmct', 'table'],
|
||||
data() {
|
||||
return {
|
||||
filterNames: [],
|
||||
filteredTelemetry: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasMixedFilters() {
|
||||
let filtersToCompare = _.omit(this.filteredTelemetry[Object.keys(this.filteredTelemetry)[0]], [USE_GLOBAL]);
|
||||
return Object.values(this.filteredTelemetry).some(filters => {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
hasMixedFilters() {
|
||||
let filtersToCompare = _.omit(this.filteredTelemetry[Object.keys(this.filteredTelemetry)[0]], [USE_GLOBAL]);
|
||||
return Object.values(this.filteredTelemetry).some(filters => {
|
||||
return !_.isEqual(filtersToCompare, _.omit(filters, [USE_GLOBAL]));
|
||||
});
|
||||
},
|
||||
label() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_LABEL_MIXED;
|
||||
} else {
|
||||
return FILTER_INDICATOR_LABEL;
|
||||
}
|
||||
},
|
||||
title() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_TITLE_MIXED;
|
||||
} else {
|
||||
return FILTER_INDICATOR_TITLE;
|
||||
}
|
||||
label() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_LABEL_MIXED;
|
||||
} else {
|
||||
return FILTER_INDICATOR_LABEL;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setFilterNames() {
|
||||
let names = [];
|
||||
let composition = this.openmct.composition.get(this.table.configuration.domainObject);
|
||||
title() {
|
||||
if (this.hasMixedFilters) {
|
||||
return FILTER_INDICATOR_TITLE_MIXED;
|
||||
} else {
|
||||
return FILTER_INDICATOR_TITLE;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let filters = this.table.configuration.getConfiguration().filters || {};
|
||||
this.table.configuration.on('change', this.handleConfigurationChanges);
|
||||
this.updateFilters(filters);
|
||||
},
|
||||
destroyed() {
|
||||
this.table.configuration.off('change', this.handleConfigurationChanges);
|
||||
},
|
||||
methods: {
|
||||
setFilterNames() {
|
||||
let names = [];
|
||||
let composition = this.openmct.composition.get(this.table.configuration.domainObject);
|
||||
|
||||
composition && composition.load().then((domainObjects) => {
|
||||
domainObjects.forEach(telemetryObject => {
|
||||
let keyString= this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
let filters = this.filteredTelemetry[keyString];
|
||||
composition && composition.load().then((domainObjects) => {
|
||||
domainObjects.forEach(telemetryObject => {
|
||||
let keyString= this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let metadataValues = this.openmct.telemetry.getMetadata(telemetryObject).values();
|
||||
let filters = this.filteredTelemetry[keyString];
|
||||
|
||||
if (filters !== undefined) {
|
||||
names.push(this.getFilterNamesFromMetadata(filters, metadataValues));
|
||||
}
|
||||
});
|
||||
|
||||
names = _.flatten(names);
|
||||
this.filterNames = names.length === 0 ? names : Array.from(new Set(names));
|
||||
});
|
||||
},
|
||||
getFilterNamesFromMetadata(filters, metadataValues) {
|
||||
let filterNames = [];
|
||||
filters = _.omit(filters, [USE_GLOBAL]);
|
||||
|
||||
Object.keys(filters).forEach(key => {
|
||||
if (!_.isEmpty(filters[key])) {
|
||||
metadataValues.forEach(metadatum => {
|
||||
if (key === metadatum.key) {
|
||||
if (typeof metadatum.filters[0] === "object") {
|
||||
filterNames.push(this.getFilterLabels(filters[key], metadatum));
|
||||
} else {
|
||||
filterNames.push(metadatum.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (filters !== undefined) {
|
||||
names.push(this.getFilterNamesFromMetadata(filters, metadataValues));
|
||||
}
|
||||
});
|
||||
|
||||
return _.flatten(filterNames);
|
||||
},
|
||||
getFilterLabels(filterObject, metadatum, ) {
|
||||
let filterLabels = [];
|
||||
Object.values(filterObject).forEach(comparator => {
|
||||
comparator.forEach(filterValue => {
|
||||
metadatum.filters[0].possibleValues.forEach(option => {
|
||||
if (option.value === filterValue) {
|
||||
filterLabels.push(option.label);
|
||||
names = _.flatten(names);
|
||||
this.filterNames = names.length === 0 ? names : Array.from(new Set(names));
|
||||
});
|
||||
},
|
||||
getFilterNamesFromMetadata(filters, metadataValues) {
|
||||
let filterNames = [];
|
||||
filters = _.omit(filters, [USE_GLOBAL]);
|
||||
|
||||
Object.keys(filters).forEach(key => {
|
||||
if (!_.isEmpty(filters[key])) {
|
||||
metadataValues.forEach(metadatum => {
|
||||
if (key === metadatum.key) {
|
||||
if (typeof metadatum.filters[0] === "object") {
|
||||
filterNames.push(this.getFilterLabels(filters[key], metadatum));
|
||||
} else {
|
||||
filterNames.push(metadatum.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return _.flatten(filterNames);
|
||||
},
|
||||
getFilterLabels(filterObject, metadatum,) {
|
||||
let filterLabels = [];
|
||||
Object.values(filterObject).forEach(comparator => {
|
||||
comparator.forEach(filterValue => {
|
||||
metadatum.filters[0].possibleValues.forEach(option => {
|
||||
if (option.value === filterValue) {
|
||||
filterLabels.push(option.label);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return filterLabels;
|
||||
},
|
||||
handleConfigurationChanges(configuration) {
|
||||
if (!_.eq(this.filteredTelemetry, configuration.filters)) {
|
||||
this.updateFilters(configuration.filters || {});
|
||||
}
|
||||
},
|
||||
updateFilters(filters) {
|
||||
this.filteredTelemetry = JSON.parse(JSON.stringify(filters));
|
||||
this.setFilterNames();
|
||||
return filterLabels;
|
||||
},
|
||||
handleConfigurationChanges(configuration) {
|
||||
if (!_.eq(this.filteredTelemetry, configuration.filters)) {
|
||||
this.updateFilters(configuration.filters || {});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let filters = this.table.configuration.getConfiguration().filters || {};
|
||||
this.table.configuration.on('change', this.handleConfigurationChanges);
|
||||
this.updateFilters(filters);
|
||||
},
|
||||
destroyed() {
|
||||
this.table.configuration.off('change', this.handleConfigurationChanges);
|
||||
updateFilters(filters) {
|
||||
this.filteredTelemetry = JSON.parse(JSON.stringify(filters));
|
||||
this.setFilterNames();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,7 +20,12 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<td @click="selectCell($event.currentTarget, columnKey)" :title="formattedValue">{{formattedValue}}</td>
|
||||
<td
|
||||
:title="formattedValue"
|
||||
@click="selectCell($event.currentTarget, columnKey)"
|
||||
>
|
||||
{{ formattedValue }}
|
||||
</td>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
@ -32,11 +37,20 @@ export default {
|
||||
},
|
||||
columnKey: {
|
||||
type: String,
|
||||
require: true
|
||||
required: true
|
||||
},
|
||||
objectPath: {
|
||||
type: Array,
|
||||
require: false
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedValue() {
|
||||
return this.row.getFormattedValue(this.columnKey);
|
||||
},
|
||||
isSelectable() {
|
||||
let column = this.row.columns[this.columnKey];
|
||||
return column && column.selectable;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -57,15 +71,6 @@ export default {
|
||||
}], false);
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
formattedValue() {
|
||||
return this.row.getFormattedValue(this.columnKey);
|
||||
},
|
||||
isSelectable() {
|
||||
let column = this.row.columns[this.columnKey];
|
||||
return column && column.selectable;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<th
|
||||
<th
|
||||
:style="{ width: columnWidth + 'px', 'max-width': columnWidth + 'px'}"
|
||||
:draggable="isEditing"
|
||||
@mouseup="sort"
|
||||
@ -29,30 +29,49 @@
|
||||
drop: columnMoveEnd,
|
||||
dragleave: hideDropTarget,
|
||||
dragover: dragOverColumn
|
||||
} : {}">
|
||||
<div class="c-telemetry-table__headers__content" :class="[
|
||||
isSortable ? 'is-sortable' : '',
|
||||
isSortable && sortOptions.key === headerKey ? 'is-sorting' : '',
|
||||
isSortable && sortOptions.direction].join(' ')">
|
||||
<div class="c-telemetry-table__resize-hitarea"
|
||||
@mousedown="resizeColumnStart"
|
||||
></div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
} : {}"
|
||||
>
|
||||
<div
|
||||
class="c-telemetry-table__headers__content"
|
||||
:class="[
|
||||
isSortable ? 'is-sortable' : '',
|
||||
isSortable && sortOptions.key === headerKey ? 'is-sorting' : '',
|
||||
isSortable && sortOptions.direction].join(' ')"
|
||||
>
|
||||
<div
|
||||
class="c-telemetry-table__resize-hitarea"
|
||||
@mousedown="resizeColumnStart"
|
||||
></div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</th>
|
||||
</template>
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
const MOVE_COLUMN_DT_TYPE = 'movecolumnfromindex';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
headerKey: String,
|
||||
headerIndex: Number,
|
||||
isHeaderTitle: Boolean,
|
||||
sortOptions: Object,
|
||||
columnWidth: Number,
|
||||
headerKey: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
headerIndex: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
isHeaderTitle: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
sortOptions: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
columnWidth: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
hotzone: Boolean,
|
||||
isEditing: Boolean
|
||||
},
|
||||
@ -71,13 +90,13 @@ export default {
|
||||
event.preventDefault();
|
||||
},
|
||||
resizeColumnEnd(event) {
|
||||
this.resizeStartX = undefined;
|
||||
this.resizeStartWidth = undefined;
|
||||
document.removeEventListener('mousemove', this.resizeColumn);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.resizeStartX = undefined;
|
||||
this.resizeStartWidth = undefined;
|
||||
document.removeEventListener('mousemove', this.resizeColumn);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.$emit('resizeColumnEnd');
|
||||
this.$emit('resizeColumnEnd');
|
||||
},
|
||||
resizeColumn(event) {
|
||||
let delta = event.clientX - this.resizeStartX;
|
||||
@ -94,7 +113,7 @@ export default {
|
||||
return [...event.dataTransfer.types].includes(MOVE_COLUMN_DT_TYPE);
|
||||
},
|
||||
dragOverColumn(event) {
|
||||
if (this.isColumnMoveEvent(event)){
|
||||
if (this.isColumnMoveEvent(event)) {
|
||||
event.preventDefault();
|
||||
this.updateDropOffset(event.currentTarget, event.clientX);
|
||||
} else {
|
||||
@ -114,19 +133,19 @@ export default {
|
||||
this.$emit('dropTargetOffsetChanged', dropOffsetLeft);
|
||||
this.$emit('dropTargetActive', true);
|
||||
},
|
||||
hideDropTarget(){
|
||||
hideDropTarget() {
|
||||
this.$emit('dropTargetActive', false);
|
||||
},
|
||||
columnMoveEnd(event){
|
||||
if (this.isColumnMoveEvent(event)){
|
||||
columnMoveEnd(event) {
|
||||
if (this.isColumnMoveEvent(event)) {
|
||||
let toIndex = this.headerIndex;
|
||||
let fromIndex = event.dataTransfer.getData(MOVE_COLUMN_DT_TYPE);
|
||||
if (event.offsetX < event.target.offsetWidth / 2) {
|
||||
if (toIndex > fromIndex){
|
||||
if (toIndex > fromIndex) {
|
||||
toIndex--;
|
||||
}
|
||||
} else {
|
||||
if (toIndex < fromIndex){
|
||||
if (toIndex < fromIndex) {
|
||||
toIndex++;
|
||||
}
|
||||
}
|
||||
@ -140,4 +159,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
@ -1,18 +1,50 @@
|
||||
<template>
|
||||
<div class="c-properties">
|
||||
<template v-if="isEditing">
|
||||
<div class="c-properties__header">Table Column Size</div>
|
||||
<div class="c-properties__header">
|
||||
Table Column Size
|
||||
</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label" title="Auto-size table"><label for="AutoSizeControl">Auto-size</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" id="AutoSizeControl" :checked="configuration.autosize !== false" @change="toggleAutosize()"></div>
|
||||
<div
|
||||
class="c-properties__label"
|
||||
title="Auto-size table"
|
||||
>
|
||||
<label for="AutoSizeControl">Auto-size</label>
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
<input
|
||||
id="AutoSizeControl"
|
||||
type="checkbox"
|
||||
:checked="configuration.autosize !== false"
|
||||
@change="toggleAutosize()"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-properties__header">Table Column Visibility</div>
|
||||
<div class="c-properties__header">
|
||||
Table Column Visibility
|
||||
</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row" v-for="(title, key) in headers">
|
||||
<div class="c-properties__label" title="Show or hide column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
|
||||
<li
|
||||
v-for="(title, key) in headers"
|
||||
:key="key"
|
||||
class="c-properties__row"
|
||||
>
|
||||
<div
|
||||
class="c-properties__label"
|
||||
title="Show or hide column"
|
||||
>
|
||||
<label :for="key + 'ColumnControl'">{{ title }}</label>
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
<input
|
||||
:id="key + 'ColumnControl'"
|
||||
type="checkbox"
|
||||
:checked="configuration.hiddenColumns[key] !== true"
|
||||
@change="toggleColumn(key)"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@ -34,6 +66,28 @@ export default {
|
||||
configuration: this.tableConfiguration.getConfiguration()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.unlisteners = [];
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
let compositionCollection = this.openmct.composition.get(this.tableConfiguration.domainObject);
|
||||
|
||||
compositionCollection.load()
|
||||
.then((composition) => {
|
||||
this.addColumnsForAllObjects(composition);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
|
||||
compositionCollection.on('add', this.addObject);
|
||||
this.unlisteners.push(compositionCollection.off.bind(compositionCollection, 'add', this.addObject));
|
||||
|
||||
compositionCollection.on('remove', this.removeObject);
|
||||
this.unlisteners.push(compositionCollection.off.bind(compositionCollection, 'remove', this.removeObject));
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
this.tableConfiguration.destroy();
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.unlisteners.forEach((unlisten) => unlisten());
|
||||
},
|
||||
methods: {
|
||||
updateHeaders(headers) {
|
||||
this.headers = headers;
|
||||
@ -45,7 +99,7 @@ export default {
|
||||
this.tableConfiguration.updateConfiguration(this.configuration);
|
||||
},
|
||||
addObject(domainObject) {
|
||||
this.addColumnsForObject(domainObject, true);
|
||||
this.addColumnsForObject(domainObject, true);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
},
|
||||
removeObject(objectIdentifier) {
|
||||
@ -70,28 +124,6 @@ export default {
|
||||
this.tableConfiguration.addSingleColumnForObject(telemetryObject, column);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.unlisteners = [];
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
let compositionCollection = this.openmct.composition.get(this.tableConfiguration.domainObject);
|
||||
|
||||
compositionCollection.load()
|
||||
.then((composition) => {
|
||||
this.addColumnsForAllObjects(composition);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
|
||||
compositionCollection.on('add', this.addObject);
|
||||
this.unlisteners.push(compositionCollection.off.bind(compositionCollection, 'add', this.addObject));
|
||||
|
||||
compositionCollection.on('remove', this.removeObject);
|
||||
this.unlisteners.push(compositionCollection.off.bind(compositionCollection, 'remove', this.removeObject));
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
this.tableConfiguration.destroy();
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.unlisteners.forEach((unlisten) => unlisten());
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,22 +20,25 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<tr :style="{ top: rowTop }"
|
||||
<tr
|
||||
:style="{ top: rowTop }"
|
||||
class="noselect"
|
||||
:class="[
|
||||
rowClass,
|
||||
{'is-selected': marked}
|
||||
]"
|
||||
v-on="listeners">
|
||||
<component v-for="(title, key) in headers"
|
||||
:key="key"
|
||||
v-on="listeners"
|
||||
>
|
||||
<component
|
||||
:is="componentList[key]"
|
||||
:columnKey="key"
|
||||
v-for="(title, key) in headers"
|
||||
:key="key"
|
||||
:column-key="key"
|
||||
:style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
|
||||
:class="[cellLimitClasses[key], selectableColumns[key] ? 'is-selectable' : '']"
|
||||
:objectPath="objectPath"
|
||||
:row="row">
|
||||
</component>
|
||||
:object-path="objectPath"
|
||||
:row="row"
|
||||
/>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
@ -55,21 +58,9 @@
|
||||
import TableCell from './table-cell.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'objectPath'],
|
||||
data: function () {
|
||||
return {
|
||||
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
|
||||
rowClass: this.row.getRowClass(),
|
||||
cellLimitClasses: this.row.getCellLimitClasses(),
|
||||
componentList: Object.keys(this.headers).reduce((components, header) => {
|
||||
components[header] = this.row.getCellComponentName(header) || 'table-cell';
|
||||
return components
|
||||
}, {}),
|
||||
selectableColumns : Object.keys(this.row.columns).reduce((selectable, columnKeys) => {
|
||||
selectable[columnKeys] = this.row.columns[columnKeys].selectable;
|
||||
return selectable;
|
||||
}, {})
|
||||
}
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
TableCell
|
||||
},
|
||||
props: {
|
||||
headers: {
|
||||
@ -86,7 +77,7 @@ export default {
|
||||
},
|
||||
objectPath: {
|
||||
type: Array,
|
||||
required: false
|
||||
required: true
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number,
|
||||
@ -109,6 +100,42 @@ export default {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
|
||||
rowClass: this.row.getRowClass(),
|
||||
cellLimitClasses: this.row.getCellLimitClasses(),
|
||||
componentList: Object.keys(this.headers).reduce((components, header) => {
|
||||
components[header] = this.row.getCellComponentName(header) || 'table-cell';
|
||||
return components
|
||||
}, {}),
|
||||
selectableColumns : Object.keys(this.row.columns).reduce((selectable, columnKeys) => {
|
||||
selectable[columnKeys] = this.row.columns[columnKeys].selectable;
|
||||
return selectable;
|
||||
}, {})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
listeners() {
|
||||
let listenersObject = {
|
||||
click: this.markRow
|
||||
}
|
||||
|
||||
if (this.row.getContextMenuActions().length) {
|
||||
listenersObject.contextmenu = this.showContextMenu;
|
||||
}
|
||||
|
||||
return listenersObject;
|
||||
}
|
||||
},
|
||||
// TODO: use computed properties
|
||||
watch: {
|
||||
rowOffset: 'calculateRowTop',
|
||||
row: {
|
||||
handler: 'formatRow',
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
calculateRowTop: function (rowOffset) {
|
||||
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
|
||||
@ -136,7 +163,7 @@ export default {
|
||||
},
|
||||
selectCell(element, columnKey) {
|
||||
if (this.selectableColumns[columnKey]) {
|
||||
//TODO: This is a hack. Cannot get parent this way.
|
||||
//TODO: This is a hack. Cannot get parent this way.
|
||||
this.openmct.selection.select([{
|
||||
element: element,
|
||||
context: {
|
||||
@ -153,7 +180,7 @@ export default {
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
showContextMenu: function (event) {
|
||||
showContextMenu: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.openmct.objects.get(this.row.objectKeyString).then((domainObject) => {
|
||||
@ -163,30 +190,6 @@ export default {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(contextualObjectPath, event.x, event.y, this.row.getContextMenuActions());
|
||||
});
|
||||
}
|
||||
},
|
||||
// TODO: use computed properties
|
||||
watch: {
|
||||
rowOffset: 'calculateRowTop',
|
||||
row: {
|
||||
handler: 'formatRow',
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
TableCell
|
||||
},
|
||||
computed: {
|
||||
listeners() {
|
||||
let listenersObject = {
|
||||
click: this.markRow
|
||||
}
|
||||
|
||||
if (this.row.getContextMenuActions().length) {
|
||||
listenersObject.contextmenu = this.showContextMenu;
|
||||
}
|
||||
|
||||
return listenersObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -22,131 +22,178 @@
|
||||
<template>
|
||||
<div class="c-table-wrapper">
|
||||
<div class="c-table-control-bar c-control-bar">
|
||||
<button class="c-button icon-download labeled"
|
||||
v-if="allowExport"
|
||||
v-on:click="exportAllDataAsCSV()"
|
||||
title="Export This View's Data">
|
||||
<button
|
||||
v-if="allowExport"
|
||||
class="c-button icon-download labeled"
|
||||
title="Export This View's Data"
|
||||
@click="exportAllDataAsCSV()"
|
||||
>
|
||||
<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">
|
||||
<button
|
||||
v-if="allowExport"
|
||||
v-show="markedRows.length"
|
||||
class="c-button icon-download labeled"
|
||||
title="Export Marked Rows As CSV"
|
||||
@click="exportMarkedDataAsCSV()"
|
||||
>
|
||||
<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">
|
||||
<button
|
||||
v-show="markedRows.length"
|
||||
class="c-button icon-x labeled"
|
||||
title="Unmark All Rows"
|
||||
@click="unmarkAllRows()"
|
||||
>
|
||||
<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>
|
||||
<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'"
|
||||
:title="paused ? 'Continue Data Flow' : 'Pause Data Flow'"
|
||||
@click="togglePauseByButton()"
|
||||
>
|
||||
<span class="c-button__label">
|
||||
{{ paused ? 'Play' : 'Pause' }}
|
||||
</span>
|
||||
</button>
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
|
||||
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
:class="{
|
||||
'loading': loading,
|
||||
'paused' : paused
|
||||
}">
|
||||
<div
|
||||
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
:class="{
|
||||
'loading': loading,
|
||||
'paused' : paused
|
||||
}"
|
||||
>
|
||||
<div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}"><slot></slot></div>
|
||||
|
||||
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
||||
<div
|
||||
v-if="isDropTargetActive"
|
||||
class="c-telemetry-table__drop-target"
|
||||
:style="dropTargetStyle"
|
||||
></div>
|
||||
<!-- Headers table -->
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
||||
<div
|
||||
ref="headersTable"
|
||||
class="c-telemetry-table__headers-w js-table__headers-w"
|
||||
:style="{ 'max-width': widthWithScroll}"
|
||||
>
|
||||
<table class="c-table__headers c-telemetry-table__headers">
|
||||
<thead>
|
||||
<tr class="c-telemetry-table__headers__labels">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
:header-key="key"
|
||||
:header-index="headerIndex"
|
||||
:column-width="columnWidths[key]"
|
||||
:sort-options="sortOptions"
|
||||
:is-editing="isEditing"
|
||||
@sort="allowSorting && sortBy(key)"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:sortOptions="sortOptions"
|
||||
:isEditing="isEditing"
|
||||
><span class="c-telemetry-table__headers__label">{{title}}</span>
|
||||
>
|
||||
<span class="c-telemetry-table__headers__label">{{ title }}</span>
|
||||
</table-column-header>
|
||||
</tr>
|
||||
<tr v-if="allowFiltering" class="c-telemetry-table__headers__filter">
|
||||
<tr
|
||||
v-if="allowFiltering"
|
||||
class="c-telemetry-table__headers__filter"
|
||||
>
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
:headerKey="key"
|
||||
:headerIndex="headerIndex"
|
||||
:header-key="key"
|
||||
:header-index="headerIndex"
|
||||
:column-width="columnWidths[key]"
|
||||
:is-editing="isEditing"
|
||||
@resizeColumn="resizeColumn"
|
||||
@dropTargetOffsetChanged="setDropTargetOffset"
|
||||
@dropTargetActive="dropTargetActive"
|
||||
@reorderColumn="reorderColumn"
|
||||
@resizeColumnEnd="updateConfiguredColumnWidths"
|
||||
:columnWidth="columnWidths[key]"
|
||||
:isEditing="isEditing"
|
||||
>
|
||||
<search class="c-table__search"
|
||||
>
|
||||
<search
|
||||
v-model="filters[key]"
|
||||
v-on:input="filterChanged(key)"
|
||||
v-on:clear="clearFilter(key)" />
|
||||
class="c-table__search"
|
||||
@input="filterChanged(key)"
|
||||
@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'}">
|
||||
<div
|
||||
class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w"
|
||||
:style="{ 'max-width': widthWithScroll}"
|
||||
@scroll="scroll"
|
||||
>
|
||||
<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"
|
||||
<telemetry-table-row
|
||||
v-for="(row, rowIndex) in visibleRows"
|
||||
:key="rowIndex"
|
||||
:headers="headers"
|
||||
:columnWidths="columnWidths"
|
||||
:rowIndex="rowIndex"
|
||||
:objectPath="objectPath"
|
||||
:rowOffset="rowOffset"
|
||||
:rowHeight="rowHeight"
|
||||
:column-widths="columnWidths"
|
||||
:row-index="rowIndex"
|
||||
:object-path="objectPath"
|
||||
:row-offset="rowOffset"
|
||||
:row-height="rowHeight"
|
||||
:row="row"
|
||||
:marked="row.marked"
|
||||
@mark="markRow"
|
||||
@unmark="unmarkRow"
|
||||
@markMultipleConcurrent="markMultipleConcurrentRows">
|
||||
</telemetry-table-row>
|
||||
@markMultipleConcurrent="markMultipleConcurrentRows"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Sizing table -->
|
||||
<table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
|
||||
<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>
|
||||
<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"
|
||||
<telemetry-table-row
|
||||
v-for="(sizingRowData, objectKeyString) in sizingRows"
|
||||
:key="objectKeyString"
|
||||
:headers="headers"
|
||||
:columnWidths="configuredColumnWidths"
|
||||
:row="sizingRowData">
|
||||
</telemetry-table-row>
|
||||
:column-widths="configuredColumnWidths"
|
||||
:row="sizingRowData"
|
||||
:object-path="objectPath"
|
||||
/>
|
||||
</table>
|
||||
<telemetry-filter-indicator></telemetry-filter-indicator>
|
||||
<telemetry-filter-indicator />
|
||||
</div>
|
||||
</div><!-- closes c-table-wrapper -->
|
||||
</template>
|
||||
@ -343,9 +390,6 @@ const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 100;
|
||||
const RESIZE_HOT_ZONE = 10;
|
||||
const MOVE_TRIGGER_WAIT = 500;
|
||||
const VERTICAL_SCROLL_WIDTH = 30;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -440,6 +484,59 @@ export default {
|
||||
return style;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
this.csvExporter = new CSVExporter();
|
||||
this.rowsAdded = _.throttle(this.rowsAdded, 200);
|
||||
this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
|
||||
this.scroll = _.throttle(this.scroll, 100);
|
||||
|
||||
this.table.on('object-added', this.addObject);
|
||||
this.table.on('object-removed', this.removeObject);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
this.table.on('refresh', this.clearRowsAndRerender);
|
||||
|
||||
this.table.filteredRows.on('add', this.rowsAdded);
|
||||
this.table.filteredRows.on('remove', this.rowsRemoved);
|
||||
this.table.filteredRows.on('sort', this.updateVisibleRows);
|
||||
this.table.filteredRows.on('filter', this.updateVisibleRows);
|
||||
|
||||
//Default sort
|
||||
this.sortOptions = this.table.filteredRows.sortBy();
|
||||
this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w');
|
||||
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
|
||||
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
|
||||
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
|
||||
|
||||
this.table.configuration.on('change', this.updateConfiguration);
|
||||
|
||||
this.calculateTableSize();
|
||||
this.pollForResize();
|
||||
this.calculateScrollbarWidth();
|
||||
|
||||
this.table.initialize();
|
||||
},
|
||||
destroyed() {
|
||||
this.table.off('object-added', this.addObject);
|
||||
this.table.off('object-removed', this.removeObject);
|
||||
this.table.off('outstanding-requests', this.outstandingRequests);
|
||||
this.table.off('refresh', this.clearRowsAndRerender);
|
||||
|
||||
this.table.filteredRows.off('add', this.rowsAdded);
|
||||
this.table.filteredRows.off('remove', this.rowsRemoved);
|
||||
this.table.filteredRows.off('sort', this.updateVisibleRows);
|
||||
this.table.filteredRows.off('filter', this.updateVisibleRows);
|
||||
|
||||
this.table.configuration.off('change', this.updateConfiguration);
|
||||
|
||||
clearInterval(this.resizePollHandle);
|
||||
|
||||
this.table.configuration.destroy();
|
||||
|
||||
this.table.destroy();
|
||||
},
|
||||
methods: {
|
||||
updateVisibleRows() {
|
||||
if (!this.updatingView) {
|
||||
@ -527,7 +624,7 @@ export default {
|
||||
}
|
||||
this.table.sortBy(this.sortOptions);
|
||||
},
|
||||
scroll () {
|
||||
scroll() {
|
||||
this.updateVisibleRows();
|
||||
this.synchronizeScrollX();
|
||||
|
||||
@ -557,7 +654,7 @@ export default {
|
||||
this.table.filteredRows.setColumnFilter(columnKey, '');
|
||||
this.setHeight();
|
||||
},
|
||||
rowsAdded (rows) {
|
||||
rowsAdded(rows) {
|
||||
this.setHeight();
|
||||
|
||||
let sizingRow;
|
||||
@ -578,7 +675,7 @@ export default {
|
||||
|
||||
this.updateVisibleRows();
|
||||
},
|
||||
rowsRemoved (rows) {
|
||||
rowsRemoved(rows) {
|
||||
this.setHeight();
|
||||
this.updateVisibleRows();
|
||||
},
|
||||
@ -588,9 +685,9 @@ export default {
|
||||
setHeight() {
|
||||
let filteredRowsLength = this.table.filteredRows.getRows().length;
|
||||
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
|
||||
// Set element height directly to avoid having to wait for Vue to update DOM
|
||||
// Set element height directly to avoid having to wait for Vue to update DOM
|
||||
// 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(data) {
|
||||
const headerKeys = Object.keys(this.headers);
|
||||
@ -725,7 +822,7 @@ export default {
|
||||
this.markedRows = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
togglePauseByButton() {
|
||||
if (this.paused) {
|
||||
@ -744,7 +841,7 @@ export default {
|
||||
positionInMarkedArray = this.markedRows.indexOf(row);
|
||||
|
||||
row.marked = false;
|
||||
this.markedRows.splice(positionInMarkedArray, 1);
|
||||
this.markedRows.splice(positionInMarkedArray, 1);
|
||||
|
||||
if (this.markedRows.length === 0) {
|
||||
this.unpause();
|
||||
@ -804,73 +901,19 @@ export default {
|
||||
if (lastRowIndex < firstRowIndex) {
|
||||
[firstRowIndex, lastRowIndex] = [lastRowIndex, firstRowIndex];
|
||||
}
|
||||
|
||||
|
||||
let baseRow = this.markedRows[0];
|
||||
|
||||
for (var i = firstRowIndex; i <= lastRowIndex; i++) {
|
||||
let row = allRows[i];
|
||||
row.marked = true;
|
||||
|
||||
if (row !== baseRow){
|
||||
if (row !== baseRow) {
|
||||
this.markedRows.push(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
mounted() {
|
||||
console.log("Table mounted");
|
||||
this.csvExporter = new CSVExporter();
|
||||
this.rowsAdded = _.throttle(this.rowsAdded, 200);
|
||||
this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
|
||||
this.scroll = _.throttle(this.scroll, 100);
|
||||
|
||||
this.table.on('object-added', this.addObject);
|
||||
this.table.on('object-removed', this.removeObject);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
this.table.on('refresh', this.clearRowsAndRerender);
|
||||
|
||||
this.table.filteredRows.on('add', this.rowsAdded);
|
||||
this.table.filteredRows.on('remove', this.rowsRemoved);
|
||||
this.table.filteredRows.on('sort', this.updateVisibleRows);
|
||||
this.table.filteredRows.on('filter', this.updateVisibleRows);
|
||||
|
||||
//Default sort
|
||||
this.sortOptions = this.table.filteredRows.sortBy();
|
||||
this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w');
|
||||
this.contentTable = this.$el.querySelector('.js-telemetry-table__content');
|
||||
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
|
||||
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
|
||||
|
||||
this.table.configuration.on('change', this.updateConfiguration);
|
||||
|
||||
this.calculateTableSize();
|
||||
this.pollForResize();
|
||||
this.calculateScrollbarWidth();
|
||||
|
||||
this.table.initialize();
|
||||
},
|
||||
destroyed() {
|
||||
this.table.off('object-added', this.addObject);
|
||||
this.table.off('object-removed', this.removeObject);
|
||||
this.table.off('outstanding-requests', this.outstandingRequests);
|
||||
this.table.off('refresh', this.clearRowsAndRerender);
|
||||
|
||||
this.table.filteredRows.off('add', this.rowsAdded);
|
||||
this.table.filteredRows.off('remove', this.rowsRemoved);
|
||||
this.table.filteredRows.off('sort', this.updateVisibleRows);
|
||||
this.table.filteredRows.off('filter', this.updateVisibleRows);
|
||||
|
||||
this.table.configuration.off('change', this.updateConfiguration);
|
||||
|
||||
clearInterval(this.resizePollHandle);
|
||||
|
||||
this.table.configuration.destroy();
|
||||
|
||||
this.table.destroy();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,87 +20,123 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-conductor"
|
||||
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']">
|
||||
<form class="u-contents" ref="conductorForm" @submit.prevent="updateTimeFromConductor">
|
||||
<div class="c-conductor__time-bounds">
|
||||
<button class="c-input--submit" type="submit" ref="submitButton"></button>
|
||||
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
|
||||
<div
|
||||
class="c-conductor"
|
||||
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']"
|
||||
>
|
||||
<form
|
||||
ref="conductorForm"
|
||||
class="u-contents"
|
||||
@submit.prevent="updateTimeFromConductor"
|
||||
>
|
||||
<div class="c-conductor__time-bounds">
|
||||
<button
|
||||
ref="submitButton"
|
||||
class="c-input--submit"
|
||||
type="submit"
|
||||
></button>
|
||||
<ConductorModeIcon class="c-conductor__mode-icon" />
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
|
||||
v-if="isFixed">
|
||||
<!-- Fixed start -->
|
||||
<div class="c-conductor__start-fixed__label">Start</div>
|
||||
<input class="c-input--datetime"
|
||||
type="text" autocorrect="off" spellcheck="false"
|
||||
ref="startDate"
|
||||
v-model="formattedBounds.start"
|
||||
@change="validateAllBounds(); submitForm()" />
|
||||
<date-picker
|
||||
v-if="isFixed && isUTCBased"
|
||||
:default-date-time="formattedBounds.start"
|
||||
:formatter="timeFormatter"
|
||||
@date-selected="startDateSelected"></date-picker>
|
||||
<div
|
||||
v-if="isFixed"
|
||||
class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
|
||||
>
|
||||
<!-- Fixed start -->
|
||||
<div class="c-conductor__start-fixed__label">
|
||||
Start
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
|
||||
v-if="!isFixed">
|
||||
<!-- RT start -->
|
||||
<div class="c-direction-indicator icon-minus"></div>
|
||||
<input class="c-input--hrs-min-sec"
|
||||
type="text" autocorrect="off"
|
||||
ref="startOffset"
|
||||
spellcheck="false"
|
||||
v-model="offsets.start"
|
||||
@change="validateAllOffsets(); submitForm()">
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
||||
<!-- Fixed end and RT 'last update' display -->
|
||||
<div class="c-conductor__end-fixed__label">
|
||||
{{ isFixed ? 'End' : 'Updated' }}
|
||||
</div>
|
||||
<input class="c-input--datetime"
|
||||
type="text" autocorrect="off" spellcheck="false"
|
||||
v-model="formattedBounds.end"
|
||||
:disabled="!isFixed"
|
||||
ref="endDate"
|
||||
@change="validateAllBounds(); submitForm()">
|
||||
<date-picker
|
||||
v-if="isFixed && isUTCBased"
|
||||
class="c-ctrl-wrapper--menus-left"
|
||||
:default-date-time="formattedBounds.end"
|
||||
:formatter="timeFormatter"
|
||||
@date-selected="endDateSelected"></date-picker>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-delta"
|
||||
v-if="!isFixed">
|
||||
<!-- RT end -->
|
||||
<div class="c-direction-indicator icon-plus"></div>
|
||||
<input class="c-input--hrs-min-sec"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
ref="endOffset"
|
||||
v-model="offsets.end"
|
||||
@change="validateAllOffsets(); submitForm()">
|
||||
</div>
|
||||
|
||||
<conductor-axis
|
||||
class="c-conductor__ticks"
|
||||
:bounds="rawBounds"
|
||||
@panAxis="setViewFromBounds"></conductor-axis>
|
||||
|
||||
<input
|
||||
ref="startDate"
|
||||
v-model="formattedBounds.start"
|
||||
class="c-input--datetime"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
@change="validateAllBounds(); submitForm()"
|
||||
>
|
||||
<date-picker
|
||||
v-if="isFixed && isUTCBased"
|
||||
:default-date-time="formattedBounds.start"
|
||||
:formatter="timeFormatter"
|
||||
@date-selected="startDateSelected"
|
||||
/>
|
||||
</div>
|
||||
<div class="c-conductor__controls">
|
||||
<!-- Mode, time system menu buttons and duration slider -->
|
||||
<ConductorMode class="c-conductor__mode-select"></ConductorMode>
|
||||
<ConductorTimeSystem class="c-conductor__time-system-select"></ConductorTimeSystem>
|
||||
|
||||
<div
|
||||
v-if="!isFixed"
|
||||
class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
|
||||
>
|
||||
<!-- RT start -->
|
||||
<div class="c-direction-indicator icon-minus"></div>
|
||||
<input
|
||||
ref="startOffset"
|
||||
v-model="offsets.start"
|
||||
class="c-input--hrs-min-sec"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
@change="validateAllOffsets(); submitForm()"
|
||||
>
|
||||
</div>
|
||||
<input type="submit" class="invisible">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
||||
<!-- Fixed end and RT 'last update' display -->
|
||||
<div class="c-conductor__end-fixed__label">
|
||||
{{ isFixed ? 'End' : 'Updated' }}
|
||||
</div>
|
||||
<input
|
||||
ref="endDate"
|
||||
v-model="formattedBounds.end"
|
||||
class="c-input--datetime"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
:disabled="!isFixed"
|
||||
@change="validateAllBounds(); submitForm()"
|
||||
>
|
||||
<date-picker
|
||||
v-if="isFixed && isUTCBased"
|
||||
class="c-ctrl-wrapper--menus-left"
|
||||
:default-date-time="formattedBounds.end"
|
||||
:formatter="timeFormatter"
|
||||
@date-selected="endDateSelected"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!isFixed"
|
||||
class="c-ctrl-wrapper c-conductor-input c-conductor__end-delta"
|
||||
>
|
||||
<!-- RT end -->
|
||||
<div class="c-direction-indicator icon-plus"></div>
|
||||
<input
|
||||
ref="endOffset"
|
||||
v-model="offsets.end"
|
||||
class="c-input--hrs-min-sec"
|
||||
type="text"
|
||||
autocorrect="off"
|
||||
spellcheck="false"
|
||||
@change="validateAllOffsets(); submitForm()"
|
||||
>
|
||||
</div>
|
||||
|
||||
<conductor-axis
|
||||
class="c-conductor__ticks"
|
||||
:bounds="rawBounds"
|
||||
@panAxis="setViewFromBounds"
|
||||
/>
|
||||
</div>
|
||||
<div class="c-conductor__controls">
|
||||
<!-- Mode, time system menu buttons and duration slider -->
|
||||
<ConductorMode class="c-conductor__mode-select" />
|
||||
<ConductorTimeSystem class="c-conductor__time-system-select" />
|
||||
</div>
|
||||
<input
|
||||
type="submit"
|
||||
class="invisible"
|
||||
>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -288,7 +324,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import ConductorMode from './ConductorMode.vue';
|
||||
import ConductorTimeSystem from './ConductorTimeSystem.vue';
|
||||
import DatePicker from './DatePicker.vue';
|
||||
@ -296,11 +331,6 @@ import ConductorAxis from './ConductorAxis.vue';
|
||||
import ConductorModeIcon from './ConductorModeIcon.vue';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
const SECONDS = 1000;
|
||||
const DAYS = 24 * 60 * 60 * SECONDS;
|
||||
const YEARS = 365 * DAYS;
|
||||
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'configuration'],
|
||||
@ -323,7 +353,7 @@ export default {
|
||||
durationFormatter: durationFormatter,
|
||||
offsets: {
|
||||
start: offsets && durationFormatter.format(Math.abs(offsets.start)),
|
||||
end: offsets && durationFormatter.format(Math.abs(offsets.end)),
|
||||
end: offsets && durationFormatter.format(Math.abs(offsets.end))
|
||||
},
|
||||
formattedBounds: {
|
||||
start: timeFormatter.format(bounds.start),
|
||||
@ -338,6 +368,14 @@ export default {
|
||||
showDatePicker: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
|
||||
|
||||
this.openmct.time.on('bounds', this.setViewFromBounds);
|
||||
this.openmct.time.on('timeSystem', this.setTimeSystem);
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
this.openmct.time.on('clockOffsets', this.setViewFromOffsets)
|
||||
},
|
||||
methods: {
|
||||
setTimeSystem(timeSystem) {
|
||||
this.timeFormatter = this.getFormatter(timeSystem.timeFormat);
|
||||
@ -347,12 +385,12 @@ export default {
|
||||
this.isUTCBased = timeSystem.isUTCBased;
|
||||
},
|
||||
setOffsetsFromView($event) {
|
||||
if (this.$refs.conductorForm.checkValidity()){
|
||||
if (this.$refs.conductorForm.checkValidity()) {
|
||||
let startOffset = 0 - this.durationFormatter.parse(this.offsets.start);
|
||||
let endOffset = this.durationFormatter.parse(this.offsets.end);
|
||||
|
||||
this.openmct.time.clockOffsets({
|
||||
start: startOffset,
|
||||
start: startOffset,
|
||||
end: endOffset
|
||||
});
|
||||
}
|
||||
@ -362,7 +400,7 @@ export default {
|
||||
}
|
||||
},
|
||||
setBoundsFromView($event) {
|
||||
if (this.$refs.conductorForm.checkValidity()){
|
||||
if (this.$refs.conductorForm.checkValidity()) {
|
||||
let start = this.timeFormatter.parse(this.formattedBounds.start);
|
||||
let end = this.timeFormatter.parse(this.formattedBounds.end);
|
||||
|
||||
@ -404,7 +442,7 @@ export default {
|
||||
[this.$refs.startOffset, this.$refs.endOffset].forEach(this.clearValidationForInput);
|
||||
}
|
||||
},
|
||||
clearValidationForInput(input){
|
||||
clearValidationForInput(input) {
|
||||
input.setCustomValidity('');
|
||||
input.title = '';
|
||||
},
|
||||
@ -419,7 +457,7 @@ export default {
|
||||
formattedDate = this.formattedBounds.end;
|
||||
}
|
||||
|
||||
if (!this.timeFormatter.validate(formattedDate)){
|
||||
if (!this.timeFormatter.validate(formattedDate)) {
|
||||
validationResult = 'Invalid date';
|
||||
} else {
|
||||
let boundsValues = {
|
||||
@ -428,8 +466,8 @@ export default {
|
||||
};
|
||||
validationResult = this.openmct.time.validateBounds(boundsValues);
|
||||
}
|
||||
|
||||
if (validationResult !== true){
|
||||
|
||||
if (validationResult !== true) {
|
||||
input.setCustomValidity(validationResult);
|
||||
input.title = validationResult;
|
||||
return false;
|
||||
@ -461,7 +499,7 @@ export default {
|
||||
validationResult = this.openmct.time.validateOffsets(offsetValues);
|
||||
}
|
||||
|
||||
if (validationResult !== true){
|
||||
if (validationResult !== true) {
|
||||
input.setCustomValidity(validationResult);
|
||||
input.title = validationResult;
|
||||
return false;
|
||||
@ -482,24 +520,16 @@ export default {
|
||||
format: key
|
||||
}).formatter;
|
||||
},
|
||||
startDateSelected(date){
|
||||
startDateSelected(date) {
|
||||
this.formattedBounds.start = this.timeFormatter.format(date);
|
||||
this.validateAllBounds();
|
||||
this.submitForm();
|
||||
},
|
||||
endDateSelected(date){
|
||||
endDateSelected(date) {
|
||||
this.formattedBounds.end = this.timeFormatter.format(date);
|
||||
this.validateAllBounds();
|
||||
this.submitForm();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
|
||||
|
||||
this.openmct.time.on('bounds', this.setViewFromBounds);
|
||||
this.openmct.time.on('timeSystem', this.setTimeSystem);
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
this.openmct.time.on('clockOffsets', this.setViewFromOffsets)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,10 +20,11 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-conductor-axis"
|
||||
ref="axisHolder"
|
||||
@mousedown="dragStart($event)">
|
||||
</div>
|
||||
<div
|
||||
ref="axisHolder"
|
||||
class="c-conductor-axis"
|
||||
@mousedown="dragStart($event)"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -126,106 +127,9 @@ const PIXELS_PER_TICK_WIDE = 200;
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
bounds: Object
|
||||
},
|
||||
methods: {
|
||||
setScale() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
let bounds = this.bounds;
|
||||
|
||||
if (timeSystem.isUTCBased) {
|
||||
this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
|
||||
} else {
|
||||
this.xScale.domain([bounds.start, bounds.end]);
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
|
||||
this.xScale.range([PADDING, this.width - PADDING * 2]);
|
||||
this.axisElement.call(this.xAxis);
|
||||
|
||||
if (this.width > 1800) {
|
||||
this.xAxis.ticks(this.width / PIXELS_PER_TICK_WIDE);
|
||||
} else {
|
||||
this.xAxis.ticks(this.width / PIXELS_PER_TICK);
|
||||
}
|
||||
|
||||
this.msPerPixel = (bounds.end - bounds.start) / this.width;
|
||||
},
|
||||
setViewFromTimeSystem(timeSystem) {
|
||||
let format = this.getActiveFormatter();
|
||||
let bounds = this.openmct.time.bounds();
|
||||
|
||||
//The D3 scale used depends on the type of time system as d3
|
||||
// supports UTC out of the box.
|
||||
if (timeSystem.isUTCBased) {
|
||||
this.xScale = d3Scale.scaleUtc();
|
||||
} else {
|
||||
this.xScale = d3Scale.scaleLinear();
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
this.xAxis.tickFormat(utcMultiTimeFormat);
|
||||
this.axisElement.call(this.xAxis);
|
||||
this.setScale();
|
||||
},
|
||||
getActiveFormatter() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
let isFixed = this.openmct.time.clock() === undefined;
|
||||
|
||||
if (isFixed) {
|
||||
return this.getFormatter(timeSystem.timeFormat);
|
||||
} else {
|
||||
return this.getFormatter(timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
||||
}
|
||||
},
|
||||
getFormatter(key) {
|
||||
return this.openmct.telemetry.getValueFormatter({
|
||||
format: key
|
||||
}).formatter;
|
||||
},
|
||||
dragStart($event){
|
||||
let isFixed = this.openmct.time.clock() === undefined;
|
||||
if (isFixed){
|
||||
this.dragStartX = $event.clientX;
|
||||
|
||||
document.addEventListener('mousemove', this.drag);
|
||||
document.addEventListener('mouseup', this.dragEnd, {
|
||||
once: true
|
||||
});
|
||||
}
|
||||
},
|
||||
drag($event) {
|
||||
if (!this.dragging){
|
||||
this.dragging = true;
|
||||
requestAnimationFrame(()=>{
|
||||
let deltaX = $event.clientX - this.dragStartX;
|
||||
let percX = deltaX / this.width;
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let deltaTime = bounds.end - bounds.start;
|
||||
let newStart = bounds.start - percX * deltaTime;
|
||||
this.$emit('panAxis',{
|
||||
start: newStart,
|
||||
end: newStart + deltaTime
|
||||
});
|
||||
this.dragging = false;
|
||||
})
|
||||
} else {
|
||||
console.log('Rejected drag due to RAF cap');
|
||||
}
|
||||
},
|
||||
dragEnd() {
|
||||
document.removeEventListener('mousemove', this.drag);
|
||||
this.openmct.time.bounds({
|
||||
start: this.bounds.start,
|
||||
end: this.bounds.end
|
||||
});
|
||||
},
|
||||
resize() {
|
||||
if (this.$refs.axisHolder.clientWidth !== this.width) {
|
||||
this.width = this.$refs.axisHolder.clientWidth;
|
||||
this.setScale();
|
||||
}
|
||||
bounds: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -259,6 +163,103 @@ export default {
|
||||
setInterval(this.resize, RESIZE_POLL_INTERVAL);
|
||||
},
|
||||
destroyed() {
|
||||
},
|
||||
methods: {
|
||||
setScale() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
let bounds = this.bounds;
|
||||
|
||||
if (timeSystem.isUTCBased) {
|
||||
this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
|
||||
} else {
|
||||
this.xScale.domain([bounds.start, bounds.end]);
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
|
||||
this.xScale.range([PADDING, this.width - PADDING * 2]);
|
||||
this.axisElement.call(this.xAxis);
|
||||
|
||||
if (this.width > 1800) {
|
||||
this.xAxis.ticks(this.width / PIXELS_PER_TICK_WIDE);
|
||||
} else {
|
||||
this.xAxis.ticks(this.width / PIXELS_PER_TICK);
|
||||
}
|
||||
|
||||
this.msPerPixel = (bounds.end - bounds.start) / this.width;
|
||||
},
|
||||
setViewFromTimeSystem(timeSystem) {
|
||||
//The D3 scale used depends on the type of time system as d3
|
||||
// supports UTC out of the box.
|
||||
if (timeSystem.isUTCBased) {
|
||||
this.xScale = d3Scale.scaleUtc();
|
||||
} else {
|
||||
this.xScale = d3Scale.scaleLinear();
|
||||
}
|
||||
|
||||
this.xAxis.scale(this.xScale);
|
||||
this.xAxis.tickFormat(utcMultiTimeFormat);
|
||||
this.axisElement.call(this.xAxis);
|
||||
this.setScale();
|
||||
},
|
||||
getActiveFormatter() {
|
||||
let timeSystem = this.openmct.time.timeSystem();
|
||||
let isFixed = this.openmct.time.clock() === undefined;
|
||||
|
||||
if (isFixed) {
|
||||
return this.getFormatter(timeSystem.timeFormat);
|
||||
} else {
|
||||
return this.getFormatter(timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
|
||||
}
|
||||
},
|
||||
getFormatter(key) {
|
||||
return this.openmct.telemetry.getValueFormatter({
|
||||
format: key
|
||||
}).formatter;
|
||||
},
|
||||
dragStart($event) {
|
||||
let isFixed = this.openmct.time.clock() === undefined;
|
||||
if (isFixed) {
|
||||
this.dragStartX = $event.clientX;
|
||||
|
||||
document.addEventListener('mousemove', this.drag);
|
||||
document.addEventListener('mouseup', this.dragEnd, {
|
||||
once: true
|
||||
});
|
||||
}
|
||||
},
|
||||
drag($event) {
|
||||
if (!this.dragging) {
|
||||
this.dragging = true;
|
||||
requestAnimationFrame(()=>{
|
||||
let deltaX = $event.clientX - this.dragStartX;
|
||||
let percX = deltaX / this.width;
|
||||
let bounds = this.openmct.time.bounds();
|
||||
let deltaTime = bounds.end - bounds.start;
|
||||
let newStart = bounds.start - percX * deltaTime;
|
||||
this.$emit('panAxis',{
|
||||
start: newStart,
|
||||
end: newStart + deltaTime
|
||||
});
|
||||
this.dragging = false;
|
||||
})
|
||||
} else {
|
||||
console.log('Rejected drag due to RAF cap');
|
||||
}
|
||||
},
|
||||
dragEnd() {
|
||||
document.removeEventListener('mousemove', this.drag);
|
||||
this.openmct.time.bounds({
|
||||
start: this.bounds.start,
|
||||
end: this.bounds.end
|
||||
});
|
||||
},
|
||||
resize() {
|
||||
if (this.$refs.axisHolder.clientWidth !== this.width) {
|
||||
this.width = this.$refs.axisHolder.clientWidth;
|
||||
this.setScale();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,33 +20,43 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
||||
<button class="c-button--menu c-mode-button"
|
||||
@click.prevent="toggle">
|
||||
<span class="c-button__label">{{selectedMode.name}}</span>
|
||||
</button>
|
||||
<div class="c-menu c-super-menu c-conductor__mode-menu"
|
||||
v-if="open">
|
||||
<div class="c-super-menu__menu">
|
||||
<ul>
|
||||
<li v-for="mode in modes"
|
||||
:key="mode.key"
|
||||
@click="setOption(mode)"
|
||||
@mouseover="hoveredMode = mode"
|
||||
@mouseleave="hoveredMode = {}"
|
||||
class="menu-item-a"
|
||||
:class="mode.cssClass">
|
||||
{{mode.name}}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
||||
<button
|
||||
class="c-button--menu c-mode-button"
|
||||
@click.prevent="toggle"
|
||||
>
|
||||
<span class="c-button__label">{{ selectedMode.name }}</span>
|
||||
</button>
|
||||
<div
|
||||
v-if="open"
|
||||
class="c-menu c-super-menu c-conductor__mode-menu"
|
||||
>
|
||||
<div class="c-super-menu__menu">
|
||||
<ul>
|
||||
<li
|
||||
v-for="mode in modes"
|
||||
:key="mode.key"
|
||||
class="menu-item-a"
|
||||
:class="mode.cssClass"
|
||||
@click="setOption(mode)"
|
||||
@mouseover="hoveredMode = mode"
|
||||
@mouseleave="hoveredMode = {}"
|
||||
>
|
||||
{{ mode.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="c-super-menu__item-description">
|
||||
<div :class="['l-item-description__icon', 'bg-' + hoveredMode.cssClass]"></div>
|
||||
<div class="l-item-description__name">
|
||||
{{ hoveredMode.name }}
|
||||
</div>
|
||||
<div class="c-super-menu__item-description">
|
||||
<div :class="['l-item-description__icon', 'bg-' + hoveredMode.cssClass]"></div>
|
||||
<div class="l-item-description__name">{{hoveredMode.name}}</div>
|
||||
<div class="l-item-description__description">{{hoveredMode.description}}</div>
|
||||
<div class="l-item-description__description">
|
||||
{{ hoveredMode.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -87,6 +97,14 @@ export default {
|
||||
hoveredMode: {}
|
||||
};
|
||||
},
|
||||
mounted: function () {
|
||||
this.loadClocksFromConfiguration();
|
||||
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.time.off('clock', this.setViewFromClock);
|
||||
},
|
||||
methods: {
|
||||
loadClocksFromConfiguration() {
|
||||
let clocks = this.configuration.menuOptions
|
||||
@ -139,7 +157,7 @@ export default {
|
||||
}
|
||||
|
||||
let configuration = this.getMatchingConfig({
|
||||
clock: clockKey,
|
||||
clock: clockKey,
|
||||
timeSystem: this.openmct.time.timeSystem().key
|
||||
});
|
||||
|
||||
@ -147,10 +165,10 @@ export default {
|
||||
configuration = this.getMatchingConfig({
|
||||
clock: clockKey
|
||||
});
|
||||
|
||||
|
||||
this.openmct.time.timeSystem(configuration.timeSystem, configuration.bounds);
|
||||
}
|
||||
|
||||
|
||||
if (clockKey === undefined) {
|
||||
this.openmct.time.stopClock();
|
||||
} else {
|
||||
@ -180,14 +198,6 @@ export default {
|
||||
setViewFromClock(clock) {
|
||||
this.selectedMode = this.getModeOptionForClock(clock);
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.loadClocksFromConfiguration();
|
||||
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.time.off('clock', this.setViewFromClock);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,10 +20,10 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-clock-symbol">
|
||||
<div class="hand-little"></div>
|
||||
<div class="hand-big"></div>
|
||||
</div>
|
||||
<div class="c-clock-symbol">
|
||||
<div class="hand-little"></div>
|
||||
<div class="hand-big"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -20,24 +20,33 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
||||
v-if="selectedTimeSystem.name">
|
||||
<button class="c-button--menu c-time-system-button"
|
||||
:class="selectedTimeSystem.cssClass"
|
||||
@click.prevent="toggle">
|
||||
<span class="c-button__label">{{selectedTimeSystem.name}}</span>
|
||||
</button>
|
||||
<div class="c-menu" v-if="open">
|
||||
<ul>
|
||||
<li @click="setTimeSystemFromView(timeSystem)"
|
||||
v-for="timeSystem in timeSystems"
|
||||
:key="timeSystem.key"
|
||||
:class="timeSystem.cssClass">
|
||||
{{timeSystem.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedTimeSystem.name"
|
||||
class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
||||
>
|
||||
<button
|
||||
class="c-button--menu c-time-system-button"
|
||||
:class="selectedTimeSystem.cssClass"
|
||||
@click.prevent="toggle"
|
||||
>
|
||||
<span class="c-button__label">{{ selectedTimeSystem.name }}</span>
|
||||
</button>
|
||||
<div
|
||||
v-if="open"
|
||||
class="c-menu"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="timeSystem in timeSystems"
|
||||
:key="timeSystem.key"
|
||||
:class="timeSystem.cssClass"
|
||||
@click="setTimeSystemFromView(timeSystem)"
|
||||
>
|
||||
{{ timeSystem.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -54,6 +63,14 @@ export default {
|
||||
timeSystems: this.getValidTimesystemsForClock(activeClock)
|
||||
};
|
||||
},
|
||||
mounted: function () {
|
||||
this.openmct.time.on('timeSystem', this.setViewFromTimeSystem);
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.time.off('timeSystem', this.setViewFromTimeSystem);
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
},
|
||||
methods: {
|
||||
getValidTimesystemsForClock(clock) {
|
||||
return this.configuration.menuOptions
|
||||
@ -64,7 +81,7 @@ export default {
|
||||
if (timeSystem.key !== this.selectedTimeSystem.key) {
|
||||
let activeClock = this.openmct.time.clock();
|
||||
let configuration = this.getMatchingConfig({
|
||||
clock: activeClock && activeClock.key,
|
||||
clock: activeClock && activeClock.key,
|
||||
timeSystem: timeSystem.key
|
||||
});
|
||||
if (activeClock === undefined) {
|
||||
@ -111,14 +128,6 @@ export default {
|
||||
let activeClock = this.openmct.time.clock();
|
||||
this.timeSystems = this.getValidTimesystemsForClock(activeClock);
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.openmct.time.on('timeSystem', this.setViewFromTimeSystem);
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.time.off('timeSystem', this.setViewFromTimeSystem);
|
||||
this.openmct.time.on('clock', this.setViewFromClock);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,41 +20,68 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper" ref="calendarHolder">
|
||||
<a class="c-icon-button icon-calendar"
|
||||
@click="toggle"></a>
|
||||
<div class="c-menu c-menu--mobile-modal c-datetime-picker"
|
||||
v-if="open">
|
||||
<div class="c-datetime-picker__close-button">
|
||||
<button class="c-click-icon icon-x-in-circle"
|
||||
@click="toggle"></button>
|
||||
</div>
|
||||
<div class="c-datetime-picker__pager c-pager l-month-year-pager">
|
||||
<div class="c-pager__prev c-icon-button icon-arrow-left"
|
||||
@click.stop="changeMonth(-1)"></div>
|
||||
<div class="c-pager__month-year">{{model.month}} {{model.year}}</div>
|
||||
<div class="c-pager__next c-icon-button icon-arrow-right"
|
||||
@click.stop="changeMonth(1)"></div>
|
||||
</div>
|
||||
<div class="c-datetime-picker__calendar c-calendar">
|
||||
<ul class="c-calendar__row--header l-cal-row">
|
||||
<li v-for="day in ['Su','Mo','Tu','We','Th','Fr','Sa']"
|
||||
:key="day">{{day}}</li>
|
||||
</ul>
|
||||
<ul class="c-calendar__row--body"
|
||||
v-for="(row, index) in table"
|
||||
:key="index">
|
||||
<li v-for="(cell, index) in row"
|
||||
:key="index"
|
||||
@click="select(cell)"
|
||||
:class="{ 'is-in-month': isInCurrentMonth(cell), selected: isSelected(cell) }">
|
||||
<div class="c-calendar__day--prime">{{cell.day}}</div>
|
||||
<div class="c-calendar__day--sub">{{cell.dayOfYear}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
ref="calendarHolder"
|
||||
class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper"
|
||||
>
|
||||
<a
|
||||
class="c-icon-button icon-calendar"
|
||||
@click="toggle"
|
||||
></a>
|
||||
<div
|
||||
v-if="open"
|
||||
class="c-menu c-menu--mobile-modal c-datetime-picker"
|
||||
>
|
||||
<div class="c-datetime-picker__close-button">
|
||||
<button
|
||||
class="c-click-icon icon-x-in-circle"
|
||||
@click="toggle"
|
||||
></button>
|
||||
</div>
|
||||
<div class="c-datetime-picker__pager c-pager l-month-year-pager">
|
||||
<div
|
||||
class="c-pager__prev c-icon-button icon-arrow-left"
|
||||
@click.stop="changeMonth(-1)"
|
||||
></div>
|
||||
<div class="c-pager__month-year">
|
||||
{{ model.month }} {{ model.year }}
|
||||
</div>
|
||||
<div
|
||||
class="c-pager__next c-icon-button icon-arrow-right"
|
||||
@click.stop="changeMonth(1)"
|
||||
></div>
|
||||
</div>
|
||||
<div class="c-datetime-picker__calendar c-calendar">
|
||||
<ul class="c-calendar__row--header l-cal-row">
|
||||
<li
|
||||
v-for="day in ['Su','Mo','Tu','We','Th','Fr','Sa']"
|
||||
:key="day"
|
||||
>
|
||||
{{ day }}
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-for="(row, tableIndex) in table"
|
||||
:key="tableIndex"
|
||||
class="c-calendar__row--body"
|
||||
>
|
||||
<li
|
||||
v-for="(cell, rowIndex) in row"
|
||||
:key="rowIndex"
|
||||
:class="{ 'is-in-month': isInCurrentMonth(cell), selected: isSelected(cell) }"
|
||||
@click="select(cell)"
|
||||
>
|
||||
<div class="c-calendar__day--prime">
|
||||
{{ cell.day }}
|
||||
</div>
|
||||
<div class="c-calendar__day--sub">
|
||||
{{ cell.dayOfYear }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -163,10 +190,10 @@ import moment from 'moment';
|
||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
|
||||
|
||||
const TIME_NAMES = {
|
||||
'hours': "Hour",
|
||||
'minutes': "Minute",
|
||||
'seconds': "Second"
|
||||
};
|
||||
'hours': "Hour",
|
||||
'minutes': "Minute",
|
||||
'seconds': "Second"
|
||||
};
|
||||
const MONTHS = moment.months();
|
||||
const TIME_OPTIONS = (function makeRanges() {
|
||||
let arr = [];
|
||||
@ -184,8 +211,14 @@ export default {
|
||||
inject: ['openmct'],
|
||||
mixins: [toggleMixin],
|
||||
props: {
|
||||
defaultDateTime: String,
|
||||
formatter: Object
|
||||
defaultDateTime: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
formatter: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
@ -196,13 +229,17 @@ export default {
|
||||
},
|
||||
model: {
|
||||
year: undefined,
|
||||
month: undefined,
|
||||
month: undefined
|
||||
},
|
||||
table: undefined,
|
||||
date: undefined,
|
||||
time: undefined
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.updateFromModel(this.defaultDateTime);
|
||||
this.updateViewForMonth();
|
||||
},
|
||||
methods: {
|
||||
generateTable() {
|
||||
let m = moment.utc({ year: this.picker.year, month: this.picker.month }).day(0),
|
||||
@ -233,9 +270,7 @@ export default {
|
||||
},
|
||||
|
||||
updateFromModel(defaultDateTime) {
|
||||
let m;
|
||||
|
||||
m = moment.utc(defaultDateTime);
|
||||
let m = moment.utc(defaultDateTime);
|
||||
|
||||
this.date = {
|
||||
year: m.year(),
|
||||
@ -314,11 +349,7 @@ export default {
|
||||
|
||||
optionsFor(key) {
|
||||
return TIME_OPTIONS[key];
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
this.updateFromModel(this.defaultDateTime);
|
||||
this.updateViewForMonth();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -37,6 +37,7 @@ export default function WebPage(openmct) {
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
el: element,
|
||||
components: {
|
||||
WebPageComponent: WebPageComponent
|
||||
},
|
||||
@ -44,7 +45,6 @@ export default function WebPage(openmct) {
|
||||
openmct,
|
||||
domainObject
|
||||
},
|
||||
el: element,
|
||||
template: '<web-page-component></web-page-component>'
|
||||
});
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="l-iframe abs">
|
||||
<iframe :src="currentDomainObject.url"></iframe>
|
||||
</div>
|
||||
<div class="l-iframe abs">
|
||||
<iframe :src="currentDomainObject.url"></iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -20,34 +20,40 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-so-view has-local-controls"
|
||||
:class="{
|
||||
'c-so-view--no-frame': !hasFrame,
|
||||
'has-complex-content': complexContent
|
||||
}">
|
||||
<div class="c-so-view__header">
|
||||
<div class="c-so-view__header__icon" :class="cssClass"></div>
|
||||
<div class="c-so-view__header__name">
|
||||
{{ domainObject && domainObject.name }}
|
||||
</div>
|
||||
<context-menu-drop-down
|
||||
:object-path="objectPath">
|
||||
</context-menu-drop-down>
|
||||
<div
|
||||
class="c-so-view has-local-controls"
|
||||
:class="{
|
||||
'c-so-view--no-frame': !hasFrame,
|
||||
'has-complex-content': complexContent
|
||||
}"
|
||||
>
|
||||
<div class="c-so-view__header">
|
||||
<div
|
||||
class="c-so-view__header__icon"
|
||||
:class="cssClass"
|
||||
></div>
|
||||
<div class="c-so-view__header__name">
|
||||
{{ domainObject && domainObject.name }}
|
||||
</div>
|
||||
<div class="c-so-view__local-controls c-so-view__view-large h-local-controls c-local-controls--show-on-hover">
|
||||
<button class="c-button icon-expand"
|
||||
title="View Large"
|
||||
@click="expand">
|
||||
</button>
|
||||
</div>
|
||||
<object-view
|
||||
class="c-so-view__object-view"
|
||||
ref="objectView"
|
||||
:object="domainObject"
|
||||
:show-edit-view="showEditView"
|
||||
:object-path="objectPath">
|
||||
</object-view>
|
||||
<context-menu-drop-down
|
||||
:object-path="objectPath"
|
||||
/>
|
||||
</div>
|
||||
<div class="c-so-view__local-controls c-so-view__view-large h-local-controls c-local-controls--show-on-hover">
|
||||
<button
|
||||
class="c-button icon-expand"
|
||||
title="View Large"
|
||||
@click="expand"
|
||||
></button>
|
||||
</div>
|
||||
<object-view
|
||||
ref="objectView"
|
||||
class="c-so-view__object-view"
|
||||
:object="domainObject"
|
||||
:show-edit-view="showEditView"
|
||||
:object-path="objectPath"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -131,58 +137,64 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ObjectView from './ObjectView.vue'
|
||||
import ContextMenuDropDown from './contextMenuDropDown.vue';
|
||||
import ObjectView from './ObjectView.vue'
|
||||
import ContextMenuDropDown from './contextMenuDropDown.vue';
|
||||
|
||||
const SIMPLE_CONTENT_TYPES = [
|
||||
'clock',
|
||||
'timer',
|
||||
'summary-widget',
|
||||
'hyperlink'
|
||||
];
|
||||
const SIMPLE_CONTENT_TYPES = [
|
||||
'clock',
|
||||
'timer',
|
||||
'summary-widget',
|
||||
'hyperlink'
|
||||
];
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
domainObject: Object,
|
||||
objectPath: Array,
|
||||
hasFrame: Boolean,
|
||||
showEditView: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
}
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
ObjectView,
|
||||
ContextMenuDropDown
|
||||
},
|
||||
props: {
|
||||
domainObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
components: {
|
||||
ObjectView,
|
||||
ContextMenuDropDown,
|
||||
objectPath: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
methods: {
|
||||
expand() {
|
||||
let objectView = this.$refs.objectView,
|
||||
parentElement = objectView.$el,
|
||||
childElement = parentElement.children[0];
|
||||
hasFrame: Boolean,
|
||||
showEditView: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let objectType = this.openmct.types.get(this.domainObject.type),
|
||||
cssClass = objectType && objectType.definition ? objectType.definition.cssClass : 'icon-object-unknown',
|
||||
complexContent = !SIMPLE_CONTENT_TYPES.includes(this.domainObject.type);
|
||||
|
||||
this.openmct.overlays.overlay({
|
||||
element: childElement,
|
||||
size: 'large',
|
||||
onDestroy() {
|
||||
parentElement.append(childElement);
|
||||
}
|
||||
});
|
||||
},
|
||||
getSelectionContext() {
|
||||
return this.$refs.objectView.getSelectionContext();
|
||||
}
|
||||
return {
|
||||
cssClass,
|
||||
complexContent
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
expand() {
|
||||
let objectView = this.$refs.objectView,
|
||||
parentElement = objectView.$el,
|
||||
childElement = parentElement.children[0];
|
||||
|
||||
this.openmct.overlays.overlay({
|
||||
element: childElement,
|
||||
size: 'large',
|
||||
onDestroy() {
|
||||
parentElement.append(childElement);
|
||||
}
|
||||
});
|
||||
},
|
||||
data() {
|
||||
let objectType = this.openmct.types.get(this.domainObject.type),
|
||||
cssClass = objectType && objectType.definition ? objectType.definition.cssClass : 'icon-object-unknown',
|
||||
complexContent = !SIMPLE_CONTENT_TYPES.includes(this.domainObject.type);
|
||||
|
||||
return {
|
||||
cssClass,
|
||||
complexContent
|
||||
}
|
||||
getSelectionContext() {
|
||||
return this.$refs.objectView.getSelectionContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,11 +1,15 @@
|
||||
<template>
|
||||
<a class="c-tree__item__label c-object-label"
|
||||
<a
|
||||
class="c-tree__item__label c-object-label"
|
||||
draggable="true"
|
||||
:href="objectLink"
|
||||
@dragstart="dragStart"
|
||||
@click="navigateOrPreview"
|
||||
:href="objectLink">
|
||||
<div class="c-tree__item__type-icon c-object-label__type-icon"
|
||||
:class="typeClass"></div>
|
||||
>
|
||||
<div
|
||||
class="c-tree__item__type-icon c-object-label__type-icon"
|
||||
:class="typeClass"
|
||||
></div>
|
||||
<div class="c-tree__item__name c-object-label__name">{{ observedObject.name }}</div>
|
||||
</a>
|
||||
</template>
|
||||
@ -53,29 +57,24 @@ export default {
|
||||
mixins: [ObjectLink, ContextMenuGesture],
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
domainObject: Object,
|
||||
domainObject: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
objectPath: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
required: true
|
||||
},
|
||||
navigateToPath: String
|
||||
navigateToPath: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
observedObject: this.domainObject
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.observedObject) {
|
||||
let removeListener = this.openmct.objects.observe(this.observedObject, '*', (newObject) => {
|
||||
this.observedObject = newObject;
|
||||
});
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
}
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
},
|
||||
computed: {
|
||||
typeClass() {
|
||||
let type = this.openmct.types.get(this.observedObject.type);
|
||||
@ -85,15 +84,24 @@ export default {
|
||||
return type.definition.cssClass;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.observedObject) {
|
||||
let removeListener = this.openmct.objects.observe(this.observedObject, '*', (newObject) => {
|
||||
this.observedObject = newObject;
|
||||
});
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
}
|
||||
this.previewAction = new PreviewAction(this.openmct);
|
||||
},
|
||||
methods: {
|
||||
navigateOrPreview(event) {
|
||||
if (this.openmct.editor.isEditing()){
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
this.preview();
|
||||
}
|
||||
},
|
||||
preview() {
|
||||
if (this.previewAction.appliesTo(this.objectPath)){
|
||||
if (this.previewAction.appliesTo(this.objectPath)) {
|
||||
this.previewAction.invoke(this.objectPath);
|
||||
}
|
||||
},
|
||||
@ -104,13 +112,13 @@ export default {
|
||||
|
||||
/*
|
||||
* Cannot inspect data transfer objects on dragover/dragenter so impossible to determine composability at
|
||||
* that point. If dragged object can be composed by navigated object, then indicate with presence of
|
||||
* that point. If dragged object can be composed by navigated object, then indicate with presence of
|
||||
* 'composable-domain-object' in data transfer
|
||||
*/
|
||||
if (this.openmct.composition.checkPolicy(navigatedObject, this.observedObject)) {
|
||||
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.)
|
||||
event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
|
||||
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.domainObject);
|
||||
|
@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -7,10 +8,23 @@ import _ from "lodash"
|
||||
export default {
|
||||
inject: ["openmct"],
|
||||
props: {
|
||||
view: String,
|
||||
object: Object,
|
||||
object: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
showEditView: Boolean,
|
||||
objectPath: Array
|
||||
objectPath: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
object(newObject, oldObject) {
|
||||
this.currentObject = newObject;
|
||||
this.debounceUpdateView();
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.clear();
|
||||
@ -18,16 +32,6 @@ export default {
|
||||
this.releaseEditModeHandler();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
view(newView, oldView) {
|
||||
this.viewKey = newView;
|
||||
this.debounceUpdateView();
|
||||
},
|
||||
object(newObject, oldObject) {
|
||||
this.currentObject = newObject;
|
||||
this.debounceUpdateView();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||
},
|
||||
@ -114,7 +118,7 @@ export default {
|
||||
this.currentView.show(this.viewContainer, this.openmct.editor.isEditing());
|
||||
|
||||
if (immediatelySelect) {
|
||||
this.removeSelectable = openmct.selection.selectable(
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el, this.getSelectionContext(), true);
|
||||
}
|
||||
|
||||
@ -190,7 +194,7 @@ export default {
|
||||
provider.canEdit &&
|
||||
provider.canEdit(this.currentObject) &&
|
||||
!this.openmct.editor.isEditing()) {
|
||||
this.openmct.editor.edit();
|
||||
this.openmct.editor.edit();
|
||||
}
|
||||
},
|
||||
hasComposableDomainObject(event) {
|
||||
|
@ -1,17 +1,20 @@
|
||||
<template>
|
||||
<div class="c-progress-bar">
|
||||
<div class="c-progress-bar__holder">
|
||||
<div class="c-progress-bar__bar"
|
||||
:class="{'--indeterminate': model.progressPerc === 'unknown'}"
|
||||
:style="`width: ${model.progressPerc}%;`">
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-progress-bar__text"
|
||||
v-if="model.progressText !== undefined">
|
||||
<span v-if="model.progressPerc > 0">{{model.progressPerc}}% complete.</span>
|
||||
{{model.progressText}}
|
||||
</div>
|
||||
<div class="c-progress-bar">
|
||||
<div class="c-progress-bar__holder">
|
||||
<div
|
||||
class="c-progress-bar__bar"
|
||||
:class="{'--indeterminate': model.progressPerc === 'unknown'}"
|
||||
:style="styleBarWidth"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-if="model.progressText !== undefined"
|
||||
class="c-progress-bar__text"
|
||||
>
|
||||
<span v-if="model.progressPerc > 0">{{ model.progressPerc }}% complete.</span>
|
||||
{{ model.progressText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -64,6 +67,16 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props:['model']
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleBarWidth() {
|
||||
return `width: ${this.model.progressPerc}%;`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<label class="c-toggle-switch">
|
||||
<input type="checkbox"
|
||||
:id="id"
|
||||
:checked="checked"
|
||||
@change="onUserSelect($event)"/>
|
||||
<span class="c-toggle-switch__slider"></span>
|
||||
</label>
|
||||
<label class="c-toggle-switch">
|
||||
<input
|
||||
:id="id"
|
||||
type="checkbox"
|
||||
:checked="checked"
|
||||
@change="onUserSelect($event)"
|
||||
>
|
||||
<span class="c-toggle-switch__slider"></span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -63,16 +65,19 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
id: String,
|
||||
checked: Boolean
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
methods: {
|
||||
onUserSelect(event) {
|
||||
this.$emit('change', event.target.checked);
|
||||
}
|
||||
checked: Boolean
|
||||
},
|
||||
methods: {
|
||||
onUserSelect(event) {
|
||||
this.$emit('change', event.target.checked);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
@ -1,14 +1,21 @@
|
||||
<template>
|
||||
<div class="c-so-view__context-actions c-disclosure-button"
|
||||
@click="showContextMenu"></div>
|
||||
<div
|
||||
class="c-so-view__context-actions c-disclosure-button"
|
||||
@click="showContextMenu"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import contextMenu from '../mixins/context-menu-gesture'
|
||||
|
||||
export default {
|
||||
props: ['objectPath'],
|
||||
mixins: [contextMenu],
|
||||
props: {
|
||||
objectPath: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<template>
|
||||
<div class="c-search"
|
||||
:class="{ 'is-active': active === true }">
|
||||
<input class="c-search__input"
|
||||
tabindex="10000"
|
||||
type="search"
|
||||
v-bind="$attrs"
|
||||
v-bind:value="value"
|
||||
v-on="inputListeners"/>
|
||||
<a class="c-search__clear-input icon-x-in-circle"
|
||||
v-on:click="clearInput"></a>
|
||||
</div>
|
||||
<div
|
||||
class="c-search"
|
||||
:class="{ 'is-active': active === true }"
|
||||
>
|
||||
<input
|
||||
class="c-search__input"
|
||||
tabindex="10000"
|
||||
type="search"
|
||||
v-bind="$attrs"
|
||||
:value="value"
|
||||
v-on="inputListeners"
|
||||
>
|
||||
<a
|
||||
class="c-search__clear-input icon-x-in-circle"
|
||||
@click="clearInput"
|
||||
></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -44,37 +50,40 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/* Emits input and clear events */
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: String
|
||||
},
|
||||
computed: {
|
||||
inputListeners: function () {
|
||||
let vm = this;
|
||||
return Object.assign({},
|
||||
this.$listeners,
|
||||
{
|
||||
input: function (event) {
|
||||
vm.$emit('input', event.target.value);
|
||||
vm.active = (event.target.value.length > 0);
|
||||
}
|
||||
/* Emits input and clear events */
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
active: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inputListeners: function () {
|
||||
let vm = this;
|
||||
return Object.assign({},
|
||||
this.$listeners,
|
||||
{
|
||||
input: function (event) {
|
||||
vm.$emit('input', event.target.value);
|
||||
vm.active = (event.target.value.length > 0);
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
active: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearInput() {
|
||||
// Clear the user's input and set 'active' to false
|
||||
this.$emit('clear','');
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearInput() {
|
||||
// Clear the user's input and set 'active' to false
|
||||
this.$emit('clear','');
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,25 +1,27 @@
|
||||
<template>
|
||||
<span class="c-disclosure-triangle"
|
||||
<span
|
||||
class="c-disclosure-triangle"
|
||||
:class="{
|
||||
'c-disclosure-triangle--expanded' : value,
|
||||
'is-enabled' : enabled
|
||||
}"
|
||||
@click="$emit('input', !value)"></span>
|
||||
@click="$emit('input', !value)"
|
||||
></span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
enabled: {
|
||||
// Provided to allow the view-control to still occupy space without displaying a control icon.
|
||||
// Used as such in the tree - when a node doesn't have children, set disabled to true.
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
enabled: {
|
||||
// Provided to allow the view-control to still occupy space without displaying a control icon.
|
||||
// Used as such in the tree - when a node doesn't have children, set disabled to true.
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,29 +1,49 @@
|
||||
<template>
|
||||
<div class="c-elements-pool">
|
||||
<Search class="c-elements-pool__search"
|
||||
<Search
|
||||
class="c-elements-pool__search"
|
||||
:value="currentSearch"
|
||||
@input="applySearch"
|
||||
@clear="applySearch">
|
||||
</Search>
|
||||
<div class="c-elements-pool__elements"
|
||||
:class="{'is-dragging': isDragging}">
|
||||
<ul class="c-tree c-elements-pool__tree" id="inspector-elements-tree"
|
||||
v-if="elements.length > 0">
|
||||
<li :key="element.identifier.key" v-for="(element, index) in elements"
|
||||
@input="applySearch"
|
||||
@clear="applySearch"
|
||||
/>
|
||||
<div
|
||||
class="c-elements-pool__elements"
|
||||
:class="{'is-dragging': isDragging}"
|
||||
>
|
||||
<ul
|
||||
v-if="elements.length > 0"
|
||||
id="inspector-elements-tree"
|
||||
class="c-tree c-elements-pool__tree"
|
||||
>
|
||||
<li
|
||||
v-for="(element, index) in elements"
|
||||
:key="element.identifier.key"
|
||||
@drop="moveTo(index)"
|
||||
@dragover="allowDrop">
|
||||
<div class="c-tree__item c-elements-pool__item"
|
||||
draggable="true"
|
||||
@dragstart="moveFrom(index)">
|
||||
<span class="c-elements-pool__grippy"
|
||||
v-if="elements.length > 1 && isEditing">
|
||||
</span>
|
||||
<object-label :domainObject="element" :objectPath="[element, parentObject]"></object-label>
|
||||
@dragover="allowDrop"
|
||||
>
|
||||
<div
|
||||
class="c-tree__item c-elements-pool__item"
|
||||
draggable="true"
|
||||
@dragstart="moveFrom(index)"
|
||||
>
|
||||
<span
|
||||
v-if="elements.length > 1 && isEditing"
|
||||
class="c-elements-pool__grippy"
|
||||
></span>
|
||||
<object-label
|
||||
:domain-object="element"
|
||||
:object-path="[element, parentObject]"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li class="js-last-place" @drop="moveToIndex(elements.length)"></li>
|
||||
<li
|
||||
class="js-last-place"
|
||||
@drop="moveToIndex(elements.length)"
|
||||
></li>
|
||||
</ul>
|
||||
<div v-if="elements.length === 0">No contained elements</div>
|
||||
<div v-if="elements.length === 0">
|
||||
No contained elements
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -89,12 +109,23 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
let selection = this.openmct.selection.get();
|
||||
if (selection && selection.length > 0){
|
||||
if (selection && selection.length > 0) {
|
||||
this.showSelection(selection);
|
||||
}
|
||||
this.openmct.selection.on('change', this.showSelection);
|
||||
this.openmct.editor.on('isEditing', this.setEditState);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.setEditState);
|
||||
this.openmct.selection.off('change', this.showSelection);
|
||||
|
||||
if (this.mutationUnobserver) {
|
||||
this.mutationUnobserver();
|
||||
}
|
||||
if (this.compositionUnlistener) {
|
||||
this.compositionUnlistener();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setEditState(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
@ -141,7 +172,7 @@ export default {
|
||||
},
|
||||
addElement(element) {
|
||||
let keyString = this.openmct.objects.makeKeyString(element.identifier);
|
||||
this.elementsCache[keyString] =
|
||||
this.elementsCache[keyString] =
|
||||
JSON.parse(JSON.stringify(element));
|
||||
this.applySearch(this.currentSearch);
|
||||
},
|
||||
@ -168,7 +199,7 @@ export default {
|
||||
moveTo(moveToIndex) {
|
||||
this.composition.reorder(this.moveFromIndex, moveToIndex);
|
||||
},
|
||||
moveFrom(index){
|
||||
moveFrom(index) {
|
||||
this.isDragging = true;
|
||||
this.moveFromIndex = index;
|
||||
document.addEventListener('dragend', this.hideDragStyling);
|
||||
@ -177,17 +208,6 @@ export default {
|
||||
this.isDragging = false;
|
||||
document.removeEventListener('dragend', this.hideDragStyling);
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.setEditState);
|
||||
this.openmct.selection.off('change', this.showSelection);
|
||||
|
||||
if (this.mutationUnobserver) {
|
||||
this.mutationUnobserver();
|
||||
}
|
||||
if (this.compositionUnlistener) {
|
||||
this.compositionUnlistener();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,17 +1,22 @@
|
||||
<template>
|
||||
<multipane class="c-inspector"
|
||||
type="vertical">
|
||||
<pane class="c-inspector__properties">
|
||||
<properties></properties>
|
||||
<location></location>
|
||||
<inspector-views></inspector-views>
|
||||
</pane>
|
||||
<pane class="c-inspector__elements"
|
||||
handle="before"
|
||||
label="Elements" v-if="isEditing && hasComposition">
|
||||
<elements></elements>
|
||||
</pane>
|
||||
</multipane>
|
||||
<multipane
|
||||
class="c-inspector"
|
||||
type="vertical"
|
||||
>
|
||||
<pane class="c-inspector__properties">
|
||||
<properties />
|
||||
<location />
|
||||
<inspector-views />
|
||||
</pane>
|
||||
<pane
|
||||
v-if="isEditing && hasComposition"
|
||||
class="c-inspector__elements"
|
||||
handle="before"
|
||||
label="Elements"
|
||||
>
|
||||
<elements />
|
||||
</pane>
|
||||
</multipane>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -174,46 +179,46 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import multipane from '../layout/multipane.vue';
|
||||
import pane from '../layout/pane.vue';
|
||||
import Elements from './Elements.vue';
|
||||
import Location from './Location.vue';
|
||||
import Properties from './Properties.vue';
|
||||
import InspectorViews from './InspectorViews.vue';
|
||||
import multipane from '../layout/multipane.vue';
|
||||
import pane from '../layout/pane.vue';
|
||||
import Elements from './Elements.vue';
|
||||
import Location from './Location.vue';
|
||||
import Properties from './Properties.vue';
|
||||
import InspectorViews from './InspectorViews.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
'isEditing': Boolean
|
||||
},
|
||||
components: {
|
||||
multipane,
|
||||
pane,
|
||||
Elements,
|
||||
Properties,
|
||||
Location,
|
||||
InspectorViews
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasComposition: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refreshComposition(selection) {
|
||||
if (selection.length > 0 && selection[0].length > 0) {
|
||||
let parentObject = selection[0][0].context.item;
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
multipane,
|
||||
pane,
|
||||
Elements,
|
||||
Properties,
|
||||
Location,
|
||||
InspectorViews
|
||||
},
|
||||
props: {
|
||||
'isEditing': Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasComposition: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.refreshComposition);
|
||||
this.refreshComposition(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.refreshComposition);
|
||||
},
|
||||
methods: {
|
||||
refreshComposition(selection) {
|
||||
if (selection.length > 0 && selection[0].length > 0) {
|
||||
let parentObject = selection[0][0].context.item;
|
||||
|
||||
this.hasComposition = !!(parentObject && this.openmct.composition.get(parentObject));
|
||||
}
|
||||
this.hasComposition = !!(parentObject && this.openmct.composition.get(parentObject));
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.refreshComposition);
|
||||
this.refreshComposition(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.refreshComposition);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,46 +1,43 @@
|
||||
<template>
|
||||
<div>
|
||||
</div>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
selection: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.updateSelection(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.updateSelection);
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
this.selection = selection;
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.updateSelection(this.openmct.selection.get());
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.updateSelection);
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selection: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
this.selection = selection;
|
||||
|
||||
if (this.selectedViews) {
|
||||
this.selectedViews.forEach(selectedView => {
|
||||
selectedView.destroy();
|
||||
});
|
||||
this.$el.innerHTML = '';
|
||||
}
|
||||
|
||||
this.selectedViews = this.openmct.inspectorViews.get(selection);
|
||||
if (this.selectedViews) {
|
||||
this.selectedViews.forEach(selectedView => {
|
||||
let viewContainer = document.createElement('div');
|
||||
this.$el.append(viewContainer)
|
||||
selectedView.show(viewContainer);
|
||||
selectedView.destroy();
|
||||
});
|
||||
this.$el.innerHTML = '';
|
||||
}
|
||||
|
||||
this.selectedViews = this.openmct.inspectorViews.get(selection);
|
||||
this.selectedViews.forEach(selectedView => {
|
||||
let viewContainer = document.createElement('div');
|
||||
this.$el.append(viewContainer)
|
||||
selectedView.show(viewContainer);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,21 +1,39 @@
|
||||
<template>
|
||||
<div class="c-properties c-properties--location">
|
||||
<div class="c-properties__header" title="The location of this linked object.">Original Location</div>
|
||||
<ul class="c-properties__section" v-if="!multiSelect">
|
||||
<li class="c-properties__row" v-if="originalPath.length">
|
||||
<div
|
||||
class="c-properties__header"
|
||||
title="The location of this linked object."
|
||||
>
|
||||
Original Location
|
||||
</div>
|
||||
<ul
|
||||
v-if="!multiSelect"
|
||||
class="c-properties__section"
|
||||
>
|
||||
<li
|
||||
v-if="originalPath.length"
|
||||
class="c-properties__row"
|
||||
>
|
||||
<ul class="c-properties__value c-location">
|
||||
<li v-for="pathObject in orderedOriginalPath"
|
||||
<li
|
||||
v-for="pathObject in orderedOriginalPath"
|
||||
:key="pathObject.key"
|
||||
class="c-location__item"
|
||||
:key="pathObject.key">
|
||||
>
|
||||
<object-label
|
||||
:domainObject="pathObject.domainObject"
|
||||
:objectPath="pathObject.objectPath">
|
||||
</object-label>
|
||||
:domain-object="pathObject.domainObject"
|
||||
:object-path="pathObject.objectPath"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-properties__row--span-all" v-if="multiSelect">No location to display for multiple items</div>
|
||||
<div
|
||||
v-if="multiSelect"
|
||||
class="c-properties__row--span-all"
|
||||
>
|
||||
No location to display for multiple items
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -78,6 +96,11 @@ export default {
|
||||
keyString: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
orderedOriginalPath() {
|
||||
return this.originalPath.slice().reverse();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.updateSelection(this.openmct.selection.get());
|
||||
@ -111,7 +134,7 @@ export default {
|
||||
if (!selection.length || !selection[0].length) {
|
||||
this.clearData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selection.length > 1) {
|
||||
this.multiSelect = true;
|
||||
@ -119,7 +142,7 @@ export default {
|
||||
} else {
|
||||
this.multiSelect = false;
|
||||
}
|
||||
|
||||
|
||||
this.domainObject = selection[0][0].context.item;
|
||||
let parentObject = selection[0][1];
|
||||
|
||||
@ -139,11 +162,6 @@ export default {
|
||||
.then(this.setOriginalPath);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
orderedOriginalPath() {
|
||||
return this.originalPath.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,32 +1,75 @@
|
||||
<template>
|
||||
<div class="c-properties c-properties--properties">
|
||||
<div class="c-properties__header">Properties</div>
|
||||
<ul class="c-properties__section" v-if="!multiSelect && !singleSelectNonObject">
|
||||
<div class="c-properties__header">
|
||||
Properties
|
||||
</div>
|
||||
<ul
|
||||
v-if="!multiSelect && !singleSelectNonObject"
|
||||
class="c-properties__section"
|
||||
>
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label">Title</div>
|
||||
<div class="c-properties__value">{{ item.name }}</div>
|
||||
<div class="c-properties__label">
|
||||
Title
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="c-properties__row">
|
||||
<div class="c-properties__label">Type</div>
|
||||
<div class="c-properties__value">{{ typeName }}</div>
|
||||
<div class="c-properties__label">
|
||||
Type
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
{{ typeName }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="c-properties__row" v-if="item.created">
|
||||
<div class="c-properties__label">Created</div>
|
||||
<div class="c-properties__value c-ne__text">{{ formatTime(item.created) }}</div>
|
||||
<li
|
||||
v-if="item.created"
|
||||
class="c-properties__row"
|
||||
>
|
||||
<div class="c-properties__label">
|
||||
Created
|
||||
</div>
|
||||
<div class="c-properties__value c-ne__text">
|
||||
{{ formatTime(item.created) }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="c-properties__row" v-if="item.modified">
|
||||
<div class="c-properties__label">Modified</div>
|
||||
<div class="c-properties__value c-ne__text">{{ formatTime(item.modified) }}</div>
|
||||
<li
|
||||
v-if="item.modified"
|
||||
class="c-properties__row"
|
||||
>
|
||||
<div class="c-properties__label">
|
||||
Modified
|
||||
</div>
|
||||
<div class="c-properties__value c-ne__text">
|
||||
{{ formatTime(item.modified) }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="c-properties__row"
|
||||
<li
|
||||
v-for="prop in typeProperties"
|
||||
:key="prop.name">
|
||||
<div class="c-properties__label">{{ prop.name }}</div>
|
||||
<div class="c-properties__value">{{ prop.value }}</div>
|
||||
:key="prop.name"
|
||||
class="c-properties__row"
|
||||
>
|
||||
<div class="c-properties__label">
|
||||
{{ prop.name }}
|
||||
</div>
|
||||
<div class="c-properties__value">
|
||||
{{ prop.value }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-properties__row--span-all" v-if="multiSelect">No properties to display for multiple items</div>
|
||||
<div class="c-properties__row--span-all" v-if="singleSelectNonObject">No properties to display for this item</div>
|
||||
<div
|
||||
v-if="multiSelect"
|
||||
class="c-properties__row--span-all"
|
||||
>
|
||||
No properties to display for multiple items
|
||||
</div>
|
||||
<div
|
||||
v-if="singleSelectNonObject"
|
||||
class="c-properties__row--span-all"
|
||||
>
|
||||
No properties to display for this item
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -77,8 +120,8 @@ export default {
|
||||
.map((field) => {
|
||||
return {
|
||||
name: field.name,
|
||||
value: field.path.reduce((object, field) => {
|
||||
return object[field];
|
||||
value: field.path.reduce((object, key) => {
|
||||
return object[key];
|
||||
}, this.item)
|
||||
};
|
||||
});
|
||||
@ -108,7 +151,7 @@ export default {
|
||||
} else {
|
||||
this.multiSelect = false;
|
||||
this.domainObject = selection[0][0].context.item;
|
||||
}
|
||||
}
|
||||
},
|
||||
formatTime(unixTime) {
|
||||
return Moment.utc(unixTime).format('YYYY-MM-DD[\n]HH:mm:ss') + ' UTC';
|
||||
|
@ -1,22 +1,34 @@
|
||||
<template>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div class="c-about c-about--splash">
|
||||
<div class="c-about__image c-splash-image"></div>
|
||||
<div class="c-about__text s-text">
|
||||
<div class="c-about__text__element" v-if="branding.aboutHtml" v-html="branding.aboutHtml"></div>
|
||||
<div
|
||||
v-if="branding.aboutHtml"
|
||||
class="c-about__text__element"
|
||||
v-html="branding.aboutHtml"
|
||||
></div>
|
||||
<div class="c-about__text__element">
|
||||
<h1 class="l-title s-title">Open MCT</h1>
|
||||
<h1 class="l-title s-title">
|
||||
Open MCT
|
||||
</h1>
|
||||
<div class="l-description s-description">
|
||||
<p>Open MCT, Copyright © 2014-2019, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.</p>
|
||||
<p>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 <a target="_blank" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>.</p>
|
||||
<p>
|
||||
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 <a
|
||||
target="_blank"
|
||||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||
>http://www.apache.org/licenses/LICENSE-2.0</a>.
|
||||
</p>
|
||||
<p>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.</p>
|
||||
<p>Open MCT includes source code licensed under additional open source licenses. See the Open Source Licenses file included with this distribution or <a @click="showLicenses">click here for third party licensing information</a>.</p>
|
||||
</div>
|
||||
<h2>Version Information</h2>
|
||||
<ul class="t-info l-info s-info">
|
||||
<li>Version: {{buildInfo.version || 'Unknown'}}</li>
|
||||
<li>Build Date: {{buildInfo.buildDate || 'Unknown'}}</li>
|
||||
<li>Revision: {{buildInfo.revision || 'Unknown'}}</li>
|
||||
<li>Branch: {{buildInfo.branch || 'Unknown'}}</li>
|
||||
<li>Version: {{ buildInfo.version || 'Unknown' }}</li>
|
||||
<li>Build Date: {{ buildInfo.buildDate || 'Unknown' }}</li>
|
||||
<li>Revision: {{ buildInfo.revision || 'Unknown' }}</li>
|
||||
<li>Branch: {{ buildInfo.branch || 'Unknown' }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -37,4 +49,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
@ -20,7 +20,11 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="l-shell__app-logo" @click="launchAbout" ref="aboutLogo"></div>
|
||||
<div
|
||||
ref="aboutLogo"
|
||||
class="l-shell__app-logo"
|
||||
@click="launchAbout"
|
||||
></div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.l-shell__app-logo {
|
||||
@ -38,12 +42,12 @@ export default {
|
||||
inject: ['openmct'],
|
||||
mounted() {
|
||||
let branding = this.openmct.branding();
|
||||
if (branding.smallLogoImage){
|
||||
if (branding.smallLogoImage) {
|
||||
this.$refs.aboutLogo.style.backgroundImage = `url('${branding.smallLogoImage}')`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
launchAbout(){
|
||||
launchAbout() {
|
||||
let vm = new Vue({
|
||||
provide: {
|
||||
openmct: this.openmct
|
||||
@ -59,4 +63,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
@ -1,61 +1,93 @@
|
||||
<template>
|
||||
<div class="l-browse-bar">
|
||||
<div class="l-browse-bar__start">
|
||||
<button v-if="hasParent"
|
||||
class="l-browse-bar__nav-to-parent-button c-icon-button c-icon-button--major icon-pointer-left"
|
||||
@click="goToParent"></button>
|
||||
<div class="l-browse-bar__object-name--w"
|
||||
:class="type.cssClass">
|
||||
<span
|
||||
class="l-browse-bar__object-name c-input-inline"
|
||||
@blur="updateName"
|
||||
@keydown.enter.prevent
|
||||
@keyup.enter.prevent="updateNameOnEnterKeyPress"
|
||||
contenteditable>
|
||||
{{ domainObject.name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-browse-bar__context-actions c-disclosure-button" @click.prevent.stop="showContextMenu"></div>
|
||||
<div class="l-browse-bar">
|
||||
<div class="l-browse-bar__start">
|
||||
<button
|
||||
v-if="hasParent"
|
||||
class="l-browse-bar__nav-to-parent-button c-icon-button c-icon-button--major icon-pointer-left"
|
||||
@click="goToParent"
|
||||
></button>
|
||||
<div
|
||||
class="l-browse-bar__object-name--w"
|
||||
:class="type.cssClass"
|
||||
>
|
||||
<span
|
||||
class="l-browse-bar__object-name c-input-inline"
|
||||
contenteditable
|
||||
@blur="updateName"
|
||||
@keydown.enter.prevent
|
||||
@keyup.enter.prevent="updateNameOnEnterKeyPress"
|
||||
>
|
||||
{{ domainObject.name }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="l-browse-bar__context-actions c-disclosure-button"
|
||||
@click.prevent.stop="showContextMenu"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="l-browse-bar__end">
|
||||
<view-switcher
|
||||
:currentView="currentView"
|
||||
:views="views"
|
||||
@setView="setView">
|
||||
</view-switcher>
|
||||
<!-- Action buttons -->
|
||||
<div class="l-browse-bar__actions">
|
||||
<button v-if="notebookEnabled"
|
||||
class="l-browse-bar__actions__notebook-entry c-button icon-notebook"
|
||||
title="New Notebook entry"
|
||||
@click="snapshot()">
|
||||
</button>
|
||||
<button class="l-browse-bar__actions__edit c-button c-button--major icon-pencil" title="Edit" v-if="isViewEditable & !isEditing" @click="edit()"></button>
|
||||
<div class="l-browse-bar__end">
|
||||
<view-switcher
|
||||
:current-view="currentView"
|
||||
:views="views"
|
||||
@setView="setView"
|
||||
/>
|
||||
<!-- Action buttons -->
|
||||
<div class="l-browse-bar__actions">
|
||||
<button
|
||||
v-if="notebookEnabled"
|
||||
class="l-browse-bar__actions__notebook-entry c-button icon-notebook"
|
||||
title="New Notebook entry"
|
||||
@click="snapshot()"
|
||||
></button>
|
||||
<button
|
||||
v-if="isViewEditable & !isEditing"
|
||||
class="l-browse-bar__actions__edit c-button c-button--major icon-pencil"
|
||||
title="Edit"
|
||||
@click="edit()"
|
||||
></button>
|
||||
|
||||
<div class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
||||
v-if="isEditing">
|
||||
<button class="c-button--menu c-button--major icon-save" title="Save" @click.stop="toggleSaveMenu"></button>
|
||||
<div class="c-menu" v-show="showSaveMenu">
|
||||
<ul>
|
||||
<li @click="saveAndFinishEditing"
|
||||
class="icon-save"
|
||||
title="Save and Finish Editing">
|
||||
Save and Finish Editing
|
||||
</li>
|
||||
<li @click="saveAndContinueEditing"
|
||||
class="icon-save"
|
||||
title="Save and Continue Editing">
|
||||
Save and Continue Editing
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
v-if="isEditing"
|
||||
class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
||||
>
|
||||
<button
|
||||
class="c-button--menu c-button--major icon-save"
|
||||
title="Save"
|
||||
@click.stop="toggleSaveMenu"
|
||||
></button>
|
||||
<div
|
||||
v-show="showSaveMenu"
|
||||
class="c-menu"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
class="icon-save"
|
||||
title="Save and Finish Editing"
|
||||
@click="saveAndFinishEditing"
|
||||
>
|
||||
Save and Finish Editing
|
||||
</li>
|
||||
<li
|
||||
class="icon-save"
|
||||
title="Save and Continue Editing"
|
||||
@click="saveAndContinueEditing"
|
||||
>
|
||||
Save and Continue Editing
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button class="l-browse-bar__actions c-button icon-x" title="Cancel Editing" v-if="isEditing" @click="promptUserandCancelEditing()"></button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="isEditing"
|
||||
class="l-browse-bar__actions c-button icon-x"
|
||||
title="Cancel Editing"
|
||||
@click="promptUserandCancelEditing()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -63,187 +95,186 @@ import NotebookSnapshot from '../utils/notebook-snapshot';
|
||||
import ViewSwitcher from './ViewSwitcher.vue';
|
||||
const PLACEHOLDER_OBJECT = {};
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
ViewSwitcher
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
ViewSwitcher
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
showViewMenu: false,
|
||||
showSaveMenu: false,
|
||||
domainObject: PLACEHOLDER_OBJECT,
|
||||
viewKey: undefined,
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
notebookEnabled: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentView() {
|
||||
return this.views.filter(v => v.key === this.viewKey)[0] || {};
|
||||
},
|
||||
methods: {
|
||||
toggleSaveMenu() {
|
||||
this.showSaveMenu = !this.showSaveMenu;
|
||||
},
|
||||
closeViewAndSaveMenu() {
|
||||
this.showViewMenu = false;
|
||||
this.showSaveMenu = false;
|
||||
},
|
||||
updateName(event) {
|
||||
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
|
||||
} else {
|
||||
event.target.innerText = this.domainObject.name;
|
||||
}
|
||||
},
|
||||
updateNameOnEnterKeyPress (event) {
|
||||
event.target.blur();
|
||||
},
|
||||
setView(view) {
|
||||
this.viewKey = view.key;
|
||||
this.openmct.router.updateParams({
|
||||
view: this.viewKey
|
||||
views() {
|
||||
return this
|
||||
.openmct
|
||||
.objectViews
|
||||
.get(this.domainObject)
|
||||
.map((p) => {
|
||||
return {
|
||||
key: p.key,
|
||||
cssClass: p.cssClass,
|
||||
name: p.name
|
||||
};
|
||||
});
|
||||
},
|
||||
edit() {
|
||||
this.openmct.editor.edit();
|
||||
},
|
||||
promptUserandCancelEditing() {
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: 'Any unsaved changes will be lost. Are you sure you want to continue?',
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: true,
|
||||
callback: () => {
|
||||
this.openmct.editor.cancel().then(() => {
|
||||
//refresh object view
|
||||
this.openmct.layout.$refs.browseObject.show(this.domainObject, this.viewKey, true);
|
||||
});
|
||||
dialog.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
promptUserbeforeNavigatingAway(event) {
|
||||
if(this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
}
|
||||
},
|
||||
saveAndFinishEditing() {
|
||||
let dialog = this.openmct.overlays.progressDialog({
|
||||
progressPerc: 'unknown',
|
||||
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
|
||||
iconClass: 'info',
|
||||
title: 'Saving',
|
||||
});
|
||||
|
||||
return this.openmct.editor.save()
|
||||
.then(()=> {
|
||||
dialog.dismiss();
|
||||
this.openmct.notifications.info('Save successful');
|
||||
}).catch((error) => {
|
||||
dialog.dismiss();
|
||||
this.openmct.notifications.error('Error saving objects');
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
saveAndContinueEditing() {
|
||||
this.saveAndFinishEditing().then(() => {
|
||||
this.openmct.editor.edit();
|
||||
});
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.openmct.router.path, event.clientX, event.clientY);
|
||||
},
|
||||
snapshot() {
|
||||
let element = document.getElementsByClassName("l-shell__main-container")[0];
|
||||
this.notebookSnapshot.capture(this.domainObject, element);
|
||||
},
|
||||
goToParent(){
|
||||
window.location.hash = this.parentUrl;
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
showViewMenu: false,
|
||||
showSaveMenu: false,
|
||||
domainObject: PLACEHOLDER_OBJECT,
|
||||
viewKey: undefined,
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
notebookEnabled: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentView() {
|
||||
return this.views.filter(v => v.key === this.viewKey)[0] || {};
|
||||
},
|
||||
views() {
|
||||
return this
|
||||
.openmct
|
||||
.objectViews
|
||||
.get(this.domainObject)
|
||||
.map((p) => {
|
||||
return {
|
||||
key: p.key,
|
||||
cssClass: p.cssClass,
|
||||
name: p.name
|
||||
};
|
||||
});
|
||||
},
|
||||
hasParent() {
|
||||
return this.domainObject !== PLACEHOLDER_OBJECT &&
|
||||
hasParent() {
|
||||
return this.domainObject !== PLACEHOLDER_OBJECT &&
|
||||
this.parentUrl !== '#/browse'
|
||||
},
|
||||
parentUrl() {
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
let hash = window.location.hash;
|
||||
return hash.slice(0, hash.lastIndexOf('/' + objectKeyString));
|
||||
},
|
||||
type() {
|
||||
let objectType = this.openmct.types.get(this.domainObject.type);
|
||||
if (!objectType) {
|
||||
return {}
|
||||
}
|
||||
return objectType.definition;
|
||||
},
|
||||
isViewEditable() {
|
||||
let currentViewKey = this.currentView.key;
|
||||
if (currentViewKey !== undefined) {
|
||||
let currentViewProvider = this.openmct.objectViews.getByProviderKey(currentViewKey);
|
||||
return currentViewProvider.canEdit && currentViewProvider.canEdit(this.domainObject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
|
||||
if (this.openmct.types.get('notebook')) {
|
||||
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
|
||||
this.notebookEnabled = true;
|
||||
}
|
||||
|
||||
document.addEventListener('click', this.closeViewAndSaveMenu);
|
||||
window.addEventListener('beforeunload', this.promptUserbeforeNavigatingAway);
|
||||
|
||||
this.openmct.editor.on('isEditing', (isEditing) => {
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
parentUrl() {
|
||||
let objectKeyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
let hash = window.location.hash;
|
||||
return hash.slice(0, hash.lastIndexOf('/' + objectKeyString));
|
||||
},
|
||||
watch: {
|
||||
domainObject() {
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver();
|
||||
}
|
||||
this.mutationObserver = this.openmct.objects.observe(this.domainObject, '*', (domainObject) => {
|
||||
this.domainObject = domainObject;
|
||||
});
|
||||
type() {
|
||||
let objectType = this.openmct.types.get(this.domainObject.type);
|
||||
if (!objectType) {
|
||||
return {}
|
||||
}
|
||||
return objectType.definition;
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
isViewEditable() {
|
||||
let currentViewKey = this.currentView.key;
|
||||
if (currentViewKey !== undefined) {
|
||||
let currentViewProvider = this.openmct.objectViews.getByProviderKey(currentViewKey);
|
||||
return currentViewProvider.canEdit && currentViewProvider.canEdit(this.domainObject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
domainObject() {
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver();
|
||||
}
|
||||
document.removeEventListener('click', this.closeViewAndSaveMenu);
|
||||
window.removeEventListener('click', this.promptUserbeforeNavigatingAway);
|
||||
this.mutationObserver = this.openmct.objects.observe(this.domainObject, '*', (domainObject) => {
|
||||
this.domainObject = domainObject;
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
|
||||
if (this.openmct.types.get('notebook')) {
|
||||
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
|
||||
this.notebookEnabled = true;
|
||||
}
|
||||
|
||||
document.addEventListener('click', this.closeViewAndSaveMenu);
|
||||
window.addEventListener('beforeunload', this.promptUserbeforeNavigatingAway);
|
||||
|
||||
this.openmct.editor.on('isEditing', (isEditing) => {
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver();
|
||||
}
|
||||
document.removeEventListener('click', this.closeViewAndSaveMenu);
|
||||
window.removeEventListener('click', this.promptUserbeforeNavigatingAway);
|
||||
},
|
||||
methods: {
|
||||
toggleSaveMenu() {
|
||||
this.showSaveMenu = !this.showSaveMenu;
|
||||
},
|
||||
closeViewAndSaveMenu() {
|
||||
this.showViewMenu = false;
|
||||
this.showSaveMenu = false;
|
||||
},
|
||||
updateName(event) {
|
||||
if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
|
||||
} else {
|
||||
event.target.innerText = this.domainObject.name;
|
||||
}
|
||||
},
|
||||
updateNameOnEnterKeyPress(event) {
|
||||
event.target.blur();
|
||||
},
|
||||
setView(view) {
|
||||
this.viewKey = view.key;
|
||||
this.openmct.router.updateParams({
|
||||
view: this.viewKey
|
||||
});
|
||||
},
|
||||
edit() {
|
||||
this.openmct.editor.edit();
|
||||
},
|
||||
promptUserandCancelEditing() {
|
||||
let dialog = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: 'Any unsaved changes will be lost. Are you sure you want to continue?',
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: true,
|
||||
callback: () => {
|
||||
this.openmct.editor.cancel().then(() => {
|
||||
//refresh object view
|
||||
this.openmct.layout.$refs.browseObject.show(this.domainObject, this.viewKey, true);
|
||||
});
|
||||
dialog.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: () => {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
promptUserbeforeNavigatingAway(event) {
|
||||
if(this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
}
|
||||
},
|
||||
saveAndFinishEditing() {
|
||||
let dialog = this.openmct.overlays.progressDialog({
|
||||
progressPerc: 'unknown',
|
||||
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
|
||||
iconClass: 'info',
|
||||
title: 'Saving'
|
||||
});
|
||||
|
||||
return this.openmct.editor.save().then(()=> {
|
||||
dialog.dismiss();
|
||||
this.openmct.notifications.info('Save successful');
|
||||
}).catch((error) => {
|
||||
dialog.dismiss();
|
||||
this.openmct.notifications.error('Error saving objects');
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
saveAndContinueEditing() {
|
||||
this.saveAndFinishEditing().then(() => {
|
||||
this.openmct.editor.edit();
|
||||
});
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.openmct.router.path, event.clientX, event.clientY);
|
||||
},
|
||||
snapshot() {
|
||||
let element = document.getElementsByClassName("l-shell__main-container")[0];
|
||||
this.notebookSnapshot.capture(this.domainObject, element);
|
||||
},
|
||||
goToParent() {
|
||||
window.location.hash = this.parentUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -1,30 +1,40 @@
|
||||
<template>
|
||||
<div class="c-create-button--w">
|
||||
<button class="c-create-button c-button--menu c-button--major icon-plus"
|
||||
@click="open">
|
||||
<span class="c-button__label">Create</span>
|
||||
</button>
|
||||
<div class="c-create-menu c-super-menu"
|
||||
v-if="opened">
|
||||
<div class="c-super-menu__menu">
|
||||
<ul>
|
||||
<li v-for="(item, index) in sortedItems"
|
||||
:key="index"
|
||||
:class="item.class"
|
||||
:title="item.title"
|
||||
@mouseover="showItemDescription(item)"
|
||||
@click="create(item)">
|
||||
{{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-create-button--w">
|
||||
<button
|
||||
class="c-create-button c-button--menu c-button--major icon-plus"
|
||||
@click="open"
|
||||
>
|
||||
<span class="c-button__label">Create</span>
|
||||
</button>
|
||||
<div
|
||||
v-if="opened"
|
||||
class="c-create-menu c-super-menu"
|
||||
>
|
||||
<div class="c-super-menu__menu">
|
||||
<ul>
|
||||
<li
|
||||
v-for="(item, index) in sortedItems"
|
||||
:key="index"
|
||||
:class="item.class"
|
||||
:title="item.title"
|
||||
@mouseover="showItemDescription(item)"
|
||||
@click="create(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="c-super-menu__item-description">
|
||||
<div :class="['l-item-description__icon', 'bg-' + selectedMenuItem.class]"></div>
|
||||
<div class="l-item-description__name">
|
||||
{{ selectedMenuItem.name }}
|
||||
</div>
|
||||
<div class="c-super-menu__item-description">
|
||||
<div :class="['l-item-description__icon', 'bg-' + selectedMenuItem.class]"></div>
|
||||
<div class="l-item-description__name">{{selectedMenuItem.name}}</div>
|
||||
<div class="l-item-description__description">{{selectedMenuItem.title}}</div>
|
||||
<div class="l-item-description__description">
|
||||
{{ selectedMenuItem.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -62,102 +72,97 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import CreateAction from '../../../platform/commonUI/edit/src/creation/CreateAction';
|
||||
import objectUtils from '../../api/objects/object-utils';
|
||||
import CreateAction from '../../../platform/commonUI/edit/src/creation/CreateAction';
|
||||
import objectUtils from '../../api/objects/object-utils';
|
||||
|
||||
function convertToLegacyObject(domainObject) {
|
||||
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||
return instantiate(oldModel, keyString);
|
||||
}
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
methods: {
|
||||
open: function () {
|
||||
if (this.opened) {
|
||||
return;
|
||||
}
|
||||
this.opened = true;
|
||||
setTimeout(() => document.addEventListener('click', this.close));
|
||||
},
|
||||
close: function () {
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
this.opened = false;
|
||||
document.removeEventListener('click', this.close);
|
||||
},
|
||||
showItemDescription: function (menuItem) {
|
||||
this.selectedMenuItem = menuItem;
|
||||
},
|
||||
create: function (item) {
|
||||
// Hack for support. TODO: rewrite create action.
|
||||
// 1. Get contextual object from navigation
|
||||
// 2. Get legacy type from legacy api
|
||||
// 3. Instantiate create action with type, parent, context
|
||||
// 4. perform action.
|
||||
return this.openmct.objects.get(this.openmct.router.path[0].identifier)
|
||||
.then((currentObject) => {
|
||||
let legacyContextualParent = this.convertToLegacy(currentObject);
|
||||
let legacyType = this.openmct.$injector.get('typeService').getType(item.key);
|
||||
let context = {
|
||||
key: "create",
|
||||
domainObject: legacyContextualParent // should be same as parent object.
|
||||
};
|
||||
let action = new CreateAction(
|
||||
legacyType,
|
||||
legacyContextualParent,
|
||||
context,
|
||||
this.openmct
|
||||
);
|
||||
return action.perform();
|
||||
});
|
||||
},
|
||||
convertToLegacy (domainObject) {
|
||||
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||
return this.openmct.$injector.get('instantiate')(oldModel, keyString);
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data: function () {
|
||||
let items = [];
|
||||
|
||||
this.openmct.types.listKeys().forEach(key => {
|
||||
let menuItem = this.openmct.types.get(key).definition;
|
||||
|
||||
if (menuItem.creatable) {
|
||||
let menuItemTemplate = {
|
||||
key: key,
|
||||
name: menuItem.name,
|
||||
class: menuItem.cssClass,
|
||||
title: menuItem.description
|
||||
};
|
||||
|
||||
items.push(menuItemTemplate);
|
||||
}
|
||||
},
|
||||
destroyed () {
|
||||
document.removeEventListener('click', this.close);
|
||||
},
|
||||
data: function() {
|
||||
let items = [];
|
||||
});
|
||||
|
||||
this.openmct.types.listKeys().forEach(key => {
|
||||
let menuItem = this.openmct.types.get(key).definition;
|
||||
|
||||
if (menuItem.creatable) {
|
||||
let menuItemTemplate = {
|
||||
key: key,
|
||||
name: menuItem.name,
|
||||
class: menuItem.cssClass,
|
||||
title: menuItem.description
|
||||
};
|
||||
|
||||
items.push(menuItemTemplate);
|
||||
return {
|
||||
items: items,
|
||||
selectedMenuItem: {},
|
||||
opened: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sortedItems() {
|
||||
return this.items.slice().sort((a,b) => {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
items: items,
|
||||
selectedMenuItem: {},
|
||||
opened: false
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('click', this.close);
|
||||
},
|
||||
methods: {
|
||||
open: function () {
|
||||
if (this.opened) {
|
||||
return;
|
||||
}
|
||||
this.opened = true;
|
||||
setTimeout(() => document.addEventListener('click', this.close));
|
||||
},
|
||||
computed: {
|
||||
sortedItems () {
|
||||
return this.items.sort((a,b) => {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
close: function () {
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
this.opened = false;
|
||||
document.removeEventListener('click', this.close);
|
||||
},
|
||||
showItemDescription: function (menuItem) {
|
||||
this.selectedMenuItem = menuItem;
|
||||
},
|
||||
create: function (item) {
|
||||
// Hack for support. TODO: rewrite create action.
|
||||
// 1. Get contextual object from navigation
|
||||
// 2. Get legacy type from legacy api
|
||||
// 3. Instantiate create action with type, parent, context
|
||||
// 4. perform action.
|
||||
return this.openmct.objects.get(this.openmct.router.path[0].identifier)
|
||||
.then((currentObject) => {
|
||||
let legacyContextualParent = this.convertToLegacy(currentObject);
|
||||
let legacyType = this.openmct.$injector.get('typeService').getType(item.key);
|
||||
let context = {
|
||||
key: "create",
|
||||
domainObject: legacyContextualParent // should be same as parent object.
|
||||
};
|
||||
let action = new CreateAction(
|
||||
legacyType,
|
||||
legacyContextualParent,
|
||||
context,
|
||||
this.openmct
|
||||
);
|
||||
return action.perform();
|
||||
});
|
||||
},
|
||||
convertToLegacy(domainObject) {
|
||||
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||
return this.openmct.$injector.get('instantiate')(oldModel, keyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,60 +1,84 @@
|
||||
<template>
|
||||
<div class="l-shell" :class="{
|
||||
'is-editing': isEditing
|
||||
}">
|
||||
<div class="l-shell__head" :class="{
|
||||
<div
|
||||
class="l-shell"
|
||||
:class="{
|
||||
'is-editing': isEditing
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="l-shell__head"
|
||||
:class="{
|
||||
'l-shell__head--expanded': headExpanded,
|
||||
'l-shell__head--minify-indicators': !headExpanded
|
||||
}">
|
||||
<CreateButton class="l-shell__create-button"></CreateButton>
|
||||
<indicators class="l-shell__head-section l-shell__indicators">
|
||||
</indicators>
|
||||
<button class="l-shell__head__collapse-button c-button"
|
||||
@click="toggleShellHead"></button>
|
||||
<notification-banner></notification-banner>
|
||||
<div class="l-shell__head-section l-shell__controls">
|
||||
<button class="c-icon-button c-icon-button--major icon-new-window" title="Open in a new browser tab"
|
||||
@click="openInNewTab"
|
||||
target="_blank">
|
||||
</button>
|
||||
<button v-bind:class="['c-icon-button c-icon-button--major', fullScreen ? 'icon-fullscreen-collapse' : 'icon-fullscreen-expand']"
|
||||
v-bind:title="`${fullScreen ? 'Exit' : 'Enable'} full screen mode`"
|
||||
@click="fullScreenToggle">
|
||||
</button>
|
||||
</div>
|
||||
<app-logo></app-logo>
|
||||
}"
|
||||
>
|
||||
<CreateButton class="l-shell__create-button" />
|
||||
<indicators class="l-shell__head-section l-shell__indicators" />
|
||||
<button
|
||||
class="l-shell__head__collapse-button c-button"
|
||||
@click="toggleShellHead"
|
||||
></button>
|
||||
<notification-banner />
|
||||
<div class="l-shell__head-section l-shell__controls">
|
||||
<button
|
||||
class="c-icon-button c-icon-button--major icon-new-window"
|
||||
title="Open in a new browser tab"
|
||||
target="_blank"
|
||||
@click="openInNewTab"
|
||||
></button>
|
||||
<button
|
||||
:class="['c-icon-button c-icon-button--major', fullScreen ? 'icon-fullscreen-collapse' : 'icon-fullscreen-expand']"
|
||||
:title="`${fullScreen ? 'Exit' : 'Enable'} full screen mode`"
|
||||
@click="fullScreenToggle"
|
||||
></button>
|
||||
</div>
|
||||
<multipane class="l-shell__main"
|
||||
type="horizontal">
|
||||
<pane class="l-shell__pane-tree"
|
||||
handle="after"
|
||||
label="Browse"
|
||||
collapsable>
|
||||
<mct-tree class="l-shell__tree"></mct-tree>
|
||||
</pane>
|
||||
<pane class="l-shell__pane-main">
|
||||
<browse-bar class="l-shell__main-view-browse-bar"
|
||||
ref="browseBar">
|
||||
</browse-bar>
|
||||
<toolbar v-if="toolbar" class="l-shell__toolbar"></toolbar>
|
||||
<object-view class="l-shell__main-container"
|
||||
ref="browseObject"
|
||||
:showEditView="true"
|
||||
data-selectable
|
||||
>
|
||||
</object-view>
|
||||
<component class="l-shell__time-conductor"
|
||||
:is="conductorComponent">
|
||||
</component>
|
||||
</pane>
|
||||
<pane class="l-shell__pane-inspector l-pane--holds-multipane"
|
||||
handle="before"
|
||||
label="Inspect"
|
||||
collapsable>
|
||||
<Inspector :isEditing="isEditing" ref="inspector"></Inspector>
|
||||
</pane>
|
||||
</multipane>
|
||||
<app-logo />
|
||||
</div>
|
||||
<multipane
|
||||
class="l-shell__main"
|
||||
type="horizontal"
|
||||
>
|
||||
<pane
|
||||
class="l-shell__pane-tree"
|
||||
handle="after"
|
||||
label="Browse"
|
||||
collapsable
|
||||
>
|
||||
<mct-tree class="l-shell__tree" />
|
||||
</pane>
|
||||
<pane class="l-shell__pane-main">
|
||||
<browse-bar
|
||||
ref="browseBar"
|
||||
class="l-shell__main-view-browse-bar"
|
||||
/>
|
||||
<toolbar
|
||||
v-if="toolbar"
|
||||
class="l-shell__toolbar"
|
||||
/>
|
||||
<object-view
|
||||
ref="browseObject"
|
||||
class="l-shell__main-container"
|
||||
:show-edit-view="true"
|
||||
data-selectable
|
||||
/>
|
||||
<component
|
||||
:is="conductorComponent"
|
||||
class="l-shell__time-conductor"
|
||||
/>
|
||||
</pane>
|
||||
<pane
|
||||
class="l-shell__pane-inspector l-pane--holds-multipane"
|
||||
handle="before"
|
||||
label="Inspect"
|
||||
collapsable
|
||||
>
|
||||
<Inspector
|
||||
ref="inspector"
|
||||
:is-editing="isEditing"
|
||||
/>
|
||||
</pane>
|
||||
</multipane>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -302,126 +326,126 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Inspector from '../inspector/Inspector.vue';
|
||||
import MctTree from './mct-tree.vue';
|
||||
import ObjectView from '../components/ObjectView.vue';
|
||||
import MctTemplate from '../legacy/mct-template.vue';
|
||||
import CreateButton from './CreateButton.vue';
|
||||
import multipane from './multipane.vue';
|
||||
import pane from './pane.vue';
|
||||
import BrowseBar from './BrowseBar.vue';
|
||||
import Toolbar from '../toolbar/Toolbar.vue';
|
||||
import AppLogo from './AppLogo.vue';
|
||||
import Indicators from './status-bar/Indicators.vue';
|
||||
import NotificationBanner from './status-bar/NotificationBanner.vue';
|
||||
import Inspector from '../inspector/Inspector.vue';
|
||||
import MctTree from './mct-tree.vue';
|
||||
import ObjectView from '../components/ObjectView.vue';
|
||||
import MctTemplate from '../legacy/mct-template.vue';
|
||||
import CreateButton from './CreateButton.vue';
|
||||
import multipane from './multipane.vue';
|
||||
import pane from './pane.vue';
|
||||
import BrowseBar from './BrowseBar.vue';
|
||||
import Toolbar from '../toolbar/Toolbar.vue';
|
||||
import AppLogo from './AppLogo.vue';
|
||||
import Indicators from './status-bar/Indicators.vue';
|
||||
import NotificationBanner from './status-bar/NotificationBanner.vue';
|
||||
|
||||
var enterFullScreen = () => {
|
||||
var docElm = document.documentElement;
|
||||
var enterFullScreen = () => {
|
||||
var docElm = document.documentElement;
|
||||
|
||||
if (docElm.requestFullscreen) {
|
||||
docElm.requestFullscreen();
|
||||
} else if (docElm.mozRequestFullScreen) { /* Firefox */
|
||||
docElm.mozRequestFullScreen();
|
||||
} else if (docElm.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
|
||||
docElm.webkitRequestFullscreen();
|
||||
} else if (docElm.msRequestFullscreen) { /* IE/Edge */
|
||||
docElm.msRequestFullscreen();
|
||||
if (docElm.requestFullscreen) {
|
||||
docElm.requestFullscreen();
|
||||
} else if (docElm.mozRequestFullScreen) { /* Firefox */
|
||||
docElm.mozRequestFullScreen();
|
||||
} else if (docElm.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
|
||||
docElm.webkitRequestFullscreen();
|
||||
} else if (docElm.msRequestFullscreen) { /* IE/Edge */
|
||||
docElm.msRequestFullscreen();
|
||||
}
|
||||
};
|
||||
var exitFullScreen = () => {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen();
|
||||
}
|
||||
else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
Inspector,
|
||||
MctTree,
|
||||
ObjectView,
|
||||
'mct-template': MctTemplate,
|
||||
CreateButton,
|
||||
multipane,
|
||||
pane,
|
||||
BrowseBar,
|
||||
Toolbar,
|
||||
AppLogo,
|
||||
Indicators,
|
||||
NotificationBanner
|
||||
},
|
||||
data: function () {
|
||||
let storedHeadProps = window.localStorage.getItem('openmct-shell-head');
|
||||
let headExpanded = true;
|
||||
if (storedHeadProps) {
|
||||
headExpanded = JSON.parse(storedHeadProps).expanded;
|
||||
}
|
||||
};
|
||||
var exitFullScreen = () => {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
|
||||
return {
|
||||
fullScreen: false,
|
||||
conductorComponent: undefined,
|
||||
isEditing: false,
|
||||
hasToolbar: false,
|
||||
headExpanded
|
||||
}
|
||||
else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
},
|
||||
computed: {
|
||||
toolbar() {
|
||||
return this.hasToolbar && this.isEditing;
|
||||
}
|
||||
else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen();
|
||||
}
|
||||
else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
Inspector,
|
||||
MctTree,
|
||||
ObjectView,
|
||||
'mct-template': MctTemplate,
|
||||
CreateButton,
|
||||
multipane,
|
||||
pane,
|
||||
BrowseBar,
|
||||
Toolbar,
|
||||
AppLogo,
|
||||
Indicators,
|
||||
NotificationBanner
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', (isEditing)=>{
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
|
||||
this.openmct.selection.on('change', this.toggleHasToolbar);
|
||||
},
|
||||
data: function () {
|
||||
let storedHeadProps = window.localStorage.getItem('openmct-shell-head');
|
||||
let headExpanded = true;
|
||||
if (storedHeadProps) {
|
||||
headExpanded = JSON.parse(storedHeadProps).expanded;
|
||||
}
|
||||
|
||||
return {
|
||||
fullScreen: false,
|
||||
conductorComponent: undefined,
|
||||
isEditing: false,
|
||||
hasToolbar: false,
|
||||
headExpanded
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
toolbar() {
|
||||
return this.hasToolbar && this.isEditing;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleShellHead() {
|
||||
this.headExpanded = !this.headExpanded;
|
||||
|
||||
window.localStorage.setItem(
|
||||
'openmct-shell-head',
|
||||
JSON.stringify(
|
||||
{
|
||||
expanded: this.headExpanded
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
fullScreenToggle() {
|
||||
if (this.fullScreen) {
|
||||
this.fullScreen = false;
|
||||
exitFullScreen();
|
||||
} else {
|
||||
this.fullScreen = true;
|
||||
enterFullScreen();
|
||||
}
|
||||
},
|
||||
openInNewTab(event) {
|
||||
window.open(window.location.href);
|
||||
},
|
||||
toggleHasToolbar(selection) {
|
||||
let structure = undefined;
|
||||
|
||||
if (!selection || !selection[0]) {
|
||||
structure = [];
|
||||
} else {
|
||||
structure = this.openmct.toolbars.get(selection);
|
||||
}
|
||||
|
||||
this.hasToolbar = structure.length > 0;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', (isEditing)=>{
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
|
||||
this.openmct.selection.on('change', this.toggleHasToolbar);
|
||||
},
|
||||
methods: {
|
||||
toggleShellHead() {
|
||||
this.headExpanded = !this.headExpanded;
|
||||
|
||||
window.localStorage.setItem(
|
||||
'openmct-shell-head',
|
||||
JSON.stringify(
|
||||
{
|
||||
expanded: this.headExpanded
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
fullScreenToggle() {
|
||||
if (this.fullScreen) {
|
||||
this.fullScreen = false;
|
||||
exitFullScreen();
|
||||
} else {
|
||||
this.fullScreen = true;
|
||||
enterFullScreen();
|
||||
}
|
||||
},
|
||||
openInNewTab(event) {
|
||||
window.open(window.location.href);
|
||||
},
|
||||
toggleHasToolbar(selection) {
|
||||
let structure = undefined;
|
||||
|
||||
if (!selection || !selection[0]) {
|
||||
structure = [];
|
||||
} else {
|
||||
structure = this.openmct.toolbars.get(selection);
|
||||
}
|
||||
|
||||
this.hasToolbar = structure.length > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div class="c-search c-search--major">
|
||||
<input type="search" placeholder="Search"/>
|
||||
</div>
|
||||
<div class="c-search c-search--major">
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -20,6 +23,6 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
}
|
||||
export default {
|
||||
}
|
||||
</script>
|
||||
|
@ -1,39 +1,60 @@
|
||||
<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
|
||||
v-if="views.length > 1"
|
||||
class="l-browse-bar__view-switcher c-ctrl-wrapper c-ctrl-wrapper--menus-left"
|
||||
>
|
||||
<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
|
||||
v-show="showViewMenu"
|
||||
class="c-menu"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(view, index) in views"
|
||||
:key="index"
|
||||
:class="view.cssClass"
|
||||
:title="view.name"
|
||||
@click="setView(view)"
|
||||
>
|
||||
{{ view.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: [
|
||||
'currentView',
|
||||
'views'
|
||||
],
|
||||
props: {
|
||||
currentView: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
views: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showViewMenu: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.hideViewMenu);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('click', this.hideViewMenu);
|
||||
},
|
||||
methods: {
|
||||
setView(view) {
|
||||
this.$emit('setView', view);
|
||||
@ -44,12 +65,6 @@ export default {
|
||||
hideViewMenu() {
|
||||
this.showViewMenu = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.hideViewMenu);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('click', this.hideViewMenu);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,44 +1,56 @@
|
||||
<template>
|
||||
<div class="c-tree-and-search">
|
||||
<div class="c-tree-and-search__search">
|
||||
<search class="c-search" ref="shell-search"
|
||||
:value="searchValue"
|
||||
@input="searchTree"
|
||||
@clear="searchTree">
|
||||
</search>
|
||||
</div>
|
||||
|
||||
<!-- loading -->
|
||||
<div class="c-tree-and-search__loading loading"
|
||||
v-if="isLoading"></div>
|
||||
<!-- end loading -->
|
||||
|
||||
<div class="c-tree-and-search__no-results"
|
||||
v-if="(allTreeItems.length === 0) || (searchValue && filteredTreeItems.length === 0)">
|
||||
No results found
|
||||
</div>
|
||||
|
||||
<!-- main tree -->
|
||||
<ul class="c-tree-and-search__tree c-tree"
|
||||
v-if="!isLoading"
|
||||
v-show="!searchValue">
|
||||
<tree-item v-for="treeItem in allTreeItems"
|
||||
:key="treeItem.id"
|
||||
:node="treeItem">
|
||||
</tree-item>
|
||||
</ul>
|
||||
<!-- end main tree -->
|
||||
|
||||
<!-- search tree -->
|
||||
<ul class="c-tree-and-search__tree c-tree"
|
||||
v-if="searchValue">
|
||||
<tree-item v-for="treeItem in filteredTreeItems"
|
||||
:key="treeItem.id"
|
||||
:node="treeItem">
|
||||
</tree-item>
|
||||
</ul>
|
||||
<!-- end search tree -->
|
||||
<div class="c-tree-and-search">
|
||||
<div class="c-tree-and-search__search">
|
||||
<search
|
||||
ref="shell-search"
|
||||
class="c-search"
|
||||
:value="searchValue"
|
||||
@input="searchTree"
|
||||
@clear="searchTree"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- loading -->
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="c-tree-and-search__loading loading"
|
||||
></div>
|
||||
<!-- end loading -->
|
||||
|
||||
<div
|
||||
v-if="(allTreeItems.length === 0) || (searchValue && filteredTreeItems.length === 0)"
|
||||
class="c-tree-and-search__no-results"
|
||||
>
|
||||
No results found
|
||||
</div>
|
||||
|
||||
<!-- main tree -->
|
||||
<ul
|
||||
v-if="!isLoading"
|
||||
v-show="!searchValue"
|
||||
class="c-tree-and-search__tree c-tree"
|
||||
>
|
||||
<tree-item
|
||||
v-for="treeItem in allTreeItems"
|
||||
:key="treeItem.id"
|
||||
:node="treeItem"
|
||||
/>
|
||||
</ul>
|
||||
<!-- end main tree -->
|
||||
|
||||
<!-- search tree -->
|
||||
<ul
|
||||
v-if="searchValue"
|
||||
class="c-tree-and-search__tree c-tree"
|
||||
>
|
||||
<tree-item
|
||||
v-for="treeItem in filteredTreeItems"
|
||||
:key="treeItem.id"
|
||||
:node="treeItem"
|
||||
/>
|
||||
</ul>
|
||||
<!-- end search tree -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -185,81 +197,81 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import treeItem from './tree-item.vue'
|
||||
import search from '../components/search.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
name: 'mct-tree',
|
||||
components: {
|
||||
search,
|
||||
treeItem
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchValue: '',
|
||||
allTreeItems: [],
|
||||
filteredTreeItems: [],
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAllChildren() {
|
||||
this.isLoading = true;
|
||||
this.openmct.objects.get('ROOT')
|
||||
.then(root => {
|
||||
return this.openmct.composition.get(root).load()
|
||||
})
|
||||
.then(children => {
|
||||
this.isLoading = false;
|
||||
this.allTreeItems = children.map(c => {
|
||||
return {
|
||||
id: this.openmct.objects.makeKeyString(c.identifier),
|
||||
object: c,
|
||||
objectPath: [c],
|
||||
navigateToParent: '/browse'
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
getFilteredChildren() {
|
||||
this.searchService.query(this.searchValue).then(children => {
|
||||
this.filteredTreeItems = children.hits.map(child => {
|
||||
|
||||
let context = child.object.getCapability('context'),
|
||||
object = child.object.useCapability('adapter'),
|
||||
objectPath = [],
|
||||
navigateToParent;
|
||||
|
||||
if (context) {
|
||||
objectPath = context.getPath().slice(1)
|
||||
.map(oldObject => oldObject.useCapability('adapter'))
|
||||
.reverse();
|
||||
navigateToParent = '/browse/' + objectPath.slice(1)
|
||||
.map((parent) => this.openmct.objects.makeKeyString(parent.identifier))
|
||||
.join('/');
|
||||
}
|
||||
import treeItem from './tree-item.vue'
|
||||
import search from '../components/search.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
name: 'MctTree',
|
||||
components: {
|
||||
search,
|
||||
treeItem
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchValue: '',
|
||||
allTreeItems: [],
|
||||
filteredTreeItems: [],
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.searchService = this.openmct.$injector.get('searchService');
|
||||
this.getAllChildren();
|
||||
},
|
||||
methods: {
|
||||
getAllChildren() {
|
||||
this.isLoading = true;
|
||||
this.openmct.objects.get('ROOT')
|
||||
.then(root => {
|
||||
return this.openmct.composition.get(root).load()
|
||||
})
|
||||
.then(children => {
|
||||
this.isLoading = false;
|
||||
this.allTreeItems = children.map(c => {
|
||||
return {
|
||||
id: this.openmct.objects.makeKeyString(object.identifier),
|
||||
object,
|
||||
objectPath,
|
||||
navigateToParent
|
||||
}
|
||||
id: this.openmct.objects.makeKeyString(c.identifier),
|
||||
object: c,
|
||||
objectPath: [c],
|
||||
navigateToParent: '/browse'
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
searchTree(value) {
|
||||
this.searchValue = value;
|
||||
|
||||
if (this.searchValue !== '') {
|
||||
this.getFilteredChildren();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.searchService = this.openmct.$injector.get('searchService');
|
||||
this.getAllChildren();
|
||||
getFilteredChildren() {
|
||||
this.searchService.query(this.searchValue).then(children => {
|
||||
this.filteredTreeItems = children.hits.map(child => {
|
||||
|
||||
let context = child.object.getCapability('context'),
|
||||
object = child.object.useCapability('adapter'),
|
||||
objectPath = [],
|
||||
navigateToParent;
|
||||
|
||||
if (context) {
|
||||
objectPath = context.getPath().slice(1)
|
||||
.map(oldObject => oldObject.useCapability('adapter'))
|
||||
.reverse();
|
||||
navigateToParent = '/browse/' + objectPath.slice(1)
|
||||
.map((parent) => this.openmct.objects.makeKeyString(parent.identifier))
|
||||
.join('/');
|
||||
}
|
||||
|
||||
return {
|
||||
id: this.openmct.objects.makeKeyString(object.identifier),
|
||||
object,
|
||||
objectPath,
|
||||
navigateToParent
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
searchTree(value) {
|
||||
this.searchValue = value;
|
||||
|
||||
if (this.searchValue !== '') {
|
||||
this.getFilteredChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div class="l-multipane"
|
||||
:class="{
|
||||
'l-multipane--vertical': type === 'vertical',
|
||||
'l-multipane--horizontal': type === 'horizontal'
|
||||
}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div
|
||||
class="l-multipane"
|
||||
:class="{
|
||||
'l-multipane--vertical': type === 'vertical',
|
||||
'l-multipane--horizontal': type === 'horizontal'
|
||||
}"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -13,6 +15,7 @@ export default {
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: function (value) {
|
||||
return ['vertical', 'horizontal'].indexOf(value) !== -1;
|
||||
}
|
||||
|
@ -1,29 +1,36 @@
|
||||
<template>
|
||||
<div class="l-pane"
|
||||
:class="{
|
||||
'l-pane--horizontal-handle-before': type === 'horizontal' && handle === 'before',
|
||||
'l-pane--horizontal-handle-after': type === 'horizontal' && handle === 'after',
|
||||
'l-pane--vertical-handle-before': type === 'vertical' && handle === 'before',
|
||||
'l-pane--vertical-handle-after': type === 'vertical' && handle === 'after',
|
||||
'l-pane--collapsed': collapsed,
|
||||
'l-pane--reacts': !handle,
|
||||
'l-pane--resizing': resizing === true
|
||||
}">
|
||||
<div v-if="handle"
|
||||
class="l-pane__handle"
|
||||
@mousedown="start">
|
||||
</div>
|
||||
<div class="l-pane__header"
|
||||
v-if="label">
|
||||
<span class="l-pane__label">{{ label }}</span>
|
||||
<button class="l-pane__collapse-button c-button"
|
||||
v-if="collapsable"
|
||||
@click="toggleCollapse"></button>
|
||||
</div>
|
||||
<div class="l-pane__contents">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div
|
||||
class="l-pane"
|
||||
:class="{
|
||||
'l-pane--horizontal-handle-before': type === 'horizontal' && handle === 'before',
|
||||
'l-pane--horizontal-handle-after': type === 'horizontal' && handle === 'after',
|
||||
'l-pane--vertical-handle-before': type === 'vertical' && handle === 'before',
|
||||
'l-pane--vertical-handle-after': type === 'vertical' && handle === 'after',
|
||||
'l-pane--collapsed': collapsed,
|
||||
'l-pane--reacts': !handle,
|
||||
'l-pane--resizing': resizing === true
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="handle"
|
||||
class="l-pane__handle"
|
||||
@mousedown="start"
|
||||
></div>
|
||||
<div
|
||||
v-if="label"
|
||||
class="l-pane__header"
|
||||
>
|
||||
<span class="l-pane__label">{{ label }}</span>
|
||||
<button
|
||||
v-if="collapsable"
|
||||
class="l-pane__collapse-button c-button"
|
||||
@click="toggleCollapse"
|
||||
></button>
|
||||
</div>
|
||||
<div class="l-pane__contents">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -62,7 +69,7 @@
|
||||
// __handle and __label don't appear in mobile
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -350,15 +357,19 @@ export default {
|
||||
props: {
|
||||
handle: {
|
||||
type: String,
|
||||
default: '',
|
||||
validator: function (value) {
|
||||
return ['before', 'after'].indexOf(value) !== -1;
|
||||
return ['', 'before', 'after'].indexOf(value) !== -1;
|
||||
}
|
||||
},
|
||||
collapsable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
label: String
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -384,7 +395,7 @@ export default {
|
||||
delete this.dragCollapse;
|
||||
}
|
||||
},
|
||||
trackSize: function() {
|
||||
trackSize: function () {
|
||||
if (!this.dragCollapse === true) {
|
||||
if (this.type === 'vertical') {
|
||||
this.initial = this.$el.offsetHeight;
|
||||
|
@ -17,6 +17,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -152,13 +153,13 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
|
||||
mounted() {
|
||||
this.openmct.indicators.indicatorObjects.forEach((indicator) => {
|
||||
this.$el.appendChild(indicator.element);
|
||||
});
|
||||
}
|
||||
mounted() {
|
||||
this.openmct.indicators.indicatorObjects.forEach((indicator) => {
|
||||
this.$el.appendChild(indicator.element);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -17,22 +17,27 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<template>
|
||||
<div class="c-message-banner"
|
||||
<div
|
||||
v-if="activeModel.message"
|
||||
class="c-message-banner"
|
||||
:class="[
|
||||
activeModel.severity,
|
||||
{
|
||||
'minimized': activeModel.minimized,
|
||||
'new': !activeModel.minimized
|
||||
}]"
|
||||
@click="maximize()"
|
||||
v-if="activeModel.message">
|
||||
<span class="c-message-banner__message">{{activeModel.message}}</span>
|
||||
@click="maximize()"
|
||||
>
|
||||
<span class="c-message-banner__message">{{ activeModel.message }}</span>
|
||||
<progress-bar
|
||||
class="c-message-banner__progress-bar"
|
||||
v-if="activeModel.progressPerc !== undefined" :model="activeModel">
|
||||
</progress-bar>
|
||||
<button class="c-message-banner__close-button c-click-icon icon-x-in-circle"
|
||||
@click.stop="dismiss()"></button>
|
||||
v-if="activeModel.progressPerc !== undefined"
|
||||
class="c-message-banner__progress-bar"
|
||||
:model="activeModel"
|
||||
/>
|
||||
<button
|
||||
class="c-message-banner__close-button c-click-icon icon-x-in-circle"
|
||||
@click.stop="dismiss()"
|
||||
></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -116,127 +121,126 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ProgressBar from '../../components/ProgressBar.vue';
|
||||
let activeNotification = undefined;
|
||||
let dialogService = undefined;
|
||||
let maximizedDialog = undefined;
|
||||
let minimizeButton = {
|
||||
label: 'Dismiss',
|
||||
callback: dismissMaximizedDialog
|
||||
}
|
||||
import ProgressBar from '../../components/ProgressBar.vue';
|
||||
let activeNotification = undefined;
|
||||
let maximizedDialog = undefined;
|
||||
let minimizeButton = {
|
||||
label: 'Dismiss',
|
||||
callback: dismissMaximizedDialog
|
||||
}
|
||||
|
||||
function dismissMaximizedDialog() {
|
||||
if (maximizedDialog) {
|
||||
maximizedDialog.dismiss();
|
||||
maximizedDialog = undefined;
|
||||
function dismissMaximizedDialog() {
|
||||
if (maximizedDialog) {
|
||||
maximizedDialog.dismiss();
|
||||
maximizedDialog = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function updateMaxProgressBar(progressPerc, progressText) {
|
||||
if (maximizedDialog) {
|
||||
maximizedDialog.updateProgress(progressPerc, progressText);
|
||||
|
||||
if (progressPerc >= 100) {
|
||||
dismissMaximizedDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateMaxProgressBar(progressPerc, progressText) {
|
||||
if (maximizedDialog) {
|
||||
maximizedDialog.updateProgress(progressPerc, progressText);
|
||||
|
||||
if (progressPerc >= 100) {
|
||||
dismissMaximizedDialog();
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
ProgressBar: ProgressBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeModel: {
|
||||
message: undefined,
|
||||
progressPerc: undefined,
|
||||
progressText: undefined,
|
||||
minimized: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
ProgressBar: ProgressBar
|
||||
},
|
||||
data() {
|
||||
},
|
||||
computed: {
|
||||
progressWidth() {
|
||||
return {
|
||||
activeModel: {
|
||||
message: undefined,
|
||||
progressPerc: undefined,
|
||||
progressText: undefined,
|
||||
minimized: undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showNotification(notification) {
|
||||
if (activeNotification) {
|
||||
activeNotification.off('progress', this.updateProgress);
|
||||
activeNotification.off('minimized', this.minimized);
|
||||
activeNotification.off('destroy', this.destroyActiveNotification);
|
||||
}
|
||||
activeNotification = notification;
|
||||
this.clearModel();
|
||||
this.applyModel(notification.model);
|
||||
|
||||
activeNotification.once('destroy', this.destroyActiveNotification);
|
||||
activeNotification.on('progress', this.updateProgress);
|
||||
activeNotification.on('minimized', this.minimized);
|
||||
},
|
||||
isEqual(modelA, modelB) {
|
||||
return modelA.message === modelB.message &&
|
||||
modelA.timestamp === modelB.timestamp;
|
||||
},
|
||||
applyModel(model) {
|
||||
Object.keys(model).forEach((key) => this.activeModel[key] = model[key]);
|
||||
},
|
||||
clearModel() {
|
||||
Object.keys(this.activeModel).forEach((key) => this.activeModel[key] = undefined);
|
||||
},
|
||||
updateProgress(progressPerc, progressText) {
|
||||
this.activeModel.progressPerc = progressPerc;
|
||||
this.activeModel.progressText = progressText;
|
||||
},
|
||||
destroyActiveNotification() {
|
||||
this.clearModel();
|
||||
activeNotification.off('destroy', this.destroyActiveNotification);
|
||||
activeNotification = undefined;
|
||||
},
|
||||
dismiss() {
|
||||
if (activeNotification.model.severity === 'info') {
|
||||
activeNotification.dismiss();
|
||||
} else {
|
||||
this.openmct.notifications._minimize(activeNotification);
|
||||
}
|
||||
},
|
||||
minimized() {
|
||||
this.activeModel.minimized = true;
|
||||
width: this.activeModel.progress + '%'
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.notifications.on('notification', this.showNotification);
|
||||
},
|
||||
methods: {
|
||||
showNotification(notification) {
|
||||
if (activeNotification) {
|
||||
activeNotification.off('progress', this.updateProgress);
|
||||
activeNotification.off('minimized', this.minimized);
|
||||
activeNotification.off('destroy', this.destroyActiveNotification);
|
||||
}
|
||||
activeNotification = notification;
|
||||
this.clearModel();
|
||||
this.applyModel(notification.model);
|
||||
|
||||
activeNotification.off('progress', updateMaxProgressBar);
|
||||
activeNotification.off('minimized', dismissMaximizedDialog);
|
||||
activeNotification.off('destroy', dismissMaximizedDialog);
|
||||
},
|
||||
maximize() {
|
||||
if (this.activeModel.progressPerc !== undefined) {
|
||||
maximizedDialog = this.openmct.overlays.progressDialog({
|
||||
buttons: [minimizeButton],
|
||||
...this.activeModel
|
||||
});
|
||||
|
||||
activeNotification.on('progress', updateMaxProgressBar);
|
||||
activeNotification.on('minimized', dismissMaximizedDialog);
|
||||
activeNotification.on('destroy', dismissMaximizedDialog);
|
||||
|
||||
} else {
|
||||
maximizedDialog = this.openmct.overlays.dialog({
|
||||
iconClass: this.activeModel.severity,
|
||||
buttons: [minimizeButton],
|
||||
...this.activeModel
|
||||
})
|
||||
}
|
||||
activeNotification.once('destroy', this.destroyActiveNotification);
|
||||
activeNotification.on('progress', this.updateProgress);
|
||||
activeNotification.on('minimized', this.minimized);
|
||||
},
|
||||
isEqual(modelA, modelB) {
|
||||
return modelA.message === modelB.message &&
|
||||
modelA.timestamp === modelB.timestamp;
|
||||
},
|
||||
applyModel(model) {
|
||||
Object.keys(model).forEach((key) => this.activeModel[key] = model[key]);
|
||||
},
|
||||
clearModel() {
|
||||
Object.keys(this.activeModel).forEach((key) => this.activeModel[key] = undefined);
|
||||
},
|
||||
updateProgress(progressPerc, progressText) {
|
||||
this.activeModel.progressPerc = progressPerc;
|
||||
this.activeModel.progressText = progressText;
|
||||
},
|
||||
destroyActiveNotification() {
|
||||
this.clearModel();
|
||||
activeNotification.off('destroy', this.destroyActiveNotification);
|
||||
activeNotification = undefined;
|
||||
},
|
||||
dismiss() {
|
||||
if (activeNotification.model.severity === 'info') {
|
||||
activeNotification.dismiss();
|
||||
} else {
|
||||
this.openmct.notifications._minimize(activeNotification);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
progressWidth() {
|
||||
return {
|
||||
width: this.activeModel.progress + '%'
|
||||
};
|
||||
}
|
||||
minimized() {
|
||||
this.activeModel.minimized = true;
|
||||
activeNotification.off('progress', this.updateProgress);
|
||||
activeNotification.off('minimized', this.minimized);
|
||||
|
||||
activeNotification.off('progress', updateMaxProgressBar);
|
||||
activeNotification.off('minimized', dismissMaximizedDialog);
|
||||
activeNotification.off('destroy', dismissMaximizedDialog);
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.notifications.on('notification', this.showNotification);
|
||||
maximize() {
|
||||
if (this.activeModel.progressPerc !== undefined) {
|
||||
maximizedDialog = this.openmct.overlays.progressDialog({
|
||||
buttons: [minimizeButton],
|
||||
...this.activeModel
|
||||
});
|
||||
|
||||
activeNotification.on('progress', updateMaxProgressBar);
|
||||
activeNotification.on('minimized', dismissMaximizedDialog);
|
||||
activeNotification.on('destroy', dismissMaximizedDialog);
|
||||
|
||||
} else {
|
||||
maximizedDialog = this.openmct.overlays.dialog({
|
||||
iconClass: this.activeModel.severity,
|
||||
buttons: [minimizeButton],
|
||||
...this.activeModel
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@ -1,136 +1,149 @@
|
||||
<template>
|
||||
<li class="c-tree__item-h">
|
||||
<div class="c-tree__item"
|
||||
:class="{ 'is-alias': isAlias, 'is-navigated-object': isNavigated }">
|
||||
<view-control class="c-tree__item__view-control"
|
||||
:enabled="hasChildren"
|
||||
v-model="expanded">
|
||||
</view-control>
|
||||
<object-label :domainObject="node.object"
|
||||
:objectPath="node.objectPath"
|
||||
:navigateToPath="navigateToPath">
|
||||
</object-label>
|
||||
</div>
|
||||
<ul v-if="expanded" class="c-tree">
|
||||
<li class="c-tree__item-h"
|
||||
v-if="isLoading && !loaded">
|
||||
<div class="c-tree__item loading">
|
||||
<span class="c-tree__item__label">Loading...</span>
|
||||
</div>
|
||||
</li>
|
||||
<tree-item v-for="child in children"
|
||||
:key="child.id"
|
||||
:node="child">
|
||||
</tree-item>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="c-tree__item-h">
|
||||
<div
|
||||
class="c-tree__item"
|
||||
:class="{ 'is-alias': isAlias, 'is-navigated-object': isNavigated }"
|
||||
>
|
||||
<view-control
|
||||
v-model="expanded"
|
||||
class="c-tree__item__view-control"
|
||||
:enabled="hasChildren"
|
||||
/>
|
||||
<object-label
|
||||
:domain-object="node.object"
|
||||
:object-path="node.objectPath"
|
||||
:navigate-to-path="navigateToPath"
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
v-if="expanded"
|
||||
class="c-tree"
|
||||
>
|
||||
<li
|
||||
v-if="isLoading && !loaded"
|
||||
class="c-tree__item-h"
|
||||
>
|
||||
<div class="c-tree__item loading">
|
||||
<span class="c-tree__item__label">Loading...</span>
|
||||
</div>
|
||||
</li>
|
||||
<tree-item
|
||||
v-for="child in children"
|
||||
:key="child.id"
|
||||
:node="child"
|
||||
/>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import viewControl from '../components/viewControl.vue';
|
||||
import ObjectLabel from '../components/ObjectLabel.vue';
|
||||
import viewControl from '../components/viewControl.vue';
|
||||
import ObjectLabel from '../components/ObjectLabel.vue';
|
||||
|
||||
export default {
|
||||
name: 'tree-item',
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
node: Object
|
||||
},
|
||||
data() {
|
||||
this.navigateToPath = this.buildPathString(this.node.navigateToParent)
|
||||
return {
|
||||
hasChildren: false,
|
||||
isLoading: false,
|
||||
loaded: false,
|
||||
isNavigated: this.navigateToPath === this.openmct.router.currentLocation.path,
|
||||
children: [],
|
||||
expanded: false
|
||||
export default {
|
||||
name: 'TreeItem',
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
viewControl,
|
||||
ObjectLabel
|
||||
},
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
this.navigateToPath = this.buildPathString(this.node.navigateToParent)
|
||||
return {
|
||||
hasChildren: false,
|
||||
isLoading: false,
|
||||
loaded: false,
|
||||
isNavigated: this.navigateToPath === this.openmct.router.currentLocation.path,
|
||||
children: [],
|
||||
expanded: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isAlias() {
|
||||
let parent = this.node.objectPath[1];
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isAlias() {
|
||||
let parent = this.node.objectPath[1];
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
return parentKeyString !== this.node.object.location;
|
||||
let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
|
||||
return parentKeyString !== this.node.object.location;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
expanded(isExpanded) {
|
||||
if (!this.hasChildren) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// TODO: should update on mutation.
|
||||
// TODO: click navigation should not fubar hash quite so much.
|
||||
// TODO: should highlight if navigated to.
|
||||
// TODO: should have context menu.
|
||||
// TODO: should support drag/drop composition
|
||||
// TODO: set isAlias per tree-item
|
||||
if (!this.loaded && !this.isLoading) {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addChild);
|
||||
this.composition.on('remove', this.removeChild);
|
||||
this.composition.load().then(this.finishLoading);
|
||||
this.isLoading = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// TODO: should update on mutation.
|
||||
// TODO: click navigation should not fubar hash quite so much.
|
||||
// TODO: should highlight if navigated to.
|
||||
// TODO: should have context menu.
|
||||
// TODO: should support drag/drop composition
|
||||
// TODO: set isAlias per tree-item
|
||||
|
||||
this.domainObject = this.node.object;
|
||||
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
|
||||
this.domainObject = newObject;
|
||||
this.domainObject = this.node.object;
|
||||
let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
|
||||
this.domainObject = newObject;
|
||||
});
|
||||
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
if (this.openmct.composition.get(this.node.object)) {
|
||||
this.hasChildren = true;
|
||||
}
|
||||
|
||||
this.openmct.router.on('change:path', this.highlightIfNavigated);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.router.off('change:path', this.highlightIfNavigated);
|
||||
if (this.composition) {
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
delete this.composition;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addChild(child) {
|
||||
this.children.push({
|
||||
id: this.openmct.objects.makeKeyString(child.identifier),
|
||||
object: child,
|
||||
objectPath: [child].concat(this.node.objectPath),
|
||||
navigateToParent: this.navigateToPath
|
||||
});
|
||||
|
||||
this.$once('hook:destroyed', removeListener);
|
||||
if (this.openmct.composition.get(this.node.object)) {
|
||||
this.hasChildren = true;
|
||||
}
|
||||
|
||||
this.openmct.router.on('change:path', this.highlightIfNavigated);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.router.off('change:path', this.highlightIfNavigated);
|
||||
if (this.composition) {
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
delete this.composition;
|
||||
}
|
||||
removeChild(identifier) {
|
||||
let removeId = this.openmct.objects.makeKeyString(identifier);
|
||||
this.children = this.children
|
||||
.filter(c => c.id !== removeId);
|
||||
},
|
||||
watch: {
|
||||
expanded(isExpanded) {
|
||||
if (!this.hasChildren) {
|
||||
return;
|
||||
}
|
||||
if (!this.loaded && !this.isLoading) {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.composition.on('add', this.addChild);
|
||||
this.composition.on('remove', this.removeChild);
|
||||
this.composition.load().then(this.finishLoading);
|
||||
this.isLoading = true;
|
||||
}
|
||||
}
|
||||
finishLoading() {
|
||||
this.isLoading = false;
|
||||
this.loaded = true;
|
||||
},
|
||||
methods: {
|
||||
addChild (child) {
|
||||
this.children.push({
|
||||
id: this.openmct.objects.makeKeyString(child.identifier),
|
||||
object: child,
|
||||
objectPath: [child].concat(this.node.objectPath),
|
||||
navigateToParent: this.navigateToPath
|
||||
});
|
||||
},
|
||||
removeChild(identifier) {
|
||||
let removeId = this.openmct.objects.makeKeyString(identifier);
|
||||
this.children = this.children
|
||||
.filter(c => c.id !== removeId);
|
||||
},
|
||||
finishLoading () {
|
||||
this.isLoading = false;
|
||||
this.loaded = true;
|
||||
},
|
||||
buildPathString(parentPath) {
|
||||
return [parentPath, this.openmct.objects.makeKeyString(this.node.object.identifier)].join('/');
|
||||
},
|
||||
highlightIfNavigated(newPath, oldPath){
|
||||
if (newPath === this.navigateToPath) {
|
||||
this.isNavigated = true;
|
||||
} else if (oldPath === this.navigateToPath) {
|
||||
this.isNavigated = false;
|
||||
}
|
||||
}
|
||||
buildPathString(parentPath) {
|
||||
return [parentPath, this.openmct.objects.makeKeyString(this.node.object.identifier)].join('/');
|
||||
},
|
||||
components: {
|
||||
viewControl,
|
||||
ObjectLabel
|
||||
highlightIfNavigated(newPath, oldPath) {
|
||||
if (newPath === this.navigateToPath) {
|
||||
this.isNavigated = true;
|
||||
} else if (oldPath === this.navigateToPath) {
|
||||
this.isNavigated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
templateKey: String
|
||||
templateKey: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let openmct = this.openmct;
|
||||
|
@ -19,37 +19,40 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="l-preview-window">
|
||||
<div class="l-browse-bar">
|
||||
<div class="l-browse-bar__start">
|
||||
<div class="l-browse-bar__object-name--w"
|
||||
:class="type.cssClass">
|
||||
<span class="l-browse-bar__object-name">
|
||||
{{ domainObject.name }}
|
||||
</span>
|
||||
<context-menu-drop-down :object-path="objectPath"></context-menu-drop-down>
|
||||
</div>
|
||||
</div>
|
||||
<div class="l-browse-bar__end">
|
||||
<div class="l-browse-bar__actions">
|
||||
<view-switcher
|
||||
:views="views"
|
||||
:currentView="currentView"
|
||||
@setView="setView">
|
||||
</view-switcher>
|
||||
<button v-if="notebookEnabled"
|
||||
class="l-browse-bar__actions__edit c-button icon-notebook"
|
||||
title="New Notebook entry"
|
||||
@click="snapshot">
|
||||
</button>
|
||||
</div>
|
||||
<template>
|
||||
<div class="l-preview-window">
|
||||
<div class="l-browse-bar">
|
||||
<div class="l-browse-bar__start">
|
||||
<div
|
||||
class="l-browse-bar__object-name--w"
|
||||
:class="type.cssClass"
|
||||
>
|
||||
<span class="l-browse-bar__object-name">
|
||||
{{ domainObject.name }}
|
||||
</span>
|
||||
<context-menu-drop-down :object-path="objectPath" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="l-preview-window__object-view">
|
||||
<div ref="objectView"></div>
|
||||
<div class="l-browse-bar__end">
|
||||
<div class="l-browse-bar__actions">
|
||||
<view-switcher
|
||||
:views="views"
|
||||
:current-view="currentView"
|
||||
@setView="setView"
|
||||
/>
|
||||
<button
|
||||
v-if="notebookEnabled"
|
||||
class="l-browse-bar__actions__edit c-button icon-notebook"
|
||||
title="New Notebook entry"
|
||||
@click="snapshot"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="l-preview-window__object-view">
|
||||
<div ref="objectView"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
@ -83,78 +86,78 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
|
||||
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
|
||||
import NotebookSnapshot from '../utils/notebook-snapshot';
|
||||
<script>
|
||||
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
|
||||
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
|
||||
import NotebookSnapshot from '../utils/notebook-snapshot';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContextMenuDropDown,
|
||||
ViewSwitcher
|
||||
export default {
|
||||
components: {
|
||||
ContextMenuDropDown,
|
||||
ViewSwitcher
|
||||
},
|
||||
inject: [
|
||||
'openmct',
|
||||
'objectPath'
|
||||
],
|
||||
data() {
|
||||
let domainObject = this.objectPath[0];
|
||||
let type = this.openmct.types.get(domainObject.type);
|
||||
|
||||
return {
|
||||
domainObject: domainObject,
|
||||
type: type,
|
||||
notebookEnabled: false,
|
||||
viewKey: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
views() {
|
||||
return this
|
||||
.openmct
|
||||
.objectViews
|
||||
.get(this.domainObject);
|
||||
},
|
||||
inject: [
|
||||
'openmct',
|
||||
'objectPath'
|
||||
],
|
||||
computed: {
|
||||
views() {
|
||||
return this
|
||||
.openmct
|
||||
.objectViews
|
||||
.get(this.domainObject);
|
||||
},
|
||||
currentView() {
|
||||
return this.views.filter(v => v.key === this.viewKey)[0] || {};
|
||||
currentView() {
|
||||
return this.views.filter(v => v.key === this.viewKey)[0] || {};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let view = this.openmct.objectViews.get(this.domainObject)[0];
|
||||
this.setView(view);
|
||||
|
||||
if (this.openmct.types.get('notebook')) {
|
||||
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
|
||||
this.notebookEnabled = true;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.view.destroy();
|
||||
},
|
||||
methods: {
|
||||
snapshot() {
|
||||
let element = document.getElementsByClassName("l-preview-window__object-view")[0];
|
||||
this.notebookSnapshot.capture(this.domainObject, element);
|
||||
},
|
||||
clear() {
|
||||
if (this.view) {
|
||||
this.view.destroy();
|
||||
this.$refs.objectView.innerHTML = '';
|
||||
}
|
||||
delete this.view;
|
||||
delete this.viewContainer;
|
||||
},
|
||||
methods: {
|
||||
snapshot() {
|
||||
let element = document.getElementsByClassName("l-preview-window__object-view")[0];
|
||||
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();
|
||||
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.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, this.objectPath);
|
||||
this.view.show(this.viewContainer, false);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let domainObject = this.objectPath[0];
|
||||
let type = this.openmct.types.get(domainObject.type);
|
||||
|
||||
return {
|
||||
domainObject: domainObject,
|
||||
type: type,
|
||||
notebookEnabled: false,
|
||||
viewKey: undefined
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let view = this.openmct.objectViews.get(this.domainObject)[0];
|
||||
this.setView(view);
|
||||
|
||||
if (this.openmct.types.get('notebook')) {
|
||||
this.notebookSnapshot = new NotebookSnapshot(this.openmct);
|
||||
this.notebookEnabled = true;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.view.destroy();
|
||||
this.view = this.currentView.view(this.domainObject, this.objectPath);
|
||||
this.view.show(this.viewContainer, false);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
@ -1,267 +1,270 @@
|
||||
<template>
|
||||
<div class="c-toolbar">
|
||||
<component v-for="item in structure"
|
||||
:is="item.control"
|
||||
:options="item"
|
||||
@click="triggerMethod(item, $event)"
|
||||
@change="updateObjectValue"></component>
|
||||
</div>
|
||||
<div class="c-toolbar">
|
||||
<component
|
||||
:is="item.control"
|
||||
v-for="(item, index) in structure"
|
||||
:key="index"
|
||||
:options="item"
|
||||
@click="triggerMethod(item, $event)"
|
||||
@change="updateObjectValue"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import toolbarButton from './components/toolbar-button.vue';
|
||||
import toolbarColorPicker from './components/toolbar-color-picker.vue';
|
||||
import toolbarCheckbox from './components/toolbar-checkbox.vue';
|
||||
import toolbarInput from './components/toolbar-input.vue';
|
||||
import toolbarMenu from './components/toolbar-menu.vue';
|
||||
import toolbarSelectMenu from './components/toolbar-select-menu.vue';
|
||||
import toolbarSeparator from './components/toolbar-separator.vue';
|
||||
import toolbarToggleButton from './components/toolbar-toggle-button.vue';
|
||||
import toolbarButton from './components/toolbar-button.vue';
|
||||
import toolbarColorPicker from './components/toolbar-color-picker.vue';
|
||||
import toolbarCheckbox from './components/toolbar-checkbox.vue';
|
||||
import toolbarInput from './components/toolbar-input.vue';
|
||||
import toolbarMenu from './components/toolbar-menu.vue';
|
||||
import toolbarSelectMenu from './components/toolbar-select-menu.vue';
|
||||
import toolbarSeparator from './components/toolbar-separator.vue';
|
||||
import toolbarToggleButton from './components/toolbar-toggle-button.vue';
|
||||
|
||||
import _ from 'lodash';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
toolbarButton,
|
||||
toolbarColorPicker,
|
||||
toolbarCheckbox,
|
||||
toolbarInput,
|
||||
toolbarMenu,
|
||||
toolbarSelectMenu,
|
||||
toolbarSeparator,
|
||||
toolbarToggleButton
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
structure: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleSelection(selection) {
|
||||
this.removeListeners();
|
||||
this.domainObjectsById = {};
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
toolbarButton,
|
||||
toolbarColorPicker,
|
||||
toolbarCheckbox,
|
||||
toolbarInput,
|
||||
toolbarMenu,
|
||||
toolbarSelectMenu,
|
||||
toolbarSeparator,
|
||||
toolbarToggleButton
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
structure: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.handleSelection);
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
|
||||
if (selection.length === 0 || !selection[0][0]) {
|
||||
this.structure = [];
|
||||
return;
|
||||
}
|
||||
// Toolbars may change when edit mode is enabled/disabled, so listen
|
||||
// for edit mode changes and update toolbars if necessary.
|
||||
this.openmct.editor.on('isEditing', this.handleEditing);
|
||||
},
|
||||
methods: {
|
||||
handleSelection(selection) {
|
||||
this.removeListeners();
|
||||
this.domainObjectsById = {};
|
||||
|
||||
let structure = this.openmct.toolbars.get(selection) || [];
|
||||
this.structure = structure.map(toolbarItem => {
|
||||
let domainObject = toolbarItem.domainObject;
|
||||
let formKeys = [];
|
||||
toolbarItem.control = "toolbar-" + toolbarItem.control;
|
||||
if (selection.length === 0 || !selection[0][0]) {
|
||||
this.structure = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (toolbarItem.dialog) {
|
||||
toolbarItem.dialog.sections.forEach(section => {
|
||||
section.rows.forEach(row => {
|
||||
formKeys.push(row.key);
|
||||
})
|
||||
});
|
||||
toolbarItem.formKeys = formKeys;
|
||||
}
|
||||
let structure = this.openmct.toolbars.get(selection) || [];
|
||||
this.structure = structure.map(toolbarItem => {
|
||||
let domainObject = toolbarItem.domainObject;
|
||||
let formKeys = [];
|
||||
toolbarItem.control = "toolbar-" + toolbarItem.control;
|
||||
|
||||
if (domainObject) {
|
||||
toolbarItem.value = this.getValue(domainObject, toolbarItem);
|
||||
this.registerListener(domainObject);
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
});
|
||||
},
|
||||
registerListener(domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!this.domainObjectsById[id]) {
|
||||
this.domainObjectsById[id] = {
|
||||
domainObject: domainObject
|
||||
}
|
||||
this.observeObject(domainObject, id);
|
||||
}
|
||||
},
|
||||
observeObject(domainObject, id) {
|
||||
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function(newObject) {
|
||||
this.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject));
|
||||
this.updateToolbarAfterMutation();
|
||||
}.bind(this));
|
||||
this.unObserveObjects.push(unobserveObject);
|
||||
},
|
||||
updateToolbarAfterMutation() {
|
||||
this.structure = this.structure.map(toolbarItem => {
|
||||
let domainObject = toolbarItem.domainObject;
|
||||
|
||||
if (domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let newObject = this.domainObjectsById[id].newObject;
|
||||
|
||||
if (newObject) {
|
||||
toolbarItem.domainObject = newObject;
|
||||
toolbarItem.value = this.getValue(newObject, toolbarItem);
|
||||
}
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
});
|
||||
|
||||
Object.values(this.domainObjectsById).forEach(function (tracker) {
|
||||
if (tracker.newObject) {
|
||||
tracker.domainObject = tracker.newObject;
|
||||
delete tracker.newObject;
|
||||
}
|
||||
});
|
||||
},
|
||||
getValue(domainObject, toolbarItem) {
|
||||
let value = undefined;
|
||||
let applicableSelectedItems = toolbarItem.applicableSelectedItems;
|
||||
|
||||
if (!applicableSelectedItems && !toolbarItem.property) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (toolbarItem.formKeys) {
|
||||
value = this.getFormValue(domainObject, toolbarItem);
|
||||
} else {
|
||||
let values = [];
|
||||
|
||||
if (applicableSelectedItems) {
|
||||
applicableSelectedItems.forEach(selectionPath => {
|
||||
values.push(this.getPropertyValue(domainObject, toolbarItem, selectionPath));
|
||||
});
|
||||
} else {
|
||||
values.push(this.getPropertyValue(domainObject, toolbarItem));
|
||||
}
|
||||
|
||||
// If all values are the same, use it, otherwise mark the item as non-specific.
|
||||
if (values.every(value => value === values[0])) {
|
||||
value = values[0];
|
||||
toolbarItem.nonSpecific = false;
|
||||
} else {
|
||||
toolbarItem.nonSpecific = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
getPropertyValue(domainObject, toolbarItem, selectionPath, formKey) {
|
||||
let property = this.getItemProperty(toolbarItem, selectionPath);
|
||||
|
||||
if (formKey) {
|
||||
property = property + "." + formKey;
|
||||
}
|
||||
|
||||
return _.get(domainObject, property);
|
||||
},
|
||||
getFormValue(domainObject, toolbarItem) {
|
||||
let value = {};
|
||||
let values = {};
|
||||
|
||||
toolbarItem.formKeys.map(key => {
|
||||
values[key] = [];
|
||||
|
||||
if (toolbarItem.applicableSelectedItems) {
|
||||
toolbarItem.applicableSelectedItems.forEach(selectionPath => {
|
||||
values[key].push(this.getPropertyValue(domainObject, toolbarItem, selectionPath, key));
|
||||
});
|
||||
} else {
|
||||
values[key].push(this.getPropertyValue(domainObject, toolbarItem, undefined, key));
|
||||
}
|
||||
});
|
||||
|
||||
for (const key in values) {
|
||||
if (values[key].every(value => value === values[key][0])) {
|
||||
value[key] = values[key][0];
|
||||
toolbarItem.nonSpecific = false;
|
||||
} else {
|
||||
toolbarItem.nonSpecific = true;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
getItemProperty(item, selectionPath) {
|
||||
return (typeof item.property === "function") ? item.property(selectionPath) : item.property;
|
||||
},
|
||||
removeListeners() {
|
||||
if (this.unObserveObjects) {
|
||||
this.unObserveObjects.forEach((unObserveObject) => {
|
||||
unObserveObject();
|
||||
if (toolbarItem.dialog) {
|
||||
toolbarItem.dialog.sections.forEach(section => {
|
||||
section.rows.forEach(row => {
|
||||
formKeys.push(row.key);
|
||||
})
|
||||
});
|
||||
}
|
||||
this.unObserveObjects = [];
|
||||
},
|
||||
updateObjectValue(value, item) {
|
||||
let changedItemId = this.openmct.objects.makeKeyString(item.domainObject.identifier);
|
||||
|
||||
this.structure = this.structure.map(toolbarItem => {
|
||||
if (toolbarItem.domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
|
||||
|
||||
if (changedItemId === id && _.isEqual(toolbarItem, item)) {
|
||||
toolbarItem.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
});
|
||||
|
||||
// If value is an object, iterate the toolbar structure and mutate all keys in form.
|
||||
// Otherwise, mutate the property.
|
||||
if (value === Object(value)) {
|
||||
this.structure.map(s => {
|
||||
if (s.formKeys) {
|
||||
s.formKeys.forEach(key => {
|
||||
if (item.applicableSelectedItems) {
|
||||
item.applicableSelectedItems.forEach(selectionPath => {
|
||||
this.mutateObject(item, value[key], selectionPath, key);
|
||||
});
|
||||
} else {
|
||||
this.mutateObject(item, value[key], undefined, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (item.applicableSelectedItems) {
|
||||
item.applicableSelectedItems.forEach(selectionPath => {
|
||||
this.mutateObject(item, value, selectionPath);
|
||||
});
|
||||
} else {
|
||||
this.mutateObject(item, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
mutateObject(item, value, selectionPath, formKey) {
|
||||
let property = this.getItemProperty(item, selectionPath);
|
||||
|
||||
if (formKey) {
|
||||
property = property + "." + formKey;
|
||||
toolbarItem.formKeys = formKeys;
|
||||
}
|
||||
|
||||
this.openmct.objects.mutate(item.domainObject, property, value);
|
||||
},
|
||||
triggerMethod(item, event) {
|
||||
if (item.method) {
|
||||
item.method({...event});
|
||||
if (domainObject) {
|
||||
toolbarItem.value = this.getValue(domainObject, toolbarItem);
|
||||
this.registerListener(domainObject);
|
||||
}
|
||||
},
|
||||
handleEditing(isEditing) {
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
|
||||
return toolbarItem;
|
||||
});
|
||||
},
|
||||
registerListener(domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!this.domainObjectsById[id]) {
|
||||
this.domainObjectsById[id] = {
|
||||
domainObject: domainObject
|
||||
}
|
||||
this.observeObject(domainObject, id);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.handleSelection);
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
|
||||
// Toolbars may change when edit mode is enabled/disabled, so listen
|
||||
// for edit mode changes and update toolbars if necessary.
|
||||
this.openmct.editor.on('isEditing', this.handleEditing);
|
||||
observeObject(domainObject, id) {
|
||||
let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
|
||||
this.domainObjectsById[id].newObject = JSON.parse(JSON.stringify(newObject));
|
||||
this.updateToolbarAfterMutation();
|
||||
}.bind(this));
|
||||
this.unObserveObjects.push(unobserveObject);
|
||||
},
|
||||
detroyed() {
|
||||
this.openmct.selection.off('change', this.handleSelection);
|
||||
this.openmct.editor.off('isEditing', this.handleEditing);
|
||||
this.removeListeners();
|
||||
updateToolbarAfterMutation() {
|
||||
this.structure = this.structure.map(toolbarItem => {
|
||||
let domainObject = toolbarItem.domainObject;
|
||||
|
||||
if (domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let newObject = this.domainObjectsById[id].newObject;
|
||||
|
||||
if (newObject) {
|
||||
toolbarItem.domainObject = newObject;
|
||||
toolbarItem.value = this.getValue(newObject, toolbarItem);
|
||||
}
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
});
|
||||
|
||||
Object.values(this.domainObjectsById).forEach(function (tracker) {
|
||||
if (tracker.newObject) {
|
||||
tracker.domainObject = tracker.newObject;
|
||||
delete tracker.newObject;
|
||||
}
|
||||
});
|
||||
},
|
||||
getValue(domainObject, toolbarItem) {
|
||||
let value = undefined;
|
||||
let applicableSelectedItems = toolbarItem.applicableSelectedItems;
|
||||
|
||||
if (!applicableSelectedItems && !toolbarItem.property) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (toolbarItem.formKeys) {
|
||||
value = this.getFormValue(domainObject, toolbarItem);
|
||||
} else {
|
||||
let values = [];
|
||||
|
||||
if (applicableSelectedItems) {
|
||||
applicableSelectedItems.forEach(selectionPath => {
|
||||
values.push(this.getPropertyValue(domainObject, toolbarItem, selectionPath));
|
||||
});
|
||||
} else {
|
||||
values.push(this.getPropertyValue(domainObject, toolbarItem));
|
||||
}
|
||||
|
||||
// If all values are the same, use it, otherwise mark the item as non-specific.
|
||||
if (values.every(val => val === values[0])) {
|
||||
value = values[0];
|
||||
toolbarItem.nonSpecific = false;
|
||||
} else {
|
||||
toolbarItem.nonSpecific = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
getPropertyValue(domainObject, toolbarItem, selectionPath, formKey) {
|
||||
let property = this.getItemProperty(toolbarItem, selectionPath);
|
||||
|
||||
if (formKey) {
|
||||
property = property + "." + formKey;
|
||||
}
|
||||
|
||||
return _.get(domainObject, property);
|
||||
},
|
||||
getFormValue(domainObject, toolbarItem) {
|
||||
let value = {};
|
||||
let values = {};
|
||||
|
||||
toolbarItem.formKeys.map(key => {
|
||||
values[key] = [];
|
||||
|
||||
if (toolbarItem.applicableSelectedItems) {
|
||||
toolbarItem.applicableSelectedItems.forEach(selectionPath => {
|
||||
values[key].push(this.getPropertyValue(domainObject, toolbarItem, selectionPath, key));
|
||||
});
|
||||
} else {
|
||||
values[key].push(this.getPropertyValue(domainObject, toolbarItem, undefined, key));
|
||||
}
|
||||
});
|
||||
|
||||
for (const key in values) {
|
||||
if (values[key].every(val => val === values[key][0])) {
|
||||
value[key] = values[key][0];
|
||||
toolbarItem.nonSpecific = false;
|
||||
} else {
|
||||
toolbarItem.nonSpecific = true;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
getItemProperty(item, selectionPath) {
|
||||
return (typeof item.property === "function") ? item.property(selectionPath) : item.property;
|
||||
},
|
||||
removeListeners() {
|
||||
if (this.unObserveObjects) {
|
||||
this.unObserveObjects.forEach((unObserveObject) => {
|
||||
unObserveObject();
|
||||
});
|
||||
}
|
||||
this.unObserveObjects = [];
|
||||
},
|
||||
updateObjectValue(value, item) {
|
||||
let changedItemId = this.openmct.objects.makeKeyString(item.domainObject.identifier);
|
||||
|
||||
this.structure = this.structure.map(toolbarItem => {
|
||||
if (toolbarItem.domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(toolbarItem.domainObject.identifier);
|
||||
|
||||
if (changedItemId === id && _.isEqual(toolbarItem, item)) {
|
||||
toolbarItem.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
});
|
||||
|
||||
// If value is an object, iterate the toolbar structure and mutate all keys in form.
|
||||
// Otherwise, mutate the property.
|
||||
if (value === Object(value)) {
|
||||
this.structure.map(s => {
|
||||
if (s.formKeys) {
|
||||
s.formKeys.forEach(key => {
|
||||
if (item.applicableSelectedItems) {
|
||||
item.applicableSelectedItems.forEach(selectionPath => {
|
||||
this.mutateObject(item, value[key], selectionPath, key);
|
||||
});
|
||||
} else {
|
||||
this.mutateObject(item, value[key], undefined, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (item.applicableSelectedItems) {
|
||||
item.applicableSelectedItems.forEach(selectionPath => {
|
||||
this.mutateObject(item, value, selectionPath);
|
||||
});
|
||||
} else {
|
||||
this.mutateObject(item, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
mutateObject(item, value, selectionPath, formKey) {
|
||||
let property = this.getItemProperty(item, selectionPath);
|
||||
|
||||
if (formKey) {
|
||||
property = property + "." + formKey;
|
||||
}
|
||||
|
||||
this.openmct.objects.mutate(item.domainObject, property, value);
|
||||
},
|
||||
triggerMethod(item, event) {
|
||||
if (item.method) {
|
||||
item.method({...event});
|
||||
}
|
||||
},
|
||||
handleEditing(isEditing) {
|
||||
this.handleSelection(this.openmct.selection.get());
|
||||
}
|
||||
},
|
||||
detroyed() {
|
||||
this.openmct.selection.off('change', this.handleSelection);
|
||||
this.openmct.editor.off('isEditing', this.handleEditing);
|
||||
this.removeListeners();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,26 +1,33 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-icon-button"
|
||||
:title="options.title"
|
||||
:class="{
|
||||
[options.icon]: true,
|
||||
'c-icon-button--caution': options.modifier === 'caution',
|
||||
'c-icon-button--mixed': nonSpecific
|
||||
}"
|
||||
@click="onClick">
|
||||
<div class="c-icon-button__label"
|
||||
v-if="options.label">
|
||||
{{ options.label }}
|
||||
</div>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div
|
||||
class="c-icon-button"
|
||||
:title="options.title"
|
||||
:class="{
|
||||
[options.icon]: true,
|
||||
'c-icon-button--caution': options.modifier === 'caution',
|
||||
'c-icon-button--mixed': nonSpecific
|
||||
}"
|
||||
@click="onClick"
|
||||
>
|
||||
<div
|
||||
v-if="options.label"
|
||||
class="c-icon-button__label"
|
||||
>
|
||||
{{ options.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
options: Object
|
||||
options: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nonSpecific() {
|
||||
@ -31,10 +38,10 @@ export default {
|
||||
onClick(event) {
|
||||
if (this.options.dialog) {
|
||||
this.openmct.$injector.get('dialogService')
|
||||
.getUserInput(this.options.dialog, this.options.value)
|
||||
.then(value => {
|
||||
this.$emit('change', {...value}, this.options);
|
||||
});
|
||||
.getUserInput(this.options.dialog, this.options.value)
|
||||
.then(value => {
|
||||
this.$emit('change', {...value}, this.options);
|
||||
});
|
||||
}
|
||||
this.$emit('click', this.options);
|
||||
}
|
||||
|
@ -1,19 +1,21 @@
|
||||
<template>
|
||||
<div class="c-custom-checkbox">
|
||||
<input type="checkbox"
|
||||
:id="uid"
|
||||
:name="options.name"
|
||||
:checked="options.value"
|
||||
:disabled="options.disabled"
|
||||
@change="onChange">
|
||||
<div class="c-custom-checkbox">
|
||||
<input
|
||||
:id="uid"
|
||||
type="checkbox"
|
||||
:name="options.name"
|
||||
:checked="options.value"
|
||||
:disabled="options.disabled"
|
||||
@change="onChange"
|
||||
>
|
||||
|
||||
<label :for="uid">
|
||||
<div class="c-custom-checkbox__box"></div>
|
||||
<div class="c-custom-checkbox__label-text">
|
||||
{{options.name}}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<label :for="uid">
|
||||
<div class="c-custom-checkbox__box"></div>
|
||||
<div class="c-custom-checkbox__label-text">
|
||||
{{ options.name }}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -72,7 +74,10 @@ let uniqueId = 100;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
options: Object
|
||||
options: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
uniqueId++;
|
||||
|
@ -1,30 +1,41 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-icon-button c-icon-button--swatched"
|
||||
:class="[options.icon, {'c-icon-button--mixed': nonSpecific}]"
|
||||
:title="options.title"
|
||||
@click="toggle">
|
||||
<div class="c-swatch" :style="{
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div
|
||||
class="c-icon-button c-icon-button--swatched"
|
||||
:class="[options.icon, {'c-icon-button--mixed': nonSpecific}]"
|
||||
:title="options.title"
|
||||
@click="toggle"
|
||||
>
|
||||
<div
|
||||
class="c-swatch"
|
||||
:style="{
|
||||
background: options.value
|
||||
}"></div>
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-if="open"
|
||||
class="c-menu c-palette c-palette--color"
|
||||
>
|
||||
<div
|
||||
v-if="!options.preventNone"
|
||||
class="c-palette__item-none"
|
||||
@click="select({value: 'transparent'})"
|
||||
>
|
||||
<div class="c-palette__item"></div>
|
||||
None
|
||||
</div>
|
||||
<div class="c-menu c-palette c-palette--color"
|
||||
v-if="open">
|
||||
<div class="c-palette__item-none"
|
||||
v-if="!this.options.preventNone"
|
||||
@click="select({value: 'transparent'})">
|
||||
<div class="c-palette__item"></div>
|
||||
None
|
||||
</div>
|
||||
<div class="c-palette__items">
|
||||
<div class="c-palette__item"
|
||||
v-for="color in colorPalette"
|
||||
:style="{ background: color.value }"
|
||||
@click="select(color)"
|
||||
></div>
|
||||
</div>
|
||||
<div class="c-palette__items">
|
||||
<div
|
||||
v-for="(color, index) in colorPalette"
|
||||
:key="index"
|
||||
class="c-palette__item"
|
||||
:style="{ background: color.value }"
|
||||
@click="select(color)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -34,18 +45,9 @@ import toggleMixin from '../../mixins/toggle-mixin';
|
||||
export default {
|
||||
mixins: [toggleMixin],
|
||||
props: {
|
||||
options: Object
|
||||
},
|
||||
computed: {
|
||||
nonSpecific() {
|
||||
return this.options.nonSpecific === true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select(color) {
|
||||
if (color.value !== this.options.value) {
|
||||
this.$emit('change', color.value, this.options);
|
||||
}
|
||||
options: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -133,6 +135,18 @@ export default {
|
||||
{ value: '#4c1130' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
nonSpecific() {
|
||||
return this.options.nonSpecific === true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select(color) {
|
||||
if (color.value !== this.options.value) {
|
||||
this.$emit('change', color.value, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,14 +1,18 @@
|
||||
<template>
|
||||
<div class="c-labeled-input"
|
||||
:title="options.title">
|
||||
<label :for="uid">
|
||||
<div class="c-labeled-input__label">{{ options.label }}</div>
|
||||
</label>
|
||||
<input :id="uid"
|
||||
:type="options.type"
|
||||
:value="options.value"
|
||||
v-bind="options.attrs"/>
|
||||
</div>
|
||||
<div
|
||||
class="c-labeled-input"
|
||||
:title="options.title"
|
||||
>
|
||||
<label :for="uid">
|
||||
<div class="c-labeled-input__label">{{ options.label }}</div>
|
||||
</label>
|
||||
<input
|
||||
:id="uid"
|
||||
:type="options.type"
|
||||
:value="options.value"
|
||||
v-bind="options.attrs"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -19,6 +23,7 @@ export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator(value) {
|
||||
return ['number', 'text'].indexOf(value.type) !== -1;
|
||||
}
|
||||
|
@ -1,24 +1,34 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-icon-button c-icon-button--menu"
|
||||
:class="options.icon"
|
||||
:title="options.title"
|
||||
@click="toggle">
|
||||
<div class="c-icon-button__label"
|
||||
v-if="options.label">
|
||||
{{ options.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-menu" v-if="open">
|
||||
<ul>
|
||||
<li v-for="option in options.options"
|
||||
@click="onClick(option)"
|
||||
:class="option.class">
|
||||
{{ option.name }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div
|
||||
class="c-icon-button c-icon-button--menu"
|
||||
:class="options.icon"
|
||||
:title="options.title"
|
||||
@click="toggle"
|
||||
>
|
||||
<div
|
||||
v-if="options.label"
|
||||
class="c-icon-button__label"
|
||||
>
|
||||
{{ options.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="open"
|
||||
class="c-menu"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(option, index) in options.options"
|
||||
:key="index"
|
||||
:class="option.class"
|
||||
@click="onClick(option)"
|
||||
>
|
||||
{{ option.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -28,6 +38,7 @@ export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator(value) {
|
||||
// must pass valid options array.
|
||||
return Array.isArray(value.options) &&
|
||||
|
@ -1,21 +1,30 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-icon-button c-icon-button--menu"
|
||||
:class="[options.icon, {'c-click-icon--mixed': nonSpecific}]"
|
||||
:title="options.title"
|
||||
@click="toggle">
|
||||
<div class="c-button__label">{{ selectedName }}</div>
|
||||
</div>
|
||||
<div class="c-menu" v-if="open">
|
||||
<ul>
|
||||
<li v-for="option in options.options"
|
||||
:key="option.value"
|
||||
@click="select(option)">
|
||||
{{ option.name || option.value }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div
|
||||
class="c-icon-button c-icon-button--menu"
|
||||
:class="[options.icon, {'c-click-icon--mixed': nonSpecific}]"
|
||||
:title="options.title"
|
||||
@click="toggle"
|
||||
>
|
||||
<div class="c-button__label">
|
||||
{{ selectedName }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="open"
|
||||
class="c-menu"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="option in options.options"
|
||||
:key="option.value"
|
||||
@click="select(option)"
|
||||
>
|
||||
{{ option.name || option.value }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -26,6 +35,7 @@ export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator(value) {
|
||||
// must pass valid options array.
|
||||
return Array.isArray(value.options) &&
|
||||
@ -33,14 +43,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select(option) {
|
||||
if (this.options.value === option.value) {
|
||||
return;
|
||||
}
|
||||
this.$emit('change', option.value, this.options);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedName() {
|
||||
let selectedOption = this.options.options.filter((o) => o.value === this.options.value)[0];
|
||||
@ -53,6 +55,14 @@ export default {
|
||||
nonSpecific() {
|
||||
return this.options.nonSpecific === true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select(option) {
|
||||
if (this.options.value === option.value) {
|
||||
return;
|
||||
}
|
||||
this.$emit('change', option.value, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,11 +1,14 @@
|
||||
<template>
|
||||
<div class="c-toolbar__separator"></div>
|
||||
<div class="c-toolbar__separator"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
options: Object
|
||||
options: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,18 +1,20 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-icon-button"
|
||||
:title="nextValue.title"
|
||||
:class="[nextValue.icon, {'c-icon-button--mixed': nonSpecific}]"
|
||||
@click="cycle">
|
||||
</div>
|
||||
</div>
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div
|
||||
class="c-icon-button"
|
||||
:title="nextValue.title"
|
||||
:class="[nextValue.icon, {'c-icon-button--mixed': nonSpecific}]"
|
||||
@click="cycle"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
Loading…
Reference in New Issue
Block a user