Compare commits

...

33 Commits

Author SHA1 Message Date
97ccaa58c7 show notifications error for rejected telemetry requests (#2334)
* show notification error for rejected telemetry requests

* change notification message details
2019-03-29 15:52:44 -07:00
08ef932926 Use instead of to avoid double digest issue (#2346) 2019-03-29 15:38:39 -07:00
1d2ed0398c Default routing (#2342)
* Working version of default navigation to last child

* Implemented as separate route to clean up code a bit
2019-03-29 14:36:15 -07:00
5a00e0c549 Fix for Chrome 73 overflow bug effecting Telem Tables in Flex Layouts (#2341)
* Voodoo fix for Chrome 73 overflow bug effecting Telem Tables in Flex
Layouts;

* Update table.vue
2019-03-29 13:34:16 -07:00
ebcf47733f Merge pull request #2345 from nasa/revert-2344-properties-dialog-digest
LGTM. Revert "Force digest on compilation of overlay template"
2019-03-29 13:29:52 -07:00
381d7e7615 Revert "Force digest on compilation of overlay template (#2344)"
This reverts commit 8246b47668.
2019-03-29 13:21:45 -07:00
8246b47668 Force digest on compilation of overlay template (#2344) 2019-03-29 10:38:56 -07:00
bc5e300ba9 Optionally provide list of object types to show as views rather than telemetry in Display Layouts (#2339)
* Optionally provide list of object types to show as views rather than alpha numerics to display layouts

* Only make table view available for objects that have telemetry to show
2019-03-28 22:21:27 -07:00
57efef3160 Digest after telemetry returned. (#2343) 2019-03-28 22:19:22 -07:00
dfc5a9f040 Tabs view fixes plus elements pool (#2340)
* use reorder api

* fix regression where elements pool doesnt update on remove

* fix issue where tabs was not updating when changes are discarded
2019-03-28 17:45:07 -07:00
57443d227d Misc UI 6 (#2338)
* Sanding and shimming on loading CSS

- Wait spinners in the tree;
- Move spinner and loading CSS from legacy to global.scss;

* Misc UI 6
- Better approach to Time Conductor overflow;
- Fixed main page left/right clipping issue;
- Fixed table header bg styling that had issues in legacy tables;
- Fixed Time Conductor datetime picker clipping issue;
- New .c-message--simple for use in Summary Widgets;
- Better styling for header in empty Tabs view;
- Fixed Chrome 73 scrolling bug in Summary Widgets;
- Fixed problem in Inspector <li> elements from new wait spinner;
- Fixed color of <a> tags in tables to be more visible;
styling;

* Misc UI 6

- Fix VISTA session selector not scrolling (Chrome 73 overflow bug);

* Misc UI 6

- Fix VISTA session selector not scrolling (Chrome 73 overflow bug);
2019-03-28 16:29:39 -07:00
d36441db73 Persistence dialog fix (#2337)
* discard persistence error dialog

* update tests
2019-03-28 13:55:39 -07:00
327782835e save object before triggering mutate (#2336) 2019-03-28 13:54:27 -07:00
994f6be535 do not return null unsubscribe function (#2335) 2019-03-27 18:09:13 -07:00
72fc8a24a5 Fixed for flex-related overflow scrolling issues in Chrome 73/Firefox (#2333) 2019-03-26 22:57:41 -07:00
07002c12eb set default theme to snow for VISTA deployments 2019-03-26 14:50:25 -07:00
c688d19e15 Fix bug introduced into .c-table 2019-03-26 11:48:03 -07:00
c0ce448dc3 Misc UI 5 (#2332)
* Style fixes for Inspector and location elements

- Code cleanup;
- Remove legacy styles;

* Tab styling WIP, for VISTA Venue dialog

* Add new c-tabs styles, replaces c-compact-button

- Remove c-compact-button and mixin;
- Refactor to use c-tabs in Tabs View;
- New notched look for tabs;

* Tweaks to c-tabs

* Misc various

- Increased mouse wheel zoom and changed to use const;
- Fixed squishy grippys in Elements pool;
- Fixed Time Conductor to prevent overrunning right pane when main pane
is very small;
- Changed message text when leaving Layout editing;
- Fixed z-index problem with splitter bars and VISTA Indicator hover
bubbles;
- Restored support for legacy `l-input-lg` to allow large text input
fields in form generation;
- Modded styles in Properties dialog to fix issue with label column
colliding with inputs when the label text is long;

* Restore hover hide/show to local controls in Summary Widgets

- Also fixed rotation transition for disclosure controls;

* Refinement to overflow hidden for Time Conductor UI

* Fix Time Conductor layout in mobile

* Fix Filter tree items in Inspector

* Move .selector-list out from within .form .form-row to allow more
flexible usage;

* Significant theme updates; table layout and Summary styling added

- Reorganized status constants;
- Added base styles for selected and active styles;
- Added styling for selected and active buttons;
- c-table changed from absolute pos to relative;
- Added c-table-and-summary styling;

* Tweaks to Location component
2019-03-26 11:38:38 -07:00
6c479d6d59 Change tree wait spinner from span to li 2019-03-26 10:59:31 -07:00
76ba487261 Remove action works on missing objects (#2330) 2019-03-26 09:35:52 -07:00
e3f4da19f9 Table migration (#2327)
* Added table migration code

* First working version

* Fixed issues with objects missing from composition
2019-03-25 22:19:33 -07:00
c7ffcbf7e0 Fix path routing issue that prevented object navigation in different deployment paths (#2331) 2019-03-25 22:17:59 -07:00
a27b3737f1 Fixes testathon 3/21 (#2328)
* fix error in location.vue because of drawing objects in selection

* add conditional to check if view is editable before forcing edit after create

* show original location only in inspector, add original location for drawing objects

* fix document title

* set document title in browse.js

* sort items in create menu

* sort children in tree by name

* remove ordering from tree items

* add loading spinner

* fix minor bug
2019-03-25 18:26:39 -07:00
78dccf1e0a Persist table sort options (#2329) 2019-03-25 11:35:47 -07:00
9cb7e09aef Better drag reordering affordance in Elements pool (#2326)
* Better drag reordering affordance in Elements pool

* add isDragging

* Better drag reordering affordance in Elements pool

* Only add dragend handler after drag starts
2019-03-23 10:01:38 -07:00
4111c12895 Added background property to table __headers-w element (#2325) 2019-03-21 15:37:09 -07:00
b6ec023920 format created and modified time to utc (#2324) 2019-03-21 15:14:45 -07:00
e8e7067993 Fix legacy messages (#2323)
* Fix legacy message styling

- Code cleanup;
- Enable constants-mobile;
- Add _legacy-messages.scss;
- Add status color fg colors to theme constants;

* CSS refactoring, significant migration for messages classes

- Many messages classes migrated;
- c-click-icon > c-icon-button;
- c-click-icon rewritten;
- __close-btn refined in dialogs;

* About and splash screen styling

- Fixed splash in About screen;

* Update _mixins.scss

* Convert c-overlay__close-button to button

- <a> -> <button>;
- Set color of button;
- Normalized naming from close-btn to close-button;

* Fixed brightness filter issue on elements in overlays in VISTA;

* Fix dismiss button
2019-03-21 15:07:16 -07:00
0e9319e97b Plot options 4.1 (#2303)
* working crosshairs -- todo -- add toggle option

* add ability to toggle crosshair

* add zoom in by onscreen button to plot

* add zoom out functionality

* switch positions of zoom in and out buttons

* working zoom with mousewheel

* add better logic to storing plot history on mouse wheel zoom

* Local controls styling in plots and imagery

- WIP

* Plot local controls, cursor guides

- Local control classes much refined;
- Cursor guides styled, theme constants added;

* Fix local controls in imagery

- LC styles refined;
- New theme constant;

* Better approach to loading

- New .c-loading--overlay that doesn't block the cursor;
- Applied to plot views (telem, overlay and stacked plots);

* Plot styles, local controls refined

- Moved plot classes into their own scss file;
- More refinement on local control styles;
- Plot local controls layout finalized;
- Buttons smallified in layout frame context;

* Convert export buttons from <a> tags

* Stubbed in cursor guide buttons in templates

- WIP!

* add toggle-cursor-guide-button to stacked plots

* move cursor guide button to top left in all plots for consistency

* Plot local controls layout refined

- Also: global styling for *[disabled]

* Change c-button 'is-enabled' to 'is-active'

* Added check for if crosshairs enabled before tracking
2019-03-21 13:57:41 -07:00
df53af7b4d Inspector location (#2317)
* working original location - todo link location

* remove link location for now

* remove legacy getPath and implement new getOriginalPath API call

* simplify getOriginalPath, and use path to calculate parent paths in location.vue
2019-03-21 12:41:40 -07:00
bcbf244fd2 Fix foreground color of option elements in selects (#2300)
* Fix foreground color of option elements in selects

- Windows users in Snow theme reporting white foreground color on system
 background color (light gray) when interacting with select elements.
 This fix hard-codes the option's `color` attribute to black.

* Add background property to style select options for Windows
2019-03-21 12:39:29 -07:00
7ff5febae0 Tables - Maintain stable sort. Requery for historical data on time system change. Parse telemetry time values before comparing to bounds. (#2322) 2019-03-21 11:00:48 -07:00
019d108bb2 Reorder api update (#2319)
* Added 'reorder' function to composition API

* Re-implemented reordering in Elements

* Make LAD table editable

* Remove test spec focus

* Fixing bugs with event listeners

* Clean up listeners properly in Elements pool

* Fixed race condition on drag-and-drop to initiate edit

* Implement reordering in LAD tables

* Reorder events emit full reorder plan

* Fixed failing specs
2019-03-21 10:59:08 -07:00
92 changed files with 2468 additions and 1805 deletions

View File

@ -54,6 +54,9 @@
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"
}));
openmct.install(openmct.plugins.DisplayLayout({
showAsView: ['summary-widget', 'example.imagery']
}));
openmct.install(openmct.plugins.Conductor({
menuOptions: [
{

View File

@ -31,7 +31,6 @@ define([
"./src/navigation/NavigateAction",
"./src/navigation/OrphanNavigationHandler",
"./src/windowing/NewTabAction",
"./src/windowing/WindowTitler",
"./res/templates/browse.html",
"./res/templates/browse-object.html",
"./res/templates/browse/object-header.html",
@ -52,7 +51,6 @@ define([
NavigateAction,
OrphanNavigationHandler,
NewTabAction,
WindowTitler,
browseTemplate,
browseObjectTemplate,
objectHeaderTemplate,
@ -226,14 +224,6 @@ define([
}
],
"runs": [
{
"implementation": WindowTitler,
"depends": [
"navigationService",
"$rootScope",
"$document"
]
},
{
"implementation": OrphanNavigationHandler,
"depends": [

View File

@ -1,51 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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 () {
/**
* Updates the title of the current window to reflect the name
* of the currently navigated-to domain object.
* @memberof platform/commonUI/browse
* @constructor
*/
function WindowTitler(navigationService, $rootScope, $document) {
// Look up name of the navigated domain object...
function getNavigatedObjectName() {
var navigatedObject = navigationService.getNavigation();
return navigatedObject && navigatedObject.getModel().name;
}
// Set the window title...
function setTitle(name) {
$document[0].title = name;
}
// Watch the former, and invoke the latter
$rootScope.$watch(getNavigatedObjectName, setTitle);
}
return WindowTitler;
}
);

View File

@ -1,78 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
/**
* WindowTitlerSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/windowing/WindowTitler"],
function (WindowTitler) {
describe("The window titler", function () {
var mockNavigationService,
mockRootScope,
mockDocument,
mockDomainObject,
titler; // eslint-disable-line
beforeEach(function () {
mockNavigationService = jasmine.createSpyObj(
'navigationService',
['getNavigation']
);
mockRootScope = jasmine.createSpyObj(
'$rootScope',
['$watch']
);
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['getModel']
);
mockDocument = [{}];
mockDomainObject.getModel.and.returnValue({ name: 'Test name' });
mockNavigationService.getNavigation.and.returnValue(mockDomainObject);
titler = new WindowTitler(
mockNavigationService,
mockRootScope,
mockDocument
);
});
it("listens for changes to the name of the navigated object", function () {
expect(mockRootScope.$watch).toHaveBeenCalledWith(
jasmine.any(Function),
jasmine.any(Function)
);
expect(mockRootScope.$watch.calls.mostRecent().args[0]())
.toEqual('Test name');
});
it("sets the title to the name of the navigated object", function () {
mockRootScope.$watch.calls.mostRecent().args[1]("Some name");
expect(mockDocument[0].title).toEqual("Some name");
});
});
}
);

View File

@ -65,7 +65,8 @@ define([
"depends": [
"$document",
"$compile",
"$rootScope"
"$rootScope",
"$timeout"
]
}
],

View File

@ -1,10 +1,10 @@
<div class="l-message"
<div class="c-message"
ng-class="'message-severity-' + ngModel.severity">
<div class="w-message-contents">
<div class="top-bar">
<div class="title">{{ngModel.title}}</div>
<div class="c-message__top-bar">
<div class="c-message__title">{{ngModel.title}}</div>
</div>
<div class="hint" ng-hide="ngModel.hint === undefined">
<div class="c-message__hint" ng-hide="ngModel.hint === undefined">
{{ngModel.hint}}
<span ng-if="ngModel.timestamp !== undefined">[{{ngModel.timestamp}}]</span>
</div>
@ -16,17 +16,17 @@
ng-model="ngModel"
ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include>
</div>
<div class="bottom-bar">
<a ng-repeat="dialogOption in ngModel.options"
class="s-button"
<div class="c-overlay__button-bar">
<button ng-repeat="dialogOption in ngModel.options"
class="c-button"
ng-click="dialogOption.callback()">
{{dialogOption.label}}
</a>
<a class="s-button major"
</button>
<button class="c-button c-button--major"
ng-if="ngModel.primaryOption"
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</a>
</button>
</div>
</div>
</div>

View File

@ -1,25 +1,25 @@
<div class="l-message"
<div class="c-message"
ng-class="'message-severity-' + ngModel.severity">
<div class="w-message-contents">
<div class="top-bar">
<div class="title">{{ngModel.message}}</div>
<div class="c-message__top-bar">
<div class="c-message__title">{{ngModel.message}}</div>
</div>
<div class="message-body">
<mct-include key="'progress-bar'"
ng-model="ngModel"
ng-show="ngModel.progressPerc !== undefined"></mct-include>
</div>
<div class="bottom-bar">
<a ng-repeat="dialogOption in ngModel.options"
class="s-button"
<div class="c-overlay__button-bar">
<button ng-repeat="dialogOption in ngModel.options"
class="c-button"
ng-click="dialogOption.callback()">
{{dialogOption.label}}
</a>
<a class="s-button major"
</button>
<button class="c-button c-button--major"
ng-if="ngModel.primaryOption"
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</a>
</button>
</div>
</div>
</div>

View File

@ -1,22 +1,23 @@
<mct-container key="overlay">
<div class="t-message-list">
<div class="top-bar">
<div class="dialog-title">{{ngModel.dialog.title}}</div>
<div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 ||
ngModel.dialog.messages.length == 0">s</span>
<div class="t-message-list c-overlay__contents">
<div class="c-overlay__top-bar">
<div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
<div class="c-overlay__dialog-hint">Displaying {{ngModel.dialog.messages.length}} message<span
ng-show="ngModel.dialog.messages.length > 1 ||
ngModel.dialog.messages.length == 0">s</span>
</div>
</div>
<div class="w-messages">
<div class="w-messages c-overlay__messages">
<mct-include
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
key="'notification-message'" ng-model="msg.model"></mct-include>
</div>
<div class="bottom-bar">
<a ng-repeat="dialogAction in ngModel.dialog.actions"
class="s-button major"
<div class="c-overlay__bottom-bar">
<button ng-repeat="dialogAction in ngModel.dialog.actions"
class="c-button c-button--major"
ng-click="dialogAction.action()">
{{dialogAction.label}}
</a>
</button>
</div>
</div>
</mct-container>

View File

@ -22,9 +22,9 @@
<div class="c-overlay l-overlay-small" ng-class="{'delayEntry100ms' : ngModel.delay}">
<div class="c-overlay__blocker"></div>
<div class="c-overlay__outer">
<a ng-click="ngModel.cancel()"
<button ng-click="ngModel.cancel()"
ng-if="ngModel.cancel"
class="c-click-icon c-overlay__close-button icon-x-in-circle"></a>
class="c-click-icon c-overlay__close-button icon-x-in-circle"></button>
<div class="c-overlay__contents" ng-transclude></div>
</div>
</div>

View File

@ -44,8 +44,9 @@ define(
* @memberof platform/commonUI/dialog
* @constructor
*/
function OverlayService($document, $compile, $rootScope) {
function OverlayService($document, $compile, $rootScope, $timeout) {
this.$compile = $compile;
this.$timeout = $timeout;
// Don't include $document and $rootScope directly;
// avoids https://docs.angularjs.org/error/ng/cpws
@ -93,12 +94,14 @@ define(
scope.key = key;
scope.typeClass = typeClass || 't-dialog';
// Create the overlay element and add it to the document's body
element = this.$compile(TEMPLATE)(scope);
// Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
// multiple overlays with the same z-index are active.
this.findBody().append(element);
this.$timeout(() => {
// Create the overlay element and add it to the document's body
element = this.$compile(TEMPLATE)(scope);
// Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when
// multiple overlays with the same z-index are active.
this.findBody().append(element);
});
return {
dismiss: dismiss

View File

@ -35,16 +35,20 @@ define(
mockTemplate,
mockElement,
mockScope,
mockTimeout,
overlayService;
beforeEach(function () {
mockDocument = jasmine.createSpyObj("$document", ["find"]);
mockCompile = jasmine.createSpy("$compile");
mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]);
mockBody = jasmine.createSpyObj("body", ["prepend"]);
mockBody = jasmine.createSpyObj("body", ["append"]);
mockTemplate = jasmine.createSpy("template");
mockElement = jasmine.createSpyObj("element", ["remove"]);
mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
mockTimeout = function (callback) {
callback();
}
mockDocument.find.and.returnValue(mockBody);
mockCompile.and.returnValue(mockTemplate);
@ -54,7 +58,8 @@ define(
overlayService = new OverlayService(
mockDocument,
mockCompile,
mockRootScope
mockRootScope,
mockTimeout
);
});
@ -67,7 +72,7 @@ define(
it("adds the templated element to the body", function () {
overlayService.createOverlay("test", {});
expect(mockBody.prepend).toHaveBeenCalledWith(mockElement);
expect(mockBody.append).toHaveBeenCalledWith(mockElement);
});
it("places the provided model/key in its template's scope", function () {

View File

@ -162,9 +162,6 @@ function (
function saveAfterClone(clonedObject) {
return this.openmct.editor.save().then(() => {
// Force mutation for search indexing
clonedObject.useCapability('mutation', (model) => {
return model;
});
return clonedObject;
})
}
@ -173,6 +170,14 @@ function (
return fetchObject(clonedObject.getId())
}
function indexForSearch(savedObject) {
savedObject.useCapability('mutation', (model) => {
return model;
});
return savedObject;
}
function onSuccess(object) {
self.notificationService.info("Save Succeeded");
return object;
@ -194,6 +199,7 @@ function (
.then(undirtyOriginals)
.then(saveAfterClone)
.then(finishEditing)
.then(indexForSearch)
.then(hideBlockingDialog)
.then(onSuccess)
.catch(onFailure);

View File

@ -71,6 +71,12 @@ define(
openmct.editor.cancel();
}
function isFirstViewEditable(domainObject) {
let firstView = openmct.objectViews.get(domainObject)[0];
return firstView && firstView.canEdit && firstView.canEdit(domainObject);
}
function navigateAndEdit(object) {
let objectPath = object.getCapability('context').getPath(),
url = '#/browse/' + objectPath
@ -82,7 +88,9 @@ define(
window.location.href = url;
openmct.editor.edit();
if (isFirstViewEditable(object.useCapability('adapter'))) {
openmct.editor.edit();
}
}
newModel.type = this.type.getKey();

View File

@ -1,13 +1,13 @@
<div ng-controller="BannerController" ng-show="active.notification"
class="l-message-banner s-message-banner {{active.notification.model.severity}}" ng-class="{
class="c-message-banner {{active.notification.model.severity}}" ng-class="{
'minimized': active.notification.model.minimized,
'new': !active.notification.model.minimized}"
ng-click="maximize(active.notification)">
<span class="banner-elem label">
<span class="c-message-banner__message">
{{active.notification.model.title}}
</span>
<span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
<mct-include key="'progress-bar'" class="banner-elem"
<mct-include key="'progress-bar'" class="c-message-banner__progress-bar"
ng-model="active.notification.model">
</mct-include>
</span>
@ -16,5 +16,5 @@
ng-click="action(active.notification.model.primaryOption.callback, $event)">
{{active.notification.model.primaryOption.label}}
</a>
<a class="banner-elem close icon-x" ng-click="dismiss(active.notification, $event)"></a>
<button class="c-message-banner__close-button c-click-icon icon-x-in-circle" ng-click="dismiss(active.notification, $event)"></button>
</div>

View File

@ -24,10 +24,10 @@
<button ng-click="timer.clickStopButton()"
ng-hide="timer.timerState == 'stopped'"
title="Reset"
class="c-timer__ctrl-reset c-click-icon c-click-icon--major icon-reset"></button>
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button>
<button ng-click="timer.clickButton()"
title="{{timer.buttonText()}}"
class="c-timer__ctrl-pause-play c-click-icon c-click-icon--major {{timer.buttonCssClass()}}"></button>
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button>
</div>
<div class="c-timer__direction {{timer.signClass()}}"
ng-hide="!timer.signClass()"></div>

View File

@ -1,22 +1,18 @@
<div class="t-imagery" ng-controller="ImageryController as imagery">
<div class="t-imagery c-imagery" ng-controller="ImageryController as imagery">
<mct-split-pane class='abs' anchor="bottom" alias="imagery">
<div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col"
ng-mouseenter="showLocalControls = true;"
ng-mouseleave="showLocalControls = false;">
<div class="h-local-controls h-local-controls-overlay-content h-local-controls-trans s-local-controls local-controls-hidden l-flex-row">
<span class="holder flex-elem grows">
<div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col">
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc">
<span class="holder flex-elem grows c-imagery__lc__sliders">
<input class="icon-brightness" type="range"
min="0"
max="500"
ng-model="filters.brightness">
</input>
ng-model="filters.brightness" />
<input class="icon-contrast" type="range"
min="0"
max="500"
ng-model="filters.contrast">
</input>
ng-model="filters.contrast" />
</span>
<span class="holder flex-elem t-reset-btn-holder">
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
<a class="s-icon-button icon-reset t-btn-reset"
ng-click="filters = { brightness: 100, contrast: 100 }"></a>
</span>

View File

@ -37,75 +37,17 @@ define(
}
/**
* Handle persistence failures by providing the user with a
* dialog summarizing these failures, and giving the option
* to overwrite/cancel as appropriate.
* Discard failures
* @param {Array} failures persistence failures, as prepared
* by PersistenceQueueHandler
* @memberof platform/persistence/queue.PersistenceFailureHandler#
*/
PersistenceFailureHandler.prototype.handle = function handleFailures(failures) {
// Prepare dialog for display
var dialogModel = new PersistenceFailureDialog(failures),
revisionErrors = dialogModel.model.revised,
$q = this.$q;
// Refresh revision information for the domain object associated
// with this persistence failure
function refresh(failure) {
// Refresh the domain object to the latest from persistence
return failure.persistence.refresh();
}
// Issue a new persist call for the domain object associated with
// this failure.
function persist(failure) {
// Note that we reissue the persist request here, but don't
// return it, to avoid a circular wait. We trust that the
// PersistenceQueue will behave correctly on the next round
// of flushing.
failure.requeue();
}
// Retry persistence (overwrite) for this set of failed attempts
function retry(failuresToRetry) {
var models = {};
// Cache a copy of the model
function cacheModel(failure) {
// Clone...
models[failure.id] = JSON.parse(JSON.stringify(
failure.domainObject.getModel()
));
}
// Mutate a domain object to restore its model
function remutate(failure) {
var model = models[failure.id];
return failure.domainObject.useCapability(
"mutation",
function () {
return model;
},
model.modified
);
}
// Cache the object models we might want to save
failuresToRetry.forEach(cacheModel);
// Strategy here:
// * Cache all of the models we might want to save (above)
// * Refresh all domain objects (so they are latest versions)
// * Re-insert the cached domain object models
// * Invoke persistence again
return $q.all(failuresToRetry.map(refresh)).then(function () {
return $q.all(failuresToRetry.map(remutate));
}).then(function () {
return $q.all(failuresToRetry.map(persist));
});
}
// Discard changes for a failed refresh
function discard(failure) {
var persistence =
@ -118,19 +60,7 @@ define(
return $q.all(failuresToDiscard.map(discard));
}
// Handle user input (did they choose to overwrite?)
function handleChoice(key) {
// If so, try again
if (key === PersistenceFailureConstants.OVERWRITE_KEY) {
return retry(revisionErrors);
} else {
return discardAll(revisionErrors);
}
}
// Prompt for user input, the overwrite if they said so.
return this.dialogService.getUserChoice(dialogModel)
.then(handleChoice, handleChoice);
return discardAll(revisionErrors);
};
return PersistenceFailureHandler;

View File

@ -74,43 +74,14 @@ define(
handler = new PersistenceFailureHandler(mockQ, mockDialogService);
});
it("shows a dialog to handle failures", function () {
it("discards on handle", function () {
handler.handle(mockFailures);
expect(mockDialogService.getUserChoice).toHaveBeenCalled();
});
it("overwrites on request", function () {
mockQ.all.and.returnValue(asPromise([]));
handler.handle(mockFailures);
// User chooses overwrite
mockPromise.then.calls.mostRecent().args[0](Constants.OVERWRITE_KEY);
// Should refresh, remutate, and requeue all objects
mockFailures.forEach(function (mockFailure, i) {
expect(mockFailure.persistence.refresh).toHaveBeenCalled();
expect(mockFailure.requeue).toHaveBeenCalled();
expect(mockFailure.domainObject.useCapability).toHaveBeenCalledWith(
'mutation',
jasmine.any(Function),
i // timestamp
);
expect(mockFailure.domainObject.useCapability.calls.mostRecent().args[1]())
.toEqual({ id: mockFailure.id, modified: i });
});
});
it("discards on request", function () {
mockQ.all.and.returnValue(asPromise([]));
handler.handle(mockFailures);
// User chooses overwrite
mockPromise.then.calls.mostRecent().args[0](false);
// Should refresh, but not remutate, and requeue all objects
mockFailures.forEach(function (mockFailure) {
expect(mockFailure.persistence.refresh).toHaveBeenCalled();
expect(mockFailure.requeue).not.toHaveBeenCalled();
expect(mockFailure.domainObject.useCapability).not.toHaveBeenCalled();
});
});
});
}
);

View File

@ -248,7 +248,6 @@ define([
this.legacyRegistry = defaultRegistry;
this.install(this.plugins.Plot());
this.install(this.plugins.TelemetryTable());
this.install(this.plugins.DisplayLayout());
this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());
this.install(LicensesPlugin.default());
@ -257,7 +256,6 @@ define([
if (typeof BUILD_CONSTANTS !== 'undefined') {
this.install(buildInfoPlugin(BUILD_CONSTANTS));
}
}
MCT.prototype = Object.create(EventEmitter.prototype);
@ -328,6 +326,12 @@ define([
* MCT; if undefined, MCT will be run in the body of the document
*/
MCT.prototype.start = function (domElement) {
if (!this.plugins.DisplayLayout._installed) {
this.install(this.plugins.DisplayLayout({
showAsView: ['summary-widget']
}));
}
if (!domElement) {
domElement = document.body;
}
@ -352,7 +356,7 @@ define([
legacyRegistry.enable('adapter');
this.router.route(/^\/$/, () => {
this.router.setPath('/browse/mine');
this.router.setPath('/browse/');
});
/**

View File

@ -137,8 +137,7 @@ define([
function callbackWrapper(series) {
callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1));
}
return capability.subscribe(callbackWrapper, request);
return capability.subscribe(callbackWrapper, request) || function () {};
};
LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) {

View File

@ -98,18 +98,24 @@ define([
composition.reorder(1, 0);
let newComposition =
publicAPI.objects.mutate.calls.mostRecent().args[2];
let reorderPlan = listener.calls.mostRecent().args[0][0];
expect(listener).toHaveBeenCalledWith(1, 0);
expect(reorderPlan.oldIndex).toBe(1);
expect(reorderPlan.newIndex).toBe(0);
expect(newComposition[0].key).toEqual('b');
expect(newComposition[1].key).toEqual('a');
expect(newComposition[2].key).toEqual('c');
});
it('', function () {
composition.reorder(0, 2);
let newComposition =
publicAPI.objects.mutate.calls.mostRecent().args[2];
let reorderPlan = listener.calls.mostRecent().args[0][0];
expect(listener).toHaveBeenCalledWith(0, 2);
expect(newComposition[0].key).toEqual('c');
expect(reorderPlan.oldIndex).toBe(0);
expect(reorderPlan.newIndex).toBe(2);
expect(newComposition[0].key).toEqual('b');
expect(newComposition[1].key).toEqual('c');
expect(newComposition[2].key).toEqual('a');
})
});

View File

@ -236,19 +236,15 @@ define([
* @name remove
*/
CompositionCollection.prototype.reorder = function (oldIndex, newIndex, skipMutate) {
if (!skipMutate) {
this.provider.reorder(this.domainObject, oldIndex, newIndex);
} else {
this.emit('reorder', oldIndex, newIndex);
}
this.provider.reorder(this.domainObject, oldIndex, newIndex);
};
/**
* Handle reorder from provider.
* @private
*/
CompositionCollection.prototype.onProviderReorder = function (oldIndex, newIndex) {
this.reorder(oldIndex, newIndex, true);
CompositionCollection.prototype.onProviderReorder = function (reorderMap) {
this.emit('reorder', reorderMap);
};
/**

View File

@ -206,8 +206,32 @@ define([
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
let newComposition = domainObject.composition.slice();
newComposition[newIndex] = domainObject.composition[oldIndex];
newComposition[oldIndex] = domainObject.composition[newIndex];
let removeId = oldIndex > newIndex ? oldIndex + 1 : oldIndex;
let insertPosition = oldIndex < newIndex ? newIndex + 1 : newIndex;
//Insert object in new position
newComposition.splice(insertPosition, 0, domainObject.composition[oldIndex]);
newComposition.splice(removeId, 1);
let reorderPlan = [{
oldIndex,
newIndex
}];
if (oldIndex > newIndex) {
for (let i = newIndex; i < oldIndex; i++) {
reorderPlan.push({
oldIndex: i,
newIndex: i + 1
});
}
} else {
for (let i = oldIndex + 1; i <= newIndex; i++) {
reorderPlan.push({
oldIndex: i,
newIndex: i - 1
});
}
}
this.publicAPI.objects.mutate(domainObject, 'composition', newComposition);
let id = objectUtils.makeKeyString(domainObject.identifier);
@ -221,9 +245,9 @@ define([
function notify(listener) {
if (listener.context) {
listener.callback.call(listener.context, oldIndex, newIndex);
listener.callback.call(listener.context, reorderPlan);
} else {
listener.callback(oldIndex, newIndex);
listener.callback(reorderPlan);
}
}
};

View File

@ -226,7 +226,20 @@ define([
(identifier.namespace === identifiers[0].namespace &&
identifier.key === identifiers[0].key);
});
}
};
ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
return this.get(identifier).then((domainObject) => {
path.push(domainObject);
let location = domainObject.location;
if (location) {
return this.getOriginalPath(utils.parseKeyString(location), path);
} else {
return path;
}
});
};
/**
* Uniquely identifies a domain object.

View File

@ -27,10 +27,16 @@
<style lang="scss">
@import "~styles/sass-base";
@mixin legacyMessage() {
flex: 0 1 auto;
font-family: symbolsfont;
font-size: $messageIconD; // Singleton message in a dialog
margin-right: $interiorMarginLg;
}
.c-message {
display: flex;
align-items: center;
padding: $interiorMarginLg;
> * + * {
margin-left: $interiorMarginLg;
@ -58,7 +64,44 @@
&__title,
&__action-text {
font-size: 1.2em; // TEMP
}
&--simple {
// Icon and text elements only
&:before {
font-size: 30px !important;
}
[class*='__text'] {
font-size: 1.25em;
}
}
/************************** LEGACY */
&.message-severity-info:before {
@include legacyMessage();
content: $glyph-icon-info;
color: $colorInfo;
}
&.message-severity-alert:before {
@include legacyMessage();
content: $glyph-icon-alert-rect;
color: $colorWarningLo;
}
&.message-severity-error:before {
@include legacyMessage();
content: $glyph-icon-alert-triangle;
color: $colorWarningLo;
}
// Messages in a list
.c-overlay__messages & {
padding: $interiorMarginLg;
&:before {
font-size: $messageListIconD;
}
}
}
</style>

View File

@ -56,9 +56,11 @@
}
&__close-button {
$p: $interiorMarginSm;
$p: $interiorMargin;
border-radius: 100% !important;
color: $overlayColorFg;
display: inline-block;
font-size: 1.25em;
position: absolute;
top: $p; right: $p;
}
@ -151,6 +153,7 @@
}
}
.t-dialog-sm .l-overlay-small, // Legacy dialog support
.l-overlay-fit {
.c-overlay__outer {
@include overlaySizing(auto);

View File

@ -280,7 +280,11 @@ define([
if (!provider) {
return Promise.reject('No provider found');
}
return provider.request.apply(provider, arguments);
return provider.request.apply(provider, arguments).catch((rejected) => {
this.openmct.notifications.error('Error requesting telemetry data, see console for details');
console.error(rejected);
return Promise.reject(rejected);
});
};
/**

View File

@ -66,10 +66,11 @@ export default {
this.items.splice(index, 1);
},
reorder(oldIndex, newIndex) {
let objectAtOldIndex = this.items[oldIndex];
this.$set(this.items, oldIndex, this.items[newIndex]);
this.$set(this.items, newIndex, objectAtOldIndex);
reorder(reorderPlan) {
let oldItems = this.items.slice();
reorderPlan.forEach((reorderEvent) => {
this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]);
});
}
},
mounted() {

View File

@ -93,11 +93,11 @@
this.primaryTelemetryObjects.splice(index,1);
primary = undefined;
},
reorderPrimary(oldIndex, newIndex) {
let objectAtOldIndex = this.primaryTelemetryObjects[oldIndex];
this.$set(this.primaryTelemetryObjects, oldIndex, this.primaryTelemetryObjects[newIndex]);
this.$set(this.primaryTelemetryObjects, newIndex, objectAtOldIndex);
reorderPrimary(reorderPlan) {
let oldComposition = this.primaryTelemetryObjects.slice();
reorderPlan.forEach(reorderEvent => {
this.$set(this.primaryTelemetryObjects, reorderEvent.newIndex, oldComposition[reorderEvent.oldIndex]);
});
},
addSecondary(primary) {
return (domainObject) => {

View File

@ -152,7 +152,7 @@
return this.internalDomainObject.configuration.items;
}
},
inject: ['openmct'],
inject: ['openmct', 'options'],
props: ['domainObject'],
components: ITEM_TYPE_VIEW_MAP,
methods: {
@ -283,9 +283,8 @@
}
},
isTelemetry(domainObject) {
if (this.openmct.telemetry.isTelemetryObject(domainObject)
&& domainObject.type !== 'summary-widget'
&& domainObject.type !== 'example.imagery') {
if (this.openmct.telemetry.isTelemetryObject(domainObject) &&
!this.options.showAsView.includes(domainObject.type)) {
return true;
} else {
return false;

View File

@ -25,8 +25,7 @@ import Vue from 'vue'
import objectUtils from '../../api/objects/object-utils.js'
import DisplayLayoutType from './DisplayLayoutType.js'
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'
export default function () {
export default function DisplayLayoutPlugin(options) {
return function (openmct) {
openmct.objectViews.addProvider({
key: 'layout.view',
@ -47,7 +46,8 @@ export default function () {
template: '<layout ref="displayLayout" :domain-object="domainObject"></layout>',
provide: {
openmct,
objectUtils
objectUtils,
options
},
el: container,
data () {
@ -83,5 +83,6 @@ export default function () {
return true;
}
});
DisplayLayoutPlugin._installed = true;
}
}

View File

@ -5,11 +5,11 @@
<span class="c-disclosure-triangle is-enabled flex-elem"
:class="{'c-disclosure-triangle--expanded': expanded}"></span>
<div class="c-tree__item__label">
<div class="t-object-label l-flex-row flex-elem grows">
<div class="t-item-icon flex-elem"
<div class="c-object-label">
<div class="c-object-label__type-icon"
:class="objectCssClass">
</div>
<div class="t-title-label flex-elem grows">{{ filterObject.name }}</div>
<div class="c-object-label__name flex-elem grows">{{ filterObject.name }}</div>
</div>
</div>
</div>

View File

@ -16,7 +16,7 @@
</div>
<div class="c-grid-item__controls">
<div class="icon-people" title='Shared'></div>
<button class="c-click-icon icon-info c-info-button" title='More Info'></button>
<button class="c-icon-button icon-info c-info-button" title='More Info'></button>
<div class="icon-pointer-right c-pointer-icon"></div>
</div>
</a>

View File

@ -26,7 +26,7 @@
</div>
<div class="c-ne__local-controls--hidden">
<button class="c-click-icon c-click-icon--major icon-trash"
<button class="c-icon-button c-icon-button--major icon-trash"
title="Delete this entry"
@click="deleteEntry">
</button>

View File

@ -25,176 +25,220 @@ define([
], function (
uuid
) {
function isTelemetry(domainObject) {
if (openmct.telemetry.isTelemetryObject(domainObject)
&& domainObject.type !== 'summary-widget'
&& domainObject.type !== 'example.imagery') {
return true;
} else {
return false;
}
}
function migrateDisplayLayout(domainObject, childObjects) {
const DEFAULT_GRID_SIZE = [32, 32];
let migratedObject = Object.assign({}, domainObject);
let panels = migratedObject.configuration.layout.panels;
let items = [];
Object.keys(panels).forEach(key => {
let panel = panels[key];
let domainObject = childObjects[key];
if (isTelemetry(domainObject)) {
items.push({
width: panel.dimensions[0],
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'telemetry-view',
displayMode: 'all',
value: openmct.telemetry.getMetadata(domainObject).getDefaultDisplayValue(),
stroke: "transparent",
fill: "",
color: "",
size: "13px"
return function Migrations(openmct) {
function getColumnNameKeyMap(domainObject) {
let composition = openmct.composition.get(domainObject);
if (composition) {
return composition.load().then(composees => {
return composees.reduce((nameKeyMap, composee) => {
let metadata = openmct.telemetry.getMetadata(composee);
if (metadata !== undefined) {
metadata.values().forEach(value => {
nameKeyMap[value.name] = value.key;
});
}
return nameKeyMap;
}, {});
});
} else {
items.push({
width: panel.dimensions[0],
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'subobject-view',
hasFrame: panel.hasFrame
});
}
});
migratedObject.configuration.items = items;
migratedObject.configuration.layoutGrid = migratedObject.layoutGrid || DEFAULT_GRID_SIZE;
delete migratedObject.layoutGrid;
delete migratedObject.configuration.layout;
return migratedObject;
}
function migrateFixedPositionConfiguration(elements, telemetryObjects) {
const DEFAULT_STROKE = "transparent";
const DEFAULT_SIZE = "13px";
const DEFAULT_COLOR = "";
const DEFAULT_FILL = "";
let items = [];
elements.forEach(element => {
let item = {
x: element.x,
y: element.y,
width: element.width,
height: element.height,
useGrid: element.useGrid,
id: uuid()
};
if (element.type === "fixed.telemetry") {
item.type = "telemetry-view";
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
item.color = element.color || DEFAULT_COLOR;
item.size = element.size || DEFAULT_SIZE;
item.identifier = telemetryObjects[element.id].identifier;
item.displayMode = element.titled ? 'all' : 'value';
item.value = openmct.telemetry.getMetadata(telemetryObjects[element.id]).getDefaultDisplayValue();
} else if (element.type === 'fixed.box') {
item.type = "box-view";
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
} else if (element.type === 'fixed.line') {
item.type = "line-view";
item.x2 = element.x2;
item.y2 = element.y2;
item.stroke = element.stroke || DEFAULT_STROKE;
delete item.height;
delete item.width;
} else if (element.type === 'fixed.text') {
item.type = "text-view";
item.text = element.text;
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
item.color = element.color || DEFAULT_COLOR;
item.size = element.size || DEFAULT_SIZE;
} else if (element.type === 'fixed.image') {
item.type = "image-view";
item.url =element.url;
item.stroke = element.stroke || DEFAULT_STROKE;
}
items.push(item);
});
return items;
}
return [
{
check(domainObject) {
return domainObject.type === 'layout' && domainObject.configuration.layout;
},
migrate(domainObject) {
let childObjects = {};
let promises = Object.keys(domainObject.configuration.layout.panels).map(key => {
return openmct.objects.get(key)
.then(object => {
childObjects[key] = object;
});
});
return Promise.all(promises)
.then(function () {
return migrateDisplayLayout(domainObject, childObjects);
});
},
},
{
check(domainObject) {
return domainObject.type === 'telemetry.fixed' && domainObject.configuration['fixed-display'];
},
migrate(domainObject) {
const DEFAULT_GRID_SIZE = [64, 16];
let newLayoutObject = {
identifier: domainObject.identifier,
location: domainObject.location,
name: domainObject.name,
type: "layout"
};
let layoutType = openmct.types.get('layout');
layoutType.definition.initialize(newLayoutObject);
newLayoutObject.composition = domainObject.composition;
newLayoutObject.configuration.layoutGrid = domainObject.layoutGrid || DEFAULT_GRID_SIZE;
let elements = domainObject.configuration['fixed-display'].elements;
let telemetryObjects = {};
let promises = elements.map(element => {
if (element.id) {
return openmct.objects.get(element.id)
.then(object => {
telemetryObjects[element.id] = object;
});
}
});
return Promise.all(promises)
.then(function () {
newLayoutObject.configuration.items =
migrateFixedPositionConfiguration(elements, telemetryObjects);
return newLayoutObject;
});
return Promise.resolve([]);
}
}
];
function isTelemetry(domainObject) {
if (openmct.telemetry.isTelemetryObject(domainObject)
&& domainObject.type !== 'summary-widget'
&& domainObject.type !== 'example.imagery') {
return true;
} else {
return false;
}
}
function migrateDisplayLayout(domainObject, childObjects) {
const DEFAULT_GRID_SIZE = [32, 32];
let migratedObject = Object.assign({}, domainObject);
let panels = migratedObject.configuration.layout.panels;
let items = [];
Object.keys(panels).forEach(key => {
let panel = panels[key];
let domainObject = childObjects[key];
if (isTelemetry(domainObject)) {
items.push({
width: panel.dimensions[0],
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'telemetry-view',
displayMode: 'all',
value: openmct.telemetry.getMetadata(domainObject).getDefaultDisplayValue(),
stroke: "transparent",
fill: "",
color: "",
size: "13px"
});
} else {
items.push({
width: panel.dimensions[0],
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'subobject-view',
hasFrame: panel.hasFrame
});
}
});
migratedObject.configuration.items = items;
migratedObject.configuration.layoutGrid = migratedObject.layoutGrid || DEFAULT_GRID_SIZE;
delete migratedObject.layoutGrid;
delete migratedObject.configuration.layout;
return migratedObject;
}
function migrateFixedPositionConfiguration(elements, telemetryObjects) {
const DEFAULT_STROKE = "transparent";
const DEFAULT_SIZE = "13px";
const DEFAULT_COLOR = "";
const DEFAULT_FILL = "";
let items = [];
elements.forEach(element => {
let item = {
x: element.x,
y: element.y,
width: element.width,
height: element.height,
useGrid: element.useGrid,
id: uuid()
};
if (element.type === "fixed.telemetry") {
item.type = "telemetry-view";
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
item.color = element.color || DEFAULT_COLOR;
item.size = element.size || DEFAULT_SIZE;
item.identifier = telemetryObjects[element.id].identifier;
item.displayMode = element.titled ? 'all' : 'value';
item.value = openmct.telemetry.getMetadata(telemetryObjects[element.id]).getDefaultDisplayValue();
} else if (element.type === 'fixed.box') {
item.type = "box-view";
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
} else if (element.type === 'fixed.line') {
item.type = "line-view";
item.x2 = element.x2;
item.y2 = element.y2;
item.stroke = element.stroke || DEFAULT_STROKE;
delete item.height;
delete item.width;
} else if (element.type === 'fixed.text') {
item.type = "text-view";
item.text = element.text;
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
item.color = element.color || DEFAULT_COLOR;
item.size = element.size || DEFAULT_SIZE;
} else if (element.type === 'fixed.image') {
item.type = "image-view";
item.url =element.url;
item.stroke = element.stroke || DEFAULT_STROKE;
}
items.push(item);
});
return items;
}
return [
{
check(domainObject) {
return domainObject.type === 'layout' && domainObject.configuration.layout;
},
migrate(domainObject) {
let childObjects = {};
let promises = Object.keys(domainObject.configuration.layout.panels).map(key => {
return openmct.objects.get(key)
.then(object => {
childObjects[key] = object;
});
});
return Promise.all(promises)
.then(function () {
return migrateDisplayLayout(domainObject, childObjects);
});
}
},
{
check(domainObject) {
return domainObject.type === 'telemetry.fixed' && domainObject.configuration['fixed-display'];
},
migrate(domainObject) {
const DEFAULT_GRID_SIZE = [64, 16];
let newLayoutObject = {
identifier: domainObject.identifier,
location: domainObject.location,
name: domainObject.name,
type: "layout"
};
let layoutType = openmct.types.get('layout');
layoutType.definition.initialize(newLayoutObject);
newLayoutObject.composition = domainObject.composition;
newLayoutObject.configuration.layoutGrid = domainObject.layoutGrid || DEFAULT_GRID_SIZE;
let elements = domainObject.configuration['fixed-display'].elements;
let telemetryObjects = {};
let promises = elements.map(element => {
if (element.id) {
return openmct.objects.get(element.id)
.then(object => {
telemetryObjects[element.id] = object;
});
}
});
return Promise.all(promises)
.then(function () {
newLayoutObject.configuration.items =
migrateFixedPositionConfiguration(elements, telemetryObjects);
return newLayoutObject;
});
}
},
{
check(domainObject) {
return domainObject.type === 'table' &&
domainObject.configuration.table;
},
migrate(domainObject) {
let currentTableConfiguration = domainObject.configuration.table || {};
let currentColumnConfiguration = currentTableConfiguration.columns || {};
return getColumnNameKeyMap(domainObject).then(nameKeyMap => {
let hiddenColumns = Object.keys(currentColumnConfiguration).filter(columnName => {
return currentColumnConfiguration[columnName] === false;
}).reduce((hiddenColumnsMap, hiddenColumnName) => {
let key = nameKeyMap[hiddenColumnName];
hiddenColumnsMap[key] = true;
return hiddenColumnsMap;
}, {});
domainObject.configuration.hiddenColumns = hiddenColumns;
delete domainObject.configuration.table;
return domainObject;
});
}
}
];
}
});

View File

@ -20,19 +20,21 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import migrations from './Migrations.js'
import Migrations from './Migrations.js'
export default function () {
function needsMigration(domainObject) {
return migrations.some(m => m.check(domainObject));
}
function migrateObject(domainObject) {
return migrations.filter(m => m.check(domainObject))[0]
.migrate(domainObject);
}
return function (openmct) {
let migrations = Migrations(openmct);
function needsMigration(domainObject) {
return migrations.some(m => m.check(domainObject));
}
function migrateObject(domainObject) {
return migrations.filter(m => m.check(domainObject))[0]
.migrate(domainObject);
}
let wrappedFunction = openmct.objects.get;
openmct.objects.get = function migrate(identifier) {
return wrappedFunction.apply(openmct.objects, [identifier])
@ -46,6 +48,6 @@ export default function () {
}
return object;
});
}
}
};
}
}

View File

@ -136,7 +136,7 @@
<span class="t-object-alert t-alert-unsynced"
title="This plot is not currently displaying the latest data.
Reset Pan/zoom to return to view latest data."></span>
<div class="gl-plot-display-area">
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
<mct-ticks axis="xAxis">
<div class="gl-plot-hash hash-v"
ng-repeat="tick in ticks track by tick.value"
@ -147,7 +147,6 @@
</div>
</mct-ticks>
<mct-ticks axis="yAxis">
<div class="gl-plot-hash hash-h"
ng-repeat="tick in ticks track by tick.value"
@ -155,7 +154,6 @@
</div>
</mct-ticks>
<mct-chart config="config"
series="series"
rectangles="rectangles"
@ -164,23 +162,37 @@
the-y-axis="yAxis">
</mct-chart>
<div class="h-local-controls h-local-controls-overlay-content"
ng-show="plotHistory.length">
<div class="l-btn-set">
<a class="s-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">
</a>
<a class="s-button icon-reset"
ng-click="plot.clear()"
title="Reset pan/zoom">
</a>
<div class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover">
<div class="c-button-set c-button-set--strip-h">
<button class="c-button icon-minus"
ng-click="plot.zoom('out', 0.2)"
title="Zoom out">
</button>
<button class="c-button icon-plus"
ng-click="plot.zoom('in', 0.2)"
title="Zoom in">
</button>
</div>
<div class="c-button-set c-button-set--strip-h"
ng-disabled="!plotHistory.length">
<button class="c-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">
</button>
<button class="c-button icon-reset"
ng-click="plot.clear()"
title="Reset pan/zoom">
</button>
</div>
</div>
<span class="t-wait-spinner loading" ng-show="plot.isRequestPending()">
</span>
<!--Cursor guides-->
<div class="c-cursor-guide--v js-cursor-guide--v"
ng-show="plot.cursorGuide">
</div>
<div class="c-cursor-guide--h js-cursor-guide--h"
ng-show="plot.cursorGuide">
</div>
</div>
<div class="gl-plot-axis-area gl-plot-x"

View File

@ -20,31 +20,34 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="PlotController as controller"
class="abs holder holder-plot has-control-bar"
ng-class="{
'loading': !!pending
}"
>
class="abs holder holder-plot has-control-bar">
<div class="l-control-bar" ng-show="!controller.hideExportButtons">
<span class="c-button-set c-button-set--strip">
<a class="c-button icon-download"
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="controller.exportPNG()"
title="Export This View's Data as PNG">
<span class="c-button__label">PNG</span>
</a>
<a class="c-button"
</button>
<button class="c-button"
ng-click="controller.exportJPG()"
title="Export This View's Data as JPG">
<span class="c-button__label">JPG</span>
</a>
</button>
</span>
<button class="c-button icon-crosshair"
ng-class="{ 'is-active': controller.cursorGuide }"
ng-click="controller.toggleCursorGuide($event)"
title="Toggle cursor guides">
</button>
</div>
<div class="l-view-section">
<div class="c-loading--overlay loading"
ng-show="!!pending"></div>
<mct-plot config="controller.config"
series="series"
the-y-axis="yAxis"
the-x-axis="xAxis">
</mct-plot>
</mct-plot>
</div>
</span>

View File

@ -20,26 +20,29 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="StackedPlotController as stackedPlot"
class="abs holder holder-plot has-control-bar t-plot-stacked"
ng-class="{
'loading': !!currentRequest.pending
}">
class="abs holder holder-plot has-control-bar t-plot-stacked">
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
<span class="c-button-set c-button-set--strip">
<a class="c-button icon-download"
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="stackedPlot.exportPNG()"
title="Export This View's Data as PNG">
<span class="c-button__label">PNG</span>
</a>
<a class="c-button"
</button>
<button class="c-button"
ng-click="stackedPlot.exportJPG()"
title="Export This View's Data as JPG">
<span class="c-button__label">JPG</span>
</a>
</span>
</button>
</span>
<button class="c-button icon-crosshair"
ng-class="{ 'is-active': stackedPlot.cursorGuide }"
ng-click="stackedPlot.toggleCursorGuide($event)"
title="Toggle cursor guides">
</button>
</div>
<div class="l-view-section">
<div class="c-loading--overlay loading"
ng-show="!!currentRequest.pending"></div>
<div class="gl-plot child-frame"
ng-repeat="telemetryObject in telemetryObjects"
ng-class="{

View File

@ -76,7 +76,6 @@ define([
PlotOptionsController.prototype.addSeries = function (series, index) {
this.$scope.plotSeries[index] = series;
series.locateOldObject(this.$scope.domainObject);
};
PlotOptionsController.prototype.resetAllSeries = function (series, index) {

View File

@ -78,6 +78,7 @@ define([
this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee();
@ -92,6 +93,12 @@ define([
this.$scope.series = this.config.series.models;
this.$scope.legend = this.config.legend;
this.cursorGuideVertical = this.$element[0].querySelector('.js-cursor-guide--v');
this.cursorGuideHorizontal = this.$element[0].querySelector('.js-cursor-guide--h');
this.cursorGuide = false;
this.listenTo(this.$scope, 'cursorguide', this.toggleCursorGuide, this);
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this);
this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this);
@ -143,6 +150,9 @@ define([
y: this.yScale.invert(this.positionOverElement.y)
};
if (this.cursorGuide) {
this.updateCrosshairs($event);
}
this.highlightValues(this.positionOverPlot.x);
this.updateMarquee();
this.updatePan();
@ -150,6 +160,11 @@ define([
$event.preventDefault();
};
MCTPlotController.prototype.updateCrosshairs = function ($event) {
this.cursorGuideVertical.style.left = ($event.clientX - this.chartElementBounds.x) + 'px';
this.cursorGuideHorizontal.style.top = ($event.clientY - this.chartElementBounds.y) + 'px';
};
MCTPlotController.prototype.trackChartElementBounds = function ($event) {
if ($event.target === this.$canvas[1]) {
this.chartElementBounds = $event.target.getBoundingClientRect();
@ -266,6 +281,103 @@ define([
this.marquee = undefined;
};
MCTPlotController.prototype.zoom = function (zoomDirection, zoomFactor) {
this.freeze();
this.trackHistory();
var currentXaxis = this.$scope.xAxis.get('displayRange'),
currentYaxis = this.$scope.yAxis.get('displayRange'),
xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
yAxisDist = (currentYaxis.max - currentYaxis.min) * zoomFactor;
if (zoomDirection === 'in') {
this.$scope.xAxis.set('displayRange', {
min: currentXaxis.min + xAxisDist,
max: currentXaxis.max - xAxisDist
});
this.$scope.yAxis.set('displayRange', {
min: currentYaxis.min + yAxisDist,
max: currentYaxis.max - yAxisDist
});
} else if (zoomDirection === 'out') {
this.$scope.xAxis.set('displayRange', {
min: currentXaxis.min - xAxisDist,
max: currentXaxis.max + xAxisDist
});
this.$scope.yAxis.set('displayRange', {
min: currentYaxis.min - yAxisDist,
max: currentYaxis.max + yAxisDist
});
}
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.wheelZoom = function (event) {
const ZOOM_AMT = 0.1;
event.preventDefault();
if (!this.positionOverPlot) {
return;
}
this.freeze();
window.clearTimeout(this.stillZooming);
let xDisplayRange = this.$scope.xAxis.get('displayRange'),
yDisplayRange = this.$scope.yAxis.get('displayRange'),
xAxisDist = (xDisplayRange.max - xDisplayRange.min),
yAxisDist = (yDisplayRange.max - yDisplayRange.min),
xDistMouseToMax = xDisplayRange.max - this.positionOverPlot.x,
xDistMouseToMin = this.positionOverPlot.x - xDisplayRange.min,
yDistMouseToMax = yDisplayRange.max - this.positionOverPlot.y,
yDistMouseToMin = this.positionOverPlot.y - yDisplayRange.min,
xAxisMaxDist = xDistMouseToMax / xAxisDist,
xAxisMinDist = xDistMouseToMin / xAxisDist,
yAxisMaxDist = yDistMouseToMax / yAxisDist,
yAxisMinDist = yDistMouseToMin / yAxisDist;
let plotHistoryStep;
if (!plotHistoryStep) {
plotHistoryStep = {
x: xDisplayRange,
y: yDisplayRange
};
}
if (event.wheelDelta < 0) {
this.$scope.xAxis.set('displayRange', {
min: xDisplayRange.min + ((xAxisDist * ZOOM_AMT) * xAxisMinDist),
max: xDisplayRange.max - ((xAxisDist * ZOOM_AMT) * xAxisMaxDist)
});
this.$scope.yAxis.set('displayRange', {
min: yDisplayRange.min + ((yAxisDist * ZOOM_AMT) * yAxisMinDist),
max: yDisplayRange.max - ((yAxisDist * ZOOM_AMT) * yAxisMaxDist)
});
} else if (event.wheelDelta >= 0) {
this.$scope.xAxis.set('displayRange', {
min: xDisplayRange.min - ((xAxisDist * ZOOM_AMT) * xAxisMinDist),
max: xDisplayRange.max + ((xAxisDist * ZOOM_AMT) * xAxisMaxDist)
});
this.$scope.yAxis.set('displayRange', {
min: yDisplayRange.min - ((yAxisDist * ZOOM_AMT) * yAxisMinDist),
max: yDisplayRange.max + ((yAxisDist * ZOOM_AMT) * yAxisMaxDist)
});
}
this.stillZooming = window.setTimeout(function () {
this.plotHistory.push(plotHistoryStep);
plotHistoryStep = undefined;
this.$scope.$emit('user:viewport:change:end');
}.bind(this), 250);
};
MCTPlotController.prototype.startPan = function ($event) {
this.trackMousePosition($event);
this.freeze();
@ -362,5 +474,9 @@ define([
this.stopListening();
};
MCTPlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
};
return MCTPlotController;
});

View File

@ -59,6 +59,7 @@ define([
this.openmct = openmct;
this.objectService = objectService;
this.exportImageService = exportImageService;
this.cursorGuide = false;
$scope.pending = 0;
@ -218,6 +219,7 @@ define([
PlotController.prototype.stopLoading = function () {
this.$scope.pending -= 1;
this.$scope.$digest();
};
/**
@ -258,8 +260,8 @@ define([
PlotController.prototype.updateFiltersAndResubscribe = function (updatedFilters) {
this.config.series.forEach(function (series) {
series.updateFiltersAndRefresh(updatedFilters[series.keyString]);
})
}
});
};
/**
* Export view as JPG.
@ -283,6 +285,11 @@ define([
}.bind(this));
};
PlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
this.$scope.$broadcast('cursorguide', $event);
};
return PlotController;
});

View File

@ -35,6 +35,8 @@ define([
this.$element = $element;
this.exportImageService = exportImageService;
this.$scope = $scope;
this.cursorGuide = false;
$scope.telemetryObjects = [];
@ -145,5 +147,10 @@ define([
}.bind(this));
};
StackedPlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
this.$scope.$broadcast('cursorguide', $event);
};
return StackedPlotController;
});

View File

@ -88,13 +88,10 @@ export default class RemoveAction {
}
appliesTo(objectPath) {
let object = objectPath[0];
let objectType = object && this.openmct.types.get(object.type);
let parent = objectPath[1];
let parentType = parent && this.openmct.types.get(parent.type);
return objectType.definition.creatable &&
parentType &&
return parentType &&
parentType.definition.creatable &&
Array.isArray(parent.composition);
}

View File

@ -4,7 +4,7 @@
<span class="t-configuration"> </span>
<span class="t-value-inputs"> </span>
</span>
<span class="flex-elem local-control local-controls-hidden l-condition-action-buttons-wrapper">
<span class="flex-elem c-local-controls--show-on-hover l-condition-action-buttons-wrapper">
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this condition"></a>
<a class="s-icon-button icon-trash t-delete" title="Delete this condition"></a>
</span>

View File

@ -1,5 +1,5 @@
<div class="c-sw-rule">
<div class="c-sw-rule__ui l-compact-form has-local-controls l-widget-rule s-widget-rule">
<div class="c-sw-rule__ui l-compact-form l-widget-rule s-widget-rule has-local-controls">
<div class="c-sw-rule__ui__header widget-rule-header">
<div class="c-sw-rule__grippy-wrapper">
<div class="c-sw-rule__grippy t-grippy local-control local-controls-hidden"></div>
@ -11,7 +11,7 @@
</div>
<div class="flex-elem rule-title">Default Title</div>
<div class="flex-elem rule-description grows">Rule description goes here</div>
<div class="flex-elem local-control local-controls-hidden l-rule-action-buttons-wrapper">
<div class="flex-elem c-local-controls--show-on-hover l-rule-action-buttons-wrapper">
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this rule"></a>
<a class="s-icon-button icon-trash t-delete" title="Delete this rule"></a>
</div>

View File

@ -7,7 +7,7 @@
<span class="equal-to hidden"> equal to </span>
<span class="t-value-inputs"></span>
</span>
<span class="flex-elem local-control local-controls-hidden l-widget-test-data-item-action-buttons-wrapper">
<span class="flex-elem c-local-controls--show-on-hover l-widget-test-data-item-action-buttons-wrapper">
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this test value"></a>
<a class="s-icon-button icon-trash t-delete" title="Delete this test value"></a>
</span>

View File

@ -3,11 +3,9 @@
<div id="widgetIcon" class="c-sw__icon js-sw__icon"></div>
<div id="widgetLabel" class="label widget-label c-sw__label js-sw__label">Default Static Name</div>
</a>
<div class="c-summary-widget__message holder flex-elem t-message-inline l-message message-severity-alert t-message-widget-no-data">
<div class="w-message-contents l-message-body-only">
<div class="message-body">
You must add at least one telemetry object to edit this widget.
</div>
<div class="js-summary-widget__message c-summary-widget__message c-message c-message--simple message-severity-alert">
<div class="c-summary-widget__text">
You must add at least one telemetry object to edit this widget.
</div>
</div>
<div class="c-sw-edit__ui holder l-flex-accordion flex-elem grows widget-edit-holder expanded-widget-test-data expanded-widget-rules">

View File

@ -1,6 +1,6 @@
<template>
<div class="c-tabs-view">
<div class="c-tabs-view__tabs-holder c-compact-button-holder"
<div class="c-tabs-view__tabs-holder c-tabs"
:class="{
'is-dragging': isDragging,
'is-mouse-over': allowDrop
@ -11,7 +11,7 @@
</div>
<div class="c-tabs-view__empty-message"
v-if="!tabsList.length > 0">Drag objects here to add them to this view.</div>
<button class="c-tabs-view__tab c-compact-button"
<button class="c-tabs-view__tab c-tab"
v-for="(tab,index) in tabsList"
:key="index"
:class="[
@ -26,7 +26,8 @@
v-for="(tab, index) in tabsList"
:key="index"
:class="{'invisible': !isCurrent(tab)}">
<div class="c-tabs-view__object-name l-browse-bar__object-name--w"
<div v-if="currentTab"
class="c-tabs-view__object-name l-browse-bar__object-name--w"
:class="currentTab.type.definition.cssClass">
<div class="l-browse-bar__object-name">
{{currentTab.domainObject.name}}
@ -53,11 +54,16 @@
}
&__tabs-holder {
@include userSelectNone();
flex: 0 0 auto;
min-height: $h;
}
&__tab {
&:before {
margin-right: $interiorMarginSm;
opacity: 0.7;
}
}
&__object-holder {
flex: 1 1 auto;
display: flex;
@ -76,6 +82,7 @@
}
&__empty-message {
background: rgba($colorBodyFg, 0.1);
color: rgba($colorBodyFg, 0.7);
font-style: italic;
text-align: center;
@ -140,6 +147,13 @@ export default {
this.showTab(this.tabsList[this.tabsList.length - 1]);
}
},
onReorder(reorderPlan) {
let oldTabs = this.tabsList.slice();
reorderPlan.forEach(reorderEvent => {
this.$set(this.tabsList, reorderEvent.newIndex, oldTabs[reorderEvent.oldIndex]);
});
},
onDrop(e) {
this.setCurrentTab = true;
},
@ -166,6 +180,7 @@ export default {
if (this.composition) {
this.composition.on('add', this.addItem);
this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.onReorder);
this.composition.load();
}
@ -182,6 +197,7 @@ export default {
destroyed() {
this.composition.off('add', this.addItem);
this.composition.off('remove', this.removeItem);
this.composition.off('reorder', this.onReorder);
document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend);

View File

@ -59,7 +59,9 @@ define([
this.filterObserver = undefined;
this.createTableRowCollections();
openmct.time.on('bounds', this.refreshData);
openmct.time.on('timeSystem', this.refreshData);
}
initialize() {
@ -73,13 +75,17 @@ define([
createTableRowCollections() {
this.boundedRows = new BoundedTableRowCollection(this.openmct);
//By default, sort by current time system, ascending.
this.filteredRows = new FilteredTableRowCollection(this.boundedRows);
this.filteredRows.sortBy({
//Fetch any persisted default sort
let sortOptions = this.configuration.getConfiguration().sortOptions;
//If no persisted sort order, default to sorting by time system, ascending.
sortOptions = sortOptions || {
key: this.openmct.time.timeSystem().key,
direction: 'asc'
});
};
this.filteredRows.sortBy(sortOptions);
}
loadComposition() {
@ -166,6 +172,7 @@ define([
if (!isTick) {
this.filteredRows.clear();
this.boundedRows.clear();
this.boundedRows.sortByTimeSystem(this.openmct.time.timeSystem());
this.telemetryObjects.forEach(this.requestDataFor);
}
}
@ -208,11 +215,22 @@ define([
delete this.subscriptions[keyString];
}
sortBy(sortOptions) {
this.filteredRows.sortBy(sortOptions);
if (this.openmct.editor.isEditing()) {
let configuration = this.configuration.getConfiguration();
configuration.sortOptions = sortOptions;
this.configuration.updateConfiguration(configuration);
}
}
destroy() {
this.boundedRows.destroy();
this.filteredRows.destroy();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
this.openmct.time.off('bounds', this.refreshData);
this.openmct.time.on('timeSystem', this.refreshData);
if (this.filterObserver) {
this.filterObserver();
}

View File

@ -32,12 +32,20 @@ define([
Vue
) {
function TelemetryTableViewProvider(openmct) {
function hasTelemetry(domainObject) {
if (!domainObject.hasOwnProperty('telemetry')) {
return false;
}
let metadata = openmct.telemetry.getMetadata(domainObject);
return metadata.values().length > 0;
}
return {
key: 'table',
name: 'Telemetry Table',
cssClass: 'icon-tabular-realtime',
canView(domainObject) {
return domainObject.type === 'table' || domainObject.hasOwnProperty('telemetry');
return domainObject.type === 'table' ||
hasTelemetry(domainObject)
},
canEdit(domainObject) {
return domainObject.type === 'table';

View File

@ -41,7 +41,6 @@ define(
this.bounds = this.bounds.bind(this)
this.sortByTimeSystem(openmct.time.timeSystem());
openmct.time.on('timeSystem', this.sortByTimeSystem);
this.lastBounds = openmct.time.bounds();
openmct.time.on('bounds', this.bounds);
@ -51,8 +50,8 @@ define(
// Insert into either in-bounds array, or the future buffer.
// Data in the future buffer will be re-evaluated for possible
// insertion on next bounds change
let beforeStartOfBounds = item.datum[this.sortOptions.key] < this.lastBounds.start;
let afterEndOfBounds = item.datum[this.sortOptions.key] > this.lastBounds.end;
let beforeStartOfBounds = this.parseTime(item.datum[this.sortOptions.key]) < this.lastBounds.start;
let afterEndOfBounds = this.parseTime(item.datum[this.sortOptions.key]) > this.lastBounds.end;
if (!afterEndOfBounds && !beforeStartOfBounds) {
return super.addOne(item);
@ -64,6 +63,12 @@ define(
sortByTimeSystem(timeSystem) {
this.sortBy({key: timeSystem.key, direction: 'asc'});
let formatter = this.openmct.telemetry.getValueFormatter({
key: timeSystem.key,
source: timeSystem.key,
format: timeSystem.timeFormat
});
this.parseTime = formatter.parse.bind(formatter);
this.futureBuffer.sortBy({key: timeSystem.key, direction: 'asc'});
}
@ -131,7 +136,6 @@ define(
}
destroy() {
this.openmct.time.off('timeSystem', this.sortByTimeSystem);
this.openmct.time.off('bounds', this.bounds);
}
}

View File

@ -127,7 +127,8 @@ define(
if (testRowValue > lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
return this.rows.length - 1;
// Maintain stable sort
return this.rows.length;
} else if (testRowValue <= firstValue) {
return 0;
} else {
@ -141,7 +142,8 @@ define(
} else if (testRowValue < lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
return this.rows.length - 1;
// Maintain stable sort
return this.rows.length;
} else {
// Use a custom comparison function to support descending sort.
return lodashFunction(rows, testRow, (thisRow) => {

View File

@ -23,11 +23,11 @@
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
:class="{'loading': loading}">
<div class="c-table__control-bar c-control-bar">
<a class="c-button icon-download labeled"
<button class="c-button icon-download labeled"
v-on:click="exportAsCSV()"
title="Export This View's Data">
<span class="c-button__label">Export As CSV</span>
</a>
</button>
</div>
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
<!-- Headers table -->
@ -142,6 +142,7 @@
// Wraps __headers table
flex: 0 0 auto;
overflow: hidden;
background: $colorTabHeaderBg;
}
/******************************* TABLES */
@ -172,6 +173,7 @@
&__body-w {
// Wraps __body table provides scrolling
flex: 1 1 100%;
height: 0; // Fixes Chrome 73 overflow bug
overflow-x: auto;
overflow-y: scroll;
}
@ -408,7 +410,7 @@ export default {
direction: 'asc'
}
}
this.table.filteredRows.sortBy(this.sortOptions);
this.table.sortBy(this.sortOptions);
},
scroll() {
if (!this.processingScroll) {

View File

@ -23,73 +23,76 @@
<div class="c-conductor"
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']">
<form class="u-contents" ref="conductorForm" @submit.prevent="updateTimeFromConductor">
<button class="c-input--submit" type="submit" ref="submitButton"></button>
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
<div class="c-conductor__time-bounds">
<button class="c-input--submit" type="submit" ref="submitButton"></button>
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
v-if="isFixed">
<!-- Fixed start -->
<div class="c-conductor__start-fixed__label">Start</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
ref="startDate"
v-model="formattedBounds.start"
@change="validateAllBounds(); submitForm()" />
<date-picker
v-if="isFixed && isUTCBased"
:default-date-time="formattedBounds.start"
:formatter="timeFormatter"
@date-selected="startDateSelected"></date-picker>
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
v-if="!isFixed">
<!-- RT start -->
<div class="c-direction-indicator icon-minus"></div>
<input class="c-input--hrs-min-sec"
type="text" autocorrect="off"
ref="startOffset"
spellcheck="false"
v-model="offsets.start"
@change="validateAllOffsets(); submitForm()">
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
<!-- Fixed end and RT 'last update' display -->
<div class="c-conductor__end-fixed__label">
{{ isFixed ? 'End' : 'Updated' }}
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
v-if="isFixed">
<!-- Fixed start -->
<div class="c-conductor__start-fixed__label">Start</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
ref="startDate"
v-model="formattedBounds.start"
@change="validateAllBounds(); submitForm()" />
<date-picker
v-if="isFixed && isUTCBased"
:default-date-time="formattedBounds.start"
:formatter="timeFormatter"
@date-selected="startDateSelected"></date-picker>
</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
v-model="formattedBounds.end"
:disabled="!isFixed"
ref="endDate"
@change="validateAllBounds(); submitForm()">
<date-picker
v-if="isFixed && isUTCBased"
class="c-ctrl-wrapper--menus-left"
:default-date-time="formattedBounds.end"
:formatter="timeFormatter"
@date-selected="endDateSelected"></date-picker>
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-delta"
v-if="!isFixed">
<!-- RT end -->
<div class="c-direction-indicator icon-plus"></div>
<input class="c-input--hrs-min-sec"
type="text"
autocorrect="off"
spellcheck="false"
ref="endOffset"
v-model="offsets.end"
@change="validateAllOffsets(); submitForm()">
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
v-if="!isFixed">
<!-- RT start -->
<div class="c-direction-indicator icon-minus"></div>
<input class="c-input--hrs-min-sec"
type="text" autocorrect="off"
ref="startOffset"
spellcheck="false"
v-model="offsets.start"
@change="validateAllOffsets(); submitForm()">
</div>
<conductor-axis
class="c-conductor__ticks"
:bounds="rawBounds"
@panAxis="setViewFromBounds"></conductor-axis>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
<!-- Fixed end and RT 'last update' display -->
<div class="c-conductor__end-fixed__label">
{{ isFixed ? 'End' : 'Updated' }}
</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
v-model="formattedBounds.end"
:disabled="!isFixed"
ref="endDate"
@change="validateAllBounds(); submitForm()">
<date-picker
v-if="isFixed && isUTCBased"
class="c-ctrl-wrapper--menus-left"
:default-date-time="formattedBounds.end"
:formatter="timeFormatter"
@date-selected="endDateSelected"></date-picker>
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-delta"
v-if="!isFixed">
<!-- RT end -->
<div class="c-direction-indicator icon-plus"></div>
<input class="c-input--hrs-min-sec"
type="text"
autocorrect="off"
spellcheck="false"
ref="endOffset"
v-model="offsets.end"
@change="validateAllOffsets(); submitForm()">
</div>
<conductor-axis
class="c-conductor__ticks"
:bounds="rawBounds"
@panAxis="setViewFromBounds"></conductor-axis>
</div>
<div class="c-conductor__controls">
<!-- Mode, time system menu buttons and duration slider -->
<ConductorMode class="c-conductor__mode-select"></ConductorMode>
@ -113,17 +116,17 @@
/*********************************************** CONDUCTOR LAYOUT */
.c-conductor {
display: grid;
grid-column-gap: $interiorMargin;
grid-row-gap: $interiorMargin;
align-items: center;
&__time-bounds {
display: grid;
grid-column-gap: $interiorMargin;
grid-row-gap: $interiorMargin;
align-items: center;
// Default: fixed mode, desktop
grid-template-rows: 1fr 1fr;
grid-template-columns: 20px auto 1fr auto;
grid-template-areas:
"tc-mode-icon tc-start tc-ticks tc-end"
"tc-controls tc-controls tc-controls tc-controls";
// Default: fixed mode, desktop
grid-template-rows: 1fr;
grid-template-columns: 20px auto 1fr auto;
grid-template-areas: "tc-mode-icon tc-start tc-ticks tc-end";
}
&__mode-icon {
grid-area: tc-mode-icon;
@ -163,10 +166,10 @@
}
&.is-realtime-mode {
grid-template-columns: 20px auto 1fr auto auto;
grid-template-areas:
"tc-mode-icon tc-start tc-ticks tc-updated tc-end"
"tc-controls tc-controls tc-controls tc-controls tc-controls";
.c-conductor__time-bounds {
grid-template-columns: 20px auto 1fr auto auto;
grid-template-areas: "tc-mode-icon tc-start tc-ticks tc-updated tc-end";
}
.c-conductor__end-fixed {
grid-area: tc-updated;
@ -174,9 +177,15 @@
}
body.phone.portrait & {
grid-row-gap: $interiorMargin;
grid-template-rows: auto auto auto;
grid-template-columns: 20px auto auto;
.c-conductor__time-bounds {
grid-row-gap: $interiorMargin;
grid-template-rows: auto auto;
grid-template-columns: 20px auto auto;
}
.c-conductor__controls {
padding-left: 25px; // Line up visually with other controls
}
&__mode-icon {
grid-row: 1;
@ -200,17 +209,19 @@
justify-content: flex-start;
}
grid-template-areas:
.c-conductor__time-bounds {
grid-template-areas:
"tc-mode-icon tc-start tc-start"
"tc-mode-icon tc-end tc-end"
"tc-mode-icon tc-controls tc-controls";
}
}
}
&.is-realtime-mode {
grid-template-areas:
.c-conductor__time-bounds {
grid-template-areas:
"tc-mode-icon tc-start tc-updated"
"tc-mode-icon tc-end tc-end"
"tc-mode-icon tc-controls tc-controls";
"tc-mode-icon tc-end tc-end";
}
.c-conductor__end-fixed {
justify-content: flex-end;

View File

@ -21,7 +21,7 @@
*****************************************************************************/
<template>
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper" ref="calendarHolder">
<a class="c-click-icon icon-calendar"
<a class="c-icon-button icon-calendar"
@click="toggle"></a>
<div class="c-menu c-menu--mobile-modal c-datetime-picker"
v-if="open">
@ -30,10 +30,10 @@
@click="toggle"></button>
</div>
<div class="c-datetime-picker__pager c-pager l-month-year-pager">
<div class="c-pager__prev c-click-icon icon-arrow-left"
<div class="c-pager__prev c-icon-button icon-arrow-left"
@click.stop="changeMonth(-1)"></div>
<div class="c-pager__month-year">{{model.month}} {{model.year}}</div>
<div class="c-pager__next c-click-icon icon-arrow-right"
<div class="c-pager__next c-icon-button icon-arrow-right"
@click.stop="changeMonth(1)"></div>
</div>
<div class="c-datetime-picker__calendar c-calendar">
@ -91,7 +91,7 @@
grid-template-columns: auto 1fr auto;
align-items: center;
.c-click-icon {
.c-icon-button {
font-size: 0.8em;
}

View File

@ -21,6 +21,46 @@
*****************************************************************************/
// Used by About screen, licenses, etc.
.c-splash-image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-image: url('../ui/layout/assets/images/bg-splash.jpg');
&:before,
&:after {
background-position: center;
background-repeat: no-repeat;
position: absolute;
background-image: url('../ui/layout/assets/images/logo-app-shdw.svg');
background-size: contain;
content: '';
}
&:before {
// NASA logo, dude
$w: 5%;
$m: 10px;
background-image: url('../ui/layout/assets/images/logo-nasa.svg');
top: $m;
right: auto;
bottom: auto;
left: $m;
height: auto;
width: $w * 2;
padding-bottom: $w;
padding-top: $w;
}
&:after {
// App logo
top: 0;
right: 15%;
bottom: 0;
left: 15%;
}
}
.c-about {
&--splash {
// Large initial image after click on app logo with text beneath
@ -37,48 +77,6 @@
&__text {
height: 50%;
flex: 1 1 0;
position: relative;
}
&__image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-image: url('../ui/layout/assets/images/bg-splash.jpg');
position: relative;
&:before,
&:after {
background-position: center;
background-repeat: no-repeat;
position: absolute;
background-image: url('../ui/layout/assets/images/logo-app-shdw.svg');
background-size: contain;
content: '';
}
&:before {
// NASA logo, dude
$w: 5%;
$m: 10px;
background-image: url('../ui/layout/assets/images/logo-nasa.svg');
top: $m;
right: auto;
bottom: auto;
left: $m;
height: auto;
width: $w * 2;
padding-bottom: $w;
padding-top: $w;
}
&:after {
// App logo
top: 0;
right: 15%;
bottom: 0;
left: 15%;
}
}
&__text {

View File

@ -83,6 +83,9 @@ $uiColor: #00b2ff; // Resize bars, splitter bars, etc.
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
$colorA: #ccc;
$colorAHov: #fff;
$filterHov: brightness(1.3); // Tree, location items
$colorSelectedBg: pushBack($colorKey, 10%);
$colorSelectedFg: pullForward($colorBodyFg, 20%);
// Layout
$shellMainPad: 4px 0;
@ -98,6 +101,20 @@ $colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324de
$colorStatusError: #da0004;
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%) contrast(115%);
$colorStatusBtnBg: #666; // Where is this used?
$colorAlert: #ff3c00;
$colorAlertFg: #fff;
$colorWarningHi: #990000;
$colorWarningHiFg: #FF9594;
$colorWarningLo: #ff9900;
$colorWarningLoFg: #523400;
$colorDiagnostic: #a4b442;
$colorDiagnosticFg: #39461A;
$colorCommand: #3693bd;
$colorCommandFg: #fff;
$colorInfo: #2294a2;
$colorInfoFg: #fff;
$colorOk: #33cc33;
$colorOkFg: #fff;
// States
$colorPausedBg: #ff9900;
@ -173,9 +190,13 @@ $colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
$colorBtnCautionBg: #f16f6f;
$colorBtnCautionBgHov: #f1504e;
$colorBtnCautionFg: $colorBtnFg;
$colorClickIcon: $colorKey;
$colorClickIconBgHov: rgba($colorKey, 0.6);
$colorClickIconFgHov: $colorKeyHov;
$colorBtnActiveBg: $colorOk;
$colorBtnActiveFg: $colorOkFg;
$colorBtnSelectedBg: $colorSelectedBg;
$colorBtnSelectedFg: $colorSelectedFg;
$colorClickIconButton: $colorKey;
$colorClickIconButtonBgHov: rgba($colorKey, 0.6);
$colorClickIconButtonFgHov: $colorKeyHov;
$colorDropHint: $colorKey;
$colorDropHintBg: pushBack($colorDropHint, 10%);
$colorDropHintBgHov: $colorDropHint;
@ -183,6 +204,9 @@ $colorDropHintFg: pullForward($colorDropHint, 40%);
$colorDisclosureCtrl: rgba($colorBodyFg, 0.5);
$colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7);
$btnStdH: 24px;
$colorCursorGuide: rgba(white, 0.6);
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
// Menus
$colorMenuBg: pullForward($colorBodyBg, 15%);
@ -241,7 +265,7 @@ $overlayColorFg: $colorMenuFg;
$colorOvrBtnBg: pullForward($overlayColorBg, 20%);
$colorOvrBtnFg: #fff;
$overlayCr: $interiorMarginLg;
$overlayBrightnessAdjust: brightness(1.3);
$overlayBrightnessAdjust: brightness(1.3); // Applied in a filter: property
// Indicator colors
$colorIndicatorAvailable: $colorKey;
@ -262,19 +286,9 @@ $colorLimitRedBg: #940000;
$colorLimitRedFg: #ffa489;
$colorLimitRedIc: #ff4222;
// Status
$colorAlert: #ff3c00;
$colorWarningHi: #990000;
$colorWarningLo: #ff9900;
$colorDiagnostic: #a4b442;
$colorCommand: #3693bd;
$colorInfo: #2294a2;
$colorOk: #33cc33;
// Bubble colors
$colorInfoBubbleBg: #dddddd;
$colorInfoBubbleFg: #666;
$colorInfoBubbleFg: #666;
$colorThumbsBubbleFg: pullForward($colorBodyFg, 10%);
$colorThumbsBubbleBg: pullForward($colorBodyBg, 10%);
@ -296,6 +310,9 @@ $colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
$colorTabGroupHeaderFg: pushBack($colorTabHeaderFg, 10%);
$colorSummaryBg: #2c2c2c;
$colorSummaryFg: rgba($colorBodyFg, 0.7);
$colorSummaryFgEm: $colorBodyFg;
// Plot
$colorPlotBg: rgba(black, 0.05);
@ -314,7 +331,7 @@ $colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorItemTreeIcon: $colorKey; // Used
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
$colorItemTreeFg: $colorBodyFg;
$colorItemTreeSelectedBg: pushBack($colorKey, 15%);
$colorItemTreeSelectedBg: $colorSelectedBg;
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
$colorItemTreeSelectedIcon: $colorItemTreeSelectedFg;
$colorItemTreeEditingBg: pushBack($editUIColor, 20%);

View File

@ -87,6 +87,9 @@ $uiColor: #00b2ff; // Resize bars, splitter bars, etc.
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
$colorA: #ccc;
$colorAHov: #fff;
$filterHov: brightness(1.3); // Tree, location items
$colorSelectedBg: pushBack($colorKey, 10%);
$colorSelectedFg: pullForward($colorBodyFg, 20%);
// Layout
$shellMainPad: 4px 0;
@ -102,6 +105,20 @@ $colorStatusAlertFilter: invert(78%) sepia(26%) saturate(1160%) hue-rotate(324de
$colorStatusError: #da0004;
$colorStatusErrorFilter: invert(10%) sepia(96%) saturate(4360%) hue-rotate(351deg) brightness(111%) contrast(115%);
$colorStatusBtnBg: #666; // Where is this used?
$colorAlert: #ff3c00;
$colorAlertFg: #fff;
$colorWarningHi: #990000;
$colorWarningHiFg: #FF9594;
$colorWarningLo: #ff9900;
$colorWarningLoFg: #523400;
$colorDiagnostic: #a4b442;
$colorDiagnosticFg: #39461A;
$colorCommand: #3693bd;
$colorCommandFg: #fff;
$colorInfo: #2294a2;
$colorInfoFg: #fff;
$colorOk: #33cc33;
$colorOkFg: #fff;
// States
$colorPausedBg: #ff9900;
@ -177,9 +194,13 @@ $colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
$colorBtnCautionBg: #f16f6f;
$colorBtnCautionBgHov: #f1504e;
$colorBtnCautionFg: $colorBtnFg;
$colorClickIcon: $colorKey;
$colorClickIconBgHov: rgba($colorKey, 0.6);
$colorClickIconFgHov: $colorKeyHov;
$colorBtnActiveBg: $colorOk;
$colorBtnActiveFg: $colorOkFg;
$colorBtnSelectedBg: $colorSelectedBg;
$colorBtnSelectedFg: $colorSelectedFg;
$colorClickIconButton: $colorKey;
$colorClickIconButtonBgHov: rgba($colorKey, 0.6);
$colorClickIconButtonFgHov: $colorKeyHov;
$colorDropHint: $colorKey;
$colorDropHintBg: pushBack($colorDropHint, 10%);
$colorDropHintBgHov: $colorDropHint;
@ -187,6 +208,9 @@ $colorDropHintFg: pullForward($colorDropHint, 40%);
$colorDisclosureCtrl: rgba($colorBodyFg, 0.5);
$colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7);
$btnStdH: 24px;
$colorCursorGuide: rgba(white, 0.6);
$shdwCursorGuide: rgba(black, 0.4) 0 0 2px;
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
// Menus
$colorMenuBg: pullForward($colorBodyBg, 15%);
@ -245,7 +269,7 @@ $overlayColorFg: $colorMenuFg;
$colorOvrBtnBg: pullForward($overlayColorBg, 20%);
$colorOvrBtnFg: #fff;
$overlayCr: $interiorMarginLg;
$overlayBrightnessAdjust: brightness(1.3);
$overlayBrightnessAdjust: brightness(1.3); // Applied in a filter: property
// Indicator colors
$colorIndicatorAvailable: $colorKey;
@ -266,19 +290,9 @@ $colorLimitRedBg: #940000;
$colorLimitRedFg: #ffa489;
$colorLimitRedIc: #ff4222;
// Status
$colorAlert: #ff3c00;
$colorWarningHi: #990000;
$colorWarningLo: #ff9900;
$colorDiagnostic: #a4b442;
$colorCommand: #3693bd;
$colorInfo: #2294a2;
$colorOk: #33cc33;
// Bubble colors
$colorInfoBubbleBg: #dddddd;
$colorInfoBubbleFg: #666;
$colorInfoBubbleFg: #666;
$colorThumbsBubbleFg: pullForward($colorBodyFg, 10%);
$colorThumbsBubbleBg: pullForward($colorBodyBg, 10%);
@ -300,6 +314,9 @@ $colorTabHeaderFg: $colorBodyFg;
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
$colorTabGroupHeaderFg: pushBack($colorTabHeaderFg, 10%);
$colorSummaryBg: #2c2c2c;
$colorSummaryFg: rgba($colorBodyFg, 0.7);
$colorSummaryFgEm: $colorBodyFg;
// Plot
$colorPlotBg: rgba(black, 0.05);
@ -318,7 +335,7 @@ $colorItemTreeHoverFg: pullForward($colorBodyFg, 20%);
$colorItemTreeIcon: $colorKey; // Used
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
$colorItemTreeFg: $colorBodyFg;
$colorItemTreeSelectedBg: pushBack($colorKey, 15%);
$colorItemTreeSelectedBg: $colorSelectedBg;
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
$colorItemTreeSelectedIcon: $colorItemTreeSelectedFg;
$colorItemTreeEditingBg: pushBack($editUIColor, 20%);
@ -365,7 +382,7 @@ $colorMobilePaneLeftTreeItemFg: $colorItemTreeFg;
$colorMobileSelectListTreeItemBg: rgba(#000, 0.05);
// About Screen
$colorAboutLink: $colorKeySubtle;
$colorAboutLink: #9bb5ff;
// Loading
$colorLoadingFg: #776ba2;

View File

@ -27,8 +27,8 @@ $mobileListIconSize: 30px;
$mobileTitleDescH: 35px;
$mobileOverlayMargin: 20px;
$mobileMenuIconD: 34px;
$phoneItemH: floor($ueBrowseGridItemLg/4);
$tabletItemH: floor($ueBrowseGridItemLg/3);
$phoneItemH: floor($gridItemMobile / 4);
$tabletItemH: floor($gridItemMobile / 3);
/************************** MOBILE TREE MENU DIMENSIONS */
$mobileTreeItemH: 35px;

View File

@ -83,6 +83,9 @@ $uiColor: #289fec; // Resize bars, splitter bars, etc.
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
$colorA: #999;
$colorAHov: $colorKey;
$filterHov: brightness(1.3); // Tree, location items
$colorSelectedBg: pushBack($colorKey, 40%);
$colorSelectedFg: pullForward($colorBodyFg, 10%);
// Layout
$shellMainPad: 4px 0;
@ -98,6 +101,20 @@ $colorStatusAlertFilter: invert(89%) sepia(26%) saturate(5035%) hue-rotate(316de
$colorStatusError: #da0004;
$colorStatusErrorFilter: invert(8%) sepia(96%) saturate(4511%) hue-rotate(352deg) brightness(136%) contrast(114%);
$colorStatusBtnBg: #666; // Where is this used?
$colorAlert: #ff3c00;
$colorAlertFg: #fff;
$colorWarningHi: #990000;
$colorWarningHiFg: #FF9594;
$colorWarningLo: #ff9900;
$colorWarningLoFg: #523400;
$colorDiagnostic: #a4b442;
$colorDiagnosticFg: #39461A;
$colorCommand: #3693bd;
$colorCommandFg: #fff;
$colorInfo: #2294a2;
$colorInfoFg: #fff;
$colorOk: #33cc33;
$colorOkFg: #fff;
// States
$colorPausedBg: #ff9900;
@ -173,9 +190,13 @@ $colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
$colorBtnCautionBg: #f16f6f;
$colorBtnCautionBgHov: #f1504e;
$colorBtnCautionFg: $colorBtnFg;
$colorClickIcon: $colorKey;
$colorClickIconBgHov: rgba($colorKey, 0.2);
$colorClickIconFgHov: $colorKeyHov;
$colorBtnActiveBg: $colorOk;
$colorBtnActiveFg: $colorOkFg;
$colorBtnSelectedBg: $colorBtnMajorBg;
$colorBtnSelectedFg: $colorBtnMajorFg;
$colorClickIconButton: $colorKey;
$colorClickIconButtonBgHov: rgba($colorKey, 0.2);
$colorClickIconButtonFgHov: $colorKeyHov;
$colorDropHint: $colorKey;
$colorDropHintBg: pushBack($colorDropHint, 10%);
$colorDropHintBgHov: pushBack($colorDropHint, 40%);
@ -183,6 +204,9 @@ $colorDropHintFg: pushBack($colorDropHint, 0);
$colorDisclosureCtrl: rgba($colorBodyFg, 0.5);
$colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7);
$btnStdH: 24px;
$colorCursorGuide: rgba(black, 0.6);
$shdwCursorGuide: rgba(white, 0.4) 0 0 2px;
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
// Menus
$colorMenuBg: pushBack($colorBodyBg, 10%);
@ -241,7 +265,7 @@ $overlayColorFg: $colorMenuFg;
$colorOvrBtnBg: pullForward($overlayColorBg, 40%);
$colorOvrBtnFg: #fff;
$overlayCr: $interiorMarginLg;
$overlayBrightnessAdjust: brightness(1.3);
$overlayBrightnessAdjust: brightness(1); // Applied in a filter: property
// Indicator colors
$colorIndicatorAvailable: $colorKey;
@ -262,15 +286,6 @@ $colorLimitRedBg: #ff0000;
$colorLimitRedFg: #fff;
$colorLimitRedIc: #ffa99a;
// Status
$colorAlert: #ff3c00;
$colorWarningHi: #990000;
$colorWarningLo: #ff9900;
$colorDiagnostic: #a4b442;
$colorCommand: #3693bd;
$colorInfo: #2294a2;
$colorOk: #33cc33;
// Bubble colors
$colorInfoBubbleBg: $colorMenuBg;
$colorInfoBubbleFg: #666;
@ -295,6 +310,9 @@ $colorTabHeaderFg: pullForward($colorBodyFg, 20%);
$colorTabHeaderBorder: $colorBodyBg;
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
$colorTabGroupHeaderFg: pullForward($colorTabGroupHeaderBg, 40%);
$colorSummaryBg: #999;
$colorSummaryFg: rgba($colorBodyBg, 0.7);
$colorSummaryFgEm: white;
// Plot
$colorPlotBg: rgba(black, 0.05);
@ -313,8 +331,8 @@ $colorItemTreeHoverFg: pullForward($colorBodyFg, 10%);
$colorItemTreeIcon: $colorKey; // Used
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
$colorItemTreeFg: $colorBodyFg;
$colorItemTreeSelectedBg: pushBack($colorKey, 15%);
$colorItemTreeSelectedFg: $colorBodyBg;
$colorItemTreeSelectedBg: $colorSelectedBg;
$colorItemTreeSelectedFg: $colorItemTreeHoverFg;
$colorItemTreeSelectedIcon: $colorItemTreeSelectedFg;
$colorItemTreeEditingBg: pushBack($editUIColor, 20%);
$colorItemTreeEditingFg: $editUIColor;
@ -337,6 +355,7 @@ $scrollbarThumbColorMenuHov: darken($scrollbarThumbColorMenu, 2%);
// Splitter
$splitterHandleD: 2px;
$splitterD: $splitterHandleD;
$splitterHandleHitMargin: 4px;
$colorSplitterBaseBg: $colorBodyBg;
$colorSplitterBg: pullForward($colorSplitterBaseBg, 20%);

View File

@ -80,7 +80,10 @@ $formLabelW: 30%;
$waitSpinnerD: 32px;
$waitSpinnerTreeD: 20px;
$waitSpinnerBorderW: 5px;
$waitSpinnerTreeBorderW: 4px;
$waitSpinnerTreeBorderW: 3px;
/*************** Messages */
$messageIconD: 80px;
$messageListIconD: 32px;
/************************** MOBILE */
$mobileMenuIconD: 24px; // Used

View File

@ -48,16 +48,26 @@ button {
height: $d; width: $d;
}
}
}
.c-compact-button {
@include cCompactButtons($bg: $colorBtnBg, $fg: $colorBtnFg, $bgHov: $colorBtnBgHov);
&.is-active {
background: $colorBtnActiveBg;
color: $colorBtnActiveFg;
}
&.is-selected {
background: $colorBtnSelectedBg;
color: $colorBtnSelectedFg;
}
}
/********* Icon Buttons */
.c-click-icon,
.c-click-swatch {
.c-click-icon {
@include cClickIcon();
}
.c-icon-button,
.c-click-swatch {
@include cClickIconButton();
&--menu {
&:after {
@ -70,8 +80,8 @@ button {
}
}
.c-click-icon {
.c-click-icon__label {
.c-icon-button {
.c-icon-button__label {
margin-left: $interiorMargin;
}
@ -139,7 +149,7 @@ button {
font-family: symbolsfont;
font-size: 1rem * $s;
transform-origin: center;
transition: transform 100ms ease-in-out;
transition: $transOut;
}
}
@ -245,7 +255,13 @@ select {
background-position: right .4em top 80%, 0 0;
border: none;
border-radius: $controlCr;
padding: 4px 20px 4px $interiorMargin;
padding: 1px 20px 1px $interiorMargin;
*,
option {
background: $colorBtnBg;
color: $colorBtnFg;
}
}
// CHECKBOX LISTS
@ -260,6 +276,63 @@ select {
}
}
/******************************************************** TABS */
.c-tabs {
// Single horizontal strip of tabs, with a bottom divider line
@include userSelectNone();
display: flex;
flex: 0 0 auto;
flex-wrap: wrap;
position: relative; // Required in case this is applied to a <ul>
&:before {
// Separator line at bottom of tabs
content: '';
display: block;
height: 1px;
width: 100%;
background: $colorBtnReverseBg;
position: absolute;
bottom: 0px;
z-index: 1;
}
}
.c-tab {
// Used in Tab View, generic tabs
background: $colorBtnBg;
color: $colorBtnFg;
cursor: pointer;
display: flex;
align-items: center;
flex: 1 1 auto;
margin: 1px 1px 0 0;
padding: $interiorMargin $interiorMarginLg;
white-space: nowrap;
--notchSize: 7px;
clip-path:
polygon(
0% 0%,
calc(100% - var(--notchSize)) 0%,
100% var(--notchSize),
100% calc(100% - var(--notchSize)),
100% 100%,
0% 100%
);
@include hover() {
background: $colorBtnBgHov;
}
&.is-current {
background: $colorBtnReverseBg;
color: $colorBtnReverseFg;
pointer-events: none;
}
}
/******************************************************** HYPERLINKS AND HYPERLINK BUTTONS */
.c-hyperlink {
&--link {
@ -502,12 +575,12 @@ select {
@include cToolbarSeparator();
}
.c-click-icon,
.c-icon-button,
.c-labeled-input {
color: $editUIBaseColorFg;
}
.c-click-icon {
.c-icon-button {
@include cControl();
$pLR: $interiorMargin - 1;
$pTB: 2px;
@ -551,8 +624,6 @@ select {
/********* Button Sets */
.c-button-set {
// When one set is adjacent to another, provides a divider between
display: inline-flex;
flex: 0 0 auto;
@ -561,21 +632,24 @@ select {
flex: 0 0 auto;
}
+ .c-button-set {
&:before {
@include cToolbarSeparator();
content: '';
}
}
&[class*='--strip'] {
// Buttons are smashed together with minimal margin
// c-buttons don't have border-radius between buttons, creates a tool-button-strip look
// c-click-icons get grouped more closely together
// c-icon-buttons get grouped more closely together
[class^="c-button"] {
// Only apply the following to buttons that have background, eg. c-button
border-radius: 0;
}
}
&[class*='--strip-h'] {
// Horizontal strip
+ .c-button-set {
margin-left: $interiorMargin;
}
[class^="c-button"] {
+ * {
margin-left: 1px;
}
@ -647,6 +721,45 @@ input[type="range"] {
}
}
.h-local-controls {
// Holder for local controls
&--horz {
// Horizontal layout be
display: inline-flex;
align-items: center;
}
&--overlay-content {
> .c-button {
background: $colorLocalControlOvrBg;
border-radius: $controlCr;
box-shadow: $colorLocalControlOvrBg 0 0 0 2px;
}
}
}
.c-local-controls {
// Controls that are in close proximity to an element they effect
&--show-on-hover {
// Hidden by default; requires a hover 1 - 3 levels above to display
transition: $transOut;
opacity: 0;
pointer-events: none;
}
}
.has-local-controls:hover {
> .c-local-controls--show-on-hover,
> * > .c-local-controls--show-on-hover,
> * > * > .c-local-controls--show-on-hover,
> * > * > * > .c-local-controls--show-on-hover
{
transition: $transIn;
opacity: 1;
pointer-events: inherit;
}
}
/***************************************************** DRAG AND DROP */
.c-drop-hint {
// Used in Tabs View, Flexible Grid Layouts
@ -701,19 +814,3 @@ input[type="range"] {
display: flex;
align-items: center;
}
.h-local-controls {
&-overlay-content {
box-shadow: $colorBodyBg 0 0 0 2px;
display: inline-block;
position: absolute;
right: $interiorMargin; top: $interiorMargin;
z-index: 2;
}
&-trans {
// Has a translucent background plate
background: rgba($colorBodyBg, 0.8);
border-radius: $controlCr;
}
}

View File

@ -101,7 +101,6 @@
min-width: 120px;
order: 1;
position: relative;
white-space: nowrap;
width: $formLabelW;
}
@ -121,26 +120,35 @@
margin-right: 5px;
}
}
.select {
.l-input-lg { // LEGACY FORM SUPPORT
input[type=text],
input[type=search],
input[type=number] {
width: 100%;
}
}
select {
margin-right: $interiorMargin;
}
}
.hint, .field-hints { color: $colorFieldHint; }
}
}
.selector-list {
// Used in create overlay to display tree view
@include nice-input();
padding: $interiorMargin;
position: relative;
min-height: 150px;
height: 100%;
>.wrapper {
$p: $interiorMargin;
box-sizing: border-box;
overflow: auto;
}
}
.selector-list {
// Displays tree view in dialogs
@include nice-input();
padding: $interiorMargin;
position: relative;
min-height: 0; // Chrome 73 overflow bug fix
height: 100%;
>.wrapper {
$p: $interiorMargin;
box-sizing: border-box;
overflow: auto;
}
}
@ -338,7 +346,6 @@
}
}
mct-form.validates {
.form-row.validates {
> .label {

View File

@ -19,8 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/******************************* RESETS */
/******************************************************** RESETS */
*,
:before,
:after {
@ -31,7 +30,7 @@ div {
position: relative;
}
/******************************* UTILITIES */
/******************************************************** UTILITIES */
.u-contents {
display: contents;
}
@ -45,7 +44,7 @@ div {
}
}
/******************************* BROWSER ELEMENTS */
/******************************************************** BROWSER ELEMENTS */
body.desktop {
::-webkit-scrollbar {
box-sizing: border-box;
@ -75,7 +74,7 @@ body.desktop {
}
}
/************************** HTML ENTITIES */
/******************************************************** HTML ENTITIES */
a {
color: $colorA;
cursor: pointer;
@ -167,7 +166,6 @@ body.desktop .has-local-controls {
/******************************************************** SELECTION AND EDITING */
// Provides supporting styles for Display Layouts and augmented legacy Fixed Position view
.c-grid,
.c-grid__x,
.c-grid__y {
@ -204,168 +202,88 @@ body.desktop .has-local-controls {
}
}
/************************** LEGACY */
mct-container {
/******************************************************** STATES */
@mixin spinner($b: 5px, $c: $colorKey) {
animation-name: rotation-centered;
animation-duration: 0.5s;
animation-iteration-count: infinite;
animation-timing-function: linear;
border-radius: 100%;
box-sizing: border-box;
border-color: rgba($c, 0.25);
border-top-color: rgba($c, 1.0);
border-style: solid;
border-width: $b;
display: block;
}
.abs {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: auto;
left: 50%; top: 50%;
transform-origin: center;
transform: translate(-50%, -50%);
}
.code {
font-family: "Lucida Console", monospace;
font-size: 0.7em;
line-height: 150%;
white-space: pre;
.wait-spinner {
@include spinner($waitSpinnerBorderW, $colorKey);
pointer-events: none;
z-index: 2;
&.inline {
display: inline-block !important;
margin-right: $interiorMargin;
position: relative !important;
vertical-align: middle;
}
}
.codehilite {
@extend .code;
background-color: rgba($colorBodyFg, 0.1);
padding: 1em;
.loading {
// Can be applied to any block element with height and width
pointer-events: none;
&:before,
&:after {
content: '';
}
&:before {
@include spinner($waitSpinnerBorderW, $colorLoadingFg);
height: $waitSpinnerD; width: $waitSpinnerD;
z-index: 10;
}
&:after {
@include abs();
background: $colorLoadingBg;
display: block;
z-index: 9;
}
&.c-tree__item {
$d: $waitSpinnerTreeD;
$spinnerL: 19px + $d/2;
display: flex;
align-items: center;
padding-left: $spinnerL + $d/2 + $interiorMargin;
background: $colorLoadingBg;
min-height: 5px + $d;
.c-tree__item__label {
font-style: italic;
opacity: 0.6;
}
&:before {
height: $d;
width: $d;
border-width: 4px;
left: $spinnerL;
}
&:after {
display: none;
}
}
&.c-loading--overlay {
@include abs();
}
}
.disabled,
a.disabled {
*[disabled],
.disabled {
opacity: $controlDisabledOpacity;
pointer-events: none !important;
cursor: default !important;
}
.s-status-missing {
// Labels. Expects .s-status-missing to be applied to mct-representation that contains
.t-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
// Item, grid item. Expects .s-status-missing to be applied to mct-representation that contains .item.grid-item
.item .t-item-icon-glyph:before {
content: $glyph-icon-object-unknown;
}
// Object header. Expects .s-status-missing to be applied to mct-representation.object-header
&.object-header {
.type-icon:before {
content: $glyph-icon-object-unknown;
}
}
// Tree item. Expects .s-status-missing to be applied to .tree-item,
// and mct-representation.search-item
&.tree-item,
&.search-item {
> .rep-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
}
}
.align-right {
text-align: right;
}
.centered {
text-align: center;
}
.no-selection {
// aka selection = "None". Used in palettes and their menu buttons.
$c: red;
$s: 48%;
$e: 52%;
background-image: linear-gradient(-45deg,
transparent $s - 5%,
$c $s,
$c $e,
transparent $e + 5%
);
box-shadow:inset rgba(black, 0.3) 0 0 0 1px;
background-repeat: no-repeat;
background-size: contain;
}
.scrolling,
.scroll {
overflow: auto;
}
.vscroll {
overflow-x: hidden;
overflow-y: auto;
&.scroll-pad {
padding-right: $interiorMargin;
}
}
.vscroll--persist {
overflow-x: hidden;
overflow-y: scroll;
}
.slidable {
cursor: move; // Fallback
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
&.horz {
cursor: col-resize;
}
&.vert {
cursor: row-resize;
}
}
.no-margin {
margin: 0;
}
.ds {
box-shadow: rgba(#000, 0.7) 0 4px 10px 2px;
}
.capitalize {
text-transform: capitalize;
}
.hide,
.hidden,
.t-main-view .hide-in-t-main-view {
display: none !important;
}
.hide-nice {
opacity: 0;
pointer-events: none;
}
.invisible {
display: block;
visibility: hidden;
height: 0;
padding: 0;
border: 0;
margin: 0 !important;
transform: scale(0);
pointer-events: none;
position: absolute;
}
.sep {
color: rgba(#fff, 0.2);
}
.comma-list span {
&:not(:first-child) {
&:before {
content: ', ';
}
}
}

View File

@ -0,0 +1,144 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
/******************************************************************* MESSAGES */
.w-message-contents {
flex: 1 1 auto;
display: flex;
flex-direction: column;
> * + * {
margin-bottom: $interiorMargin;
}
.message-body {
flex: 1 1 100%;
}
}
// Singleton in an overlay dialog
.t-message-single .l-message,
.t-message-single.l-message {
$iconW: $messageListIconD;
&:before {
font-size: $iconW;
width: $iconW + 2;
}
.title {
font-size: 1.2em;
}
}
// Singleton inline in a view
.t-message-inline .l-message,
.t-message-inline.l-message {
border-radius: $controlCr;
&.message-severity-info { background-color: rgba($colorInfo, 0.3); }
&.message-severity-alert { background-color: rgba($colorWarningLo, 0.3); }
&.message-severity-error { background-color: rgba($colorWarningHi, 0.3); }
.w-message-contents.l-message-body-only {
.message-body {
margin-top: $interiorMargin;
}
}
}
// In a list
.c-overlay__messages {
//@include abs();
display: flex;
flex-direction: column;
overflow: auto;
padding-right: $interiorMargin;
> div,
> span {
margin-bottom: $interiorMargin;
}
.w-messages {
flex: 1 1 100%;
overflow-y: auto;
padding-right: $interiorMargin;
}
// Each message
.c-message {
@include discreteItem();
flex: 0 0 auto;
margin-bottom: $interiorMarginSm;
.hint,
.bottom-bar {
text-align: left;
}
}
}
@include phonePortrait {
.t-message-single .l-message,
.t-message-single.l-message {
flex-direction: column;
&:before {
margin-right: 0;
margin-bottom: $interiorMarginLg;
text-align: center;
width: 100%;
}
.bottom-bar {
text-align: center;
.s-button {
display: block;
width: 100%;
}
}
}
}
body.desktop .t-message-list {
.w-message-contents { padding-right: $interiorMargin; }
}
// Alert elements in views
mixin sUnSynced() {
$c: $colorPausedBg;
border: 1px solid $c;
}
.s-unsynced {
@include sUnsynced();
}
.s-status-timeconductor-unsynced {
// Plot areas
.gl-plot .gl-plot-display-area {
@include sUnsynced();
}
// Object headers
.object-header {
.t-object-alert {
display: inline;
}
}
}

View File

@ -0,0 +1,550 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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.
*****************************************************************************/
/********************************************************************* PLOTS */
mct-plot {
display: contents;
}
/*********************** STACKED PLOT LAYOUT */
.t-plot-stacked {
.l-view-section {
// Make this a flex container
display: flex;
flex-flow: column nowrap;
.gl-plot.child-frame {
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
}
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: block;
}
}
}
.gl-plot {
color: $colorPlotFg;
display: flex;
//font-size: 0.7rem;
position: relative;
width: 100%;
height: 100%;
min-height: $plotMinH;
/*********************** AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area {
margin-top: $interiorMargin; // Keep the top tick label from getting clipped
position: relative;
flex: 1 1 auto;
.t-object-alert {
position: absolute;
display: block;
font-size: 1.5em;
top: $interiorMarginSm;
left: $interiorMarginSm;
z-index: 2;
}
}
.gl-plot-wrapper-display-area-and-x-axis {
// Holds the plot area and the X-axis only
position: absolute;
top: nth($plotDisplayArea, 1);
right:0;
bottom: 0;
left: nth($plotDisplayArea, 4);
.gl-plot-display-area {
position: absolute;
top: 0;
right: 0;
bottom: nth($plotDisplayArea, 3);
left: 0;
}
.gl-plot-axis-area.gl-plot-x {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: $plotXBarH;
width: auto;
overflow: hidden;
}
}
.gl-plot-axis-area {
position: absolute;
&.gl-plot-y {
top: nth($plotDisplayArea, 1);
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
width: $plotYBarW;
}
}
.gl-plot-coords {
box-sizing: border-box;
border-radius: $controlCr;
background: black;
color: lighten($colorBodyFg, 30%);
padding: 2px 5px;
position: absolute;
top: nth($plotDisplayArea,1) + $interiorMarginLg;
right: auto;
bottom: auto;
left: nth($plotDisplayArea,4) + $interiorMarginLg;
z-index: 10;
&:empty {
display: none;
}
}
.gl-plot-label,
.l-plot-label {
color: $colorPlotLabelFg;
position: absolute;
text-align: center;
&.gl-plot-x-label,
&.l-plot-x-label {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: auto;
}
&.gl-plot-y-label,
&.l-plot-y-label {
$x: -50%;
$r: -90deg;
transform-origin: 50% 0;
transform: translateX($x) rotate($r);
display: inline-block;
margin-left: $interiorMargin; // Kick off the left edge
left: 0;
top: 50%;
white-space: nowrap;
}
}
.gl-plot-x-options,
.gl-plot-y-options {
$h: 24px;
position: absolute;
height: $h;
min-height: $h;
z-index: 2;
}
.gl-plot-x-options {
transform: translateX(-50%);
bottom: 0;
left: 50%;
}
.gl-plot-y-options {
transform: translateY(-50%);
min-width: 150px; // Need this due to enclosure of .select
top: 50%;
left: $plotYLabelW + $interiorMargin * 2;
}
.t-plot-display-controls {
position: absolute;
top: $interiorMargin;
right: $interiorMargin;
}
.gl-plot-hash {
position: absolute;
opacity: $opacityPlotHash;
&.hash-v {
border-right: 1px $colorPlotHash $stylePlotHash;
height: 100%;
}
&.hash-h {
border-bottom: 1px $colorPlotHash $stylePlotHash;
width: 100%;
}
}
&__local-controls {
// Plot local controls
$m: $interiorMargin;
display: flex;
align-items: center;
position: absolute;
top: $m;
right: $m;
z-index: 9;
&__reset {
transition: right 100ms;
top: $m;
right: $m;
}
&__zoom-and-guides {
top: $m;
right: $m;
}
}
}
.gl-plot-display-area,
.plot-display-area {
@if $colorPlotBg != none {
background-color: $colorPlotBg;
}
cursor: crosshair;
border: 1px solid $colorPlotAreaBorder;
}
.tick {
position: absolute;
border: 0 $colorPlotHash solid;
&.tick-x {
border-right-width: 1px;
height: 100%; // Assumption is that the tick will be in a holder that will set it's height;
}
}
.gl-plot-tick,
.tick-label {
@include reverseEllipsis();
font-size: 0.7rem;
position: absolute;
&.gl-plot-x-tick-label,
&.tick-label-x {
right: auto;
bottom: auto;
left: auto;
height: auto;
width: 20%;
margin-left: -10%;
text-align: center;
}
&.gl-plot-y-tick-label,
&.tick-label-y {
top: auto;
height: 1em;
width: auto;
margin-bottom: -0.5em;
text-align: right;
}
}
.gl-plot-tick {
&.gl-plot-x-tick-label {
top: $interiorMargin;
}
&.gl-plot-y-tick-label {
right: $interiorMargin;
left: $interiorMargin;
}
}
.tick-label {
&.tick-label-x {
top: 0;
}
&.tick-label-y {
right: 0;
left: 0;
}
}
.export-plot {
$bg: white;
$fg: black;
$gry: #999;
background: $bg !important;
z-index: -10;
.l-view-section {
$m: $interiorMargin;
top: $m !important;
right: $m;
bottom: $m;
left: $m;
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: none;
}
}
}
.gl-plot-display-area {
background: none !important;
border-color: $gry !important;
.gl-plot-local-controls,
.h-local-controls {
opacity: 0;
}
}
.gl-plot {
color: $fg;
.gl-plot-hash {
opacity: 0.1;
border-color: $fg;
}
}
table {
thead {
border-bottom: none;
th {
background: #eee;
border-left-color: $bg;
color: #666;
}
tr {
border: none;
}
}
tbody {
tr {
border-top: 1px solid #ccc;
}
td {
color: $fg;
}
}
}
}
/****************** _LEGEND.SCSS */
.gl-plot-legend {
display: flex;
align-items: flex-start;
&__view-control {
padding-top: 2px;
margin-right: $interiorMarginSm;
}
table {
table-layout: fixed;
th,
td {
@include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
padding: 1px 3px; // Tighter than standard tabular padding
}
}
&.hover-on-plot {
// User is hovering over the plot to get a value at a point
.hover-value-enabled {
background-color: $legendHoverValueBg;
border-radius: $smallCr;
padding: 0 $interiorMarginSm;
&.value-to-display-min:before {
content: 'MIN ';
}
&.value-to-display-max:before {
content: 'MAX ';
}
}
}
}
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item {
// General styles for legend items, both expanded and collapsed legend states
.plot-series-color-swatch {
border-radius: $smallCr;
border: 1px solid $colorBodyBg;
display: inline-block;
height: $plotSwatchD;
width: $plotSwatchD;
}
.plot-series-name {
display: inline;
}
.plot-series-value {
@include ellipsize();
}
}
.plot-wrapper-expanded-legend {
flex: 1 1 auto;
}
.gl-plot {
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
/***************** GENERAL STYLES, COLLAPSED */
&.plot-legend-collapsed {
// .plot-legend-item is a span of spans.
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
&.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
.plot-legend-item {
display: flex;
align-items: center;
justify-content: stretch;
&:not(:first-child) {
margin-left: $interiorMarginLg;
}
.plot-series-swatch-and-name,
.plot-series-value {
@include ellipsize();
flex: 1 1 auto;
}
.plot-series-swatch-and-name {
margin-right: $interiorMarginSm;
}
.plot-series-value {
text-align: left;
}
}
}
/***************** GENERAL STYLES, EXPANDED */
&.plot-legend-expanded {
.gl-plot-legend {
max-height: 70%;
}
.plot-wrapper-expanded-legend {
overflow-y: auto;
}
}
/***************** TOP OR BOTTOM */
&.plot-legend-top,
&.plot-legend-bottom {
// General styles when legend is on the top or bottom
flex-direction: column;
&.plot-legend-collapsed {
// COLLAPSED ON TOP OR BOTTOM
.plot-wrapper-collapsed-legend {
display: flex;
flex: 1 1 auto;
overflow: hidden;
}
}
}
/***************** EITHER SIDE */
&.plot-legend-left,
&.plot-legend-right {
// If the legend is expanded, use flex-col instead so that the legend gets the width it needs.
&.plot-legend-expanded {
// EXPANDED, ON EITHER SIDE
flex-direction: column;
}
&.plot-legend-collapsed {
// COLLAPSED, ON EITHER SIDE
.gl-plot-legend {
max-height: inherit;
width: 25%;
}
.plot-wrapper-collapsed-legend {
display: flex;
flex-flow: column nowrap;
min-width: 0;
flex: 1 1 auto;
overflow-y: auto;
}
.plot-legend-item {
margin-bottom: 1px;
margin-left: 0;
flex-wrap: wrap;
.plot-series-swatch-and-name {
flex: 0 1 auto;
min-width: 20%;
}
.plot-series-value {
flex: 0 1 auto;
width: auto;
}
}
}
}
/***************** ON BOTTOM OR RIGHT */
&.plot-legend-right:not(.plot-legend-expanded),
&.plot-legend-bottom {
.gl-plot-legend {
order: 2;
}
.plot-wrapper-axis-and-display-area {
order: 1;
}
}
}
/***************** CURSOR GUIDES */
[class*='c-cursor-guide'] {
box-shadow: $shdwCursorGuide;
background-color: $colorCursorGuide;
display: none; // Displayed when an element with has-cursor-guides gets a hover; see below
pointer-events: none;
position: absolute;
}
.has-cursor-guides:hover [class*='c-cursor-guide'] {
display: block;
}
.c-cursor-guide {
&--h {
height: 1px;
left: 0; right: 0;
}
&--v {
width: 1px;
top: 0; bottom: 0;
}
}

View File

@ -19,491 +19,6 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/********************************************************************* PLOTS */
mct-plot {
display: contents;
}
/*********************** STACKED PLOT LAYOUT */
.t-plot-stacked {
.l-view-section {
// Make this a flex container
display: flex;
flex-flow: column nowrap;
.gl-plot.child-frame {
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
}
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: block;
}
}
}
.gl-plot {
color: $colorPlotFg;
display: flex;
font-size: 0.7rem;
position: relative;
width: 100%;
height: 100%;
min-height: $plotMinH;
/*********************** AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area {
margin-top: $interiorMargin; // Keep the top tick label from getting clipped
position: relative;
flex: 1 1 auto;
.t-object-alert {
position: absolute;
display: block;
font-size: 1.5em;
top: $interiorMarginSm;
left: $interiorMarginSm;
z-index: 2;
}
}
.gl-plot-wrapper-display-area-and-x-axis {
// Holds the plot area and the X-axis only
position: absolute;
top: nth($plotDisplayArea, 1);
right:0;
bottom: 0;
left: nth($plotDisplayArea, 4);
.gl-plot-display-area {
position: absolute;
top: 0;
right: 0;
bottom: nth($plotDisplayArea, 3);
left: 0;
}
.gl-plot-axis-area.gl-plot-x {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: $plotXBarH;
width: auto;
overflow: hidden;
}
}
.gl-plot-axis-area {
position: absolute;
&.gl-plot-y {
top: nth($plotDisplayArea, 1);
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
width: $plotYBarW;
}
}
.gl-plot-coords {
box-sizing: border-box;
border-radius: $controlCr;
background: black;
color: lighten($colorBodyFg, 30%);
padding: 2px 5px;
position: absolute;
top: nth($plotDisplayArea,1) + $interiorMarginLg;
right: auto;
bottom: auto;
left: nth($plotDisplayArea,4) + $interiorMarginLg;
z-index: 10;
&:empty {
display: none;
}
}
.gl-plot-label,
.l-plot-label {
color: $colorPlotLabelFg;
position: absolute;
text-align: center;
&.gl-plot-x-label,
&.l-plot-x-label {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: auto;
}
&.gl-plot-y-label,
&.l-plot-y-label {
$x: -50%;
$r: -90deg;
transform-origin: 50% 0;
transform: translateX($x) rotate($r);
display: inline-block;
margin-left: $interiorMargin; // Kick off the left edge
left: 0;
top: 50%;
white-space: nowrap;
}
}
.gl-plot-x-options,
.gl-plot-y-options {
$h: 24px;
position: absolute;
height: $h;
min-height: $h;
z-index: 2;
}
.gl-plot-x-options {
transform: translateX(-50%);
bottom: 0;
left: 50%;
}
.gl-plot-y-options {
transform: translateY(-50%);
min-width: 150px; // Need this due to enclosure of .select
top: 50%;
left: $plotYLabelW + $interiorMargin * 2;
}
.t-plot-display-controls {
position: absolute;
top: $interiorMargin;
right: $interiorMargin;
}
.gl-plot-hash {
position: absolute;
opacity: $opacityPlotHash;
&.hash-v {
border-right: 1px $colorPlotHash $stylePlotHash;
height: 100%;
}
&.hash-h {
border-bottom: 1px $colorPlotHash $stylePlotHash;
width: 100%;
}
}
}
.gl-plot-display-area,
.plot-display-area {
@if $colorPlotBg != none {
background-color: $colorPlotBg;
}
cursor: crosshair;
border: 1px solid $colorPlotAreaBorder;
}
.tick {
position: absolute;
border: 0 $colorPlotHash solid;
&.tick-x {
border-right-width: 1px;
height: 100%; // Assumption is that the tick will be in a holder that will set it's height;
}
}
.gl-plot-tick,
.tick-label {
@include reverseEllipsis();
font-size: 0.7rem;
position: absolute;
&.gl-plot-x-tick-label,
&.tick-label-x {
right: auto;
bottom: auto;
left: auto;
height: auto;
width: 20%;
margin-left: -10%;
text-align: center;
}
&.gl-plot-y-tick-label,
&.tick-label-y {
top: auto;
height: 1em;
width: auto;
margin-bottom: -0.5em;
text-align: right;
}
}
.gl-plot-tick {
&.gl-plot-x-tick-label {
top: $interiorMargin;
}
&.gl-plot-y-tick-label {
right: $interiorMargin;
left: $interiorMargin;
}
}
.tick-label {
&.tick-label-x {
top: 0;
}
&.tick-label-y {
right: 0;
left: 0;
}
}
.export-plot {
$bg: white;
$fg: black;
$gry: #999;
background: $bg !important;
z-index: -10;
.l-view-section {
$m: $interiorMargin;
top: $m !important;
right: $m;
bottom: $m;
left: $m;
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: none;
}
}
}
.gl-plot-display-area {
background: none !important;
border-color: $gry !important;
.gl-plot-local-controls,
.h-local-controls {
opacity: 0;
}
}
.gl-plot {
color: $fg;
.gl-plot-hash {
opacity: 0.1;
border-color: $fg;
}
}
table {
thead {
border-bottom: none;
th {
background: #eee;
border-left-color: $bg;
color: #666;
}
tr {
border: none;
}
}
tbody {
tr {
border-top: 1px solid #ccc;
}
td {
color: $fg;
}
}
}
}
/****************** _LEGEND.SCSS */
.gl-plot-legend {
display: flex;
align-items: flex-start;
&__view-control {
padding-top: 2px;
margin-right: $interiorMarginSm;
}
table {
table-layout: fixed;
th,
td {
@include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
padding: 1px 3px; // Tighter than standard tabular padding
//width: 1%;
}
}
&.hover-on-plot {
// User is hovering over the plot to get a value at a point
.hover-value-enabled {
background-color: $legendHoverValueBg;
border-radius: $smallCr;
padding: 0 $interiorMarginSm;
&.value-to-display-min:before {
content: 'MIN ';
}
&.value-to-display-max:before {
content: 'MAX ';
}
}
}
}
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item {
// General styles for legend items, both expanded and collapsed legend states
.plot-series-color-swatch {
border-radius: $smallCr;
border: 1px solid $colorBodyBg;
display: inline-block;
height: $plotSwatchD;
width: $plotSwatchD;
}
.plot-series-name {
display: inline;
}
.plot-series-value {
@include ellipsize();
}
}
.plot-wrapper-expanded-legend {
flex: 1 1 auto;
}
.gl-plot {
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
/***************** GENERAL STYLES, COLLAPSED */
&.plot-legend-collapsed {
// .plot-legend-item is a span of spans.
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
&.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
.plot-legend-item {
display: flex;
align-items: center;
justify-content: stretch;
&:not(:first-child) {
margin-left: $interiorMarginLg;
}
.plot-series-swatch-and-name,
.plot-series-value {
@include ellipsize();
flex: 1 1 auto;
}
.plot-series-swatch-and-name {
margin-right: $interiorMarginSm;
}
.plot-series-value {
text-align: left;
}
}
}
/***************** GENERAL STYLES, EXPANDED */
&.plot-legend-expanded {
.gl-plot-legend {
max-height: 70%;
}
.plot-wrapper-expanded-legend {
overflow-y: auto;
}
}
/***************** TOP OR BOTTOM */
&.plot-legend-top,
&.plot-legend-bottom {
// General styles when legend is on the top or bottom
flex-direction: column;
&.plot-legend-collapsed {
// COLLAPSED ON TOP OR BOTTOM
.plot-wrapper-collapsed-legend {
display: flex;
flex: 1 1 auto;
overflow: hidden;
}
}
}
/***************** EITHER SIDE */
&.plot-legend-left,
&.plot-legend-right {
// If the legend is expanded, use flex-col instead so that the legend gets the width it needs.
&.plot-legend-expanded {
// EXPANDED, ON EITHER SIDE
flex-direction: column;
}
&.plot-legend-collapsed {
// COLLAPSED, ON EITHER SIDE
.gl-plot-legend {
max-height: inherit;
width: 25%;
}
.plot-wrapper-collapsed-legend {
display: flex;
flex-flow: column nowrap;
min-width: 0;
flex: 1 1 auto;
overflow-y: auto;
}
.plot-legend-item {
margin-bottom: 1px;
margin-left: 0;
flex-wrap: wrap;
.plot-series-swatch-and-name {
flex: 0 1 auto;
min-width: 20%;
}
.plot-series-value {
flex: 0 1 auto;
width: auto;
}
}
}
}
/***************** ON BOTTOM OR RIGHT */
&.plot-legend-right:not(.plot-legend-expanded),
&.plot-legend-bottom {
.gl-plot-legend {
order: 2;
}
.plot-wrapper-axis-and-display-area {
order: 1;
}
}
}
/******************************************************************* VIEWS */
// From _views.scss
// Legacy overlay and stacked plots depend on this for now
@ -582,7 +97,6 @@ mct-plot {
}
.l-image-main-controlbar {
font-size: 0.8em;
line-height: inherit;
.l-datetime-w, .l-controls-w {
direction: rtl;
@ -624,7 +138,6 @@ mct-plot {
}
/*************************************** THUMBS */
.l-image-thumbs-wrapper {
overflow-x: hidden;
overflow-y: auto;
@ -639,7 +152,6 @@ mct-plot {
direction: ltr;
display: inline-block;
float: left;
font-size: 0.8em;
padding: 1px;
margin-left: $interiorMarginSm;
position: relative;
@ -679,9 +191,14 @@ mct-plot {
}
/*************************************** LOCAL CONTROLS */
.t-imagery {
.c-imagery {
display: contents;
.h-local-controls.h-local-controls-overlay-content {
.h-local-controls--overlay-content {
position: absolute;
right: $interiorMargin; top: $interiorMargin;
z-index: 2;
background: $colorLocalControlOvrBg;
border-radius: $basicCr;
max-width: 200px;
min-width: 100px;
width: 35%;
@ -699,8 +216,10 @@ mct-plot {
margin-right: $interiorMarginSm;
}
}
}
.t-reset-btn-holder {
&__lc {
&__reset-btn {
$bc: $scrollbarTrackColorBg;
&:before,
&:after {
@ -730,8 +249,8 @@ mct-plot {
.l-image-main-wrapper {
bottom: 0 !important;
height: 100% !important;
.l-image-main-controlbar {
font-size: 0.7em;
.l-image-main-controlbar .c-button {
//font-size: 0.7em;
}
}
.l-image-thumbs-wrapper,
@ -795,7 +314,7 @@ body.mobile.phone {
transition: $transOut;
width: 0;
.c-click-icon:before { font-size: 1em; }
.c-icon-button:before { font-size: 1em; }
}
&__direction {
@ -942,7 +461,7 @@ body.mobile.phone {
width: $ruleLabelW;
}
.t-message-widget-no-data {
.js-summary-widget__message {
display: none;
}
@ -989,7 +508,7 @@ body.mobile.phone {
&.expanded-widget-rules {
.widget-rules-wrapper {
min-height: 50px;
height: auto;
height: 100%; // Fix for Chrome 73 scrolling bug
opacity: 1;
pointer-events: inherit;
}
@ -1001,7 +520,7 @@ body.mobile.phone {
opacity: 0.3;
pointer-events: none;
}
.t-message-widget-no-data {
.js-summary-widget__message {
display: flex;
}
}
@ -1278,16 +797,20 @@ mct-indicators mct-include {
display: inline-block;
position: relative;
padding: 1px $interiorMarginSm; // Use padding instead of margin to keep hover chatter to a minimum
text-transform: uppercase;
&:before {
display: inline-block;
}
.label {
// Hover bubbles that appear when hovering on an Indicator
display: inline-block;
a,
button,
.s-button {
s-button,
.c-button {
// Make <a> in label look like buttons
transition: $transIn;
background: transparent;
@ -1312,8 +835,6 @@ mct-indicators mct-include {
margin-right: $interiorMarginSm;
}
}
button { text-transform: uppercase !important; }
}
&.no-collapse {
@ -1332,8 +853,6 @@ mct-indicators mct-include {
}
&:not(.no-collapse) {
z-index: 0;
&:before {
margin-right: 0 !important;
}
@ -1351,6 +870,8 @@ mct-indicators mct-include {
transform-origin: 10px 100%;
transform: scale(0.0);
white-space: nowrap;
z-index: 50;
&:before {
// Infobubble-style arrow element
content: '';
@ -1361,9 +882,9 @@ mct-indicators mct-include {
}
}
&:hover {
@include hover() {
background: $bg;
z-index: 1;
.label {
opacity: 1;
transform: scale(1.0);
@ -1656,75 +1177,6 @@ body.desktop {
}
}
/******************************************************************* WAIT SPINNERS */
@mixin spinner($b: 5px, $c: $colorKey) {
animation-name: rotation-centered;
animation-duration: 0.5s;
animation-iteration-count: infinite;
animation-timing-function: linear;
border-radius: 100%;
box-sizing: border-box;
border-color: rgba($c, 0.25);
border-top-color: rgba($c, 1.0);
border-style: solid;
border-width: $b;
display: block;
position: absolute;
left: 50%; top: 50%;
transform-origin: center;
transform: translate(-50%, -50%);
}
.wait-spinner {
@include spinner($waitSpinnerBorderW, $colorKey);
pointer-events: none;
z-index: 2;
&.inline {
display: inline-block !important;
margin-right: $interiorMargin;
position: relative !important;
vertical-align: middle;
}
}
.loading {
// Can be applied to any block element with height and width
pointer-events: none;
&:before,
&:after {
content: '';
}
&:before {
@include spinner($waitSpinnerBorderW, $colorLoadingFg);
height: $waitSpinnerD; width: $waitSpinnerD;
z-index: 10;
}
&:after {
@include abs();
background: $colorLoadingBg;
display: block;
z-index: 9;
}
&.tree-item.t-wait-node {
$d: $waitSpinnerTreeD;
$spinnerL: $interiorMargin + 19px + $d/2;
padding-left: $spinnerL + $d/2 + $interiorMargin;
.t-title-label {
font-style: italic;
opacity: 0.6;
}
&:before {
height: $d;
width: $d;
border-width: 4px;
left: $spinnerL;
}
&:after {
display: none;
}
}
}
/******************************************************************* FLEX STYLES */
.l-flex-row,
.l-flex-col {
@ -1791,7 +1243,7 @@ body.desktop {
.grid-properties {
display: grid;
grid-row-gap: 0;
grid-template-columns: 1fr 1fr;
grid-template-columns: 1fr 2fr;
}
.grid-span-all,
@ -1989,6 +1441,10 @@ body.desktop {
display: contents;
}
mct-container {
display: block;
}
.overlay {
.outer-holder {
background: $colorMenuBg;
@ -2054,3 +1510,157 @@ body.desktop {
}
}
}
.abs {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: auto;
}
.code {
font-family: "Lucida Console", monospace;
font-size: 0.7em;
line-height: 150%;
white-space: pre;
}
.codehilite {
@extend .code;
background-color: rgba($colorBodyFg, 0.1);
padding: 1em;
}
.s-status-missing {
// Labels. Expects .s-status-missing to be applied to mct-representation that contains
.t-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
// Item, grid item. Expects .s-status-missing to be applied to mct-representation that contains .item.grid-item
.item .t-item-icon-glyph:before {
content: $glyph-icon-object-unknown;
}
// Object header. Expects .s-status-missing to be applied to mct-representation.object-header
&.object-header {
.type-icon:before {
content: $glyph-icon-object-unknown;
}
}
// Tree item. Expects .s-status-missing to be applied to .tree-item,
// and mct-representation.search-item
&.tree-item,
&.search-item {
> .rep-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
}
}
.align-right {
text-align: right;
}
.centered {
text-align: center;
}
.no-selection {
// aka selection = "None". Used in palettes and their menu buttons.
$c: red;
$s: 48%;
$e: 52%;
background-image: linear-gradient(-45deg,
transparent $s - 5%,
$c $s,
$c $e,
transparent $e + 5%
);
box-shadow:inset rgba(black, 0.3) 0 0 0 1px;
background-repeat: no-repeat;
background-size: contain;
}
.scrolling,
.scroll {
overflow: auto;
}
.vscroll {
overflow-x: hidden;
overflow-y: auto;
&.scroll-pad {
padding-right: $interiorMargin;
}
}
.vscroll--persist {
overflow-x: hidden;
overflow-y: scroll;
}
.slidable {
cursor: move; // Fallback
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
&.horz {
cursor: col-resize;
}
&.vert {
cursor: row-resize;
}
}
.no-margin {
margin: 0;
}
.ds {
box-shadow: rgba(#000, 0.7) 0 4px 10px 2px;
}
.capitalize {
text-transform: capitalize;
}
.hide,
.hidden,
.t-main-view .hide-in-t-main-view {
display: none !important;
}
.hide-nice {
opacity: 0;
pointer-events: none;
}
.invisible {
display: block;
visibility: hidden;
height: 0;
padding: 0;
border: 0;
margin: 0 !important;
transform: scale(0);
pointer-events: none;
position: absolute;
}
.sep {
color: rgba(#fff, 0.2);
}
.comma-list span {
&:not(:first-child) {
&:before {
content: ', ';
}
}
}

View File

@ -51,33 +51,15 @@
/************************** EFFECTS */
@mixin pulse($animName: pulse, $dur: 500ms, $iteration: infinite, $opacity0: 0.5, $opacity100: 1) {
@include keyframes($animName) {
@keyframes pulse {
0% { opacity: $opacity0; }
100% { opacity: $opacity100; }
}
@include animation-name($animName);
@include animation-duration($dur);
@include animation-direction(alternate);
@include animation-iteration-count($iteration);
@include animation-timing-function(ease-in-out);
}
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) {
@include keyframes($animName) {
from { #{propName}: $propValStart; }
to { #{$propName}: $propValEnd; }
}
@include animToParams($animName, $dur: $dur, $delay: $delay, $dir: $dir, $count: $count)
}
@mixin animToParams($animName, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) {
@include animation-name($animName);
@include animation-duration($dur);
@include animation-delay($delay);
@include animation-fill-mode(both);
@include animation-direction($dir);
@include animation-iteration-count($count);
@include animation-timing-function(ease-in-out);
animation-name: $animName;
animation-duration: $dur;
animation-direction: alternate;
animation-iteration-count: $iteration;
animation-timing-function: ease-in-out;
}
/************************** VISUALS */
@ -391,7 +373,8 @@
color: $colorBtnFgHov;
}
&[class*="--major"] {
&[class*="--major"],
&[class*='is-active']{
background: $colorBtnMajorBg;
color: $colorBtnMajorFg;
@ -411,24 +394,37 @@
}
}
@mixin cClickIcon() {
// A clickable element that just includes the icon, no background
@include cControl();
cursor: pointer;
padding: 4px; // Bigger hit area
opacity: 0.6;
transition: $transOut;
transform-origin: center;
@include hover() {
transform: scale(1.1);
transition: $transIn;
opacity: 1;
}
}
@mixin cClickIconButton() {
// A clickable element that just includes the icon
// Background is displayed on hover
// Padding is included to facilitate a bigger hit area
// Make the icon bigger relative to its container
@include cControl();
$pLR: 4px;
$pTB: 4px;
background: none;
box-shadow: none;
border-radius: $controlCr;
cursor: pointer;
$pLR: 4px;
$pTB: 4px;
transition: $transOut;
border-radius: $controlCr;
padding: $pTB $pLR;
@include hover() {
background: $colorClickIconBgHov;
color: $colorClickIconFgHov;
}
&:before,
*:before {
// *:before handles any nested containers that may contain glyph elements
@ -436,6 +432,12 @@
font-size: 1.25em;
}
@include hover() {
transition: $transIn;
background: $colorClickIconButtonBgHov;
color: $colorClickIconButtonFgHov;
}
&[class*="--major"] {
color: $colorKey;
}
@ -465,50 +467,6 @@
}
}
@mixin cCompactButtons($bg, $fg, $bgHov) {
// Used in Tab view
// To be used in indicators popups?
$m: 1px;
$btnM: $m;
&-holder {
background: $colorTabsHolderBg;
border-radius: $controlCr;
padding: $m ($m - $btnM) ($m - $btnM) $m;
display: flex;
flex-flow: row wrap;
justify-content: stretch;
> * {
margin: 0 $btnM $btnM 0;
}
}
background: $colorBtnBg;
color: $colorBtnFg;
flex: 1 1 auto;
padding: $interiorMargin;
&:before {
opacity: 0.5;
}
> * {
margin-left: $interiorMarginSm;
}
@include hover() {
background: $bgHov;
}
&.is-current {
background: $colorBtnReverseBg;
color: $colorBtnReverseFg;
pointer-events: none;
}
}
@mixin cSelect($bg, $fg, $arwClr, $shdw) {
$svgArwClr: str-slice(inspect($arwClr), 2, str-length(inspect($arwClr))); // Remove initial # in color value
background: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{$svgArwClr}' d='M5 5l5-5H0z'/%3e%3c/svg%3e"), $bg;

View File

@ -19,16 +19,18 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/******************************************************** TABLE */
table {
$minW: 50px;
width: 100%;
thead {
background: $colorTabHeaderBg;
th + th {
border-left: 1px solid $colorTabHeaderBorder;
th {
background: $colorTabHeaderBg;
+ th {
border-left: 1px solid $colorTabHeaderBorder;
}
}
}
@ -47,14 +49,19 @@ table {
td {
vertical-align: top;
}
a { color: $colorBtnMajorBg; }
}
div.c-table {
// When c-table is used as a wrapper element in more complex table views
height: 100%;
}
.c-table {
// Can be used by any type of table, scrolling, LAD, etc.
$min-w: 50px;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
width: 100%;
&__control-bar,
@ -117,4 +124,36 @@ table {
th, td {
width: 33%; // Needed to prevent size jumping as values dynamically update
}
}
}
/************************************** TABLE AND SUMMARY VIEWS */
// Displays summary values above a table.
.c-table-and-summary {
height: 100%;
width: 100%;
overflow: auto;
display: flex;
flex-direction: column;
> * + * { margin-top: $interiorMargin; }
&__summary {
display: flex;
justify-items: stretch;
> * + * { margin-left: 1px; }
}
&__summary-item {
background: $colorSummaryBg;
color: $colorSummaryFg;
flex: 1 1 auto;
padding: $interiorMargin $interiorMarginLg;
em {
font-weight: bold;
color: $colorSummaryFgEm;
}
}
}

View File

@ -24,6 +24,7 @@
@import "sass-base";
/******************** RENDERS CSS */
@import "about";
@import "glyphs";
@import "global";
@import "status";
@ -31,3 +32,4 @@
@import "forms";
@import "table";
@import "legacy";
@import "legacy-plots";

View File

@ -24,8 +24,9 @@
// Meant for use as a single line import in Vue SFC's.
// Do not include anything that renders to CSS!
@import "constants";
@import "constants-espresso"; // TEMP
//@import "constants-snow"; // TEMP
@import "constants-mobile.scss";
//@import "constants-espresso"; // TEMP
@import "constants-snow"; // TEMP
//@import "constants-maelstrom";
@import "mixins";
@import "animations";

View File

@ -113,6 +113,12 @@
}
}
}
.c-click-icon,
.c-button {
// Shrink buttons a bit when they appear in a frame
font-size: 0.8em;
}
}
</style>

View File

@ -5,15 +5,18 @@
@input="applySearch"
@clear="applySearch">
</Search>
<div class="c-elements-pool__elements">
<div class="c-elements-pool__elements"
:class="{'is-dragging': isDragging}">
<ul class="c-tree c-elements-pool__tree" id="inspector-elements-tree"
v-if="elements.length > 0">
<li :key="element.identifier.key" v-for="(element, index) in elements" @drop="moveTo(index)" @dragover="allowDrop">
<div class="c-tree__item c-elements-pool__item">
<li :key="element.identifier.key" v-for="(element, index) in elements"
@drop="moveTo(index)"
@dragover="allowDrop">
<div class="c-tree__item c-elements-pool__item"
draggable="true"
@dragstart="moveFrom(index)">
<span class="c-elements-pool__grippy"
v-if="elements.length > 1 && isEditing"
draggable="true"
@dragstart="moveFrom(index)">
v-if="elements.length > 1 && isEditing">
</span>
<object-label :domainObject="element" :objectPath="[element, parentObject]"></object-label>
</div>
@ -44,18 +47,22 @@
&__elements {
flex: 1 1 auto;
overflow: auto;
&.is-dragging {
li { opacity: 0.2; }
}
}
&__grippy {
$d: 8px;
@include grippy($c: $colorItemTreeVC, $dir: 'y');
flex: 0 0 auto;
margin-right: $interiorMarginSm;
transform: translateY(-2px);
width: $d; height: $d;
}
}
.js-last-place{
.js-last-place {
height: 10px;
}
</style>
@ -74,7 +81,8 @@ export default {
elements: [],
isEditing: this.openmct.editor.isEditing(),
parentObject: undefined,
currentSearch: ''
currentSearch: '',
isDragging: false
}
},
mounted() {
@ -134,7 +142,7 @@ export default {
this.applySearch(this.currentSearch);
},
removeElement(identifier) {
let keyString = this.openmct.objects.makeKeyString(element.identifier);
let keyString = this.openmct.objects.makeKeyString(identifier);
delete this.elementsCache[keyString];
this.applySearch(this.currentSearch);
},
@ -154,12 +162,19 @@ export default {
this.composition.reorder(this.moveFromIndex, moveToIndex);
},
moveFrom(index){
this.isDragging = true;
this.moveFromIndex = index;
document.addEventListener('dragend', this.hideDragStyling);
},
hideDragStyling() {
this.isDragging = false;
document.removeEventListener('dragend', this.hideDragStyling);
}
},
destroyed() {
this.openmct.editor.off('isEditing', this.setEditState);
this.openmct.selection.off('change', this.showSelection);
if (this.mutationUnobserver) {
this.mutationUnobserver();
}

View File

@ -22,7 +22,6 @@
min-height: 50px;
+ [class*="__"] {
// Margin between elements
margin-top: $interiorMargin;
}
@ -37,9 +36,7 @@
}
&__elements {
// LEGACY TODO: Refactor when markup is updated, fix scrolling
// so that only tree holder handles overflow
height: 200px;
height: 200px; // Initial height
.tree-item {
.t-object-label {
@ -64,48 +61,6 @@
}
/************************************************************** LEGACY */
// TODO: refactor when legacy properties markup can be converted
.inspector-location {
display: inline-block;
.location-item {
$h: 1.2em;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
line-height: $h;
position: relative;
padding: 2px 4px;
.t-object-label {
.t-item-icon {
height: $h;
margin-right: $interiorMarginSm;
}
}
&:hover {
background: $colorItemTreeHoverBg;
color: $colorItemTreeHoverFg;
.icon {
color: $colorItemTreeIconHover;
}
}
}
&:not(.last) .t-object-label .t-title-label:after {
color: pushBack($colorInspectorFg, 15%);
content: '\e904';
display: inline-block;
font-family: symbolsfont;
font-size: 8px;
font-style: normal !important;
line-height: inherit;
margin-left: $interiorMarginSm;
width: 4px;
}
}
.l-inspector-part {
display: contents;
}
@ -116,8 +71,8 @@
grid-column: 1 / 3;
}
.tree .grid-properties {
margin-left: $treeItemIndent + $interiorMarginLg;
.c-tree .grid-properties {
margin-left: $treeItemIndent;
}
}
@ -143,7 +98,6 @@
}
+ .c-properties {
// Margin between components
margin-top: $interiorMarginLg;
}

View File

@ -1,25 +1,79 @@
<template>
<div class="c-properties c-properties--location">
<div class="c-properties__header" title="The location of this linked object.">Location</div>
<div class="c-properties__header" title="The location of this linked object.">Original Location</div>
<ul class="c-properties__section">
<li class="c-properties__row">
<div class="c-properties__label">This Link</div>
<div class="c-properties__value">TODO</div>
</li>
<li class="c-properties__row">
<div class="c-properties__label">Original</div>
<div class="c-properties__value">TODO</div>
<li class="c-properties__row" v-if="originalPath.length">
<ul class="c-properties__value c-location">
<li v-for="pathObject in orderedOriginalPath"
class="c-location__item"
:key="pathObject.key">
<object-label
:domainObject="pathObject.domainObject"
:objectPath="pathObject.objectPath">
</object-label>
</li>
</ul>
</li>
</ul>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-location {
display: flex;
flex-wrap: wrap;
&__item {
$m: $interiorMarginSm;
cursor: pointer;
display: flex;
align-items: center;
margin: 0 $m $m 0;
&:not(:last-child) {
&:after {
color: $colorInspectorPropName;
content: $glyph-icon-arrow-right;
font-family: symbolsfont;
font-size: 0.7em;
margin-left: $m;
opacity: 0.8;
}
}
.c-object-label {
padding: 0;
transition: $transOut;
&__type-icon {
width: auto;
font-size: 1em;
}
&:hover {
transition: $transIn;
filter: $filterHov;
}
}
}
}
</style>
<script>
import ObjectLabel from '../components/ObjectLabel.vue';
export default {
inject: ['openmct'],
components: {
ObjectLabel
},
data() {
return {
domainObject: {}
domainObject: {},
originalPath: [],
keyString: ''
}
},
mounted() {
@ -30,12 +84,52 @@ export default {
this.openmct.selection.off('change', this.updateSelection);
},
methods: {
setOriginalPath(path, skipSlice) {
let originalPath = path;
if (!skipSlice) {
originalPath = path.slice(1,-1);
}
this.originalPath = originalPath.map((domainObject, index, pathArray) => {
let key = this.openmct.objects.makeKeyString(domainObject.identifier);
return {
domainObject,
key,
objectPath: pathArray.slice(index)
}
});
},
clearData() {
this.domainObject = {};
this.originalPath = [];
this.keyString = '';
},
updateSelection(selection) {
if (selection.length === 0) {
this.domainObject = {};
if (!selection.length) {
this.clearData();
return;
} else if (!selection[0].context.item && selection[1] && selection[1].context.item) {
this.setOriginalPath([selection[1].context.item], true);
return;
}
this.domainObject = selection[0].context.item;
let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
if (keyString && this.keyString !== keyString) {
this.keyString = keyString;
this.originalPath = [];
this.openmct.objects.getOriginalPath(this.keyString)
.then(this.setOriginalPath);
}
}
},
computed: {
orderedOriginalPath() {
return this.originalPath.reverse();
}
}
}

View File

@ -12,11 +12,11 @@
</li>
<li class="c-properties__row" v-if="item.created">
<div class="c-properties__label">Created</div>
<div class="c-properties__value">{{ item.created }}</div>
<div class="c-properties__value c-ne__text">{{ formatTime(item.created) }}</div>
</li>
<li class="c-properties__row" v-if="item.modified">
<div class="c-properties__label">Modified</div>
<div class="c-properties__value">{{ item.modified }}</div>
<div class="c-properties__value c-ne__text">{{ formatTime(item.modified) }}</div>
</li>
<li class="c-properties__row"
v-for="prop in typeProperties"
@ -29,6 +29,8 @@
</template>
<script>
import Moment from "moment";
export default {
inject: ['openmct'],
data() {
@ -93,6 +95,9 @@ export default {
return;
}
this.domainObject = selection[0].context.item;
},
formatTime(unixTime) {
return Moment.utc(unixTime).format('YYYY-MM-DD[\n]HH:mm:ss') + ' UTC';
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="c-about c-about--splash">
<div v-if="branding.aboutHtml" class="s-text l-content" v-html="branding.aboutHtml"></div>
<div v-else class="c-about__image"></div>
<div v-else class="c-about__image c-splash-image"></div>
<div class="c-about__text s-text">
<h1 class="l-title s-title">Open MCT</h1>

View File

@ -2,7 +2,7 @@
<div class="l-browse-bar">
<div class="l-browse-bar__start">
<button v-if="hasParent"
class="l-browse-bar__nav-to-parent-button c-click-icon c-click-icon--major icon-pointer-left"
class="l-browse-bar__nav-to-parent-button c-icon-button c-icon-button--major icon-pointer-left"
@click="goToParent"></button>
<div class="l-browse-bar__object-name--w"
:class="type.cssClass">
@ -112,7 +112,7 @@ const PLACEHOLDER_OBJECT = {};
promptUserandCancelEditing() {
let dialog = this.openmct.overlays.dialog({
iconClass: 'alert',
message: 'Are you sure you want to continue? All unsaved changes will be lost!',
message: 'Any unsaved changes will be lost. Are you sure you want to continue?',
buttons: [
{
label: 'Ok',

View File

@ -8,7 +8,7 @@
v-if="opened">
<div class="c-super-menu__menu">
<ul>
<li v-for="(item, index) in items"
<li v-for="(item, index) in sortedItems"
:key="index"
:class="item.class"
:title="item.title"
@ -145,6 +145,19 @@
selectedMenuItem: {},
opened: false
}
},
computed: {
sortedItems () {
return this.items.sort((a,b) => {
if (a.name < b.name) {
return -1;
} else if (a.name > b.name) {
return 1;
} else {
return 0;
}
});
}
}
}
</script>

View File

@ -5,11 +5,11 @@
<div class="l-shell__head">
<CreateButton class="l-shell__create-button"></CreateButton>
<div class="l-shell__controls">
<button class="c-click-icon c-click-icon--major icon-new-window" title="Open in a new browser tab"
<button class="c-icon-button c-icon-button--major icon-new-window" title="Open in a new browser tab"
@click="openInNewTab"
target="_blank">
</button>
<button v-bind:class="['c-click-icon c-click-icon--major', fullScreen ? 'icon-fullscreen-collapse' : 'icon-fullscreen-expand']"
<button v-bind:class="['c-icon-button c-icon-button--major', fullScreen ? 'icon-fullscreen-collapse' : 'icon-fullscreen-expand']"
v-bind:title="`${fullScreen ? 'Exit' : 'Enable'} full screen mode`"
@click="fullScreenToggle">
</button>
@ -74,7 +74,7 @@
[class*="collapse-button"] {
// For mobile, collapse button becomes menu icon
body.mobile & {
@include cClickIcon();
@include cClickIconButton();
position: absolute;
right: -2 * nth($shellPanePad, 2); // Needs to be -1 * when pane is collapsed
top: 0;
@ -95,6 +95,7 @@
.l-pane__contents {
display: flex;
flex-flow: column nowrap;
overflow-x: hidden;
> * {
flex: 0 0 auto;
@ -186,7 +187,6 @@
// Wrapper for main views
flex: 1 1 auto !important;
overflow: auto;
//font-size: 16px; // TEMP FOR LEGACY STYLING
}
&__tree {
@ -209,6 +209,8 @@
&__main {
// Top and bottom padding in container that holds tree, __pane-main and Inspector
padding: $shellMainPad;
min-height: 0;
> .l-pane {
padding-top: 0;
padding-bottom: 0;
@ -240,7 +242,10 @@
.is-editing {
.l-shell__main-container {
$m: 3px;
box-shadow: $colorBodyBg 0 0 0 1px, $editUIAreaShdw;
margin-left: $m;
margin-right: $m;
&[s-selected] {
// Provide a clearer selection context articulation for the main edit area

View File

@ -7,10 +7,17 @@
@clear="searchTree">
</search>
</div>
<!-- loading -->
<div class="c-tree-and-search__loading loading"
v-if="isLoading"></div>
<!-- end loading -->
<div class="c-tree-and-search__no-results" v-if="treeItems.length === 0">
No results found
</div>
<ul class="c-tree-and-search__tree c-tree">
<ul class="c-tree-and-search__tree c-tree"
v-if="!isLoading">
<tree-item v-for="treeItem in treeItems"
:key="treeItem.id"
:node="treeItem">
@ -34,6 +41,10 @@
flex: 0 0 auto;
}
&__loading {
flex: 1 1 auto;
}
&__no-results {
font-style: italic;
opacity: 0.6;
@ -47,10 +58,16 @@
.c-tree {
@include userSelectNone();
height: 100%; // Chrome 73 overflow bug fix
overflow-x: hidden;
overflow-y: auto;
padding-right: $interiorMargin;
li {
position: relative;
&.c-tree__item-h { display: block; }
}
.c-tree {
margin-left: 15px;
}
@ -168,7 +185,8 @@
return {
searchValue: '',
allTreeItems: [],
filteredTreeItems: []
filteredTreeItems: [],
isLoading: false
}
},
computed: {
@ -182,9 +200,13 @@
},
methods: {
getAllChildren() {
this.isLoading = true;
this.openmct.objects.get('ROOT')
.then(root => this.openmct.composition.get(root).load())
.then(root => {
return this.openmct.composition.get(root).load()
})
.then(children => {
this.isLoading = false;
this.allTreeItems = children.map(c => {
return {
id: this.openmct.objects.makeKeyString(c.identifier),

View File

@ -128,7 +128,6 @@
background: $colorSplitterBg;
display: block;
position: absolute;
z-index: 10;
transition: $transOut;
&:before {

View File

@ -17,7 +17,7 @@
at runtime from the About dialog for additional information.
-->
<template>
<div class="c-message-banner s-message-banner"
<div class="c-message-banner"
:class="[
activeModel.severity,
{
@ -31,36 +31,68 @@
class="c-message-banner__progress-bar"
v-if="activeModel.progressPerc !== undefined" :model="activeModel">
</progress-bar>
<button class="c-message-banner__close-btn c-click-icon icon-x"
@click="dismiss()"></button>
<button class="c-message-banner__close-button c-click-icon icon-x-in-circle"
@click.stop="dismiss()"></button>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
$bgPb: 10%;
$bgPbD: 10%;
background-color: darken($bg, $bgPb);
color: $fg;
&:hover {
background-color: darken($bg, $bgPb - $bgPbD);
}
.s-action {
background-color: darken($bg, $bgPb + $bgPbD);
&:hover {
background-color: darken($bg, $bgPb);
}
}
}
.c-message-banner {
$closeBtnSize: 7px;
$m: 1px;
border-radius: $controlCr;
@include statusBannerColors($colorStatusDefault, $colorStatusFg);
cursor: pointer;
display: flex;
align-items: center;
left: 50%;
max-width: 50%;
padding: $interiorMargin $interiorMarginLg;
padding: $interiorMargin $interiorMargin $interiorMargin $interiorMarginLg;
position: absolute;
transform: translateX(-50%);
bottom: $m;
z-index: 2;
// TODOs:
// - Styling for message types, add color values to theme scss files
// - Factor out s-message-banner
> * + * {
margin-left: $interiorMargin;
}
&.ok {
@include statusBannerColors($colorOk, $colorOkFg);
}
&.info {
@include statusBannerColors($colorInfo, $colorInfoFg);
}
&.caution,
&.warning,
&.alert {
@include statusBannerColors($colorWarningLo,$colorWarningLoFg);
}
&.error {
@include statusBannerColors($colorWarningHi, $colorWarningHiFg);
}
&__message {
@include ellipsize();
flex: 1 1 auto;
@ -78,29 +110,10 @@
}
}
&__close-btn {
font-size: $closeBtnSize;
&__close-button {
font-size: 1.25em;
}
}
/*
.l-message-banner {
display: flex;
left: 50%;
position: absolute;
}
.banner-elem {
display: inline;
}*/
</style>
<script>
@ -176,6 +189,7 @@
},
destroyActiveNotification() {
this.clearModel();
activeNotification.off('destroy', this.destroyActiveNotification);
activeNotification = undefined;
},
dismiss() {
@ -189,7 +203,6 @@
this.activeModel.minimized = true;
activeNotification.off('progress', this.updateProgress);
activeNotification.off('minimized', this.minimized);
activeNotification.off('destroy', this.destroyActiveNotification);
activeNotification.off('progress', updateMaxProgressBar);
activeNotification.off('minimized', dismissMaximizedDialog);

View File

@ -11,10 +11,15 @@
</object-label>
</div>
<ul v-if="expanded" class="c-tree">
<li class="c-tree__item-h"
v-if="isLoading && !loaded">
<div class="c-tree__item loading">
<span class="c-tree__item__label">Loading...</span>
</div>
</li>
<tree-item v-for="child in children"
:key="child.id"
:node="child"
>
:node="child">
</tree-item>
</ul>
</li>
@ -50,7 +55,7 @@
}
let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
return parentKeyString !== this.node.object.location;
},
}
},
mounted() {
// TODO: should update on mutation.
@ -88,7 +93,8 @@
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addChild);
this.composition.on('remove', this.removeChild);
this.composition.load().then(this.finishLoading());
this.composition.load().then(this.finishLoading);
this.isLoading = true;
}
}
},

View File

@ -60,6 +60,10 @@ class ApplicationRouter extends EventEmitter {
}
handleLocationChange(pathString) {
if (pathString[0] !== '/') {
pathString = '/' + pathString
}
let url = new URL(
pathString,
`${location.protocol}//${location.host}${location.pathname}`

View File

@ -9,11 +9,27 @@ define([
let browseObject;
let unobserve = undefined;
openmct.router.route(/^\/browse\/?$/, navigateToFirstChildOfRoot);
openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => {
let navigatePath = results[1];
navigateToPath(navigatePath, params.view);
});
openmct.router.on('change:params', function (newParams, oldParams, changed) {
if (changed.view && browseObject) {
let provider = openmct
.objectViews
.getByProviderKey(changed.view);
viewObject(browseObject, provider);
}
});
function viewObject(object, viewProvider) {
openmct.layout.$refs.browseObject.show(object, viewProvider.key, true);
openmct.layout.$refs.browseBar.domainObject = object;
openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
};
}
function navigateToPath(path, currentViewKey) {
navigateCall++;
@ -24,13 +40,12 @@ define([
unobserve = undefined;
}
//Split path into object identifiers
if (!Array.isArray(path)) {
path = path.split('/');
}
return Promise.all(path.map((keyString)=>{
return openmct.objects.get(keyString);
})).then((objects)=>{
return pathToObjects(path).then((objects)=>{
if (currentNavigation !== navigateCall) {
return; // Prevent race.
}
@ -42,20 +57,23 @@ define([
// navigation service and router to expose a clear and minimal
// API for this.
openmct.router.path = objects.reverse();
unobserve = this.openmct.objects.observe(openmct.router.path[0], '*', (newObject) => {
openmct.router.path[0] = newObject;
});
openmct.layout.$refs.browseBar.domainObject = navigatedObject;
browseObject = navigatedObject;
if (!navigatedObject) {
openmct.layout.$refs.browseObject.clear();
return;
}
let currentProvider = openmct
.objectViews
.getByProviderKey(currentViewKey)
.getByProviderKey(currentViewKey);
document.title = browseObject.name; //change document title to current object in main view
if (currentProvider && currentProvider.canView(navigatedObject)) {
viewObject(navigatedObject, currentProvider);
@ -76,23 +94,25 @@ define([
});
}
openmct.router.route(/^\/browse\/(.*)$/, (path, results, params) => {
let navigatePath = results[1];
if (!navigatePath) {
navigatePath = 'mine';
}
navigateToPath(navigatePath, params.view);
});
openmct.router.on('change:params', function (newParams, oldParams, changed) {
if (changed.view && browseObject) {
let provider = openmct
.objectViews
.getByProviderKey(changed.view);
viewObject(browseObject, provider);
}
});
function pathToObjects(path) {
return Promise.all(path.map((keyString)=>{
return openmct.objects.get(keyString);
}));
}
function navigateToFirstChildOfRoot() {
openmct.objects.get('ROOT').then(rootObject => {
openmct.composition.get(rootObject).load()
.then(children => {
let lastChild = children[children.length - 1];
if (!lastChild) {
console.error('Unable to navigate to anything. No root objects found.');
} else {
let lastChildId = openmct.objects.makeKeyString(lastChild.identifier);
openmct.router.setPath(`#/browse/${lastChildId}`);
}
});
});
}
}
});

View File

@ -1,13 +1,13 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-click-icon"
<div class="c-icon-button"
:title="options.title"
:class="{
[options.icon]: true,
'c-click-icon--caution': options.modifier === 'caution'
'c-icon-button--caution': options.modifier === 'caution'
}"
@click="onClick">
<div class="c-click-icon__label"
<div class="c-icon-button__label"
v-if="options.label">
{{ options.label }}
</div>

View File

@ -1,6 +1,6 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--swatched"
<div class="c-icon-button c-icon-button--swatched"
:class="options.icon"
:title="options.title"
@click="toggle">

View File

@ -1,10 +1,10 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--menu"
<div class="c-icon-button c-icon-button--menu"
:class="options.icon"
:title="options.title"
@click="toggle">
<div class="c-click-icon__label"
<div class="c-icon-button__label"
v-if="options.label">
{{ options.label }}
</div>

View File

@ -1,6 +1,6 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--menu"
<div class="c-icon-button c-icon-button--menu"
:class="options.icon"
:title="options.title"
@click="toggle">

View File

@ -1,6 +1,6 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-click-icon"
<div class="c-icon-button"
:title="nextValue.title"
:class="nextValue.icon"
@click="cycle">