mirror of
https://github.com/nasa/openmct.git
synced 2025-06-02 23:50:49 +00:00
parent
e7e5116773
commit
ee4a81bdfd
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
Open MCT, Copyright (c) 2014-2018, United States Government
|
Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
as represented by the Administrator of the National Aeronautics and Space
|
as represented by the Administrator of the National Aeronautics and Space
|
||||||
Administration. All rights reserved.
|
Administration. All rights reserved.
|
||||||
|
|
||||||
@ -18,4 +18,4 @@
|
|||||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
* Administration. All rights reserved.
|
* Administration. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -18,4 +18,4 @@
|
|||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@ -9,7 +9,8 @@ define([
|
|||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
name: "Name"
|
name: "Name",
|
||||||
|
format: "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "utc",
|
key: "utc",
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
"split": "^1.0.0",
|
"split": "^1.0.0",
|
||||||
"style-loader": "^1.0.1",
|
"style-loader": "^1.0.1",
|
||||||
|
"uuid": "^3.3.3",
|
||||||
"v8-compile-cache": "^1.1.0",
|
"v8-compile-cache": "^1.1.0",
|
||||||
"vue": "2.5.6",
|
"vue": "2.5.6",
|
||||||
"vue-loader": "^15.2.6",
|
"vue-loader": "^15.2.6",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
* Administration. All rights reserved.
|
* Administration. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -264,6 +264,8 @@ define([
|
|||||||
this.install(this.plugins.GoToOriginalAction());
|
this.install(this.plugins.GoToOriginalAction());
|
||||||
this.install(this.plugins.ImportExport());
|
this.install(this.plugins.ImportExport());
|
||||||
this.install(this.plugins.WebPage());
|
this.install(this.plugins.WebPage());
|
||||||
|
this.install(this.plugins.Condition());
|
||||||
|
this.install(this.plugins.ConditionWidget());
|
||||||
}
|
}
|
||||||
|
|
||||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||||
|
@ -21,22 +21,24 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table class="c-table c-lad-table">
|
<div class="c-lad-table-wrapper">
|
||||||
<thead>
|
<table class="c-table c-lad-table">
|
||||||
<tr>
|
<thead>
|
||||||
<th>Name</th>
|
<tr>
|
||||||
<th>Timestamp</th>
|
<th>Name</th>
|
||||||
<th>Value</th>
|
<th>Timestamp</th>
|
||||||
</tr>
|
<th>Value</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
<lad-row
|
<tbody>
|
||||||
v-for="item in items"
|
<lad-row
|
||||||
:key="item.key"
|
v-for="item in items"
|
||||||
:domain-object="item.domainObject"
|
:key="item.key"
|
||||||
/>
|
:domain-object="item.domainObject"
|
||||||
</tbody>
|
/>
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
309
src/plugins/condition/Condition.js
Normal file
309
src/plugins/condition/Condition.js
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
|
import uuid from 'uuid';
|
||||||
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||||
|
import { TRIGGER } from "./utils/constants";
|
||||||
|
import {computeCondition, computeConditionByLimit} from "./utils/evaluator";
|
||||||
|
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* conditionConfiguration = {
|
||||||
|
* id: uuid,
|
||||||
|
* trigger: 'any'/'all'/'not','xor',
|
||||||
|
* criteria: [
|
||||||
|
* {
|
||||||
|
* telemetry: '',
|
||||||
|
* operation: '',
|
||||||
|
* input: [],
|
||||||
|
* metadata: ''
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export default class ConditionClass extends EventEmitter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
|
||||||
|
* @constructor
|
||||||
|
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
|
||||||
|
* @param openmct
|
||||||
|
*/
|
||||||
|
constructor(conditionConfiguration, openmct, conditionManager) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.conditionManager = conditionManager;
|
||||||
|
this.id = conditionConfiguration.id;
|
||||||
|
this.criteria = [];
|
||||||
|
this.criteriaResults = {};
|
||||||
|
this.result = undefined;
|
||||||
|
this.latestTimestamp = {};
|
||||||
|
|
||||||
|
if (conditionConfiguration.configuration.criteria) {
|
||||||
|
this.createCriteria(conditionConfiguration.configuration.criteria);
|
||||||
|
}
|
||||||
|
this.trigger = conditionConfiguration.configuration.trigger;
|
||||||
|
this.conditionManager.on('broadcastTelemetry', this.handleBroadcastTelemetry, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBroadcastTelemetry(datum) {
|
||||||
|
if (!datum || !datum.id) {
|
||||||
|
console.log('no data received');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.criteria.forEach(criterion => {
|
||||||
|
if (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any')) {
|
||||||
|
criterion.handleSubscription(datum, this.conditionManager.telemetryObjects);
|
||||||
|
} else {
|
||||||
|
criterion.emit(`subscription:${datum.id}`, datum);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(conditionConfiguration) {
|
||||||
|
this.updateTrigger(conditionConfiguration.configuration.trigger);
|
||||||
|
this.updateCriteria(conditionConfiguration.configuration.criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTrigger(trigger) {
|
||||||
|
if (this.trigger !== trigger) {
|
||||||
|
this.trigger = trigger;
|
||||||
|
this.handleConditionUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCriterion(criterionConfiguration) {
|
||||||
|
return {
|
||||||
|
id: uuid(),
|
||||||
|
telemetry: criterionConfiguration.telemetry || '',
|
||||||
|
telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)],
|
||||||
|
operation: criterionConfiguration.operation || '',
|
||||||
|
input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
|
||||||
|
metadata: criterionConfiguration.metadata || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createCriteria(criterionConfigurations) {
|
||||||
|
criterionConfigurations.forEach((criterionConfiguration) => {
|
||||||
|
this.addCriterion(criterionConfiguration);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCriteria(criterionConfigurations) {
|
||||||
|
this.destroyCriteria();
|
||||||
|
this.createCriteria(criterionConfigurations);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTelemetry() {
|
||||||
|
this.criteria.forEach((criterion) => {
|
||||||
|
criterion.updateTelemetry(this.conditionManager.telemetryObjects);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds criterion to the condition.
|
||||||
|
*/
|
||||||
|
addCriterion(criterionConfiguration) {
|
||||||
|
let criterion;
|
||||||
|
let criterionConfigurationWithId = this.generateCriterion(criterionConfiguration || null);
|
||||||
|
if (criterionConfiguration.telemetry && (criterionConfiguration.telemetry === 'any' || criterionConfiguration.telemetry === 'all')) {
|
||||||
|
criterion = new AllTelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
||||||
|
} else {
|
||||||
|
criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
|
||||||
|
}
|
||||||
|
criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
|
criterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
||||||
|
if (!this.criteria) {
|
||||||
|
this.criteria = [];
|
||||||
|
}
|
||||||
|
this.criteria.push(criterion);
|
||||||
|
return criterionConfigurationWithId.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
findCriterion(id) {
|
||||||
|
let criterion;
|
||||||
|
|
||||||
|
for (let i=0, ii=this.criteria.length; i < ii; i ++) {
|
||||||
|
if (this.criteria[i].id === id) {
|
||||||
|
criterion = {
|
||||||
|
item: this.criteria[i],
|
||||||
|
index: i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return criterion;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCriterion(id, criterionConfiguration) {
|
||||||
|
let found = this.findCriterion(id);
|
||||||
|
if (found) {
|
||||||
|
const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
|
||||||
|
let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
|
||||||
|
newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
|
newCriterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
||||||
|
|
||||||
|
let criterion = found.item;
|
||||||
|
criterion.unsubscribe();
|
||||||
|
criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
|
||||||
|
criterion.off('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
|
||||||
|
this.criteria.splice(found.index, 1, newCriterion);
|
||||||
|
if (this.criteriaResults[criterion.id] !== undefined) {
|
||||||
|
delete this.criteriaResults[criterion.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCriterion(id) {
|
||||||
|
if (this.destroyCriterion(id)) {
|
||||||
|
this.handleConditionUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyCriterion(id) {
|
||||||
|
let found = this.findCriterion(id);
|
||||||
|
if (found) {
|
||||||
|
let criterion = found.item;
|
||||||
|
criterion.destroy();
|
||||||
|
// TODO this is passing the wrong args
|
||||||
|
criterion.off('criterionUpdated', (result) => {
|
||||||
|
this.handleCriterionUpdated(id, result);
|
||||||
|
});
|
||||||
|
this.criteria.splice(found.index, 1);
|
||||||
|
if (this.criteriaResults[criterion.id] !== undefined) {
|
||||||
|
delete this.criteriaResults[criterion.id];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCriterionUpdated(criterion) {
|
||||||
|
let found = this.findCriterion(criterion.id);
|
||||||
|
if (found) {
|
||||||
|
this.criteria[found.index] = criterion.data;
|
||||||
|
// TODO nothing is listening to this
|
||||||
|
this.emitEvent('conditionUpdated', {
|
||||||
|
trigger: this.trigger,
|
||||||
|
criteria: this.criteria
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCriteriaResults(eventData) {
|
||||||
|
const id = eventData.id;
|
||||||
|
|
||||||
|
if (this.findCriterion(id)) {
|
||||||
|
// The !! here is important to convert undefined to false otherwise the criteriaResults won't get deleted when the criteria is destroyed
|
||||||
|
this.criteriaResults[id] = !!eventData.data.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCriterionResult(eventData) {
|
||||||
|
this.updateCriteriaResults(eventData);
|
||||||
|
this.handleConditionUpdated(eventData.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestLADConditionResult() {
|
||||||
|
const criteriaResults = this.criteria
|
||||||
|
.map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects}));
|
||||||
|
|
||||||
|
return Promise.all(criteriaResults)
|
||||||
|
.then(results => {
|
||||||
|
results.forEach(result => {
|
||||||
|
this.updateCriteriaResults(result);
|
||||||
|
this.latestTimestamp = this.getLatestTimestamp(this.latestTimestamp, result.data)
|
||||||
|
});
|
||||||
|
this.evaluate();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: Object.assign({}, this.latestTimestamp, { result: this.result })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTelemetrySubscriptions() {
|
||||||
|
return this.criteria.map(criterion => criterion.telemetryObjectIdAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConditionUpdated(datum) {
|
||||||
|
// trigger an updated event so that consumers can react accordingly
|
||||||
|
this.evaluate();
|
||||||
|
this.emitEvent('conditionResultUpdated',
|
||||||
|
Object.assign({}, datum, { result: this.result })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCriteria() {
|
||||||
|
return this.criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyCriteria() {
|
||||||
|
let success = true;
|
||||||
|
//looping through the array backwards since destroyCriterion modifies the criteria array
|
||||||
|
for (let i=this.criteria.length-1; i >= 0; i--) {
|
||||||
|
success = success && this.destroyCriterion(this.criteria[i].id);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate() {
|
||||||
|
if (this.trigger && this.trigger === TRIGGER.XOR) {
|
||||||
|
this.result = computeConditionByLimit(this.criteriaResults, 1);
|
||||||
|
} else if (this.trigger && this.trigger === TRIGGER.NOT) {
|
||||||
|
this.result = computeConditionByLimit(this.criteriaResults, 0);
|
||||||
|
} else {
|
||||||
|
this.result = computeCondition(this.criteriaResults, this.trigger === TRIGGER.ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLatestTimestamp(current, compare) {
|
||||||
|
const timestamp = Object.assign({}, current);
|
||||||
|
|
||||||
|
this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
|
||||||
|
if (!timestamp[timeSystem.key]
|
||||||
|
|| compare[timeSystem.key] > timestamp[timeSystem.key]
|
||||||
|
) {
|
||||||
|
timestamp[timeSystem.key] = compare[timeSystem.key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitEvent(eventName, data) {
|
||||||
|
this.emit(eventName, {
|
||||||
|
id: this.id,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.conditionManager.off('broadcastTelemetry', this.handleBroadcastTelemetry, this);
|
||||||
|
if (typeof this.stopObservingForChanges === 'function') {
|
||||||
|
this.stopObservingForChanges();
|
||||||
|
}
|
||||||
|
this.destroyCriteria();
|
||||||
|
}
|
||||||
|
}
|
314
src/plugins/condition/ConditionManager.js
Normal file
314
src/plugins/condition/ConditionManager.js
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import Condition from "./Condition";
|
||||||
|
import uuid from "uuid";
|
||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
|
|
||||||
|
export default class ConditionManager extends EventEmitter {
|
||||||
|
constructor(conditionSetDomainObject, openmct) {
|
||||||
|
super();
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
this.timeAPI = this.openmct.time;
|
||||||
|
this.latestTimestamp = {};
|
||||||
|
this.composition = this.openmct.composition.get(conditionSetDomainObject);
|
||||||
|
this.composition.on('add', this.subscribeToTelemetry, this);
|
||||||
|
this.composition.on('remove', this.unsubscribeFromTelemetry, this);
|
||||||
|
this.compositionLoad = this.composition.load();
|
||||||
|
this.subscriptions = {};
|
||||||
|
this.telemetryObjects = {};
|
||||||
|
this.testData = {conditionTestData: [], applied: false};
|
||||||
|
this.initialize();
|
||||||
|
|
||||||
|
this.stopObservingForChanges = this.openmct.objects.observe(this.conditionSetDomainObject, '*', (newDomainObject) => {
|
||||||
|
this.conditionSetDomainObject = newDomainObject;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToTelemetry(endpoint) {
|
||||||
|
const id = this.openmct.objects.makeKeyString(endpoint.identifier);
|
||||||
|
if (this.subscriptions[id]) {
|
||||||
|
console.log('subscription already exists');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
|
||||||
|
this.subscriptions[id] = this.openmct.telemetry.subscribe(
|
||||||
|
endpoint,
|
||||||
|
this.broadcastTelemetry.bind(this, id)
|
||||||
|
);
|
||||||
|
this.updateConditionTelemetry();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribeFromTelemetry(endpointIdentifier) {
|
||||||
|
const id = this.openmct.objects.makeKeyString(endpointIdentifier);
|
||||||
|
if (!this.subscriptions[id]) {
|
||||||
|
console.log('no subscription to remove');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subscriptions[id]();
|
||||||
|
delete this.subscriptions[id];
|
||||||
|
delete this.telemetryObjects[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.conditionResults = {};
|
||||||
|
this.conditionClassCollection = [];
|
||||||
|
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
|
this.initCondition(conditionConfiguration, index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConditionTelemetry() {
|
||||||
|
this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCondition(conditionConfiguration, index) {
|
||||||
|
let condition = this.conditionClassCollection[index];
|
||||||
|
condition.update(conditionConfiguration);
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
|
||||||
|
this.persistConditions();
|
||||||
|
}
|
||||||
|
|
||||||
|
initCondition(conditionConfiguration, index) {
|
||||||
|
let condition = new Condition(conditionConfiguration, this.openmct, this);
|
||||||
|
condition.on('conditionResultUpdated', this.handleConditionResult.bind(this));
|
||||||
|
if (index !== undefined) {
|
||||||
|
this.conditionClassCollection.splice(index + 1, 0, condition);
|
||||||
|
} else {
|
||||||
|
this.conditionClassCollection.unshift(condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createCondition(conditionConfiguration) {
|
||||||
|
let conditionObj;
|
||||||
|
if (conditionConfiguration) {
|
||||||
|
conditionObj = {
|
||||||
|
...conditionConfiguration,
|
||||||
|
id: uuid(),
|
||||||
|
configuration: {
|
||||||
|
...conditionConfiguration.configuration,
|
||||||
|
name: `Copy of ${conditionConfiguration.configuration.name}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
conditionObj = {
|
||||||
|
id: uuid(),
|
||||||
|
configuration: {
|
||||||
|
name: 'Unnamed Condition',
|
||||||
|
output: 'false',
|
||||||
|
trigger: 'all',
|
||||||
|
criteria: [{
|
||||||
|
telemetry: '',
|
||||||
|
operation: '',
|
||||||
|
input: [],
|
||||||
|
metadata: ''
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
summary: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditionObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
addCondition() {
|
||||||
|
this.createAndSaveCondition();
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneCondition(conditionConfiguration, index) {
|
||||||
|
this.createAndSaveCondition(index, JSON.parse(JSON.stringify(conditionConfiguration)));
|
||||||
|
}
|
||||||
|
|
||||||
|
createAndSaveCondition(index, conditionConfiguration) {
|
||||||
|
const newCondition = this.createCondition(conditionConfiguration);
|
||||||
|
if (index !== undefined) {
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.splice(index + 1, 0, newCondition);
|
||||||
|
} else {
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.unshift(newCondition);
|
||||||
|
}
|
||||||
|
this.initCondition(newCondition, index);
|
||||||
|
this.persistConditions();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCondition(index) {
|
||||||
|
let condition = this.conditionClassCollection[index];
|
||||||
|
condition.destroyCriteria();
|
||||||
|
condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
|
||||||
|
this.conditionClassCollection.splice(index, 1);
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
|
||||||
|
if (this.conditionResults[condition.id] !== undefined) {
|
||||||
|
delete this.conditionResults[condition.id];
|
||||||
|
}
|
||||||
|
this.persistConditions();
|
||||||
|
this.handleConditionResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
findConditionById(id) {
|
||||||
|
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//this.$set(this.conditionClassCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);
|
||||||
|
reorderConditions(reorderPlan) {
|
||||||
|
let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
|
||||||
|
let newCollection = [];
|
||||||
|
reorderPlan.forEach((reorderEvent) => {
|
||||||
|
let item = oldConditions[reorderEvent.oldIndex];
|
||||||
|
newCollection.push(item);
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection = newCollection;
|
||||||
|
});
|
||||||
|
this.persistConditions();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentCondition() {
|
||||||
|
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
|
||||||
|
let currentCondition = conditionCollection[conditionCollection.length-1];
|
||||||
|
|
||||||
|
for (let i = 0; i < conditionCollection.length - 1; i++) {
|
||||||
|
if (this.conditionResults[conditionCollection[i].id]) {
|
||||||
|
//first condition to be true wins
|
||||||
|
currentCondition = conditionCollection[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConditionResults(resultObj) {
|
||||||
|
if (!resultObj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = resultObj.id;
|
||||||
|
|
||||||
|
if (this.findConditionById(id)) {
|
||||||
|
this.conditionResults[id] = resultObj.data.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTimestamp(resultObj.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConditionResult(resultObj) {
|
||||||
|
// update conditions results and then calculate the current condition
|
||||||
|
this.updateConditionResults(resultObj);
|
||||||
|
const currentCondition = this.getCurrentCondition();
|
||||||
|
this.emit('conditionSetResultUpdated',
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
|
output: currentCondition.configuration.output,
|
||||||
|
id: this.conditionSetDomainObject.identifier,
|
||||||
|
conditionId: currentCondition.id
|
||||||
|
},
|
||||||
|
this.latestTimestamp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimestamp(timestamp) {
|
||||||
|
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
||||||
|
if (!this.latestTimestamp[timeSystem.key]
|
||||||
|
|| timestamp[timeSystem.key] > this.latestTimestamp[timeSystem.key]
|
||||||
|
) {
|
||||||
|
this.latestTimestamp[timeSystem.key] = timestamp[timeSystem.key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
requestLADConditionSetOutput() {
|
||||||
|
if (!this.conditionClassCollection.length || this.conditionClassCollection.length === 1) {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.compositionLoad.then(() => {
|
||||||
|
const ladConditionResults = this.conditionClassCollection
|
||||||
|
.map(condition => condition.requestLADConditionResult());
|
||||||
|
|
||||||
|
return Promise.all(ladConditionResults)
|
||||||
|
.then((results) => {
|
||||||
|
results.forEach(resultObj => { this.updateConditionResults(resultObj); });
|
||||||
|
const currentCondition = this.getCurrentCondition();
|
||||||
|
|
||||||
|
return Object.assign(
|
||||||
|
{
|
||||||
|
output: currentCondition.configuration.output,
|
||||||
|
id: this.conditionSetDomainObject.identifier,
|
||||||
|
conditionId: currentCondition.id
|
||||||
|
},
|
||||||
|
this.latestTimestamp
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastTelemetry(id, datum) {
|
||||||
|
this.emit(`broadcastTelemetry`, Object.assign({}, this.createNormalizedDatum(datum, id), {id: id}));
|
||||||
|
}
|
||||||
|
|
||||||
|
getTestData(metadatum) {
|
||||||
|
let data = undefined;
|
||||||
|
if (this.testData.applied) {
|
||||||
|
const found = this.testData.conditionTestInputs.find((testInput) => (testInput.metadata === metadatum.source));
|
||||||
|
if (found) {
|
||||||
|
data = found.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
createNormalizedDatum(telemetryDatum, id) {
|
||||||
|
return Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((normalizedDatum, metadatum) => {
|
||||||
|
const testValue = this.getTestData(metadatum);
|
||||||
|
const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
|
||||||
|
normalizedDatum[metadatum.key] = testValue !== undefined ? formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
|
||||||
|
return normalizedDatum;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTestData(testData) {
|
||||||
|
this.testData = testData;
|
||||||
|
this.openmct.objects.mutate(this.conditionSetDomainObject, 'configuration.conditionTestData', this.testData.conditionTestInputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
persistConditions() {
|
||||||
|
this.openmct.objects.mutate(this.conditionSetDomainObject, 'configuration.conditionCollection', this.conditionSetDomainObject.configuration.conditionCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.composition.off('add', this.subscribeToTelemetry, this);
|
||||||
|
this.composition.off('remove', this.unsubscribeFromTelemetry, this);
|
||||||
|
Object.values(this.subscriptions).forEach(unsubscribe => unsubscribe());
|
||||||
|
this.subscriptions = undefined;
|
||||||
|
|
||||||
|
if(this.stopObservingForChanges) {
|
||||||
|
this.stopObservingForChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.conditionClassCollection.forEach((condition) => {
|
||||||
|
condition.off('conditionResultUpdated', this.handleConditionResult);
|
||||||
|
condition.destroy();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
126
src/plugins/condition/ConditionManagerSpec.js
Normal file
126
src/plugins/condition/ConditionManagerSpec.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import ConditionManager from './ConditionManager';
|
||||||
|
|
||||||
|
describe('ConditionManager', () => {
|
||||||
|
|
||||||
|
let conditionMgr;
|
||||||
|
let mockListener;
|
||||||
|
let openmct = {};
|
||||||
|
let mockCondition = {
|
||||||
|
isDefault: true,
|
||||||
|
id: '1234-5678',
|
||||||
|
configuration: {
|
||||||
|
criteria: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let conditionSetDomainObject = {
|
||||||
|
identifier: {
|
||||||
|
namespace: "",
|
||||||
|
key: "600a7372-8d48-4dc4-98b6-548611b1ff7e"
|
||||||
|
},
|
||||||
|
type: "conditionSet",
|
||||||
|
location: "mine",
|
||||||
|
configuration: {
|
||||||
|
conditionCollection: [
|
||||||
|
mockCondition
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mockComposition;
|
||||||
|
let loader;
|
||||||
|
|
||||||
|
function mockAngularComponents() {
|
||||||
|
let mockInjector = jasmine.createSpyObj('$injector', ['get']);
|
||||||
|
|
||||||
|
let mockInstantiate = jasmine.createSpy('mockInstantiate');
|
||||||
|
mockInstantiate.and.returnValue(mockInstantiate);
|
||||||
|
|
||||||
|
let mockDomainObject = {
|
||||||
|
useCapability: function () {
|
||||||
|
return mockCondition;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mockInstantiate.and.callFake(function () {
|
||||||
|
return mockDomainObject;
|
||||||
|
});
|
||||||
|
mockInjector.get.and.callFake(function (service) {
|
||||||
|
return {
|
||||||
|
'instantiate': mockInstantiate
|
||||||
|
}[service];
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.$injector = mockInjector;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
|
||||||
|
mockAngularComponents();
|
||||||
|
mockListener = jasmine.createSpy('mockListener');
|
||||||
|
loader = {};
|
||||||
|
loader.promise = new Promise(function (resolve, reject) {
|
||||||
|
loader.resolve = resolve;
|
||||||
|
loader.reject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
mockComposition = jasmine.createSpyObj('compositionCollection', [
|
||||||
|
'load',
|
||||||
|
'on',
|
||||||
|
'off'
|
||||||
|
]);
|
||||||
|
mockComposition.load.and.callFake(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loader.resolve();
|
||||||
|
});
|
||||||
|
return loader.promise;
|
||||||
|
});
|
||||||
|
mockComposition.on('add', mockListener);
|
||||||
|
mockComposition.on('remove', mockListener);
|
||||||
|
openmct.composition = jasmine.createSpyObj('compositionAPI', [
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
openmct.composition.get.and.returnValue(mockComposition);
|
||||||
|
|
||||||
|
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString', 'observe', 'mutate']);
|
||||||
|
openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) {
|
||||||
|
resolve(conditionSetDomainObject);
|
||||||
|
}), new Promise(function (resolve, reject) {
|
||||||
|
resolve(mockCondition);
|
||||||
|
}));
|
||||||
|
openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key);
|
||||||
|
openmct.objects.observe.and.returnValue(function () {});
|
||||||
|
openmct.objects.mutate.and.returnValue(function () {});
|
||||||
|
|
||||||
|
conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
|
||||||
|
|
||||||
|
conditionMgr.on('conditionSetResultUpdated', mockListener);
|
||||||
|
conditionMgr.on('broadcastTelemetry', mockListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a conditionCollection with a default condition', function () {
|
||||||
|
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
|
||||||
|
let defaultConditionId = conditionMgr.conditionClassCollection[0].id;
|
||||||
|
expect(defaultConditionId).toEqual(mockCondition.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
33
src/plugins/condition/ConditionSetCompositionPolicy.js
Normal file
33
src/plugins/condition/ConditionSetCompositionPolicy.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export default function ConditionSetCompositionPolicy(openmct) {
|
||||||
|
return {
|
||||||
|
allow: function (parent, child) {
|
||||||
|
if (parent.type === 'conditionSet' && !openmct.telemetry.isTelemetryObject(child)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
src/plugins/condition/ConditionSetCompositionPolicySpec.js
Normal file
85
src/plugins/condition/ConditionSetCompositionPolicySpec.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import ConditionSetCompositionPolicy from './ConditionSetCompositionPolicy';
|
||||||
|
|
||||||
|
describe('ConditionSetCompositionPolicy', () => {
|
||||||
|
|
||||||
|
let policy;
|
||||||
|
let testTelemetryObject;
|
||||||
|
let openmct = {};
|
||||||
|
let parentDomainObject;
|
||||||
|
|
||||||
|
beforeAll(function () {
|
||||||
|
testTelemetryObject = {
|
||||||
|
identifier:{ namespace: "", key: "test-object"},
|
||||||
|
type: "test-object",
|
||||||
|
name: "Test Object",
|
||||||
|
telemetry: {
|
||||||
|
values: [{
|
||||||
|
key: "some-key",
|
||||||
|
name: "Some attribute",
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: "some-other-key",
|
||||||
|
name: "Another attribute",
|
||||||
|
hints: {
|
||||||
|
range: 1
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
|
||||||
|
openmct.objects.get.and.returnValue(testTelemetryObject);
|
||||||
|
openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
|
||||||
|
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject']);
|
||||||
|
policy = new ConditionSetCompositionPolicy(openmct);
|
||||||
|
parentDomainObject = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for object types that are not conditionSets', function () {
|
||||||
|
parentDomainObject.type = 'random';
|
||||||
|
openmct.telemetry.isTelemetryObject.and.returnValue(false);
|
||||||
|
expect(policy.allow(parentDomainObject, {})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for object types that are not telemetry objects when parent is a conditionSet', function () {
|
||||||
|
parentDomainObject.type = 'conditionSet';
|
||||||
|
openmct.telemetry.isTelemetryObject.and.returnValue(false);
|
||||||
|
expect(policy.allow(parentDomainObject, {})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for object types that are telemetry objects when parent is a conditionSet', function () {
|
||||||
|
parentDomainObject.type = 'conditionSet';
|
||||||
|
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
||||||
|
expect(policy.allow(parentDomainObject, testTelemetryObject)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true for object types that are telemetry objects when parent is not a conditionSet', function () {
|
||||||
|
parentDomainObject.type = 'random';
|
||||||
|
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
||||||
|
expect(policy.allow(parentDomainObject, testTelemetryObject)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
68
src/plugins/condition/ConditionSetMetadataProvider.js
Normal file
68
src/plugins/condition/ConditionSetMetadataProvider.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export default class ConditionSetMetadataProvider {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsMetadata(domainObject) {
|
||||||
|
return domainObject.type === 'conditionSet';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDomains(domainObject) {
|
||||||
|
return this.openmct.time.getAllTimeSystems().map(function (ts, i) {
|
||||||
|
return {
|
||||||
|
key: ts.key,
|
||||||
|
name: ts.name,
|
||||||
|
format: ts.timeFormat,
|
||||||
|
hints: {
|
||||||
|
domain: i
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMetadata(domainObject) {
|
||||||
|
const enumerations = domainObject.configuration.conditionCollection
|
||||||
|
.map((condition, index) => {
|
||||||
|
return {
|
||||||
|
string: condition.configuration.output,
|
||||||
|
value: index
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
values: this.getDomains().concat([
|
||||||
|
{
|
||||||
|
name: 'Output',
|
||||||
|
key: 'output',
|
||||||
|
format: 'enum',
|
||||||
|
enumerations: enumerations,
|
||||||
|
hints: {
|
||||||
|
range: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
86
src/plugins/condition/ConditionSetTelemetryProvider.js
Normal file
86
src/plugins/condition/ConditionSetTelemetryProvider.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import ConditionManager from './ConditionManager'
|
||||||
|
|
||||||
|
export default class ConditionSetTelemetryProvider {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.conditionManagerPool = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelemetryObject(domainObject) {
|
||||||
|
return domainObject.type === 'conditionSet';
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsRequest(domainObject) {
|
||||||
|
return domainObject.type === 'conditionSet';
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsSubscribe(domainObject) {
|
||||||
|
return domainObject.type === 'conditionSet';
|
||||||
|
}
|
||||||
|
|
||||||
|
request(domainObject) {
|
||||||
|
let conditionManager = this.getConditionManager(domainObject);
|
||||||
|
|
||||||
|
return conditionManager.requestLADConditionSetOutput()
|
||||||
|
.then(latestOutput => {
|
||||||
|
return latestOutput ? [latestOutput] : [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(domainObject, callback) {
|
||||||
|
let conditionManager = this.getConditionManager(domainObject);
|
||||||
|
|
||||||
|
conditionManager.on('conditionSetResultUpdated', callback);
|
||||||
|
|
||||||
|
return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns conditionManager instance for corresponding domain object
|
||||||
|
* creates the instance if it is not yet created
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getConditionManager(domainObject) {
|
||||||
|
const id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
|
||||||
|
if (!this.conditionManagerPool[id]) {
|
||||||
|
this.conditionManagerPool[id] = new ConditionManager(domainObject, this.openmct);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.conditionManagerPool[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cleans up and destroys conditionManager instance for corresponding domain object id
|
||||||
|
* can be called manually for views that only request but do not subscribe to data
|
||||||
|
*/
|
||||||
|
destroyConditionManager(id) {
|
||||||
|
if (this.conditionManagerPool[id]) {
|
||||||
|
this.conditionManagerPool[id].off('conditionSetResultUpdated');
|
||||||
|
this.conditionManagerPool[id].destroy();
|
||||||
|
delete this.conditionManagerPool[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/plugins/condition/ConditionSetViewPolicy.js
Normal file
33
src/plugins/condition/ConditionSetViewPolicy.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
function ConditionSetViewPolicy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
|
||||||
|
if (domainObject.getModel().type === 'conditionSet') {
|
||||||
|
return view.key === 'conditionSet.view';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConditionSetViewPolicy;
|
84
src/plugins/condition/ConditionSetViewProvider.js
Normal file
84
src/plugins/condition/ConditionSetViewProvider.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import ConditionSet from './components/ConditionSet.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
const DEFAULT_VIEW_PRIORITY = 100;
|
||||||
|
|
||||||
|
export default class ConditionSetViewProvider {
|
||||||
|
constructor(openmct) {
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.name = 'Conditions View';
|
||||||
|
this.key = 'conditionSet.view';
|
||||||
|
this.cssClass = 'icon-conditional';
|
||||||
|
}
|
||||||
|
|
||||||
|
canView(domainObject) {
|
||||||
|
return domainObject.type === 'conditionSet';
|
||||||
|
}
|
||||||
|
|
||||||
|
canEdit(domainObject) {
|
||||||
|
return domainObject.type === 'conditionSet';
|
||||||
|
}
|
||||||
|
|
||||||
|
view(domainObject, objectPath) {
|
||||||
|
let component;
|
||||||
|
const openmct = this.openmct;
|
||||||
|
return {
|
||||||
|
show: (container, isEditing) => {
|
||||||
|
component = new Vue({
|
||||||
|
el: container,
|
||||||
|
components: {
|
||||||
|
ConditionSet
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct,
|
||||||
|
domainObject,
|
||||||
|
objectPath
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isEditing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<condition-set :isEditing="isEditing"></condition-set>'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onEditModeChange: (isEditing) => {
|
||||||
|
component.isEditing = isEditing;
|
||||||
|
},
|
||||||
|
destroy: () => {
|
||||||
|
component.$destroy();
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
priority(domainObject) {
|
||||||
|
if (domainObject.type === 'conditionSet') {
|
||||||
|
return Number.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
return DEFAULT_VIEW_PRIORITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
src/plugins/condition/ConditionSpec.js
Normal file
136
src/plugins/condition/ConditionSpec.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import Condition from "./Condition";
|
||||||
|
import {TRIGGER} from "./utils/constants";
|
||||||
|
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||||
|
|
||||||
|
let openmct = {},
|
||||||
|
mockListener,
|
||||||
|
testConditionDefinition,
|
||||||
|
testTelemetryObject,
|
||||||
|
conditionObj,
|
||||||
|
conditionManager,
|
||||||
|
mockBroadcastTelemetry;
|
||||||
|
|
||||||
|
describe("The condition", function () {
|
||||||
|
|
||||||
|
beforeEach (() => {
|
||||||
|
conditionManager = jasmine.createSpyObj('conditionManager',
|
||||||
|
['on']
|
||||||
|
);
|
||||||
|
mockBroadcastTelemetry = jasmine.createSpy('listener');
|
||||||
|
conditionManager.on('broadcastTelemetry', mockBroadcastTelemetry);
|
||||||
|
|
||||||
|
mockListener = jasmine.createSpy('listener');
|
||||||
|
testTelemetryObject = {
|
||||||
|
identifier:{ namespace: "", key: "test-object"},
|
||||||
|
type: "test-object",
|
||||||
|
name: "Test Object",
|
||||||
|
telemetry: {
|
||||||
|
values: [{
|
||||||
|
key: "some-key",
|
||||||
|
name: "Some attribute",
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: "some-other-key",
|
||||||
|
name: "Another attribute",
|
||||||
|
hints: {
|
||||||
|
range: 1
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
conditionManager.telemetryObjects = {
|
||||||
|
"test-object": testTelemetryObject
|
||||||
|
};
|
||||||
|
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
|
||||||
|
openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) {
|
||||||
|
resolve(testTelemetryObject);
|
||||||
|
})); openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
|
||||||
|
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', 'subscribe', 'getMetadata']);
|
||||||
|
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
||||||
|
openmct.telemetry.subscribe.and.returnValue(function () {});
|
||||||
|
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
|
||||||
|
|
||||||
|
testConditionDefinition = {
|
||||||
|
id: '123-456',
|
||||||
|
configuration: {
|
||||||
|
name: 'mock condition',
|
||||||
|
output: 'mock output',
|
||||||
|
trigger: TRIGGER.ANY,
|
||||||
|
criteria: [
|
||||||
|
{
|
||||||
|
id: '1234-5678-9999-0000',
|
||||||
|
operation: 'equalTo',
|
||||||
|
input: ['0'],
|
||||||
|
metadata: 'value',
|
||||||
|
telemetry: testTelemetryObject.identifier
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
conditionObj = new Condition(
|
||||||
|
testConditionDefinition,
|
||||||
|
openmct,
|
||||||
|
conditionManager
|
||||||
|
);
|
||||||
|
|
||||||
|
conditionObj.on('conditionUpdated', mockListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("generates criteria with the correct properties", function () {
|
||||||
|
const testCriterion = testConditionDefinition.configuration.criteria[0];
|
||||||
|
let criterion = conditionObj.generateCriterion(testCriterion);
|
||||||
|
expect(criterion.id).toBeDefined();
|
||||||
|
expect(criterion.operation).toEqual(testCriterion.operation);
|
||||||
|
expect(criterion.input).toEqual(testCriterion.input);
|
||||||
|
expect(criterion.metadata).toEqual(testCriterion.metadata);
|
||||||
|
expect(criterion.telemetry).toEqual(testCriterion.telemetry);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initializes with an id", function () {
|
||||||
|
expect(conditionObj.id).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initializes with criteria from the condition definition", function () {
|
||||||
|
expect(conditionObj.criteria.length).toEqual(1);
|
||||||
|
let criterion = conditionObj.criteria[0];
|
||||||
|
expect(criterion instanceof TelemetryCriterion).toBeTrue();
|
||||||
|
expect(criterion.operator).toEqual(testConditionDefinition.configuration.criteria[0].operator);
|
||||||
|
expect(criterion.input).toEqual(testConditionDefinition.configuration.criteria[0].input);
|
||||||
|
expect(criterion.metadata).toEqual(testConditionDefinition.configuration.criteria[0].metadata);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initializes with the trigger from the condition definition", function () {
|
||||||
|
expect(conditionObj.trigger).toEqual(testConditionDefinition.configuration.trigger);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("destroys all criteria for a condition", function () {
|
||||||
|
const result = conditionObj.destroyCriteria();
|
||||||
|
expect(result).toBeTrue();
|
||||||
|
expect(conditionObj.criteria.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
128
src/plugins/condition/StyleRuleManager.js
Normal file
128
src/plugins/condition/StyleRuleManager.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
|
|
||||||
|
export default class StyleRuleManager extends EventEmitter {
|
||||||
|
constructor(styleConfiguration, openmct, callback) {
|
||||||
|
super();
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.callback = callback;
|
||||||
|
if (styleConfiguration) {
|
||||||
|
this.initialize(styleConfiguration);
|
||||||
|
if (styleConfiguration.conditionSetIdentifier) {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
} else {
|
||||||
|
this.applyStaticStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(styleConfiguration) {
|
||||||
|
this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
|
||||||
|
this.staticStyle = styleConfiguration.staticStyle;
|
||||||
|
this.updateConditionStylesMap(styleConfiguration.styles || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToConditionSet() {
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||||
|
this.openmct.telemetry.request(conditionSetDomainObject)
|
||||||
|
.then(output => {
|
||||||
|
if (output && output.length) {
|
||||||
|
this.handleConditionSetResultUpdated(output[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateObjectStyleConfig(styleConfiguration) {
|
||||||
|
if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
|
||||||
|
this.initialize(styleConfiguration || {});
|
||||||
|
this.destroy();
|
||||||
|
} else {
|
||||||
|
let isNewConditionSet = !this.conditionSetIdentifier ||
|
||||||
|
!this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
|
||||||
|
this.initialize(styleConfiguration);
|
||||||
|
//Only resubscribe if the conditionSet has changed.
|
||||||
|
if (isNewConditionSet) {
|
||||||
|
this.subscribeToConditionSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConditionStylesMap(conditionStyles) {
|
||||||
|
let conditionStyleMap = {};
|
||||||
|
conditionStyles.forEach((conditionStyle) => {
|
||||||
|
if (conditionStyle.conditionId) {
|
||||||
|
conditionStyleMap[conditionStyle.conditionId] = conditionStyle.style;
|
||||||
|
} else {
|
||||||
|
conditionStyleMap.static = conditionStyle.style;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.conditionalStyleMap = conditionStyleMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConditionSetResultUpdated(resultData) {
|
||||||
|
let foundStyle = this.conditionalStyleMap[resultData.conditionId];
|
||||||
|
if (foundStyle) {
|
||||||
|
if (foundStyle !== this.currentStyle) {
|
||||||
|
this.currentStyle = foundStyle;
|
||||||
|
}
|
||||||
|
this.updateDomainObjectStyle();
|
||||||
|
} else {
|
||||||
|
this.applyStaticStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDomainObjectStyle() {
|
||||||
|
if (this.callback) {
|
||||||
|
this.callback(Object.assign({}, this.currentStyle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyStaticStyle() {
|
||||||
|
if (this.staticStyle) {
|
||||||
|
this.currentStyle = this.staticStyle.style;
|
||||||
|
} else {
|
||||||
|
if (this.currentStyle) {
|
||||||
|
Object.keys(this.currentStyle).forEach(key => {
|
||||||
|
this.currentStyle[key] = 'transparent';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.updateDomainObjectStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.applyStaticStyle();
|
||||||
|
if (this.stopProvidingTelemetry) {
|
||||||
|
this.stopProvidingTelemetry();
|
||||||
|
}
|
||||||
|
delete this.stopProvidingTelemetry;
|
||||||
|
this.conditionSetIdentifier = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
325
src/plugins/condition/components/Condition.vue
Normal file
325
src/plugins/condition/components/Condition.vue
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="isEditing"
|
||||||
|
class="c-condition c-condition--edit js-condition-drag-wrapper"
|
||||||
|
>
|
||||||
|
<!-- Edit view -->
|
||||||
|
<div class="c-condition__header">
|
||||||
|
<span class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
|
||||||
|
title="Drag to reorder conditions"
|
||||||
|
:class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
|
||||||
|
:draggable="!condition.isDefault"
|
||||||
|
@dragstart="dragStart"
|
||||||
|
@dragstop="dragStop"
|
||||||
|
@dragover.stop
|
||||||
|
></span>
|
||||||
|
|
||||||
|
<span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||||
|
:class="{ 'c-disclosure-triangle--expanded': expanded }"
|
||||||
|
@click="expanded = !expanded"
|
||||||
|
></span>
|
||||||
|
|
||||||
|
<span class="c-condition__name">{{ condition.configuration.name }}</span>
|
||||||
|
<!-- TODO: description should be derived from criteria -->
|
||||||
|
<span class="c-condition__summary">
|
||||||
|
<template v-if="!canEvaluateCriteria">
|
||||||
|
Define criteria
|
||||||
|
</template>
|
||||||
|
<span v-else>
|
||||||
|
<condition-description :show-label="false"
|
||||||
|
:condition="condition"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="c-condition__buttons">
|
||||||
|
<button v-if="!condition.isDefault"
|
||||||
|
class="c-click-icon c-condition__duplicate-button icon-duplicate"
|
||||||
|
title="Duplicate this condition"
|
||||||
|
@click="cloneCondition"
|
||||||
|
></button>
|
||||||
|
|
||||||
|
<button v-if="!condition.isDefault"
|
||||||
|
class="c-click-icon c-condition__delete-button icon-trash"
|
||||||
|
title="Delete this condition"
|
||||||
|
@click="removeCondition"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="expanded"
|
||||||
|
class="c-condition__definition c-cdef"
|
||||||
|
>
|
||||||
|
<span class="c-cdef__separator c-row-separator"></span>
|
||||||
|
<span class="c-cdef__label">Condition Name</span>
|
||||||
|
<span class="c-cdef__controls">
|
||||||
|
<input v-model="condition.configuration.name"
|
||||||
|
class="t-condition-input__name"
|
||||||
|
type="text"
|
||||||
|
@blur="persist"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="c-cdef__label">Output</span>
|
||||||
|
<span class="c-cdef__controls">
|
||||||
|
<span class="c-cdef__control">
|
||||||
|
<select v-model="selectedOutputSelection"
|
||||||
|
@change="setOutputValue"
|
||||||
|
>
|
||||||
|
<option v-for="option in outputOptions"
|
||||||
|
:key="option"
|
||||||
|
:value="option"
|
||||||
|
>
|
||||||
|
{{ initCap(option) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span class="c-cdef__control">
|
||||||
|
<input v-if="selectedOutputSelection === outputOptions[2]"
|
||||||
|
v-model="condition.configuration.output"
|
||||||
|
class="t-condition-name-input"
|
||||||
|
type="text"
|
||||||
|
@blur="persist"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div v-if="!condition.isDefault"
|
||||||
|
class="c-cdef__match-and-criteria"
|
||||||
|
>
|
||||||
|
<span class="c-cdef__separator c-row-separator"></span>
|
||||||
|
<span class="c-cdef__label">Match</span>
|
||||||
|
<span class="c-cdef__controls">
|
||||||
|
<select v-model="condition.configuration.trigger"
|
||||||
|
@change="persist"
|
||||||
|
>
|
||||||
|
<option v-for="option in triggers"
|
||||||
|
:key="option.value"
|
||||||
|
:value="option.value"
|
||||||
|
> {{ option.label }}</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<template v-if="telemetry.length || condition.configuration.criteria.length">
|
||||||
|
<div v-for="(criterion, index) in condition.configuration.criteria"
|
||||||
|
:key="index"
|
||||||
|
class="c-cdef__criteria"
|
||||||
|
>
|
||||||
|
<Criterion :telemetry="telemetry"
|
||||||
|
:criterion="criterion"
|
||||||
|
:index="index"
|
||||||
|
:trigger="condition.configuration.trigger"
|
||||||
|
:is-default="condition.configuration.criteria.length === 1"
|
||||||
|
@persist="persist"
|
||||||
|
/>
|
||||||
|
<div class="c-cdef__criteria__buttons">
|
||||||
|
<button class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
|
||||||
|
title="Duplicate this criteria"
|
||||||
|
@click="cloneCriterion(index)"
|
||||||
|
></button>
|
||||||
|
<button v-if="!(condition.configuration.criteria.length === 1)"
|
||||||
|
class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
|
||||||
|
title="Delete this criteria"
|
||||||
|
@click="removeCriterion(index)"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="c-cdef__separator c-row-separator"></div>
|
||||||
|
<div class="c-cdef__controls"
|
||||||
|
:disabled="!telemetry.length"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="c-cdef__add-criteria-button c-button c-button--labeled icon-plus"
|
||||||
|
@click="addCriteria"
|
||||||
|
>
|
||||||
|
<span class="c-button__label">Add Criteria</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else
|
||||||
|
class="c-condition c-condition--browse"
|
||||||
|
>
|
||||||
|
<!-- Browse view -->
|
||||||
|
<div class="c-condition__header">
|
||||||
|
<span class="c-condition__name">
|
||||||
|
{{ condition.configuration.name }}
|
||||||
|
</span>
|
||||||
|
<span class="c-condition__output">
|
||||||
|
Output: {{ condition.configuration.output }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="c-condition__summary">
|
||||||
|
<condition-description :show-label="false"
|
||||||
|
:condition="condition"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Criterion from './Criterion.vue';
|
||||||
|
import ConditionDescription from "./ConditionDescription.vue";
|
||||||
|
import { TRIGGER, TRIGGER_LABEL } from "@/plugins/condition/utils/constants";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
Criterion,
|
||||||
|
ConditionDescription
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
condition: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
conditionIndex: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
isEditing: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
telemetry: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentCriteria: this.currentCriteria,
|
||||||
|
expanded: true,
|
||||||
|
trigger: 'all',
|
||||||
|
selectedOutputSelection: '',
|
||||||
|
outputOptions: ['false', 'true', 'string'],
|
||||||
|
criterionIndex: 0,
|
||||||
|
selectedTelemetryName: '',
|
||||||
|
selectedFieldName: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
triggers() {
|
||||||
|
const keys = Object.keys(TRIGGER);
|
||||||
|
const triggerOptions = [];
|
||||||
|
keys.forEach((trigger) => {
|
||||||
|
triggerOptions.push({
|
||||||
|
value: TRIGGER[trigger],
|
||||||
|
label: TRIGGER_LABEL[TRIGGER[trigger]]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return triggerOptions;
|
||||||
|
},
|
||||||
|
canEvaluateCriteria: function () {
|
||||||
|
let criteria = this.condition.configuration.criteria;
|
||||||
|
if (criteria.length) {
|
||||||
|
let lastCriterion = criteria[criteria.length - 1];
|
||||||
|
if (lastCriterion.telemetry &&
|
||||||
|
lastCriterion.operation &&
|
||||||
|
(lastCriterion.input.length ||
|
||||||
|
lastCriterion.operation === 'isDefined' ||
|
||||||
|
lastCriterion.operation === 'isUndefined')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.destroy();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setOutputSelection();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setOutputSelection() {
|
||||||
|
let conditionOutput = this.condition.configuration.output;
|
||||||
|
if (conditionOutput) {
|
||||||
|
if (conditionOutput !== 'false' && conditionOutput !== 'true') {
|
||||||
|
this.selectedOutputSelection = 'string';
|
||||||
|
} else {
|
||||||
|
this.selectedOutputSelection = conditionOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setOutputValue() {
|
||||||
|
if (this.selectedOutputSelection === 'string') {
|
||||||
|
this.condition.configuration.output = '';
|
||||||
|
} else {
|
||||||
|
this.condition.configuration.output = this.selectedOutputSelection;
|
||||||
|
}
|
||||||
|
this.persist();
|
||||||
|
},
|
||||||
|
addCriteria() {
|
||||||
|
const criteriaObject = {
|
||||||
|
telemetry: '',
|
||||||
|
operation: '',
|
||||||
|
input: '',
|
||||||
|
metadata: ''
|
||||||
|
};
|
||||||
|
this.condition.configuration.criteria.push(criteriaObject);
|
||||||
|
},
|
||||||
|
dragStart(e) {
|
||||||
|
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
|
||||||
|
e.dataTransfer.effectAllowed = "copyMove";
|
||||||
|
e.dataTransfer.setDragImage(e.target.closest('.js-condition-drag-wrapper'), 0, 0);
|
||||||
|
this.$emit('setMoveIndex', this.conditionIndex);
|
||||||
|
},
|
||||||
|
dragStop(e) {
|
||||||
|
e.dataTransfer.clearData();
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
},
|
||||||
|
removeCondition(ev) {
|
||||||
|
this.$emit('removeCondition', this.conditionIndex);
|
||||||
|
},
|
||||||
|
cloneCondition(ev) {
|
||||||
|
this.$emit('cloneCondition', {
|
||||||
|
condition: this.condition,
|
||||||
|
index: this.conditionIndex
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeCriterion(index) {
|
||||||
|
this.condition.configuration.criteria.splice(index, 1);
|
||||||
|
this.persist();
|
||||||
|
},
|
||||||
|
cloneCriterion(index) {
|
||||||
|
const clonedCriterion = JSON.parse(JSON.stringify(this.condition.configuration.criteria[index]));
|
||||||
|
this.condition.configuration.criteria.splice(index + 1, 0, clonedCriterion);
|
||||||
|
this.persist();
|
||||||
|
},
|
||||||
|
persist() {
|
||||||
|
this.$emit('updateCondition', {
|
||||||
|
condition: this.condition,
|
||||||
|
index: this.conditionIndex
|
||||||
|
});
|
||||||
|
},
|
||||||
|
initCap: function (str) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
245
src/plugins/condition/components/ConditionCollection.vue
Normal file
245
src/plugins/condition/components/ConditionCollection.vue
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section id="conditionCollection"
|
||||||
|
class="c-cs__conditions"
|
||||||
|
:class="{ 'is-expanded': expanded }"
|
||||||
|
>
|
||||||
|
<div class="c-cs__header c-section__header">
|
||||||
|
<span
|
||||||
|
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||||
|
:class="{ 'c-disclosure-triangle--expanded': expanded }"
|
||||||
|
@click="expanded = !expanded"
|
||||||
|
></span>
|
||||||
|
<div class="c-cs__header-label c-section__label">Conditions</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="expanded"
|
||||||
|
class="c-cs__content"
|
||||||
|
>
|
||||||
|
<div v-show="isEditing"
|
||||||
|
class="hint"
|
||||||
|
:class="{ 's-status-icon-warning-lo': !telemetryObjs.length }"
|
||||||
|
>
|
||||||
|
<template v-if="!telemetryObjs.length">Drag telemetry into this Condition Set to configure Conditions and add criteria.</template>
|
||||||
|
<template v-else>The first condition to match is the one that is applied. Drag conditions to reorder.</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-show="isEditing"
|
||||||
|
id="addCondition"
|
||||||
|
class="c-button c-button--major icon-plus labeled"
|
||||||
|
@click="addCondition"
|
||||||
|
>
|
||||||
|
<span class="c-cs-button__label">Add Condition</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="c-cs__conditions-h">
|
||||||
|
<div v-for="(condition, index) in conditionCollection"
|
||||||
|
:key="condition.id"
|
||||||
|
class="c-condition-h"
|
||||||
|
>
|
||||||
|
<div v-if="isEditing"
|
||||||
|
class="c-c__drag-ghost"
|
||||||
|
@drop.prevent="dropCondition"
|
||||||
|
@dragenter="dragEnter"
|
||||||
|
@dragleave="dragLeave"
|
||||||
|
@dragover.prevent
|
||||||
|
></div>
|
||||||
|
<Condition :condition="condition"
|
||||||
|
:condition-index="index"
|
||||||
|
:telemetry="telemetryObjs"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
@updateCondition="updateCondition"
|
||||||
|
@removeCondition="removeCondition"
|
||||||
|
@cloneCondition="cloneCondition"
|
||||||
|
@setMoveIndex="setMoveIndex"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Condition from './Condition.vue';
|
||||||
|
import ConditionManager from '../ConditionManager';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'domainObject'],
|
||||||
|
components: {
|
||||||
|
Condition
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isEditing: Boolean,
|
||||||
|
testData: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
applied: false,
|
||||||
|
conditionTestInputs: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expanded: true,
|
||||||
|
conditionCollection: [],
|
||||||
|
conditionResults: {},
|
||||||
|
conditions: [],
|
||||||
|
telemetryObjs: [],
|
||||||
|
moveIndex: Number,
|
||||||
|
isDragging: false,
|
||||||
|
defaultOutput: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
defaultOutput(newOutput, oldOutput) {
|
||||||
|
this.$emit('updateDefaultOutput', newOutput);
|
||||||
|
},
|
||||||
|
testData: {
|
||||||
|
handler() {
|
||||||
|
this.updateTestData();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.composition.off('add', this.addTelemetryObject);
|
||||||
|
this.composition.off('remove', this.removeTelemetryObject);
|
||||||
|
if(this.conditionManager) {
|
||||||
|
this.conditionManager.off('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
|
||||||
|
this.conditionManager.destroy();
|
||||||
|
}
|
||||||
|
if (this.stopObservingForChanges) {
|
||||||
|
this.stopObservingForChanges();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.composition = this.openmct.composition.get(this.domainObject);
|
||||||
|
this.composition.on('add', this.addTelemetryObject);
|
||||||
|
this.composition.on('remove', this.removeTelemetryObject);
|
||||||
|
this.composition.load();
|
||||||
|
this.conditionCollection = this.domainObject.configuration.conditionCollection;
|
||||||
|
this.observeForChanges();
|
||||||
|
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
|
||||||
|
this.conditionManager.on('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
|
||||||
|
this.updateDefaultCondition();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleConditionSetResultUpdated(data) {
|
||||||
|
this.$emit('conditionSetResultUpdated', data)
|
||||||
|
},
|
||||||
|
observeForChanges() {
|
||||||
|
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
|
||||||
|
this.conditionCollection = newConditionCollection;
|
||||||
|
this.updateDefaultCondition();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateDefaultCondition() {
|
||||||
|
const defaultCondition = this.domainObject.configuration.conditionCollection
|
||||||
|
.find(conditionConfiguration => conditionConfiguration.isDefault);
|
||||||
|
this.defaultOutput = defaultCondition.configuration.output;
|
||||||
|
},
|
||||||
|
setMoveIndex(index) {
|
||||||
|
this.moveIndex = index;
|
||||||
|
this.isDragging = true;
|
||||||
|
},
|
||||||
|
dropCondition(e) {
|
||||||
|
let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
|
||||||
|
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
|
||||||
|
const oldIndexArr = Object.keys(this.conditionCollection);
|
||||||
|
const move = function (arr, old_index, new_index) {
|
||||||
|
while (old_index < 0) {
|
||||||
|
old_index += arr.length;
|
||||||
|
}
|
||||||
|
while (new_index < 0) {
|
||||||
|
new_index += arr.length;
|
||||||
|
}
|
||||||
|
if (new_index >= arr.length) {
|
||||||
|
var k = new_index - arr.length;
|
||||||
|
while ((k--) + 1) {
|
||||||
|
arr.push(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
const newIndexArr = move(oldIndexArr, this.moveIndex, targetIndex);
|
||||||
|
const reorderPlan = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < oldIndexArr.length; i++) {
|
||||||
|
reorderPlan.push({oldIndex: Number(newIndexArr[i]), newIndex: i});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reorder(reorderPlan);
|
||||||
|
|
||||||
|
e.target.classList.remove("dragging");
|
||||||
|
this.isDragging = false;
|
||||||
|
},
|
||||||
|
dragEnter(e) {
|
||||||
|
if (!this.isDragging) { return }
|
||||||
|
let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
|
||||||
|
if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
|
||||||
|
if (this.moveIndex === targetIndex) { return }
|
||||||
|
e.target.classList.add("dragging");
|
||||||
|
},
|
||||||
|
dragLeave(e) {
|
||||||
|
e.target.classList.remove("dragging");
|
||||||
|
},
|
||||||
|
addTelemetryObject(domainObject) {
|
||||||
|
this.telemetryObjs.push(domainObject);
|
||||||
|
this.$emit('telemetryUpdated', this.telemetryObjs);
|
||||||
|
},
|
||||||
|
removeTelemetryObject(identifier) {
|
||||||
|
let index = _.findIndex(this.telemetryObjs, (obj) => {
|
||||||
|
let objId = this.openmct.objects.makeKeyString(obj.identifier);
|
||||||
|
let id = this.openmct.objects.makeKeyString(identifier);
|
||||||
|
return objId === id;
|
||||||
|
});
|
||||||
|
if (index > -1) {
|
||||||
|
this.telemetryObjs.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addCondition() {
|
||||||
|
this.conditionManager.addCondition();
|
||||||
|
},
|
||||||
|
updateCondition(data) {
|
||||||
|
this.conditionManager.updateCondition(data.condition, data.index);
|
||||||
|
},
|
||||||
|
removeCondition(index) {
|
||||||
|
this.conditionManager.removeCondition(index);
|
||||||
|
},
|
||||||
|
reorder(reorderPlan) {
|
||||||
|
this.conditionManager.reorderConditions(reorderPlan);
|
||||||
|
},
|
||||||
|
cloneCondition(data) {
|
||||||
|
this.conditionManager.cloneCondition(data.condition, data.index);
|
||||||
|
},
|
||||||
|
updateTestData() {
|
||||||
|
this.conditionManager.updateTestData(this.testData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
145
src/plugins/condition/components/ConditionDescription.vue
Normal file
145
src/plugins/condition/components/ConditionDescription.vue
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="c-style__condition-desc">
|
||||||
|
<span v-if="showLabel && condition"
|
||||||
|
class="c-style__condition-desc__name c-condition__name"
|
||||||
|
>
|
||||||
|
{{ condition.configuration.name }}
|
||||||
|
</span>
|
||||||
|
<span v-for="(criterionDescription, index) in criterionDescriptions"
|
||||||
|
:key="criterionDescription"
|
||||||
|
class="c-style__condition-desc__text"
|
||||||
|
>
|
||||||
|
<template v-if="!index">When</template>
|
||||||
|
{{ criterionDescription }}
|
||||||
|
<template v-if="index < (criterionDescriptions.length-1)">{{ triggerDescription }}</template>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { TRIGGER } from "@/plugins/condition/utils/constants";
|
||||||
|
import { OPERATIONS } from "@/plugins/condition/utils/operations";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConditionDescription',
|
||||||
|
inject: [
|
||||||
|
'openmct'
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
showLabel: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
condition: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
criterionDescriptions: [],
|
||||||
|
triggerDescription: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
condition: {
|
||||||
|
handler(val) {
|
||||||
|
this.getConditionDescription();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getConditionDescription();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTriggerDescription(trigger) {
|
||||||
|
let description = '';
|
||||||
|
switch(trigger) {
|
||||||
|
case TRIGGER.ANY:
|
||||||
|
case TRIGGER.XOR:
|
||||||
|
description = 'or';
|
||||||
|
break;
|
||||||
|
case TRIGGER.ALL:
|
||||||
|
case TRIGGER.NOT: description = 'and';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return description;
|
||||||
|
},
|
||||||
|
getConditionDescription() {
|
||||||
|
if (this.condition) {
|
||||||
|
this.triggerDescription = this.getTriggerDescription(this.condition.configuration.trigger);
|
||||||
|
this.criterionDescriptions = [];
|
||||||
|
this.condition.configuration.criteria.forEach((criterion, index) => {
|
||||||
|
this.getCriterionDescription(criterion, index);
|
||||||
|
});
|
||||||
|
if (this.condition.isDefault) {
|
||||||
|
this.criterionDescriptions.splice(0, 0, 'all else fails');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.criterionDescriptions = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCriterionDescription(criterion, index) {
|
||||||
|
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
||||||
|
if (telemetryObject.type === 'unknown') {
|
||||||
|
let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
|
||||||
|
this.criterionDescriptions.splice(index, 0, description);
|
||||||
|
} else {
|
||||||
|
let metadataValue = criterion.metadata;
|
||||||
|
let inputValue = criterion.input;
|
||||||
|
if (criterion.metadata) {
|
||||||
|
this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
|
|
||||||
|
const metadataObj = this.telemetryMetadata.valueMetadatas.find((metadata) => metadata.key === criterion.metadata);
|
||||||
|
if (metadataObj) {
|
||||||
|
if (metadataObj.name) {
|
||||||
|
metadataValue = metadataObj.name;
|
||||||
|
}
|
||||||
|
if(metadataObj.enumerations && inputValue.length) {
|
||||||
|
if (metadataObj.enumerations[inputValue[0]] && metadataObj.enumerations[inputValue[0]].string) {
|
||||||
|
inputValue = [metadataObj.enumerations[inputValue[0]].string];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let description = `${telemetryObject.name} ${metadataValue} ${this.getOperatorText(criterion.operation, inputValue)}`;
|
||||||
|
if (this.criterionDescriptions[index]) {
|
||||||
|
this.criterionDescriptions[index] = description;
|
||||||
|
} else {
|
||||||
|
this.criterionDescriptions.splice(index, 0, description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getOperatorText(operationName, values) {
|
||||||
|
const found = OPERATIONS.find((operation) => operation.name === operationName);
|
||||||
|
return found ? found.getDescription(values) : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
80
src/plugins/condition/components/ConditionError.vue
Normal file
80
src/plugins/condition/components/ConditionError.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="conditionErrors.length"
|
||||||
|
class="c-condition__errors"
|
||||||
|
>
|
||||||
|
<div v-for="(error, index) in conditionErrors"
|
||||||
|
:key="index"
|
||||||
|
class="u-alert u-alert--block u-alert--with-icon"
|
||||||
|
>{{ error.message.errorText }} {{ error.additionalInfo }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { ERROR } from "@/plugins/condition/utils/constants";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConditionError',
|
||||||
|
inject: [
|
||||||
|
'openmct'
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
condition: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
conditionErrors: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getConditionErrors();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getConditionErrors() {
|
||||||
|
if (this.condition) {
|
||||||
|
this.condition.configuration.criteria.forEach((criterion, index) => {
|
||||||
|
this.getCriterionErrors(criterion, index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCriterionErrors(criterion, index) {
|
||||||
|
this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
|
||||||
|
if (telemetryObject.type === 'unknown') {
|
||||||
|
this.conditionErrors.push({
|
||||||
|
message: ERROR.TELEMETRY_NOT_FOUND,
|
||||||
|
additionalInfo: criterion.telemetry ? `Key: ${this.openmct.objects.makeKeyString(criterion.telemetry)}` : ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
97
src/plugins/condition/components/ConditionSet.vue
Normal file
97
src/plugins/condition/components/ConditionSet.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="c-cs">
|
||||||
|
<section class="c-cs__current-output c-section">
|
||||||
|
<div class="c-cs__header c-section__header">
|
||||||
|
<span class="c-cs__header-label c-section__label">Current Output</span>
|
||||||
|
</div>
|
||||||
|
<div class="c-cs__content c-cs__current-output-value">
|
||||||
|
<template v-if="currentConditionOutput">
|
||||||
|
{{ currentConditionOutput }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ defaultConditionOutput }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<TestData :is-editing="isEditing"
|
||||||
|
:test-data="testData"
|
||||||
|
:telemetry="telemetryObjs"
|
||||||
|
@updateTestData="updateTestData"
|
||||||
|
/>
|
||||||
|
<ConditionCollection
|
||||||
|
:is-editing="isEditing"
|
||||||
|
:test-data="testData"
|
||||||
|
@conditionSetResultUpdated="updateCurrentOutput"
|
||||||
|
@updateDefaultOutput="updateDefaultOutput"
|
||||||
|
@telemetryUpdated="updateTelemetry"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TestData from './TestData.vue';
|
||||||
|
import ConditionCollection from './ConditionCollection.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ["openmct", "domainObject"],
|
||||||
|
components: {
|
||||||
|
TestData,
|
||||||
|
ConditionCollection
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isEditing: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentConditionOutput: '',
|
||||||
|
defaultConditionOutput: '',
|
||||||
|
telemetryObjs: [],
|
||||||
|
testData: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||||
|
this.testData = {
|
||||||
|
applied: false,
|
||||||
|
conditionTestInputs: this.domainObject.configuration.conditionTestData || []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateCurrentOutput(currentConditionResult) {
|
||||||
|
this.currentConditionOutput = currentConditionResult.output;
|
||||||
|
},
|
||||||
|
updateDefaultOutput(output) {
|
||||||
|
this.currentConditionOutput = output;
|
||||||
|
},
|
||||||
|
updateTelemetry(telemetryObjs) {
|
||||||
|
this.telemetryObjs = telemetryObjs;
|
||||||
|
},
|
||||||
|
updateTestData(testData) {
|
||||||
|
this.testData = testData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
293
src/plugins/condition/components/Criterion.vue
Normal file
293
src/plugins/condition/components/Criterion.vue
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="u-contents">
|
||||||
|
<div class="c-cdef__separator c-row-separator"></div>
|
||||||
|
<span class="c-cdef__label">{{ setRowLabel }}</span>
|
||||||
|
<span class="c-cdef__controls">
|
||||||
|
<span class="c-cdef__control">
|
||||||
|
<select ref="telemetrySelect"
|
||||||
|
v-model="criterion.telemetry"
|
||||||
|
@change="updateMetadataOptions"
|
||||||
|
>
|
||||||
|
<option value="">- Select Telemetry -</option>
|
||||||
|
<option value="all">all telemetry</option>
|
||||||
|
<option value="any">any telemetry</option>
|
||||||
|
<option v-for="telemetryOption in telemetry"
|
||||||
|
:key="telemetryOption.identifier.key"
|
||||||
|
:value="telemetryOption.identifier"
|
||||||
|
>
|
||||||
|
{{ telemetryOption.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span v-if="criterion.telemetry"
|
||||||
|
class="c-cdef__control"
|
||||||
|
>
|
||||||
|
<select ref="metadataSelect"
|
||||||
|
v-model="criterion.metadata"
|
||||||
|
@change="updateOperations"
|
||||||
|
>
|
||||||
|
<option value="">- Select Field -</option>
|
||||||
|
<option v-for="option in telemetryMetadataOptions"
|
||||||
|
:key="option.key"
|
||||||
|
:value="option.key"
|
||||||
|
>
|
||||||
|
{{ option.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span v-if="criterion.telemetry && criterion.metadata"
|
||||||
|
class="c-cdef__control"
|
||||||
|
>
|
||||||
|
<select v-model="criterion.operation"
|
||||||
|
@change="updateInputVisibilityAndValues"
|
||||||
|
>
|
||||||
|
<option value="">- Select Comparison -</option>
|
||||||
|
<option v-for="option in filteredOps"
|
||||||
|
:key="option.name"
|
||||||
|
:value="option.name"
|
||||||
|
>
|
||||||
|
{{ option.text }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<template v-if="!enumerations.length">
|
||||||
|
<span v-for="(item, inputIndex) in inputCount"
|
||||||
|
:key="inputIndex"
|
||||||
|
class="c-cdef__control__inputs"
|
||||||
|
>
|
||||||
|
<input v-model="criterion.input[inputIndex]"
|
||||||
|
class="c-cdef__control__input"
|
||||||
|
:type="setInputType"
|
||||||
|
@blur="persist"
|
||||||
|
>
|
||||||
|
<span v-if="inputIndex < inputCount-1">and</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span v-else>
|
||||||
|
<span v-if="inputCount && criterion.operation"
|
||||||
|
class="c-cdef__control"
|
||||||
|
>
|
||||||
|
<select v-model="criterion.input[0]"
|
||||||
|
@change="persist"
|
||||||
|
>
|
||||||
|
<option v-for="option in enumerations"
|
||||||
|
:key="option.string"
|
||||||
|
:value="option.value.toString()"
|
||||||
|
>
|
||||||
|
{{ option.string }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { OPERATIONS } from '../utils/operations';
|
||||||
|
import { INPUT_TYPES } from '../utils/operations';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
criterion: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
telemetry: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
trigger: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
telemetryMetadataOptions: [],
|
||||||
|
operations: OPERATIONS,
|
||||||
|
inputCount: 0,
|
||||||
|
rowLabel: '',
|
||||||
|
operationFormat: '',
|
||||||
|
enumerations: [],
|
||||||
|
inputTypes: INPUT_TYPES
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
setRowLabel: function () {
|
||||||
|
let operator = this.trigger === 'all' ? 'and ': 'or ';
|
||||||
|
return (this.index !== 0 ? operator : '') + 'when';
|
||||||
|
},
|
||||||
|
filteredOps: function () {
|
||||||
|
return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
|
||||||
|
},
|
||||||
|
setInputType: function () {
|
||||||
|
let type = '';
|
||||||
|
for (let i = 0; i < this.filteredOps.length; i++) {
|
||||||
|
if (this.criterion.operation === this.filteredOps[i].name) {
|
||||||
|
if (this.filteredOps[i].appliesTo.length) {
|
||||||
|
type = this.inputTypes[this.filteredOps[i].appliesTo[0]];
|
||||||
|
} else {
|
||||||
|
type = 'text'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
telemetry: {
|
||||||
|
handler(newTelemetry, oldTelemetry) {
|
||||||
|
this.checkTelemetry();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateMetadataOptions();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkTelemetry() {
|
||||||
|
if(this.criterion.telemetry) {
|
||||||
|
if (this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all') {
|
||||||
|
this.updateMetadataOptions();
|
||||||
|
} else {
|
||||||
|
if (!this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier))) {
|
||||||
|
//telemetry being used was removed. So reset this criterion.
|
||||||
|
this.criterion.telemetry = '';
|
||||||
|
this.criterion.metadata = '';
|
||||||
|
this.criterion.input = [];
|
||||||
|
this.criterion.operation = '';
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateOperationFormat() {
|
||||||
|
this.enumerations = [];
|
||||||
|
let foundMetadata = this.telemetryMetadataOptions.find((value) => {
|
||||||
|
return value.key === this.criterion.metadata;
|
||||||
|
});
|
||||||
|
if (foundMetadata) {
|
||||||
|
if (foundMetadata.enumerations !== undefined) {
|
||||||
|
this.operationFormat = 'enum';
|
||||||
|
this.enumerations = foundMetadata.enumerations;
|
||||||
|
} else if (foundMetadata.hints.hasOwnProperty('range')) {
|
||||||
|
this.operationFormat = 'number';
|
||||||
|
} else if (foundMetadata.hints.hasOwnProperty('domain')) {
|
||||||
|
this.operationFormat = 'number';
|
||||||
|
} else if (foundMetadata.key === 'name') {
|
||||||
|
this.operationFormat = 'string';
|
||||||
|
} else {
|
||||||
|
this.operationFormat = 'string';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.updateInputVisibilityAndValues();
|
||||||
|
},
|
||||||
|
updateMetadataOptions(ev) {
|
||||||
|
if (ev) {
|
||||||
|
this.clearDependentFields(ev.target);
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
if (this.criterion.telemetry) {
|
||||||
|
const telemetry = (this.criterion.telemetry === 'all' || this.criterion.telemetry === 'any') ? this.telemetry : [{
|
||||||
|
identifier: this.criterion.telemetry
|
||||||
|
}];
|
||||||
|
|
||||||
|
let telemetryPromises = telemetry.map((telemetryObject) => this.openmct.objects.get(telemetryObject.identifier));
|
||||||
|
Promise.all(telemetryPromises).then(telemetryObjects => {
|
||||||
|
this.telemetryMetadataOptions = [];
|
||||||
|
telemetryObjects.forEach(telemetryObject => {
|
||||||
|
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
|
this.addMetaDataOptions(telemetryMetadata.values());
|
||||||
|
});
|
||||||
|
this.updateOperations();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addMetaDataOptions(options) {
|
||||||
|
if (!this.telemetryMetadataOptions) {
|
||||||
|
this.telemetryMetadataOptions = options;
|
||||||
|
}
|
||||||
|
options.forEach((option) => {
|
||||||
|
const found = this.telemetryMetadataOptions.find((metadataOption) => {
|
||||||
|
return (metadataOption.key && (metadataOption.key === option.key)) && (metadataOption.name && (metadataOption.name === option.name))
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
this.telemetryMetadataOptions.push(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateOperations(ev) {
|
||||||
|
this.updateOperationFormat();
|
||||||
|
if (ev) {
|
||||||
|
this.clearDependentFields(ev.target);
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateInputVisibilityAndValues(ev) {
|
||||||
|
if (ev) {
|
||||||
|
this.clearDependentFields();
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.filteredOps.length; i++) {
|
||||||
|
if (this.criterion.operation === this.filteredOps[i].name) {
|
||||||
|
this.inputCount = this.filteredOps[i].inputCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.inputCount) {
|
||||||
|
this.criterion.input = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearDependentFields(el) {
|
||||||
|
if (el === this.$refs.telemetrySelect) {
|
||||||
|
this.criterion.metadata = '';
|
||||||
|
} else if (el === this.$refs.metadataSelect) {
|
||||||
|
if (!this.filteredOps.find(operation => operation.name === this.criterion.operation)) {
|
||||||
|
this.criterion.operation = '';
|
||||||
|
this.criterion.input = this.enumerations.length ? [this.enumerations[0].value.toString()] : [];
|
||||||
|
this.inputCount = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.enumerations.length && !this.criterion.input.length) {
|
||||||
|
this.criterion.input = [this.enumerations[0].value.toString()];
|
||||||
|
}
|
||||||
|
this.inputCount = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
persist() {
|
||||||
|
this.$emit('persist', this.criterion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
237
src/plugins/condition/components/TestData.vue
Normal file
237
src/plugins/condition/components/TestData.vue
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section v-show="isEditing"
|
||||||
|
id="test-data"
|
||||||
|
class="c-cs__test-data"
|
||||||
|
:class="{ 'is-expanded': expanded }"
|
||||||
|
>
|
||||||
|
<div class="c-cs__header c-section__header">
|
||||||
|
<span
|
||||||
|
class="c-disclosure-triangle c-tree__item__view-control is-enabled"
|
||||||
|
:class="{ 'c-disclosure-triangle--expanded': expanded }"
|
||||||
|
@click="expanded = !expanded"
|
||||||
|
></span>
|
||||||
|
<div class="c-cs__header-label c-section__label">Test Data</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="expanded"
|
||||||
|
class="c-cs__content"
|
||||||
|
>
|
||||||
|
<div class="c-cdef__controls"
|
||||||
|
:disabled="!telemetry.length"
|
||||||
|
>
|
||||||
|
<label class="c-toggle-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="isApplied"
|
||||||
|
@change="applyTestData"
|
||||||
|
>
|
||||||
|
<span class="c-toggle-switch__slider"></span>
|
||||||
|
<span class="c-toggle-switch__label">Apply Test Data</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="c-cs-tests">
|
||||||
|
<span v-for="(testInput, tIndex) in testInputs"
|
||||||
|
:key="tIndex"
|
||||||
|
class="c-test-datum c-cs-test"
|
||||||
|
>
|
||||||
|
<span class="c-cs-test__label">Set</span>
|
||||||
|
<span class="c-cs-test__controls">
|
||||||
|
<span class="c-cdef__control">
|
||||||
|
<select v-model="testInput.telemetry"
|
||||||
|
@change="updateMetadata(testInput)"
|
||||||
|
>
|
||||||
|
<option value="">- Select Telemetry -</option>
|
||||||
|
<option v-for="(telemetryOption, index) in telemetry"
|
||||||
|
:key="index"
|
||||||
|
:value="telemetryOption.identifier"
|
||||||
|
>
|
||||||
|
{{ telemetryOption.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span v-if="testInput.telemetry"
|
||||||
|
class="c-cdef__control"
|
||||||
|
>
|
||||||
|
<select v-model="testInput.metadata"
|
||||||
|
@change="updateTestData"
|
||||||
|
>
|
||||||
|
<option value="">- Select Field -</option>
|
||||||
|
<option v-for="(option, index) in telemetryMetadataOptions[getId(testInput.telemetry)]"
|
||||||
|
:key="index"
|
||||||
|
:value="option.key"
|
||||||
|
>
|
||||||
|
{{ option.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span v-if="testInput.metadata"
|
||||||
|
class="c-cdef__control__inputs"
|
||||||
|
>
|
||||||
|
<input v-model="testInput.value"
|
||||||
|
placeholder="Enter test input"
|
||||||
|
type="text"
|
||||||
|
class="c-cdef__control__input"
|
||||||
|
@change="updateTestData"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="c-test-datum__buttons">
|
||||||
|
<button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
|
||||||
|
title="Duplicate this test datum"
|
||||||
|
@click="addTestInput(testInput)"
|
||||||
|
></button>
|
||||||
|
<button class="c-click-icon c-test-data__delete-button icon-trash"
|
||||||
|
title="Delete this test datum"
|
||||||
|
@click="removeTestInput(tIndex)"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
v-show="isEditing"
|
||||||
|
id="addTestDatum"
|
||||||
|
class="c-button c-button--major icon-plus labeled"
|
||||||
|
@click="addTestInput"
|
||||||
|
>
|
||||||
|
<span class="c-cs-button__label">Add Test Datum</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
isEditing: Boolean,
|
||||||
|
telemetry: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
testData: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
applied: false,
|
||||||
|
conditionTestInputs: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expanded: true,
|
||||||
|
isApplied: false,
|
||||||
|
testInputs: [],
|
||||||
|
telemetryMetadataOptions: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isEditing(editing) {
|
||||||
|
if (!editing) {
|
||||||
|
this.resetApplied();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
telemetry: {
|
||||||
|
handler() {
|
||||||
|
this.initializeMetadata();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
testData: {
|
||||||
|
handler() {
|
||||||
|
this.initialize();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.resetApplied();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initialize();
|
||||||
|
this.initializeMetadata();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
applyTestData() {
|
||||||
|
this.isApplied = !this.isApplied;
|
||||||
|
this.updateTestData();
|
||||||
|
},
|
||||||
|
initialize() {
|
||||||
|
if (this.testData && this.testData.conditionTestInputs) {
|
||||||
|
this.testInputs = this.testData.conditionTestInputs;
|
||||||
|
}
|
||||||
|
if (!this.testInputs.length) {
|
||||||
|
this.addTestInput();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initializeMetadata() {
|
||||||
|
this.telemetry.forEach((telemetryObject) => {
|
||||||
|
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
|
let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
|
this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addTestInput(testInput) {
|
||||||
|
this.testInputs.push(Object.assign({
|
||||||
|
telemetry: '',
|
||||||
|
metadata: '',
|
||||||
|
input: ''
|
||||||
|
}, testInput));
|
||||||
|
},
|
||||||
|
removeTestInput(index) {
|
||||||
|
this.testInputs.splice(index, 1);
|
||||||
|
this.updateTestData();
|
||||||
|
},
|
||||||
|
getId(identifier) {
|
||||||
|
if (identifier) {
|
||||||
|
return this.openmct.objects.makeKeyString(identifier);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
updateMetadata(testInput) {
|
||||||
|
if (testInput.telemetry) {
|
||||||
|
const id = this.openmct.objects.makeKeyString(testInput.telemetry);
|
||||||
|
if(this.telemetryMetadataOptions[id]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let telemetryMetadata = this.openmct.telemetry.getMetadata(testInput);
|
||||||
|
this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetApplied() {
|
||||||
|
this.isApplied = false;
|
||||||
|
this.updateTestData();
|
||||||
|
},
|
||||||
|
updateTestData() {
|
||||||
|
this.$emit('updateTestData', {
|
||||||
|
applied: this.isApplied,
|
||||||
|
conditionTestInputs: this.testInputs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
116
src/plugins/condition/components/condition-set.scss
Normal file
116
src/plugins/condition/components/condition-set.scss
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
.c-cs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
+ * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-button {
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
// Add some space to kick away from blue editing border indication
|
||||||
|
padding: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions-h {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
padding-right: $interiorMarginSm;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__conditions {
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
padding: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************** SPECIFIC ITEMS */
|
||||||
|
&__current-output-value {
|
||||||
|
font-size: 1.25em;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** TEST DATA */
|
||||||
|
.c-cs-tests {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
padding-right: $interiorMarginSm;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-cs-test {
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
+ * {
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
133
src/plugins/condition/components/condition.scss
Normal file
133
src/plugins/condition/components/condition.scss
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
.c-condition,
|
||||||
|
.c-test-datum {
|
||||||
|
@include discreteItem();
|
||||||
|
display: flex;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
|
||||||
|
&--edit {
|
||||||
|
line-height: 160%; // For layout when inputs wrap, like in criteria
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-condition {
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 400px;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
&--browse {
|
||||||
|
.c-condition__summary {
|
||||||
|
border-top: 1px solid $colorInteriorBorder;
|
||||||
|
padding-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** HEADER */
|
||||||
|
&__header {
|
||||||
|
$h: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
align-content: stretch;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: $h;
|
||||||
|
line-height: $h;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
+ * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__drag-grippy {
|
||||||
|
transform: translateY(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
font-weight: bold;
|
||||||
|
align-self: baseline; // Fixes bold line-height offset problem
|
||||||
|
}
|
||||||
|
|
||||||
|
&__output,
|
||||||
|
&__summary {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************** CONDITION DEFINITION, EDITING */
|
||||||
|
.c-cdef {
|
||||||
|
display: grid;
|
||||||
|
grid-row-gap: $interiorMarginSm;
|
||||||
|
grid-column-gap: $interiorMargin;
|
||||||
|
grid-auto-columns: min-content 1fr max-content;
|
||||||
|
align-items: start;
|
||||||
|
min-width: 150px;
|
||||||
|
margin-left: 29px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__criteria,
|
||||||
|
&__match-and-criteria {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
grid-column: 1;
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__separator {
|
||||||
|
grid-column: 1 / span 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
grid-column: 2;
|
||||||
|
|
||||||
|
> * > * {
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
grid-column: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-c__drag-ghost {
|
||||||
|
width: 100%;
|
||||||
|
min-height: $interiorMarginSm;
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
min-height: 5em;
|
||||||
|
background-color: lightblue;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,185 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<li class="c-tree__item-h">
|
||||||
|
<div
|
||||||
|
class="c-tree__item"
|
||||||
|
:class="{ 'is-alias': isAlias, 'is-navigated-object': navigated }"
|
||||||
|
@click="handleItemSelected(node.object, node)"
|
||||||
|
>
|
||||||
|
<view-control
|
||||||
|
v-model="expanded"
|
||||||
|
class="c-tree__item__view-control"
|
||||||
|
:enabled="hasChildren"
|
||||||
|
:propagate="false"
|
||||||
|
/>
|
||||||
|
<div class="c-tree__item__label c-object-label">
|
||||||
|
<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">{{ node.object.name }}</div>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
<condition-set-dialog-tree-item
|
||||||
|
v-for="child in children"
|
||||||
|
:key="child.id"
|
||||||
|
:node="child"
|
||||||
|
:selected-item="selectedItem"
|
||||||
|
:handle-item-selected="handleItemSelected"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import viewControl from '@/ui/components/viewControl.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConditionSetDialogTreeItem',
|
||||||
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
viewControl
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
selectedItem: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleItemSelected: {
|
||||||
|
type: Function,
|
||||||
|
default() {
|
||||||
|
return (item) => {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hasChildren: false,
|
||||||
|
isLoading: false,
|
||||||
|
loaded: false,
|
||||||
|
children: [],
|
||||||
|
expanded: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
navigated() {
|
||||||
|
const itemId = this.selectedItem && this.selectedItem.itemId;
|
||||||
|
const isSelectedObject = itemId && this.openmct.objects.areIdsEqual(this.node.object.identifier, itemId);
|
||||||
|
if (isSelectedObject && this.node.objectPath && this.node.objectPath.length > 1) {
|
||||||
|
const isParent = this.openmct.objects.areIdsEqual(this.node.objectPath[1].identifier, this.selectedItem.parentId);
|
||||||
|
return isSelectedObject && isParent;
|
||||||
|
}
|
||||||
|
return isSelectedObject;
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
typeClass() {
|
||||||
|
let type = this.openmct.types.get(this.node.object.type);
|
||||||
|
if (!type) {
|
||||||
|
return 'icon-object-unknown';
|
||||||
|
}
|
||||||
|
return type.definition.cssClass;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
expanded() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.expanded = false;
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
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
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,172 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="u-contents">
|
||||||
|
<div class="c-overlay__top-bar">
|
||||||
|
<div class="c-overlay__dialog-title">Select Condition Set</div>
|
||||||
|
</div>
|
||||||
|
<div class="c-overlay__contents-main c-selector 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"
|
||||||
|
>
|
||||||
|
<condition-set-dialog-tree-item
|
||||||
|
v-for="treeItem in allTreeItems"
|
||||||
|
:key="treeItem.id"
|
||||||
|
:node="treeItem"
|
||||||
|
:selected-item="selectedItem"
|
||||||
|
:handle-item-selected="handleItemSelection"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
<!-- end main tree -->
|
||||||
|
|
||||||
|
<!-- search tree -->
|
||||||
|
<ul v-if="searchValue"
|
||||||
|
class="c-tree-and-search__tree c-tree"
|
||||||
|
>
|
||||||
|
<condition-set-dialog-tree-item
|
||||||
|
v-for="treeItem in filteredTreeItems"
|
||||||
|
:key="treeItem.id"
|
||||||
|
:node="treeItem"
|
||||||
|
:selected-item="selectedItem"
|
||||||
|
:handle-item-selected="handleItemSelection"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
<!-- end search tree -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import search from '@/ui/components/search.vue';
|
||||||
|
import ConditionSetDialogTreeItem from './ConditionSetDialogTreeItem.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
name: 'ConditionSetSelectorDialog',
|
||||||
|
components: {
|
||||||
|
search,
|
||||||
|
ConditionSetDialogTreeItem
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expanded: false,
|
||||||
|
searchValue: '',
|
||||||
|
allTreeItems: [],
|
||||||
|
filteredTreeItems: [],
|
||||||
|
isLoading: false,
|
||||||
|
selectedItem: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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(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('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: this.openmct.objects.makeKeyString(object.identifier),
|
||||||
|
object,
|
||||||
|
objectPath,
|
||||||
|
navigateToParent
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
searchTree(value) {
|
||||||
|
this.searchValue = value;
|
||||||
|
|
||||||
|
if (this.searchValue !== '') {
|
||||||
|
this.getFilteredChildren();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleItemSelection(item, node) {
|
||||||
|
if (item && item.type === 'conditionSet') {
|
||||||
|
const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
|
||||||
|
this.selectedItem = {
|
||||||
|
itemId: item.identifier,
|
||||||
|
parentId
|
||||||
|
};
|
||||||
|
this.$emit('conditionSetSelected', item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,335 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="c-inspector__styles c-inspect-styles">
|
||||||
|
<template v-if="!conditionSetDomainObject">
|
||||||
|
<div class="c-inspect-styles__header">
|
||||||
|
Object Style
|
||||||
|
</div>
|
||||||
|
<div class="c-inspect-styles__content">
|
||||||
|
<div v-if="staticStyle"
|
||||||
|
class="c-inspect-styles__style"
|
||||||
|
>
|
||||||
|
<style-editor class="c-inspect-styles__editor"
|
||||||
|
:style-item="staticStyle"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
@persist="updateStaticStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
id="addConditionSet"
|
||||||
|
class="c-button c-button--major c-toggle-styling-button labeled"
|
||||||
|
@click="addConditionSet"
|
||||||
|
>
|
||||||
|
<span class="c-cs-button__label">Use Conditional Styling...</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="c-inspect-styles__header">
|
||||||
|
Conditional Object Styles
|
||||||
|
</div>
|
||||||
|
<div class="c-inspect-styles__content c-inspect-styles__condition-set">
|
||||||
|
<a v-if="conditionSetDomainObject"
|
||||||
|
class="c-object-label icon-conditional"
|
||||||
|
:href="navigateToPath"
|
||||||
|
@click="navigateOrPreview"
|
||||||
|
>
|
||||||
|
<span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
|
||||||
|
</a>
|
||||||
|
<template v-if="isEditing">
|
||||||
|
<button
|
||||||
|
id="changeConditionSet"
|
||||||
|
class="c-button labeled"
|
||||||
|
@click="addConditionSet"
|
||||||
|
>
|
||||||
|
<span class="c-button__label">Change...</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="c-click-icon icon-x"
|
||||||
|
title="Remove conditional styles"
|
||||||
|
@click="removeConditionSet"
|
||||||
|
></button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="conditionsLoaded"
|
||||||
|
class="c-inspect-styles__conditions"
|
||||||
|
>
|
||||||
|
<div v-for="(conditionStyle, index) in conditionalStyles"
|
||||||
|
:key="index"
|
||||||
|
class="c-inspect-styles__condition"
|
||||||
|
>
|
||||||
|
<condition-error :show-label="true"
|
||||||
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
|
/>
|
||||||
|
<condition-description :show-label="true"
|
||||||
|
:condition="getCondition(conditionStyle.conditionId)"
|
||||||
|
/>
|
||||||
|
<style-editor class="c-inspect-styles__editor"
|
||||||
|
:style-item="conditionStyle"
|
||||||
|
:is-editing="isEditing"
|
||||||
|
@persist="updateConditionalStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import StyleEditor from "./StyleEditor.vue";
|
||||||
|
import ConditionSetSelectorDialog from "./ConditionSetSelectorDialog.vue";
|
||||||
|
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
|
||||||
|
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
|
||||||
|
import Vue from 'vue';
|
||||||
|
import PreviewAction from "@/ui/preview/PreviewAction.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ConditionalStylesView',
|
||||||
|
components: {
|
||||||
|
ConditionDescription,
|
||||||
|
ConditionError,
|
||||||
|
StyleEditor
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'openmct',
|
||||||
|
'domainObject'
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
itemId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
initialStyles: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canHide: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
conditionalStyles: [],
|
||||||
|
staticStyle: undefined,
|
||||||
|
conditionSetDomainObject: undefined,
|
||||||
|
isEditing: this.openmct.editor.isEditing(),
|
||||||
|
conditions: undefined,
|
||||||
|
conditionsLoaded: false,
|
||||||
|
navigateToPath: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.openmct.editor.off('isEditing', this.setEditState);
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.previewAction = new PreviewAction(this.openmct);
|
||||||
|
if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
|
||||||
|
let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
|
||||||
|
this.initializeStaticStyle(objectStyles);
|
||||||
|
if (objectStyles && objectStyles.conditionSetIdentifier) {
|
||||||
|
this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
|
||||||
|
this.conditionalStyles = objectStyles.styles;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.initializeStaticStyle();
|
||||||
|
}
|
||||||
|
this.openmct.editor.on('isEditing', this.setEditState);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initialize(conditionSetDomainObject) {
|
||||||
|
//If there are new conditions in the conditionSet we need to set those styles to default
|
||||||
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
this.enableConditionSetNav();
|
||||||
|
this.initializeConditionalStyles();
|
||||||
|
},
|
||||||
|
setEditState(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
},
|
||||||
|
addConditionSet() {
|
||||||
|
let conditionSetDomainObject;
|
||||||
|
const handleItemSelection = (item) => {
|
||||||
|
if (item) {
|
||||||
|
conditionSetDomainObject = item;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dismissDialog = (overlay, initialize) => {
|
||||||
|
overlay.dismiss();
|
||||||
|
if (initialize && conditionSetDomainObject) {
|
||||||
|
this.conditionSetDomainObject = conditionSetDomainObject;
|
||||||
|
this.conditionalStyles = [];
|
||||||
|
this.initializeConditionalStyles();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let vm = new Vue({
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct
|
||||||
|
},
|
||||||
|
components: {ConditionSetSelectorDialog},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
handleItemSelection
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<condition-set-selector-dialog @conditionSetSelected="handleItemSelection"></condition-set-selector-dialog>'
|
||||||
|
}).$mount();
|
||||||
|
|
||||||
|
let overlay = this.openmct.overlays.overlay({
|
||||||
|
element: vm.$el,
|
||||||
|
size: 'small',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'OK',
|
||||||
|
emphasis: 'true',
|
||||||
|
callback: () => dismissDialog(overlay, true)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cancel',
|
||||||
|
callback: () => dismissDialog(overlay, false)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onDestroy: () => vm.$destroy()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enableConditionSetNav() {
|
||||||
|
this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
|
||||||
|
(objectPath) => {
|
||||||
|
this.objectPath = objectPath;
|
||||||
|
this.navigateToPath = '#/browse/' + this.objectPath
|
||||||
|
.map(o => o && this.openmct.objects.makeKeyString(o.identifier))
|
||||||
|
.reverse()
|
||||||
|
.join('/');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
navigateOrPreview(event) {
|
||||||
|
// If editing, display condition set in Preview overlay; otherwise nav to it while browsing
|
||||||
|
if (this.openmct.editor.isEditing()) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.previewAction.invoke(this.objectPath);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeConditionSet() {
|
||||||
|
this.conditionSetDomainObject = undefined;
|
||||||
|
this.conditionalStyles = [];
|
||||||
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
|
if (this.itemId) {
|
||||||
|
domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
|
||||||
|
delete domainObjectStyles[this.itemId].conditionSetIdentifier;
|
||||||
|
domainObjectStyles[this.itemId].styles = undefined;
|
||||||
|
delete domainObjectStyles[this.itemId].styles;
|
||||||
|
if (_.isEmpty(domainObjectStyles[this.itemId])) {
|
||||||
|
delete domainObjectStyles[this.itemId];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
domainObjectStyles.conditionSetIdentifier = undefined;
|
||||||
|
delete domainObjectStyles.conditionSetIdentifier;
|
||||||
|
domainObjectStyles.styles = undefined;
|
||||||
|
delete domainObjectStyles.styles;
|
||||||
|
}
|
||||||
|
if (_.isEmpty(domainObjectStyles)) {
|
||||||
|
domainObjectStyles = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.persist(domainObjectStyles);
|
||||||
|
},
|
||||||
|
initializeConditionalStyles() {
|
||||||
|
if (!this.conditions) {
|
||||||
|
this.conditions = {};
|
||||||
|
}
|
||||||
|
let conditionalStyles = [];
|
||||||
|
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
|
||||||
|
this.conditions[conditionConfiguration.id] = conditionConfiguration;
|
||||||
|
let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
|
||||||
|
if (foundStyle) {
|
||||||
|
foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
|
||||||
|
conditionalStyles.push(foundStyle);
|
||||||
|
} else {
|
||||||
|
conditionalStyles.splice(index, 0, {
|
||||||
|
conditionId: conditionConfiguration.id,
|
||||||
|
style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//we're doing this so that we remove styles for any conditions that have been removed from the condition set
|
||||||
|
this.conditionalStyles = conditionalStyles;
|
||||||
|
this.conditionsLoaded = true;
|
||||||
|
this.persist(this.getDomainObjectConditionalStyle());
|
||||||
|
},
|
||||||
|
initializeStaticStyle(objectStyles) {
|
||||||
|
let staticStyle = objectStyles && objectStyles.staticStyle;
|
||||||
|
this.staticStyle = staticStyle || {
|
||||||
|
style: Object.assign({}, this.initialStyles)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
findStyleByConditionId(id) {
|
||||||
|
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
|
||||||
|
},
|
||||||
|
updateStaticStyle(staticStyle) {
|
||||||
|
this.staticStyle = staticStyle;
|
||||||
|
this.persist(this.getDomainObjectConditionalStyle());
|
||||||
|
},
|
||||||
|
updateConditionalStyle(conditionStyle) {
|
||||||
|
let found = this.findStyleByConditionId(conditionStyle.conditionId);
|
||||||
|
if (found) {
|
||||||
|
found.style = conditionStyle.style;
|
||||||
|
this.persist(this.getDomainObjectConditionalStyle());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getDomainObjectConditionalStyle() {
|
||||||
|
let objectStyle = {
|
||||||
|
styles: this.conditionalStyles,
|
||||||
|
staticStyle: this.staticStyle
|
||||||
|
};
|
||||||
|
if (this.conditionSetDomainObject) {
|
||||||
|
objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
let domainObjectStyles = (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
|
||||||
|
|
||||||
|
if (this.itemId) {
|
||||||
|
domainObjectStyles[this.itemId] = objectStyle;
|
||||||
|
} else {
|
||||||
|
//we're deconstructing here to ensure that if an item within a domainObject already had a style we don't lose it
|
||||||
|
domainObjectStyles = {
|
||||||
|
...domainObjectStyles,
|
||||||
|
...objectStyle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObjectStyles;
|
||||||
|
},
|
||||||
|
getCondition(id) {
|
||||||
|
return this.conditions ? this.conditions[id] : {};
|
||||||
|
},
|
||||||
|
persist(style) {
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
179
src/plugins/condition/components/inspector/StyleEditor.vue
Normal file
179
src/plugins/condition/components/inspector/StyleEditor.vue
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="c-style">
|
||||||
|
<span class="c-style-thumb"
|
||||||
|
:class="{ 'is-style-invisible': styleItem.style.isStyleInvisible }"
|
||||||
|
:style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : styleItem.style ]"
|
||||||
|
>
|
||||||
|
<span class="c-style-thumb__text"
|
||||||
|
:class="{ 'hide-nice': !styleItem.style.color }"
|
||||||
|
>
|
||||||
|
ABC
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="c-toolbar">
|
||||||
|
<toolbar-color-picker v-if="styleItem.style.border"
|
||||||
|
class="c-style__toolbar-button--border-color u-menu-to--center"
|
||||||
|
:options="borderColorOption"
|
||||||
|
@change="updateStyleValue"
|
||||||
|
/>
|
||||||
|
<toolbar-color-picker v-if="styleItem.style.backgroundColor"
|
||||||
|
class="c-style__toolbar-button--background-color u-menu-to--center"
|
||||||
|
:options="backgroundColorOption"
|
||||||
|
@change="updateStyleValue"
|
||||||
|
/>
|
||||||
|
<toolbar-color-picker v-if="styleItem.style.color"
|
||||||
|
class="c-style__toolbar-button--color u-menu-to--center"
|
||||||
|
:options="colorOption"
|
||||||
|
@change="updateStyleValue"
|
||||||
|
/>
|
||||||
|
<toolbar-button v-if="styleItem.style.imageUrl !== undefined"
|
||||||
|
class="c-style__toolbar-button--image-url"
|
||||||
|
:options="imageUrlOption"
|
||||||
|
@change="updateStyleValue"
|
||||||
|
/>
|
||||||
|
<toolbar-toggle-button v-if="styleItem.style.isStyleInvisible !== undefined"
|
||||||
|
class="c-style__toolbar-button--toggle-visible"
|
||||||
|
:options="isStyleInvisibleOption"
|
||||||
|
@change="updateStyleValue"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue";
|
||||||
|
import ToolbarButton from "@/ui/toolbar/components/toolbar-button.vue";
|
||||||
|
import ToolbarToggleButton from "@/ui/toolbar/components/toolbar-toggle-button.vue";
|
||||||
|
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'StyleEditor',
|
||||||
|
components: {
|
||||||
|
ToolbarButton,
|
||||||
|
ToolbarColorPicker,
|
||||||
|
ToolbarToggleButton
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'openmct'
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
isEditing: {
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
styleItem: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
borderColorOption() {
|
||||||
|
return {
|
||||||
|
icon: 'icon-line-horz',
|
||||||
|
title: STYLE_CONSTANTS.borderColorTitle,
|
||||||
|
value: this.styleItem.style.border.replace('1px solid ', ''),
|
||||||
|
property: 'border',
|
||||||
|
isEditing: this.isEditing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColorOption() {
|
||||||
|
return {
|
||||||
|
icon: 'icon-paint-bucket',
|
||||||
|
title: STYLE_CONSTANTS.backgroundColorTitle,
|
||||||
|
value: this.styleItem.style.backgroundColor,
|
||||||
|
property: 'backgroundColor',
|
||||||
|
isEditing: this.isEditing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colorOption() {
|
||||||
|
return {
|
||||||
|
icon: 'icon-font',
|
||||||
|
title: STYLE_CONSTANTS.textColorTitle,
|
||||||
|
value: this.styleItem.style.color,
|
||||||
|
property: 'color',
|
||||||
|
isEditing: this.isEditing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imageUrlOption() {
|
||||||
|
return {
|
||||||
|
icon: 'icon-image',
|
||||||
|
title: STYLE_CONSTANTS.imagePropertiesTitle,
|
||||||
|
dialog: {
|
||||||
|
name: "Image Properties",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: "url",
|
||||||
|
control: "textfield",
|
||||||
|
name: "Image URL",
|
||||||
|
"cssClass": "l-input-lg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
property: 'imageUrl',
|
||||||
|
formKeys: ['url'],
|
||||||
|
value: {url: this.styleItem.style.imageUrl},
|
||||||
|
isEditing: this.isEditing
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isStyleInvisibleOption() {
|
||||||
|
return {
|
||||||
|
value: this.styleItem.style.isStyleInvisible,
|
||||||
|
property: 'isStyleInvisible',
|
||||||
|
isEditing: this.isEditing,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
icon: 'icon-eye-disabled',
|
||||||
|
title: STYLE_CONSTANTS.visibilityHidden
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: STYLE_CONSTANTS.isStyleInvisible,
|
||||||
|
icon: 'icon-eye-open',
|
||||||
|
title: STYLE_CONSTANTS.visibilityVisible
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateStyleValue(value, item) {
|
||||||
|
if (item.property === 'border') {
|
||||||
|
value = '1px solid ' + value;
|
||||||
|
}
|
||||||
|
if (value && (value.url !== undefined)) {
|
||||||
|
this.styleItem.style[item.property] = value.url;
|
||||||
|
} else {
|
||||||
|
this.styleItem.style[item.property] = value;
|
||||||
|
}
|
||||||
|
this.$emit('persist', this.styleItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,124 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/********************************************* INSPECTOR STYLES TAB */
|
||||||
|
.c-inspect-styles {
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content,
|
||||||
|
&__conditions,
|
||||||
|
&__condition {
|
||||||
|
> * + * {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__condition-set {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.c-object-label {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__style,
|
||||||
|
&__condition {
|
||||||
|
padding: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__condition {
|
||||||
|
@include discreteItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-style {
|
||||||
|
padding: 2px; // Allow a bit of room for thumb box-shadow
|
||||||
|
|
||||||
|
&__condition-desc {
|
||||||
|
@include ellipsize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-inspect-styles__style {
|
||||||
|
.is-editing & {
|
||||||
|
border-bottom: 1px solid $colorInteriorBorder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-shell:not(.is-editing) .c-inspect-styles {
|
||||||
|
.c-toolbar {
|
||||||
|
// Disabled-look toolbar when not editing
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: inherit;
|
||||||
|
|
||||||
|
// Hide control buttons, like image URL
|
||||||
|
[class*='--image-url'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make buttons look disabled by knocking back icon, not swatch element
|
||||||
|
.c-icon-button {
|
||||||
|
&:before {
|
||||||
|
opacity: $controlDisabledOpacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-toggle-styling-button {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
display: block;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-style-invisible {
|
||||||
|
display: none !important;
|
||||||
|
|
||||||
|
.is-editing & {
|
||||||
|
display: block !important;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.c-style-thumb {
|
||||||
|
display: block !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
border-color: transparent !important;
|
||||||
|
@include bgCheckerboard($size: 10px, $imp: true);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
172
src/plugins/condition/criterion/AllTelemetryCriterion.js
Normal file
172
src/plugins/condition/criterion/AllTelemetryCriterion.js
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
|
import {OPERATIONS} from '../utils/operations';
|
||||||
|
import {computeCondition} from "@/plugins/condition/utils/evaluator";
|
||||||
|
|
||||||
|
export default class TelemetryCriterion extends EventEmitter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes/Unsubscribes to telemetry and emits the result
|
||||||
|
* of operations performed on the telemetry data returned and a given input value.
|
||||||
|
* @constructor
|
||||||
|
* @param telemetryDomainObjectDefinition {id: uuid, operation: enum, input: Array, metadata: string, key: {domainObject.identifier} }
|
||||||
|
* @param openmct
|
||||||
|
*/
|
||||||
|
constructor(telemetryDomainObjectDefinition, openmct) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.objectAPI = this.openmct.objects;
|
||||||
|
this.telemetryAPI = this.openmct.telemetry;
|
||||||
|
this.timeAPI = this.openmct.time;
|
||||||
|
this.id = telemetryDomainObjectDefinition.id;
|
||||||
|
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
||||||
|
this.operation = telemetryDomainObjectDefinition.operation;
|
||||||
|
this.telemetryObjects = Object.assign({}, telemetryDomainObjectDefinition.telemetryObjects);
|
||||||
|
this.input = telemetryDomainObjectDefinition.input;
|
||||||
|
this.metadata = telemetryDomainObjectDefinition.metadata;
|
||||||
|
this.telemetryDataCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTelemetry(telemetryObjects) {
|
||||||
|
this.telemetryObjects = Object.assign({}, telemetryObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatData(data, telemetryObjects) {
|
||||||
|
if (data) {
|
||||||
|
this.telemetryDataCache[data.id] = this.computeResult(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = Object.keys(telemetryObjects);
|
||||||
|
keys.forEach((key) => {
|
||||||
|
let telemetryObject = telemetryObjects[key];
|
||||||
|
const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||||
|
if (this.telemetryDataCache[id] === undefined) {
|
||||||
|
this.telemetryDataCache[id] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const datum = {
|
||||||
|
result: computeCondition(this.telemetryDataCache, this.telemetry === 'all')
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
// TODO check back to see if we should format times here
|
||||||
|
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
||||||
|
datum[timeSystem.key] = data[timeSystem.key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return datum;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubscription(data, telemetryObjects) {
|
||||||
|
if(this.isValid()) {
|
||||||
|
this.emitEvent('criterionResultUpdated', this.formatData(data, telemetryObjects));
|
||||||
|
} else {
|
||||||
|
this.emitEvent('criterionResultUpdated', this.formatData({}, telemetryObjects));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findOperation(operation) {
|
||||||
|
for (let i=0; i < OPERATIONS.length; i++) {
|
||||||
|
if (operation === OPERATIONS[i].name) {
|
||||||
|
return OPERATIONS[i].operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
computeResult(data) {
|
||||||
|
let result = false;
|
||||||
|
if (data) {
|
||||||
|
let comparator = this.findOperation(this.operation);
|
||||||
|
let params = [];
|
||||||
|
params.push(data[this.metadata]);
|
||||||
|
if (this.input instanceof Array && this.input.length) {
|
||||||
|
this.input.forEach(input => params.push(input));
|
||||||
|
}
|
||||||
|
if (typeof comparator === 'function') {
|
||||||
|
result = comparator(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitEvent(eventName, data) {
|
||||||
|
this.emit(eventName, {
|
||||||
|
id: this.id,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestLAD(options) {
|
||||||
|
options = Object.assign({},
|
||||||
|
options,
|
||||||
|
{
|
||||||
|
strategy: 'latest',
|
||||||
|
size: 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.isValid()) {
|
||||||
|
return this.formatData({}, options.telemetryObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
const telemetryRequests = options.telemetryObjects
|
||||||
|
.map(telemetryObject => this.telemetryAPI.request(
|
||||||
|
telemetryObject,
|
||||||
|
options
|
||||||
|
));
|
||||||
|
|
||||||
|
return Promise.all(telemetryRequests)
|
||||||
|
.then(telemetryRequestsResults => {
|
||||||
|
telemetryRequestsResults.forEach((results, index) => {
|
||||||
|
const latestDatum = results.length ? results[results.length - 1] : {};
|
||||||
|
if (index === telemetryRequestsResults.length-1) {
|
||||||
|
//when the last result is computed, we return the result
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData(latestDatum, options.telemetryObjects)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (latestDatum) {
|
||||||
|
this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.emitEvent('criterionRemoved');
|
||||||
|
delete this.telemetryObjects;
|
||||||
|
delete this.telemetryDataCache;
|
||||||
|
delete this.telemetryObjectIdAsString;
|
||||||
|
delete this.telemetryObject;
|
||||||
|
}
|
||||||
|
}
|
149
src/plugins/condition/criterion/TelemetryCriterion.js
Normal file
149
src/plugins/condition/criterion/TelemetryCriterion.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import EventEmitter from 'EventEmitter';
|
||||||
|
import {OPERATIONS} from '../utils/operations';
|
||||||
|
|
||||||
|
export default class TelemetryCriterion extends EventEmitter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes/Unsubscribes to telemetry and emits the result
|
||||||
|
* of operations performed on the telemetry data returned and a given input value.
|
||||||
|
* @constructor
|
||||||
|
* @param telemetryDomainObjectDefinition {id: uuid, operation: enum, input: Array, metadata: string, key: {domainObject.identifier} }
|
||||||
|
* @param openmct
|
||||||
|
*/
|
||||||
|
constructor(telemetryDomainObjectDefinition, openmct) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.objectAPI = this.openmct.objects;
|
||||||
|
this.telemetryAPI = this.openmct.telemetry;
|
||||||
|
this.timeAPI = this.openmct.time;
|
||||||
|
this.id = telemetryDomainObjectDefinition.id;
|
||||||
|
this.telemetry = telemetryDomainObjectDefinition.telemetry;
|
||||||
|
this.operation = telemetryDomainObjectDefinition.operation;
|
||||||
|
this.input = telemetryDomainObjectDefinition.input;
|
||||||
|
this.metadata = telemetryDomainObjectDefinition.metadata;
|
||||||
|
this.telemetryObject = telemetryDomainObjectDefinition.telemetryObject;
|
||||||
|
this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(telemetryDomainObjectDefinition.telemetry);
|
||||||
|
this.on(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
||||||
|
this.emitEvent('criterionUpdated', this);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTelemetry(telemetryObjects) {
|
||||||
|
this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
|
||||||
|
}
|
||||||
|
|
||||||
|
formatData(data) {
|
||||||
|
const datum = {
|
||||||
|
result: this.computeResult(data)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
// TODO check back to see if we should format times here
|
||||||
|
this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
|
||||||
|
datum[timeSystem.key] = data[timeSystem.key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return datum;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubscription(data) {
|
||||||
|
if(this.isValid()) {
|
||||||
|
this.emitEvent('criterionResultUpdated', this.formatData(data));
|
||||||
|
} else {
|
||||||
|
this.emitEvent('criterionResultUpdated', this.formatData({}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findOperation(operation) {
|
||||||
|
for (let i=0, ii=OPERATIONS.length; i < ii; i++) {
|
||||||
|
if (operation === OPERATIONS[i].name) {
|
||||||
|
return OPERATIONS[i].operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
computeResult(data) {
|
||||||
|
let result = false;
|
||||||
|
if (data) {
|
||||||
|
let comparator = this.findOperation(this.operation);
|
||||||
|
let params = [];
|
||||||
|
params.push(data[this.metadata]);
|
||||||
|
if (this.input instanceof Array && this.input.length) {
|
||||||
|
this.input.forEach(input => params.push(input));
|
||||||
|
}
|
||||||
|
if (typeof comparator === 'function') {
|
||||||
|
result = comparator(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitEvent(eventName, data) {
|
||||||
|
this.emit(eventName, {
|
||||||
|
id: this.id,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return this.telemetryObject && this.metadata && this.operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestLAD(options) {
|
||||||
|
options = Object.assign({},
|
||||||
|
options,
|
||||||
|
{
|
||||||
|
strategy: 'latest',
|
||||||
|
size: 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.isValid()) {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData({})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.telemetryAPI.request(
|
||||||
|
this.telemetryObject,
|
||||||
|
options
|
||||||
|
).then(results => {
|
||||||
|
const latestDatum = results.length ? results[results.length - 1] : {};
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: this.formatData(latestDatum)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.off(`subscription:${this.telemetryObjectIdAsString}`, this.handleSubscription);
|
||||||
|
this.emitEvent('criterionRemoved');
|
||||||
|
delete this.telemetryObjectIdAsString;
|
||||||
|
delete this.telemetryObject;
|
||||||
|
}
|
||||||
|
}
|
111
src/plugins/condition/criterion/TelemetryCriterionSpec.js
Normal file
111
src/plugins/condition/criterion/TelemetryCriterionSpec.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import TelemetryCriterion from "./TelemetryCriterion";
|
||||||
|
|
||||||
|
let openmct = {},
|
||||||
|
mockListener,
|
||||||
|
testCriterionDefinition,
|
||||||
|
testTelemetryObject,
|
||||||
|
telemetryCriterion;
|
||||||
|
|
||||||
|
describe("The telemetry criterion", function () {
|
||||||
|
|
||||||
|
beforeEach (() => {
|
||||||
|
testTelemetryObject = {
|
||||||
|
identifier:{ namespace: "", key: "test-object"},
|
||||||
|
type: "test-object",
|
||||||
|
name: "Test Object",
|
||||||
|
telemetry: {
|
||||||
|
valueMetadatas: [{
|
||||||
|
key: "value",
|
||||||
|
name: "Value",
|
||||||
|
hints: {
|
||||||
|
range: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "utc",
|
||||||
|
name: "Time",
|
||||||
|
format: "utc",
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: "testSource",
|
||||||
|
source: "value",
|
||||||
|
name: "Test",
|
||||||
|
format: "enum"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
|
||||||
|
openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
|
||||||
|
openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter"]);
|
||||||
|
openmct.telemetry.isTelemetryObject.and.returnValue(true);
|
||||||
|
openmct.telemetry.subscribe.and.returnValue(function () {});
|
||||||
|
openmct.telemetry.getValueFormatter.and.returnValue({
|
||||||
|
parse: function (value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
|
||||||
|
|
||||||
|
openmct.time = jasmine.createSpyObj('timeAPI',
|
||||||
|
['timeSystem', 'bounds', 'getAllTimeSystems']
|
||||||
|
);
|
||||||
|
openmct.time.timeSystem.and.returnValue({key: 'system'});
|
||||||
|
openmct.time.bounds.and.returnValue({start: 0, end: 1});
|
||||||
|
openmct.time.getAllTimeSystems.and.returnValue([{key: 'system'}]);
|
||||||
|
|
||||||
|
testCriterionDefinition = {
|
||||||
|
id: 'test-criterion-id',
|
||||||
|
telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
|
||||||
|
operation: 'lessThan',
|
||||||
|
metadata: 'sin',
|
||||||
|
telemetryObject: testTelemetryObject
|
||||||
|
};
|
||||||
|
|
||||||
|
mockListener = jasmine.createSpy('listener');
|
||||||
|
|
||||||
|
telemetryCriterion = new TelemetryCriterion(
|
||||||
|
testCriterionDefinition,
|
||||||
|
openmct
|
||||||
|
);
|
||||||
|
|
||||||
|
telemetryCriterion.on('criterionResultUpdated', mockListener);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initializes with a telemetry objectId as string", function () {
|
||||||
|
expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates and emits event on new data from telemetry providers", function () {
|
||||||
|
spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
|
||||||
|
telemetryCriterion.handleSubscription({
|
||||||
|
value: 'Hello',
|
||||||
|
utc: 'Hi'
|
||||||
|
});
|
||||||
|
expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
68
src/plugins/condition/plugin.js
Normal file
68
src/plugins/condition/plugin.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
import ConditionSetViewProvider from './ConditionSetViewProvider.js';
|
||||||
|
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
|
||||||
|
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
|
||||||
|
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
|
||||||
|
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
|
||||||
|
import uuid from "uuid";
|
||||||
|
|
||||||
|
export default function ConditionPlugin() {
|
||||||
|
|
||||||
|
return function install(openmct) {
|
||||||
|
|
||||||
|
openmct.types.addType('conditionSet', {
|
||||||
|
name: 'Condition Set',
|
||||||
|
key: 'conditionSet',
|
||||||
|
description: 'Monitor and evaluate telemetry values in real-time with a wide variety of criteria. Use to control the styling of many objects in Open MCT.',
|
||||||
|
creatable: true,
|
||||||
|
cssClass: 'icon-conditional',
|
||||||
|
initialize: function (domainObject) {
|
||||||
|
domainObject.configuration = {
|
||||||
|
conditionTestData: [],
|
||||||
|
conditionCollection: [{
|
||||||
|
isDefault: true,
|
||||||
|
id: uuid(),
|
||||||
|
configuration: {
|
||||||
|
name: 'Default',
|
||||||
|
output: 'Default',
|
||||||
|
trigger: 'all',
|
||||||
|
criteria: []
|
||||||
|
},
|
||||||
|
summary: 'Default condition'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
domainObject.composition = [];
|
||||||
|
domainObject.telemetry = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
openmct.legacyExtension('policies', {
|
||||||
|
category: 'view',
|
||||||
|
implementation: ConditionSetViewPolicy
|
||||||
|
});
|
||||||
|
openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
|
||||||
|
openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
|
||||||
|
openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
|
||||||
|
openmct.objectViews.addProvider(new ConditionSetViewProvider(openmct));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
97
src/plugins/condition/pluginSpec.js
Normal file
97
src/plugins/condition/pluginSpec.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import { createOpenMct } from "testTools";
|
||||||
|
import ConditionPlugin from "./plugin";
|
||||||
|
|
||||||
|
let openmct = createOpenMct();
|
||||||
|
openmct.install(new ConditionPlugin());
|
||||||
|
|
||||||
|
let conditionSetDefinition;
|
||||||
|
let mockConditionSetDomainObject;
|
||||||
|
let element;
|
||||||
|
let child;
|
||||||
|
|
||||||
|
describe('the plugin', function () {
|
||||||
|
|
||||||
|
beforeAll((done) => {
|
||||||
|
|
||||||
|
conditionSetDefinition = openmct.types.get('conditionSet').definition;
|
||||||
|
const appHolder = document.createElement('div');
|
||||||
|
appHolder.style.width = '640px';
|
||||||
|
appHolder.style.height = '480px';
|
||||||
|
|
||||||
|
element = document.createElement('div');
|
||||||
|
child = document.createElement('div');
|
||||||
|
element.appendChild(child);
|
||||||
|
|
||||||
|
mockConditionSetDomainObject = {
|
||||||
|
identifier: {
|
||||||
|
key: 'testConditionSetKey',
|
||||||
|
namespace: ''
|
||||||
|
},
|
||||||
|
type: 'conditionSet'
|
||||||
|
};
|
||||||
|
|
||||||
|
conditionSetDefinition.initialize(mockConditionSetDomainObject);
|
||||||
|
|
||||||
|
openmct.on('start', done);
|
||||||
|
openmct.start(appHolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mockConditionSetObject = {
|
||||||
|
name: 'Condition Set',
|
||||||
|
key: 'conditionSet',
|
||||||
|
creatable: true
|
||||||
|
};
|
||||||
|
|
||||||
|
it('defines a conditionSet object type with the correct key', () => {
|
||||||
|
expect(conditionSetDefinition.key).toEqual(mockConditionSetObject.key);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('the conditionSet object', () => {
|
||||||
|
|
||||||
|
it('is creatable', () => {
|
||||||
|
expect(conditionSetDefinition.creatable).toEqual(mockConditionSetObject.creatable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initializes with an empty composition list', () => {
|
||||||
|
expect(mockConditionSetDomainObject.composition instanceof Array).toBeTrue();
|
||||||
|
expect(mockConditionSetDomainObject.composition.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides a view', () => {
|
||||||
|
const testViewObject = {
|
||||||
|
id:"test-object",
|
||||||
|
type: "conditionSet",
|
||||||
|
configuration: {
|
||||||
|
conditionCollection: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applicableViews = openmct.objectViews.get(testViewObject);
|
||||||
|
let conditionSetView = applicableViews.find((viewProvider) => viewProvider.key === 'conditionSet.view');
|
||||||
|
expect(conditionSetView).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
54
src/plugins/condition/utils/constants.js
Normal file
54
src/plugins/condition/utils/constants.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export const TRIGGER = {
|
||||||
|
ANY: 'any',
|
||||||
|
ALL: 'all',
|
||||||
|
NOT: 'not',
|
||||||
|
XOR: 'xor'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TRIGGER_LABEL = {
|
||||||
|
'any': 'when any criteria are met',
|
||||||
|
'all': 'when all criteria are met',
|
||||||
|
'not': 'when no criteria are met',
|
||||||
|
'xor': 'when only one criteria is met'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const STYLE_CONSTANTS = {
|
||||||
|
isStyleInvisible: 'is-style-invisible',
|
||||||
|
borderColorTitle: 'Set border color',
|
||||||
|
textColorTitle: 'Set text color',
|
||||||
|
backgroundColorTitle: 'Set background color',
|
||||||
|
imagePropertiesTitle: 'Edit image properties',
|
||||||
|
visibilityHidden: 'Hidden',
|
||||||
|
visibilityVisible: 'Visible'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ERROR = {
|
||||||
|
'TELEMETRY_NOT_FOUND': {
|
||||||
|
errorText: 'Telemetry not found for criterion'
|
||||||
|
},
|
||||||
|
'CONDITION_NOT_FOUND': {
|
||||||
|
errorText: 'Condition not found'
|
||||||
|
}
|
||||||
|
};
|
54
src/plugins/condition/utils/evaluator.js
Normal file
54
src/plugins/condition/utils/evaluator.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export const computeCondition = (resultMap, allMustBeTrue) => {
|
||||||
|
let result = false;
|
||||||
|
for (let key in resultMap) {
|
||||||
|
if (resultMap.hasOwnProperty(key)) {
|
||||||
|
result = resultMap[key];
|
||||||
|
if (allMustBeTrue && !result) {
|
||||||
|
//If we want all conditions to be true, then even one negative result should break.
|
||||||
|
break;
|
||||||
|
} else if (!allMustBeTrue && result) {
|
||||||
|
//If we want at least one condition to be true, then even one positive result should break.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Returns true only if limit number of results are satisfied
|
||||||
|
export const computeConditionByLimit = (resultMap, limit) => {
|
||||||
|
let trueCount = 0;
|
||||||
|
for (let key in resultMap) {
|
||||||
|
if (resultMap.hasOwnProperty(key)) {
|
||||||
|
if (resultMap[key]) {
|
||||||
|
trueCount++;
|
||||||
|
}
|
||||||
|
if (trueCount > limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trueCount === limit;
|
||||||
|
};
|
66
src/plugins/condition/utils/evaluatorSpec.js
Normal file
66
src/plugins/condition/utils/evaluatorSpec.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import { computeConditionByLimit } from "./evaluator";
|
||||||
|
|
||||||
|
describe('evaluate results based on trigger', function () {
|
||||||
|
|
||||||
|
it('should evaluate to true if trigger is NOT', () => {
|
||||||
|
const results = {
|
||||||
|
result: false,
|
||||||
|
result1: false,
|
||||||
|
result2: false
|
||||||
|
};
|
||||||
|
const result = computeConditionByLimit(results, 0);
|
||||||
|
expect(result).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate to false if trigger is NOT', () => {
|
||||||
|
const results = {
|
||||||
|
result: true,
|
||||||
|
result1: false,
|
||||||
|
result2: false
|
||||||
|
};
|
||||||
|
const result = computeConditionByLimit(results, 0);
|
||||||
|
expect(result).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate to true if trigger is XOR', () => {
|
||||||
|
const results = {
|
||||||
|
result: false,
|
||||||
|
result1: true,
|
||||||
|
result2: false
|
||||||
|
};
|
||||||
|
const result = computeConditionByLimit(results, 1);
|
||||||
|
expect(result).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate to false if trigger is XOR', () => {
|
||||||
|
const results = {
|
||||||
|
result: false,
|
||||||
|
result1: true,
|
||||||
|
result2: true
|
||||||
|
};
|
||||||
|
const result = computeConditionByLimit(results, 1);
|
||||||
|
expect(result).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
276
src/plugins/condition/utils/operations.js
Normal file
276
src/plugins/condition/utils/operations.js
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export const OPERATIONS = [
|
||||||
|
{
|
||||||
|
name: 'equalTo',
|
||||||
|
operation: function (input) {
|
||||||
|
return Number(input[0]) === Number(input[1]);
|
||||||
|
},
|
||||||
|
text: 'is equal to',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'notEqualTo',
|
||||||
|
operation: function (input) {
|
||||||
|
return Number(input[0]) !== Number(input[1]);
|
||||||
|
},
|
||||||
|
text: 'is not equal to',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is not ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'greaterThan',
|
||||||
|
operation: function (input) {
|
||||||
|
return Number(input[0]) > Number(input[1]);
|
||||||
|
},
|
||||||
|
text: 'is greater than',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' > ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lessThan',
|
||||||
|
operation: function (input) {
|
||||||
|
return Number(input[0]) < Number(input[1]);
|
||||||
|
},
|
||||||
|
text: 'is less than',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' < ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'greaterThanOrEq',
|
||||||
|
operation: function (input) {
|
||||||
|
return Number(input[0]) >= Number(input[1]);
|
||||||
|
},
|
||||||
|
text: 'is greater than or equal to',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' >= ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lessThanOrEq',
|
||||||
|
operation: function (input) {
|
||||||
|
return Number(input[0]) <= Number(input[1]);
|
||||||
|
},
|
||||||
|
text: 'is less than or equal to',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' <= ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'between',
|
||||||
|
operation: function (input) {
|
||||||
|
let numberInputs = [];
|
||||||
|
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
|
||||||
|
let larger = Math.max(...numberInputs.slice(1,3));
|
||||||
|
let smaller = Math.min(...numberInputs.slice(1,3));
|
||||||
|
return (numberInputs[0] > smaller) && (numberInputs[0] < larger);
|
||||||
|
},
|
||||||
|
text: 'is between',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 2,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is between ' + values[0] + ' and ' + values[1];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'notBetween',
|
||||||
|
operation: function (input) {
|
||||||
|
let numberInputs = [];
|
||||||
|
input.forEach(inputValue => numberInputs.push(Number(inputValue)));
|
||||||
|
let larger = Math.max(...numberInputs.slice(1,3));
|
||||||
|
let smaller = Math.min(...numberInputs.slice(1,3));
|
||||||
|
return (numberInputs[0] < smaller) || (numberInputs[0] > larger);
|
||||||
|
},
|
||||||
|
text: 'is not between',
|
||||||
|
appliesTo: ['number'],
|
||||||
|
inputCount: 2,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is not between ' + values[0] + ' and ' + values[1];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'textContains',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0] && input[1] && input[0].includes(input[1]);
|
||||||
|
},
|
||||||
|
text: 'text contains',
|
||||||
|
appliesTo: ['string'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' contains ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'textDoesNotContain',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0] && input[1] && !input[0].includes(input[1]);
|
||||||
|
},
|
||||||
|
text: 'text does not contain',
|
||||||
|
appliesTo: ['string'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' does not contain ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'textStartsWith',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0].startsWith(input[1]);
|
||||||
|
},
|
||||||
|
text: 'text starts with',
|
||||||
|
appliesTo: ['string'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' starts with ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'textEndsWith',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0].endsWith(input[1]);
|
||||||
|
},
|
||||||
|
text: 'text ends with',
|
||||||
|
appliesTo: ['string'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' ends with ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'textIsExactly',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0] === input[1];
|
||||||
|
},
|
||||||
|
text: 'text is exactly',
|
||||||
|
appliesTo: ['string'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is exactly ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'isUndefined',
|
||||||
|
operation: function (input) {
|
||||||
|
return typeof input[0] === 'undefined';
|
||||||
|
},
|
||||||
|
text: 'is undefined',
|
||||||
|
appliesTo: ['string', 'number', 'enum'],
|
||||||
|
inputCount: 0,
|
||||||
|
getDescription: function () {
|
||||||
|
return ' is undefined';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'isDefined',
|
||||||
|
operation: function (input) {
|
||||||
|
return typeof input[0] !== 'undefined';
|
||||||
|
},
|
||||||
|
text: 'is defined',
|
||||||
|
appliesTo: ['string', 'number', 'enum'],
|
||||||
|
inputCount: 0,
|
||||||
|
getDescription: function () {
|
||||||
|
return ' is defined';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enumValueIs',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0] === input[1];
|
||||||
|
},
|
||||||
|
text: 'is',
|
||||||
|
appliesTo: ['enum'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enumValueIsNot',
|
||||||
|
operation: function (input) {
|
||||||
|
return input[0] !== input[1];
|
||||||
|
},
|
||||||
|
text: 'is not',
|
||||||
|
appliesTo: ['enum'],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is not ' + values.join(', ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'valueIs',
|
||||||
|
operation: function (input) {
|
||||||
|
if (input[1]) {
|
||||||
|
const values = input[1].split(',');
|
||||||
|
return values.find((value) => input[0].toString() === _.trim(value.toString()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
text: 'is one of',
|
||||||
|
appliesTo: ["string", "number"],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is one of ' + values[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'valueIsNot',
|
||||||
|
operation: function (input) {
|
||||||
|
if (input[1]) {
|
||||||
|
const values = input[1].split(',');
|
||||||
|
const found = values.find((value) => input[0].toString() === _.trim(value.toString()));
|
||||||
|
return !found;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
text: 'is not one of',
|
||||||
|
appliesTo: ["string", "number"],
|
||||||
|
inputCount: 1,
|
||||||
|
getDescription: function (values) {
|
||||||
|
return ' is not one of ' + values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const INPUT_TYPES = {
|
||||||
|
'string': 'text',
|
||||||
|
'number': 'number'
|
||||||
|
};
|
90
src/plugins/condition/utils/operationsSpec.js
Normal file
90
src/plugins/condition/utils/operationsSpec.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import { OPERATIONS } from "./operations";
|
||||||
|
let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIs');
|
||||||
|
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot');
|
||||||
|
let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
|
||||||
|
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
|
||||||
|
|
||||||
|
describe('Is one of and is not one of operations', function () {
|
||||||
|
|
||||||
|
it('should evaluate isOneOf to true for number inputs', () => {
|
||||||
|
const inputs = [45, "5,6,45,8"];
|
||||||
|
expect(!!isOneOfOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isOneOf to true for string inputs', () => {
|
||||||
|
const inputs = ["45", " 45, 645, 4,8 "];
|
||||||
|
expect(!!isOneOfOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isNotOneOf to true for number inputs', () => {
|
||||||
|
const inputs = [45, "5,6,4,8"];
|
||||||
|
expect(!!isNotOneOfOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isNotOneOf to true for string inputs', () => {
|
||||||
|
const inputs = ["45", " 5,645, 4,8 "];
|
||||||
|
expect(!!isNotOneOfOperation.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isOneOf to false for number inputs', () => {
|
||||||
|
const inputs = [4, "5, 6, 7, 8"];
|
||||||
|
expect(!!isOneOfOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isOneOf to false for string inputs', () => {
|
||||||
|
const inputs = ["4", "5,645 ,7,8"];
|
||||||
|
expect(!!isOneOfOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isNotOneOf to false for number inputs', () => {
|
||||||
|
const inputs = [4, "5,4, 7,8"];
|
||||||
|
expect(!!isNotOneOfOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isNotOneOf to false for string inputs', () => {
|
||||||
|
const inputs = ["4", "5,46,4,8"];
|
||||||
|
expect(!!isNotOneOfOperation.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isBetween to true', () => {
|
||||||
|
const inputs = ["4", "3", "89"];
|
||||||
|
expect(!!isBetween.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isNotBetween to true', () => {
|
||||||
|
const inputs = ["45", "100", "89"];
|
||||||
|
expect(!!isNotBetween.operation(inputs)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isBetween to false', () => {
|
||||||
|
const inputs = ["4", "100", "89"];
|
||||||
|
expect(!!isBetween.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should evaluate isNotBetween to false', () => {
|
||||||
|
const inputs = ["45", "30", "50"];
|
||||||
|
expect(!!isNotBetween.operation(inputs)).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
45
src/plugins/condition/utils/styleUtils.js
Normal file
45
src/plugins/condition/utils/styleUtils.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
export const getStyleProp = (key, defaultValue) => {
|
||||||
|
let styleProp = undefined;
|
||||||
|
switch(key) {
|
||||||
|
case 'fill': styleProp = {
|
||||||
|
backgroundColor: defaultValue || 'transparent'
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'stroke': styleProp = {
|
||||||
|
border: '1px solid ' + (defaultValue || 'transparent')
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'color': styleProp = {
|
||||||
|
color: defaultValue || 'transparent'
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'url': styleProp = {
|
||||||
|
imageUrl: defaultValue || 'transparent'
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return styleProp;
|
||||||
|
};
|
64
src/plugins/conditionWidget/ConditionWidgetViewProvider.js
Normal file
64
src/plugins/conditionWidget/ConditionWidgetViewProvider.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import ConditionWidgetComponent from './components/ConditionWidget.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default function ConditionWidget(openmct) {
|
||||||
|
return {
|
||||||
|
key: 'conditionWidget',
|
||||||
|
name: 'Condition Widget',
|
||||||
|
cssClass: 'icon-condition-widget',
|
||||||
|
canView: function (domainObject) {
|
||||||
|
return domainObject.type === 'conditionWidget';
|
||||||
|
},
|
||||||
|
canEdit: function (domainObject) {
|
||||||
|
return domainObject.type === 'conditionWidget';
|
||||||
|
},
|
||||||
|
view: function (domainObject) {
|
||||||
|
let component;
|
||||||
|
|
||||||
|
return {
|
||||||
|
show: function (element) {
|
||||||
|
component = new Vue({
|
||||||
|
el: element,
|
||||||
|
components: {
|
||||||
|
ConditionWidgetComponent: ConditionWidgetComponent
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
openmct,
|
||||||
|
domainObject
|
||||||
|
},
|
||||||
|
template: '<condition-widget-component></condition-widget-component>'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
destroy: function (element) {
|
||||||
|
component.$destroy();
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
priority: function () {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
55
src/plugins/conditionWidget/components/ConditionWidget.vue
Normal file
55
src/plugins/conditionWidget/components/ConditionWidget.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a class="c-condition-widget"
|
||||||
|
:href="internalDomainObject.url"
|
||||||
|
>
|
||||||
|
<div class="c-condition-widget__label">
|
||||||
|
{{ internalDomainObject.label }}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['openmct', 'domainObject'],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
internalDomainObject: this.domainObject
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.unlisten) {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateInternalDomainObject(domainObject) {
|
||||||
|
this.internalDomainObject = domainObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
58
src/plugins/conditionWidget/components/condition-widget.scss
Normal file
58
src/plugins/conditionWidget/components/condition-widget.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
.c-condition-widget {
|
||||||
|
$shdwSize: 3px;
|
||||||
|
@include userSelectNone();
|
||||||
|
background-color: rgba($colorBodyFg, 0.1); // Give a little presence if the user hasn't defined a fill color
|
||||||
|
border-radius: $basicCr;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
display: inline-block;
|
||||||
|
padding: $interiorMarginLg $interiorMarginLg * 2;
|
||||||
|
cursor: inherit !important;
|
||||||
|
&[href] {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make Condition Widget expand when in a hidden frame Layout context
|
||||||
|
// For both static and Flexible Layouts
|
||||||
|
.c-so-view--no-frame > .c-so-view__object-view > .c-condition-widget {
|
||||||
|
@include abs();
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some margin when a Condition Widget is in a Flexible Layout
|
||||||
|
.c-fl .c-so-view--no-frame .c-condition-widget {
|
||||||
|
@include abs(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the widget is in the main view, center it in the space
|
||||||
|
.l-shell__main-container > .c-condition-widget {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
58
src/plugins/conditionWidget/plugin.js
Normal file
58
src/plugins/conditionWidget/plugin.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import ConditionWidgetViewProvider from './ConditionWidgetViewProvider.js';
|
||||||
|
|
||||||
|
export default function plugin() {
|
||||||
|
return function install(openmct) {
|
||||||
|
openmct.objectViews.addProvider(new ConditionWidgetViewProvider(openmct));
|
||||||
|
|
||||||
|
openmct.types.addType('conditionWidget', {
|
||||||
|
name: "Condition Widget",
|
||||||
|
description: "A button that can be used on its own, or dynamically styled with a Condition Set.",
|
||||||
|
creatable: true,
|
||||||
|
cssClass: 'icon-condition-widget',
|
||||||
|
initialize(domainObject) {
|
||||||
|
domainObject.label = 'Condition Widget';
|
||||||
|
},
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
"key": "label",
|
||||||
|
"name": "Label",
|
||||||
|
"control": "textfield",
|
||||||
|
property: [
|
||||||
|
"label"
|
||||||
|
],
|
||||||
|
"required": true,
|
||||||
|
"cssClass": "l-input"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url",
|
||||||
|
"name": "URL",
|
||||||
|
"control": "textfield",
|
||||||
|
"required": false,
|
||||||
|
"cssClass": "l-input-lg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
@ -347,78 +347,6 @@ define(['lodash'], function (_) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFillMenu(selectedParent, selection) {
|
|
||||||
return {
|
|
||||||
control: "color-picker",
|
|
||||||
domainObject: selectedParent,
|
|
||||||
applicableSelectedItems: selection.filter(selectionPath => {
|
|
||||||
let type = selectionPath[0].context.layoutItem.type;
|
|
||||||
return type === 'text-view' ||
|
|
||||||
type === 'telemetry-view' ||
|
|
||||||
type === 'box-view';
|
|
||||||
}),
|
|
||||||
property: function (selectionPath) {
|
|
||||||
return getPath(selectionPath) + ".fill";
|
|
||||||
},
|
|
||||||
icon: "icon-paint-bucket",
|
|
||||||
title: "Set fill color"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStrokeMenu(selectedParent, selection) {
|
|
||||||
return {
|
|
||||||
control: "color-picker",
|
|
||||||
domainObject: selectedParent,
|
|
||||||
applicableSelectedItems: selection.filter(selectionPath => {
|
|
||||||
let type = selectionPath[0].context.layoutItem.type;
|
|
||||||
return type === 'text-view' ||
|
|
||||||
type === 'telemetry-view' ||
|
|
||||||
type === 'box-view' ||
|
|
||||||
type === 'image-view' ||
|
|
||||||
type === 'line-view';
|
|
||||||
}),
|
|
||||||
property: function (selectionPath) {
|
|
||||||
return getPath(selectionPath) + ".stroke";
|
|
||||||
},
|
|
||||||
icon: "icon-line-horz",
|
|
||||||
title: "Set border color"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTextColorMenu(selectedParent, selection) {
|
|
||||||
return {
|
|
||||||
control: "color-picker",
|
|
||||||
domainObject: selectedParent,
|
|
||||||
applicableSelectedItems: selection.filter(selectionPath => {
|
|
||||||
let type = selectionPath[0].context.layoutItem.type;
|
|
||||||
return type === 'text-view' || type === 'telemetry-view';
|
|
||||||
}),
|
|
||||||
property: function (selectionPath) {
|
|
||||||
return getPath(selectionPath) + ".color";
|
|
||||||
},
|
|
||||||
icon: "icon-font",
|
|
||||||
mandatory: true,
|
|
||||||
title: "Set text color",
|
|
||||||
preventNone: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getURLButton(selectedParent, selection) {
|
|
||||||
return {
|
|
||||||
control: "button",
|
|
||||||
domainObject: selectedParent,
|
|
||||||
applicableSelectedItems: selection.filter(selectionPath => {
|
|
||||||
return selectionPath[0].context.layoutItem.type === 'image-view';
|
|
||||||
}),
|
|
||||||
property: function (selectionPath) {
|
|
||||||
return getPath(selectionPath);
|
|
||||||
},
|
|
||||||
icon: "icon-image",
|
|
||||||
title: "Edit image properties",
|
|
||||||
dialog: DIALOG_FORM.image
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTextButton(selectedParent, selection) {
|
function getTextButton(selectedParent, selection) {
|
||||||
return {
|
return {
|
||||||
control: "button",
|
control: "button",
|
||||||
@ -429,7 +357,7 @@ define(['lodash'], function (_) {
|
|||||||
property: function (selectionPath) {
|
property: function (selectionPath) {
|
||||||
return getPath(selectionPath);
|
return getPath(selectionPath);
|
||||||
},
|
},
|
||||||
icon: "icon-gear",
|
icon: "icon-font",
|
||||||
title: "Edit text properties",
|
title: "Edit text properties",
|
||||||
dialog: DIALOG_FORM.text
|
dialog: DIALOG_FORM.text
|
||||||
};
|
};
|
||||||
@ -505,14 +433,14 @@ define(['lodash'], function (_) {
|
|||||||
|
|
||||||
let toolbar = {
|
let toolbar = {
|
||||||
'add-menu': [],
|
'add-menu': [],
|
||||||
|
'text': [],
|
||||||
|
'url': [],
|
||||||
'toggle-frame': [],
|
'toggle-frame': [],
|
||||||
'display-mode': [],
|
'display-mode': [],
|
||||||
'telemetry-value': [],
|
'telemetry-value': [],
|
||||||
'style': [],
|
'style': [],
|
||||||
'text-style': [],
|
'text-style': [],
|
||||||
'position': [],
|
'position': [],
|
||||||
'text': [],
|
|
||||||
'url': [],
|
|
||||||
'remove': []
|
'remove': []
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -550,15 +478,8 @@ define(['lodash'], function (_) {
|
|||||||
if (toolbar['telemetry-value'].length === 0) {
|
if (toolbar['telemetry-value'].length === 0) {
|
||||||
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)];
|
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
if (toolbar.style.length < 2) {
|
|
||||||
toolbar.style = [
|
|
||||||
getFillMenu(selectedParent, selectedObjects),
|
|
||||||
getStrokeMenu(selectedParent, selectedObjects)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (toolbar['text-style'].length === 0) {
|
if (toolbar['text-style'].length === 0) {
|
||||||
toolbar['text-style'] = [
|
toolbar['text-style'] = [
|
||||||
getTextColorMenu(selectedParent, selectedObjects),
|
|
||||||
getTextSizeMenu(selectedParent, selectedObjects)
|
getTextSizeMenu(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -575,15 +496,8 @@ define(['lodash'], function (_) {
|
|||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'text-view') {
|
} else if (layoutItem.type === 'text-view') {
|
||||||
if (toolbar.style.length < 2) {
|
|
||||||
toolbar.style = [
|
|
||||||
getFillMenu(selectedParent, selectedObjects),
|
|
||||||
getStrokeMenu(selectedParent, selectedObjects)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (toolbar['text-style'].length === 0) {
|
if (toolbar['text-style'].length === 0) {
|
||||||
toolbar['text-style'] = [
|
toolbar['text-style'] = [
|
||||||
getTextColorMenu(selectedParent, selectedObjects),
|
|
||||||
getTextSizeMenu(selectedParent, selectedObjects)
|
getTextSizeMenu(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -603,12 +517,6 @@ define(['lodash'], function (_) {
|
|||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'box-view') {
|
} else if (layoutItem.type === 'box-view') {
|
||||||
if (toolbar.style.length < 2) {
|
|
||||||
toolbar.style = [
|
|
||||||
getFillMenu(selectedParent, selectedObjects),
|
|
||||||
getStrokeMenu(selectedParent, selectedObjects)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
getStackOrder(selectedParent, selectionPath),
|
||||||
@ -622,11 +530,6 @@ define(['lodash'], function (_) {
|
|||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'image-view') {
|
} else if (layoutItem.type === 'image-view') {
|
||||||
if (toolbar.style.length === 0) {
|
|
||||||
toolbar.style = [
|
|
||||||
getStrokeMenu(selectedParent, selectedObjects)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
getStackOrder(selectedParent, selectionPath),
|
||||||
@ -636,18 +539,10 @@ define(['lodash'], function (_) {
|
|||||||
getWidthInput(selectedParent, selectedObjects)
|
getWidthInput(selectedParent, selectedObjects)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (toolbar.url.length === 0) {
|
|
||||||
toolbar.url = [getURLButton(selectedParent, selectedObjects)];
|
|
||||||
}
|
|
||||||
if (toolbar.remove.length === 0) {
|
if (toolbar.remove.length === 0) {
|
||||||
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
|
||||||
}
|
}
|
||||||
} else if (layoutItem.type === 'line-view') {
|
} else if (layoutItem.type === 'line-view') {
|
||||||
if (toolbar.style.length === 0) {
|
|
||||||
toolbar.style = [
|
|
||||||
getStrokeMenu(selectedParent, selectedObjects)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (toolbar.position.length === 0) {
|
if (toolbar.position.length === 0) {
|
||||||
toolbar.position = [
|
toolbar.position = [
|
||||||
getStackOrder(selectedParent, selectionPath),
|
getStackOrder(selectedParent, selectionPath),
|
||||||
|
@ -23,20 +23,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="isEditing"
|
v-if="isEditing"
|
||||||
class="c-properties"
|
class="c-inspect-properties"
|
||||||
>
|
>
|
||||||
<div class="c-properties__header">
|
<div class="c-inspect-properties__header">
|
||||||
Alphanumeric Format
|
Alphanumeric Format
|
||||||
</div>
|
</div>
|
||||||
<ul class="c-properties__section">
|
<ul class="c-inspect-properties__section">
|
||||||
<li class="c-properties__row">
|
<li class="c-inspect-properties__row">
|
||||||
<div
|
<div
|
||||||
class="c-properties__label"
|
class="c-inspect-properties__label"
|
||||||
title="Printf formatting for the selected telemetry"
|
title="Printf formatting for the selected telemetry"
|
||||||
>
|
>
|
||||||
<label for="telemetryPrintfFormat">Format</label>
|
<label for="telemetryPrintfFormat">Format</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
<input
|
<input
|
||||||
id="telemetryPrintfFormat"
|
id="telemetryPrintfFormat"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-box-view"
|
class="c-box-view"
|
||||||
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
></div>
|
></div>
|
||||||
</layout-frame>
|
</layout-frame>
|
||||||
@ -36,6 +37,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
import LayoutFrame from './LayoutFrame.vue'
|
||||||
|
import conditionalStylesMixin from '../mixins/objectStyles-mixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition() {
|
makeDefinition() {
|
||||||
@ -52,6 +54,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
LayoutFrame
|
LayoutFrame
|
||||||
},
|
},
|
||||||
|
mixins: [conditionalStylesMixin],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -71,10 +74,13 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
return {
|
return Object.assign({
|
||||||
backgroundColor: this.item.fill,
|
backgroundColor: this.item.fill,
|
||||||
border: '1px solid ' + this.item.stroke
|
border: '1px solid ' + this.item.stroke
|
||||||
};
|
}, this.itemStyle);
|
||||||
|
},
|
||||||
|
styleClass() {
|
||||||
|
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-image-view"
|
class="c-image-view"
|
||||||
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
></div>
|
></div>
|
||||||
</layout-frame>
|
</layout-frame>
|
||||||
@ -36,6 +37,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
import LayoutFrame from './LayoutFrame.vue'
|
||||||
|
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, element) {
|
makeDefinition(openmct, gridSize, element) {
|
||||||
@ -52,6 +54,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
LayoutFrame
|
LayoutFrame
|
||||||
},
|
},
|
||||||
|
mixins: [conditionalStylesMixin],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -72,9 +75,12 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
return {
|
return {
|
||||||
backgroundImage: 'url(' + this.item.url + ')',
|
backgroundImage: this.itemStyle ? ('url(' + this.itemStyle.imageUrl + ')') : 'url(' + this.item.url + ')',
|
||||||
border: '1px solid ' + this.item.stroke
|
border: (this.itemStyle && this.itemStyle.border) ? this.itemStyle.border : ('1px solid ' + this.item.stroke)
|
||||||
}
|
};
|
||||||
|
},
|
||||||
|
styleClass() {
|
||||||
|
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="l-layout__frame c-frame no-frame"
|
class="l-layout__frame c-frame no-frame"
|
||||||
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@ -31,7 +32,7 @@
|
|||||||
>
|
>
|
||||||
<line
|
<line
|
||||||
v-bind="linePosition"
|
v-bind="linePosition"
|
||||||
:stroke="item.stroke"
|
:stroke="stroke"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@ -60,6 +61,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
||||||
|
|
||||||
const START_HANDLE_QUADRANTS = {
|
const START_HANDLE_QUADRANTS = {
|
||||||
1: 'c-frame-edit__handle--sw',
|
1: 'c-frame-edit__handle--sw',
|
||||||
2: 'c-frame-edit__handle--se',
|
2: 'c-frame-edit__handle--se',
|
||||||
@ -85,6 +88,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
mixins: [conditionalStylesMixin],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -122,6 +126,13 @@ export default {
|
|||||||
}
|
}
|
||||||
return {x, y, x2, y2};
|
return {x, y, x2, y2};
|
||||||
},
|
},
|
||||||
|
stroke() {
|
||||||
|
if (this.itemStyle && this.itemStyle.border) {
|
||||||
|
return this.itemStyle.border.replace('1px solid ', '');
|
||||||
|
} else {
|
||||||
|
return this.item.stroke;
|
||||||
|
}
|
||||||
|
},
|
||||||
style() {
|
style() {
|
||||||
let {x, y, x2, y2} = this.position;
|
let {x, y, x2, y2} = this.position;
|
||||||
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);
|
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);
|
||||||
@ -135,6 +146,9 @@ export default {
|
|||||||
height: `${height}px`
|
height: `${height}px`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
styleClass() {
|
||||||
|
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
||||||
|
},
|
||||||
startHandleClass() {
|
startHandleClass() {
|
||||||
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
||||||
},
|
},
|
||||||
|
@ -45,7 +45,7 @@ import LayoutFrame from './LayoutFrame.vue'
|
|||||||
const MINIMUM_FRAME_SIZE = [320, 180],
|
const MINIMUM_FRAME_SIZE = [320, 180],
|
||||||
DEFAULT_DIMENSIONS = [10, 10],
|
DEFAULT_DIMENSIONS = [10, 10],
|
||||||
DEFAULT_POSITION = [1, 1],
|
DEFAULT_POSITION = [1, 1],
|
||||||
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget'];
|
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget', 'conditionWidget'];
|
||||||
|
|
||||||
function getDefaultDimensions(gridSize) {
|
function getDefaultDimensions(gridSize) {
|
||||||
return MINIMUM_FRAME_SIZE.map((min, index) => {
|
return MINIMUM_FRAME_SIZE.map((min, index) => {
|
||||||
|
@ -36,6 +36,8 @@
|
|||||||
<div
|
<div
|
||||||
v-if="showLabel"
|
v-if="showLabel"
|
||||||
class="c-telemetry-view__label"
|
class="c-telemetry-view__label"
|
||||||
|
:class="[styleClass]"
|
||||||
|
:style="objectStyle"
|
||||||
>
|
>
|
||||||
<div class="c-telemetry-view__label-text">
|
<div class="c-telemetry-view__label-text">
|
||||||
{{ domainObject.name }}
|
{{ domainObject.name }}
|
||||||
@ -46,7 +48,8 @@
|
|||||||
v-if="showValue"
|
v-if="showValue"
|
||||||
:title="fieldName"
|
:title="fieldName"
|
||||||
class="c-telemetry-view__value"
|
class="c-telemetry-view__value"
|
||||||
:class="[telemetryClass]"
|
:class="[telemetryClass, !telemetryClass && styleClass]"
|
||||||
|
:style="!telemetryClass && objectStyle"
|
||||||
>
|
>
|
||||||
<div class="c-telemetry-view__value-text">
|
<div class="c-telemetry-view__value-text">
|
||||||
{{ telemetryValue }}
|
{{ telemetryValue }}
|
||||||
@ -59,6 +62,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
import LayoutFrame from './LayoutFrame.vue'
|
||||||
import printj from 'printj'
|
import printj from 'printj'
|
||||||
|
import StyleRuleManager from "../../condition/StyleRuleManager";
|
||||||
|
|
||||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||||
DEFAULT_POSITION = [1, 1],
|
DEFAULT_POSITION = [1, 1],
|
||||||
@ -109,7 +113,8 @@ export default {
|
|||||||
datum: undefined,
|
datum: undefined,
|
||||||
formats: undefined,
|
formats: undefined,
|
||||||
domainObject: undefined,
|
domainObject: undefined,
|
||||||
currentObjectPath: undefined
|
currentObjectPath: undefined,
|
||||||
|
objectStyle: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -129,6 +134,9 @@ export default {
|
|||||||
fontSize: this.item.size
|
fontSize: this.item.size
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
styleClass() {
|
||||||
|
return this.objectStyle && this.objectStyle.isStyleInvisible;
|
||||||
|
},
|
||||||
fieldName() {
|
fieldName() {
|
||||||
return this.valueMetadata && this.valueMetadata.name;
|
return this.valueMetadata && this.valueMetadata.name;
|
||||||
},
|
},
|
||||||
@ -182,6 +190,15 @@ export default {
|
|||||||
this.removeSelectable();
|
this.removeSelectable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.unlistenStyles) {
|
||||||
|
this.unlistenStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.styleRuleManager) {
|
||||||
|
this.styleRuleManager.destroy();
|
||||||
|
delete this.styleRuleManager;
|
||||||
|
}
|
||||||
|
|
||||||
this.openmct.time.off("bounds", this.refreshData);
|
this.openmct.time.off("bounds", this.refreshData);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -224,6 +241,7 @@ export default {
|
|||||||
},
|
},
|
||||||
setObject(domainObject) {
|
setObject(domainObject) {
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
|
this.initObjectStyles();
|
||||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||||
@ -248,6 +266,30 @@ export default {
|
|||||||
},
|
},
|
||||||
showContextMenu(event) {
|
showContextMenu(event) {
|
||||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||||
|
},
|
||||||
|
initObjectStyles() {
|
||||||
|
if (this.domainObject.configuration) {
|
||||||
|
this.styleRuleManager = new StyleRuleManager(this.domainObject.configuration.objectStyles, this.openmct, this.updateStyle.bind(this));
|
||||||
|
|
||||||
|
if (this.unlistenStyles) {
|
||||||
|
this.unlistenStyles();
|
||||||
|
}
|
||||||
|
this.unlistenStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => {
|
||||||
|
//Updating object styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||||
|
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateStyle(styleObj) {
|
||||||
|
let keys = Object.keys(styleObj);
|
||||||
|
keys.forEach(key => {
|
||||||
|
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('transparent') > -1)) {
|
||||||
|
if (styleObj[key]) {
|
||||||
|
styleObj[key] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.objectStyle = styleObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-text-view"
|
class="c-text-view"
|
||||||
|
:class="[styleClass]"
|
||||||
:style="style"
|
:style="style"
|
||||||
>
|
>
|
||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
@ -38,6 +39,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
import LayoutFrame from './LayoutFrame.vue'
|
||||||
|
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, element) {
|
makeDefinition(openmct, gridSize, element) {
|
||||||
@ -57,6 +59,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
LayoutFrame
|
LayoutFrame
|
||||||
},
|
},
|
||||||
|
mixins: [conditionalStylesMixin],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -76,12 +79,15 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
return {
|
return Object.assign({
|
||||||
backgroundColor: this.item.fill,
|
backgroundColor: this.item.fill,
|
||||||
borderColor: this.item.stroke,
|
border: '1px solid ' + this.item.stroke,
|
||||||
color: this.item.color,
|
color: this.item.color,
|
||||||
fontSize: this.item.size
|
fontSize: this.item.size
|
||||||
};
|
}, this.itemStyle);
|
||||||
|
},
|
||||||
|
styleClass() {
|
||||||
|
return this.itemStyle && this.itemStyle.isStyleInvisible;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
81
src/plugins/displayLayout/mixins/objectStyles-mixin.js
Normal file
81
src/plugins/displayLayout/mixins/objectStyles-mixin.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
itemStyle: this.itemStyle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.domainObject = this.$parent.domainObject;
|
||||||
|
this.itemId = this.item.id;
|
||||||
|
this.objectStyle = this.getObjectStyleForItem(this.domainObject.configuration.objectStyles);
|
||||||
|
this.initObjectStyles();
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
if (this.stopListeningObjectStyles) {
|
||||||
|
this.stopListeningObjectStyles();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getObjectStyleForItem(objectStyle) {
|
||||||
|
if (objectStyle) {
|
||||||
|
return objectStyle[this.itemId] ? Object.assign({}, objectStyle[this.itemId]) : undefined;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initObjectStyles() {
|
||||||
|
if (!this.styleRuleManager) {
|
||||||
|
this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this));
|
||||||
|
} else {
|
||||||
|
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stopListeningObjectStyles) {
|
||||||
|
this.stopListeningObjectStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopListeningObjectStyles = this.openmct.objects.observe(this.domainObject, 'configuration.objectStyles', (newObjectStyle) => {
|
||||||
|
//Updating object styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||||
|
let newItemObjectStyle = this.getObjectStyleForItem(newObjectStyle);
|
||||||
|
if (this.objectStyle !== newItemObjectStyle) {
|
||||||
|
this.objectStyle = newItemObjectStyle;
|
||||||
|
this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateStyle(style) {
|
||||||
|
this.itemStyle = style;
|
||||||
|
let keys = Object.keys(this.itemStyle);
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if ((typeof this.itemStyle[key] === 'string') && (this.itemStyle[key].indexOf('transparent') > -1)) {
|
||||||
|
delete this.itemStyle[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-properties__section c-filter-settings">
|
<div class="c-inspect-properties__section c-filter-settings">
|
||||||
<li
|
<li
|
||||||
v-for="(filter, index) in filterField.filters"
|
v-for="(filter, index) in filterField.filters"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="c-properties__row c-filter-settings__setting"
|
class="c-inspect-properties__row c-filter-settings__setting"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-properties__label label"
|
class="c-inspect-properties__label label"
|
||||||
:disabled="useGlobal"
|
:disabled="useGlobal"
|
||||||
>
|
>
|
||||||
{{ filterField.name }} =
|
{{ filterField.name }} =
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value value">
|
<div class="c-inspect-properties__value value">
|
||||||
<!-- EDITING -->
|
<!-- EDITING -->
|
||||||
<!-- String input, editing -->
|
<!-- String input, editing -->
|
||||||
<template v-if="!filter.possibleValues && isEditing">
|
<template v-if="!filter.possibleValues && isEditing">
|
||||||
|
@ -26,17 +26,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="expanded">
|
<div v-if="expanded">
|
||||||
<ul class="c-properties">
|
<ul class="c-inspect-properties">
|
||||||
<div
|
<div
|
||||||
v-if="!isEditing && persistedFilters.useGlobal"
|
v-if="!isEditing && persistedFilters.useGlobal"
|
||||||
class="c-properties__label span-all"
|
class="c-inspect-properties__label span-all"
|
||||||
>
|
>
|
||||||
Uses global filter
|
Uses global filter
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isEditing"
|
v-if="isEditing"
|
||||||
class="c-properties__label span-all"
|
class="c-inspect-properties__label span-all"
|
||||||
>
|
>
|
||||||
<toggle-switch
|
<toggle-switch
|
||||||
:id="keyString"
|
:id="keyString"
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
v-if="expanded"
|
v-if="expanded"
|
||||||
class="c-properties"
|
class="c-inspect-properties"
|
||||||
>
|
>
|
||||||
<filter-field
|
<filter-field
|
||||||
v-for="metadatum in globalMetadata"
|
v-for="metadatum in globalMetadata"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
}
|
}
|
||||||
.c-filter-tree {
|
.c-filter-tree {
|
||||||
// Filters UI uses a tree-based structure
|
// Filters UI uses a tree-based structure
|
||||||
.c-properties {
|
.c-inspect-properties {
|
||||||
// Add extra margin to account for filter-indicator
|
// Add extra margin to account for filter-indicator
|
||||||
margin-left: 38px;
|
margin-left: 38px;
|
||||||
}
|
}
|
||||||
|
@ -313,8 +313,4 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-object-view {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ export default function ImageryViewProvider(openmct) {
|
|||||||
return {
|
return {
|
||||||
show: function (element) {
|
show: function (element) {
|
||||||
component = new Vue({
|
component = new Vue({
|
||||||
|
el: element,
|
||||||
components: {
|
components: {
|
||||||
ImageryViewLayout
|
ImageryViewLayout
|
||||||
},
|
},
|
||||||
@ -33,7 +34,6 @@ export default function ImageryViewProvider(openmct) {
|
|||||||
openmct,
|
openmct,
|
||||||
domainObject
|
domainObject
|
||||||
},
|
},
|
||||||
el: element,
|
|
||||||
template: '<imagery-view-layout ref="ImageryLayout"></imagery-view-layout>'
|
template: '<imagery-view-layout ref="ImageryLayout"></imagery-view-layout>'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<span ng-controller="PlotController as controller"
|
<div ng-controller="PlotController as controller"
|
||||||
class="abs holder holder-plot has-control-bar">
|
class="c-plot holder holder-plot has-control-bar">
|
||||||
<div class="c-control-bar" ng-show="!controller.hideExportButtons">
|
<div class="c-control-bar" ng-show="!controller.hideExportButtons">
|
||||||
<span class="c-button-set c-button-set--strip-h">
|
<span class="c-button-set c-button-set--strip-h">
|
||||||
<button class="c-button icon-download"
|
<button class="c-button icon-download"
|
||||||
@ -50,4 +50,4 @@
|
|||||||
the-x-axis="xAxis">
|
the-x-axis="xAxis">
|
||||||
</mct-plot>
|
</mct-plot>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</div>
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<span ng-controller="StackedPlotController as stackedPlot"
|
<div ng-controller="StackedPlotController as stackedPlot"
|
||||||
class="abs holder holder-plot has-control-bar t-plot-stacked">
|
class="c-plot c-plot--stacked holder holder-plot has-control-bar">
|
||||||
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
|
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
|
||||||
<span class="c-button-set c-button-set--strip-h">
|
<span class="c-button-set c-button-set--strip-h">
|
||||||
<button class="c-button icon-download"
|
<button class="c-button icon-download"
|
||||||
@ -57,4 +57,4 @@
|
|||||||
<mct-overlay-plot domain-object="telemetryObject"></mct-overlay-plot>
|
<mct-overlay-plot domain-object="telemetryObject"></mct-overlay-plot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
* Administration. All rights reserved.
|
* Administration. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -47,6 +47,8 @@ define([
|
|||||||
'./goToOriginalAction/plugin',
|
'./goToOriginalAction/plugin',
|
||||||
'./clearData/plugin',
|
'./clearData/plugin',
|
||||||
'./webPage/plugin',
|
'./webPage/plugin',
|
||||||
|
'./condition/plugin',
|
||||||
|
'./conditionWidget/plugin',
|
||||||
'./themes/espresso',
|
'./themes/espresso',
|
||||||
'./themes/maelstrom',
|
'./themes/maelstrom',
|
||||||
'./themes/snow'
|
'./themes/snow'
|
||||||
@ -77,6 +79,8 @@ define([
|
|||||||
GoToOriginalAction,
|
GoToOriginalAction,
|
||||||
ClearData,
|
ClearData,
|
||||||
WebPagePlugin,
|
WebPagePlugin,
|
||||||
|
ConditionPlugin,
|
||||||
|
ConditionWidgetPlugin,
|
||||||
Espresso,
|
Espresso,
|
||||||
Maelstrom,
|
Maelstrom,
|
||||||
Snow
|
Snow
|
||||||
@ -185,6 +189,8 @@ define([
|
|||||||
plugins.Espresso = Espresso.default;
|
plugins.Espresso = Espresso.default;
|
||||||
plugins.Maelstrom = Maelstrom.default;
|
plugins.Maelstrom = Maelstrom.default;
|
||||||
plugins.Snow = Snow.default;
|
plugins.Snow = Snow.default;
|
||||||
|
plugins.Condition = ConditionPlugin.default;
|
||||||
|
plugins.ConditionWidget = ConditionWidgetPlugin.default;
|
||||||
|
|
||||||
return plugins;
|
return plugins;
|
||||||
});
|
});
|
||||||
|
@ -34,9 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__object-name {
|
&__object-name {
|
||||||
flex: 0 0 auto;
|
font-size: 1em;
|
||||||
@include headerFont();
|
|
||||||
font-size: 1.2em !important;
|
|
||||||
margin: $interiorMargin 0 $interiorMarginLg 0;
|
margin: $interiorMargin 0 $interiorMarginLg 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-properties">
|
<div class="c-inspect-properties">
|
||||||
<template v-if="isEditing">
|
<template v-if="isEditing">
|
||||||
<div class="c-properties__header">
|
<div class="c-inspect-properties__header">
|
||||||
Table Column Size
|
Table Column Size
|
||||||
</div>
|
</div>
|
||||||
<ul class="c-properties__section">
|
<ul class="c-inspect-properties__section">
|
||||||
<li class="c-properties__row">
|
<li class="c-inspect-properties__row">
|
||||||
<div
|
<div
|
||||||
class="c-properties__label"
|
class="c-inspect-properties__label"
|
||||||
title="Auto-size table"
|
title="Auto-size table"
|
||||||
>
|
>
|
||||||
<label for="AutoSizeControl">Auto-size</label>
|
<label for="AutoSizeControl">Auto-size</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
<input
|
<input
|
||||||
id="AutoSizeControl"
|
id="AutoSizeControl"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -22,22 +22,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="c-properties__header">
|
<div class="c-inspect-properties__header">
|
||||||
Table Column Visibility
|
Table Column Visibility
|
||||||
</div>
|
</div>
|
||||||
<ul class="c-properties__section">
|
<ul class="c-inspect-properties__section">
|
||||||
<li
|
<li
|
||||||
v-for="(title, key) in headers"
|
v-for="(title, key) in headers"
|
||||||
:key="key"
|
:key="key"
|
||||||
class="c-properties__row"
|
class="c-inspect-properties__row"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c-properties__label"
|
class="c-inspect-properties__label"
|
||||||
title="Show or hide column"
|
title="Show or hide column"
|
||||||
>
|
>
|
||||||
<label :for="key + 'ColumnControl'">{{ title }}</label>
|
<label :for="key + 'ColumnControl'">{{ title }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
<input
|
<input
|
||||||
:id="key + 'ColumnControl'"
|
:id="key + 'ColumnControl'"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
@ -21,10 +21,6 @@
|
|||||||
vertical-align: middle; // This is crucial to hiding 4px height injected by browser by default
|
vertical-align: middle; // This is crucial to hiding 4px height injected by browser by default
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
|
||||||
color: $colorTelemFresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************* WRAPPERS */
|
/******************************* WRAPPERS */
|
||||||
&__headers-w {
|
&__headers-w {
|
||||||
// Wraps __headers table
|
// Wraps __headers table
|
||||||
|
@ -105,10 +105,12 @@ $colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351de
|
|||||||
$colorStatusBtnBg: #666; // Where is this used?
|
$colorStatusBtnBg: #666; // Where is this used?
|
||||||
$colorStatusPartialBg: #3f5e8b;
|
$colorStatusPartialBg: #3f5e8b;
|
||||||
$colorStatusCompleteBg: #457638;
|
$colorStatusCompleteBg: #457638;
|
||||||
$colorAlert: #ff3c00;
|
$colorAlert: #ff8a0d;
|
||||||
$colorAlertFg: #fff;
|
$colorAlertFg: #fff;
|
||||||
$colorWarningHi: #ff0000;
|
$colorError: #ff3c00;
|
||||||
$colorWarningHiFg: #ffdad0;
|
$colorErrorFg: #fff;
|
||||||
|
$colorWarningHi: #990000;
|
||||||
|
$colorWarningHiFg: #FF9594;
|
||||||
$colorWarningLo: #ff9900;
|
$colorWarningLo: #ff9900;
|
||||||
$colorWarningLoFg: #523400;
|
$colorWarningLoFg: #523400;
|
||||||
$colorDiagnostic: #a4b442;
|
$colorDiagnostic: #a4b442;
|
||||||
@ -126,7 +128,6 @@ $colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
|||||||
// States
|
// States
|
||||||
$colorPausedBg: #ff9900;
|
$colorPausedBg: #ff9900;
|
||||||
$colorPausedFg: #333;
|
$colorPausedFg: #333;
|
||||||
$colorOk: #33cc33;
|
|
||||||
|
|
||||||
// Base variations
|
// Base variations
|
||||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||||
@ -221,6 +222,7 @@ $colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due
|
|||||||
$colorSelectFg: $colorBtnFg;
|
$colorSelectFg: $colorBtnFg;
|
||||||
$colorSelectArw: lighten($colorBtnBg, 20%);
|
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||||
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
||||||
|
$controlDisabledOpacity: 0.2;
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
||||||
@ -326,7 +328,7 @@ $shdwItemText: none;
|
|||||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||||
$colorTabBodyBg: $colorBodyBg;
|
$colorTabBodyBg: $colorBodyBg;
|
||||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
||||||
$colorTabHeaderBg: pullForward($colorBodyBg, 10%);
|
$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
|
||||||
$colorTabHeaderFg: $colorBodyFg;
|
$colorTabHeaderFg: $colorBodyFg;
|
||||||
$colorTabHeaderBorder: $colorBodyBg;
|
$colorTabHeaderBorder: $colorBodyBg;
|
||||||
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
||||||
@ -416,15 +418,21 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
|||||||
|
|
||||||
// Discrete items, like Notebook entries, Widget rules
|
// Discrete items, like Notebook entries, Widget rules
|
||||||
$createBtnTextTransform: uppercase;
|
$createBtnTextTransform: uppercase;
|
||||||
|
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
|
||||||
|
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
|
||||||
|
|
||||||
@mixin discreteItem() {
|
@mixin discreteItem() {
|
||||||
background: rgba($colorBodyFg,0.1);
|
background: $colorDiscreteItemBg;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
|
|
||||||
.c-input-inline:hover {
|
.c-input-inline:hover {
|
||||||
background: $colorBodyBg;
|
background: $colorBodyBg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--current-match {
|
||||||
|
background: $colorDiscreteItemCurrentBg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin discreteItemInnerElem() {
|
@mixin discreteItemInnerElem() {
|
||||||
|
@ -109,10 +109,12 @@ $colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351de
|
|||||||
$colorStatusBtnBg: #666; // Where is this used?
|
$colorStatusBtnBg: #666; // Where is this used?
|
||||||
$colorStatusPartialBg: #3f5e8b;
|
$colorStatusPartialBg: #3f5e8b;
|
||||||
$colorStatusCompleteBg: #457638;
|
$colorStatusCompleteBg: #457638;
|
||||||
$colorAlert: #ff3c00;
|
$colorAlert: #ff8a0d;
|
||||||
$colorAlertFg: #fff;
|
$colorAlertFg: #fff;
|
||||||
$colorWarningHi: #ff0000;
|
$colorError: #ff3c00;
|
||||||
$colorWarningHiFg: #ffdad0;
|
$colorErrorFg: #fff;
|
||||||
|
$colorWarningHi: #990000;
|
||||||
|
$colorWarningHiFg: #FF9594;
|
||||||
$colorWarningLo: #ff9900;
|
$colorWarningLo: #ff9900;
|
||||||
$colorWarningLoFg: #523400;
|
$colorWarningLoFg: #523400;
|
||||||
$colorDiagnostic: #a4b442;
|
$colorDiagnostic: #a4b442;
|
||||||
@ -130,7 +132,6 @@ $colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
|||||||
// States
|
// States
|
||||||
$colorPausedBg: #ff9900;
|
$colorPausedBg: #ff9900;
|
||||||
$colorPausedFg: #333;
|
$colorPausedFg: #333;
|
||||||
$colorOk: #33cc33;
|
|
||||||
|
|
||||||
// Base variations
|
// Base variations
|
||||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||||
@ -225,6 +226,7 @@ $colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due
|
|||||||
$colorSelectFg: $colorBtnFg;
|
$colorSelectFg: $colorBtnFg;
|
||||||
$colorSelectArw: lighten($colorBtnBg, 20%);
|
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||||
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
$shdwSelect: rgba(black, 0.5) 0 0.5px 3px;
|
||||||
|
$controlDisabledOpacity: 0.2;
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
$colorMenuBg: pullForward($colorBodyBg, 15%);
|
||||||
@ -330,7 +332,7 @@ $shdwItemText: none;
|
|||||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||||
$colorTabBodyBg: $colorBodyBg;
|
$colorTabBodyBg: $colorBodyBg;
|
||||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
||||||
$colorTabHeaderBg: pullForward($colorBodyBg, 10%);
|
$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
|
||||||
$colorTabHeaderFg: $colorBodyFg;
|
$colorTabHeaderFg: $colorBodyFg;
|
||||||
$colorTabHeaderBorder: $colorBodyBg;
|
$colorTabHeaderBorder: $colorBodyBg;
|
||||||
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
||||||
@ -420,14 +422,16 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
|||||||
|
|
||||||
// Discrete items, like Notebook entries, Widget rules
|
// Discrete items, like Notebook entries, Widget rules
|
||||||
$createBtnTextTransform: uppercase;
|
$createBtnTextTransform: uppercase;
|
||||||
|
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
|
||||||
|
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
|
||||||
|
|
||||||
@mixin discreteItem() {
|
@mixin discreteItem() {
|
||||||
background: rgba($colorBodyFg,0.1);
|
background: rgba($colorBodyFg,0.1);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
|
|
||||||
.c-input-inline:hover {
|
&--current-match {
|
||||||
background: $colorBodyBg;
|
background: $colorDiscreteItemCurrentBg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +105,10 @@ $colorStatusErrorFilter: invert(8%) sepia(96%) saturate(4511%) hue-rotate(352deg
|
|||||||
$colorStatusBtnBg: #666; // Where is this used?
|
$colorStatusBtnBg: #666; // Where is this used?
|
||||||
$colorStatusPartialBg: #c9d6ff;
|
$colorStatusPartialBg: #c9d6ff;
|
||||||
$colorStatusCompleteBg: #a4e4b4;
|
$colorStatusCompleteBg: #a4e4b4;
|
||||||
$colorAlert: #ff3c00;
|
$colorAlert: #ff8a0d;
|
||||||
$colorAlertFg: #fff;
|
$colorAlertFg: #fff;
|
||||||
|
$colorError: #ff3c00;
|
||||||
|
$colorErrorFg: #fff;
|
||||||
$colorWarningHi: #990000;
|
$colorWarningHi: #990000;
|
||||||
$colorWarningHiFg: #FF9594;
|
$colorWarningHiFg: #FF9594;
|
||||||
$colorWarningLo: #ff9900;
|
$colorWarningLo: #ff9900;
|
||||||
@ -126,7 +128,6 @@ $colorFilter: $colorFilterBg; // Standalone against $colorBodyBg
|
|||||||
// States
|
// States
|
||||||
$colorPausedBg: #ff9900;
|
$colorPausedBg: #ff9900;
|
||||||
$colorPausedFg: #fff;
|
$colorPausedFg: #fff;
|
||||||
$colorOk: #33cc33;
|
|
||||||
|
|
||||||
// Base variations
|
// Base variations
|
||||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||||
@ -221,6 +222,7 @@ $colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due
|
|||||||
$colorSelectFg: $colorBtnFg;
|
$colorSelectFg: $colorBtnFg;
|
||||||
$colorSelectArw: lighten($colorBtnBg, 20%);
|
$colorSelectArw: lighten($colorBtnBg, 20%);
|
||||||
$shdwSelect: none;
|
$shdwSelect: none;
|
||||||
|
$controlDisabledOpacity: 0.3;
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
||||||
@ -326,8 +328,8 @@ $shdwItemText: none;
|
|||||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||||
$colorTabBodyBg: $colorBodyBg;
|
$colorTabBodyBg: $colorBodyBg;
|
||||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
||||||
$colorTabHeaderBg: pullForward($colorBodyBg, 10%);
|
$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
|
||||||
$colorTabHeaderFg: pullForward($colorBodyFg, 20%);
|
$colorTabHeaderFg: $colorBodyFg;
|
||||||
$colorTabHeaderBorder: $colorBodyBg;
|
$colorTabHeaderBorder: $colorBodyBg;
|
||||||
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
|
||||||
$colorTabGroupHeaderFg: pullForward($colorTabGroupHeaderBg, 40%);
|
$colorTabGroupHeaderFg: pullForward($colorTabGroupHeaderBg, 40%);
|
||||||
@ -416,12 +418,18 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
|||||||
|
|
||||||
// Discrete items, like Notebook entries, Widget rules
|
// Discrete items, like Notebook entries, Widget rules
|
||||||
$createBtnTextTransform: uppercase;
|
$createBtnTextTransform: uppercase;
|
||||||
|
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
|
||||||
|
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
|
||||||
|
|
||||||
@mixin discreteItem() {
|
@mixin discreteItem() {
|
||||||
background: rgba($colorBodyFg,0.1);
|
background: $colorDiscreteItemBg;
|
||||||
border: 1px solid $colorInteriorBorder;
|
border: 1px solid $colorInteriorBorder;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
|
|
||||||
|
&--current-match {
|
||||||
|
background: $colorDiscreteItemCurrentBg;
|
||||||
|
}
|
||||||
|
|
||||||
.c-input-inline:hover {
|
.c-input-inline:hover {
|
||||||
background: $colorBodyBg;
|
background: $colorBodyBg;
|
||||||
}
|
}
|
||||||
|
8
src/styles/_constants.scss
Normal file → Executable file
8
src/styles/_constants.scss
Normal file → Executable file
@ -44,6 +44,7 @@ $overlayOuterMarginLg: 5%;
|
|||||||
$overlayOuterMarginFullscreen: 0%;
|
$overlayOuterMarginFullscreen: 0%;
|
||||||
$overlayOuterMarginDialog: 20%;
|
$overlayOuterMarginDialog: 20%;
|
||||||
$overlayInnerMargin: 25px;
|
$overlayInnerMargin: 25px;
|
||||||
|
$mainViewPad: 2px;
|
||||||
/*************** Items */
|
/*************** Items */
|
||||||
$itemPadLR: 5px;
|
$itemPadLR: 5px;
|
||||||
$gridItemDesk: 175px;
|
$gridItemDesk: 175px;
|
||||||
@ -94,9 +95,6 @@ $tableResizeColHitareaD: 6px;
|
|||||||
$mobileMenuIconD: 24px; // Used
|
$mobileMenuIconD: 24px; // Used
|
||||||
$mobileTreeItemH: 35px; // Used
|
$mobileTreeItemH: 35px; // Used
|
||||||
|
|
||||||
/************************** VISUAL */
|
|
||||||
$controlDisabledOpacity: 0.5;
|
|
||||||
|
|
||||||
/************************** UI ELEMENTS */
|
/************************** UI ELEMENTS */
|
||||||
/*************** Progress Bar */
|
/*************** Progress Bar */
|
||||||
$colorProgressBarHolder: rgba(black, 0.1);
|
$colorProgressBarHolder: rgba(black, 0.1);
|
||||||
@ -240,6 +238,7 @@ $glyph-icon-spectra: '\eb24';
|
|||||||
$glyph-icon-spectra-telemetry: '\eb25';
|
$glyph-icon-spectra-telemetry: '\eb25';
|
||||||
$glyph-icon-command: '\eb26';
|
$glyph-icon-command: '\eb26';
|
||||||
$glyph-icon-conditional: '\eb27';
|
$glyph-icon-conditional: '\eb27';
|
||||||
|
$glyph-icon-condition-widget: '\eb28';
|
||||||
|
|
||||||
/************************** GLYPHS AS DATA URI */
|
/************************** GLYPHS AS DATA URI */
|
||||||
// Only objects have been converted, for use in Create menu and folder views
|
// Only objects have been converted, for use in Create menu and folder views
|
||||||
@ -289,4 +288,5 @@ $bg-icon-gauge: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w
|
|||||||
$bg-icon-spectra: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M384 352H128l51.2-89.6L0 288v127c0 53.3 43.7 97 97 97h318c53.4 0 97-43.7 97-97v-31l-162.9-93.1zM415 0H97C43.7 0 0 43.6 0 97v159l200-30.1 56-97.9 54.9 96H512V97a97.2 97.2 0 00-97-97zM512 320v-32l-192-32 192 64z'/%3e%3c/svg%3e");
|
$bg-icon-spectra: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M384 352H128l51.2-89.6L0 288v127c0 53.3 43.7 97 97 97h318c53.4 0 97-43.7 97-97v-31l-162.9-93.1zM415 0H97C43.7 0 0 43.6 0 97v159l200-30.1 56-97.9 54.9 96H512V97a97.2 97.2 0 00-97-97zM512 320v-32l-192-32 192 64z'/%3e%3c/svg%3e");
|
||||||
$bg-icon-spectra-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 128l54.9 96H510C494.3 97.7 386.5 0 256 0 114.6 0 0 114.6 0 256l200-30.1zM384 352H128l51.2-89.6L2 287.7C17.6 414.1 125.4 512 256 512c100.8 0 188-58.3 229.8-143l-136.7-78.1zM320 256l192 64v-32l-192-32z'/%3e%3c/svg%3e");
|
$bg-icon-spectra-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 128l54.9 96H510C494.3 97.7 386.5 0 256 0 114.6 0 0 114.6 0 256l200-30.1zM384 352H128l51.2-89.6L2 287.7C17.6 414.1 125.4 512 256 512c100.8 0 188-58.3 229.8-143l-136.7-78.1zM320 256l192 64v-32l-192-32z'/%3e%3c/svg%3e");
|
||||||
$bg-icon-command: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M185.1 229.7a96.5 96.5 0 0015.8 11.7A68.5 68.5 0 01192 208c0-19.8 8.9-38.8 25.1-53.7 18.5-17 43.7-26.3 70.9-26.3 20.1 0 39.1 5.1 55.1 14.6a81.3 81.3 0 00-16.2-20.3C308.4 105.3 283.2 96 256 96s-52.4 9.3-70.9 26.3C168.9 137.2 160 156.2 160 176s8.9 38.8 25.1 53.7z'/%3e%3cpath d='M442.7 134.8C422.4 57.5 346.5 0 256 0S89.6 57.5 69.3 134.8C26.3 174.8 0 228.7 0 288c0 123.7 114.6 224 256 224s256-100.3 256-224c0-59.3-26.3-113.2-69.3-153.2zM256 64c70.6 0 128 50.2 128 112s-57.4 112-128 112-128-50.2-128-112S185.4 64 256 64zm0 352c-87.7 0-159.2-63.9-160-142.7 34.4 47.4 93.2 78.7 160 78.7s125.6-31.3 160-78.7c-.8 78.8-72.3 142.7-160 142.7z'/%3e%3c/svg%3e");
|
$bg-icon-command: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M185.1 229.7a96.5 96.5 0 0015.8 11.7A68.5 68.5 0 01192 208c0-19.8 8.9-38.8 25.1-53.7 18.5-17 43.7-26.3 70.9-26.3 20.1 0 39.1 5.1 55.1 14.6a81.3 81.3 0 00-16.2-20.3C308.4 105.3 283.2 96 256 96s-52.4 9.3-70.9 26.3C168.9 137.2 160 156.2 160 176s8.9 38.8 25.1 53.7z'/%3e%3cpath d='M442.7 134.8C422.4 57.5 346.5 0 256 0S89.6 57.5 69.3 134.8C26.3 174.8 0 228.7 0 288c0 123.7 114.6 224 256 224s256-100.3 256-224c0-59.3-26.3-113.2-69.3-153.2zM256 64c70.6 0 128 50.2 128 112s-57.4 112-128 112-128-50.2-128-112S185.4 64 256 64zm0 352c-87.7 0-159.2-63.9-160-142.7 34.4 47.4 93.2 78.7 160 78.7s125.6-31.3 160-78.7c-.8 78.8-72.3 142.7-160 142.7z'/%3e%3c/svg%3e");
|
||||||
$bg-icon-conditional: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M256 0C114.62 0 0 114.62 0 256s114.62 256 256 256 256-114.62 256-256S397.38 0 256 0zm0 384L64 256l192-128 192 128z' fill='%23000000'/%3e%3c/svg%3e");
|
$bg-icon-conditional: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M256 0C114.62 0 0 114.62 0 256s114.62 256 256 256 256-114.62 256-256S397.38 0 256 0zm0 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
|
||||||
|
$bg-icon-condition-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23000000' d='M416 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM256 384L64 256l192-128 192 128z'/%3e%3c/svg%3e");
|
||||||
|
@ -65,6 +65,11 @@ button {
|
|||||||
@include desktop() {
|
@include desktop() {
|
||||||
font-size: 6px;
|
font-size: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: $glyph-icon-arrow-down;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
@ -81,6 +86,20 @@ button {
|
|||||||
/********* Icon Buttons and Links */
|
/********* Icon Buttons and Links */
|
||||||
.c-click-icon {
|
.c-click-icon {
|
||||||
@include cClickIcon();
|
@include cClickIcon();
|
||||||
|
|
||||||
|
&--section-collapse {
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
transition: transform $transOutTime;
|
||||||
|
&:before {
|
||||||
|
content: $glyph-icon-arrow-down;
|
||||||
|
font-family: symbolsfont;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-collapsed {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-click-link {
|
.c-click-link {
|
||||||
@ -212,6 +231,49 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************** DRAG AFFORDANCES */
|
||||||
|
.c-grippy {
|
||||||
|
$d: 10px;
|
||||||
|
@include grippy($c: $colorItemTreeVC, $dir: 'y');
|
||||||
|
width: $d; height: $d;
|
||||||
|
|
||||||
|
&--vertical-drag {
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************** SECTION */
|
||||||
|
section {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
+ section {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
|
|
||||||
|
&.is-expanded {
|
||||||
|
margin-bottom: $interiorMargin * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-expanded {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-section__header {
|
||||||
|
@include propertiesHeader();
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: $interiorMargin;
|
||||||
|
|
||||||
|
> * + * { margin-left: $interiorMarginSm; }
|
||||||
|
}
|
||||||
|
|
||||||
|
> [class*='__label'] {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************** FORM ELEMENTS */
|
/******************************************************** FORM ELEMENTS */
|
||||||
input, textarea {
|
input, textarea {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@ -324,7 +386,7 @@ select {
|
|||||||
background-position: right .4em top 80%, 0 0;
|
background-position: right .4em top 80%, 0 0;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
padding: 1px 20px 1px $interiorMargin;
|
padding: 2px 20px 2px $interiorMargin;
|
||||||
|
|
||||||
*,
|
*,
|
||||||
option {
|
option {
|
||||||
@ -645,6 +707,11 @@ select {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-row-separator {
|
||||||
|
border-top: 1px solid $colorInteriorBorder;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.c-toolbar {
|
.c-toolbar {
|
||||||
> * + * {
|
> * + * {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
@ -746,7 +813,25 @@ select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************** SLIDERS */
|
/******************************************************** STYLE EDITING */
|
||||||
|
.c-style {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * + * { margin-left: $interiorMargin; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-style-thumb {
|
||||||
|
background-size: cover;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: $basicCr;
|
||||||
|
box-shadow: rgba($colorBodyFg, 0.4) 0 0 3px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: $interiorMargin $interiorMarginLg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************** SLIDERS */
|
||||||
.c-slider {
|
.c-slider {
|
||||||
@include cControl();
|
@include cControl();
|
||||||
> * + * { margin-left: $interiorMargin; }
|
> * + * { margin-left: $interiorMargin; }
|
||||||
|
@ -35,6 +35,22 @@ div {
|
|||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.u-menu-to {
|
||||||
|
&--left {
|
||||||
|
.c-menu {
|
||||||
|
left: auto !important;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--center {
|
||||||
|
.c-menu {
|
||||||
|
left: 50% !important;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.u-space {
|
.u-space {
|
||||||
// Provides a separator space between elements
|
// Provides a separator space between elements
|
||||||
&--right {
|
&--right {
|
||||||
|
2
src/styles/_glyphs.scss
Normal file → Executable file
2
src/styles/_glyphs.scss
Normal file → Executable file
@ -174,6 +174,7 @@
|
|||||||
.icon-spectra-telemetry { @include glyphBefore($glyph-icon-spectra-telemetry); }
|
.icon-spectra-telemetry { @include glyphBefore($glyph-icon-spectra-telemetry); }
|
||||||
.icon-command { @include glyphBefore($glyph-icon-command); }
|
.icon-command { @include glyphBefore($glyph-icon-command); }
|
||||||
.icon-conditional { @include glyphBefore($glyph-icon-conditional); }
|
.icon-conditional { @include glyphBefore($glyph-icon-conditional); }
|
||||||
|
.icon-condition-widget { @include glyphBefore($glyph-icon-condition-widget); }
|
||||||
|
|
||||||
/************************** 12 PX CLASSES */
|
/************************** 12 PX CLASSES */
|
||||||
// TODO: sync with 16px redo as of 10/25/18
|
// TODO: sync with 16px redo as of 10/25/18
|
||||||
@ -232,3 +233,4 @@
|
|||||||
.bg-icon-spectra-telemetry { @include glyphBg($bg-icon-spectra-telemetry); }
|
.bg-icon-spectra-telemetry { @include glyphBg($bg-icon-spectra-telemetry); }
|
||||||
.bg-icon-command { @include glyphBg($bg-icon-command); }
|
.bg-icon-command { @include glyphBg($bg-icon-command); }
|
||||||
.bg-icon-conditional { @include glyphBg($bg-icon-conditional); }
|
.bg-icon-conditional { @include glyphBg($bg-icon-conditional); }
|
||||||
|
.bg-icon-condition-widget { @include glyphBg($bg-icon-condition-widget); }
|
||||||
|
@ -67,15 +67,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__object-name--w {
|
&__object-name--w {
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
@include headerFont(1.4em);
|
@include headerFont(1.4em);
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
// Icon
|
// Icon
|
||||||
opacity: 0.5;
|
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,32 +25,6 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*********************** STACKED PLOT LAYOUT */
|
/*********************** STACKED PLOT LAYOUT */
|
||||||
.t-plot-stacked {
|
|
||||||
.l-view-section {
|
|
||||||
// Make this a flex container
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
.gl-plot.child-frame {
|
|
||||||
mct-plot {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
flex: 1 1 auto;
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.s-status-timeconductor-unsynced .holder-plot {
|
|
||||||
.t-object-alert.t-alert-unsynced {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-editing {
|
.is-editing {
|
||||||
.gl-plot.child-frame {
|
.gl-plot.child-frame {
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -66,11 +40,42 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.c-plot {
|
||||||
|
$p: $mainViewPad;
|
||||||
|
position: absolute;
|
||||||
|
top: $p; right: $p; bottom: $p; left: $p;
|
||||||
|
|
||||||
|
&--stacked {
|
||||||
|
.l-view-section {
|
||||||
|
// Make this a flex container
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
.gl-plot.child-frame {
|
||||||
|
mct-plot {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
flex: 1 1 auto;
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-status-timeconductor-unsynced .holder-plot {
|
||||||
|
.t-object-alert.t-alert-unsynced {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.gl-plot {
|
.gl-plot {
|
||||||
color: $colorPlotFg;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
//font-size: 0.7rem;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -146,10 +151,10 @@ mct-plot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gl-plot-coords {
|
.gl-plot-coords {
|
||||||
|
// This does not appear to be in use in Open MCT
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
background: black;
|
background: black;
|
||||||
color: lighten($colorBodyFg, 30%);
|
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: nth($plotDisplayArea,1) + $interiorMarginLg;
|
top: nth($plotDisplayArea,1) + $interiorMarginLg;
|
||||||
@ -164,7 +169,6 @@ mct-plot {
|
|||||||
|
|
||||||
.gl-plot-label,
|
.gl-plot-label,
|
||||||
.l-plot-label {
|
.l-plot-label {
|
||||||
color: $colorPlotLabelFg;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -139,6 +139,24 @@
|
|||||||
background-size: $bgSize;
|
background-size: $bgSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin bgCheckerboard($c: $colorBodyFg, $opacity: 0.3, $size: 32px, $imp: false) {
|
||||||
|
$color: rgba($c, $opacity);
|
||||||
|
$bgPos: floor($size / 2);
|
||||||
|
|
||||||
|
$impStr: null;
|
||||||
|
@if $imp {
|
||||||
|
$impStr: !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
background-image:
|
||||||
|
linear-gradient(45deg, $color 25%, transparent 25%),
|
||||||
|
linear-gradient(45deg, transparent 75%, $color 75%),
|
||||||
|
linear-gradient(45deg, transparent 75%, $color 75%),
|
||||||
|
linear-gradient(45deg, $color 25%, transparent 25%) $impStr;
|
||||||
|
background-size: $size $size;
|
||||||
|
background-position:0 0, 0 0, -1*$bgPos -1*$bgPos, $bgPos $bgPos;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin disabled() {
|
@mixin disabled() {
|
||||||
opacity: $controlDisabledOpacity;
|
opacity: $controlDisabledOpacity;
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
@ -415,9 +433,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@mixin cClickIcon() {
|
@mixin cClickIcon() {
|
||||||
@include cControl();
|
@include cControl();
|
||||||
|
color: $colorBodyFg;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 4px; // Bigger hit area
|
padding: 4px; // Bigger hit area
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: symbolsfont;
|
font-family: symbolsfont;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMarginSm;
|
||||||
@if $glyph != null {
|
@if $glyph != null {
|
||||||
content: $glyph $impStr;
|
content: $glyph $impStr;
|
||||||
}
|
}
|
||||||
@ -71,6 +71,30 @@
|
|||||||
&.is-limit--lwr:before { content: $glyph-icon-arrow-down !important; }
|
&.is-limit--lwr:before { content: $glyph-icon-arrow-down !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin uIndicator($bg, $fg, $glyph) {
|
||||||
|
background: $bg;
|
||||||
|
color: $fg;
|
||||||
|
|
||||||
|
&[class*='--with-icon'] {
|
||||||
|
&:before {
|
||||||
|
color: $fg;
|
||||||
|
display: inline-block;
|
||||||
|
font-family: symbolsfont;
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
@if $glyph != null {
|
||||||
|
content: $glyph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[class*='--block'] {
|
||||||
|
border-radius: $controlCr;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/*************************************************** STYLES */
|
/*************************************************** STYLES */
|
||||||
*:not(tr) {
|
*:not(tr) {
|
||||||
&.is-limit--yellow {
|
&.is-limit--yellow {
|
||||||
@ -108,8 +132,9 @@ tr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************** STATUS */
|
/*************************************************** STATUS */
|
||||||
[class*='s-status'] {
|
[class*='s-status-icon'] {
|
||||||
&:before {
|
&:before {
|
||||||
|
font-family: symbolsfont;
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,3 +197,5 @@ tr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.u-alert { @include uIndicator($colorAlert, $colorAlertFg, $glyph-icon-alert-triangle); }
|
||||||
|
.u-error { @include uIndicator($colorError, $colorErrorFg, $glyph-icon-alert-triangle); }
|
||||||
|
@ -78,10 +78,14 @@ div.c-table {
|
|||||||
|
|
||||||
.c-table-wrapper {
|
.c-table-wrapper {
|
||||||
// Wraps .c-control-bar and .c-table
|
// Wraps .c-control-bar and .c-table
|
||||||
@include abs();
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
// Using absolute here because sizing table can't calc width properly if padding is used
|
||||||
|
$p: $mainViewPad;
|
||||||
|
position: absolute;
|
||||||
|
top: $p; right: $p; bottom: $p; left: $p;
|
||||||
|
|
||||||
> .c-table {
|
> .c-table {
|
||||||
height: auto;
|
height: auto;
|
||||||
@ -158,6 +162,12 @@ div.c-table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c-lad-table-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: $mainViewPad;
|
||||||
|
}
|
||||||
|
|
||||||
.c-lad-table {
|
.c-lad-table {
|
||||||
th, td {
|
th, td {
|
||||||
width: 33%; // Needed to prevent size jumping as values dynamically update
|
width: 33%; // Needed to prevent size jumping as values dynamically update
|
||||||
|
0
src/styles/fonts/Open MCT Symbols 12px.json
Normal file → Executable file
0
src/styles/fonts/Open MCT Symbols 12px.json
Normal file → Executable file
671
src/styles/fonts/Open MCT Symbols 16px.json
Normal file → Executable file
671
src/styles/fonts/Open MCT Symbols 16px.json
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
0
src/styles/fonts/Open-MCT-Symbols-12px.ttf
Normal file → Executable file
0
src/styles/fonts/Open-MCT-Symbols-12px.ttf
Normal file → Executable file
0
src/styles/fonts/Open-MCT-Symbols-12px.woff
Normal file → Executable file
0
src/styles/fonts/Open-MCT-Symbols-12px.woff
Normal file → Executable file
@ -142,4 +142,5 @@
|
|||||||
<glyph unicode="" glyph-name="icon-telemetry-spectra" d="M512 576l109.8-192h398.2c-31.4 252.6-247 448-508 448-282.8 0-512-229.2-512-512l400 60.2zM768 128h-512l102.4 179.2-354.4-50.6c31.2-252.8 246.8-448.6 508-448.6 201.6 0 376 116.6 459.6 286l-273.4 156.2zM640 320l384-128v64l-384 64z" />
|
<glyph unicode="" glyph-name="icon-telemetry-spectra" d="M512 576l109.8-192h398.2c-31.4 252.6-247 448-508 448-282.8 0-512-229.2-512-512l400 60.2zM768 128h-512l102.4 179.2-354.4-50.6c31.2-252.8 246.8-448.6 508-448.6 201.6 0 376 116.6 459.6 286l-273.4 156.2zM640 320l384-128v64l-384 64z" />
|
||||||
<glyph unicode="" glyph-name="icon-pushbutton" d="M370.2 372.6c9.326-8.53 19.666-16.261 30.729-22.914l0.871-0.486c-11.077 19.209-17.664 42.221-17.8 66.76v0.040c0 39.6 17.8 77.6 50.2 107.4 37 34 87.4 52.6 141.8 52.6 40.2 0 78.2-10.2 110.2-29.2-8.918 15.653-19.693 29.040-32.268 40.482l-0.132 0.118c-37 34-87.4 52.6-141.8 52.6s-104.8-18.6-141.8-52.6c-32.4-29.8-50.2-67.8-50.2-107.4s17.8-77.6 50.2-107.4zM885.4 562.4c-40.6 154.6-192.4 269.6-373.4 269.6s-332.8-115-373.4-269.6c-86-80-138.6-187.8-138.6-306.4 0-247.4 229.2-448 512-448s512 200.6 512 448c0 118.6-52.6 226.4-138.6 306.4zM512 704c141.2 0 256-100.4 256-224s-114.8-224-256-224-256 100.4-256 224 114.8 224 256 224zM512 0c-175.4 0-318.4 127.8-320 285.4 68.8-94.8 186.4-157.4 320-157.4s251.2 62.6 320 157.4c-1.6-157.6-144.6-285.4-320-285.4z" />
|
<glyph unicode="" glyph-name="icon-pushbutton" d="M370.2 372.6c9.326-8.53 19.666-16.261 30.729-22.914l0.871-0.486c-11.077 19.209-17.664 42.221-17.8 66.76v0.040c0 39.6 17.8 77.6 50.2 107.4 37 34 87.4 52.6 141.8 52.6 40.2 0 78.2-10.2 110.2-29.2-8.918 15.653-19.693 29.040-32.268 40.482l-0.132 0.118c-37 34-87.4 52.6-141.8 52.6s-104.8-18.6-141.8-52.6c-32.4-29.8-50.2-67.8-50.2-107.4s17.8-77.6 50.2-107.4zM885.4 562.4c-40.6 154.6-192.4 269.6-373.4 269.6s-332.8-115-373.4-269.6c-86-80-138.6-187.8-138.6-306.4 0-247.4 229.2-448 512-448s512 200.6 512 448c0 118.6-52.6 226.4-138.6 306.4zM512 704c141.2 0 256-100.4 256-224s-114.8-224-256-224-256 100.4-256 224 114.8 224 256 224zM512 0c-175.4 0-318.4 127.8-320 285.4 68.8-94.8 186.4-157.4 320-157.4s251.2 62.6 320 157.4c-1.6-157.6-144.6-285.4-320-285.4z" />
|
||||||
<glyph unicode="" glyph-name="icon-conditional" d="M512 832c-282.76 0-512-229.24-512-512s229.24-512 512-512 512 229.24 512 512-229.24 512-512 512zM512 64l-384 256 384 256 384-256z" />
|
<glyph unicode="" glyph-name="icon-conditional" d="M512 832c-282.76 0-512-229.24-512-512s229.24-512 512-512 512 229.24 512 512-229.24 512-512 512zM512 64l-384 256 384 256 384-256z" />
|
||||||
|
<glyph unicode="" glyph-name="icon-condition-widget" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM512 64l-384 256 384 256 384-256z" />
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Binary file not shown.
Binary file not shown.
@ -1,5 +1,9 @@
|
|||||||
@import "../api/overlays/components/dialog-component.scss";
|
@import "../api/overlays/components/dialog-component.scss";
|
||||||
@import "../api/overlays/components/overlay-component.scss";
|
@import "../api/overlays/components/overlay-component.scss";
|
||||||
|
@import "../plugins/condition/components/condition.scss";
|
||||||
|
@import "../plugins/condition/components/condition-set.scss";
|
||||||
|
@import "../plugins/conditionWidget/components/condition-widget.scss";
|
||||||
|
@import "../plugins/condition/components/inspector/conditional-styles.scss";
|
||||||
@import "../plugins/displayLayout/components/box-view.scss";
|
@import "../plugins/displayLayout/components/box-view.scss";
|
||||||
@import "../plugins/displayLayout/components/display-layout.scss";
|
@import "../plugins/displayLayout/components/display-layout.scss";
|
||||||
@import "../plugins/displayLayout/components/edit-marquee.scss";
|
@import "../plugins/displayLayout/components/edit-marquee.scss";
|
||||||
|
@ -64,7 +64,8 @@ const SIMPLE_CONTENT_TYPES = [
|
|||||||
'clock',
|
'clock',
|
||||||
'timer',
|
'timer',
|
||||||
'summary-widget',
|
'summary-widget',
|
||||||
'hyperlink'
|
'hyperlink',
|
||||||
|
'conditionWidget'
|
||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
|
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
||||||
|
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ["openmct"],
|
inject: ["openmct"],
|
||||||
@ -31,6 +33,19 @@ export default {
|
|||||||
if (this.releaseEditModeHandler) {
|
if (this.releaseEditModeHandler) {
|
||||||
this.releaseEditModeHandler();
|
this.releaseEditModeHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.unlisten) {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stopListeningStyles) {
|
||||||
|
this.stopListeningStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.styleRuleManager) {
|
||||||
|
this.styleRuleManager.destroy();
|
||||||
|
delete this.styleRuleManager;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||||
@ -43,6 +58,11 @@ export default {
|
|||||||
capture: true
|
capture: true
|
||||||
});
|
});
|
||||||
this.$el.addEventListener('drop', this.addObjectToParent);
|
this.$el.addEventListener('drop', this.addObjectToParent);
|
||||||
|
if (this.currentObject) {
|
||||||
|
//This is to apply styles to subobjects in a layout
|
||||||
|
this.initObjectStyles();
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clear() {
|
clear() {
|
||||||
@ -76,6 +96,29 @@ export default {
|
|||||||
this.clear();
|
this.clear();
|
||||||
this.updateView(true);
|
this.updateView(true);
|
||||||
},
|
},
|
||||||
|
updateStyle(styleObj) {
|
||||||
|
if (!styleObj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let keys = Object.keys(styleObj);
|
||||||
|
keys.forEach(key => {
|
||||||
|
let firstChild = this.$el.querySelector(':first-child');
|
||||||
|
if (firstChild) {
|
||||||
|
if ((typeof styleObj[key] === 'string') && (styleObj[key].indexOf('transparent') > -1)) {
|
||||||
|
if (firstChild.style[key]) {
|
||||||
|
firstChild.style[key] = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!styleObj.isStyleInvisible && firstChild.classList.contains(STYLE_CONSTANTS.isStyleInvisible)) {
|
||||||
|
firstChild.classList.remove(STYLE_CONSTANTS.isStyleInvisible);
|
||||||
|
} else if (styleObj.isStyleInvisible && !firstChild.classList.contains(styleObj.isStyleInvisible)) {
|
||||||
|
firstChild.classList.add(styleObj.isStyleInvisible);
|
||||||
|
}
|
||||||
|
firstChild.style[key] = styleObj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
updateView(immediatelySelect) {
|
updateView(immediatelySelect) {
|
||||||
this.clear();
|
this.clear();
|
||||||
if (!this.currentObject) {
|
if (!this.currentObject) {
|
||||||
@ -89,7 +132,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.viewContainer = document.createElement('div');
|
this.viewContainer = document.createElement('div');
|
||||||
this.viewContainer.classList.add('c-object-view','u-contents');
|
this.viewContainer.classList.add('u-angular-object-view-wrapper');
|
||||||
this.$el.append(this.viewContainer);
|
this.$el.append(this.viewContainer);
|
||||||
let provider = this.getViewProvider();
|
let provider = this.getViewProvider();
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
@ -125,6 +168,8 @@ export default {
|
|||||||
this.openmct.objectViews.on('clearData', this.clearData);
|
this.openmct.objectViews.on('clearData', this.clearData);
|
||||||
},
|
},
|
||||||
show(object, viewKey, immediatelySelect, currentObjectPath) {
|
show(object, viewKey, immediatelySelect, currentObjectPath) {
|
||||||
|
this.updateStyle();
|
||||||
|
|
||||||
if (this.unlisten) {
|
if (this.unlisten) {
|
||||||
this.unlisten();
|
this.unlisten();
|
||||||
}
|
}
|
||||||
@ -149,7 +194,26 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.viewKey = viewKey;
|
this.viewKey = viewKey;
|
||||||
|
|
||||||
this.updateView(immediatelySelect);
|
this.updateView(immediatelySelect);
|
||||||
|
|
||||||
|
this.initObjectStyles();
|
||||||
|
},
|
||||||
|
initObjectStyles() {
|
||||||
|
if (!this.styleRuleManager) {
|
||||||
|
this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this));
|
||||||
|
} else {
|
||||||
|
this.styleRuleManager.updateObjectStyleConfig(this.currentObject.configuration && this.currentObject.configuration.objectStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stopListeningStyles) {
|
||||||
|
this.stopListeningStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopListeningStyles = this.openmct.objects.observe(this.currentObject, 'configuration.objectStyles', (newObjectStyle) => {
|
||||||
|
//Updating styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||||
|
this.styleRuleManager.updateObjectStyleConfig(newObjectStyle);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
loadComposition() {
|
loadComposition() {
|
||||||
return this.composition.load();
|
return this.composition.load();
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
height: 0; // Chrome 73 overflow bug fix
|
height: 0; // Chrome 73 overflow bug fix
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.c-object-view {
|
.u-angular-object-view-wrapper {
|
||||||
.u-fills-container {
|
.u-fills-container {
|
||||||
// Expand component types that fill a container
|
// Expand component types that fill a container
|
||||||
@include abs();
|
@include abs();
|
||||||
@ -69,7 +69,14 @@
|
|||||||
.c-click-icon,
|
.c-click-icon,
|
||||||
.c-button {
|
.c-button {
|
||||||
// Shrink buttons a bit when they appear in a frame
|
// Shrink buttons a bit when they appear in a frame
|
||||||
font-size: 0.85em;
|
font-size: 0.9em;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.u-angular-object-view-wrapper {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
$d: 12px;
|
$d: 12px;
|
||||||
$m: 2px;
|
$m: 2px;
|
||||||
$br: $d/1.5;
|
$br: $d/1.5;
|
||||||
|
cursor: pointer;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -39,7 +40,6 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: $d + ($m*2);
|
height: $d + ($m*2);
|
||||||
position: relative;
|
position: relative;
|
||||||
transform: translateY(2px); // TODO: get this to work without this kind of hack!
|
|
||||||
width: $d*2 + $m*2;
|
width: $d*2 + $m*2;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
'c-disclosure-triangle--expanded' : value,
|
'c-disclosure-triangle--expanded' : value,
|
||||||
'is-enabled' : enabled
|
'is-enabled' : enabled
|
||||||
}"
|
}"
|
||||||
@click="$emit('input', !value)"
|
@click="handleClick"
|
||||||
></span>
|
></span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -21,6 +21,18 @@ export default {
|
|||||||
// Used as such in the tree - when a node doesn't have children, set disabled to true.
|
// Used as such in the tree - when a node doesn't have children, set disabled to true.
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
propagate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClick(event) {
|
||||||
|
this.$emit('input', !this.value);
|
||||||
|
if (!this.propagate) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="elements.length > 1 && isEditing"
|
v-if="elements.length > 1 && isEditing"
|
||||||
class="c-elements-pool__grippy"
|
class="c-elements-pool__grippy c-grippy c-grippy--vertical-drag"
|
||||||
></span>
|
></span>
|
||||||
<object-label
|
<object-label
|
||||||
:domain-object="element"
|
:domain-object="element"
|
||||||
|
@ -1,22 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<multipane
|
<div class="c-inspector">
|
||||||
class="c-inspector"
|
<object-name />
|
||||||
type="vertical"
|
<div v-if="showStyles"
|
||||||
>
|
class="c-inspector__tabs c-tabs"
|
||||||
<pane class="c-inspector__properties">
|
|
||||||
<properties />
|
|
||||||
<location />
|
|
||||||
<inspector-views />
|
|
||||||
</pane>
|
|
||||||
<pane
|
|
||||||
v-if="isEditing && hasComposition"
|
|
||||||
class="c-inspector__elements"
|
|
||||||
handle="before"
|
|
||||||
label="Elements"
|
|
||||||
>
|
>
|
||||||
<elements />
|
<div v-for="tabbedView in tabbedViews"
|
||||||
</pane>
|
:key="tabbedView.key"
|
||||||
</multipane>
|
class="c-inspector__tab c-tab"
|
||||||
|
:class="{'is-current': isCurrent(tabbedView)}"
|
||||||
|
@click="updateCurrentTab(tabbedView)"
|
||||||
|
>
|
||||||
|
{{ tabbedView.name }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="c-inspector__content">
|
||||||
|
<multipane v-if="currentTabbedView.key === '__properties'"
|
||||||
|
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 v-else>
|
||||||
|
<styles-inspector-view />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -25,15 +45,21 @@ import pane from '../layout/pane.vue';
|
|||||||
import Elements from './Elements.vue';
|
import Elements from './Elements.vue';
|
||||||
import Location from './Location.vue';
|
import Location from './Location.vue';
|
||||||
import Properties from './Properties.vue';
|
import Properties from './Properties.vue';
|
||||||
|
import ObjectName from './ObjectName.vue';
|
||||||
import InspectorViews from './InspectorViews.vue';
|
import InspectorViews from './InspectorViews.vue';
|
||||||
|
import _ from "lodash";
|
||||||
|
import StylesInspectorView from "./StylesInspectorView.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
components: {
|
components: {
|
||||||
|
StylesInspectorView,
|
||||||
|
// StylesInspectorView,
|
||||||
multipane,
|
multipane,
|
||||||
pane,
|
pane,
|
||||||
Elements,
|
Elements,
|
||||||
Properties,
|
Properties,
|
||||||
|
ObjectName,
|
||||||
Location,
|
Location,
|
||||||
InspectorViews
|
InspectorViews
|
||||||
},
|
},
|
||||||
@ -42,23 +68,59 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hasComposition: false
|
hasComposition: false,
|
||||||
|
showStyles: false,
|
||||||
|
tabbedViews: [{
|
||||||
|
key: '__properties',
|
||||||
|
name: 'Properties'
|
||||||
|
},{
|
||||||
|
key: '__styles',
|
||||||
|
name: 'Styles'
|
||||||
|
}],
|
||||||
|
currentTabbedView: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.openmct.selection.on('change', this.refreshComposition);
|
this.excludeObjectTypes = ['folder', 'webPage', 'conditionSet'];
|
||||||
this.refreshComposition(this.openmct.selection.get());
|
this.openmct.selection.on('change', this.updateInspectorViews);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.openmct.selection.off('change', this.refreshComposition);
|
this.openmct.selection.off('change', this.updateInspectorViews);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
updateInspectorViews(selection) {
|
||||||
|
this.refreshComposition(selection);
|
||||||
|
if (this.openmct.types.get('conditionSet')) {
|
||||||
|
this.refreshTabs(selection);
|
||||||
|
}
|
||||||
|
},
|
||||||
refreshComposition(selection) {
|
refreshComposition(selection) {
|
||||||
if (selection.length > 0 && selection[0].length > 0) {
|
if (selection.length > 0 && selection[0].length > 0) {
|
||||||
let parentObject = selection[0][0].context.item;
|
let parentObject = selection[0][0].context.item;
|
||||||
|
|
||||||
this.hasComposition = !!(parentObject && this.openmct.composition.get(parentObject));
|
this.hasComposition = !!(parentObject && this.openmct.composition.get(parentObject));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
refreshTabs(selection) {
|
||||||
|
if (selection.length > 0 && selection[0].length > 0) {
|
||||||
|
//layout items are not domain objects but should allow conditional styles
|
||||||
|
this.showStyles = selection[0][0].context.layoutItem;
|
||||||
|
let object = selection[0][0].context.item;
|
||||||
|
if (object) {
|
||||||
|
let type = this.openmct.types.get(object.type);
|
||||||
|
this.showStyles = (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
|
||||||
|
}
|
||||||
|
if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key))
|
||||||
|
{
|
||||||
|
this.updateCurrentTab(this.tabbedViews[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateCurrentTab(view) {
|
||||||
|
this.currentTabbedView = view;
|
||||||
|
},
|
||||||
|
isCurrent(view) {
|
||||||
|
return _.isEqual(this.currentTabbedView, view)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,11 +30,10 @@ export default {
|
|||||||
});
|
});
|
||||||
this.$el.innerHTML = '';
|
this.$el.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedViews = this.openmct.inspectorViews.get(selection);
|
this.selectedViews = this.openmct.inspectorViews.get(selection);
|
||||||
this.selectedViews.forEach(selectedView => {
|
this.selectedViews.forEach(selectedView => {
|
||||||
let viewContainer = document.createElement('div');
|
let viewContainer = document.createElement('div');
|
||||||
this.$el.append(viewContainer)
|
this.$el.append(viewContainer);
|
||||||
selectedView.show(viewContainer);
|
selectedView.show(viewContainer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-properties c-properties--location">
|
<div class="c-inspect-properties c-inspect-properties--location">
|
||||||
<div
|
<div
|
||||||
class="c-properties__header"
|
class="c-inspect-properties__header"
|
||||||
title="The location of this linked object."
|
title="The location of this linked object."
|
||||||
>
|
>
|
||||||
Original Location
|
Original Location
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
v-if="!multiSelect"
|
v-if="!multiSelect"
|
||||||
class="c-properties__section"
|
class="c-inspect-properties__section"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-if="originalPath.length"
|
v-if="originalPath.length"
|
||||||
class="c-properties__row"
|
class="c-inspect-properties__row"
|
||||||
>
|
>
|
||||||
<ul class="c-properties__value c-location">
|
<ul class="c-inspect-properties__value c-location">
|
||||||
<li
|
<li
|
||||||
v-for="pathObject in orderedOriginalPath"
|
v-for="pathObject in orderedOriginalPath"
|
||||||
:key="pathObject.key"
|
:key="pathObject.key"
|
||||||
@ -30,7 +30,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div
|
<div
|
||||||
v-if="multiSelect"
|
v-if="multiSelect"
|
||||||
class="c-properties__row--span-all"
|
class="c-inspect-properties__row--span-all"
|
||||||
>
|
>
|
||||||
No location to display for multiple items
|
No location to display for multiple items
|
||||||
</div>
|
</div>
|
||||||
|
76
src/ui/inspector/ObjectName.vue
Normal file
76
src/ui/inspector/ObjectName.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<div class="c-inspector__header">
|
||||||
|
<div v-if="!multiSelect && !singleSelectNonObject"
|
||||||
|
class="c-inspector__selected-w c-object-label"
|
||||||
|
:class="typeCssClass"
|
||||||
|
>
|
||||||
|
<span class="c-inspector__selected c-object-label__name">{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="singleSelectNonObject"
|
||||||
|
class="c-inspector__selected-w c-object-label"
|
||||||
|
:class="typeCssClass"
|
||||||
|
>
|
||||||
|
<span class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object">Layout Object</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="multiSelect"
|
||||||
|
class="c-inspector__multiple-selected-w"
|
||||||
|
>
|
||||||
|
{{ itemsSelected }} items selected
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
domainObject: {},
|
||||||
|
multiSelect: false,
|
||||||
|
itemsSelected: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
item() {
|
||||||
|
return this.domainObject || {};
|
||||||
|
},
|
||||||
|
type() {
|
||||||
|
return this.openmct.types.get(this.item.type);
|
||||||
|
},
|
||||||
|
typeCssClass() {
|
||||||
|
if (this.type.definition.cssClass === undefined) {
|
||||||
|
return 'icon-object';
|
||||||
|
}
|
||||||
|
return this.type.definition.cssClass;
|
||||||
|
},
|
||||||
|
singleSelectNonObject() {
|
||||||
|
return !this.item.identifier && !this.multiSelect;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.openmct.selection.on('change', this.updateSelection);
|
||||||
|
this.updateSelection(this.openmct.selection.get());
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.openmct.selection.off('change', this.updateSelection);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSelection(selection) {
|
||||||
|
if (selection.length === 0 || selection[0].length === 0) {
|
||||||
|
this.domainObject = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selection.length > 1) {
|
||||||
|
this.multiSelect = true;
|
||||||
|
this.domainObject = {};
|
||||||
|
this.itemsSelected = selection.length;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.multiSelect = false;
|
||||||
|
this.domainObject = selection[0][0].context.item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,72 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-properties c-properties--properties">
|
<div class="c-inspector__properties c-inspect-properties">
|
||||||
<div class="c-properties__header">
|
<div class="c-inspect-properties__header">
|
||||||
Properties
|
Details
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
v-if="!multiSelect && !singleSelectNonObject"
|
v-if="!multiSelect && !singleSelectNonObject"
|
||||||
class="c-properties__section"
|
class="c-inspect-properties__section"
|
||||||
>
|
>
|
||||||
<li class="c-properties__row">
|
<li class="c-inspect-properties__row">
|
||||||
<div class="c-properties__label">
|
<div class="c-inspect-properties__label">
|
||||||
Title
|
Title
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="c-properties__row">
|
<li class="c-inspect-properties__row">
|
||||||
<div class="c-properties__label">
|
<div class="c-inspect-properties__label">
|
||||||
Type
|
Type
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
{{ typeName }}
|
{{ typeName }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="item.created"
|
v-if="item.created"
|
||||||
class="c-properties__row"
|
class="c-inspect-properties__row"
|
||||||
>
|
>
|
||||||
<div class="c-properties__label">
|
<div class="c-inspect-properties__label">
|
||||||
Created
|
Created
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
{{ formatTime(item.created) }}
|
{{ formatTime(item.created) }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="item.modified"
|
v-if="item.modified"
|
||||||
class="c-properties__row"
|
class="c-inspect-properties__row"
|
||||||
>
|
>
|
||||||
<div class="c-properties__label">
|
<div class="c-inspect-properties__label">
|
||||||
Modified
|
Modified
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
{{ formatTime(item.modified) }}
|
{{ formatTime(item.modified) }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-for="prop in typeProperties"
|
v-for="prop in typeProperties"
|
||||||
:key="prop.name"
|
:key="prop.name"
|
||||||
class="c-properties__row"
|
class="c-inspect-properties__row"
|
||||||
>
|
>
|
||||||
<div class="c-properties__label">
|
<div class="c-inspect-properties__label">
|
||||||
{{ prop.name }}
|
{{ prop.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="c-properties__value">
|
<div class="c-inspect-properties__value">
|
||||||
{{ prop.value }}
|
{{ prop.value }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div
|
<div
|
||||||
v-if="multiSelect"
|
v-if="multiSelect"
|
||||||
class="c-properties__row--span-all"
|
class="c-inspect-properties__row--span-all"
|
||||||
>
|
>
|
||||||
No properties to display for multiple items
|
No properties to display for multiple items
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="singleSelectNonObject"
|
v-if="singleSelectNonObject"
|
||||||
class="c-properties__row--span-all"
|
class="c-inspect-properties__row--span-all"
|
||||||
>
|
>
|
||||||
No properties to display for this item
|
No properties to display for this item
|
||||||
</div>
|
</div>
|
||||||
|
105
src/ui/inspector/StylesInspectorView.vue
Normal file
105
src/ui/inspector/StylesInspectorView.vue
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="u-contents"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConditionalStylesView from '../../plugins/condition/components/inspector/ConditionalStylesView.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { getStyleProp } from "../../plugins/condition/utils/styleUtils";
|
||||||
|
|
||||||
|
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: {
|
||||||
|
getStyleProperties(item) {
|
||||||
|
let styleProps = {};
|
||||||
|
Object.keys(item).forEach((key) => {
|
||||||
|
Object.assign(styleProps, getStyleProp(key, item[key]));
|
||||||
|
});
|
||||||
|
return styleProps;
|
||||||
|
},
|
||||||
|
updateSelection(selection) {
|
||||||
|
if (selection.length > 0 && selection[0].length > 0) {
|
||||||
|
let isChildItem = false;
|
||||||
|
let domainObject = selection[0][0].context.item;
|
||||||
|
let layoutItem = {};
|
||||||
|
let styleProps = this.getStyleProperties({
|
||||||
|
fill: 'transparent',
|
||||||
|
stroke: 'transparent',
|
||||||
|
color: 'transparent'
|
||||||
|
});
|
||||||
|
if (selection[0].length > 1) {
|
||||||
|
isChildItem = true;
|
||||||
|
//If there are more than 1 items in the selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
|
||||||
|
//The second item in the selection[0] list is the container object (usually a layout)
|
||||||
|
if (!domainObject) {
|
||||||
|
styleProps = {};
|
||||||
|
layoutItem = selection[0][0].context.layoutItem;
|
||||||
|
styleProps = this.getStyleProperties(layoutItem);
|
||||||
|
domainObject = selection[0][1].context.item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.component) {
|
||||||
|
this.component.$destroy();
|
||||||
|
this.component = undefined;
|
||||||
|
this.$el.innerHTML = '';
|
||||||
|
}
|
||||||
|
let viewContainer = document.createElement('div');
|
||||||
|
this.$el.append(viewContainer);
|
||||||
|
this.component = new Vue({
|
||||||
|
provide: {
|
||||||
|
openmct: this.openmct,
|
||||||
|
domainObject: domainObject
|
||||||
|
},
|
||||||
|
el: viewContainer,
|
||||||
|
components: {
|
||||||
|
ConditionalStylesView
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
layoutItem,
|
||||||
|
styleProps,
|
||||||
|
isChildItem
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: '<conditional-styles-view :can-hide="isChildItem" :item-id="layoutItem.id" :initial-styles="styleProps"></conditional-styles-view>'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user