Plots limit lines (#3882)

This commit is contained in:
Shefali Joshi 2021-05-28 14:51:29 -07:00 committed by GitHub
parent 5fafde5f23
commit 8274c23129
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 586 additions and 19 deletions

View File

@ -93,5 +93,36 @@ define([
};
};
SinewaveLimitProvider.prototype.getLimits = function (domainObject) {
return {
limits: function () {
return {
WARNING: {
low: {
cssClass: "is-limit--lwr is-limit--yellow",
sin: -YELLOW.sin,
cos: -YELLOW.cos
},
high: {
cssClass: "is-limit--upr is-limit--yellow",
...YELLOW
}
},
DISTRESS: {
low: {
cssClass: "is-limit--lwr is-limit--red",
sin: -RED.sin,
cos: -RED.cos
},
high: {
cssClass: "is-limit--upr is-limit--red",
...RED
}
}
};
}
};
};
return SinewaveLimitProvider;
});

View File

@ -161,6 +161,22 @@ define([
evaluate: function (datum, property) {
return limitEvaluator.evaluate(datum, property && property.key);
}
};
};
LegacyTelemetryProvider.prototype.getLimits = function (domainObject) {
const oldObject = this.instantiate(
utils.toOldFormat(domainObject),
utils.makeKeyString(domainObject.identifier)
);
const limitEvaluator = oldObject.getCapability("limit");
return {
limits: function () {
return limitEvaluator.limits();
}
};
};

View File

@ -504,6 +504,26 @@ define([
return this.getLimitEvaluator(domainObject);
};
/**
* Get a limits for this domain object.
* Limits help you display limits and alarms of
* telemetry for display purposes without having to interact directly
* with the Limit API.
*
* This method is optional.
* If a provider does not implement this method, it is presumed
* that no limits are defined for this domain object's telemetry.
*
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to get limits
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
* @method limits
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.limitDefinition = function (domainObject) {
return this.getLimits(domainObject);
};
/**
* Get a limit evaluator for this domain object.
* Limit Evaluators help you evaluate limit and alarm status of individual
@ -531,5 +551,42 @@ define([
return provider.getLimitEvaluator(domainObject);
};
/**
* Get a limit definitions for this domain object.
* Limit Definitions help you indicate limits and alarms of
* telemetry for display purposes without having to interact directly
* with the Limit API.
*
* This method is optional.
* If a provider does not implement this method, it is presumed
* that no limits are defined for this domain object's telemetry.
*
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to display limits
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
* @method limits returns a limits object of
* type {
* level1: {
* low: { key1: value1, key2: value2 },
* high: { key1: value1, key2: value2 }
* },
* level2: {
* low: { key1: value1, key2: value2 },
* high: { key1: value1, key2: value2 }
* }
* }
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.getLimits = function (domainObject) {
const provider = this.findLimitEvaluator(domainObject);
if (!provider) {
return {
limits: function () {}
};
}
return provider.getLimits(domainObject);
};
return TelemetryAPI;
});

View File

@ -43,15 +43,15 @@ export default function LADTableSetViewProvider(openmct) {
components: {
LadTableSet: LadTableSet
},
provide: {
openmct,
objectPath
},
data() {
return {
domainObject
};
},
provide: {
openmct,
objectPath
},
template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
});
},

View File

@ -28,6 +28,7 @@
:series="config.series.models"
:highlights="highlights"
:legend="config.legend"
@legendHoverChanged="legendHoverChanged"
/>
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
<y-axis v-if="config.series.models.length > 0"
@ -67,6 +68,7 @@
>
<mct-chart :rectangles="rectangles"
:highlights="highlights"
:show-limit-line-labels="showLimitLineLabels"
@plotReinitializeCanvas="initCanvas"
/>
</div>
@ -213,7 +215,8 @@ export default {
pending: 0,
isRealTime: this.openmct.time.clock() !== undefined,
loaded: false,
isTimeOutOfSync: false
isTimeOutOfSync: false,
showLimitLineLabels: undefined
};
},
computed: {
@ -1018,6 +1021,9 @@ export default {
this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth;
this.config.series.models.forEach(this.loadSeriesData, this);
}
},
legendHoverChanged(data) {
this.showLimitLineLabels = data;
}
}
};

View File

@ -0,0 +1,46 @@
<template>
<div class="plot-series-limit-label"
:style="styleObj"
:class="limit.cssClass"
>
<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>
</template>
<script>
export default {
props: {
limit: {
type: Object,
required: true,
default() {
return {};
}
},
point: {
type: Object,
required: true,
default() {
return {};
}
}
},
computed: {
styleObj() {
const top = `${this.point.top - 10}px`;
const left = `${this.point.left + 5}px`;
return {
'position': 'absolute',
'top': top,
'left': left,
'color': '#fff'
};
}
}
};
</script>

View File

@ -0,0 +1,44 @@
<template>
<hr :style="styleObj"
:class="cssWithoutUprLwr"
>
</template>
<script>
export default {
props: {
point: {
type: Object,
required: true,
default() {
return {};
}
},
cssClass: {
type: String,
default() {
return '';
}
}
},
computed: {
styleObj() {
const top = `${this.point.top}px`;
const left = `${this.point.left}px`;
return {
'position': 'absolute',
'width': '100%',
'top': top,
'left': left
};
},
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;
}
}
};
</script>

View File

@ -0,0 +1,105 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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 eventHelpers from '../lib/eventHelpers';
export default class MCTChartAlarmLineSet {
constructor(series, chart, offset, bounds) {
this.series = series;
this.chart = chart;
this.offset = offset;
this.bounds = bounds;
this.limits = [];
eventHelpers.extend(this);
this.listenTo(series, 'limitBounds', this.updateBounds, this);
this.listenTo(series, 'change:xKey', this.getLimitPoints, this);
if (series.limits) {
this.getLimitPoints(series);
}
}
updateBounds(bounds) {
this.bounds = bounds;
this.getLimitPoints(this.series);
}
color() {
return this.series.get('color');
}
name() {
return this.series.get('name');
}
makePoint(point, series) {
if (!this.offset.xVal) {
this.chart.setOffset(point, undefined, series);
}
return {
x: this.offset.xVal(point, series),
y: this.offset.yVal(point, series)
};
}
getLimitPoints(series) {
this.limits = [];
let xKey = series.get('xKey');
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(),
name: this.name(),
point,
cssClass: limitForLevel.high.cssClass
});
}
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(),
name: this.name(),
point,
cssClass: limitForLevel.low.cssClass
});
}
}, this);
}
reset() {
this.limits = [];
}
destroy() {
this.stopListening();
}
}

View File

@ -23,6 +23,9 @@
<div class="gl-plot-chart-area">
<span v-html="canvasTemplate"></span>
<span v-html="canvasTemplate"></span>
<div ref="limitArea"
class="js-limit-area"
></div>
</div>
</template>
@ -34,8 +37,12 @@ import MCTChartLineLinear from './MCTChartLineLinear';
import MCTChartLineStepAfter from './MCTChartLineStepAfter';
import MCTChartPointSet from './MCTChartPointSet';
import MCTChartAlarmPointSet from './MCTChartAlarmPointSet';
import MCTChartAlarmLineSet from "./MCTChartAlarmLineSet";
import configStore from "../configuration/configStore";
import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
import LimitLine from "./LimitLine.vue";
import LimitLabel from "./LimitLabel.vue";
import Vue from 'vue';
const MARKER_SIZE = 6.0;
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
@ -54,6 +61,12 @@ export default {
default() {
return [];
}
},
showLimitLineLabels: {
type: Object,
default() {
return {};
}
}
},
data() {
@ -67,18 +80,22 @@ export default {
},
rectangles() {
this.scheduleDraw();
},
showLimitLineLabels() {
this.drawLimitLines();
}
},
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
this.isDestroyed = false;
this.lines = [];
this.limitLines = [];
this.pointSets = [];
this.alarmSets = [];
this.offset = {};
this.seriesElements = new WeakMap();
this.seriesLimits = new WeakMap();
let canvasEls = this.$parent.$refs.chartContainer.querySelectorAll("canvas");
const mainCanvas = canvasEls[1];
@ -90,8 +107,8 @@ export default {
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.yAxis, 'change', this.updateLimitsAndDraw);
this.listenTo(this.config.xAxis, 'change', this.updateLimitsAndDraw);
this.config.series.forEach(this.onSeriesAdd, this);
},
beforeDestroy() {
@ -116,15 +133,18 @@ export default {
this.changeInterpolate(mode, o, series);
this.changeMarkers(mode, o, series);
this.changeAlarmMarkers(mode, o, series);
this.changeLimitLines(mode, o, series);
},
onSeriesAdd(series) {
this.listenTo(series, 'change:xKey', this.reDraw, this);
this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
this.listenTo(series, 'change:markers', this.changeMarkers, this);
this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
this.listenTo(series, 'change:limitLines', this.changeLimitLines, this);
this.listenTo(series, 'change', this.scheduleDraw);
this.listenTo(series, 'add', this.scheduleDraw);
this.makeChartElement(series);
this.makeLimitLines(series);
},
changeInterpolate(mode, o, series) {
if (mode === o) {
@ -178,6 +198,14 @@ export default {
this.pointSets.push(pointSet);
}
},
changeLimitLines(mode, o, series) {
if (mode === o) {
return;
}
this.makeLimitLines(series);
this.updateLimitsAndDraw();
},
onSeriesRemove(series) {
this.stopListening(series);
this.removeChartElement(series);
@ -187,6 +215,7 @@ export default {
this.isDestroyed = true;
this.stopListening();
this.lines.forEach(line => line.destroy());
this.limitLines.forEach(line => line.destroy());
DrawLoader.releaseDrawAPI(this.drawAPI);
},
clearOffset() {
@ -199,6 +228,9 @@ export default {
this.lines.forEach(function (line) {
line.reset();
});
this.limitLines.forEach(function (line) {
line.reset();
});
this.pointSets.forEach(function (pointSet) {
pointSet.reset();
});
@ -269,6 +301,8 @@ export default {
}
this.seriesElements.delete(series);
this.clearLimitLines(series);
},
lineForSeries(series) {
if (series.get('interpolate') === 'linear') {
@ -287,6 +321,14 @@ export default {
);
}
},
limitLineForSeries(series) {
return new MCTChartAlarmLineSet(
series,
this,
this.offset,
this.openmct.time.bounds()
);
},
pointSetForSeries(series) {
if (series.get('markers')) {
return new MCTChartPointSet(
@ -308,7 +350,8 @@ export default {
makeChartElement(series) {
const elements = {
lines: [],
pointSets: []
pointSets: [],
limitLines: []
};
const line = this.lineForSeries(series);
@ -330,6 +373,37 @@ export default {
this.seriesElements.set(series, elements);
},
makeLimitLines(series) {
this.clearLimitLines(series);
if (!series.get('limitLines')) {
return;
}
const limitElements = {
limitLines: []
};
const limitLine = this.limitLineForSeries(series);
if (limitLine) {
limitElements.limitLines.push(limitLine);
this.limitLines.push(limitLine);
}
this.seriesLimits.set(series, limitElements);
},
clearLimitLines(series) {
const seriesLimits = this.seriesLimits.get(series);
if (seriesLimits) {
seriesLimits.limitLines.forEach(function (line) {
this.limitLines.splice(this.limitLines.indexOf(line), 1);
line.destroy();
}, this);
this.seriesLimits.delete(series);
}
},
canDraw() {
if (!this.offset.x || !this.offset.y) {
return false;
@ -337,6 +411,10 @@ export default {
return true;
},
updateLimitsAndDraw() {
this.drawLimitLines();
this.scheduleDraw();
},
scheduleDraw() {
if (!this.drawScheduled) {
requestAnimationFrame(this.draw);
@ -385,6 +463,68 @@ export default {
this.pointSets.forEach(this.drawPoints, this);
this.alarmSets.forEach(this.drawAlarmPoints, this);
},
drawLimitLines() {
if (this.canDraw()) {
this.updateViewport();
if (!this.drawAPI.origin) {
return;
}
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
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);
limitContainerEl.appendChild(limitLabelEl);
}
let limitEl = this.getLimitElement(limit);
limitContainerEl.appendChild(limitEl);
}, this);
});
}
},
showLabels(seriesKey) {
return this.showLimitLineLabels.seriesKey
&& (this.showLimitLineLabels.seriesKey === seriesKey);
},
getLimitElement(limit) {
let point = {
left: 0,
top: this.drawAPI.y(limit.point.y)
};
let LimitLineClass = Vue.extend(LimitLine);
const component = new LimitLineClass({
propsData: {
point,
cssClass: limit.cssClass
}
});
component.$mount();
return component.$el;
},
getLimitLabel(limit) {
let point = {
left: 0,
top: this.drawAPI.y(limit.point.y)
};
let LimitLabelClass = Vue.extend(LimitLabel);
const component = new LimitLabelClass({
propsData: {
limit,
point
}
});
component.$mount();
return component.$el;
},
drawAlarmPoints(alarmSet) {
this.drawAPI.drawLimitPoints(
alarmSet.points,
@ -401,11 +541,12 @@ export default {
chartElement.series.get('markerShape')
);
},
drawLine(chartElement) {
drawLine(chartElement, disconnected) {
this.drawAPI.drawLine(
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count
chartElement.count,
disconnected
);
},
drawHighlights() {

View File

@ -97,7 +97,8 @@ export default class PlotSeries extends Model {
markers: true,
markerShape: 'point',
markerSize: 2.0,
alarmMarkers: true
alarmMarkers: true,
limitLines: false
};
}
@ -115,9 +116,16 @@ export default class PlotSeries extends Model {
this.domainObject = options.domainObject;
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.openmct.time.on('bounds', this.updateLimits);
this.on('destroy', this.onDestroy, this);
}
updateLimits(bounds) {
this.emit('limitBounds', bounds);
}
locateOldObject(oldStyleParent) {
return oldStyleParent.useCapability('composition')
.then(function (children) {

View File

@ -60,6 +60,14 @@
{{ alarmMarkers ? "Enabled" : "Disabled" }}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display lines visually denoting alarm limits."
>Limit lines</div>
<div class="grid-cell value">
{{ limitLines ? "Enabled" : "Disabled" }}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The plot line and marker color for this series."
@ -135,6 +143,9 @@ export default {
alarmMarkers() {
return this.series.get('alarmMarkers');
},
limitLines() {
return this.series.get('limitLines');
},
seriesHexColor() {
return this.series.get('color').asHexString();
}

View File

@ -91,6 +91,17 @@
>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display limit lines"
>Limit lines</div>
<div class="grid-cell value">
<input v-model="limitLines"
type="checkbox"
@change="updateForm('limitLines')"
>
</div>
</li>
<li v-show="markers || alarmMarkers"
class="grid-row"
>
@ -168,6 +179,7 @@ export default {
markers: this.series.get('markers'),
markerShape: this.series.get('markerShape'),
alarmMarkers: this.series.get('alarmMarkers'),
limitLines: this.series.get('limitLines'),
markerSize: this.series.get('markerSize'),
validation: {},
swatchActive: false
@ -240,6 +252,11 @@ export default {
modelProp: 'alarmMarkers',
coerce: Boolean,
objectPath: this.dynamicPathForKey('alarmMarkers')
},
{
modelProp: 'limitLines',
coerce: Boolean,
objectPath: this.dynamicPathForKey('limitLines')
}
];

View File

@ -48,7 +48,7 @@
:highlights="highlights"
:value-to-show-when-collapsed="legend.get('valueToShowWhenCollapsed')"
:series-object="seriesObject"
:closest="seriesObject.closest"
@legendHoverChanged="legendHoverChanged"
/>
</div>
<!-- EXPANDED PLOT LEGEND -->
@ -89,6 +89,7 @@
:series-object="seriesObject"
:highlights="highlights"
:legend="legend"
@legendHoverChanged="legendHoverChanged"
/>
</tbody>
</table>
@ -160,6 +161,9 @@ export default {
expandLegend() {
this.isLegendExpanded = !this.isLegendExpanded;
this.legend.set('expanded', this.isLegendExpanded);
},
legendHoverChanged(data) {
this.$emit('legendHoverChanged', data);
}
}
};

View File

@ -24,6 +24,8 @@
:class="{
'is-status--missing': isMissing
}"
@mouseover="toggleHover(true)"
@mouseleave="toggleHover(false)"
>
<div class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
@ -121,6 +123,12 @@ export default {
} else {
this.formattedYValueFromStats = '';
}
},
toggleHover(hover) {
this.hover = hover;
this.$emit('legendHoverChanged', {
seriesKey: this.hover ? this.seriesObject.keyString : ''
});
}
}
};

View File

@ -25,6 +25,8 @@
:class="{
'is-status--missing': isMissing
}"
@mouseover="toggleHover(true)"
@mouseleave="toggleHover(false)"
>
<td class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
@ -160,6 +162,12 @@ export default {
this.formattedMinY = '';
this.formattedMaxY = '';
}
},
toggleHover(hover) {
this.hover = hover;
this.$emit('legendHoverChanged', {
seriesKey: this.hover ? this.seriesObject.keyString : ''
});
}
}
};

View File

@ -36,6 +36,7 @@ describe("the plugin", function () {
let telemetryPromise;
let cleanupFirst;
let mockObjectPath;
let telemetrylimitProvider;
beforeEach((done) => {
mockObjectPath = [
@ -88,6 +89,45 @@ describe("the plugin", function () {
return telemetryPromise;
});
telemetrylimitProvider = jasmine.createSpyObj('telemetrylimitProvider', [
'supportsLimits',
'getLimits',
'getLimitEvaluator'
]);
telemetrylimitProvider.supportsLimits.and.returnValue(true);
telemetrylimitProvider.getLimits.and.returnValue({
limits: function () {
return {
WARNING: {
low: {
cssClass: "is-limit--lwr is-limit--yellow",
'some-key': -0.5
},
high: {
cssClass: "is-limit--upr is-limit--yellow",
'some-key': 0.5
}
},
DISTRESS: {
low: {
cssClass: "is-limit--lwr is-limit--red",
'some-key': -0.9
},
high: {
cssClass: "is-limit--upr is-limit--red",
'some-key': 0.9
}
}
};
}
});
telemetrylimitProvider.getLimitEvaluator.and.returnValue({
evaluate: function () {
return {};
}
});
openmct.telemetry.addProvider(telemetrylimitProvider);
openmct.install(new PlotVuePlugin());
element = document.createElement("div");
@ -657,6 +697,25 @@ describe("the plugin", function () {
done();
});
});
describe('limits', () => {
it('lines are not displayed by default', () => {
let limitEl = element.querySelectorAll(".js-limit-area hr");
expect(limitEl.length).toBe(0);
});
it('lines are displayed when configuration is set to true', (done) => {
config.series.models[0].set('limitLines', true);
Vue.nextTick(() => {
let limitEl = element.querySelectorAll(".js-limit-area hr");
expect(limitEl.length).toBe(4);
done();
});
});
});
});
describe('the inspector view', () => {
@ -803,7 +862,7 @@ describe("the plugin", function () {
expandControl.dispatchEvent(clickEvent);
const plotOptionsProperties = browseOptionsEl.querySelectorAll('.js-plot-options-browse-properties .grid-row');
expect(plotOptionsProperties.length).toEqual(5);
expect(plotOptionsProperties.length).toEqual(6);
});
});
@ -845,7 +904,7 @@ describe("the plugin", function () {
expandControl.dispatchEvent(clickEvent);
const plotOptionsProperties = editOptionsEl.querySelectorAll(".js-plot-options-edit-properties .grid-row");
expect(plotOptionsProperties.length).toEqual(6);
expect(plotOptionsProperties.length).toEqual(7);
});
it('shows yKeyOptions', () => {

View File

@ -83,9 +83,9 @@
@update="timePopUpdate"
/>
<button
@click="showTimePopupStart"
class="c-button c-conductor__delta-button"
ref="startOffset"
class="c-button c-conductor__delta-button"
@click="showTimePopupStart"
>
{{ offsets.start }}
</button>
@ -135,7 +135,7 @@
class="c-button c-conductor__delta-button"
@click="showTimePopupEnd"
>
{{offsets.end}}
{{ offsets.end }}
</button>
</div>

View File

@ -531,7 +531,7 @@ mct-plot {
}
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item {
.plot-legend-item, .plot-series-limit-label {
// General styles for legend items, both expanded and collapsed legend states
> * + * {
margin-left: $interiorMarginSm;

View File

@ -70,6 +70,10 @@
&.is-limit--lwr:before { content: $glyph-icon-arrow-down !important; }
}
@mixin andLine {
&.is-limit--line:before { content: '' !important; }
}
@mixin uIndicator($bg, $fg, $glyph) {
background: $bg;
color: $fg;
@ -100,12 +104,14 @@
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg, true);
@include statusIcon($colorLimitYellowIc, $glyph-icon-alert-rect);
@include andUprLwr();
@include andLine();
}
&.is-limit--red {
@include statusStyle($colorLimitRedBg, $colorLimitRedFg, true);
@include statusIcon($colorLimitRedIc, $glyph-icon-alert-triangle);
@include andUprLwr();
@include andLine();
}
}