mirror of
https://github.com/nasa/openmct.git
synced 2025-02-01 00:45:41 +00:00
Merge branch 'topic-conditionals' into dave/condition-telemetry-request
This commit is contained in:
commit
084df5329a
2
API.md
2
API.md
@ -231,7 +231,7 @@ attributes
|
||||
of this object. This is used for specifying an icon to appear next to each
|
||||
object of this type.
|
||||
|
||||
The [Open MCT Tutorials](https://github.com/openmct/openmct-tutorial) provide a
|
||||
The [Open MCT Tutorials](https://github.com/nasa/openmct-tutorial) provide a
|
||||
step-by-step examples of writing code for Open MCT that includes a [section on
|
||||
defining a new object type](https://github.com/nasa/openmct-tutorial#step-3---providing-objects).
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import uuid from 'uuid';
|
||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
|
||||
import { TRIGGER } from "./utils/constants";
|
||||
|
@ -22,13 +22,13 @@
|
||||
|
||||
import Condition from "./Condition";
|
||||
import uuid from "uuid";
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class ConditionManager extends EventEmitter {
|
||||
constructor(domainObject, openmct) {
|
||||
super();
|
||||
this.domainObject = domainObject;
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
this.timeAPI = this.openmct.time;
|
||||
this.latestTimestamp = {};
|
||||
this.instantiate = this.openmct.$injector.get('instantiate');
|
||||
|
@ -0,0 +1,102 @@
|
||||
/*****************************************************************************
|
||||
* 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(conditionalStyleConfiguration, openmct) {
|
||||
super();
|
||||
this.openmct = openmct;
|
||||
if (conditionalStyleConfiguration && conditionalStyleConfiguration.conditionSetIdentifier) {
|
||||
this.initialize(conditionalStyleConfiguration);
|
||||
this.subscribeToConditionSet();
|
||||
}
|
||||
}
|
||||
|
||||
initialize(conditionalStyleConfiguration) {
|
||||
this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier;
|
||||
this.defaultStyle = conditionalStyleConfiguration.defaultStyle;
|
||||
this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []);
|
||||
}
|
||||
|
||||
subscribeToConditionSet() {
|
||||
if (this.stopProvidingTelemetry) {
|
||||
this.stopProvidingTelemetry();
|
||||
}
|
||||
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||
this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
|
||||
});
|
||||
}
|
||||
|
||||
updateConditionalStyleConfig(conditionalStyleConfiguration) {
|
||||
if (!conditionalStyleConfiguration || !conditionalStyleConfiguration.conditionSetIdentifier) {
|
||||
this.destroy();
|
||||
} else {
|
||||
let isNewConditionSet = !this.conditionSetIdentifier ||
|
||||
this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, conditionalStyleConfiguration.conditionSetIdentifier);
|
||||
this.initialize(conditionalStyleConfiguration);
|
||||
//Only resubscribe if the conditionSet has changed.
|
||||
if (isNewConditionSet) {
|
||||
this.subscribeToConditionSet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateConditionStylesMap(conditionStyles) {
|
||||
let conditionStyleMap = {};
|
||||
conditionStyles.forEach((conditionStyle) => {
|
||||
const identifier = this.openmct.objects.makeKeyString(conditionStyle.conditionIdentifier);
|
||||
conditionStyleMap[identifier] = conditionStyle.style;
|
||||
});
|
||||
this.conditionalStyleMap = conditionStyleMap;
|
||||
}
|
||||
|
||||
handleConditionSetResultUpdated(resultData) {
|
||||
let identifier = this.openmct.objects.makeKeyString(resultData.conditionId);
|
||||
let foundStyle = this.conditionalStyleMap[identifier];
|
||||
if (foundStyle) {
|
||||
if (foundStyle !== this.currentStyle) {
|
||||
this.currentStyle = foundStyle;
|
||||
}
|
||||
} else {
|
||||
if (this.currentStyle !== this.defaultStyle) {
|
||||
this.currentStyle = this.defaultStyle;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateDomainObjectStyle();
|
||||
}
|
||||
|
||||
updateDomainObjectStyle() {
|
||||
this.emit('conditionalStyleUpdated', this.currentStyle)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.currentStyle = this.defaultStyle;
|
||||
this.updateDomainObjectStyle();
|
||||
if (this.stopProvidingTelemetry) {
|
||||
this.stopProvidingTelemetry();
|
||||
}
|
||||
this.conditionSetIdentifier = undefined;
|
||||
}
|
||||
|
||||
}
|
@ -1,178 +1,168 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
* 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">
|
||||
<div v-if="domainObject"
|
||||
class="c-c-editui__conditions c-c-container__container c-c__drag-wrapper"
|
||||
:class="['widget-condition', { 'widget-condition--current': currentConditionIdentifier && (currentConditionIdentifier.key === conditionIdentifier.key) }]"
|
||||
>
|
||||
<div class="title-bar">
|
||||
<span class="c-c__menu-hamburger"
|
||||
:class="{ 'is-enabled': !domainObject.isDefault }"
|
||||
:draggable="!domainObject.isDefault"
|
||||
@dragstart="dragStart"
|
||||
@dragstop="dragStop"
|
||||
@dragover.stop
|
||||
></span>
|
||||
<span
|
||||
class="is-enabled flex-elem"
|
||||
:class="['c-c__disclosure-triangle', { 'c-c__disclosure-triangle--expanded': expanded }]"
|
||||
@click="expanded = !expanded"
|
||||
></span>
|
||||
<div class="condition-summary">
|
||||
<span class="condition-name">{{ domainObject.configuration.name }}</span>
|
||||
<!-- TODO: description should be derived from criteria -->
|
||||
<span class="condition-description">{{ domainObject.configuration.name }}</span>
|
||||
</div>
|
||||
<span v-if="!domainObject.isDefault"
|
||||
class="is-enabled c-c__duplicate"
|
||||
@click="cloneCondition"
|
||||
></span>
|
||||
<span v-if="!domainObject.isDefault"
|
||||
class="is-enabled c-c__trash"
|
||||
@click="removeCondition"
|
||||
></span>
|
||||
<div v-if="isEditing"
|
||||
class="c-condition c-condition--edit js-condition-drag-wrapper"
|
||||
:class="{ 'c-condition--current-match': currentConditionIdentifier && (currentConditionIdentifier.key === conditionIdentifier.key) }"
|
||||
>
|
||||
<!-- 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': !domainObject.isDefault }, { 'hide-nice': domainObject.isDefault }]"
|
||||
:draggable="!domainObject.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">{{ domainObject.configuration.name }}</span>
|
||||
<!-- TODO: description should be derived from criteria -->
|
||||
<span class="c-condition__summary">
|
||||
Description/summary goes here {{ domainObject.configuration.description }}
|
||||
</span>
|
||||
|
||||
<div class="c-condition__buttons">
|
||||
<button v-if="!domainObject.isDefault"
|
||||
class="c-click-icon c-condition__duplicate-button icon-duplicate"
|
||||
title="Duplicate this condition"
|
||||
@click="cloneCondition"
|
||||
></button>
|
||||
|
||||
<button v-if="!domainObject.isDefault"
|
||||
class="c-click-icon c-condition__delete-button icon-trash"
|
||||
title="Delete this condition"
|
||||
@click="removeCondition"
|
||||
></button>
|
||||
</div>
|
||||
<div v-if="expanded"
|
||||
class="condition-config-edit widget-condition-content c-sw-editui__conditions-wrapper holder widget-conditions-wrapper flex-elem expanded"
|
||||
>
|
||||
<div id="conditionArea"
|
||||
class="c-c-editui__condition widget-conditions"
|
||||
</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="domainObject.configuration.name"
|
||||
class="t-condition-input__name"
|
||||
type="text"
|
||||
@blur="persist"
|
||||
>
|
||||
<div class="c-c-condition">
|
||||
<div class="c-c-condition__ui l-compact-form l-widget-condition has-local-controls">
|
||||
<div>
|
||||
<ul class="t-widget-condition-config">
|
||||
<li>
|
||||
<label>Condition Name</label>
|
||||
<span class="controls">
|
||||
<input v-model="domainObject.configuration.name"
|
||||
class="t-condition-input__name"
|
||||
type="text"
|
||||
@blur="persist"
|
||||
>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>Output</label>
|
||||
<span class="controls">
|
||||
<select v-model="selectedOutputSelection"
|
||||
@change="setOutputValue"
|
||||
>
|
||||
<option value="">- Select Output -</option>
|
||||
<option v-for="option in outputOptions"
|
||||
:key="option"
|
||||
:value="option"
|
||||
>
|
||||
{{ initCap(option) }}
|
||||
</option>
|
||||
</select>
|
||||
<input v-if="selectedOutputSelection === outputOptions[2]"
|
||||
v-model="domainObject.configuration.output"
|
||||
class="t-condition-name-input"
|
||||
type="text"
|
||||
@blur="persist"
|
||||
>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="!domainObject.isDefault"
|
||||
class="widget-condition-content expanded"
|
||||
>
|
||||
<ul class="t-widget-condition-config">
|
||||
<li class="has-local-controls t-condition">
|
||||
<label>Match when</label>
|
||||
<span class="controls">
|
||||
<select v-model="domainObject.configuration.trigger"
|
||||
@change="persist"
|
||||
>
|
||||
<option value="all">all criteria are met</option>
|
||||
<option value="any">any criteria are met</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="telemetry.length"
|
||||
class="t-widget-condition-config"
|
||||
>
|
||||
<li v-for="(criterion, index) in domainObject.configuration.criteria"
|
||||
:key="index"
|
||||
class="has-local-controls t-condition"
|
||||
>
|
||||
<Criterion :telemetry="telemetry"
|
||||
:criterion="criterion"
|
||||
:index="index"
|
||||
:trigger="domainObject.configuration.trigger"
|
||||
:is-default="domainObject.configuration.criteria.length === 1"
|
||||
@persist="persist"
|
||||
/>
|
||||
<div class="c-c__criterion-controls">
|
||||
<span class="is-enabled c-c__duplicate"
|
||||
@click="cloneCriterion(index)"
|
||||
></span>
|
||||
<span v-if="!(domainObject.configuration.criteria.length === 1)"
|
||||
class="is-enabled c-c__trash"
|
||||
@click="removeCriterion(index)"
|
||||
></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="holder c-c-button-wrapper align-left">
|
||||
<span class="c-c-label-spacer"></span>
|
||||
<button
|
||||
class="c-c-button c-c-button--minor add-criteria-button"
|
||||
@click="addCriteria"
|
||||
>
|
||||
<span class="c-c-button__label">Add Criteria</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<span class="c-cdef__label">Output</span>
|
||||
<span class="c-cdef__controls">
|
||||
<select v-model="selectedOutputSelection"
|
||||
@change="setOutputValue"
|
||||
>
|
||||
<option value="">- Select Output -</option>
|
||||
<option v-for="option in outputOptions"
|
||||
:key="option"
|
||||
:value="option"
|
||||
>
|
||||
{{ initCap(option) }}
|
||||
</option>
|
||||
</select>
|
||||
<input v-if="selectedOutputSelection === outputOptions[2]"
|
||||
v-model="domainObject.configuration.output"
|
||||
class="t-condition-name-input"
|
||||
type="text"
|
||||
@blur="persist"
|
||||
>
|
||||
</span>
|
||||
|
||||
<div v-if="!domainObject.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="domainObject.configuration.trigger"
|
||||
@change="persist"
|
||||
>
|
||||
<option value="all">when all criteria are met</option>
|
||||
<option value="any">when any criteria are met</option>
|
||||
</select>
|
||||
</span>
|
||||
|
||||
<template v-if="telemetry.length">
|
||||
<div v-for="(criterion, index) in domainObject.configuration.criteria"
|
||||
:key="index"
|
||||
class="c-cdef__criteria"
|
||||
>
|
||||
<Criterion :telemetry="telemetry"
|
||||
:criterion="criterion"
|
||||
:index="index"
|
||||
:trigger="domainObject.configuration.trigger"
|
||||
:is-default="domainObject.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="!(domainObject.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>
|
||||
<div v-if="domainObject"
|
||||
id="conditionArea"
|
||||
class="c-cs-ui__conditions"
|
||||
:class="['widget-condition', { 'widget-condition--current': currentConditionIdentifier && (currentConditionIdentifier.key === conditionIdentifier.key) }]"
|
||||
>
|
||||
<div class="title-bar">
|
||||
<span class="condition-name">
|
||||
{{ domainObject.configuration.name }}
|
||||
</span>
|
||||
<span class="condition-output">
|
||||
Output: {{ domainObject.configuration.output }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="condition-config">
|
||||
<span class="condition-description">
|
||||
{{ domainObject.configuration.description }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-else
|
||||
class="c-condition c-condition--browse"
|
||||
:class="{ 'c-condition--current': currentConditionIdentifier && (currentConditionIdentifier.key === conditionIdentifier.key) }"
|
||||
>
|
||||
<!-- Browse view -->
|
||||
<div class="c-condition__header">
|
||||
<span class="c-condition__name">
|
||||
{{ domainObject.configuration.name }}
|
||||
</span>
|
||||
<span class="c-condition__output">
|
||||
Output: {{ domainObject.configuration.output }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="c-condition__summary">
|
||||
Description/summary goes here {{ domainObject.configuration.description }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -210,7 +200,9 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domainObject: this.domainObject,
|
||||
domainObject: {
|
||||
configuration: {}
|
||||
},
|
||||
currentCriteria: this.currentCriteria,
|
||||
expanded: true,
|
||||
trigger: 'all',
|
||||
@ -262,7 +254,7 @@ export default {
|
||||
dragStart(e) {
|
||||
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
|
||||
e.dataTransfer.effectAllowed = "copyMove";
|
||||
e.dataTransfer.setDragImage(e.target.closest('.c-c-container__container'), 0, 0);
|
||||
e.dataTransfer.setDragImage(e.target.closest('.js-condition-drag-wrapper'), 0, 0);
|
||||
this.$emit('setMoveIndex', this.conditionIndex);
|
||||
},
|
||||
dragStop(e) {
|
||||
@ -301,5 +293,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -22,60 +22,59 @@
|
||||
|
||||
<template>
|
||||
<section id="conditionCollection"
|
||||
class="c-cs__ui_section"
|
||||
class="c-cs__conditions"
|
||||
:class="{ 'is-expanded': expanded }"
|
||||
>
|
||||
<div class="c-cs__ui__header">
|
||||
<span class="c-cs__ui__header-label">Conditions</span>
|
||||
<div class="c-cs__header c-section__header">
|
||||
<span
|
||||
class="is-enabled flex-elem"
|
||||
:class="['c-cs__disclosure-triangle', { 'c-cs__disclosure-triangle--expanded': expanded }]"
|
||||
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__ui_content"
|
||||
class="c-cs__content"
|
||||
>
|
||||
<div v-show="isEditing"
|
||||
class="help"
|
||||
class="hint"
|
||||
:class="{ 's-status-icon-warning-lo': !telemetryObjs.length }"
|
||||
>
|
||||
<span v-if="!telemetryObjs.length">Drag telemetry into Condition Set in order to add conditions.</span>
|
||||
<span v-else>The first condition to match is the one that wins. Drag conditions to rearrange.</span>
|
||||
<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>
|
||||
<div class="holder add-condition-button-wrapper align-left">
|
||||
<button
|
||||
v-show="isEditing"
|
||||
id="addCondition"
|
||||
class="c-cs-button c-cs-button--major add-condition-button"
|
||||
:class="{ 'is-disabled': !telemetryObjs.length}"
|
||||
:disabled="!telemetryObjs.length"
|
||||
@click="addCondition"
|
||||
|
||||
<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="(conditionIdentifier, index) in conditionCollection"
|
||||
:key="conditionIdentifier.key"
|
||||
class="c-condition-h"
|
||||
>
|
||||
<span class="c-cs-button__label">Add Condition</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="c-c__condition-collection">
|
||||
<ul class="c-c__container-holder">
|
||||
<li v-for="(conditionIdentifier, index) in conditionCollection"
|
||||
:key="conditionIdentifier.key"
|
||||
>
|
||||
<div v-if="isEditing"
|
||||
class="c-c__drag-ghost"
|
||||
@drop.prevent="dropCondition"
|
||||
@dragenter="dragEnter"
|
||||
@dragleave="dragLeave"
|
||||
@dragover.prevent
|
||||
></div>
|
||||
<Condition :condition-identifier="conditionIdentifier"
|
||||
:current-condition-identifier="currentConditionIdentifier"
|
||||
:condition-index="index"
|
||||
:telemetry="telemetryObjs"
|
||||
:is-editing="isEditing"
|
||||
@removeCondition="removeCondition"
|
||||
@cloneCondition="cloneCondition"
|
||||
@setMoveIndex="setMoveIndex"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="isEditing"
|
||||
class="c-c__drag-ghost"
|
||||
@drop.prevent="dropCondition"
|
||||
@dragenter="dragEnter"
|
||||
@dragleave="dragLeave"
|
||||
@dragover.prevent
|
||||
></div>
|
||||
<Condition :condition-identifier="conditionIdentifier"
|
||||
:current-condition-identifier="currentConditionIdentifier"
|
||||
:condition-index="index"
|
||||
:telemetry="telemetryObjs"
|
||||
:is-editing="isEditing"
|
||||
@removeCondition="removeCondition"
|
||||
@cloneCondition="cloneCondition"
|
||||
@setMoveIndex="setMoveIndex"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -21,24 +21,20 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-cs-edit w-condition-set">
|
||||
<div class="c-sw-edit__ui holder">
|
||||
<section id="current-output">
|
||||
<div class="c-cs__ui__header">
|
||||
<span class="c-cs__ui__header-label">Current Output</span>
|
||||
</div>
|
||||
<div class="c-cs__ui_content">
|
||||
<span v-if="currentConditionOutput"
|
||||
class="current-output"
|
||||
>
|
||||
{{ currentConditionOutput }}
|
||||
</span>
|
||||
<span v-else>No output selected</span>
|
||||
</div>
|
||||
</section>
|
||||
<TestData :is-editing="isEditing" />
|
||||
<ConditionCollection :is-editing="isEditing" />
|
||||
</div>
|
||||
<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>No output selected</template>
|
||||
</div>
|
||||
</section>
|
||||
<TestData :is-editing="isEditing" />
|
||||
<ConditionCollection :is-editing="isEditing" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<label>{{ setRowLabel }}</label>
|
||||
<span class="t-configuration">
|
||||
<span class="controls">
|
||||
<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 v-model="criterion.telemetry"
|
||||
@change="updateMetadataOptions"
|
||||
>
|
||||
@ -16,7 +17,7 @@
|
||||
</select>
|
||||
</span>
|
||||
<span v-if="criterion.telemetry"
|
||||
class="controls"
|
||||
class="c-cdef__control"
|
||||
>
|
||||
<select v-model="criterion.metadata"
|
||||
@change="updateOperations"
|
||||
@ -31,7 +32,7 @@
|
||||
</select>
|
||||
</span>
|
||||
<span v-if="criterion.telemetry && criterion.metadata"
|
||||
class="controls"
|
||||
class="c-cdef__control"
|
||||
>
|
||||
<select v-model="criterion.operation"
|
||||
@change="updateOperationInputVisibility"
|
||||
@ -46,9 +47,10 @@
|
||||
</select>
|
||||
<span v-for="(item, inputIndex) in inputCount"
|
||||
:key="inputIndex"
|
||||
class="c-cdef__control__inputs"
|
||||
>
|
||||
<input v-model="criterion.input[inputIndex]"
|
||||
class="t-condition-name-input"
|
||||
class="c-cdef__control__input"
|
||||
type="text"
|
||||
@blur="persist"
|
||||
>
|
||||
|
0
src/plugins/condition/components/CurrentOutput.vue
Normal file
0
src/plugins/condition/components/CurrentOutput.vue
Normal file
@ -23,18 +23,19 @@
|
||||
<template>
|
||||
<section v-show="isEditing"
|
||||
id="test-data"
|
||||
class="test-data"
|
||||
class="c-cs__test-data"
|
||||
:class="{ 'is-expanded': expanded }"
|
||||
>
|
||||
<div class="c-cs__ui__header">
|
||||
<span class="c-cs__ui__header-label">Test Data</span>
|
||||
<div class="c-cs__header c-section__header">
|
||||
<span
|
||||
class="is-enabled flex-elem"
|
||||
:class="['c-cs__disclosure-triangle', { 'c-cs__disclosure-triangle--expanded': expanded }]"
|
||||
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__ui_content"
|
||||
class="c-cs__content"
|
||||
>
|
||||
<label class="c-toggle-switch">
|
||||
<input
|
||||
@ -43,32 +44,27 @@
|
||||
@change="applyTestData"
|
||||
>
|
||||
<span class="c-toggle-switch__slider"></span>
|
||||
<span>Apply Test Data</span>
|
||||
<span class="c-toggle-switch__label">Apply Test Data</span>
|
||||
</label>
|
||||
<div class="t-test-data-config">
|
||||
<div class="c-cs-editui__conditions widget-condition">
|
||||
<form>
|
||||
<label>
|
||||
<span>Set</span>
|
||||
<select>
|
||||
<option>- Select Input -</option>
|
||||
</select>
|
||||
</label>
|
||||
<span class="is-enabled flex-elem c-cs__duplicate"></span>
|
||||
<span class="is-enabled flex-elem c-cs__trash"></span>
|
||||
</form>
|
||||
</div>
|
||||
<div class="c-cs-editui__conditions widget-condition">
|
||||
<form>
|
||||
<label>
|
||||
<span>Set</span>
|
||||
<select>
|
||||
<option>- Select Input -</option>
|
||||
</select>
|
||||
</label>
|
||||
<span class="is-enabled c-cs__duplicate"></span>
|
||||
<span class="is-enabled c-cs__trash"></span>
|
||||
</form>
|
||||
<div class="c-cs-test-h">
|
||||
<div v-for="n in 5"
|
||||
:key="n"
|
||||
class="c-test-datum"
|
||||
>
|
||||
<span class="c-test-datum__label">Set</span>
|
||||
<div class="c-test-datum__controls">
|
||||
<select>
|
||||
<option>- Select Input -</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="c-test-datum__buttons">
|
||||
<button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
|
||||
title="Duplicate this test data value"
|
||||
></button>
|
||||
<button class="c-click-icon c-test-data__delete-button icon-trash"
|
||||
title="Delete this test data value"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,155 +1,89 @@
|
||||
.c-cs-edit {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.c-cs__ui__header {
|
||||
background-color: #D0D1D3;
|
||||
padding: 0.4em 0.6em;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
color: #7C7D80;
|
||||
.c-cs {
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
}
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.c-cs__ui__header-label {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
|
||||
.c-cs__ui_content {
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
.c-cs-ui__label {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.c-cs__ui_content .help {
|
||||
font-style: italic;
|
||||
padding: 0.4em 0;
|
||||
}
|
||||
|
||||
.current-output {
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
margin: 0.4em 0.6em;
|
||||
}
|
||||
|
||||
.condition-output {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.condition-description {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.checkbox.custom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 0.6em;
|
||||
}
|
||||
.checkbox.custom span {
|
||||
display: inline-block;
|
||||
margin-left: 0.6em;
|
||||
}
|
||||
|
||||
.t-test-data-config {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.c-c__condition-collection.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.widget-condition form {
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.widget-condition form label {
|
||||
flex-grow: 1;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.widget-condition form input {
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.c-cs-button[class*="--major"],
|
||||
.c-cs-button[class*='is-active'],
|
||||
.c-cs-button--menu[class*="--major"],
|
||||
.c-cs-button--menu[class*='is-active'] {
|
||||
border: solid 1px #0B427C;
|
||||
background-color: #4778A3;
|
||||
padding: 0.2em 0.6em;
|
||||
margin: 0.4em;
|
||||
font-weight: bold;
|
||||
color: #eee;
|
||||
border-radius: 6px;
|
||||
|
||||
&.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.c-cs__disclosure-triangle,
|
||||
.c-cs__menu-hamburger,
|
||||
.c-cs__duplicate,
|
||||
.c-cs__trash {
|
||||
$d: 8px;
|
||||
color: $colorDisclosureCtrl;
|
||||
width: $d;
|
||||
visibility: hidden;
|
||||
|
||||
&.is-enabled {
|
||||
cursor: pointer;
|
||||
visibility: visible;
|
||||
|
||||
&:hover {
|
||||
color: $colorDisclosureCtrlHov;
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
+ * {
|
||||
margin-top: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
&:before {
|
||||
$s: .5;
|
||||
display: block;
|
||||
font-family: symbolsfont;
|
||||
font-size: 1rem * $s;
|
||||
.c-button {
|
||||
align-self: start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-cs__disclosure-triangle {
|
||||
&:before {
|
||||
content: $glyph-icon-arrow-right;
|
||||
.is-editing & {
|
||||
// Add some space to kick away from blue editing border indication
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
|
||||
&--expanded {
|
||||
&:before {
|
||||
transform: rotate(90deg);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-cs__duplicate {
|
||||
margin-right: 0.3em;
|
||||
&:before {
|
||||
content: $glyph-icon-duplicate;
|
||||
&__conditions {
|
||||
> * + * {
|
||||
margin-top: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.hint {
|
||||
padding: $interiorMarginSm;
|
||||
}
|
||||
|
||||
/************************** SPECIFIC ITEMS */
|
||||
&__current-output-value {
|
||||
font-size: 1.25em;
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
.c-cs__trash {
|
||||
&:before {
|
||||
content: $glyph-icon-trash;
|
||||
/***************************** TEST DATA */
|
||||
.c-cs-test-h {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
padding-right: $interiorMarginSm;
|
||||
|
||||
> * + * {
|
||||
margin-top: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.c-test-datum {
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
+ * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
}
|
||||
&__controls {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,208 +1,111 @@
|
||||
.widget-condition {
|
||||
background-color: #eee;
|
||||
margin: 0 0 0.33em;
|
||||
border-radius: 3px;
|
||||
|
||||
&--current {
|
||||
background-color: #DEECFA;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.c-c-editui__conditions.widget-condition {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.c-c-button-wrapper {
|
||||
border-top: solid 1px #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.c-c-label-spacer {
|
||||
display: inline-block;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.c-c-button[class*="--minor"],
|
||||
.c-c-button[class*='is-active'],
|
||||
.c-c-button--menu[class*="--minor"],
|
||||
.c-c-button--menu[class*='is-active'] {
|
||||
border: solid 1px #666;
|
||||
background-color: #fff;
|
||||
padding: 0.1em 0.4em;
|
||||
margin: 0.4em;
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.title-bar {
|
||||
.c-condition,
|
||||
.c-test-datum {
|
||||
@include discreteItem();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
padding: 0.4em 0;
|
||||
padding: $interiorMargin;
|
||||
|
||||
&--edit {
|
||||
line-height: 160%; // For layout when inputs wrap, like in criteria
|
||||
}
|
||||
}
|
||||
|
||||
.title-bar span {
|
||||
margin-right: 0.6em;
|
||||
}
|
||||
.c-condition {
|
||||
flex-direction: column;
|
||||
|
||||
.title-bar span.c-c__duplicate,
|
||||
.title-bar span.c-c__trash{
|
||||
margin-right: 0;
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
|
||||
.widget-condition > div {
|
||||
margin: 0 0.4em;
|
||||
}
|
||||
|
||||
.condition-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.condition-summary .condition-description {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.condition-summary {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.condition-config {
|
||||
padding: 0.3em 0;
|
||||
}
|
||||
|
||||
.widget-condition form label {
|
||||
flex-grow: 1;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.l-widget-condition {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.l-compact-form ul li {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.widget-condition-content.expanded {
|
||||
margin: 0 3px;
|
||||
}
|
||||
|
||||
.widget-condition-content.expanded ul li {
|
||||
border-top: solid 1px #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.l-compact-form ul li .controls {
|
||||
display: inline-flex;
|
||||
flex-grow: inherit;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.l-compact-form ul li > label {
|
||||
display: block;
|
||||
width: 90px;
|
||||
min-width: 90px;
|
||||
text-align: right;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.l-compact-form ul li .controls input[type="text"],
|
||||
.l-compact-form ul li .controls input[type="search"],
|
||||
.l-compact-form ul li .controls input[type="number"],
|
||||
.l-compact-form ul li .controls button,
|
||||
.l-compact-form ul li .controls select {
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.condition-config-edit {
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
|
||||
.c-c__disclosure-triangle,
|
||||
.c-c__menu-hamburger,
|
||||
.c-c__duplicate,
|
||||
.c-c__trash {
|
||||
$d: 8px;
|
||||
color: $colorDisclosureCtrl;
|
||||
width: $d;
|
||||
visibility: hidden;
|
||||
|
||||
&.is-enabled {
|
||||
cursor: pointer;
|
||||
visibility: visible;
|
||||
|
||||
&:hover {
|
||||
color: $colorDisclosureCtrlHov;
|
||||
}
|
||||
|
||||
&:before {
|
||||
$s: .5;
|
||||
display: block;
|
||||
font-family: symbolsfont;
|
||||
font-size: 1rem * $s;
|
||||
> * + * {
|
||||
margin-top: $interiorMarginSm;
|
||||
}
|
||||
&--browse {
|
||||
.c-condition__summary {
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
padding-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-c__disclosure-triangle {
|
||||
&:before {
|
||||
content: $glyph-icon-arrow-right;
|
||||
}
|
||||
/***************************** HEADER */
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-content: stretch;
|
||||
overflow: hidden;
|
||||
|
||||
&--expanded {
|
||||
&:before {
|
||||
transform: rotate(90deg);
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
+ * {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.c-c__menu-hamburger {
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
&__name {
|
||||
font-weight: bold;
|
||||
align-self: baseline; // Fixes bold line-height offset problem
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: $glyph-icon-menu-hamburger;
|
||||
&__output,
|
||||
&__summary {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.c-c__duplicate {
|
||||
&:before {
|
||||
content: $glyph-icon-duplicate;
|
||||
}
|
||||
}
|
||||
/***************************** 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;
|
||||
|
||||
.c-c__trash {
|
||||
&:before {
|
||||
content: $glyph-icon-trash;
|
||||
&__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;
|
||||
//line-height: 200%;
|
||||
grid-column: 2;
|
||||
|
||||
> * > * {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
grid-column: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.c-c__drag-ghost {
|
||||
width: 100%;
|
||||
min-height: 0.33em;
|
||||
min-height: $interiorMarginSm;
|
||||
|
||||
//&:before {
|
||||
// @include test();
|
||||
// content: '';
|
||||
// display: block;
|
||||
// z-index: 2;
|
||||
//}
|
||||
|
||||
&.dragging {
|
||||
min-height: 2em;
|
||||
border: solid 1px blue;
|
||||
min-height: 5em;
|
||||
//border: solid 1px blue;
|
||||
background-color: lightblue;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.c-c__criterion-controls {
|
||||
width: 28px;
|
||||
|
||||
.c-c__duplicate,
|
||||
.c-c__trash {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="condition"
|
||||
class="holder c-c-button-wrapper align-left"
|
||||
>
|
||||
<div>{{ condition.configuration.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
],
|
||||
props: {
|
||||
conditionIdentifier: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
condition: null
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.objects.get(this.conditionIdentifier).then((conditionDomainObject) => {
|
||||
this.condition = conditionDomainObject;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,14 +1,115 @@
|
||||
<template>
|
||||
<div>Conditional Styles inspector view</div>
|
||||
<div>
|
||||
<div v-if="!conditionalStyles.length"
|
||||
class="holder c-c-button-wrapper align-left"
|
||||
>
|
||||
<button
|
||||
class="c-c-button c-c-button--minor add-criteria-button"
|
||||
@click="addConditionSet"
|
||||
>
|
||||
<span class="c-c-button__label">Use conditional styling</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="holder c-c-button-wrapper align-left">
|
||||
<button
|
||||
class="c-c-button c-c-button--minor add-criteria-button"
|
||||
@click="removeConditionSet"
|
||||
>
|
||||
<span class="c-c-button__label">Remove conditional styling</span>
|
||||
</button>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="conditionStyle in conditionalStyles"
|
||||
:key="conditionStyle.conditionIdentifier.key"
|
||||
>
|
||||
<conditional-style :condition-identifier="conditionStyle.conditionIdentifier"
|
||||
:condition-style="conditionStyle.style"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import ConditionalStyle from "./ConditionalStyle.vue";
|
||||
export default {
|
||||
components: {
|
||||
ConditionalStyle
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
]
|
||||
'openmct',
|
||||
'domainObject',
|
||||
'layoutItem'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
conditionalStyles: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.layoutItem) {
|
||||
//TODO: Handle layout items
|
||||
}
|
||||
if (this.domainObject.configuration) {
|
||||
this.defautStyle = this.domainObject.configuration.defaultStyle;
|
||||
if (this.domainObject.configuration.conditionalStyle) {
|
||||
this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || [];
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addConditionSet() {
|
||||
//TODO: this.conditionSetIdentifier will be set by the UI before calling this
|
||||
this.conditionSetIdentifier = {
|
||||
namespace: '',
|
||||
key: 'bb0f61ad-268d-4d3e-bb30-90ca4a2053c4'
|
||||
};
|
||||
this.initializeConditionalStyles();
|
||||
},
|
||||
removeConditionSet() {
|
||||
this.conditionSetIdentifier = '';
|
||||
this.conditionalStyles = [];
|
||||
this.persist(undefined);
|
||||
},
|
||||
initializeConditionalStyles() {
|
||||
const backgroundColors = [{backgroundColor: 'red'},{backgroundColor: 'orange'}, {backgroundColor: 'blue'}];
|
||||
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
|
||||
conditionSetDomainObject.configuration.conditionCollection.forEach((identifier, index) => {
|
||||
this.conditionalStyles.push({
|
||||
conditionIdentifier: identifier,
|
||||
style: backgroundColors[index]
|
||||
});
|
||||
});
|
||||
this.persist({
|
||||
defaultStyle: this.defaultStyle || {backgroundColor: 'inherit'},
|
||||
conditionSetIdentifier: this.conditionSetIdentifier,
|
||||
styles: this.conditionalStyles
|
||||
});
|
||||
});
|
||||
},
|
||||
findStyleByConditionId(id) {
|
||||
for(let i=0, ii=this.conditionalStyles.length; i < ii; i++) {
|
||||
if (this.openmct.objects.makeKeyString(this.conditionalStyles[i].conditionIdentifier) === this.openmct.objects.makeKeyString(id)) {
|
||||
return {
|
||||
index: i,
|
||||
item: this.conditionalStyles[i]
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
updateConditionalStyle(conditionIdentifier, style) {
|
||||
let found = this.findStyleByConditionId(conditionIdentifier);
|
||||
if (found) {
|
||||
this.conditionalStyles[found.index].style = style;
|
||||
}
|
||||
this.persist(undefined);
|
||||
},
|
||||
persist(conditionalStyle) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'configuration.conditionalStyle', conditionalStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import {OPERATIONS} from '../utils/operations';
|
||||
|
||||
export default class TelemetryCriterion extends EventEmitter {
|
||||
|
@ -36,6 +36,7 @@
|
||||
<div
|
||||
v-if="showLabel"
|
||||
class="c-telemetry-view__label"
|
||||
:style="conditionalStyle"
|
||||
>
|
||||
<div class="c-telemetry-view__label-text">
|
||||
{{ domainObject.name }}
|
||||
@ -47,6 +48,7 @@
|
||||
:title="fieldName"
|
||||
class="c-telemetry-view__value"
|
||||
:class="[telemetryClass]"
|
||||
:style="!telemetryClass && conditionalStyle"
|
||||
>
|
||||
<div class="c-telemetry-view__value-text">
|
||||
{{ telemetryValue }}
|
||||
@ -59,6 +61,7 @@
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue'
|
||||
import printj from 'printj'
|
||||
import StyleRuleManager from "../../condition/StyleRuleManager";
|
||||
|
||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||
DEFAULT_POSITION = [1, 1],
|
||||
@ -109,7 +112,8 @@ export default {
|
||||
datum: undefined,
|
||||
formats: undefined,
|
||||
domainObject: undefined,
|
||||
currentObjectPath: undefined
|
||||
currentObjectPath: undefined,
|
||||
conditionalStyle: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -182,6 +186,16 @@ export default {
|
||||
this.removeSelectable();
|
||||
}
|
||||
|
||||
if (this.unlistenStyles) {
|
||||
this.unlistenStyles();
|
||||
}
|
||||
|
||||
if (this.styleRuleManager) {
|
||||
this.styleRuleManager.destroy();
|
||||
this.styleRuleManager.off('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
delete this.styleRuleManager;
|
||||
}
|
||||
|
||||
this.openmct.time.off("bounds", this.refreshData);
|
||||
},
|
||||
methods: {
|
||||
@ -224,6 +238,7 @@ export default {
|
||||
},
|
||||
setObject(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
this.initConditionalStyles();
|
||||
this.keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
||||
@ -248,6 +263,21 @@ export default {
|
||||
},
|
||||
showContextMenu(event) {
|
||||
this.openmct.contextMenu._showContextMenuForObjectPath(this.currentObjectPath, event.x, event.y, CONTEXT_MENU_ACTIONS);
|
||||
},
|
||||
initConditionalStyles() {
|
||||
this.styleRuleManager = new StyleRuleManager(this.domainObject.configuration.conditionalStyle, this.openmct);
|
||||
this.styleRuleManager.on('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
|
||||
if (this.unlistenStyles) {
|
||||
this.unlistenStyles();
|
||||
}
|
||||
this.unlistenStyles = this.openmct.objects.observe(this.domainObject, 'configuration.conditionalStyle', (newConditionalStyle) => {
|
||||
//Updating conditional styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||
this.styleRuleManager.updateConditionalStyleConfig(newConditionalStyle);
|
||||
});
|
||||
},
|
||||
updateStyle(styleObj) {
|
||||
this.conditionalStyle = styleObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,18 +232,16 @@ export default {
|
||||
this.newFrameLocation = [containerIndex, insertFrameIndex];
|
||||
},
|
||||
addFrame(domainObject) {
|
||||
if (this.newFrameLocation.length) {
|
||||
let containerIndex = this.newFrameLocation[0],
|
||||
frameIndex = this.newFrameLocation[1],
|
||||
frame = new Frame(domainObject.identifier),
|
||||
container = this.containers[containerIndex];
|
||||
let containerIndex = this.newFrameLocation.length ? this.newFrameLocation[0] : 0;
|
||||
let container = this.containers[containerIndex];
|
||||
let frameIndex = this.newFrameLocation.length ? this.newFrameLocation[1] : container.frames.length;
|
||||
let frame = new Frame(domainObject.identifier);
|
||||
|
||||
container.frames.splice(frameIndex + 1, 0, frame);
|
||||
sizeItems(container.frames, frame);
|
||||
container.frames.splice(frameIndex + 1, 0, frame);
|
||||
sizeItems(container.frames, frame);
|
||||
|
||||
this.newFrameLocation = [];
|
||||
this.persist(containerIndex);
|
||||
}
|
||||
this.newFrameLocation = [];
|
||||
this.persist(containerIndex);
|
||||
},
|
||||
deleteFrame(frameId) {
|
||||
let container = this.containers
|
||||
|
@ -1,90 +1,68 @@
|
||||
<template>
|
||||
<multipane class="c-imagery-layout"
|
||||
type="vertical"
|
||||
>
|
||||
<pane :style="{'min-height': `300px`}">
|
||||
<div class="main-image-wrapper c-imagery has-local-controls">
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc">
|
||||
<span class="holder flex-elem grows c-imagery__lc__sliders">
|
||||
<input v-model="filters.brightness"
|
||||
class="icon-brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
<input v-model="filters.contrast"
|
||||
class="icon-contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
</span>
|
||||
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
|
||||
<a class="s-icon-button icon-reset t-btn-reset"
|
||||
@click="filters={brightness: 100, contrast: 100}"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="main-image s-image-main"
|
||||
:class="{'paused unnsynced': paused(),'stale':false }"
|
||||
:style="{'background-image': `url(${getImageUrl()})`,
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`}"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="l-image-controller flex-elem l-flex-row">
|
||||
<div class="l-datetime-w flex-elem grows">
|
||||
<a class="c-button show-thumbs sm hidden icon-thumbs-strip"></a>
|
||||
<span class="l-time">{{ getTime() }}</span>
|
||||
</div>
|
||||
<div class="h-local-controls flex-elem">
|
||||
<a class="c-button icon-pause pause-play"
|
||||
:class="{'is-paused': paused()}"
|
||||
@click="paused(!paused())"
|
||||
></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</pane>
|
||||
|
||||
<pane class="c-inspector__elements"
|
||||
handle="before"
|
||||
:style="{'min-height': `100px`}"
|
||||
>
|
||||
<div class="c-elements-pool">
|
||||
<div ref="thumbsWrapper"
|
||||
class="thumbs-layout"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<div v-for="(imageData, index) in imageHistory"
|
||||
:key="index"
|
||||
class="l-image-thumb-item"
|
||||
:class="{selected: imageData.selected}"
|
||||
@click="setSelectedImage(imageData)"
|
||||
<div class="c-imagery">
|
||||
<div class="c-imagery__main-image-wrapper has-local-controls">
|
||||
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc">
|
||||
<span class="holder flex-elem grows c-imagery__lc__sliders">
|
||||
<input v-model="filters.brightness"
|
||||
class="icon-brightness"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
<img class="l-thumb"
|
||||
:src="getImageUrl(imageData)"
|
||||
>
|
||||
<div class="l-time">{{ getTime(imageData) }}</div>
|
||||
</div>
|
||||
<input v-model="filters.contrast"
|
||||
class="icon-contrast"
|
||||
type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
>
|
||||
</span>
|
||||
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
|
||||
<a class="s-icon-button icon-reset t-btn-reset"
|
||||
@click="filters={brightness: 100, contrast: 100}"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="main-image s-image-main c-imagery__main-image"
|
||||
:class="{'paused unnsynced': paused(),'stale':false }"
|
||||
:style="{'background-image': `url(${getImageUrl()})`,
|
||||
'filter': `brightness(${filters.brightness}%) contrast(${filters.contrast}%)`}"
|
||||
>
|
||||
</div>
|
||||
<div class="c-imagery__control-bar">
|
||||
<div class="c-imagery__timestamp">{{ getTime() }}</div>
|
||||
<div class="h-local-controls flex-elem">
|
||||
<a class="c-button icon-pause pause-play"
|
||||
:class="{'is-paused': paused()}"
|
||||
@click="paused(!paused())"
|
||||
></a>
|
||||
</div>
|
||||
</div>
|
||||
</pane>
|
||||
</multipane>
|
||||
</div>
|
||||
<div ref="thumbsWrapper"
|
||||
class="c-imagery__thumbs-wrapper"
|
||||
:class="{'is-paused': paused()}"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<div v-for="(imageData, index) in imageHistory"
|
||||
:key="index"
|
||||
class="c-imagery__thumb c-thumb"
|
||||
:class="{selected: imageData.selected}"
|
||||
@click="setSelectedImage(imageData)"
|
||||
>
|
||||
<img class="c-thumb__image"
|
||||
:src="getImageUrl(imageData)"
|
||||
>
|
||||
<div class="c-thumb__timestamp">{{ getTime(imageData) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import multipane from '@/ui/layout/multipane.vue';
|
||||
import pane from '@/ui/layout/pane.vue';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
components: {
|
||||
multipane,
|
||||
pane
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
autoScroll: true,
|
||||
@ -109,7 +87,7 @@ export default {
|
||||
this.subscribe(this.domainObject);
|
||||
},
|
||||
updated() {
|
||||
this.scrollToBottom();
|
||||
this.scrollToRight();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.stopListening();
|
||||
@ -152,6 +130,10 @@ export default {
|
||||
if (arguments.length > 0 && state !== this.isPaused) {
|
||||
this.unselectAllImages();
|
||||
this.isPaused = state;
|
||||
if (state === true) {
|
||||
// If we are pausing, select the latest image in imageHistory
|
||||
this.setSelectedImage(this.imageHistory[this.imageHistory.length - 1]);
|
||||
}
|
||||
|
||||
if (this.nextDatum) {
|
||||
this.updateValues(this.nextDatum);
|
||||
@ -180,24 +162,31 @@ export default {
|
||||
this.updateValues(values[values.length - 1]);
|
||||
});
|
||||
},
|
||||
scrollToBottom() {
|
||||
scrollToRight() {
|
||||
if (this.isPaused || !this.$refs.thumbsWrapper || !this.autoScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollHeight = this.$refs.thumbsWrapper.scrollHeight || 0;
|
||||
if (!scrollHeight) {
|
||||
const scrollWidth = this.$refs.thumbsWrapper.scrollWidth || 0;
|
||||
if (!scrollWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => this.$refs.thumbsWrapper.scrollTop = scrollHeight, 0);
|
||||
setTimeout(() => this.$refs.thumbsWrapper.scrollLeft = scrollWidth, 0);
|
||||
},
|
||||
setSelectedImage(image) {
|
||||
this.imageUrl = this.getImageUrl(image);
|
||||
this.time = this.getTime(image);
|
||||
this.paused(true);
|
||||
this.unselectAllImages();
|
||||
image.selected = true;
|
||||
// If we are paused and the current image IS selected, unpause
|
||||
// Otherwise, set current image and pause
|
||||
if (this.isPaused && image.selected) {
|
||||
this.paused(false);
|
||||
this.unselectAllImages();
|
||||
} else {
|
||||
this.imageUrl = this.getImageUrl(image);
|
||||
this.time = this.getTime(image);
|
||||
this.paused(true);
|
||||
this.unselectAllImages();
|
||||
image.selected = true;
|
||||
}
|
||||
},
|
||||
stopListening() {
|
||||
if (this.unsubscribe) {
|
||||
|
@ -1,16 +1,20 @@
|
||||
.c-imagery-layout {
|
||||
.c-imagery {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
.main-image-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding-bottom: 5px;
|
||||
> * + * {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
|
||||
.main-image {
|
||||
&__main-image-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__main-image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
@ -21,12 +25,137 @@
|
||||
}
|
||||
}
|
||||
|
||||
.l-image-controller {
|
||||
&__control-bar {
|
||||
padding: 5px 0 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.thumbs-layout {
|
||||
margin-top: 5px;
|
||||
overflow: auto;
|
||||
&__timestamp {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__thumbs-wrapper {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 135px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
&.is-paused {
|
||||
background: rgba($colorPausedBg, 0.4);
|
||||
}
|
||||
|
||||
.c-thumb:last-child {
|
||||
// Hilite the lastest thumb
|
||||
background: $colorBodyFg;
|
||||
color: $colorBodyBg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** THUMBS */
|
||||
.c-thumb {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4px;
|
||||
width: $imageThumbsD;
|
||||
|
||||
&:hover {
|
||||
background: $colorThumbHoverBg;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: $colorPausedBg !important;
|
||||
color: $colorPausedFg !important;
|
||||
}
|
||||
|
||||
&__image {
|
||||
background-color: rgba($colorBodyFg, 0.2);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__timestamp {
|
||||
flex: 0 0 auto;
|
||||
padding: 2px 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.l-layout,
|
||||
.c-fl {
|
||||
.c-imagery__thumbs-wrapper {
|
||||
// When Imagery is in a layout, hide the thumbs area
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.s-image-main {
|
||||
background-color: $colorPlotBg;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
/*************************************** IMAGERY LOCAL CONTROLS*/
|
||||
.c-imagery {
|
||||
.h-local-controls--overlay-content {
|
||||
position: absolute;
|
||||
right: $interiorMargin; top: $interiorMargin;
|
||||
z-index: 2;
|
||||
background: $colorLocalControlOvrBg;
|
||||
border-radius: $basicCr;
|
||||
max-width: 200px;
|
||||
min-width: 100px;
|
||||
width: 35%;
|
||||
align-items: center;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
|
||||
&:before {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__lc {
|
||||
&__reset-btn {
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
&:before,
|
||||
&:after {
|
||||
border-right: 1px solid $bc;
|
||||
content:'';
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-top: 1px solid $bc;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-bottom: 1px solid $bc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** BUTTONS */
|
||||
.c-button.pause-play {
|
||||
// Pause icon set by default in markup
|
||||
&.is-paused {
|
||||
background: $colorPausedBg !important;
|
||||
color: $colorPausedFg;
|
||||
|
||||
&:before {
|
||||
content: $glyph-icon-play;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,13 +377,17 @@ define([
|
||||
* @public
|
||||
*/
|
||||
updateFiltersAndRefresh: function (updatedFilters) {
|
||||
this.filters = updatedFilters;
|
||||
this.reset();
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
delete this.unsubscribe;
|
||||
if (this.filters && !_.isEqual(this.filters, updatedFilters)) {
|
||||
this.filters = updatedFilters;
|
||||
this.reset();
|
||||
if (this.unsubscribe) {
|
||||
this.unsubscribe();
|
||||
delete this.unsubscribe;
|
||||
}
|
||||
this.fetch();
|
||||
} else {
|
||||
this.filters = updatedFilters;
|
||||
}
|
||||
this.fetch();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -47,6 +47,7 @@ define([
|
||||
this.subscriptions = {};
|
||||
this.tableComposition = undefined;
|
||||
this.telemetryObjects = [];
|
||||
this.datumCache = [];
|
||||
this.outstandingRequests = 0;
|
||||
this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||
this.paused = false;
|
||||
@ -155,6 +156,7 @@ define([
|
||||
processHistoricalData(telemetryData, columnMap, keyString, limitEvaluator) {
|
||||
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
this.boundedRows.add(telemetryRows);
|
||||
this.emit('historical-rows-processed');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -227,12 +229,28 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.paused) {
|
||||
if (this.paused) {
|
||||
let realtimeDatum = {
|
||||
datum,
|
||||
columnMap,
|
||||
keyString,
|
||||
limitEvaluator
|
||||
};
|
||||
|
||||
this.datumCache.push(realtimeDatum);
|
||||
} else {
|
||||
this.processRealtimeDatum(datum, columnMap, keyString, limitEvaluator);
|
||||
}
|
||||
}, subscribeOptions);
|
||||
}
|
||||
|
||||
processDatumCache() {
|
||||
this.datumCache.forEach(cachedDatum => {
|
||||
this.processRealtimeDatum(cachedDatum.datum, cachedDatum.columnMap, cachedDatum.keyString, cachedDatum.limitEvaluator);
|
||||
});
|
||||
this.datumCache = [];
|
||||
}
|
||||
|
||||
processRealtimeDatum(datum, columnMap, keyString, limitEvaluator) {
|
||||
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
}
|
||||
@ -272,8 +290,8 @@ define([
|
||||
|
||||
unpause() {
|
||||
this.paused = false;
|
||||
this.processDatumCache();
|
||||
this.boundedRows.subscribeToBounds();
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -51,6 +51,14 @@ define([
|
||||
view(domainObject, objectPath) {
|
||||
let table = new TelemetryTable(domainObject, openmct);
|
||||
let component;
|
||||
|
||||
let markingProp = {
|
||||
enable: true,
|
||||
useAlternateControlBar: false,
|
||||
rowName: '',
|
||||
rowNamePlural: ''
|
||||
};
|
||||
|
||||
return {
|
||||
show: function (element, editMode) {
|
||||
component = new Vue({
|
||||
@ -60,15 +68,16 @@ define([
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEditing: editMode
|
||||
}
|
||||
isEditing: editMode,
|
||||
markingProp
|
||||
};
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
table,
|
||||
objectPath
|
||||
},
|
||||
template: '<table-component :isEditing="isEditing" :enableMarking="true"></table-component>'
|
||||
template: '<table-component :isEditing="isEditing" :marking="markingProp"/>'
|
||||
});
|
||||
},
|
||||
onEditModeChange(editMode) {
|
||||
@ -86,7 +95,7 @@ define([
|
||||
priority() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return TelemetryTableViewProvider;
|
||||
});
|
||||
|
@ -21,7 +21,10 @@
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-table-wrapper">
|
||||
<div class="c-table-control-bar c-control-bar">
|
||||
<!-- main contolbar start-->
|
||||
<div v-if="!marking.useAlternateControlBar"
|
||||
class="c-table-control-bar c-control-bar"
|
||||
>
|
||||
<button
|
||||
v-if="allowExport"
|
||||
class="c-button icon-download labeled"
|
||||
@ -48,11 +51,11 @@
|
||||
<span class="c-button__label">Unmark All Rows</span>
|
||||
</button>
|
||||
<div
|
||||
v-if="enableMarking"
|
||||
v-if="marking.enable"
|
||||
class="c-separator"
|
||||
></div>
|
||||
<button
|
||||
v-if="enableMarking"
|
||||
v-if="marking.enable"
|
||||
class="c-button icon-pause pause-play labeled"
|
||||
:class=" paused ? 'icon-play is-paused' : 'icon-pause'"
|
||||
:title="paused ? 'Continue Data Flow' : 'Pause Data Flow'"
|
||||
@ -62,8 +65,38 @@
|
||||
{{ paused ? 'Play' : 'Pause' }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
<!-- main controlbar end -->
|
||||
|
||||
<!-- alternate controlbar start -->
|
||||
<div v-if="marking.useAlternateControlBar"
|
||||
class="c-table-control-bar c-control-bar"
|
||||
>
|
||||
<div class="c-control-bar__label">
|
||||
{{ markedRows.length > 1 ? `${markedRows.length} ${marking.rowNamePlural} selected`: `${markedRows.length} ${marking.rowName} selected` }}
|
||||
</div>
|
||||
|
||||
<toggle-switch
|
||||
id="show-filtered-rows-toggle"
|
||||
label="Show selected items only"
|
||||
:checked="isShowingMarkedRowsOnly"
|
||||
@change="toggleMarkedRows"
|
||||
/>
|
||||
|
||||
<button
|
||||
:class="{'hide-nice': !markedRows.length}"
|
||||
class="c-button icon-x labeled"
|
||||
title="Deselect All"
|
||||
@click="unmarkAllRows()"
|
||||
>
|
||||
<span class="c-button__label">Deselect All</span>
|
||||
</button>
|
||||
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
<!-- alternate controlbar end -->
|
||||
|
||||
<div
|
||||
class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
|
||||
@ -205,6 +238,7 @@ import TableColumnHeader from './table-column-header.vue';
|
||||
import TelemetryFilterIndicator from './TelemetryFilterIndicator.vue';
|
||||
import CSVExporter from '../../../exporters/CSVExporter.js';
|
||||
import _ from 'lodash';
|
||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||
|
||||
const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
@ -216,7 +250,8 @@ export default {
|
||||
TelemetryTableRow,
|
||||
TableColumnHeader,
|
||||
search,
|
||||
TelemetryFilterIndicator
|
||||
TelemetryFilterIndicator,
|
||||
ToggleSwitch
|
||||
},
|
||||
inject: ['table', 'openmct', 'objectPath'],
|
||||
props: {
|
||||
@ -236,9 +271,16 @@ export default {
|
||||
'type': Boolean,
|
||||
'default': true
|
||||
},
|
||||
enableMarking: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
marking: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
enable: false,
|
||||
useAlternateControlBar: false,
|
||||
rowName: '',
|
||||
rowNamePlural: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -270,7 +312,8 @@ export default {
|
||||
scrollW: 0,
|
||||
markCounter: 0,
|
||||
paused: false,
|
||||
markedRows: []
|
||||
markedRows: [],
|
||||
isShowingMarkedRowsOnly: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -304,6 +347,13 @@ export default {
|
||||
return style;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
markedRows: {
|
||||
handler(newVal, oldVal) {
|
||||
this.$emit('marked-rows-updated', newVal, oldVal);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filterChanged = _.debounce(this.filterChanged, 500);
|
||||
},
|
||||
@ -317,6 +367,7 @@ export default {
|
||||
this.table.on('object-removed', this.removeObject);
|
||||
this.table.on('outstanding-requests', this.outstandingRequests);
|
||||
this.table.on('refresh', this.clearRowsAndRerender);
|
||||
this.table.on('historical-rows-processed', this.checkForMarkedRows);
|
||||
|
||||
this.table.filteredRows.on('add', this.rowsAdded);
|
||||
this.table.filteredRows.on('remove', this.rowsRemoved);
|
||||
@ -631,18 +682,19 @@ export default {
|
||||
},
|
||||
unpause(unpausedByButton) {
|
||||
if (unpausedByButton) {
|
||||
this.paused = false;
|
||||
this.undoMarkedRows();
|
||||
this.table.unpause();
|
||||
this.markedRows = [];
|
||||
this.paused = false;
|
||||
this.pausedByButton = false;
|
||||
} else {
|
||||
if (!this.pausedByButton) {
|
||||
this.paused = false;
|
||||
this.undoMarkedRows();
|
||||
this.table.unpause();
|
||||
this.markedRows = [];
|
||||
this.paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.isShowingMarkedRowsOnly = false;
|
||||
},
|
||||
togglePauseByButton() {
|
||||
if (this.paused) {
|
||||
@ -655,24 +707,27 @@ export default {
|
||||
this.markedRows.forEach(r => r.marked = false);
|
||||
this.markedRows = [];
|
||||
},
|
||||
unmarkRow(rowIndex, ctrlKeyModifier) {
|
||||
if (ctrlKeyModifier) {
|
||||
unmarkRow(rowIndex) {
|
||||
if (this.markedRows.length > 1) {
|
||||
let row = this.visibleRows[rowIndex],
|
||||
positionInMarkedArray = this.markedRows.indexOf(row);
|
||||
|
||||
row.marked = false;
|
||||
this.markedRows.splice(positionInMarkedArray, 1);
|
||||
|
||||
if (this.markedRows.length === 0) {
|
||||
this.unpause();
|
||||
if (this.isShowingMarkedRowsOnly) {
|
||||
this.visibleRows.splice(rowIndex, 1);
|
||||
}
|
||||
} else if (this.markedRows.length) {
|
||||
this.undoMarkedRows();
|
||||
this.markRow(rowIndex);
|
||||
} else if (this.markedRows.length === 1) {
|
||||
this.unmarkAllRows();
|
||||
}
|
||||
|
||||
if (this.markedRows.length === 0) {
|
||||
this.unpause();
|
||||
}
|
||||
},
|
||||
markRow(rowIndex, keyModifier) {
|
||||
if (!this.enableMarking) {
|
||||
if (!this.marking.enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -691,12 +746,13 @@ export default {
|
||||
this.markedRows[insertMethod](markedRow);
|
||||
},
|
||||
unmarkAllRows(skipUnpause) {
|
||||
this.markedRows.forEach(row => row.marked = false);
|
||||
this.markedRows = [];
|
||||
this.undoMarkedRows();
|
||||
this.isShowingMarkedRowsOnly = false;
|
||||
this.unpause();
|
||||
this.restorePreviousRows();
|
||||
},
|
||||
markMultipleConcurrentRows(rowIndex) {
|
||||
if (!this.enableMarking) {
|
||||
if (!this.marking.enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -733,6 +789,35 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
checkForMarkedRows() {
|
||||
this.isShowingMarkedRowsOnly = false;
|
||||
this.markedRows = this.table.filteredRows.getRows().filter(row => row.marked);
|
||||
},
|
||||
showRows(rows) {
|
||||
this.table.filteredRows.rows = rows;
|
||||
this.table.filteredRows.emit('filter');
|
||||
},
|
||||
toggleMarkedRows(flag) {
|
||||
if (flag) {
|
||||
this.isShowingMarkedRowsOnly = true;
|
||||
this.userScroll = this.scrollable.scrollTop;
|
||||
this.allRows = this.table.filteredRows.getRows();
|
||||
|
||||
this.showRows(this.markedRows);
|
||||
this.setHeight();
|
||||
} else {
|
||||
this.isShowingMarkedRowsOnly = false;
|
||||
this.restorePreviousRows();
|
||||
}
|
||||
},
|
||||
restorePreviousRows() {
|
||||
if (this.allRows && this.allRows.length) {
|
||||
this.showRows(this.allRows);
|
||||
this.allRows = [];
|
||||
this.setHeight();
|
||||
this.scrollable.scrollTop = this.userScroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,8 +120,7 @@ $colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
$colorPausedFg: #333;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
@ -411,15 +410,21 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
// Discrete items, like Notebook entries, Widget rules
|
||||
$createBtnTextTransform: uppercase;
|
||||
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
|
||||
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: rgba($colorBodyFg,0.1);
|
||||
background: $colorDiscreteItemBg;
|
||||
border: none;
|
||||
border-radius: $controlCr;
|
||||
|
||||
.c-input-inline:hover {
|
||||
background: $colorBodyBg;
|
||||
}
|
||||
|
||||
&--current-match {
|
||||
background: $colorDiscreteItemCurrentBg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin discreteItemInnerElem() {
|
||||
|
@ -124,8 +124,7 @@ $colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
$colorPausedFg: #333;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
@ -415,14 +414,16 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
// Discrete items, like Notebook entries, Widget rules
|
||||
$createBtnTextTransform: uppercase;
|
||||
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
|
||||
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: rgba($colorBodyFg,0.1);
|
||||
border: none;
|
||||
border-radius: $controlCr;
|
||||
|
||||
.c-input-inline:hover {
|
||||
background: $colorBodyBg;
|
||||
&--current-match {
|
||||
background: $colorDiscreteItemCurrentBg;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,6 @@ $colorFilter: $colorFilterBg; // Standalone against $colorBodyBg
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
@ -411,12 +410,18 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
// Discrete items, like Notebook entries, Widget rules
|
||||
$createBtnTextTransform: uppercase;
|
||||
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
|
||||
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: rgba($colorBodyFg,0.1);
|
||||
background: $colorDiscreteItemBg;
|
||||
border: 1px solid $colorInteriorBorder;
|
||||
border-radius: $controlCr;
|
||||
|
||||
&--current-match {
|
||||
background: $colorDiscreteItemCurrentBg;
|
||||
}
|
||||
|
||||
.c-input-inline:hover {
|
||||
background: $colorBodyBg;
|
||||
}
|
||||
|
@ -62,6 +62,11 @@ button {
|
||||
background: $colorBtnBgHov;
|
||||
color: $colorBtnFgHov;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: $glyph-icon-arrow-down;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
@ -78,6 +83,20 @@ button {
|
||||
/********* Icon Buttons and Links */
|
||||
.c-click-icon {
|
||||
@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 {
|
||||
@ -189,6 +208,44 @@ 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 {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.c-section__header {
|
||||
@include propertiesHeader();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: $interiorMargin;
|
||||
|
||||
> * + * { margin-left: $interiorMarginSm; }
|
||||
}
|
||||
|
||||
> [class*='__label'] {
|
||||
flex: 1 1 auto;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** FORM ELEMENTS */
|
||||
input, textarea {
|
||||
font-family: inherit;
|
||||
@ -621,6 +678,11 @@ select {
|
||||
@include cToolbarSeparator();
|
||||
}
|
||||
|
||||
.c-row-separator {
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.c-toolbar {
|
||||
> * + * {
|
||||
margin-left: 2px;
|
||||
|
@ -28,6 +28,16 @@
|
||||
height: $controlBarH;
|
||||
}
|
||||
|
||||
.c-control-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.l-view-section {
|
||||
@include abs();
|
||||
overflow: auto;
|
||||
@ -52,236 +62,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************* IMAGERY */
|
||||
.l-image-main-wrapper,
|
||||
.l-image-thumbs-wrapper,
|
||||
.image-main {
|
||||
@include abs(0);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*************************************** MAIN LAYOUT */
|
||||
.l-image-main-wrapper {
|
||||
// Imagery thumbs
|
||||
bottom: $interiorMargin*2 + $imageThumbsWrapperH;
|
||||
|
||||
min-width: 150px;
|
||||
.l-image-main {
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
.l-image-main-controlbar {
|
||||
&.l-flex-row { align-items: center; }
|
||||
}
|
||||
}
|
||||
|
||||
.l-image-thumbs-wrapper {
|
||||
top: auto;
|
||||
min-height: $imageThumbsWrapperH;
|
||||
max-height: 60%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.l-date,
|
||||
.l-time,
|
||||
.l-timezone {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*************************************** MAIN IMAGE */
|
||||
|
||||
.image-main,
|
||||
.l-image-thumb-item .l-thumb {
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.l-image-main-controlbar {
|
||||
line-height: inherit;
|
||||
.l-datetime-w, .l-controls-w {
|
||||
direction: rtl;
|
||||
overflow: hidden;
|
||||
}
|
||||
.l-datetime-w {
|
||||
@include ellipsize();
|
||||
margin-right: $interiorMarginSm;
|
||||
text-align: left;
|
||||
}
|
||||
.l-controls-w {
|
||||
z-index: 2;
|
||||
}
|
||||
.l-date,
|
||||
.l-time {
|
||||
color: pullForward($colorBodyFg, 20%); // TODO: do this as a theme constant
|
||||
}
|
||||
.l-mag {
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
&:before {
|
||||
content: "\000049";
|
||||
}
|
||||
}
|
||||
.s-mag {
|
||||
color: pushBack($colorBodyFg, 20%); // TODO: do this as a theme constant
|
||||
}
|
||||
.l-btn.show-thumbs {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.s-image-main {
|
||||
background-color: $colorPlotBg;
|
||||
border: 1px solid transparent;
|
||||
&.paused {
|
||||
//@include sUnsynced();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** THUMBS */
|
||||
.l-image-thumbs-wrapper {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-bottom: $interiorMargin;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.l-image-thumb-item {
|
||||
transition: background-color 0.25s;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
padding: 1px;
|
||||
margin-left: $interiorMarginSm;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: $imageThumbsD + $imageThumbPad*2;
|
||||
white-space: normal;
|
||||
.l-thumb,
|
||||
.l-date,
|
||||
.l-time {
|
||||
display: inline-block;
|
||||
}
|
||||
.l-date,
|
||||
.l-time {
|
||||
padding: 2px 3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $colorThumbHoverBg;
|
||||
.l-date,
|
||||
.l-time {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
&.selected {
|
||||
background: $colorKeySelectedBg;
|
||||
.l-date,
|
||||
.l-time {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.l-thumb {
|
||||
background-color: rgba(#fff, 0.1);
|
||||
height: $imageThumbsD;
|
||||
width: $imageThumbsD;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** LOCAL CONTROLS */
|
||||
.c-imagery {
|
||||
display: contents;
|
||||
.h-local-controls--overlay-content {
|
||||
position: absolute;
|
||||
right: $interiorMargin; top: $interiorMargin;
|
||||
z-index: 2;
|
||||
background: $colorLocalControlOvrBg;
|
||||
border-radius: $basicCr;
|
||||
max-width: 200px;
|
||||
min-width: 100px;
|
||||
width: 35%;
|
||||
align-items: center;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
|
||||
&:before {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__lc {
|
||||
&__reset-btn {
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
&:before,
|
||||
&:after {
|
||||
border-right: 1px solid $bc;
|
||||
content:'';
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-top: 1px solid $bc;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-bottom: 1px solid $bc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************** WHEN IN FRAME */
|
||||
.c-frame .t-imagery {
|
||||
.l-image-main-wrapper {
|
||||
bottom: 0 !important;
|
||||
height: 100% !important;
|
||||
.l-image-main-controlbar .c-button {
|
||||
//font-size: 0.7em;
|
||||
}
|
||||
}
|
||||
.l-image-thumbs-wrapper,
|
||||
mct-splitter {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** MOBILE */
|
||||
body.mobile.phone {
|
||||
.t-imagery {
|
||||
.l-image-main-wrapper,
|
||||
.l-image-thumbs-wrapper {
|
||||
min-height: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-button.pause-play {
|
||||
// Pause icon set by default in markup
|
||||
|
||||
&.is-paused {
|
||||
background: $colorPausedBg !important;
|
||||
color: $colorPausedFg;
|
||||
|
||||
&:before {
|
||||
content: $glyph-icon-play;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************** CLOCKS AND TIMERS */
|
||||
.c-clock,
|
||||
.c-timer {
|
||||
|
@ -251,7 +251,7 @@
|
||||
|
||||
@mixin reverseEllipsis() {
|
||||
@include ellipsize();
|
||||
direction: rtl;
|
||||
direction: ltr;
|
||||
unicode-bidi:bidi-override;
|
||||
}
|
||||
|
||||
|
@ -108,8 +108,9 @@ tr {
|
||||
}
|
||||
|
||||
/*************************************************** STATUS */
|
||||
[class*='s-status'] {
|
||||
[class*='s-status-icon'] {
|
||||
&:before {
|
||||
font-family: symbolsfont;
|
||||
margin-right: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import _ from "lodash"
|
||||
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
|
||||
|
||||
export default {
|
||||
inject: ["openmct"],
|
||||
@ -31,6 +32,20 @@ export default {
|
||||
if (this.releaseEditModeHandler) {
|
||||
this.releaseEditModeHandler();
|
||||
}
|
||||
|
||||
if (this.unlisten) {
|
||||
this.unlisten();
|
||||
}
|
||||
|
||||
if (this.stopListeningConditionalStyles) {
|
||||
this.stopListeningConditionalStyles();
|
||||
}
|
||||
|
||||
if (this.styleRuleManager) {
|
||||
this.styleRuleManager.destroy();
|
||||
this.styleRuleManager.off('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
delete this.styleRuleManager;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||
@ -43,6 +58,11 @@ export default {
|
||||
capture: true
|
||||
});
|
||||
this.$el.addEventListener('drop', this.addObjectToParent);
|
||||
if (this.currentObject) {
|
||||
//This is to apply styles to subobjects in a layout
|
||||
this.initConditionalStyles();
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
@ -76,6 +96,15 @@ export default {
|
||||
this.clear();
|
||||
this.updateView(true);
|
||||
},
|
||||
updateStyle(styleObj) {
|
||||
if (!styleObj) {
|
||||
return;
|
||||
}
|
||||
let keys = Object.keys(styleObj);
|
||||
keys.forEach(key => {
|
||||
this.$el.style[key] = styleObj[key];
|
||||
})
|
||||
},
|
||||
updateView(immediatelySelect) {
|
||||
this.clear();
|
||||
if (!this.currentObject) {
|
||||
@ -149,8 +178,28 @@ export default {
|
||||
});
|
||||
|
||||
this.viewKey = viewKey;
|
||||
|
||||
this.initConditionalStyles();
|
||||
|
||||
this.updateView(immediatelySelect);
|
||||
},
|
||||
initConditionalStyles() {
|
||||
if (!this.styleRuleManager) {
|
||||
this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.conditionalStyle), this.openmct);
|
||||
this.styleRuleManager.on('conditionalStyleUpdated', this.updateStyle.bind(this));
|
||||
} else {
|
||||
this.styleRuleManager.updateConditionalStyleConfig(this.currentObject.configuration && this.currentObject.configuration.conditionalStyle);
|
||||
}
|
||||
|
||||
if (this.stopListeningConditionalStyles) {
|
||||
this.stopListeningConditionalStyles();
|
||||
}
|
||||
|
||||
this.stopListeningConditionalStyles = this.openmct.objects.observe(this.currentObject, 'configuration.conditionalStyle', (newConditionalStyle) => {
|
||||
//Updating conditional styles in the inspector view will trigger this so that the changes are reflected immediately
|
||||
this.styleRuleManager.updateConditionalStyleConfig(newConditionalStyle);
|
||||
});
|
||||
},
|
||||
loadComposition() {
|
||||
return this.composition.load();
|
||||
},
|
||||
|
@ -1,16 +1,25 @@
|
||||
<template>
|
||||
<label class="c-toggle-switch">
|
||||
<input
|
||||
:id="id"
|
||||
type="checkbox"
|
||||
:checked="checked"
|
||||
@change="onUserSelect($event)"
|
||||
<div class="c-toggle-switch">
|
||||
<label class="c-toggle-switch__control">
|
||||
<input
|
||||
:id="id"
|
||||
type="checkbox"
|
||||
:checked="checked"
|
||||
@change="onUserSelect($event)"
|
||||
>
|
||||
<span class="c-toggle-switch__slider"></span>
|
||||
</label>
|
||||
<div
|
||||
v-if="label && label.length"
|
||||
class="c-toggle-switch__label"
|
||||
>
|
||||
<span class="c-toggle-switch__slider"></span>
|
||||
</label>
|
||||
{{ label }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
@ -18,6 +27,11 @@ export default {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
checked: Boolean
|
||||
},
|
||||
methods: {
|
||||
@ -26,4 +40,5 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
@ -3,32 +3,19 @@
|
||||
$m: 2px;
|
||||
$br: $d/1.5;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
display: inline;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
|
||||
&__slider {
|
||||
background: $colorBtnBg; // TODO: make discrete theme constants for these colors
|
||||
border-radius: $br;
|
||||
//box-shadow: inset rgba($colorBtnFg, 0.4) 0 0 0 1px;
|
||||
display: inline-block;
|
||||
height: $d + ($m*2);
|
||||
position: relative;
|
||||
transform: translateY(2px); // TODO: get this to work without this kind of hack!
|
||||
width: $d*2 + $m*2;
|
||||
&__control,
|
||||
&__label {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&:before {
|
||||
// Knob
|
||||
background: $colorBtnFg; // TODO: make discrete theme constants for these colors
|
||||
border-radius: floor($br * 0.8);
|
||||
box-shadow: rgba(black, 0.4) 0 0 2px;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: $d; width: $d;
|
||||
top: $m; left: $m; right: auto;
|
||||
transition: transform 100ms ease-in-out;
|
||||
}
|
||||
&__control {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input {
|
||||
@ -45,4 +32,33 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__slider {
|
||||
// Sits within __switch
|
||||
background: $colorBtnBg; // TODO: make discrete theme constants for these colors
|
||||
border-radius: $br;
|
||||
display: inline-block;
|
||||
height: $d + ($m*2);
|
||||
position: relative;
|
||||
width: $d*2 + $m*2;
|
||||
|
||||
&:before {
|
||||
// Knob
|
||||
background: $colorBtnFg; // TODO: make discrete theme constants for these colors
|
||||
border-radius: floor($br * 0.8);
|
||||
box-shadow: rgba(black, 0.4) 0 0 2px;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: $d; width: $d;
|
||||
top: $m; left: $m; right: auto;
|
||||
transition: transform 100ms ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
margin-left: $interiorMarginSm;
|
||||
margin-right: $interiorMargin;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
>
|
||||
<span
|
||||
v-if="elements.length > 1 && isEditing"
|
||||
class="c-elements-pool__grippy"
|
||||
class="c-elements-pool__grippy c-grippy c-grippy--vertical-drag"
|
||||
></span>
|
||||
<object-label
|
||||
:domain-object="element"
|
||||
|
@ -25,27 +25,40 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
this.selection = selection;
|
||||
if (selection.length > 0 && selection[0].length > 0) {
|
||||
let domainObject = selection[0][0].context.item;
|
||||
let layoutItem;
|
||||
if (selection[0].length > 1) {
|
||||
//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)
|
||||
domainObject = selection[0][0].context.item;
|
||||
if (!domainObject) {
|
||||
layoutItem = selection[0][0].context.layoutItem;
|
||||
domainObject = selection[0][1].context.item;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.component) {
|
||||
this.component.$destroy();
|
||||
this.component = undefined;
|
||||
this.$el.innerHTML = '';
|
||||
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,
|
||||
layoutItem: layoutItem
|
||||
},
|
||||
el: viewContainer,
|
||||
components: {
|
||||
ConditionalStylesView
|
||||
},
|
||||
template: '<conditional-styles-view></conditional-styles-view>'
|
||||
});
|
||||
}
|
||||
|
||||
let viewContainer = document.createElement('div');
|
||||
this.$el.append(viewContainer);
|
||||
this.component = new Vue({
|
||||
provide: {
|
||||
openmct: this.openmct
|
||||
},
|
||||
el: viewContainer,
|
||||
components: {
|
||||
ConditionalStylesView
|
||||
},
|
||||
template: '<conditional-styles-view></conditional-styles-view>'
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__grippy {
|
||||
.c-grippy {
|
||||
$d: 8px;
|
||||
@include grippy($c: $colorItemTreeVC, $dir: 'y');
|
||||
flex: 0 0 auto;
|
||||
margin-right: $interiorMarginSm;
|
||||
transform: translateY(-2px);
|
||||
|
@ -62,6 +62,8 @@
|
||||
}
|
||||
|
||||
&[class*="--horizontal"] {
|
||||
padding-left: $interiorMargin;
|
||||
padding-right: $interiorMargin;
|
||||
&.l-pane--collapsed {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
@ -69,9 +71,11 @@
|
||||
}
|
||||
|
||||
&[class*="--vertical"] {
|
||||
padding-top: $interiorMargin;
|
||||
padding-bottom: $interiorMargin;
|
||||
&.l-pane--collapsed {
|
||||
padding-top: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,6 +281,9 @@
|
||||
/************************** Vertical Splitter Before */
|
||||
// Pane collapses downward. Used by Elements pool in Inspector
|
||||
&[class*="-before"] {
|
||||
$m: $interiorMarginLg;
|
||||
margin-top: $m;
|
||||
padding-top: $m;
|
||||
> .l-pane__handle {
|
||||
top: 0;
|
||||
transform: translateY(floor($splitterHandleD / -1));
|
||||
|
@ -2,7 +2,7 @@
|
||||
<li class="c-tree__item-h">
|
||||
<div
|
||||
class="c-tree__item"
|
||||
:class="{ 'is-alias': isAlias, 'is-navigated-object': isNavigated }"
|
||||
:class="{ 'is-alias': isAlias, 'is-navigated-object': navigated }"
|
||||
>
|
||||
<view-control
|
||||
v-model="expanded"
|
||||
@ -40,6 +40,8 @@
|
||||
import viewControl from '../components/viewControl.vue';
|
||||
import ObjectLabel from '../components/ObjectLabel.vue';
|
||||
|
||||
const LOCAL_STORAGE_KEY__TREE_EXPANDED = 'mct-tree-expanded';
|
||||
|
||||
export default {
|
||||
name: 'TreeItem',
|
||||
inject: ['openmct'],
|
||||
@ -54,12 +56,12 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
this.navigateToPath = this.buildPathString(this.node.navigateToParent)
|
||||
this.navigateToPath = this.buildPathString(this.node.navigateToParent);
|
||||
return {
|
||||
hasChildren: false,
|
||||
isLoading: false,
|
||||
loaded: false,
|
||||
isNavigated: this.navigateToPath === this.openmct.router.currentLocation.path,
|
||||
navigated: this.navigateToPath === this.openmct.router.currentLocation.path,
|
||||
children: [],
|
||||
expanded: false
|
||||
}
|
||||
@ -75,7 +77,7 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
expanded(isExpanded) {
|
||||
expanded() {
|
||||
if (!this.hasChildren) {
|
||||
return;
|
||||
}
|
||||
@ -86,6 +88,7 @@ export default {
|
||||
this.composition.load().then(this.finishLoading);
|
||||
this.isLoading = true;
|
||||
}
|
||||
this.setLocalStorageExpanded(this.navigateToPath);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -107,6 +110,17 @@ export default {
|
||||
}
|
||||
|
||||
this.openmct.router.on('change:path', this.highlightIfNavigated);
|
||||
|
||||
this.getLocalStorageExpanded();
|
||||
},
|
||||
beforeDestroy() {
|
||||
/****
|
||||
* calling this.setLocalStorageExpanded explicitly here because for whatever reason,
|
||||
* the watcher on this.expanded is not triggering this.setLocalStorageExpanded(),
|
||||
* even though Vue documentation states, "At this stage the instance is still fully functional."
|
||||
*****/
|
||||
this.expanded = false;
|
||||
this.setLocalStorageExpanded();
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.router.off('change:path', this.highlightIfNavigated);
|
||||
@ -139,10 +153,40 @@ export default {
|
||||
},
|
||||
highlightIfNavigated(newPath, oldPath) {
|
||||
if (newPath === this.navigateToPath) {
|
||||
this.isNavigated = true;
|
||||
this.navigated = true;
|
||||
} else if (oldPath === this.navigateToPath) {
|
||||
this.isNavigated = false;
|
||||
this.navigated = false;
|
||||
}
|
||||
},
|
||||
getLocalStorageExpanded() {
|
||||
let expandedPaths = localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED);
|
||||
|
||||
if (expandedPaths) {
|
||||
expandedPaths = JSON.parse(expandedPaths);
|
||||
this.expanded = expandedPaths.includes(this.navigateToPath);
|
||||
}
|
||||
},
|
||||
// expanded nodes/paths are stored in local storage as an array
|
||||
setLocalStorageExpanded() {
|
||||
let expandedPaths = localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED);
|
||||
expandedPaths = expandedPaths ? JSON.parse(expandedPaths) : [];
|
||||
|
||||
if (this.expanded) {
|
||||
if (!expandedPaths.includes(this.navigateToPath)) {
|
||||
expandedPaths.push(this.navigateToPath);
|
||||
}
|
||||
} else {
|
||||
// remove this node path and all children paths from stored expanded paths
|
||||
expandedPaths = expandedPaths.filter(path => !path.startsWith(this.navigateToPath));
|
||||
}
|
||||
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY__TREE_EXPANDED, JSON.stringify(expandedPaths));
|
||||
},
|
||||
removeLocalStorageExpanded() {
|
||||
let expandedPaths = localStorage.getItem(LOCAL_STORAGE_KEY__TREE_EXPANDED);
|
||||
expandedPaths = expandedPaths ? JSON.parse(expandedPaths) : [];
|
||||
expandedPaths = expandedPaths.filter(path => !path.startsWith(this.navigateToPath));
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY__TREE_EXPANDED, JSON.stringify(expandedPaths));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user