mirror of
https://github.com/nasa/openmct.git
synced 2025-06-14 21:28:12 +00:00
Styling for plot limits (#3917)
* Styling for plot limits and colors * Updates to limit provider and css * Change limits related CSS "*--upper" and "*--lower" to "*--upr" and "*--lwr" for better parity with legacy naming; * Refactor limit class to util * Use new classes for sine wave generator for red-low and yellow-high * Added modifier classes for right and below-aligned labels; * Prevent label overlap of limits as much as possible * Add border colors to limit labels for better visual ties to their lines * Add documentation for limit level specification API change Co-authored-by: Shefali Joshi <simplyrender@gmail.com> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
committed by
GitHub
parent
d56d176aac
commit
2889e88a97
@ -1,17 +1,23 @@
|
||||
<template>
|
||||
<div class="plot-series-limit-label"
|
||||
<div class="c-plot-limit"
|
||||
:style="styleObj"
|
||||
:class="limit.cssClass"
|
||||
:class="limitClass"
|
||||
>
|
||||
<span class="plot-series-limit-value">{{ limit.value }}</span>
|
||||
<span class="plot-series-color-swatch"
|
||||
:style="{ 'background-color': limit.color }"
|
||||
></span>
|
||||
<span class="plot-series-name">{{ limit.name }}</span>
|
||||
<div class="c-plot-limit__label">
|
||||
<span class="c-plot-limit__direction-icon"></span>
|
||||
<span class="c-plot-limit__severity-icon"></span>
|
||||
<span class="c-plot-limit__limit-value">{{ limit.value }}</span>
|
||||
<span class="c-plot-limit__series-color-swatch"
|
||||
:style="{ 'background-color': limit.seriesColor }"
|
||||
></span>
|
||||
<span class="c-plot-limit__series-name">{{ limit.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLimitClass } from "./limitUtil";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
limit: {
|
||||
@ -31,15 +37,14 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
styleObj() {
|
||||
const top = `${this.point.top - 10}px`;
|
||||
const left = `${this.point.left + 5}px`;
|
||||
const top = `${this.point.top}px`;
|
||||
|
||||
return {
|
||||
'position': 'absolute',
|
||||
'top': top,
|
||||
'left': left,
|
||||
'color': '#fff'
|
||||
'top': top
|
||||
};
|
||||
},
|
||||
limitClass() {
|
||||
return getLimitClass(this.limit, 'c-plot-limit--');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<hr :style="styleObj"
|
||||
:class="cssWithoutUprLwr"
|
||||
>
|
||||
<div :style="styleObj"
|
||||
class="c-plot-limit-line js-limit-line"
|
||||
:class="limitClass"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLimitClass } from "./limitUtil";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
point: {
|
||||
@ -14,30 +17,23 @@ export default {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
cssClass: {
|
||||
type: String,
|
||||
limit: {
|
||||
type: Object,
|
||||
default() {
|
||||
return '';
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleObj() {
|
||||
const top = `${this.point.top}px`;
|
||||
const left = `${this.point.left}px`;
|
||||
|
||||
return {
|
||||
'position': 'absolute',
|
||||
'width': '100%',
|
||||
'top': top,
|
||||
'left': left
|
||||
'top': top
|
||||
};
|
||||
},
|
||||
cssWithoutUprLwr() {
|
||||
let cssClass = this.cssClass.replace(/is-limit--upr/gi, 'is-limit--line');
|
||||
cssClass = cssClass.replace(/is-limit--lwr/gi, 'is-limit--line');
|
||||
|
||||
return cssClass;
|
||||
limitClass() {
|
||||
return getLimitClass(this.limit, 'c-plot-limit-line--');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -32,6 +32,7 @@ export default class MCTChartAlarmLineSet {
|
||||
|
||||
eventHelpers.extend(this);
|
||||
this.listenTo(series, 'limitBounds', this.updateBounds, this);
|
||||
this.listenTo(series, 'limits', this.getLimitPoints, this);
|
||||
this.listenTo(series, 'change:xKey', this.getLimitPoints, this);
|
||||
|
||||
if (series.limits) {
|
||||
@ -69,26 +70,28 @@ export default class MCTChartAlarmLineSet {
|
||||
Object.keys(series.limits).forEach((key) => {
|
||||
const limitForLevel = series.limits[key];
|
||||
if (limitForLevel.high) {
|
||||
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.high), series);
|
||||
this.limits.push({
|
||||
seriesKey: series.keyString,
|
||||
value: series.getYVal(limitForLevel.high),
|
||||
color: this.color().asHexString(),
|
||||
level: key.toLowerCase(),
|
||||
name: this.name(),
|
||||
point,
|
||||
cssClass: limitForLevel.high.cssClass
|
||||
seriesColor: series.get('color').asHexString(),
|
||||
point: this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.high), series),
|
||||
value: series.getYVal(limitForLevel.high),
|
||||
color: limitForLevel.high.color,
|
||||
isUpper: true
|
||||
});
|
||||
}
|
||||
|
||||
if (limitForLevel.low) {
|
||||
const point = this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.low), series);
|
||||
this.limits.push({
|
||||
seriesKey: series.keyString,
|
||||
value: series.getYVal(limitForLevel.low),
|
||||
color: this.color().asHexString(),
|
||||
level: key.toLowerCase(),
|
||||
name: this.name(),
|
||||
point,
|
||||
cssClass: limitForLevel.low.cssClass
|
||||
seriesColor: series.get('color').asHexString(),
|
||||
point: this.makePoint(Object.assign({ [xKey]: this.bounds.start }, limitForLevel.low), series),
|
||||
value: series.getYVal(limitForLevel.low),
|
||||
color: limitForLevel.low.color,
|
||||
isUpper: false
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
|
@ -46,6 +46,7 @@ import Vue from 'vue';
|
||||
|
||||
const MARKER_SIZE = 6.0;
|
||||
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
|
||||
const CLEARANCE = 15;
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
@ -472,13 +473,15 @@ export default {
|
||||
}
|
||||
|
||||
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
|
||||
|
||||
let limitPointOverlap = [];
|
||||
this.limitLines.forEach((limitLine) => {
|
||||
let limitContainerEl = this.$refs.limitArea;
|
||||
limitLine.limits.forEach((limit) => {
|
||||
const showLabels = this.showLabels(limit.seriesKey);
|
||||
if (showLabels) {
|
||||
let limitLabelEl = this.getLimitLabel(limit);
|
||||
const overlap = this.getLimitOverlap(limit, limitPointOverlap);
|
||||
limitPointOverlap.push(overlap);
|
||||
let limitLabelEl = this.getLimitLabel(limit, overlap);
|
||||
limitContainerEl.appendChild(limitLabelEl);
|
||||
}
|
||||
|
||||
@ -502,14 +505,41 @@ export default {
|
||||
const component = new LimitLineClass({
|
||||
propsData: {
|
||||
point,
|
||||
cssClass: limit.cssClass
|
||||
limit
|
||||
}
|
||||
});
|
||||
component.$mount();
|
||||
|
||||
return component.$el;
|
||||
},
|
||||
getLimitLabel(limit) {
|
||||
getLimitOverlap(limit, overlapMap) {
|
||||
//calculate if limit lines are too close to each other
|
||||
let limitTop = this.drawAPI.y(limit.point.y);
|
||||
const needsVerticalAdjustment = limitTop - CLEARANCE <= 0;
|
||||
let needsHorizontalAdjustment = false;
|
||||
overlapMap.forEach(value => {
|
||||
let diffTop;
|
||||
if (limitTop > value.overlapTop) {
|
||||
diffTop = limitTop - value.overlapTop;
|
||||
} else {
|
||||
diffTop = value.overlapTop - limitTop;
|
||||
}
|
||||
|
||||
//need to compare +ves to +ves and -ves to -ves
|
||||
if (!needsHorizontalAdjustment
|
||||
&& Math.abs(diffTop) <= CLEARANCE
|
||||
&& value.needsHorizontalAdjustment !== true) {
|
||||
needsHorizontalAdjustment = true;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
needsHorizontalAdjustment,
|
||||
needsVerticalAdjustment,
|
||||
overlapTop: limitTop
|
||||
};
|
||||
},
|
||||
getLimitLabel(limit, overlap) {
|
||||
let point = {
|
||||
left: 0,
|
||||
top: this.drawAPI.y(limit.point.y)
|
||||
@ -517,7 +547,7 @@ export default {
|
||||
let LimitLabelClass = Vue.extend(LimitLabel);
|
||||
const component = new LimitLabelClass({
|
||||
propsData: {
|
||||
limit,
|
||||
limit: Object.assign({}, overlap, limit),
|
||||
point
|
||||
}
|
||||
});
|
||||
|
32
src/plugins/plot/chart/limitUtil.js
Normal file
32
src/plugins/plot/chart/limitUtil.js
Normal file
@ -0,0 +1,32 @@
|
||||
export function getLimitClass(limit, prefix) {
|
||||
let cssClass = '';
|
||||
//If color exists then use it, fall back to the cssClass
|
||||
if (limit.color) {
|
||||
cssClass = `${cssClass} ${prefix}${limit.color}`;
|
||||
} else if (limit.cssClass) {
|
||||
cssClass = `${cssClass}${limit.cssClass}`;
|
||||
}
|
||||
|
||||
// If we applied the cssClass then skip these classes
|
||||
if (limit.cssClass === undefined) {
|
||||
if (limit.isUpper) {
|
||||
cssClass = `${cssClass} ${prefix}upr`;
|
||||
} else {
|
||||
cssClass = `${cssClass} ${prefix}lwr`;
|
||||
}
|
||||
|
||||
if (limit.level) {
|
||||
cssClass = `${cssClass} ${prefix}${limit.level}`;
|
||||
}
|
||||
|
||||
if (limit.needsHorizontalAdjustment) {
|
||||
cssClass = `${cssClass} --align-label-right`;
|
||||
}
|
||||
|
||||
if (limit.needsVerticalAdjustment) {
|
||||
cssClass = `${cssClass} --align-label-below`;
|
||||
}
|
||||
}
|
||||
|
||||
return cssClass;
|
||||
}
|
@ -117,7 +117,17 @@ export default class PlotSeries extends Model {
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
|
||||
this.limitDefinition = this.openmct.telemetry.limitDefinition(options.domainObject);
|
||||
this.limits = this.limitDefinition.limits();
|
||||
this.limits = [];
|
||||
this.limitDefinition.limits().then(response => {
|
||||
this.limits = [];
|
||||
|
||||
if (response) {
|
||||
this.limits = response;
|
||||
}
|
||||
|
||||
this.emit('limits', this);
|
||||
|
||||
});
|
||||
this.openmct.time.on('bounds', this.updateLimits);
|
||||
this.on('destroy', this.onDestroy, this);
|
||||
}
|
||||
|
@ -49,6 +49,8 @@
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import {getLimitClass} from "@/plugins/plot/chart/limitUtil";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
valueToShowWhenCollapsed: {
|
||||
@ -110,7 +112,7 @@ export default {
|
||||
if (closest) {
|
||||
this.formattedYValue = seriesObject.formatY(closest);
|
||||
this.formattedXValue = seriesObject.formatX(closest);
|
||||
this.mctLimitStateClass = closest.mctLimitState ? `${closest.mctLimitState.cssClass}` : '';
|
||||
this.mctLimitStateClass = closest.mctLimitState ? getLimitClass(closest.mctLimitState, 'c-plot-limit--') : '';
|
||||
} else {
|
||||
this.formattedYValue = '';
|
||||
this.formattedXValue = '';
|
||||
|
@ -74,6 +74,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getLimitClass} from "@/plugins/plot/chart/limitUtil";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
seriesObject: {
|
||||
@ -147,7 +149,7 @@ export default {
|
||||
if (closest) {
|
||||
this.formattedYValue = seriesObject.formatY(closest);
|
||||
this.formattedXValue = seriesObject.formatX(closest);
|
||||
this.mctLimitStateClass = seriesObject.closest.mctLimitState ? seriesObject.closest.mctLimitState.cssClass : '';
|
||||
this.mctLimitStateClass = seriesObject.closest.mctLimitState ? getLimitClass(seriesObject.closest.mctLimitState, 'c-plot-limit--') : '';
|
||||
} else {
|
||||
this.formattedYValue = '';
|
||||
this.formattedXValue = '';
|
||||
|
@ -21,36 +21,36 @@
|
||||
*****************************************************************************/
|
||||
|
||||
export const COLOR_PALETTE = [
|
||||
[0x20, 0xB2, 0xAA],
|
||||
[0x9A, 0xCD, 0x32],
|
||||
[0xFF, 0x8C, 0x00],
|
||||
[0xD2, 0xB4, 0x8C],
|
||||
[0x40, 0xE0, 0xD0],
|
||||
[0x41, 0x69, 0xFF],
|
||||
[0xFF, 0xD7, 0x00],
|
||||
[0x6A, 0x5A, 0xCD],
|
||||
[0xEE, 0x82, 0xEE],
|
||||
[0xCC, 0x99, 0x66],
|
||||
[0x99, 0xCC, 0xCC],
|
||||
[0x66, 0xCC, 0x33],
|
||||
[0xFF, 0xCC, 0x00],
|
||||
[0xFF, 0x66, 0x33],
|
||||
[0xCC, 0x66, 0xFF],
|
||||
[0xFF, 0x00, 0x66],
|
||||
[0xFF, 0xFF, 0x00],
|
||||
[0x80, 0x00, 0x80],
|
||||
[0x00, 0x86, 0x8B],
|
||||
[0x00, 0x8A, 0x00],
|
||||
[0xFF, 0x00, 0x00],
|
||||
[0x00, 0x00, 0xFF],
|
||||
[0xF5, 0xDE, 0xB3],
|
||||
[0xBC, 0x8F, 0x8F],
|
||||
[0x46, 0x82, 0xB4],
|
||||
[0xFF, 0xAF, 0xAF],
|
||||
[0x43, 0xCD, 0x80],
|
||||
[0xCD, 0xC1, 0xC5],
|
||||
[0xA0, 0x52, 0x2D],
|
||||
[0x64, 0x95, 0xED]
|
||||
[0x00, 0x37, 0xFF],
|
||||
[0xF0, 0x60, 0x00],
|
||||
[0x00, 0x70, 0x40],
|
||||
[0xFB, 0x49, 0x49],
|
||||
[0xC8, 0x00, 0xCF],
|
||||
[0x55, 0x77, 0xF2],
|
||||
[0xFF, 0xA6, 0x3D],
|
||||
[0x05, 0xA3, 0x00],
|
||||
[0xF0, 0x00, 0x6C],
|
||||
[0x77, 0x17, 0x7A],
|
||||
[0x23, 0xA9, 0xDB],
|
||||
[0xFA, 0xF0, 0x6F],
|
||||
[0x4E, 0xF0, 0x48],
|
||||
[0xAD, 0x50, 0x72],
|
||||
[0x94, 0x25, 0xEA],
|
||||
[0x21, 0x87, 0x82],
|
||||
[0x8F, 0x6E, 0x47],
|
||||
[0xf0, 0x59, 0xcb],
|
||||
[0x34, 0xB6, 0x7D],
|
||||
[0x6A, 0x36, 0xFF],
|
||||
[0x56, 0xF0, 0xE8],
|
||||
[0xA1, 0x8C, 0x1C],
|
||||
[0xCB, 0xE1, 0x44],
|
||||
[0xFF, 0x84, 0x9E],
|
||||
[0xB7, 0x79, 0xE7],
|
||||
[0x8C, 0xC9, 0xFD],
|
||||
[0xDB, 0xAA, 0x6E],
|
||||
[0xB8, 0xDF, 0x97],
|
||||
[0xFF, 0xBC, 0xDA],
|
||||
[0xD3, 0xB6, 0xDE]
|
||||
];
|
||||
|
||||
export function isDefaultColor(color) {
|
||||
|
@ -97,7 +97,7 @@ describe("the plugin", function () {
|
||||
telemetrylimitProvider.supportsLimits.and.returnValue(true);
|
||||
telemetrylimitProvider.getLimits.and.returnValue({
|
||||
limits: function () {
|
||||
return {
|
||||
return Promise.resolve({
|
||||
WARNING: {
|
||||
low: {
|
||||
cssClass: "is-limit--lwr is-limit--yellow",
|
||||
@ -118,7 +118,7 @@ describe("the plugin", function () {
|
||||
'some-key': 0.9
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
telemetrylimitProvider.getLimitEvaluator.and.returnValue({
|
||||
@ -709,7 +709,7 @@ describe("the plugin", function () {
|
||||
config.series.models[0].set('limitLines', true);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
let limitEl = element.querySelectorAll(".js-limit-area hr");
|
||||
let limitEl = element.querySelectorAll(".js-limit-area .js-limit-line");
|
||||
expect(limitEl.length).toBe(4);
|
||||
done();
|
||||
});
|
||||
|
Reference in New Issue
Block a user