Merge branch 'master' into adding-units

Merg'n main branch
This commit is contained in:
Jamie Vigliotta
2020-07-14 10:25:39 -07:00
41 changed files with 620 additions and 235 deletions

3
.gitignore vendored
View File

@ -37,4 +37,7 @@ protractor/logs
# npm-debug log # npm-debug log
npm-debug.log npm-debug.log
# karma reports
report.*.json
package-lock.json package-lock.json

View File

@ -59,7 +59,7 @@
"moment-timezone": "0.5.28", "moment-timezone": "0.5.28",
"node-bourbon": "^4.2.3", "node-bourbon": "^4.2.3",
"node-sass": "^4.9.2", "node-sass": "^4.9.2",
"painterro": "^0.2.65", "painterro": "^1.0.35",
"printj": "^1.2.1", "printj": "^1.2.1",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"request": "^2.69.0", "request": "^2.69.0",

View File

@ -19,7 +19,13 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<div class="c-object-label"> <div class="c-object-label"
<div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div> ng-class="{ 'is-missing': model.status === 'missing' }"
>
<div class="c-object-label__type-icon {{type.getCssClass()}}"
ng-class="{ 'l-icon-link':location.isLink() }"
>
<span class="is-missing__indicator" title="This item is missing"></span>
</div>
<div class='c-object-label__name'>{{model.name}}</div> <div class='c-object-label__name'>{{model.name}}</div>
</div> </div>

View File

@ -31,10 +31,16 @@
<div <div
v-if="domainObject" v-if="domainObject"
class="c-telemetry-view" class="c-telemetry-view"
:class="styleClass" :class="{
styleClass,
'is-missing': domainObject.status === 'missing'
}"
:style="styleObject" :style="styleObject"
@contextmenu.prevent="showContextMenu" @contextmenu.prevent="showContextMenu"
> >
<div class="is-missing__indicator"
title="This item is missing"
></div>
<div <div
v-if="showLabel" v-if="showLabel"
class="c-telemetry-view__label" class="c-telemetry-view__label"

View File

@ -26,4 +26,14 @@
@include abs(); @include abs();
border: 1px solid transparent; border: 1px solid transparent;
} }
&.is-missing {
@include isMissing($absPos: true);
border: $borderMissing;
.is-missing__indicator {
top: 0;
left: 0;
}
}
} }

View File

@ -1,13 +1,18 @@
<template> <template>
<a <a
class="l-grid-view__item c-grid-item" class="l-grid-view__item c-grid-item"
:class="{ 'is-alias': item.isAlias === true }" :class="{
'is-alias': item.isAlias === true,
'is-missing': item.model.status === 'missing',
'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
}"
:href="objectLink" :href="objectLink"
> >
<div <div
class="c-grid-item__type-icon" class="c-grid-item__type-icon"
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'" :class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"
></div> >
</div>
<div class="c-grid-item__details"> <div class="c-grid-item__details">
<!-- Name and metadata --> <!-- Name and metadata -->
<div <div
@ -22,6 +27,9 @@
</div> </div>
</div> </div>
<div class="c-grid-item__controls"> <div class="c-grid-item__controls">
<div class="is-missing__indicator"
title="This item is missing"
></div>
<div <div
class="icon-people" class="icon-people"
title="Shared" title="Shared"

View File

@ -7,13 +7,19 @@
<td class="c-list-item__name"> <td class="c-list-item__name">
<a <a
ref="objectLink" ref="objectLink"
class="c-object-label"
:class="{ 'is-missing': item.model.status === 'missing' }"
:href="objectLink" :href="objectLink"
> >
<div <div
class="c-list-item__type-icon" class="c-object-label__type-icon c-list-item__type-icon"
:class="item.type.cssClass" :class="item.type.cssClass"
></div> >
<div class="c-list-item__name-value">{{ item.model.name }}</div> <span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<div class="c-object-label__name c-list-item__name">{{ item.model.name }}</div>
</a> </a>
</td> </td>
<td class="c-list-item__type"> <td class="c-list-item__type">

View File

@ -38,7 +38,15 @@
// Object is an alias to an original. // Object is an alias to an original.
[class*='__type-icon'] { [class*='__type-icon'] {
@include isAlias(); @include isAlias();
color: $colorIconAliasForKeyFilter; }
}
&.is-missing {
@include isMissing();
[class*='__type-icon'],
[class*='__details'] {
opacity: $opacityMissing;
} }
} }
@ -85,15 +93,14 @@
body.desktop & { body.desktop & {
$transOutMs: 300ms; $transOutMs: 300ms;
flex-flow: column nowrap; flex-flow: column nowrap;
transition: background $transOutMs ease-in-out; transition: $transOutMs ease-in-out;
&:hover { &:hover {
background: $colorItemBgHov; filter: $filterItemHoverFg;
transition: $transIn; transition: $transIn;
.c-grid-item__type-icon { .c-grid-item__type-icon {
filter: $colorKeyFilterHov; transform: scale(1.1);
transform: scale(1);
transition: $transInBounce; transition: $transInBounce;
} }
} }
@ -103,7 +110,7 @@
} }
&__controls { &__controls {
align-items: start; align-items: baseline;
flex: 0 0 auto; flex: 0 0 auto;
order: 1; order: 1;
.c-info-button, .c-info-button,
@ -115,7 +122,6 @@
font-size: floor($gridItemDesk / 3); font-size: floor($gridItemDesk / 3);
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%; margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
order: 2; order: 2;
transform: scale(0.9);
transform-origin: center; transform-origin: center;
transition: all $transOutMs ease-in-out; transition: all $transOutMs ease-in-out;
} }

View File

@ -1,37 +1,17 @@
/******************************* LIST ITEM */ /******************************* LIST ITEM */
.c-list-item { .c-list-item {
&__name a {
display: flex;
> * + * { margin-left: $interiorMarginSm; }
}
&__type-icon { &__type-icon {
// Have to do it this way instead of using icon-* class, due to need to apply alias to the icon color: $colorItemTreeIcon;
color: $colorKey;
display: inline-block;
width: 1em;
margin-right:$interiorMarginSm;
} }
&__name-value { &__name {
@include ellipsize(); @include ellipsize();
} }
&.is-alias { &.is-alias {
// Object is an alias to an original. // Object is an alias to an original.
[class*='__type-icon'] { [class*='__type-icon'] {
&:after { @include isAlias();
color: $colorIconAlias;
content: $glyph-icon-link;
font-family: symbolsfont;
display: block;
position: absolute;
text-shadow: rgba(black, 0.5) 0 1px 2px;
top: auto; left: -1px; bottom: 1px; right: auto;
transform-origin: bottom left;
transform: scale(0.65);
}
} }
} }
} }

View File

@ -13,7 +13,8 @@
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background: $colorListItemBgHov; background: $colorItemTreeHoverBg;
filter: $filterHov;
transition: $transIn; transition: $transIn;
} }
} }

View File

@ -28,17 +28,22 @@
ng-click="legend.set('expanded', !legend.get('expanded'));"> ng-click="legend.set('expanded', !legend.get('expanded'));">
</div> </div>
<div class="c-plot-legend__wrapper"> <div class="c-plot-legend__wrapper"
ng-class="{ 'is-cursor-locked': !!lockHighlightPoint }">
<!-- COLLAPSED PLOT LEGEND --> <!-- COLLAPSED PLOT LEGEND -->
<div class="plot-wrapper-collapsed-legend" <div class="plot-wrapper-collapsed-legend"
ng-class="{'icon-cursor-lock': !!lockHighlightPoint}"> ng-class="{'is-cursor-locked': !!lockHighlightPoint }">
<div class="c-state-indicator__alert-cursor-lock icon-cursor-lock" title="Cursor is point locked. Click anywhere in the plot to unlock."></div>
<div class="plot-legend-item" <div class="plot-legend-item"
ng-repeat="series in series track by $index"> ng-class="{'is-missing': series.domainObject.status === 'missing'}"
ng-repeat="series in series track by $index"
>
<div class="plot-series-swatch-and-name"> <div class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch" <span class="plot-series-color-swatch "
ng-style="{ 'background-color': series.get('color').asHexString() }"> ng-style="{ 'background-color': series.get('color').asHexString() }">
</span> </span>
<span class="is-missing__indicator" title="This item is missing"></span>
<span class="plot-series-name">{{ series.get('name') }}</span> <span class="plot-series-name">{{ series.get('name') }}</span>
</div> </div>
<div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}" <div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}"
@ -55,7 +60,10 @@
</div> </div>
<!-- EXPANDED PLOT LEGEND --> <!-- EXPANDED PLOT LEGEND -->
<div class="plot-wrapper-expanded-legend"> <div class="plot-wrapper-expanded-legend"
ng-class="{'is-cursor-locked': !!lockHighlightPoint }"
>
<div class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock" title="Click anywhere in the plot to unlock."> Cursor locked to point</div>
<table> <table>
<thead> <thead>
<tr> <tr>
@ -76,12 +84,15 @@
</th> </th>
</tr> </tr>
</thead> </thead>
<tr ng-repeat="series in series" class="plot-legend-item"> <tr ng-repeat="series in series"
<td class="plot-series-swatch-and-name" class="plot-legend-item"
ng-class="{'icon-cursor-lock': !!lockHighlightPoint}"> ng-class="{'is-missing': series.domainObject.status === 'missing'}"
>
<td class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch" <span class="plot-series-color-swatch"
ng-style="{ 'background-color': series.get('color').asHexString() }"> ng-style="{ 'background-color': series.get('color').asHexString() }">
</span> </span>
<span class="is-missing__indicator" title="This item is missing"></span>
<span class="plot-series-name">{{ series.get('name') }}</span> <span class="plot-series-name">{{ series.get('name') }}</span>
</td> </td>
@ -134,7 +145,7 @@
{{option.name}} {{option.name}}
</option> </option>
</select> </select>
<mct-ticks axis="yAxis"> <mct-ticks axis="yAxis">
<div ng-repeat="tick in ticks track by tick.value" <div ng-repeat="tick in ticks track by tick.value"

View File

@ -44,7 +44,7 @@
</li> </li>
<li class="grid-row"> <li class="grid-row">
<div class="grid-cell label" <div class="grid-cell label"
title="The line rendering style for this series.">Line Style</div> title="The rendering method to join lines for this series.">Line Method</div>
<div class="grid-cell value">{{ { <div class="grid-cell value">{{ {
'none': 'None', 'none': 'None',
'linear': 'Linear interpolation', 'linear': 'Linear interpolation',
@ -56,7 +56,7 @@
<div class="grid-cell label" <div class="grid-cell label"
title="Whether markers are displayed, and their size.">Markers</div> title="Whether markers are displayed, and their size.">Markers</div>
<div class="grid-cell value"> <div class="grid-cell value">
{{series.get('markers') ? "Enabled: " + series.get('markerSize') + "px" : "Disabled"}} {{ series.markerOptionsDisplayText() }}
</div> </div>
</li> </li>
<li class="grid-row"> <li class="grid-row">

View File

@ -52,7 +52,7 @@
</li> </li>
<li class="grid-row"> <li class="grid-row">
<div class="grid-cell label" <div class="grid-cell label"
title="The line rendering style for this series.">Line Style</div> title="The rendering method to join lines for this series.">Line Method</div>
<div class="grid-cell value"> <div class="grid-cell value">
<select ng-model="form.interpolate"> <select ng-model="form.interpolate">
<option value="none">None</option> <option value="none">None</option>
@ -64,12 +64,27 @@
<li class="grid-row"> <li class="grid-row">
<div class="grid-cell label" <div class="grid-cell label"
title="Whether markers are displayed.">Markers</div> title="Whether markers are displayed.">Markers</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.markers"/></div> <div class="grid-cell value">
<input type="checkbox" ng-model="form.markers"/>
<select
ng-show="form.markers"
ng-model="form.markerShape">
<option
ng-repeat="option in markerShapeOptions"
value="{{ option.value }}"
ng-selected="option.value == form.markerShape"
>
{{ option.name }}
</option>
</select>
</div>
</li> </li>
<li class="grid-row"> <li class="grid-row">
<div class="grid-cell label" <div class="grid-cell label"
title="Display markers visually denoting points in alarm.">Alarm Markers</div> title="Display markers visually denoting points in alarm.">Alarm Markers</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.alarmMarkers"/></div> <div class="grid-cell value">
<input type="checkbox" ng-model="form.alarmMarkers"/>
</div>
</li> </li>
<li class="grid-row" ng-show="form.markers || form.alarmMarkers"> <li class="grid-row" ng-show="form.markers || form.alarmMarkers">
<div class="grid-cell label" <div class="grid-cell label"

View File

@ -371,7 +371,8 @@ function (
chartElement.getBuffer(), chartElement.getBuffer(),
chartElement.color().asRGBAArray(), chartElement.color().asRGBAArray(),
chartElement.count, chartElement.count,
chartElement.series.get('markerSize') chartElement.series.get('markerSize'),
chartElement.series.get('markerShape')
); );
}; };
@ -395,9 +396,10 @@ function (
this.offset.yVal(highlight.point, highlight.series) this.offset.yVal(highlight.point, highlight.series)
]), ]),
color = highlight.series.get('color').asRGBAArray(), color = highlight.series.get('color').asRGBAArray(),
pointCount = 1; pointCount = 1,
shape = highlight.series.get('markerShape');
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE); this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape);
}; };
MCTChartController.prototype.drawRectangles = function () { MCTChartController.prototype.drawRectangles = function () {

View File

@ -25,12 +25,14 @@ define([
'lodash', 'lodash',
'../configuration/Model', '../configuration/Model',
'../lib/extend', '../lib/extend',
'EventEmitter' 'EventEmitter',
'../draw/MarkerShapes'
], function ( ], function (
_, _,
Model, Model,
extend, extend,
EventEmitter EventEmitter,
MARKER_SHAPES
) { ) {
/** /**
@ -56,6 +58,7 @@ define([
* `linear` (points are connected via straight lines), or * `linear` (points are connected via straight lines), or
* `stepAfter` (points are connected by steps). * `stepAfter` (points are connected by steps).
* `markers`: boolean, whether or not this series should render with markers. * `markers`: boolean, whether or not this series should render with markers.
* `markerShape`: string, shape of markers.
* `markerSize`: number, size in pixels of markers for this series. * `markerSize`: number, size in pixels of markers for this series.
* `alarmMarkers`: whether or not to display alarm markers for this series. * `alarmMarkers`: whether or not to display alarm markers for this series.
* `stats`: An object that tracks the min and max y values observed in this * `stats`: An object that tracks the min and max y values observed in this
@ -101,6 +104,7 @@ define([
xKey: options.collection.plot.xAxis.get('key'), xKey: options.collection.plot.xAxis.get('key'),
yKey: range.key, yKey: range.key,
markers: true, markers: true,
markerShape: 'point',
markerSize: 2.0, markerSize: 2.0,
alarmMarkers: true alarmMarkers: true
}; };
@ -410,6 +414,18 @@ define([
} else { } else {
this.filters = deepCopiedFilters; this.filters = deepCopiedFilters;
} }
},
markerOptionsDisplayText: function () {
const showMarkers = this.get('markers');
if (!showMarkers) {
return "Disabled";
}
const markerShapeKey = this.get('markerShape');
const markerShape = MARKER_SHAPES[markerShapeKey].label;
const markerSize = this.get('markerSize');
return `${markerShape}: ${markerSize}px`;
} }
}); });

View File

@ -23,10 +23,12 @@
define([ define([
'EventEmitter', 'EventEmitter',
'../lib/eventHelpers' '../lib/eventHelpers',
'./MarkerShapes'
], function ( ], function (
EventEmitter, EventEmitter,
eventHelpers eventHelpers,
MARKER_SHAPES
) { ) {
/** /**
@ -121,18 +123,17 @@ define([
buf, buf,
color, color,
points, points,
pointSize pointSize,
shape
) { ) {
var i = 0, const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
offset = pointSize / 2;
this.setColor(color); this.setColor(color);
for (; i < points; i++) { for (let i = 0; i < points; i++) {
this.c2d.fillRect( drawC2DShape(
this.x(buf[i * 2]) - offset, this.x(buf[i * 2]),
this.y(buf[i * 2 + 1]) - offset, this.y(buf[i * 2 + 1]),
pointSize,
pointSize pointSize
); );
} }

View File

@ -23,30 +23,64 @@
define([ define([
'EventEmitter', 'EventEmitter',
'../lib/eventHelpers' '../lib/eventHelpers',
'./MarkerShapes'
], function ( ], function (
EventEmitter, EventEmitter,
eventHelpers eventHelpers,
MARKER_SHAPES
) { ) {
// WebGL shader sources (for drawing plain colors) // WebGL shader sources (for drawing plain colors)
var FRAGMENT_SHADER = [ const FRAGMENT_SHADER = `
"precision mediump float;", precision mediump float;
"uniform vec4 uColor;", uniform vec4 uColor;
"void main(void) {", uniform int uMarkerShape;
"gl_FragColor = uColor;",
"}" void main(void) {
].join('\n'), gl_FragColor = uColor;
VERTEX_SHADER = [
"attribute vec2 aVertexPosition;", if (uMarkerShape > 1) {
"uniform vec2 uDimensions;", vec2 clipSpacePointCoord = 2.0 * gl_PointCoord - 1.0;
"uniform vec2 uOrigin;",
"uniform float uPointSize;", if (uMarkerShape == 2) { // circle
"void main(void) {", float distance = length(clipSpacePointCoord);
"gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
"gl_PointSize = uPointSize;", if (distance > 1.0) {
"}" discard;
].join('\n'); }
} else if (uMarkerShape == 3) { // diamond
float distance = abs(clipSpacePointCoord.x) + abs(clipSpacePointCoord.y);
if (distance > 1.0) {
discard;
}
} else if (uMarkerShape == 4) { // triangle
float x = clipSpacePointCoord.x;
float y = clipSpacePointCoord.y;
float distance = 2.0 * x - 1.0;
float distance2 = -2.0 * x - 1.0;
if (distance > y || distance2 > y) {
discard;
}
}
}
}
`;
const VERTEX_SHADER = `
attribute vec2 aVertexPosition;
uniform vec2 uDimensions;
uniform vec2 uOrigin;
uniform float uPointSize;
void main(void) {
gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);
gl_PointSize = uPointSize;
}
`;
/** /**
* Create a draw api utilizing WebGL. * Create a draw api utilizing WebGL.
@ -90,6 +124,7 @@ define([
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER); this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER); this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
this.gl.compileShader(this.vertexShader); this.gl.compileShader(this.vertexShader);
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER); this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER); this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
this.gl.compileShader(this.fragmentShader); this.gl.compileShader(this.fragmentShader);
@ -105,6 +140,7 @@ define([
// shader programs (to pass values into shaders at draw-time) // shader programs (to pass values into shaders at draw-time)
this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition"); this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition");
this.uColor = this.gl.getUniformLocation(this.program, "uColor"); this.uColor = this.gl.getUniformLocation(this.program, "uColor");
this.uMarkerShape = this.gl.getUniformLocation(this.program, "uMarkerShape");
this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions"); this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions");
this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin"); this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin");
this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize"); this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize");
@ -114,9 +150,6 @@ define([
// Create a buffer to holds points which will be drawn // Create a buffer to holds points which will be drawn
this.buffer = this.gl.createBuffer(); this.buffer = this.gl.createBuffer();
// Use a line width of 2.0 for legibility
this.gl.lineWidth(2.0);
// Enable blending, for smoothness // Enable blending, for smoothness
this.gl.enable(this.gl.BLEND); this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
@ -138,14 +171,18 @@ define([
((v - this.origin[1]) / this.dimensions[1]) * this.height; ((v - this.origin[1]) / this.dimensions[1]) * this.height;
}; };
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) { DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
if (this.isContextLost) { if (this.isContextLost) {
return; return;
} }
const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW); this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0); this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
this.gl.uniform4fv(this.uColor, color); this.gl.uniform4fv(this.uColor, color);
this.gl.uniform1i(this.uMarkerShape, shapeCode)
this.gl.drawArrays(drawType, 0, points); this.gl.drawArrays(drawType, 0, points);
}; };
@ -210,12 +247,12 @@ define([
* Draw the buffer as points. * Draw the buffer as points.
* *
*/ */
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) { DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
if (this.isContextLost) { if (this.isContextLost) {
return; return;
} }
this.gl.uniform1f(this.uPointSize, pointSize); this.gl.uniform1f(this.uPointSize, pointSize);
this.doDraw(this.gl.POINTS, buf, color, points); this.doDraw(this.gl.POINTS, buf, color, points, shape);
}; };
/** /**

View File

@ -0,0 +1,90 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([], function () {
/**
* @label string (required) display name of shape
* @drawWebGL integer (unique, required) index provided to WebGL Fragment Shader
* @drawC2D function (required) canvas2d draw function
*/
const MARKER_SHAPES = {
point: {
label: 'Point',
drawWebGL: 1,
drawC2D: function (x, y, size) {
const offset = size / 2;
this.c2d.fillRect(x - offset, y - offset, size, size);
}
},
circle: {
label: 'Circle',
drawWebGL: 2,
drawC2D: function (x, y, size) {
const radius = size / 2;
this.c2d.beginPath();
this.c2d.arc(x, y, radius, 0, 2 * Math.PI, false);
this.c2d.closePath();
this.c2d.fill();
}
},
diamond: {
label: 'Diamond',
drawWebGL: 3,
drawC2D: function (x, y, size) {
const offset = size / 2;
const top = [x, y + offset];
const right = [x + offset, y];
const bottom = [x, y - offset];
const left = [x - offset, y];
this.c2d.beginPath();
this.c2d.moveTo(...top);
this.c2d.lineTo(...right);
this.c2d.lineTo(...bottom);
this.c2d.lineTo(...left);
this.c2d.closePath();
this.c2d.fill();
}
},
triangle: {
label: 'Triangle',
drawWebGL: 4,
drawC2D: function (x, y, size) {
const offset = size / 2;
const v1 = [x, y - offset];
const v2 = [x - offset, y + offset];
const v3 = [x + offset, y + offset];
this.c2d.beginPath();
this.c2d.moveTo(...v1);
this.c2d.lineTo(...v2);
this.c2d.lineTo(...v3);
this.c2d.closePath();
this.c2d.fill();
}
}
};
return MARKER_SHAPES;
});

View File

@ -22,9 +22,11 @@
define([ define([
'./PlotModelFormController', './PlotModelFormController',
'../draw/MarkerShapes',
'lodash' 'lodash'
], function ( ], function (
PlotModelFormController, PlotModelFormController,
MARKER_SHAPES,
_ _
) { ) {
@ -93,6 +95,13 @@ define([
value: o.key value: o.key
}; };
}); });
this.$scope.markerShapeOptions = Object.entries(MARKER_SHAPES)
.map(([key, obj]) => {
return {
name: obj.label,
value: key
};
});
}, },
fields: [ fields: [
@ -108,6 +117,10 @@ define([
modelProp: 'markers', modelProp: 'markers',
objectPath: dynamicPathForKey('markers') objectPath: dynamicPathForKey('markers')
}, },
{
modelProp: 'markerShape',
objectPath: dynamicPathForKey('markerShape')
},
{ {
modelProp: 'markerSize', modelProp: 'markerSize',
coerce: Number, coerce: Number,

View File

@ -71,8 +71,6 @@ define([
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this); this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this); this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this); this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee();
}; };
MCTPlotController.prototype.initialize = function () { MCTPlotController.prototype.initialize = function () {
@ -83,11 +81,6 @@ define([
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this); this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this); this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee();
this.listenTo(this.$window, 'keydown', this.toggleInteractionMode, this);
this.listenTo(this.$window, 'keyup', this.resetInteractionMode, this);
this.$scope.rectangles = []; this.$scope.rectangles = [];
this.$scope.tickWidth = 0; this.$scope.tickWidth = 0;
@ -243,12 +236,16 @@ define([
}; };
MCTPlotController.prototype.onMouseDown = function ($event) { MCTPlotController.prototype.onMouseDown = function ($event) {
// do not monitor drag events on browser context click
if (event.ctrlKey) {
return;
}
this.listenTo(this.$window, 'mouseup', this.onMouseUp, this); this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this); this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
if (this.allowPan) { if (event.altKey) {
return this.startPan($event); return this.startPan($event);
} } else {
if (this.allowMarquee) {
return this.startMarquee($event); return this.startMarquee($event);
} }
}; };
@ -261,11 +258,11 @@ define([
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint; this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
} }
if (this.allowPan) { if (this.pan) {
return this.endPan($event); return this.endPan($event);
} }
if (this.allowMarquee) { if (this.marquee) {
return this.endMarquee($event); return this.endMarquee($event);
} }
}; };
@ -289,6 +286,9 @@ define([
}; };
MCTPlotController.prototype.startMarquee = function ($event) { MCTPlotController.prototype.startMarquee = function ($event) {
this.$canvas.removeClass('plot-drag');
this.$canvas.addClass('plot-marquee');
this.trackMousePosition($event); this.trackMousePosition($event);
if (this.positionOverPlot) { if (this.positionOverPlot) {
this.freeze(); this.freeze();
@ -444,6 +444,9 @@ define([
}; };
MCTPlotController.prototype.startPan = function ($event) { MCTPlotController.prototype.startPan = function ($event) {
this.$canvas.addClass('plot-drag');
this.$canvas.removeClass('plot-marquee');
this.trackMousePosition($event); this.trackMousePosition($event);
this.freeze(); this.freeze();
this.pan = { this.pan = {
@ -486,32 +489,6 @@ define([
this.$scope.$emit('user:viewport:change:end'); this.$scope.$emit('user:viewport:change:end');
}; };
MCTPlotController.prototype.watchForMarquee = function () {
this.$canvas.removeClass('plot-drag');
this.$canvas.addClass('plot-marquee');
this.allowPan = false;
this.allowMarquee = true;
};
MCTPlotController.prototype.watchForPan = function () {
this.$canvas.addClass('plot-drag');
this.$canvas.removeClass('plot-marquee');
this.allowPan = true;
this.allowMarquee = false;
};
MCTPlotController.prototype.toggleInteractionMode = function (event) {
if (event.keyCode === 18) { // control key.
this.watchForPan();
}
};
MCTPlotController.prototype.resetInteractionMode = function (event) {
if (event.keyCode === 18) {
this.watchForMarquee();
}
};
MCTPlotController.prototype.freeze = function () { MCTPlotController.prototype.freeze = function () {
this.config.yAxis.set('frozen', true); this.config.yAxis.set('frozen', true);
this.config.xAxis.set('frozen', true); this.config.xAxis.set('frozen', true);

View File

@ -1,7 +0,0 @@
# Espresso Theme
A light colored theme for the Open MCT user interface.
## Installation
```js
openmct.install(openmct.plugins.Snow());
```

View File

@ -3,7 +3,7 @@
<div <div
class="c-tabs-view__tabs-holder c-tabs" class="c-tabs-view__tabs-holder c-tabs"
:class="{ :class="{
'is-dragging': isDragging, 'is-dragging': isDragging && allowEditing,
'is-mouse-over': allowDrop 'is-mouse-over': allowDrop
}" }"
> >
@ -22,14 +22,21 @@
<button <button
v-for="(tab,index) in tabsList" v-for="(tab,index) in tabsList"
:key="index" :key="index"
class="c-tabs-view__tab c-tab" class="c-tabs-view__tab c-tab c-object-label"
:class="[ :class="{
{'is-current': isCurrent(tab)}, 'is-current': isCurrent(tab),
tab.type.definition.cssClass 'is-missing': tab.domainObject.status === 'missing'
]" }"
@click="showTab(tab, index)" @click="showTab(tab, index)"
> >
<span class="c-button__label">{{ tab.domainObject.name }}</span> <div class="c-object-label__type-icon"
:class="tab.type.definition.cssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
</button> </button>
</div> </div>
<div <div
@ -38,15 +45,6 @@
class="c-tabs-view__object-holder" class="c-tabs-view__object-holder"
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}" :class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
> >
<div
v-if="currentTab"
class="c-tabs-view__object-name c-object-label l-browse-bar__object-name--w"
:class="currentTab.type.definition.cssClass"
>
<div class="l-browse-bar__object-name c-object-label__name">
{{ currentTab.domainObject.name }}
</div>
</div>
<object-view <object-view
v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)" v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)"
class="c-tabs-view__object" class="c-tabs-view__object"
@ -58,6 +56,12 @@
<script> <script>
import ObjectView from '../../../ui/components/ObjectView.vue'; import ObjectView from '../../../ui/components/ObjectView.vue';
import {
getSearchParam,
setSearchParam,
deleteSearchParam
} from 'utils/openmctLocation';
var unknownObjectType = { var unknownObjectType = {
definition: { definition: {
@ -71,26 +75,45 @@ export default {
components: { components: {
ObjectView ObjectView
}, },
props: {
isEditing: {
type: Boolean,
required: true
}
},
data: function () { data: function () {
let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
return { return {
internalDomainObject: this.domainObject, internalDomainObject: this.domainObject,
currentTab: {}, currentTab: {},
currentTabIndex: undefined,
tabsList: [], tabsList: [],
setCurrentTab: true, setCurrentTab: true,
isDragging: false, isDragging: false,
allowDrop: false allowDrop: false,
searchTabKey: `tabs.pos.${keyString}`
}; };
}, },
computed: {
allowEditing() {
return !this.internalDomainObject.locked && this.isEditing;
}
},
mounted() { mounted() {
if (this.composition) { if (this.composition) {
this.composition.on('add', this.addItem); this.composition.on('add', this.addItem);
this.composition.on('remove', this.removeItem); this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.onReorder); this.composition.on('reorder', this.onReorder);
this.composition.load().then(() => { this.composition.load().then(() => {
let currentTabIndex = this.domainObject.currentTabIndex; let currentTabIndexFromURL = getSearchParam(this.searchTabKey);
let currentTabIndexFromDomainObject = this.internalDomainObject.currentTabIndex;
if (currentTabIndex !== undefined && this.tabsList.length > currentTabIndex) { if (currentTabIndexFromURL !== null) {
this.currentTab = this.tabsList[currentTabIndex]; this.setCurrentTabByIndex(currentTabIndexFromURL);
} else if (currentTabIndexFromDomainObject !== undefined) {
this.setCurrentTabByIndex(currentTabIndexFromDomainObject);
this.storeCurrentTabIndexInURL(currentTabIndexFromDomainObject);
} }
}); });
} }
@ -100,20 +123,29 @@ export default {
document.addEventListener('dragstart', this.dragstart); document.addEventListener('dragstart', this.dragstart);
document.addEventListener('dragend', this.dragend); document.addEventListener('dragend', this.dragend);
}, },
beforeDestroy() {
this.persistCurrentTabIndex(this.currentTabIndex);
},
destroyed() { destroyed() {
this.composition.off('add', this.addItem); this.composition.off('add', this.addItem);
this.composition.off('remove', this.removeItem); this.composition.off('remove', this.removeItem);
this.composition.off('reorder', this.onReorder); this.composition.off('reorder', this.onReorder);
this.unsubscribe(); this.unsubscribe();
this.clearCurrentTabIndexFromURL();
document.removeEventListener('dragstart', this.dragstart); document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend); document.removeEventListener('dragend', this.dragend);
}, },
methods:{ methods:{
setCurrentTabByIndex(index) {
if (this.tabsList[index]) {
this.currentTab = this.tabsList[index];
}
},
showTab(tab, index) { showTab(tab, index) {
if (index !== undefined) { if (index !== undefined) {
this.storeCurrentTabIndex(index); this.storeCurrentTabIndexInURL(index);
} }
this.currentTab = tab; this.currentTab = tab;
@ -133,6 +165,10 @@ export default {
this.setCurrentTab = false; this.setCurrentTab = false;
} }
}, },
reset() {
this.currentTab = {};
this.setCurrentTab = true;
},
removeItem(identifier) { removeItem(identifier) {
let pos = this.tabsList.findIndex(tab => let pos = this.tabsList.findIndex(tab =>
tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key
@ -144,6 +180,10 @@ export default {
if (this.isCurrent(tabToBeRemoved)) { if (this.isCurrent(tabToBeRemoved)) {
this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1); this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1);
} }
if (!this.tabsList.length) {
this.reset();
}
}, },
onReorder(reorderPlan) { onReorder(reorderPlan) {
let oldTabs = this.tabsList.slice(); let oldTabs = this.tabsList.slice();
@ -154,7 +194,7 @@ export default {
}, },
onDrop(e) { onDrop(e) {
this.setCurrentTab = true; this.setCurrentTab = true;
this.storeCurrentTabIndex(this.tabsList.length); this.storeCurrentTabIndexInURL(this.tabsList.length);
}, },
dragstart(e) { dragstart(e) {
if (e.dataTransfer.types.includes('openmct/domain-object-path')) { if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
@ -177,8 +217,19 @@ export default {
updateInternalDomainObject(domainObject) { updateInternalDomainObject(domainObject) {
this.internalDomainObject = domainObject; this.internalDomainObject = domainObject;
}, },
storeCurrentTabIndex(index) { persistCurrentTabIndex(index) {
this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index); this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
},
storeCurrentTabIndexInURL(index) {
let currentTabIndexInURL = getSearchParam(this.searchTabKey);
if (index !== currentTabIndexInURL) {
setSearchParam(this.searchTabKey, index);
this.currentTabIndex = index;
}
},
clearCurrentTabIndexFromURL() {
deleteSearchParam(this.searchTabKey);
} }
} }
} }

View File

@ -42,20 +42,28 @@ define([
let component; let component;
return { return {
show: function (element) { show: function (element, editMode) {
component = new Vue({ component = new Vue({
el: element, el: element,
components: { components: {
TabsComponent: TabsComponent.default TabsComponent: TabsComponent.default
}, },
data() {
return {
isEditing: editMode
};
},
provide: { provide: {
openmct, openmct,
domainObject, domainObject,
composition: openmct.composition.get(domainObject) composition: openmct.composition.get(domainObject)
}, },
template: '<tabs-component></tabs-component>' template: '<tabs-component :isEditing="isEditing"></tabs-component>'
}); });
}, },
onEditModeChange(editMode) {
component.isEditing = editMode;
},
destroy: function (element) { destroy: function (element) {
component.$destroy(); component.$destroy();
component = undefined; component = undefined;

View File

@ -151,6 +151,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing $browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px; $browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4); $browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
$filterItemHoverFg: brightness(1.2) contrast(1.1);
$filterItemMissing: brightness(0.6) grayscale(1);
$opacityMissing: 0.5;
$borderMissing: 1px dashed $colorAlert !important;
/************************************************** EDITING */ /************************************************** EDITING */
$editUIColor: $uiColor; // Base color $editUIColor: $uiColor; // Base color

View File

@ -155,6 +155,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing $browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px; $browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4); $browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
$filterItemHoverFg: brightness(1.2) contrast(1.1);
$filterItemMissing: contrast(0.2);
$opacityMissing: 0.5;
$borderMissing: 1px dashed $colorAlert !important;
/************************************************** EDITING */ /************************************************** EDITING */
$editUIColor: $uiColor; // Base color $editUIColor: $uiColor; // Base color

View File

@ -151,6 +151,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing $browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px; $browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4); $browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
$filterItemHoverFg: brightness(0.9);
$filterItemMissing: contrast(0.2);
$opacityMissing: 0.4;
$borderMissing: 1px dashed $colorAlert !important;
/************************************************** EDITING */ /************************************************** EDITING */
$editUIColor: $uiColor; // Base color $editUIColor: $uiColor; // Base color

View File

@ -61,7 +61,7 @@ $plotXBarH: 35px;
$plotLegendH: 20px; $plotLegendH: 20px;
$plotLegendWidthCollapsed: 20%; $plotLegendWidthCollapsed: 20%;
$plotLegendWidthExpanded: 50%; $plotLegendWidthExpanded: 50%;
$plotSwatchD: 10px; $plotSwatchD: 12px;
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW); // 1: Top, 2: right, 3: bottom, 4: left $plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW); // 1: Top, 2: right, 3: bottom, 4: left
$plotMinH: 95px; $plotMinH: 95px;
$controlBarH: 25px; $controlBarH: 25px;

View File

@ -420,9 +420,7 @@ select {
margin: 1px 1px 0 0; margin: 1px 1px 0 0;
padding: $interiorMargin $interiorMarginLg; padding: $interiorMargin $interiorMarginLg;
white-space: nowrap; white-space: nowrap;
--notchSize: 7px; --notchSize: 7px;
clip-path: clip-path:
polygon( polygon(
0% 0%, 0% 0%,
@ -433,8 +431,12 @@ select {
0% 100% 0% 100%
); );
> * + * {
margin-left: $interiorMargin;
}
@include hover() { @include hover() {
background: $colorBtnBgHov; filter: $filterHov;
} }
&.is-current { &.is-current {

View File

@ -43,7 +43,9 @@ mct-plot {
.c-plot, .c-plot,
.gl-plot { .gl-plot {
.s-status-taking-snapshot & { overflow: hidden;
.s-status-taking-snapshot & {
.c-control-bar { .c-control-bar {
display: none; display: none;
} }
@ -51,6 +53,17 @@ mct-plot {
display: none; display: none;
} }
} }
/*********************** MISSING ITEM INDICATORS */
.is-missing__indicator {
display: none;
}
.is-missing {
@include isMissing();
.is-missing__indicator {
font-size: 0.8em;
}
}
} }
.c-plot { .c-plot {
@ -74,6 +87,7 @@ mct-plot {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
flex-direction: column; flex-direction: column;
overflow: hidden;
} }
&--stacked { &--stacked {
@ -102,18 +116,17 @@ mct-plot {
} }
} }
.gl-plot { .gl-plot {
display: flex; display: flex;
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: $plotMinH;
/*********************** AXIS AND DISPLAY AREA */ /*********************** AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area { .plot-wrapper-axis-and-display-area {
position: relative; position: relative;
flex: 1 1 auto; flex: 1 1 auto;
min-height: $plotMinH;
} }
.gl-plot-wrapper-display-area-and-x-axis { .gl-plot-wrapper-display-area-and-x-axis {
@ -196,7 +209,6 @@ mct-plot {
left: 0; top: 0; right: auto; bottom: 0; left: 0; top: 0; right: auto; bottom: 0;
padding-left: 5px; padding-left: 5px;
text-orientation: mixed; text-orientation: mixed;
//overflow: hidden;
writing-mode: vertical-lr; writing-mode: vertical-lr;
&:before { &:before {
// Icon denoting configurability // Icon denoting configurability
@ -368,12 +380,6 @@ mct-plot {
z-index: -10; z-index: -10;
.l-view-section { .l-view-section {
//$m: $interiorMargin;
//top: $m !important;
//right: $m;
//bottom: $m;
//left: $m;
.s-status-timeconductor-unsynced .holder-plot { .s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced { .t-object-alert.t-alert-unsynced {
display: none; display: none;
@ -429,14 +435,18 @@ mct-plot {
/****************** _LEGEND.SCSS */ /****************** _LEGEND.SCSS */
.gl-plot-legend, .gl-plot-legend,
.c-plot-legend { .c-plot-legend {
overflow: hidden;
&__wrapper { &__wrapper {
// Holds view-control and both collapsed and expanded legends // Holds view-control and both collapsed and expanded legends
flex: 1 1 auto; flex: 1 1 auto;
overflow: auto; // Prevents collapsed legend from forcing scrollbars on higher parent containers height: 100%;
overflow: auto;
padding: 2px;
} }
&__view-control { &__view-control {
padding-top: 2px; padding-top: 4px;
margin-right: $interiorMarginSm; margin-right: $interiorMarginSm;
} }
@ -481,15 +491,21 @@ mct-plot {
/***************** GENERAL STYLES, ALL STATES */ /***************** GENERAL STYLES, ALL STATES */
.plot-legend-item { .plot-legend-item {
// General styles for legend items, both expanded and collapsed legend states // General styles for legend items, both expanded and collapsed legend states
> * + * {
margin-left: $interiorMarginSm;
}
.plot-series-color-swatch { .plot-series-color-swatch {
border-radius: $smallCr; border-radius: 30%; //$smallCr;
border: 1px solid $colorBodyBg; border: 1px solid $colorBodyBg;
display: inline-block; display: inline-block;
flex: 0 0 auto;
height: $plotSwatchD; height: $plotSwatchD;
width: $plotSwatchD; width: $plotSwatchD;
} }
.plot-series-name { .plot-series-name {
display: inline; display: inline;
@include ellipsize();
} }
.plot-series-value { .plot-series-value {
@ -497,6 +513,16 @@ mct-plot {
} }
} }
.plot-series-swatch-and-name {
display: flex;
flex: 0 1 auto;
align-items: center;
> * + * {
margin-left: $interiorMarginSm;
}
}
.plot-wrapper-expanded-legend { .plot-wrapper-expanded-legend {
flex: 1 1 auto; flex: 1 1 auto;
} }
@ -505,9 +531,6 @@ mct-plot {
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; } &.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; } &.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
&.plot-legend-collapsed .icon-cursor-lock::before { padding-right: 5px; }
&.plot-legend-expanded .icon-cursor-lock::before { padding-right: 5px; }
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; } &.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; } &.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; } &.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
@ -521,19 +544,13 @@ mct-plot {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: stretch; justify-content: stretch;
&:not(:first-child) {
margin-left: $interiorMarginLg;
}
.plot-series-swatch-and-name, .plot-series-swatch-and-name,
.plot-series-value { .plot-series-value {
@include ellipsize(); @include ellipsize();
flex: 1 1 auto; flex: 1 1 auto;
} }
.plot-series-swatch-and-name {
margin-right: $interiorMarginSm;
}
.plot-series-value { .plot-series-value {
text-align: left; text-align: left;
} }
@ -543,7 +560,7 @@ mct-plot {
/***************** GENERAL STYLES, EXPANDED */ /***************** GENERAL STYLES, EXPANDED */
&.plot-legend-expanded { &.plot-legend-expanded {
.gl-plot-legend { .gl-plot-legend {
// max-height: 70%; max-height: 70%;
} }
.plot-wrapper-expanded-legend { .plot-wrapper-expanded-legend {
@ -564,6 +581,11 @@ mct-plot {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
> .plot-legend-item + .plot-legend-item {
// Space between plot items
margin-left: $interiorMarginLg;
}
} }
} }
} }
@ -595,12 +617,17 @@ mct-plot {
min-width: 0; min-width: 0;
flex: 1 1 auto; flex: 1 1 auto;
overflow-y: auto; overflow-y: auto;
> * + * {
// Space between plot items
margin-top: $interiorMarginSm;
}
} }
.plot-legend-item { .plot-legend-item {
margin-bottom: 1px;
margin-left: 0; margin-left: 0;
flex-wrap: wrap; flex-wrap: nowrap;
.plot-series-swatch-and-name { .plot-series-swatch-and-name {
@include ellipsize();
flex: 0 1 auto; flex: 0 1 auto;
min-width: 20%; min-width: 20%;
} }
@ -654,3 +681,24 @@ mct-plot {
display: inline-block !important; display: inline-block !important;
} }
} }
/*********************** CURSOR LOCK INDICATOR */
[class*='c-state-indicator__alert-cursor-lock'] {
display: none;
}
[class*='is-cursor-locked'] {
background: rgba($colorInfo, 0.1);
[class*='c-state-indicator__alert-cursor-lock'] {
@include userSelectNone();
color: $colorInfo;
display: block;
margin-right: $interiorMarginSm;
&[class*='--verbose'] {
padding: $interiorMarginSm;
}
}
}

View File

@ -117,6 +117,30 @@
} }
} }
@mixin isMissing($absPos: false) {
// Common styles to be applied to tree items, object labels, grid and list item views
//opacity: 0.7;
//pointer-events: none; // Don't think we can do this, as disables title hover on icon element
.is-missing__indicator {
display: block;
text-shadow: $colorBodyBg 0 0 2px;
color: $colorAlert;
font-family: symbolsfont;
&:before {
content: $glyph-icon-alert-triangle;
}
}
@if $absPos {
.is-missing__indicator {
position: absolute;
z-index: 3;
}
}
}
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) { @mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
background-image: linear-gradient(-45deg, background-image: linear-gradient(-45deg,
rgba($c, $a) 25%, transparent 25%, rgba($c, $a) 25%, transparent 25%,

View File

@ -49,8 +49,6 @@ table {
td { td {
vertical-align: top; vertical-align: top;
} }
a { color: $colorBtnMajorBg; }
} }
.is-editing { .is-editing {

View File

@ -13,7 +13,6 @@
@import "../plugins/filters/components/filters-view.scss"; @import "../plugins/filters/components/filters-view.scss";
@import "../plugins/filters/components/global-filters.scss"; @import "../plugins/filters/components/global-filters.scss";
@import "../plugins/flexibleLayout/components/flexible-layout.scss"; @import "../plugins/flexibleLayout/components/flexible-layout.scss";
@import "../plugins/folderView/components/grid-item.scss";
@import "../plugins/folderView/components/grid-view.scss"; @import "../plugins/folderView/components/grid-view.scss";
@import "../plugins/folderView/components/list-item.scss"; @import "../plugins/folderView/components/list-item.scss";
@import "../plugins/folderView/components/list-view.scss"; @import "../plugins/folderView/components/list-view.scss";

View File

@ -24,13 +24,24 @@
class="c-so-view has-local-controls" class="c-so-view has-local-controls"
:class="{ :class="{
'c-so-view--no-frame': !hasFrame, 'c-so-view--no-frame': !hasFrame,
'has-complex-content': complexContent 'has-complex-content': complexContent,
'is-missing': domainObject.status === 'missing'
}" }"
> >
<div class="c-so-view__header"> <div class="c-so-view__header">
<div class="c-object-label" <div class="c-object-label"
:class="[cssClass, classList]" :class="{
classList,
'is-missing': domainObject.status === 'missing'
}"
> >
<div class="c-object-label__type-icon"
:class="cssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<div class="c-object-label__name"> <div class="c-object-label__name">
{{ domainObject && domainObject.name }} {{ domainObject && domainObject.name }}
</div> </div>
@ -46,6 +57,9 @@
@click="expand" @click="expand"
></button> ></button>
</div> </div>
<div class="is-missing__indicator"
title="This item is missing"
></div>
<object-view <object-view
ref="objectView" ref="objectView"
class="c-so-view__object-view" class="c-so-view__object-view"

View File

@ -1,7 +1,10 @@
<template> <template>
<a <a
class="c-tree__item__label c-object-label" class="c-tree__item__label c-object-label"
:class="classList" :class="{
classList,
'is-missing': observedObject.status === 'missing'
}"
draggable="true" draggable="true"
:href="objectLink" :href="objectLink"
@dragstart="dragStart" @dragstart="dragStart"
@ -10,8 +13,14 @@
<div <div
class="c-tree__item__type-icon c-object-label__type-icon" class="c-tree__item__type-icon c-object-label__type-icon"
:class="typeClass" :class="typeClass"
></div> >
<div class="c-tree__item__name c-object-label__name">{{ observedObject.name }}</div> <span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<div class="c-tree__item__name c-object-label__name">
{{ observedObject.name }}
</div>
</a> </a>
</template> </template>

View File

@ -301,7 +301,7 @@ export default {
objectPath= this.currentObjectPath || this.objectPath, objectPath= this.currentObjectPath || this.objectPath,
parentObject = objectPath[1]; parentObject = objectPath[1];
return [browseObject, parentObject, this.currentObject].every(object => !object.locked); return [browseObject, parentObject, this.currentObject].every(object => object && !object.locked);
} }
} }
} }

View File

@ -2,6 +2,10 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
&.is-missing {
border: $borderMissing;
}
/*************************** HEADER */ /*************************** HEADER */
&__header { &__header {
flex: 0 0 auto; flex: 0 0 auto;
@ -39,6 +43,15 @@
> .c-so-view__local-controls { > .c-so-view__local-controls {
top: $interiorMarginSm; right: $interiorMarginSm; top: $interiorMarginSm; right: $interiorMarginSm;
} }
&.is-missing {
@include isMissing($absPos: true);
.is-missing__indicator {
top: $interiorMargin;
left: $interiorMargin;
}
}
} }
&__local-controls { &__local-controls {

View File

@ -3,23 +3,38 @@
// Used mostly in trees and lists // Used mostly in trees and lists
display: flex; display: flex;
align-items: center; align-items: center;
flex: 0 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
> * + * { margin-left: $interiorMargin; }
&__name { &__name {
@include ellipsize(); @include ellipsize();
display: inline; display: inline;
} }
&__type-icon, &__type-icon {
&:before {
// Type icon. Must be an HTML entity to allow inclusion of alias indicator. // Type icon. Must be an HTML entity to allow inclusion of alias indicator.
display: block; display: block;
flex: 0 0 auto; flex: 0 0 auto;
font-size: 1.1em; font-size: 1.1em;
opacity: 0.6; //margin-right: $interiorMargin;
margin-right: $interiorMargin; }
&.is-missing {
@include isMissing($absPos: true);
[class*='__type-icon']:before,
[class*='__type-icon']:after{
opacity: $opacityMissing;
}
.is-missing__indicator {
right: -3px;
top: -3px;
transform: scale(0.7);
}
} }
} }
@ -27,6 +42,8 @@
border-radius: $controlCr; border-radius: $controlCr;
padding: $interiorMarginSm 1px; padding: $interiorMarginSm 1px;
> * + * { margin-left: $interiorMarginSm; }
&__name { &__name {
display: inline; display: inline;
width: 100%; width: 100%;

View File

@ -1,16 +1,24 @@
<template> <template>
<div class="c-inspector__header"> <div class="c-inspector__header">
<div v-if="!multiSelect && !singleSelectNonObject" <div v-if="!multiSelect"
class="c-inspector__selected-w c-object-label" class="c-inspector__selected-w c-object-label"
:class="typeCssClass" :class="{'is-missing': domainObject.status === 'missing' }"
> >
<span class="c-inspector__selected c-object-label__name">{{ item.name }}</span> <div class="c-object-label__type-icon"
</div> :class="typeCssClass"
<div v-if="singleSelectNonObject" >
class="c-inspector__selected-w c-object-label" <span class="is-missing__indicator"
:class="typeCssClass" title="This item is missing"
> ></span>
<span class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object">Layout Object</span> </div>
<span v-if="!singleSelectNonObject"
class="c-inspector__selected c-object-label__name"
>{{ item.name }}</span>
<span v-if="singleSelectNonObject"
class="c-inspector__selected c-object-label__name c-inspector__selected--non-domain-object"
>Layout Object</span>
</div> </div>
<div v-if="multiSelect" <div v-if="multiSelect"
class="c-inspector__multiple-selected-w" class="c-inspector__multiple-selected-w"

View File

@ -8,8 +8,18 @@
></button> ></button>
<div <div
class="l-browse-bar__object-name--w c-object-label" class="l-browse-bar__object-name--w c-object-label"
:class="[ type.cssClass, classList ]" :class="{
classList,
'is-missing': domainObject.status === 'missing'
}"
> >
<div class="c-object-label__type-icon"
:class="type.cssClass"
>
<span class="is-missing__indicator"
title="This item is missing"
></span>
</div>
<span <span
class="l-browse-bar__object-name c-object-label__name c-input-inline" class="l-browse-bar__object-name c-object-label__name c-input-inline"
contenteditable contenteditable

View File

@ -354,9 +354,9 @@
@include headerFont(1.4em); @include headerFont(1.4em);
min-width: 0; min-width: 0;
&:before { .is-missing__indicator {
// Icon right: -5px !important;
margin-right: $interiorMargin; top: -4px !important;
} }
} }

View File

@ -52,12 +52,13 @@
padding: $interiorMarginSm $interiorMargin; padding: $interiorMarginSm $interiorMargin;
transition: background 150ms ease; transition: background 150ms ease;
&__type-icon {
color: $colorItemTreeIcon;
}
&:hover { &:hover {
background: $colorItemTreeHoverBg; background: $colorItemTreeHoverBg;
filter: $filterHov;
[class*="__name"] {
color: $colorItemTreeHoverFg;
}
} }
&.is-navigated-object, &.is-navigated-object,
@ -81,12 +82,6 @@
margin-left: $interiorMarginSm; margin-left: $interiorMarginSm;
} }
&:hover {
.c-tree__item__type-icon:before {
color: $colorItemTreeIconHover;
}
}
&.is-navigated-object, &.is-navigated-object,
&.is-selected { &.is-selected {
.c-tree__item__type-icon:before { .c-tree__item__type-icon:before {
@ -115,10 +110,6 @@
color: $colorItemTreeFg; color: $colorItemTreeFg;
} }
&__type-icon {
color: $colorItemTreeIcon;
}
&.is-alias { &.is-alias {
// Object is an alias to an original. // Object is an alias to an original.
[class*='__type-icon'] { [class*='__type-icon'] {