Compare commits

...

29 Commits

Author SHA1 Message Date
charlesh88
d9e2125bbc Merge branch 'topic-conditionals' into conditionals-styling-2 2020-03-17 15:51:22 -07:00
Shefali Joshi
43a82ec05f Conditional styles for drawing objects (#2740)
* Hardcoded prototype - conditional styles for display layout or generator domain objects only.
* Adds background colors list
* Show conditional styles for subObjectViews and telemetryViews
* Uses telemetry provider to determine which style is active
* Removes hardcoded conditionSet work used for prototype
* Add default styles to conditionalStyles when a condition set is added to the domain object.
* Use EventEmitter alias instead of eventEmitter3 in imports
* Change StyleRuleManager to accept conditionStyle objects instead of domainObjects
* Uses a map for conditional styles instead of a list in order to improve performance
* Use in-built api to check for identifier equality
* Removes unnecessary object get call.
* Adds conditional styles for drawing objects
* Removes hard coded conditionSetIdentifier
* Fixes small conditionManager bug
2020-03-17 14:42:15 -07:00
David Tsay
fe2e29d69b Merge pull request #2753 from nasa/remove-ConditionSetView-telemetry
Remove telemetry subscription from conditionSet edit view
2020-03-17 13:49:25 -07:00
Joshi
5d21a8b6fe Merge branch 'topic-conditionals' of https://github.com/nasa/openmct into remove-ConditionSetView-telemetry 2020-03-17 11:06:51 -07:00
Shefali Joshi
500ab52476 Merge pull request #2754 from nasa/conditions-observables
[Conditions] Remove uneccessary update function
2020-03-17 11:05:46 -07:00
charlesh88
c7b326e3f2 Merge branch 'topic-conditionals' into conditionals-styling-2 2020-03-16 16:06:05 -07:00
David Tsay
e339d743ed remove uneccessary update call 2020-03-16 16:01:12 -07:00
Joshi
6ab60ab52e ConditionSet view listens to a listener from conditionCollection to display current output. 2020-03-16 15:00:16 -07:00
David Tsay
55e5c49f6e Merge pull request #2747 from nasa/conditions-do-refactor
Conditions refactor - not domain objects anymore
2020-03-16 10:59:31 -07:00
Joshi
f090f7ffe7 Removes comment. 2020-03-16 10:51:33 -07:00
Joshi
94b5617e63 Since we're observing for changes to the conditionSet domain object, we don't need to re-fetch it with persist. 2020-03-16 10:50:11 -07:00
David Tsay
41c79c6032 [Conditions] ConditionManager should observe its own mutations (#2748)
* change blur listener to change listener to watch for immediate updates to input

* move observe ConditionManager logic into ConditionManager
2020-03-16 10:14:46 -07:00
Joshi
83c648cc26 Addresses comments 2020-03-13 14:24:47 -07:00
Joshi
76e7fec1a0 Ensures the conditionManager is in sync with outside changes so that composition is not thrown away. 2020-03-13 14:08:14 -07:00
Joshi
09bfd80f69 Fix bug with telemetry disappearing 2020-03-13 12:50:06 -07:00
Joshi
8f05c57d1a Fixes clone condition bug 2020-03-13 09:09:50 -07:00
Joshi
81caa27cba Fixes failing tests 2020-03-12 14:38:39 -07:00
Joshi
74a7ef2565 Fix bug with removeCondition 2020-03-12 14:06:43 -07:00
Joshi
649575fd2d Moves domain object observe logic to the condition set telemetry provider 2020-03-12 13:29:50 -07:00
Joshi
625b39d722 Fix typo 2020-03-12 13:06:56 -07:00
Joshi
65f80f4c45 Resubscribe to the conditionSet telemetry provider when the condition set domain object changes. 2020-03-12 13:05:03 -07:00
Joshi
02cd3048c8 Removed conditions as domain objects 2020-03-12 12:33:04 -07:00
charlesh88
f6d2f9f578 Merge remote-tracking branch 'origin' into conditionals-styling-2 2020-03-12 09:28:09 -07:00
charlesh88
408e27c796 Conditionals styling WIP
- Tweaks for layout/overflow issues;
- Better header alignment;
- Much better approach to "accordion" spacing;
2020-03-09 14:44:50 -07:00
David Tsay
a7e57c62f4 linting fixes (#2733) 2020-03-09 11:15:15 -07:00
Deep Tailor
5fcc4eebe1 Add a re-calculate column width button (#2719)
* Add a recalculate Column width button

* Tweaks to telemetry table for recalculateColumnWidths

- Recalc button now hidden if isAutosizeEnabled === false;
- Recalc button label, title edited for clarity;
- Normalized button titles for other table buttons;
- Fixed `.c-separator` height issue;

* toggle between expand and autosize

* Tweaked button title text

* remove nested loop

* fix lint errors

* remove unecessary promise and use clientWidth instead of offsetWidth

Co-authored-by: charlesh88 <charlesh88@gmail.com>
2020-03-09 10:31:26 -07:00
Charles Hacskaylo
27a09239e3 Integrate Conditionals styling into topic-conditionals (try 2) (#2730)
* Styling for Conditionals WIP

- Condition Set markup and styling WIP;

* Styling for Conditionals WIP

- Condition Set markup and styling WIP;

* Styling for Conditionals WIP

- Main layout and container styling refinement, simplification and
normalization;

* Styling for Conditionals WIP

- Begin styling for individual condition elements;

* Styling for Conditionals WIP

- Condition styling, very initial;

* Conditionals styling WIP

- Redo work done previously and lost due to merging;
- Overall layout in edit mode;
- Styling for hint element;

* Conditionals styling WIP

- Major progress on Conditionals edit-view styling;
- Grid layout WIP in condition element;
- Added new `.c-grippy` class;

* Conditionals styling WIP

- discreteItem theme constants refined, add
`$colorDiscreteItemCurrentBg` color value;
- `.c-grippy` enhanced;
- Condition layout significantly refined;

* Conditionals styling WIP

- Styling for browse view in Condition Set;
- Refined alignment and styles for condition header;

* Conditionals styling WIP

- Cleanups;
- Significant improvements to flex layout;
- Test Data layout and element formatting;

* Conditionals styling WIP

- Better approach to condition set hinting;

* Conditionals styling WIP

- Merge and integrate changes from Joel;
- 'Add Criteria' button now disabled until telemetry has been added;
- Fix JS configuration error with help from Joel;

* Conditionals styling WIP

- Much better flex approach to sections layout;
- Sanding and shimming;

* Conditionals styling WIP

- Fixed some linting;

* Conditionals styling WIP

- Tweaks for layouts issues;
2020-03-09 10:08:39 -07:00
Nikhil
8d86c914a1 Merge pull request #2732 from nasa/minmax-fix-382020
Revert minMax to minmax
2020-03-09 10:05:42 -07:00
Deep Tailor
fab04519c6 fix minmax typo 2020-03-08 20:11:04 -07:00
36 changed files with 1000 additions and 927 deletions

View File

@@ -28,10 +28,7 @@ import {computeCondition} from "./utils/evaluator";
/*
* conditionConfiguration = {
* identifier: {
* key: '',
* namespace: ''
* },
* id: uuid,
* trigger: 'any'/'all',
* criteria: [
* {
@@ -48,14 +45,14 @@ export default class ConditionClass extends EventEmitter {
/**
* Manages criteria and emits the result of - true or false - based on criteria evaluated.
* @constructor
* @param conditionConfiguration: {identifier: {domainObject.identifier},trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
* @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
* @param openmct
*/
constructor(conditionConfiguration, openmct) {
super();
this.openmct = openmct;
this.id = this.openmct.objects.makeKeyString(conditionConfiguration.identifier);
this.id = conditionConfiguration.id;
this.criteria = [];
this.criteriaResults = {};
this.result = undefined;
@@ -63,16 +60,11 @@ export default class ConditionClass extends EventEmitter {
this.createCriteria(conditionConfiguration.configuration.criteria);
}
this.trigger = conditionConfiguration.configuration.trigger;
this.openmct.objects.get(this.id).then(obj => this.observeForChanges(obj));
}
observeForChanges(conditionDO) {
this.stopObservingForChanges = this.openmct.objects.observe(conditionDO, '*', this.update.bind(this));
}
update(newDomainObject) {
this.updateTrigger(newDomainObject.configuration.trigger);
this.updateCriteria(newDomainObject.configuration.criteria);
update(conditionConfiguration) {
this.updateTrigger(conditionConfiguration.configuration.trigger);
this.updateCriteria(conditionConfiguration.configuration.criteria);
}
updateTrigger(trigger) {

View File

@@ -25,71 +25,43 @@ import uuid from "uuid";
import EventEmitter from 'EventEmitter';
export default class ConditionManager extends EventEmitter {
constructor(domainObject, openmct) {
constructor(conditionSetDomainObject, openmct) {
super();
this.openmct = openmct;
this.domainObject = domainObject;
this.conditionSetDomainObject = conditionSetDomainObject;
this.timeAPI = this.openmct.time;
this.latestTimestamp = {};
this.instantiate = this.openmct.$injector.get('instantiate');
this.initialize();
this.stopObservingForChanges = this.openmct.objects.observe(this.conditionSetDomainObject, '*', (newDomainObject) => {
this.conditionSetDomainObject = newDomainObject;
});
}
initialize() {
this.conditionResults = {};
this.observeForChanges(this.domainObject);
this.conditionCollection = [];
if (this.domainObject.configuration.conditionCollection.length) {
this.domainObject.configuration.conditionCollection.forEach((conditionConfigurationId, index) => {
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
this.initCondition(conditionConfiguration, index)
});
this.conditionClassCollection = [];
if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
this.initCondition(conditionConfiguration, index);
});
} else {
this.addCondition(true);
}
}
observeForChanges(domainObject) {
//TODO: Observe only the conditionCollection property instead of the whole domainObject
this.stopObservingForChanges = this.openmct.objects.observe(domainObject, '*', this.handleConditionCollectionUpdated.bind(this));
}
handleConditionCollectionUpdated(newDomainObject) {
let oldConditionIdentifiers = this.domainObject.configuration.conditionCollection.map((conditionConfigurationId) => {
return this.openmct.objects.makeKeyString(conditionConfigurationId);
});
let newConditionIdentifiers = newDomainObject.configuration.conditionCollection.map((conditionConfigurationId) => {
return this.openmct.objects.makeKeyString(conditionConfigurationId);
});
this.domainObject = newDomainObject;
//check for removed conditions
oldConditionIdentifiers.forEach((identifier, index) => {
if (newConditionIdentifiers.indexOf(identifier) < 0) {
this.removeCondition(identifier);
}
});
let newConditionCount = this.domainObject.configuration.conditionCollection.length - this.conditionCollection.length;
for (let i = 0; i < newConditionCount; i++) {
let conditionConfigurationId = this.domainObject.configuration.conditionCollection[i];
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
this.initCondition(conditionConfiguration, i);
});
}
updateCondition(conditionConfiguration, index) {
let condition = this.conditionClassCollection[index];
condition.update(conditionConfiguration);
this.conditionSetDomainObject.configuration.conditionCollection[index] = conditionConfiguration;
this.persistConditions();
}
initCondition(conditionConfiguration, index) {
let condition = new Condition(conditionConfiguration, this.openmct);
condition.on('conditionResultUpdated', this.handleConditionResult.bind(this));
if (index !== undefined) {
this.conditionCollection.splice(index + 1, 0, condition);
this.conditionClassCollection.splice(index + 1, 0, condition);
} else {
this.conditionCollection.unshift(condition);
this.conditionClassCollection.unshift(condition);
}
//There are no criteria for a default condition and hence no subscriptions.
//Hence the conditionResult must be manually triggered for it.
@@ -98,30 +70,25 @@ export default class ConditionManager extends EventEmitter {
}
}
createConditionDomainObject(isDefault, conditionConfiguration) {
createCondition(conditionConfiguration) {
let conditionObj;
if (conditionConfiguration) {
conditionObj = {
...conditionConfiguration,
name: `Copy of ${conditionConfiguration.name}`,
identifier: {
...this.domainObject.identifier,
key: uuid()
id: uuid(),
configuration: {
...conditionConfiguration.configuration,
name: `Copy of ${conditionConfiguration.configuration.name}`
}
};
} else {
conditionObj = {
isDefault: isDefault,
type: 'condition',
identifier: {
...this.domainObject.identifier,
key: uuid()
},
id: uuid(),
configuration: {
name: isDefault ? 'Default' : 'Unnamed Condition',
name: 'Unnamed Condition',
output: 'false',
trigger: 'all',
criteria: isDefault ? [] : [{
criteria: [{
telemetry: '',
operation: '',
input: [],
@@ -131,112 +98,88 @@ export default class ConditionManager extends EventEmitter {
summary: ''
};
}
let conditionDomainObjectKeyString = this.openmct.objects.makeKeyString(conditionObj.identifier);
let newDomainObject = this.instantiate(conditionObj, conditionDomainObjectKeyString);
return newDomainObject.useCapability('adapter');
return conditionObj;
}
addCondition(isDefault, index) {
this.createAndSaveConditionDomainObject(!!isDefault, index);
addCondition() {
this.createAndSaveCondition();
}
cloneCondition(conditionConfigurationId, index) {
this.openmct.objects.get(conditionConfigurationId).then((conditionConfiguration) => {
this.createAndSaveConditionDomainObject(false, index, conditionConfiguration);
});
cloneCondition(conditionConfiguration, index) {
this.createAndSaveCondition(index, conditionConfiguration);
}
createAndSaveConditionDomainObject(isDefault, index, conditionConfiguration) {
let newConditionDomainObject = this.createConditionDomainObject(isDefault, conditionConfiguration);
//persist the condition domain object so that we can do an openmct.objects.get on it and only persist the identifier in the conditionCollection of conditionSet
this.openmct.objects.mutate(newConditionDomainObject, 'created', new Date());
createAndSaveCondition(index, conditionConfiguration) {
let newCondition = this.createCondition(conditionConfiguration);
if (index !== undefined) {
this.domainObject.configuration.conditionCollection.splice(index + 1, 0, newConditionDomainObject.identifier);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index + 1, 0, newCondition);
} else {
this.domainObject.configuration.conditionCollection.unshift(newConditionDomainObject.identifier);
this.conditionSetDomainObject.configuration.conditionCollection.unshift(newCondition);
}
this.persist();
this.initCondition(newCondition, index);
this.persistConditions();
}
removeCondition(identifier) {
let found = this.findConditionById(identifier);
if (found) {
let index = found.index;
let condition = this.conditionCollection[index];
let conditionIdAsString = condition.id;
condition.destroyCriteria();
condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
this.conditionCollection.splice(index, 1);
this.domainObject.configuration.conditionCollection.splice(index, 1);
if (this.conditionResults[conditionIdAsString] !== undefined) {
delete this.conditionResults[conditionIdAsString];
}
this.persist();
this.handleConditionResult();
removeCondition(index) {
let condition = this.conditionClassCollection[index];
condition.destroyCriteria();
condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
this.conditionClassCollection.splice(index, 1);
this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
if (this.conditionResults[condition.id] !== undefined) {
delete this.conditionResults[condition.id];
}
this.persistConditions();
this.handleConditionResult();
}
findConditionById(identifier) {
let found;
for (let i=0, ii=this.conditionCollection.length; i < ii; i++) {
if (this.conditionCollection[i].id === this.openmct.objects.makeKeyString(identifier)) {
found = {
item: this.conditionCollection[i],
index: i
};
break;
}
}
return found;
findConditionById(id) {
return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
}
//this.$set(this.conditionCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);
//this.$set(this.conditionClassCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);
reorderConditions(reorderPlan) {
let oldConditions = Array.from(this.domainObject.configuration.conditionCollection);
let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
let newCollection = [];
reorderPlan.forEach((reorderEvent) => {
let item = oldConditions[reorderEvent.oldIndex];
newCollection.push(item);
this.domainObject.configuration.conditionCollection = newCollection;
this.conditionSetDomainObject.configuration.conditionCollection = newCollection;
});
this.persist();
this.persistConditions();
}
handleConditionResult(resultObj) {
let conditionCollection = this.domainObject.configuration.conditionCollection;
let currentConditionIdentifier = conditionCollection[conditionCollection.length-1];
const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
let currentCondition = conditionCollection[conditionCollection.length-1];
if (resultObj) {
let idAsString = this.openmct.objects.makeKeyString(resultObj.id);
if (this.findConditionById(idAsString)) {
this.conditionResults[idAsString] = resultObj.data.result;
const id = resultObj.id;
if (this.findConditionById(id)) {
this.conditionResults[id] = resultObj.data.result;
}
this.updateTimestamp(resultObj.data);
}
for (let i = 0; i < conditionCollection.length - 1; i++) {
let conditionIdAsString = this.openmct.objects.makeKeyString(conditionCollection[i]);
if (this.conditionResults[conditionIdAsString]) {
if (this.conditionResults[conditionCollection[i].id]) {
//first condition to be true wins
currentConditionIdentifier = conditionCollection[i];
currentCondition = conditionCollection[i];
break;
}
}
this.openmct.objects.get(currentConditionIdentifier).then((obj) => {
this.emit('conditionSetResultUpdated',
Object.assign(
{
output: obj.configuration.output,
id: this.domainObject.identifier,
conditionId: currentConditionIdentifier
},
this.latestTimestamp
)
this.emit('conditionSetResultUpdated',
Object.assign(
{
output: currentCondition.configuration.output,
id: this.conditionSetDomainObject.identifier,
conditionId: currentCondition.id
},
this.latestTimestamp
)
});
)
}
updateTimestamp(timestamp) {
@@ -249,15 +192,15 @@ export default class ConditionManager extends EventEmitter {
});
}
persist() {
this.openmct.objects.mutate(this.domainObject, 'configuration.conditionCollection', this.domainObject.configuration.conditionCollection);
persistConditions() {
this.openmct.objects.mutate(this.conditionSetDomainObject, 'configuration.conditionCollection', this.conditionSetDomainObject.configuration.conditionCollection);
}
destroy() {
if (typeof this.stopObservingForChanges === 'function') {
if(this.stopObservingForChanges) {
this.stopObservingForChanges();
}
this.conditionCollection.forEach((condition) => {
this.conditionClassCollection.forEach((condition) => {
condition.off('conditionResultUpdated', this.handleConditionResult);
condition.destroy();
})

View File

@@ -27,6 +27,13 @@ describe('ConditionManager', () => {
let conditionMgr;
let mockListener;
let openmct = {};
let mockCondition = {
isDefault: true,
id: '1234-5678',
configuration: {
criteria: []
}
};
let conditionSetDomainObject = {
identifier: {
namespace: "",
@@ -35,15 +42,9 @@ describe('ConditionManager', () => {
type: "conditionSet",
location: "mine",
configuration: {
conditionCollection: []
}
};
let mockConditionDomainObject = {
isDefault: true,
type: 'condition',
identifier: {
namespace: '',
key: '1234-5678'
conditionCollection: [
mockCondition
]
}
};
@@ -55,7 +56,7 @@ describe('ConditionManager', () => {
let mockDomainObject = {
useCapability: function () {
return mockConditionDomainObject;
return mockCondition;
}
};
mockInstantiate.and.callFake(function () {
@@ -78,7 +79,7 @@ describe('ConditionManager', () => {
openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) {
resolve(conditionSetDomainObject);
}), new Promise(function (resolve, reject) {
resolve(mockConditionDomainObject);
resolve(mockCondition);
}));
openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key);
openmct.objects.observe.and.returnValue(function () {});
@@ -90,9 +91,9 @@ describe('ConditionManager', () => {
});
it('creates a conditionCollection with a default condition', function () {
expect(conditionMgr.domainObject.configuration.conditionCollection.length).toEqual(1);
let defaultConditionIdentifier = conditionMgr.domainObject.configuration.conditionCollection[0];
expect(defaultConditionIdentifier).toEqual(mockConditionDomainObject.identifier);
expect(conditionMgr.conditionSetDomainObject.configuration.conditionCollection.length).toEqual(1);
let defaultConditionId = conditionMgr.conditionClassCollection[0].id;
expect(defaultConditionId).toEqual(mockCondition.id);
});
});

View File

@@ -1,3 +1,25 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import ConditionManager from './ConditionManager'
export default class ConditionSetTelemetryProvider {

View File

@@ -64,6 +64,7 @@ describe("The condition", function () {
openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
testConditionDefinition = {
id: '123-456',
configuration: {
trigger: TRIGGER.ANY,
criteria: [

View File

@@ -34,7 +34,6 @@ export default class StyleRuleManager extends EventEmitter {
initialize(conditionalStyleConfiguration) {
this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier;
this.defaultStyle = conditionalStyleConfiguration.defaultStyle;
this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []);
}
@@ -64,15 +63,13 @@ export default class StyleRuleManager extends EventEmitter {
updateConditionStylesMap(conditionStyles) {
let conditionStyleMap = {};
conditionStyles.forEach((conditionStyle) => {
const identifier = this.openmct.objects.makeKeyString(conditionStyle.conditionIdentifier);
conditionStyleMap[identifier] = conditionStyle.style;
conditionStyleMap[conditionStyle.conditionId] = conditionStyle.style;
});
this.conditionalStyleMap = conditionStyleMap;
}
handleConditionSetResultUpdated(resultData) {
let identifier = this.openmct.objects.makeKeyString(resultData.conditionId);
let foundStyle = this.conditionalStyleMap[identifier];
let foundStyle = this.conditionalStyleMap[resultData.conditionId];
if (foundStyle) {
if (foundStyle !== this.currentStyle) {
this.currentStyle = foundStyle;
@@ -91,7 +88,11 @@ export default class StyleRuleManager extends EventEmitter {
}
destroy() {
this.currentStyle = this.defaultStyle;
if (this.currentStyle) {
Object.keys(this.currentStyle).forEach(key => {
this.currentStyle[key] = 'inherit';
});
}
this.updateDomainObjectStyle();
if (this.stopProvidingTelemetry) {
this.stopProvidingTelemetry();

View File

@@ -1,178 +1,166 @@
/*****************************************************************************
* 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"
>
<!-- Edit view -->
<div class="c-condition__header">
<span class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
title="Drag to reorder conditions"
:class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
:draggable="!condition.isDefault"
@dragstart="dragStart"
@dragstop="dragStop"
@dragover.stop
></span>
<span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
:class="{ 'c-disclosure-triangle--expanded': expanded }"
@click="expanded = !expanded"
></span>
<span class="c-condition__name">{{ condition.configuration.name }}</span>
<!-- TODO: description should be derived from criteria -->
<span class="c-condition__summary">
Description/summary goes here {{ condition.configuration.description }}
</span>
<div class="c-condition__buttons">
<button v-if="!condition.isDefault"
class="c-click-icon c-condition__duplicate-button icon-duplicate"
title="Duplicate this condition"
@click="cloneCondition"
></button>
<button v-if="!condition.isDefault"
class="c-click-icon c-condition__delete-button icon-trash"
title="Delete this condition"
@click="removeCondition"
></button>
</div>
<div 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="condition.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="condition.configuration.output"
class="t-condition-name-input"
type="text"
@blur="persist"
>
</span>
<div v-if="!condition.isDefault"
class="c-cdef__match-and-criteria"
>
<span class="c-cdef__separator c-row-separator"></span>
<span class="c-cdef__label">Match</span>
<span class="c-cdef__controls">
<select v-model="condition.configuration.trigger"
@change="persist"
>
<option 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 condition.configuration.criteria"
:key="index"
class="c-cdef__criteria"
>
<Criterion :telemetry="telemetry"
:criterion="criterion"
:index="index"
:trigger="condition.configuration.trigger"
:is-default="condition.configuration.criteria.length === 1"
@persist="persist"
/>
<div class="c-cdef__criteria__buttons">
<button class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
title="Duplicate this criteria"
@click="cloneCriterion(index)"
></button>
<button v-if="!(condition.configuration.criteria.length === 1)"
class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
title="Delete this criteria"
@click="removeCriterion(index)"
></button>
</div>
</div>
</template>
<div class="c-cdef__separator c-row-separator"></div>
<div class="c-cdef__controls"
:disabled="!telemetry.length"
>
<button
class="c-cdef__add-criteria-button c-button c-button--labeled icon-plus"
@click="addCriteria"
>
<span class="c-button__label">Add Criteria</span>
</button>
</div>
</div>
</div>
</div>
<div v-else>
<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"
>
<!-- Browse view -->
<div class="c-condition__header">
<span class="c-condition__name">
{{ condition.configuration.name }}
</span>
<span class="c-condition__output">
Output: {{ condition.configuration.output }}
</span>
</div>
<div class="c-condition__summary">
Description/summary goes here {{ condition.configuration.description }}
</div>
</div>
</template>
@@ -186,11 +174,7 @@ export default {
Criterion
},
props: {
conditionIdentifier: {
type: Object,
required: true
},
currentConditionIdentifier: {
condition: {
type: Object,
required: true
},
@@ -210,7 +194,6 @@ export default {
},
data() {
return {
domainObject: this.domainObject,
currentCriteria: this.currentCriteria,
expanded: true,
trigger: 'all',
@@ -223,17 +206,11 @@ export default {
this.destroy();
},
mounted() {
this.openmct.objects.get(this.conditionIdentifier).then((domainObject => {
this.domainObject = domainObject;
this.initialize();
}));
this.setOutputSelection();
},
methods: {
initialize() {
this.setOutputSelection();
},
setOutputSelection() {
let conditionOutput = this.domainObject.configuration.output;
let conditionOutput = this.condition.configuration.output;
if (conditionOutput) {
if (conditionOutput !== 'false' && conditionOutput !== 'true') {
this.selectedOutputSelection = 'string';
@@ -244,9 +221,9 @@ export default {
},
setOutputValue() {
if (this.selectedOutputSelection === 'string') {
this.domainObject.configuration.output = '';
this.condition.configuration.output = '';
} else {
this.domainObject.configuration.output = this.selectedOutputSelection;
this.condition.configuration.output = this.selectedOutputSelection;
}
this.persist();
},
@@ -257,12 +234,12 @@ export default {
input: '',
metadata: ''
};
this.domainObject.configuration.criteria.push(criteriaObject);
this.condition.configuration.criteria.push(criteriaObject);
},
dragStart(e) {
e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
e.dataTransfer.effectAllowed = "copyMove";
e.dataTransfer.setDragImage(e.target.closest('.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) {
@@ -271,35 +248,36 @@ export default {
destroy() {
},
removeCondition(ev) {
this.$emit('removeCondition', this.conditionIdentifier);
this.$emit('removeCondition', this.conditionIndex);
},
cloneCondition(ev) {
this.$emit('cloneCondition', {
identifier: this.conditionIdentifier,
index: Number(ev.target.closest('.widget-condition').getAttribute('data-condition-index'))
condition: this.condition,
index: this.conditionIndex
});
},
removeCriterion(index) {
this.domainObject.configuration.criteria.splice(index, 1);
this.condition.configuration.criteria.splice(index, 1);
this.persist()
},
cloneCriterion(index) {
const clonedCriterion = {...this.domainObject.configuration.criteria[index]};
this.domainObject.configuration.criteria.splice(index + 1, 0, clonedCriterion);
const clonedCriterion = {...this.condition.configuration.criteria[index]};
this.condition.configuration.criteria.splice(index + 1, 0, clonedCriterion);
this.persist()
},
hasTelemetry(identifier) {
// TODO: check parent domainObject.composition.hasTelemetry
// TODO: check parent condition.composition.hasTelemetry
return this.currentCriteria && identifier;
},
persist() {
this.openmct.objects.mutate(this.domainObject, 'configuration', this.domainObject.configuration);
this.$emit('updateCondition', {
condition: this.condition,
index: this.conditionIndex
});
},
initCap: function (string) {
return string.charAt(0).toUpperCase() + string.slice(1)
initCap: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
}
}
</script>

View File

@@ -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="(condition, index) in conditionCollection"
:key="condition.id"
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="condition"
:condition-index="index"
:telemetry="telemetryObjs"
:is-editing="isEditing"
@updateCondition="updateCondition"
@removeCondition="removeCondition"
@cloneCondition="cloneCondition"
@setMoveIndex="setMoveIndex"
/>
</div>
</div>
</div>
</section>
@@ -96,11 +95,9 @@ export default {
data() {
return {
expanded: true,
parentKeyString: this.openmct.objects.makeKeyString(this.domainObject.identifier),
conditionCollection: [],
conditionResults: {},
conditions: [],
currentConditionIdentifier: this.currentConditionIdentifier || {},
telemetryObjs: [],
moveIndex: Number,
isDragging: false
@@ -112,7 +109,7 @@ export default {
if(this.conditionManager) {
this.conditionManager.destroy();
}
if (typeof this.stopObservingForChanges === 'function') {
if (this.stopObservingForChanges) {
this.stopObservingForChanges();
}
},
@@ -122,13 +119,16 @@ export default {
this.composition.on('remove', this.removeTelemetryObject);
this.composition.load();
this.conditionCollection = this.domainObject.configuration.conditionCollection;
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
this.observeForChanges();
this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
this.conditionManager.on('conditionSetResultUpdated', (data) => {
this.$emit('conditionSetResultUpdated', data);
})
},
methods: {
observeForChanges() {
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, '*', (newDomainObject) => {
this.conditionCollection = newDomainObject.configuration.conditionCollection;
this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
this.conditionCollection = newConditionCollection;
});
},
setMoveIndex(index) {
@@ -190,20 +190,20 @@ export default {
this.telemetryObjs.splice(index, 1);
}
},
addCondition(event, isDefault, index) {
this.conditionManager.addCondition(!!isDefault, index);
addCondition() {
this.conditionManager.addCondition();
},
updateCurrentCondition(identifier) {
this.currentConditionIdentifier = identifier;
updateCondition(data) {
this.conditionManager.updateCondition(data.condition, data.index);
},
removeCondition(identifier) {
this.conditionManager.removeCondition(identifier);
removeCondition(index) {
this.conditionManager.removeCondition(index);
},
reorder(reorderPlan) {
this.conditionManager.reorderConditions(reorderPlan);
},
cloneCondition(condition) {
this.conditionManager.cloneCondition(condition.identifier, condition.index);
cloneCondition(data) {
this.conditionManager.cloneCondition(data.condition, data.index);
}
}
}

View File

@@ -21,24 +21,21 @@
*****************************************************************************/
<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"
@conditionSetResultUpdated="updateCurrentOutput" />
</div>
</template>
@@ -62,7 +59,6 @@ export default {
},
mounted() {
this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.provideTelemetry();
},
beforeDestroy() {
if (this.stopProvidingTelemetry) {
@@ -72,10 +68,6 @@ export default {
methods: {
updateCurrentOutput(currentConditionResult) {
this.currentConditionOutput = currentConditionResult.output;
},
provideTelemetry() {
this.stopProvidingTelemetry = this.openmct.telemetry
.subscribe(this.domainObject, output => { this.updateCurrentOutput(output); });
}
}
};

View File

@@ -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,11 +47,12 @@
</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"
@change="persist"
>
<span v-if="inputIndex < inputCount-1">and</span>
</span>

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

View File

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

View File

@@ -1,208 +1,120 @@
.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;
min-width: 400px;
.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 {
$h: 22px;
display: flex;
align-items: start;
align-content: stretch;
overflow: hidden;
min-height: $h;
line-height: $h;
&--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;
&__drag-grippy {
transform: translateY(50%);
}
&:before {
content: $glyph-icon-menu-hamburger;
&__name {
font-weight: bold;
align-self: baseline; // Fixes bold line-height offset problem
}
&__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;
}
}

View File

@@ -1,23 +1,34 @@
<template>
<div>
<div v-if="condition"
<div v-if="conditionStyle"
class="holder c-c-button-wrapper align-left"
>
<div>{{ condition.configuration.name }}</div>
<div>{{ conditionStyle.conditionName }}</div>
<span :style="conditionStyle.style">ABC</span>
<toolbar-color-picker v-if="conditionStyle.style.border"
:options="borderColorOption"
@change="updateStyleValue"
/>
<toolbar-color-picker v-if="conditionStyle.style.backgroundColor"
:options="backgroundColorOption"
@change="updateStyleValue"
/>
</div>
</div>
</template>
<script>
import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue";
export default {
components: {
ToolbarColorPicker
},
inject: [
'openmct'
],
props: {
conditionIdentifier: {
conditionStyle: {
type: Object,
required: true
}
@@ -27,12 +38,32 @@ export default {
condition: null
}
},
destroyed() {
computed: {
borderColorOption() {
return {
icon: 'icon-line-horz',
title: 'Set border color',
value: this.conditionStyle.style.border.replace('1px solid ', ''),
property: 'border'
}
},
backgroundColorOption() {
return {
icon: 'icon-paint-bucket',
title: 'Set background color',
value: this.conditionStyle.style.backgroundColor,
property: 'backgroundColor'
}
}
},
mounted() {
this.openmct.objects.get(this.conditionIdentifier).then((conditionDomainObject) => {
this.condition = conditionDomainObject;
});
methods: {
updateStyleValue(value, item) {
if (item.property === 'border') {
value = '1px solid ' + value;
}
this.conditionStyle.style[item.property] = value;
this.$emit('persist', this.conditionStyle)
}
}
}
</script>

View File

@@ -1,10 +1,12 @@
<template>
<div>
<div v-if="!conditionalStyles.length"
class="holder c-c-button-wrapper align-left">
class="holder c-c-button-wrapper align-left"
>
<button
class="c-c-button c-c-button--minor add-criteria-button"
@click="addConditionSet">
@click="addConditionSet"
>
<span class="c-c-button__label">Use conditional styling</span>
</button>
</div>
@@ -12,15 +14,18 @@
<div class="holder c-c-button-wrapper align-left">
<button
class="c-c-button c-c-button--minor add-criteria-button"
@click="removeConditionSet">
@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"/>
:key="conditionStyle.conditionId"
>
<conditional-style :condition-style="conditionStyle"
@persist="updateConditionalStyle"
/>
</li>
</ul>
</div>
@@ -36,21 +41,33 @@ export default {
},
inject: [
'openmct',
'domainObject',
'layoutItem'
'domainObject'
],
props: {
itemId: {
type: String,
default: ''
},
initialStyles: {
type: Object,
default() {
return undefined;
}
}
},
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) {
if (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) {
if (this.itemId) {
let conditionalStyle = this.domainObject.configuration.conditionalStyle[this.itemId];
if (conditionalStyle) {
this.conditionalStyles = conditionalStyle.styles || [];
}
} else {
this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || [];
}
}
@@ -60,48 +77,86 @@ export default {
//TODO: this.conditionSetIdentifier will be set by the UI before calling this
this.conditionSetIdentifier = {
namespace: '',
key: 'bb0f61ad-268d-4d3e-bb30-90ca4a2053c4'
key: "81088c8a-4b80-41fe-9d07-fda8b22d6f5f"
};
this.initializeConditionalStyles();
},
removeConditionSet() {
//TODO: Handle the case where domainObject has items with styles but we're trying to remove the styles on the domainObject itself
this.conditionSetIdentifier = '';
this.conditionalStyles = [];
this.persist(undefined);
let domainObjectConditionalStyle = (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) || {};
if (domainObjectConditionalStyle) {
if (this.itemId) {
domainObjectConditionalStyle[this.itemId] = undefined;
delete domainObjectConditionalStyle[this.itemId];
} else {
domainObjectConditionalStyle.conditionSetIdentifier = undefined;
delete domainObjectConditionalStyle.conditionSetIdentifier;
domainObjectConditionalStyle.styles = undefined;
delete domainObjectConditionalStyle.styles;
}
if (_.isEmpty(domainObjectConditionalStyle)) {
domainObjectConditionalStyle = undefined;
}
}
this.persist(domainObjectConditionalStyle);
},
initializeConditionalStyles() {
const backgroundColors = [{backgroundColor: 'red'},{backgroundColor: 'orange'}, {backgroundColor: 'blue'}];
this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
conditionSetDomainObject.configuration.conditionCollection.forEach((identifier, index) => {
conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
this.conditionalStyles.push({
conditionIdentifier: identifier,
style: backgroundColors[index]
conditionId: conditionConfiguration.id,
conditionName: conditionConfiguration.name,
style: Object.assign({}, this.initialStyles)
});
});
this.persist({
defaultStyle: this.defaultStyle || {backgroundColor: 'inherit'},
let domainObjectConditionalStyle = (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) || {};
let conditionalStyle = {
conditionSetIdentifier: this.conditionSetIdentifier,
styles: this.conditionalStyles
});
};
if (this.itemId) {
this.persist({
...domainObjectConditionalStyle,
[this.itemId]: conditionalStyle
});
} else {
this.persist({
...domainObjectConditionalStyle,
...conditionalStyle
});
}
});
},
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]
};
return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
},
updateConditionalStyle(conditionStyle) {
let found = this.findStyleByConditionId(conditionStyle.conditionId);
if (found) {
found.style = conditionStyle.style;
let domainObjectConditionalStyle = this.domainObject.configuration.conditionalStyle || {};
if (this.itemId) {
let itemConditionalStyle = domainObjectConditionalStyle[this.itemId];
if (itemConditionalStyle) {
this.persist({
...domainObjectConditionalStyle,
[this.itemId]: {
...itemConditionalStyle,
styles: this.conditionalStyles
}
});
}
} else {
domainObjectConditionalStyle.styles = this.conditionalStyles;
this.persist(domainObjectConditionalStyle);
}
}
},
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);
}

View File

@@ -23,19 +23,11 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
import uuid from "uuid";
export default function ConditionPlugin() {
return function install(openmct) {
openmct.types.addType('condition', {
name: 'Condition',
key: 'condition',
description: 'A list of criteria which will be evaluated based on a trigger',
creatable: false,
initialize: function (domainObject) {
domainObject.composition = [];
}
});
openmct.types.addType('conditionSet', {
name: 'Condition Set',
@@ -45,7 +37,17 @@ export default function ConditionPlugin() {
cssClass: 'icon-conditional', // TODO: replace with class for new icon
initialize: function (domainObject) {
domainObject.configuration = {
conditionCollection: []
conditionCollection: [{
isDefault: true,
id: uuid(),
configuration: {
name: 'Default',
output: 'false',
trigger: 'all',
criteria: []
},
summary: 'Default condition'
}]
};
domainObject.composition = [];
domainObject.telemetry = {};

View File

@@ -26,26 +26,14 @@ import ConditionPlugin from "./plugin";
let openmct = createOpenMct();
openmct.install(new ConditionPlugin());
let conditionDefinition;
let conditionSetDefinition;
let mockDomainObject;
let mockDomainObject2;
let mockConditionSetDomainObject;
let element;
let child;
describe('the plugin', function () {
beforeAll((done) => {
conditionDefinition = openmct.types.get('condition').definition;
mockDomainObject = {
identifier: {
key: 'testConditionKey',
namespace: ''
},
type: 'condition'
};
conditionDefinition.initialize(mockDomainObject);
conditionSetDefinition = openmct.types.get('conditionSet').definition;
const appHolder = document.createElement('div');
@@ -56,7 +44,7 @@ describe('the plugin', function () {
child = document.createElement('div');
element.appendChild(child);
mockDomainObject2 = {
mockConditionSetDomainObject = {
identifier: {
key: 'testConditionSetKey',
namespace: ''
@@ -64,22 +52,12 @@ describe('the plugin', function () {
type: 'conditionSet'
};
conditionSetDefinition.initialize(mockDomainObject2);
conditionSetDefinition.initialize(mockConditionSetDomainObject);
openmct.on('start', done);
openmct.start(appHolder);
});
let mockConditionObject = {
name: 'Condition',
key: 'condition',
creatable: false
};
it('defines a condition object type with the correct key', () => {
expect(conditionDefinition.key).toEqual(mockConditionObject.key);
});
let mockConditionSetObject = {
name: 'Condition Set',
key: 'conditionSet',
@@ -90,18 +68,6 @@ describe('the plugin', function () {
expect(conditionSetDefinition.key).toEqual(mockConditionSetObject.key);
});
describe('the condition object', () => {
it('is not creatable', () => {
expect(conditionDefinition.creatable).toEqual(mockConditionObject.creatable);
});
it('initializes with an empty composition list', () => {
expect(mockDomainObject.composition instanceof Array).toBeTrue();
expect(mockDomainObject.composition.length).toEqual(0);
});
});
describe('the conditionSet object', () => {
it('is creatable', () => {
@@ -109,8 +75,8 @@ describe('the plugin', function () {
});
it('initializes with an empty composition list', () => {
expect(mockDomainObject2.composition instanceof Array).toBeTrue();
expect(mockDomainObject2.composition.length).toEqual(0);
expect(mockConditionSetDomainObject.composition instanceof Array).toBeTrue();
expect(mockConditionSetDomainObject.composition.length).toEqual(0);
});
it('provides a view', () => {

View File

@@ -0,0 +1,19 @@
export const getStyleProp = (key, defaultValue) => {
let styleProp = undefined;
switch(key) {
case 'fill': styleProp = {
backgroundColor: defaultValue || 'none'
};
break;
case 'stroke': styleProp = {
border: '1px solid ' + defaultValue || 'none'
};
break;
case 'color': styleProp = {
color: defaultValue || 'inherit'
};
break;
}
return styleProp;
};

View File

@@ -36,6 +36,7 @@
<script>
import LayoutFrame from './LayoutFrame.vue'
import conditionalStylesMixin from '../mixins/conditionalStyles-mixin';
export default {
makeDefinition() {
@@ -52,6 +53,7 @@ export default {
components: {
LayoutFrame
},
mixins: [conditionalStylesMixin],
props: {
item: {
type: Object,
@@ -71,10 +73,14 @@ export default {
},
computed: {
style() {
return {
backgroundColor: this.item.fill,
border: '1px solid ' + this.item.stroke
};
if (this.itemStyle) {
return this.itemStyle;
} else {
return {
backgroundColor: this.item.fill,
border: '1px solid ' + this.item.stroke
};
}
}
},
watch: {

View File

@@ -36,6 +36,7 @@
<script>
import LayoutFrame from './LayoutFrame.vue'
import conditionalStylesMixin from "../mixins/conditionalStyles-mixin";
export default {
makeDefinition(openmct, gridSize, element) {
@@ -52,6 +53,7 @@ export default {
components: {
LayoutFrame
},
mixins: [conditionalStylesMixin],
props: {
item: {
type: Object,
@@ -73,7 +75,7 @@ export default {
style() {
return {
backgroundImage: 'url(' + this.item.url + ')',
border: '1px solid ' + this.item.stroke
border: this.itemStyle ? this.itemStyle.border : '1px solid ' + this.item.stroke
}
}
},

View File

@@ -31,7 +31,7 @@
>
<line
v-bind="linePosition"
:stroke="item.stroke"
:stroke="stroke"
stroke-width="2"
/>
</svg>
@@ -60,6 +60,8 @@
<script>
import conditionalStylesMixin from "../mixins/conditionalStyles-mixin";
const START_HANDLE_QUADRANTS = {
1: 'c-frame-edit__handle--sw',
2: 'c-frame-edit__handle--se',
@@ -85,6 +87,7 @@ export default {
};
},
inject: ['openmct'],
mixins: [conditionalStylesMixin],
props: {
item: {
type: Object,
@@ -122,6 +125,13 @@ export default {
}
return {x, y, x2, y2};
},
stroke() {
if (this.itemStyle && this.itemStyle.border) {
return this.itemStyle.border.replace('1px solid ', '');
} else {
return this.item.stroke;
}
},
style() {
let {x, y, x2, y2} = this.position;
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);

View File

@@ -38,6 +38,7 @@
<script>
import LayoutFrame from './LayoutFrame.vue'
import conditionalStylesMixin from "../mixins/conditionalStyles-mixin";
export default {
makeDefinition(openmct, gridSize, element) {
@@ -57,6 +58,7 @@ export default {
components: {
LayoutFrame
},
mixins: [conditionalStylesMixin],
props: {
item: {
type: Object,
@@ -76,12 +78,16 @@ export default {
},
computed: {
style() {
return {
backgroundColor: this.item.fill,
borderColor: this.item.stroke,
color: this.item.color,
fontSize: this.item.size
};
if (this.itemStyle) {
return this.itemStyle;
} else {
return {
backgroundColor: this.item.fill,
borderColor: this.item.stroke,
color: this.item.color,
fontSize: this.item.size
};
}
}
},
watch: {

View File

@@ -0,0 +1,54 @@
import StyleRuleManager from "@/plugins/condition/StyleRuleManager";
export default {
inject: ['openmct'],
data() {
return {
itemStyle: this.itemStyle
}
},
mounted() {
this.domainObject = this.$parent.domainObject;
this.itemId = this.item.id;
this.conditionalStyle = this.getConditionalStyleForItem(this.domainObject.configuration.conditionalStyle);
this.initConditionalStyles();
},
destroyed() {
if (this.stopListeningConditionalStyles) {
this.stopListeningConditionalStyles();
}
},
methods: {
getConditionalStyleForItem(conditionalStyle) {
if (conditionalStyle) {
return conditionalStyle[this.itemId];
} else {
return undefined;
}
},
initConditionalStyles() {
if (!this.styleRuleManager) {
this.styleRuleManager = new StyleRuleManager(this.conditionalStyle, this.openmct);
this.styleRuleManager.on('conditionalStyleUpdated', this.updateStyle.bind(this));
} else {
this.styleRuleManager.updateConditionalStyleConfig(this.conditionalStyle);
}
if (this.stopListeningConditionalStyles) {
this.stopListeningConditionalStyles();
}
this.stopListeningConditionalStyles = 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
let newItemConditionalStyle = this.getConditionalStyleForItem(newConditionalStyle);
if (this.conditionalStyle !== newItemConditionalStyle) {
this.conditionalStyle = newItemConditionalStyle;
this.styleRuleManager.updateConditionalStyleConfig(this.conditionalStyle);
}
});
},
updateStyle(style) {
this.itemStyle = style;
}
}
};

View File

@@ -143,7 +143,7 @@ define([
let strategy;
if (this.model.interpolate !== 'none') {
strategy = 'minMax';
strategy = 'minmax';
}
options = _.extend({}, { size: 1000, strategy, filters: this.filters }, options || {});

View File

@@ -28,7 +28,7 @@
<button
v-if="allowExport"
class="c-button icon-download labeled"
title="Export This View's Data"
title="Export this view's data"
@click="exportAllDataAsCSV()"
>
<span class="c-button__label">Export Table Data</span>
@@ -37,7 +37,7 @@
v-if="allowExport"
v-show="markedRows.length"
class="c-button icon-download labeled"
title="Export Marked Rows As CSV"
title="Export marked rows as CSV"
@click="exportMarkedDataAsCSV()"
>
<span class="c-button__label">Export Marked Rows</span>
@@ -45,7 +45,7 @@
<button
v-show="markedRows.length"
class="c-button icon-x labeled"
title="Unmark All Rows"
title="Unmark all rows"
@click="unmarkAllRows()"
>
<span class="c-button__label">Unmark All Rows</span>
@@ -58,7 +58,7 @@
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'"
:title="paused ? 'Continue real-time data flow' : 'Pause real-time data flow'"
@click="togglePauseByButton()"
>
<span class="c-button__label">
@@ -66,6 +66,29 @@
</span>
</button>
<template v-if="!isEditing">
<div
class="c-separator"
>
</div>
<button
v-if="isAutosizeEnabled"
class="c-button icon-arrows-right-left labeled"
title="Increase column widths to fit currently available data."
@click="recalculateColumnWidths"
>
<span class="c-button__label">Expand Columns</span>
</button>
<button
v-else
class="c-button icon-expand labeled"
title="Automatically size columns to fit the table into the available space."
@click="autosizeColumns"
>
<span class="c-button__label">Autosize Columns</span>
</button>
</template>
<slot name="buttons"></slot>
</div>
<!-- main controlbar end -->
@@ -461,17 +484,20 @@ export default {
this.scrollW = (this.scrollable.offsetWidth - this.scrollable.clientWidth) + 1;
},
calculateColumnWidths() {
let columnWidths = {};
let totalWidth = 0;
let sizingRowEl = this.sizingTable.children[0];
let sizingCells = Array.from(sizingRowEl.children);
let headerKeys = Object.keys(this.headers);
let columnWidths = {},
totalWidth = 0,
headerKeys = Object.keys(this.headers),
sizingTableRow = this.sizingTable.children[0],
sizingCells = sizingTableRow.children;
headerKeys.forEach((headerKey, headerIndex)=>{
let cell = sizingCells[headerIndex];
let columnWidth = cell.offsetWidth;
columnWidths[headerKey] = columnWidth;
totalWidth += columnWidth;
headerKeys.forEach((headerKey, headerIndex, array)=>{
if (this.isAutosizeEnabled) {
columnWidths[headerKey] = this.sizingTable.clientWidth / array.length;
} else {
let cell = sizingCells[headerIndex];
columnWidths[headerKey] = cell.offsetWidth;
}
totalWidth += columnWidths[headerKey];
});
this.columnWidths = columnWidths;
@@ -818,6 +844,31 @@ export default {
this.setHeight();
this.scrollable.scrollTop = this.userScroll;
}
},
updateWidthsAndClearSizingTable() {
this.calculateColumnWidths();
this.configuredColumnWidths = this.columnWidths;
this.visibleRows.forEach((row, i) => {
this.$set(this.sizingRows, i, undefined);
delete this.sizingRows[i];
});
},
recalculateColumnWidths() {
this.visibleRows.forEach((row,i) => {
this.$set(this.sizingRows, i, row);
});
this.configuredColumnWidths = {};
this.isAutosizeEnabled = false;
this.$nextTick()
.then(this.updateWidthsAndClearSizingTable);
},
autosizeColumns() {
this.isAutosizeEnabled = true;
this.$nextTick().then(this.calculateColumnWidths);
}
}
}

View File

@@ -121,7 +121,6 @@ $colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
// States
$colorPausedBg: #ff9900;
$colorPausedFg: #333;
$colorOk: #33cc33;
// 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() {

View File

@@ -125,7 +125,6 @@ $colorFilter: $colorFilterFg; // Standalone against $colorBodyBg
// States
$colorPausedBg: #ff9900;
$colorPausedFg: #333;
$colorOk: #33cc33;
// 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;
}
}

View File

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

View File

@@ -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,45 @@ 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%;
max-height: max-content;
}
.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;
@@ -619,6 +677,12 @@ select {
.c-separator {
@include cToolbarSeparator();
height: 100%;
}
.c-row-separator {
border-top: 1px solid $colorInteriorBorder;
height: 1px;
}
.c-toolbar {

View File

@@ -108,8 +108,9 @@ tr {
}
/*************************************************** STATUS */
[class*='s-status'] {
[class*='s-status-icon'] {
&:before {
font-family: symbolsfont;
margin-right: $interiorMargin;
}
}

View File

@@ -154,6 +154,8 @@ export default {
this.openmct.objectViews.on('clearData', this.clearData);
},
show(object, viewKey, immediatelySelect, currentObjectPath) {
this.updateStyle();
if (this.unlisten) {
this.unlisten();
}

View File

@@ -2,6 +2,7 @@
$d: 12px;
$m: 2px;
$br: $d/1.5;
cursor: pointer;
display: inline-flex;
align-items: center;
vertical-align: middle;
@@ -39,7 +40,6 @@
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;
&:before {

View File

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

View File

@@ -8,6 +8,7 @@
<script>
import ConditionalStylesView from '../../plugins/condition/components/inspector/ConditionalStylesView.vue';
import Vue from 'vue';
import { getStyleProp } from "../../plugins/condition/utils/styleUtils";
export default {
inject: ['openmct'],
@@ -24,16 +25,30 @@ export default {
this.openmct.selection.off('change', this.updateSelection);
},
methods: {
getStyleProperties(item) {
let styleProps = {};
Object.keys(item).forEach((key) => {
Object.assign(styleProps, getStyleProp(key, item[key]));
});
return styleProps;
},
updateSelection(selection) {
if (selection.length > 0 && selection[0].length > 0) {
let domainObject = selection[0][0].context.item;
let layoutItem;
let layoutItem = {};
let styleProps = this.getStyleProperties({
fill: 'inherit',
stroke: 'inherit',
color: 'inherit'
});
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) {
styleProps = {};
layoutItem = selection[0][0].context.layoutItem;
styleProps = this.getStyleProperties(layoutItem);
domainObject = selection[0][1].context.item;
}
}
@@ -49,14 +64,19 @@ export default {
this.component = new Vue({
provide: {
openmct: this.openmct,
domainObject: domainObject,
layoutItem: layoutItem
domainObject: domainObject
},
el: viewContainer,
components: {
ConditionalStylesView
},
template: '<conditional-styles-view></conditional-styles-view>'
data() {
return {
layoutItem,
styleProps
}
},
template: '<conditional-styles-view :item-id="layoutItem.id" :initial-styles="styleProps"></conditional-styles-view>'
});
}
}

View File

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