Compare commits

...

12 Commits

Author SHA1 Message Date
Deep Tailor
f04c274d33 fix lint issues 2020-11-12 16:02:57 -08:00
Deep Tailor
3624236c26 Merge branch 'three-dot-menu-proto' of https://github.com/nasa/openmct into openmct-status-api 2020-11-12 15:05:28 -08:00
Deep Tailor
2c49e62863 merge with latest three-dots 2020-11-12 15:01:37 -08:00
Charles Hacskaylo
c2df3cdd14 CSS and markup refactoring to support addition of 'suspect' telemetry (#3499)
* CSS and markup refactoring to support addition of 'suspect' telemetry

- Significant refactoring of CSS classing: `is-missing` is now
`is-status--missing` and `is-missing__indicator` is now simply
`is-status__indicator`, allowing the wrapping `is-missing--*` class to
control what is displayed;
- New SCSS mixin @isStatus, and changes to mixin @isMissing to support
new `is-status--suspect` class;
- Changed titling for missing objects from 'This item is missing' to
'This item is missing or suspect'. **IMPORTANT NOTE** This is temporary
and should be replaced with a more robust approach to titling that
allows title strings to be defined in configuration and dynamically
applied;
- Refactored computed property `statusClass` across multiple components
to return an empty string when status is undefined - this was
previously erroneously returning `is-undefined` in that circumstance;
- Removed commented code;

* CSS and markup refactoring to support addition of 'suspect' telemetry

- Refinements to broaden capability of `is-status*` mixin;
2020-11-12 14:58:51 -08:00
Deep Tailor
5f03dc45ee fix style application in telemetry vue 2020-10-29 15:44:57 -07:00
Deep Tailor
14ac758760 Merge branch 'three-dot-menu-proto' of https://github.com/nasa/openmct into openmct-status-api 2020-10-29 15:30:48 -07:00
Deep Tailor
eba1a48a44 fix lint errors 2020-10-29 15:19:42 -07:00
Deep Tailor
4a0654dbcb add tests for status API 2020-10-29 15:07:02 -07:00
Deep Tailor
9b6d339d69 add status to tabs view 2020-10-29 13:48:51 -07:00
Deep Tailor
f90afb9277 simplifying class applications 2020-10-29 13:35:19 -07:00
Deep Tailor
018dfb1e28 Merge branch 'master' of https://github.com/nasa/openmct into openmct-status-api 2020-10-29 11:59:03 -07:00
Deep Tailor
c72a02aaa3 wip 2020-10-29 11:07:49 -07:00
26 changed files with 344 additions and 133 deletions

View File

@@ -20,12 +20,12 @@
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"
ng-class="{ 'is-missing': model.status === 'missing' }" ng-class="{ 'is-status--missing': model.status === 'missing' }"
> >
<div class="c-object-label__type-icon {{type.getCssClass()}}" <div class="c-object-label__type-icon {{type.getCssClass()}}"
ng-class="{ 'l-icon-link':location.isLink() }" ng-class="{ 'l-icon-link':location.isLink() }"
> >
<span class="is-missing__indicator" title="This item is missing"></span> <span class="is-status__indicator" title="This item is missing or suspect"></span>
</div> </div>
<div class='c-object-label__name'>{{model.name}}</div> <div class='c-object-label__name'>{{model.name}}</div>
</div> </div>

View File

@@ -246,6 +246,8 @@ define([
this.actions = new api.ActionsAPI(this); this.actions = new api.ActionsAPI(this);
this.status = new api.StatusAPI(this);
this.router = new ApplicationRouter(); this.router = new ApplicationRouter();
this.branding = BrandingAPI.default; this.branding = BrandingAPI.default;

View File

@@ -21,17 +21,14 @@
*****************************************************************************/ *****************************************************************************/
import ActionsAPI from './ActionsAPI'; import ActionsAPI from './ActionsAPI';
import ActionsCollection from './ActionCollection';
import { createOpenMct, resetApplicationState } from '../../utils/testing'; import { createOpenMct, resetApplicationState } from '../../utils/testing';
describe('The Actions API', () => { describe('The Actions API', () => {
let openmct; let openmct;
let actionsAPI; let actionsAPI;
let mockAction; let mockAction;
let invoked;
let mockObjectPath; let mockObjectPath;
let mockViewContext1; let mockViewContext1;
let mockViewContext2;
beforeEach(() => { beforeEach(() => {
openmct = createOpenMct(); openmct = createOpenMct();
@@ -46,6 +43,7 @@ describe('The Actions API', () => {
appliesTo: (objectPath, view = {}) => { appliesTo: (objectPath, view = {}) => {
if (view.getViewContext) { if (view.getViewContext) {
let viewContext = view.getViewContext(); let viewContext = view.getViewContext();
return viewContext.onlyAppliesToTestCase; return viewContext.onlyAppliesToTestCase;
} else if (objectPath.length) { } else if (objectPath.length) {
return objectPath[0].type === 'fake-folder'; return objectPath[0].type === 'fake-folder';
@@ -54,7 +52,6 @@ describe('The Actions API', () => {
return false; return false;
}, },
invoke: () => { invoke: () => {
invoked = true;
} }
}; };
mockObjectPath = [ mockObjectPath = [
@@ -83,13 +80,6 @@ describe('The Actions API', () => {
}; };
} }
}; };
mockViewContext2 = {
getViewContext:() => {
return {
onlyAppliesToTestCase: true
};
}
};
}); });
afterEach(() => { afterEach(() => {

View File

@@ -30,8 +30,8 @@ define([
'./notifications/NotificationAPI', './notifications/NotificationAPI',
'./Editor', './Editor',
'./menu/MenuAPI', './menu/MenuAPI',
'./actions/ActionsAPI' './actions/ActionsAPI',
'./status/StatusAPI'
], function ( ], function (
TimeAPI, TimeAPI,
ObjectAPI, ObjectAPI,
@@ -42,7 +42,8 @@ define([
NotificationAPI, NotificationAPI,
EditorAPI, EditorAPI,
MenuAPI, MenuAPI,
ActionsAPI ActionsAPI,
StatusAPI
) { ) {
return { return {
TimeAPI: TimeAPI, TimeAPI: TimeAPI,
@@ -54,6 +55,7 @@ define([
NotificationAPI: NotificationAPI.default, NotificationAPI: NotificationAPI.default,
EditorAPI: EditorAPI, EditorAPI: EditorAPI,
MenuAPI: MenuAPI.default, MenuAPI: MenuAPI.default,
ActionsAPI: ActionsAPI.default ActionsAPI: ActionsAPI.default,
StatusAPI: StatusAPI.default
}; };
}); });

View File

@@ -0,0 +1,67 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2020, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import EventEmitter from 'EventEmitter';
export default class StatusAPI extends EventEmitter {
constructor(openmct) {
super();
this._openmct = openmct;
this._statusCache = {};
this.get = this.get.bind(this);
this.set = this.set.bind(this);
this.observe = this.observe.bind(this);
}
get(identifier) {
let keyString = this._openmct.objects.makeKeyString(identifier);
return this._statusCache[keyString];
}
set(identifier, value) {
let keyString = this._openmct.objects.makeKeyString(identifier);
this._statusCache[keyString] = value;
this.emit(keyString, value);
}
delete(identifier) {
let keyString = this._openmct.objects.makeKeyString(identifier);
this._statusCache[keyString] = undefined;
this.emit(keyString, undefined);
delete this._statusCache[keyString];
}
observe(identifier, callback) {
let key = this._openmct.objects.makeKeyString(identifier);
this.on(key, callback);
return () => {
this.off(key, callback);
};
}
}

View File

@@ -0,0 +1,85 @@
import StatusAPI from './StatusAPI.js';
import { createOpenMct, resetApplicationState } from '../../utils/testing';
describe("The Status API", () => {
let statusAPI;
let openmct;
let identifier;
let status;
let status2;
let callback;
beforeEach(() => {
openmct = createOpenMct();
statusAPI = new StatusAPI(openmct);
identifier = {
namespace: "test-namespace",
key: "test-key"
};
status = "test-status";
status2 = 'test-status-deux';
callback = jasmine.createSpy('callback', (statusUpdate) => statusUpdate);
});
afterEach(() => {
resetApplicationState(openmct);
});
describe("set function", () => {
it("sets status for identifier", () => {
statusAPI.set(identifier, status);
let resultingStatus = statusAPI.get(identifier);
expect(resultingStatus).toEqual(status);
});
});
describe("get function", () => {
it("returns status for identifier", () => {
statusAPI.set(identifier, status2);
let resultingStatus = statusAPI.get(identifier);
expect(resultingStatus).toEqual(status2);
});
});
describe("delete function", () => {
it("deletes status for identifier", () => {
statusAPI.set(identifier, status);
let resultingStatus = statusAPI.get(identifier);
expect(resultingStatus).toEqual(status);
statusAPI.delete(identifier);
resultingStatus = statusAPI.get(identifier);
expect(resultingStatus).toBeUndefined();
});
});
describe("observe function", () => {
it("allows callbacks to be attached to status set and delete events", () => {
let unsubscribe = statusAPI.observe(identifier, callback);
statusAPI.set(identifier, status);
expect(callback).toHaveBeenCalledWith(status);
statusAPI.delete(identifier);
expect(callback).toHaveBeenCalledWith(undefined);
unsubscribe();
});
it("returns a unsubscribe function", () => {
let unsubscribe = statusAPI.observe(identifier, callback);
unsubscribe();
statusAPI.set(identifier, status);
expect(callback).toHaveBeenCalledTimes(0);
});
});
});

View File

@@ -30,18 +30,15 @@
> >
<div <div
v-if="domainObject" v-if="domainObject"
class="u-style-receiver c-telemetry-view" class="c-telemetry-view"
:class="{ :class="[statusClass]"
styleClass,
'is-missing': domainObject.status === 'missing'
}"
:style="styleObject" :style="styleObject"
:data-font-size="item.fontSize" :data-font-size="item.fontSize"
:data-font="item.font" :data-font="item.font"
@contextmenu.prevent="showContextMenu" @contextmenu.prevent="showContextMenu"
> >
<div class="is-missing__indicator" <div class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></div> ></div>
<div <div
v-if="showLabel" v-if="showLabel"
@@ -134,10 +131,14 @@ export default {
datum: undefined, datum: undefined,
domainObject: undefined, domainObject: undefined,
formats: undefined, formats: undefined,
viewKey: `alphanumeric-format-${Math.random()}` viewKey: `alphanumeric-format-${Math.random()}`,
status: ''
}; };
}, },
computed: { computed: {
statusClass() {
return (this.status) ? `is-status--${this.status}` : '';
},
showLabel() { showLabel() {
let displayMode = this.item.displayMode; let displayMode = this.item.displayMode;
@@ -215,9 +216,13 @@ export default {
this.openmct.objects.get(this.item.identifier) this.openmct.objects.get(this.item.identifier)
.then(this.setObject); .then(this.setObject);
this.openmct.time.on("bounds", this.refreshData); this.openmct.time.on("bounds", this.refreshData);
this.status = this.openmct.status.get(this.item.identifier);
this.removeStatusListener = this.openmct.status.observe(this.item.identifier, this.setStatus);
}, },
destroyed() { destroyed() {
this.removeSubscription(); this.removeSubscription();
this.removeStatusListener();
if (this.removeSelectable) { if (this.removeSelectable) {
this.removeSelectable(); this.removeSelectable();
@@ -339,6 +344,9 @@ export default {
async showContextMenu(event) { async showContextMenu(event) {
const contextMenuActions = await this.getContextMenuActions(); const contextMenuActions = await this.getContextMenuActions();
this.openmct.menus.showMenu(event.x, event.y, contextMenuActions); this.openmct.menus.showMenu(event.x, event.y, contextMenuActions);
},
setStatus(status) {
this.status = status;
} }
} }
}; };

View File

@@ -29,12 +29,12 @@
@include isMissing($absPos: true); @include isMissing($absPos: true);
.is-missing__indicator { .is-status__indicator {
top: 0; top: 0;
left: 0; left: 0;
} }
&.is-missing { &.is-status--missing {
border: $borderMissing; border: $borderMissing;
} }
} }

View File

@@ -1,11 +1,10 @@
<template> <template>
<a <a
class="l-grid-view__item c-grid-item" class="l-grid-view__item c-grid-item"
:class="{ :class="[{
'is-alias': item.isAlias === true, '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 'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
}" }, statusClass]"
:href="objectLink" :href="objectLink"
> >
<div <div
@@ -27,8 +26,8 @@
</div> </div>
</div> </div>
<div class="c-grid-item__controls"> <div class="c-grid-item__controls">
<div class="is-missing__indicator" <div class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></div> ></div>
<div <div
class="icon-people" class="icon-people"
@@ -46,9 +45,10 @@
<script> <script>
import contextMenuGesture from '../../../ui/mixins/context-menu-gesture'; import contextMenuGesture from '../../../ui/mixins/context-menu-gesture';
import objectLink from '../../../ui/mixins/object-link'; import objectLink from '../../../ui/mixins/object-link';
import statusListener from './status-listener';
export default { export default {
mixins: [contextMenuGesture, objectLink], mixins: [contextMenuGesture, objectLink, statusListener],
props: { props: {
item: { item: {
type: Object, type: Object,

View File

@@ -8,15 +8,15 @@
<a <a
ref="objectLink" ref="objectLink"
class="c-object-label" class="c-object-label"
:class="{ 'is-missing': item.model.status === 'missing' }" :class="[statusClass]"
:href="objectLink" :href="objectLink"
> >
<div <div
class="c-object-label__type-icon c-list-item__name__type-icon" class="c-object-label__type-icon c-list-item__name__type-icon"
:class="item.type.cssClass" :class="item.type.cssClass"
> >
<span class="is-missing__indicator" <span class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></span> ></span>
</div> </div>
<div class="c-object-label__name c-list-item__name__name">{{ item.model.name }}</div> <div class="c-object-label__name c-list-item__name__name">{{ item.model.name }}</div>
@@ -39,9 +39,10 @@
import moment from 'moment'; import moment from 'moment';
import contextMenuGesture from '../../../ui/mixins/context-menu-gesture'; import contextMenuGesture from '../../../ui/mixins/context-menu-gesture';
import objectLink from '../../../ui/mixins/object-link'; import objectLink from '../../../ui/mixins/object-link';
import statusListener from './status-listener';
export default { export default {
mixins: [contextMenuGesture, objectLink], mixins: [contextMenuGesture, objectLink, statusListener],
props: { props: {
item: { item: {
type: Object, type: Object,

View File

@@ -43,7 +43,7 @@
} }
} }
&.is-missing { &.is-status--missing {
@include isMissing(); @include isMissing();
[class*='__type-icon'], [class*='__type-icon'],

View File

@@ -0,0 +1,33 @@
export default {
inject: ['openmct'],
props: {
item: {
type: Object,
required: true
}
},
computed: {
statusClass() {
return (this.status) ? `is-status--${this.status}` : '';
}
},
data() {
return {
status: ''
};
},
methods: {
setStatus(status) {
this.status = status;
}
},
mounted() {
let identifier = this.item.model.identifier;
this.status = this.openmct.status.get(identifier);
this.removeStatusListener = this.openmct.status.observe(identifier, this.setStatus);
},
destroyed() {
this.removeStatusListener();
}
};

View File

@@ -111,7 +111,7 @@ import Search from '@/ui/components/search.vue';
import SearchResults from './SearchResults.vue'; import SearchResults from './SearchResults.vue';
import Sidebar from './Sidebar.vue'; import Sidebar from './Sidebar.vue';
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage'; import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
import { DEFAULT_CLASS, addNotebookEntry, createNewEmbed, getNotebookEntries } from '../utils/notebook-entries'; import { addNotebookEntry, createNewEmbed, getNotebookEntries } from '../utils/notebook-entries';
import objectUtils from 'objectUtils'; import objectUtils from 'objectUtils';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
@@ -416,14 +416,7 @@ export default {
return; return;
} }
const classList = domainObject.classList || []; this.openmct.status.delete(domainObject.identifier);
const index = classList.indexOf(DEFAULT_CLASS);
if (!classList.length || index < 0) {
return;
}
classList.splice(index, 1);
this.openmct.objects.mutate(domainObject, 'classList', classList);
}, },
searchItem(input) { searchItem(input) {
this.search = input; this.search = input;

View File

@@ -50,6 +50,7 @@ export default {
}, },
mounted() { mounted() {
this.notebookSnapshot = new Snapshot(this.openmct); this.notebookSnapshot = new Snapshot(this.openmct);
this.setDefaultNotebookStatus();
}, },
methods: { methods: {
showMenu(event) { showMenu(event) {
@@ -105,6 +106,15 @@ export default {
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element); this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
}); });
},
setDefaultNotebookStatus() {
let defaultNotebookObject = getDefaultNotebook();
if (defaultNotebookObject && defaultNotebookObject.notebookMeta) {
let notebookIdentifier = defaultNotebookObject.notebookMeta.identifier;
this.openmct.status.set(notebookIdentifier, 'notebook-default');
}
} }
} }
}; };

View File

@@ -1,6 +1,6 @@
import objectLink from '../../../ui/mixins/object-link'; import objectLink from '../../../ui/mixins/object-link';
export const DEFAULT_CLASS = 'is-notebook-default'; export const DEFAULT_CLASS = 'notebook-default';
const TIME_BOUNDS = { const TIME_BOUNDS = {
START_BOUND: 'tc.startBound', START_BOUND: 'tc.startBound',
END_BOUND: 'tc.endBound', END_BOUND: 'tc.endBound',
@@ -129,7 +129,7 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
embeds embeds
}); });
addDefaultClass(domainObject); addDefaultClass(domainObject, openmct);
openmct.objects.mutate(domainObject, 'configuration.entries', entries); openmct.objects.mutate(domainObject, 'configuration.entries', entries);
return id; return id;
@@ -199,11 +199,6 @@ export function deleteNotebookEntries(openmct, domainObject, selectedSection, se
openmct.objects.mutate(domainObject, 'configuration.entries', entries); openmct.objects.mutate(domainObject, 'configuration.entries', entries);
} }
function addDefaultClass(domainObject) { function addDefaultClass(domainObject, openmct) {
const classList = domainObject.classList || []; openmct.status.set(domainObject.identifier, DEFAULT_CLASS);
if (classList.includes(DEFAULT_CLASS)) {
return;
}
classList.push(DEFAULT_CLASS);
} }

View File

@@ -40,7 +40,7 @@
<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="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-class="{ ng-class="{
'is-missing': series.domainObject.status === 'missing' 'is-status--missing': series.domainObject.status === 'missing'
}" }"
ng-repeat="series in series track by $index" ng-repeat="series in series track by $index"
> >
@@ -48,7 +48,7 @@
<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="is-status__indicator" title="This item is missing or suspect"></span>
<span class="plot-series-name">{{ series.nameWithUnit() }}</span> <span class="plot-series-name">{{ series.nameWithUnit() }}</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 }}"
@@ -95,14 +95,14 @@
<tr ng-repeat="series in series" <tr ng-repeat="series in series"
class="plot-legend-item" class="plot-legend-item"
ng-class="{ ng-class="{
'is-missing': series.domainObject.status === 'missing' 'is-status--missing': series.domainObject.status === 'missing'
}" }"
> >
<td class="plot-series-swatch-and-name"> <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="is-status__indicator" title="This item is missing or suspect"></span>
<span class="plot-series-name">{{ series.get('name') }}</span> <span class="plot-series-name">{{ series.get('name') }}</span>
</td> </td>

View File

@@ -29,13 +29,13 @@
@click="showTab(tab, index)" @click="showTab(tab, index)"
> >
<div class="c-tabs-view__tab__label c-object-label" <div class="c-tabs-view__tab__label c-object-label"
:class="{'is-missing': tab.domainObject.status === 'missing'}" :class="[tab.status ? `is-${tab.status}` : '']"
> >
<div class="c-object-label__type-icon" <div class="c-object-label__type-icon"
:class="tab.type.definition.cssClass" :class="tab.type.definition.cssClass"
> >
<span class="is-missing__indicator" <span class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></span> ></span>
</div> </div>
<span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span> <span class="c-button__label c-object-label__name">{{ tab.domainObject.name }}</span>
@@ -192,8 +192,10 @@ export default {
}, },
addItem(domainObject) { addItem(domainObject) {
let type = this.openmct.types.get(domainObject.type) || unknownObjectType; let type = this.openmct.types.get(domainObject.type) || unknownObjectType;
let status = this.openmct.status.get(domainObject.identifier);
let tabItem = { let tabItem = {
domainObject, domainObject,
status,
type: type, type: type,
key: this.openmct.objects.makeKeyString(domainObject.identifier) key: this.openmct.objects.makeKeyString(domainObject.identifier)
}; };

View File

@@ -59,12 +59,12 @@ mct-plot {
} }
/*********************** MISSING ITEM INDICATORS */ /*********************** MISSING ITEM INDICATORS */
.is-missing__indicator { .is-status__indicator {
display: none; display: none;
} }
.is-missing { .is-status--missing {
@include isMissing(); @include isMissing();
.is-missing__indicator { .is-status__indicator {
font-size: 0.8em; font-size: 0.8em;
} }
} }

View File

@@ -129,31 +129,44 @@
} }
} }
@mixin isMissing($absPos: false) { @mixin isStatus($absPos: false) {
// Supports CSS classing as follows:
// is-status--missing, is-status--suspect, etc.
// Common styles to be applied to tree items, object labels, grid and list item views // 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 { .is-status__indicator {
display: none ; display: none ;
text-shadow: $colorBodyBg 0 0 2px; text-shadow: $colorBodyBg 0 0 2px;
color: $colorAlert;
font-family: symbolsfont; font-family: symbolsfont;
&:before { &[class^='is-status'] .is-status__indicator,
content: $glyph-icon-alert-triangle; [class^='is-status'] .is-status__indicator {
display: block !important;
} }
}
@if $absPos { @if $absPos {
.is-missing__indicator {
position: absolute; position: absolute;
z-index: 3; z-index: 3;
} }
} }
}
&.is-missing .is-missing__indicator, @mixin isMissing($absPos: false) {
.is-missing .is-missing__indicator { display: block !important; } @include isStatus($absPos);
.is-status__indicator:before {
color: $colorAlert;
content: $glyph-icon-alert-triangle;
}
}
@mixin isSuspect($absPos: false) {
@include isStatus($absPos);
.is-status__indicator:before {
color: $colorWarningLo;
content: $glyph-icon-alert-rect;
}
} }
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) { @mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {

View File

@@ -35,16 +35,13 @@
class="c-so-view__header" class="c-so-view__header"
> >
<div class="c-object-label" <div class="c-object-label"
:class="{ :class="[ statusClass ]"
classList,
'is-missing': domainObject.status === 'missing'
}"
> >
<div class="c-object-label__type-icon" <div class="c-object-label__type-icon"
:class="cssClass" :class="cssClass"
> >
<span class="is-missing__indicator" <span class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></span> ></span>
</div> </div>
<div class="c-object-label__name"> <div class="c-object-label__name">
@@ -88,8 +85,8 @@
</div> </div>
</div> </div>
<div class="is-missing__indicator" <div class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></div> ></div>
<object-view <object-view
ref="objectView" ref="objectView"
@@ -160,20 +157,22 @@ export default {
cssClass, cssClass,
complexContent, complexContent,
viewProvider, viewProvider,
statusBarItems statusBarItems,
status: ''
}; };
}, },
computed: { computed: {
classList() { statusClass() {
const classList = this.domainObject.classList; return (this.status) ? `is-status--${this.status}` : '';
if (!classList || !classList.length) {
return '';
}
return classList.join(' ');
} }
}, },
mounted() {
this.status = this.openmct.status.get(this.domainObject.identifier);
this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.setStatus);
},
beforeDestroy() { beforeDestroy() {
this.removeStatusListener();
if (this.actionCollection) { if (this.actionCollection) {
this.unlistenToActionCollection(); this.unlistenToActionCollection();
} }
@@ -264,6 +263,9 @@ export default {
let sortedActions = this.openmct.actions._groupAndSortActions(actions); let sortedActions = this.openmct.actions._groupAndSortActions(actions);
this.openmct.menus.showMenu(event.x, event.y, sortedActions); this.openmct.menus.showMenu(event.x, event.y, sortedActions);
},
setStatus(status) {
this.status = status;
} }
} }
}; };

View File

@@ -1,10 +1,7 @@
<template> <template>
<a <a
class="c-tree__item__label c-object-label" class="c-tree__item__label c-object-label"
:class="{ :class="[statusClass]"
classList,
'is-missing': observedObject.status === 'missing'
}"
draggable="true" draggable="true"
:href="objectLink" :href="objectLink"
@dragstart="dragStart" @dragstart="dragStart"
@@ -14,8 +11,8 @@
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"
> >
<span class="is-missing__indicator" <span class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></span> ></span>
</div> </div>
<div class="c-tree__item__name c-object-label__name"> <div class="c-tree__item__name c-object-label__name">
@@ -49,18 +46,11 @@ export default {
}, },
data() { data() {
return { return {
observedObject: this.domainObject observedObject: this.domainObject,
status: ''
}; };
}, },
computed: { computed: {
classList() {
const classList = this.observedObject.classList;
if (!classList || !classList.length) {
return '';
}
return classList.join(' ');
},
typeClass() { typeClass() {
let type = this.openmct.types.get(this.observedObject.type); let type = this.openmct.types.get(this.observedObject.type);
if (!type) { if (!type) {
@@ -68,6 +58,9 @@ export default {
} }
return type.definition.cssClass; return type.definition.cssClass;
},
statusClass() {
return (this.status) ? `is-status--${this.status}` : '';
} }
}, },
mounted() { mounted() {
@@ -78,8 +71,13 @@ export default {
this.$once('hook:destroyed', removeListener); this.$once('hook:destroyed', removeListener);
} }
this.removeStatusListener = this.openmct.status.observe(this.observedObject.identifier, this.setStatus);
this.status = this.openmct.status.get(this.observedObject.identifier);
this.previewAction = new PreviewAction(this.openmct); this.previewAction = new PreviewAction(this.openmct);
}, },
destroyed() {
this.removeStatusListener();
},
methods: { methods: {
navigateOrPreview(event) { navigateOrPreview(event) {
if (this.openmct.editor.isEditing()) { if (this.openmct.editor.isEditing()) {
@@ -110,6 +108,9 @@ export default {
// (eg. notabook.) // (eg. notabook.)
event.dataTransfer.setData("openmct/domain-object-path", serializedPath); event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.domainObject); event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.domainObject);
},
setStatus(status) {
this.status = status;
} }
} }
}; };

View File

@@ -110,10 +110,10 @@
} }
} }
&.is-missing { &.is-status--missing {
@include isMissing($absPos: true); @include isMissing($absPos: true);
.is-missing__indicator { .is-status__indicator {
top: $interiorMargin; top: $interiorMargin;
left: $interiorMargin; left: $interiorMargin;
} }
@@ -145,7 +145,7 @@
> .c-so-view__view-large { display: block; } > .c-so-view__view-large { display: block; }
} }
&.is-missing { &.is-status--missing {
border: $borderMissing; border: $borderMissing;
} }

View File

@@ -21,7 +21,7 @@
font-size: 1.1em; font-size: 1.1em;
} }
&.is-missing { &.is-status--missing {
@include isMissing($absPos: true); @include isMissing($absPos: true);
[class*='__type-icon']:before, [class*='__type-icon']:before,
@@ -29,7 +29,7 @@
opacity: $opacityMissing; opacity: $opacityMissing;
} }
.is-missing__indicator { .is-status__indicator {
right: -3px; right: -3px;
top: -3px; top: -3px;
transform: scale(0.7); transform: scale(0.7);

View File

@@ -2,13 +2,13 @@
<div class="c-inspector__header"> <div class="c-inspector__header">
<div v-if="!multiSelect" <div v-if="!multiSelect"
class="c-inspector__selected c-object-label" class="c-inspector__selected c-object-label"
:class="{'is-missing': isMissing }" :class="{'is-status--missing': isMissing }"
> >
<div class="c-object-label__type-icon" <div class="c-object-label__type-icon"
:class="typeCssClass" :class="typeCssClass"
> >
<span class="is-missing__indicator" <span class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></span> ></span>
</div> </div>
<span v-if="!singleSelectNonObject" <span v-if="!singleSelectNonObject"

View File

@@ -9,16 +9,13 @@
></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="{ :class="[statusClass]"
classList,
'is-missing': domainObject.status === 'missing'
}"
> >
<div class="c-object-label__type-icon" <div class="c-object-label__type-icon"
:class="type.cssClass" :class="type.cssClass"
> >
<span class="is-missing__indicator" <span class="is-status__indicator"
title="This item is missing" title="This item is missing or suspect"
></span> ></span>
</div> </div>
<span <span
@@ -149,17 +146,13 @@ export default {
viewKey: undefined, viewKey: undefined,
isEditing: this.openmct.editor.isEditing(), isEditing: this.openmct.editor.isEditing(),
notebookEnabled: this.openmct.types.get('notebook'), notebookEnabled: this.openmct.types.get('notebook'),
statusBarItems: [] statusBarItems: [],
status: ''
}; };
}, },
computed: { computed: {
classList() { statusClass() {
const classList = this.domainObject.classList; return (this.status) ? `is-status--${this.status}` : '';
if (!classList || !classList.length) {
return '';
}
return classList.join(' ');
}, },
currentView() { currentView() {
return this.views.filter(v => v.key === this.viewKey)[0] || {}; return this.views.filter(v => v.key === this.viewKey)[0] || {};
@@ -225,6 +218,13 @@ export default {
this.mutationObserver = this.openmct.objects.observe(this.domainObject, '*', (domainObject) => { this.mutationObserver = this.openmct.objects.observe(this.domainObject, '*', (domainObject) => {
this.domainObject = domainObject; this.domainObject = domainObject;
}); });
if (this.removeStatusListener) {
this.removeStatusListener();
}
this.status = this.openmct.status.get(this.domainObject.identifier, this.setStatus);
this.removeStatusListener = this.openmct.status.observe(this.domainObject.identifier, this.setStatus);
}, },
viewProvider(viewProvider) { viewProvider(viewProvider) {
if (this.actionCollection) { if (this.actionCollection) {
@@ -259,6 +259,10 @@ export default {
this.unlistenToActionCollection(); this.unlistenToActionCollection();
} }
if (this.removeStatusListener) {
this.removeStatusListener();
}
document.removeEventListener('click', this.closeViewAndSaveMenu); document.removeEventListener('click', this.closeViewAndSaveMenu);
window.removeEventListener('click', this.promptUserbeforeNavigatingAway); window.removeEventListener('click', this.promptUserbeforeNavigatingAway);
}, },
@@ -366,6 +370,9 @@ export default {
}, },
toggleLock(flag) { toggleLock(flag) {
this.openmct.objects.mutate(this.domainObject, 'locked', flag); this.openmct.objects.mutate(this.domainObject, 'locked', flag);
},
setStatus(status) {
this.status = status;
} }
} }
}; };

View File

@@ -380,7 +380,7 @@
margin-left: $interiorMarginLg; margin-left: $interiorMarginLg;
min-width: 0; min-width: 0;
.is-missing__indicator { .is-status__indicator {
right: -5px !important; right: -5px !important;
top: -4px !important; top: -4px !important;
} }